diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2015-10-22 01:07:13 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2015-10-22 01:07:13 +0200 |
commit | 6c563e370d0f2f3cf36f3b274e8fe1392ca7125f (patch) | |
tree | 8e9a7cd2e979bc6c956b863128086a342dd1a676 | |
parent | c47917865d5f40f9044dd8845814c591d801318d (diff) | |
download | busybox-w32-6c563e370d0f2f3cf36f3b274e8fe1392ca7125f.tar.gz busybox-w32-6c563e370d0f2f3cf36f3b274e8fe1392ca7125f.tar.bz2 busybox-w32-6c563e370d0f2f3cf36f3b274e8fe1392ca7125f.zip |
tar: add support for --strip-components=N
function old new delta
data_extract_all 882 995 +113
tar_longopts 290 309 +19
tar_main 938 942 +4
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 3/0 up/down: 136/0) Total: 136 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | archival/libarchive/data_extract_all.c | 108 | ||||
-rw-r--r-- | archival/libarchive/get_header_tar.c | 1 | ||||
-rw-r--r-- | archival/tar.c | 80 | ||||
-rw-r--r-- | include/bb_archive.h | 3 | ||||
-rwxr-xr-x | testsuite/tar.tests | 10 |
5 files changed, 156 insertions, 46 deletions
diff --git a/archival/libarchive/data_extract_all.c b/archival/libarchive/data_extract_all.c index 45776dcbe..bd51d2ad3 100644 --- a/archival/libarchive/data_extract_all.c +++ b/archival/libarchive/data_extract_all.c | |||
@@ -8,9 +8,17 @@ | |||
8 | 8 | ||
9 | void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | 9 | void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) |
10 | { | 10 | { |
11 | |||
11 | file_header_t *file_header = archive_handle->file_header; | 12 | file_header_t *file_header = archive_handle->file_header; |
12 | int dst_fd; | 13 | int dst_fd; |
13 | int res; | 14 | int res; |
15 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | ||
16 | char *dst_name; | ||
17 | char *dst_link; | ||
18 | #else | ||
19 | # define dst_name (file_header->name) | ||
20 | # define dst_link (file_header->link_target) | ||
21 | #endif | ||
14 | 22 | ||
15 | #if ENABLE_FEATURE_TAR_SELINUX | 23 | #if ENABLE_FEATURE_TAR_SELINUX |
16 | char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; | 24 | char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; |
@@ -23,11 +31,47 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
23 | } | 31 | } |
24 | #endif | 32 | #endif |
25 | 33 | ||
34 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | ||
35 | dst_name = file_header->name; | ||
36 | dst_link = file_header->link_target; | ||
37 | if (archive_handle->tar__strip_components) { | ||
38 | unsigned n = archive_handle->tar__strip_components; | ||
39 | do { | ||
40 | dst_name = strchr(dst_name, '/'); | ||
41 | if (!dst_name || dst_name[1] == '\0') { | ||
42 | data_skip(archive_handle); | ||
43 | return; | ||
44 | } | ||
45 | dst_name++; | ||
46 | /* | ||
47 | * Link target is shortened only for hardlinks: | ||
48 | * softlinks restored unchanged. | ||
49 | */ | ||
50 | if (S_ISREG(file_header->mode) | ||
51 | && file_header->size == 0 | ||
52 | && dst_link | ||
53 | ) { | ||
54 | // GNU tar 1.26 does not check that we reached end of link name: | ||
55 | // if "dir/hardlink" is hardlinked to "file", | ||
56 | // tar xvf a.tar --strip-components=1 says: | ||
57 | // tar: hardlink: Cannot hard link to '': No such file or directory | ||
58 | // and continues processing. We silently skip such entries. | ||
59 | dst_link = strchr(dst_link, '/'); | ||
60 | if (!dst_link || dst_link[1] == '\0') { | ||
61 | data_skip(archive_handle); | ||
62 | return; | ||
63 | } | ||
64 | dst_link++; | ||
65 | } | ||
66 | } while (--n != 0); | ||
67 | } | ||
68 | #endif | ||
69 | |||
26 | if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { | 70 | if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { |
27 | char *slash = strrchr(file_header->name, '/'); | 71 | char *slash = strrchr(dst_name, '/'); |
28 | if (slash) { | 72 | if (slash) { |
29 | *slash = '\0'; | 73 | *slash = '\0'; |
30 | bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); | 74 | bb_make_directory(dst_name, -1, FILEUTILS_RECUR); |
31 | *slash = '/'; | 75 | *slash = '/'; |
32 | } | 76 | } |
33 | } | 77 | } |
@@ -38,8 +82,8 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
38 | /* Is it hardlink? | 82 | /* Is it hardlink? |
39 | * We encode hard links as regular files of size 0 with a symlink */ | 83 | * We encode hard links as regular files of size 0 with a symlink */ |
40 | if (S_ISREG(file_header->mode) | 84 | if (S_ISREG(file_header->mode) |
41 | && file_header->link_target | ||
42 | && file_header->size == 0 | 85 | && file_header->size == 0 |
86 | && dst_link | ||
43 | ) { | 87 | ) { |
44 | /* Ugly special case: | 88 | /* Ugly special case: |
45 | * tar cf t.tar hardlink1 hardlink2 hardlink1 | 89 | * tar cf t.tar hardlink1 hardlink2 hardlink1 |
@@ -48,22 +92,22 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
48 | * hardlink2 -> hardlink1 | 92 | * hardlink2 -> hardlink1 |
49 | * hardlink1 -> hardlink1 <== !!! | 93 | * hardlink1 -> hardlink1 <== !!! |
50 | */ | 94 | */ |
51 | if (strcmp(file_header->link_target, file_header->name) == 0) | 95 | if (strcmp(dst_link, dst_name) == 0) |
52 | goto ret; | 96 | goto ret; |
53 | } | 97 | } |
54 | /* Proceed with deleting */ | 98 | /* Proceed with deleting */ |
55 | if (unlink(file_header->name) == -1 | 99 | if (unlink(dst_name) == -1 |
56 | && errno != ENOENT | 100 | && errno != ENOENT |
57 | ) { | 101 | ) { |
58 | bb_perror_msg_and_die("can't remove old file %s", | 102 | bb_perror_msg_and_die("can't remove old file %s", |
59 | file_header->name); | 103 | dst_name); |
60 | } | 104 | } |
61 | } | 105 | } |
62 | } | 106 | } |
63 | else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) { | 107 | else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) { |
64 | /* Remove the existing entry if its older than the extracted entry */ | 108 | /* Remove the existing entry if its older than the extracted entry */ |
65 | struct stat existing_sb; | 109 | struct stat existing_sb; |
66 | if (lstat(file_header->name, &existing_sb) == -1) { | 110 | if (lstat(dst_name, &existing_sb) == -1) { |
67 | if (errno != ENOENT) { | 111 | if (errno != ENOENT) { |
68 | bb_perror_msg_and_die("can't stat old file"); | 112 | bb_perror_msg_and_die("can't stat old file"); |
69 | } | 113 | } |
@@ -73,30 +117,30 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
73 | && !S_ISDIR(file_header->mode) | 117 | && !S_ISDIR(file_header->mode) |
74 | ) { | 118 | ) { |
75 | bb_error_msg("%s not created: newer or " | 119 | bb_error_msg("%s not created: newer or " |
76 | "same age file exists", file_header->name); | 120 | "same age file exists", dst_name); |
77 | } | 121 | } |
78 | data_skip(archive_handle); | 122 | data_skip(archive_handle); |
79 | goto ret; | 123 | goto ret; |
80 | } | 124 | } |
81 | else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { | 125 | else if ((unlink(dst_name) == -1) && (errno != EISDIR)) { |
82 | bb_perror_msg_and_die("can't remove old file %s", | 126 | bb_perror_msg_and_die("can't remove old file %s", |
83 | file_header->name); | 127 | dst_name); |
84 | } | 128 | } |
85 | } | 129 | } |
86 | 130 | ||
87 | /* Handle hard links separately | 131 | /* Handle hard links separately |
88 | * We encode hard links as regular files of size 0 with a symlink */ | 132 | * We encode hard links as regular files of size 0 with a symlink */ |
89 | if (S_ISREG(file_header->mode) | 133 | if (S_ISREG(file_header->mode) |
90 | && file_header->link_target | ||
91 | && file_header->size == 0 | 134 | && file_header->size == 0 |
135 | && dst_link | ||
92 | ) { | 136 | ) { |
93 | /* hard link */ | 137 | /* Hard link */ |
94 | res = link(file_header->link_target, file_header->name); | 138 | res = link(dst_link, dst_name); |
95 | if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { | 139 | if (res != 0 && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { |
96 | bb_perror_msg("can't create %slink " | 140 | bb_perror_msg("can't create %slink " |
97 | "from %s to %s", "hard", | 141 | "from %s to %s", "hard", |
98 | file_header->name, | 142 | dst_name, |
99 | file_header->link_target); | 143 | dst_link); |
100 | } | 144 | } |
101 | /* Hardlinks have no separate mode/ownership, skip chown/chmod */ | 145 | /* Hardlinks have no separate mode/ownership, skip chown/chmod */ |
102 | goto ret; | 146 | goto ret; |
@@ -106,17 +150,17 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
106 | switch (file_header->mode & S_IFMT) { | 150 | switch (file_header->mode & S_IFMT) { |
107 | case S_IFREG: { | 151 | case S_IFREG: { |
108 | /* Regular file */ | 152 | /* Regular file */ |
109 | char *dst_name; | 153 | char *dst_nameN; |
110 | int flags = O_WRONLY | O_CREAT | O_EXCL; | 154 | int flags = O_WRONLY | O_CREAT | O_EXCL; |
111 | if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) | 155 | if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) |
112 | flags = O_WRONLY | O_CREAT | O_TRUNC; | 156 | flags = O_WRONLY | O_CREAT | O_TRUNC; |
113 | dst_name = file_header->name; | 157 | dst_nameN = dst_name; |
114 | #ifdef ARCHIVE_REPLACE_VIA_RENAME | 158 | #ifdef ARCHIVE_REPLACE_VIA_RENAME |
115 | if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) | 159 | if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) |
116 | /* rpm-style temp file name */ | 160 | /* rpm-style temp file name */ |
117 | dst_name = xasprintf("%s;%x", dst_name, (int)getpid()); | 161 | dst_nameN = xasprintf("%s;%x", dst_name, (int)getpid()); |
118 | #endif | 162 | #endif |
119 | dst_fd = xopen3(dst_name, | 163 | dst_fd = xopen3(dst_nameN, |
120 | flags, | 164 | flags, |
121 | file_header->mode | 165 | file_header->mode |
122 | ); | 166 | ); |
@@ -124,32 +168,32 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
124 | close(dst_fd); | 168 | close(dst_fd); |
125 | #ifdef ARCHIVE_REPLACE_VIA_RENAME | 169 | #ifdef ARCHIVE_REPLACE_VIA_RENAME |
126 | if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) { | 170 | if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) { |
127 | xrename(dst_name, file_header->name); | 171 | xrename(dst_nameN, dst_name); |
128 | free(dst_name); | 172 | free(dst_nameN); |
129 | } | 173 | } |
130 | #endif | 174 | #endif |
131 | break; | 175 | break; |
132 | } | 176 | } |
133 | case S_IFDIR: | 177 | case S_IFDIR: |
134 | res = mkdir(file_header->name, file_header->mode); | 178 | res = mkdir(dst_name, file_header->mode); |
135 | if ((res == -1) | 179 | if ((res == -1) |
136 | && (errno != EISDIR) /* btw, Linux doesn't return this */ | 180 | && (errno != EISDIR) /* btw, Linux doesn't return this */ |
137 | && (errno != EEXIST) | 181 | && (errno != EEXIST) |
138 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | 182 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
139 | ) { | 183 | ) { |
140 | bb_perror_msg("can't make dir %s", file_header->name); | 184 | bb_perror_msg("can't make dir %s", dst_name); |
141 | } | 185 | } |
142 | break; | 186 | break; |
143 | case S_IFLNK: | 187 | case S_IFLNK: |
144 | /* Symlink */ | 188 | /* Symlink */ |
145 | //TODO: what if file_header->link_target == NULL (say, corrupted tarball?) | 189 | //TODO: what if file_header->link_target == NULL (say, corrupted tarball?) |
146 | res = symlink(file_header->link_target, file_header->name); | 190 | res = symlink(file_header->link_target, dst_name); |
147 | if ((res == -1) | 191 | if (res != 0 |
148 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | 192 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
149 | ) { | 193 | ) { |
150 | bb_perror_msg("can't create %slink " | 194 | bb_perror_msg("can't create %slink " |
151 | "from %s to %s", "sym", | 195 | "from %s to %s", "sym", |
152 | file_header->name, | 196 | dst_name, |
153 | file_header->link_target); | 197 | file_header->link_target); |
154 | } | 198 | } |
155 | break; | 199 | break; |
@@ -157,11 +201,11 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
157 | case S_IFBLK: | 201 | case S_IFBLK: |
158 | case S_IFCHR: | 202 | case S_IFCHR: |
159 | case S_IFIFO: | 203 | case S_IFIFO: |
160 | res = mknod(file_header->name, file_header->mode, file_header->device); | 204 | res = mknod(dst_name, file_header->mode, file_header->device); |
161 | if ((res == -1) | 205 | if ((res == -1) |
162 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) | 206 | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
163 | ) { | 207 | ) { |
164 | bb_perror_msg("can't create node %s", file_header->name); | 208 | bb_perror_msg("can't create node %s", dst_name); |
165 | } | 209 | } |
166 | break; | 210 | break; |
167 | default: | 211 | default: |
@@ -186,20 +230,20 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |||
186 | } | 230 | } |
187 | #endif | 231 | #endif |
188 | /* GNU tar 1.15.1 uses chown, not lchown */ | 232 | /* GNU tar 1.15.1 uses chown, not lchown */ |
189 | chown(file_header->name, uid, gid); | 233 | chown(dst_name, uid, gid); |
190 | } | 234 | } |
191 | /* uclibc has no lchmod, glibc is even stranger - | 235 | /* uclibc has no lchmod, glibc is even stranger - |
192 | * it has lchmod which seems to do nothing! | 236 | * it has lchmod which seems to do nothing! |
193 | * so we use chmod... */ | 237 | * so we use chmod... */ |
194 | if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) { | 238 | if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) { |
195 | chmod(file_header->name, file_header->mode); | 239 | chmod(dst_name, file_header->mode); |
196 | } | 240 | } |
197 | if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { | 241 | if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { |
198 | struct timeval t[2]; | 242 | struct timeval t[2]; |
199 | 243 | ||
200 | t[1].tv_sec = t[0].tv_sec = file_header->mtime; | 244 | t[1].tv_sec = t[0].tv_sec = file_header->mtime; |
201 | t[1].tv_usec = t[0].tv_usec = 0; | 245 | t[1].tv_usec = t[0].tv_usec = 0; |
202 | utimes(file_header->name, t); | 246 | utimes(dst_name, t); |
203 | } | 247 | } |
204 | } | 248 | } |
205 | 249 | ||
diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c index fb68673b9..ac2be726f 100644 --- a/archival/libarchive/get_header_tar.c +++ b/archival/libarchive/get_header_tar.c | |||
@@ -418,6 +418,7 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle) | |||
418 | 418 | ||
419 | /* Everything up to and including last ".." component is stripped */ | 419 | /* Everything up to and including last ".." component is stripped */ |
420 | overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name)); | 420 | overlapping_strcpy(file_header->name, strip_unsafe_prefix(file_header->name)); |
421 | //TODO: do the same for file_header->link_target? | ||
421 | 422 | ||
422 | /* Strip trailing '/' in directories */ | 423 | /* Strip trailing '/' in directories */ |
423 | /* Must be done after mode is set as '/' is used to check if it's a directory */ | 424 | /* Must be done after mode is set as '/' is used to check if it's a directory */ |
diff --git a/archival/tar.c b/archival/tar.c index aa03ba990..566ba34f6 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -152,9 +152,12 @@ | |||
152 | # define FNM_LEADING_DIR 0 | 152 | # define FNM_LEADING_DIR 0 |
153 | #endif | 153 | #endif |
154 | 154 | ||
155 | 155 | #if 0 | |
156 | //#define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) | 156 | # define DBG(fmt, ...) bb_error_msg("%s: " fmt, __func__, ## __VA_ARGS__) |
157 | #define DBG(...) ((void)0) | 157 | #else |
158 | # define DBG(...) ((void)0) | ||
159 | #endif | ||
160 | #define DBG_OPTION_PARSING 0 | ||
158 | 161 | ||
159 | 162 | ||
160 | #define block_buf bb_common_bufsiz1 | 163 | #define block_buf bb_common_bufsiz1 |
@@ -855,6 +858,7 @@ enum { | |||
855 | IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) | 858 | IF_FEATURE_SEAMLESS_Z( OPTBIT_COMPRESS ,) |
856 | IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) | 859 | IF_FEATURE_TAR_NOPRESERVE_TIME(OPTBIT_NOPRESERVE_TIME,) |
857 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | 860 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS |
861 | OPTBIT_STRIP_COMPONENTS, | ||
858 | OPTBIT_NORECURSION, | 862 | OPTBIT_NORECURSION, |
859 | IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) | 863 | IF_FEATURE_TAR_TO_COMMAND(OPTBIT_2COMMAND ,) |
860 | OPTBIT_NUMERIC_OWNER, | 864 | OPTBIT_NUMERIC_OWNER, |
@@ -879,12 +883,13 @@ enum { | |||
879 | OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z | 883 | OPT_GZIP = IF_FEATURE_SEAMLESS_GZ( (1 << OPTBIT_GZIP )) + 0, // z |
880 | OPT_XZ = IF_FEATURE_SEAMLESS_XZ( (1 << OPTBIT_XZ )) + 0, // J | 884 | OPT_XZ = IF_FEATURE_SEAMLESS_XZ( (1 << OPTBIT_XZ )) + 0, // J |
881 | OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z | 885 | OPT_COMPRESS = IF_FEATURE_SEAMLESS_Z( (1 << OPTBIT_COMPRESS )) + 0, // Z |
882 | OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m | 886 | OPT_NOPRESERVE_TIME = IF_FEATURE_TAR_NOPRESERVE_TIME((1 << OPTBIT_NOPRESERVE_TIME)) + 0, // m |
883 | OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion | 887 | OPT_STRIP_COMPONENTS = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_STRIP_COMPONENTS)) + 0, // strip-components |
884 | OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command | 888 | OPT_NORECURSION = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NORECURSION )) + 0, // no-recursion |
885 | OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner | 889 | OPT_2COMMAND = IF_FEATURE_TAR_TO_COMMAND( (1 << OPTBIT_2COMMAND )) + 0, // to-command |
886 | OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions | 890 | OPT_NUMERIC_OWNER = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NUMERIC_OWNER )) + 0, // numeric-owner |
887 | OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite | 891 | OPT_NOPRESERVE_PERM = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_NOPRESERVE_PERM)) + 0, // no-same-permissions |
892 | OPT_OVERWRITE = IF_FEATURE_TAR_LONG_OPTIONS((1 << OPTBIT_OVERWRITE )) + 0, // overwrite | ||
888 | 893 | ||
889 | OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS), | 894 | OPT_ANY_COMPRESS = (OPT_BZIP2 | OPT_LZMA | OPT_GZIP | OPT_XZ | OPT_COMPRESS), |
890 | }; | 895 | }; |
@@ -928,6 +933,7 @@ static const char tar_longopts[] ALIGN1 = | |||
928 | # if ENABLE_FEATURE_TAR_NOPRESERVE_TIME | 933 | # if ENABLE_FEATURE_TAR_NOPRESERVE_TIME |
929 | "touch\0" No_argument "m" | 934 | "touch\0" No_argument "m" |
930 | # endif | 935 | # endif |
936 | "strip-components\0" Required_argument "\xf9" | ||
931 | "no-recursion\0" No_argument "\xfa" | 937 | "no-recursion\0" No_argument "\xfa" |
932 | # if ENABLE_FEATURE_TAR_TO_COMMAND | 938 | # if ENABLE_FEATURE_TAR_TO_COMMAND |
933 | "to-command\0" Required_argument "\xfb" | 939 | "to-command\0" Required_argument "\xfb" |
@@ -973,11 +979,15 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |||
973 | "tt:vv:" // count -t,-v | 979 | "tt:vv:" // count -t,-v |
974 | IF_FEATURE_TAR_FROM("X::T::") // cumulative lists | 980 | IF_FEATURE_TAR_FROM("X::T::") // cumulative lists |
975 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM | 981 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM |
976 | "\xff::" // cumulative lists for --exclude | 982 | "\xff::" // --exclude=PATTERN is a list |
977 | #endif | 983 | #endif |
978 | IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd | 984 | IF_FEATURE_TAR_CREATE("c:") "t:x:" // at least one of these is reqd |
979 | IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive | 985 | IF_FEATURE_TAR_CREATE("c--tx:t--cx:x--ct") // mutually exclusive |
980 | IF_NOT_FEATURE_TAR_CREATE("t--x:x--t"); // mutually exclusive | 986 | IF_NOT_FEATURE_TAR_CREATE("t--x:x--t") // mutually exclusive |
987 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | ||
988 | ":\xf9+" // --strip-components=NUM | ||
989 | #endif | ||
990 | ; | ||
981 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | 991 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS |
982 | applet_long_options = tar_longopts; | 992 | applet_long_options = tar_longopts; |
983 | #endif | 993 | #endif |
@@ -1018,10 +1028,14 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |||
1018 | IF_FEATURE_SEAMLESS_XZ( "J" ) | 1028 | IF_FEATURE_SEAMLESS_XZ( "J" ) |
1019 | IF_FEATURE_SEAMLESS_Z( "Z" ) | 1029 | IF_FEATURE_SEAMLESS_Z( "Z" ) |
1020 | IF_FEATURE_TAR_NOPRESERVE_TIME("m") | 1030 | IF_FEATURE_TAR_NOPRESERVE_TIME("m") |
1031 | IF_FEATURE_TAR_LONG_OPTIONS("\xf9:") // --strip-components | ||
1021 | , &base_dir // -C dir | 1032 | , &base_dir // -C dir |
1022 | , &tar_filename // -f filename | 1033 | , &tar_filename // -f filename |
1023 | IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T | 1034 | IF_FEATURE_TAR_FROM(, &(tar_handle->accept)) // T |
1024 | IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X | 1035 | IF_FEATURE_TAR_FROM(, &(tar_handle->reject)) // X |
1036 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | ||
1037 | , &tar_handle->tar__strip_components // --strip-components | ||
1038 | #endif | ||
1025 | IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command | 1039 | IF_FEATURE_TAR_TO_COMMAND(, &(tar_handle->tar__to_command)) // --to-command |
1026 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM | 1040 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS && ENABLE_FEATURE_TAR_FROM |
1027 | , &excludes // --exclude | 1041 | , &excludes // --exclude |
@@ -1029,11 +1043,49 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |||
1029 | , &verboseFlag // combined count for -t and -v | 1043 | , &verboseFlag // combined count for -t and -v |
1030 | , &verboseFlag // combined count for -t and -v | 1044 | , &verboseFlag // combined count for -t and -v |
1031 | ); | 1045 | ); |
1032 | //bb_error_msg("opt:%08x", opt); | 1046 | #if DBG_OPTION_PARSING |
1047 | bb_error_msg("opt: 0x%08x", opt); | ||
1048 | # define showopt(o) bb_error_msg("opt & %s(%x): %x", #o, o, opt & o); | ||
1049 | showopt(OPT_TEST ); | ||
1050 | showopt(OPT_EXTRACT ); | ||
1051 | showopt(OPT_BASEDIR ); | ||
1052 | showopt(OPT_TARNAME ); | ||
1053 | showopt(OPT_2STDOUT ); | ||
1054 | showopt(OPT_NOPRESERVE_OWNER); | ||
1055 | showopt(OPT_P ); | ||
1056 | showopt(OPT_VERBOSE ); | ||
1057 | showopt(OPT_KEEP_OLD ); | ||
1058 | showopt(OPT_CREATE ); | ||
1059 | showopt(OPT_DEREFERENCE ); | ||
1060 | showopt(OPT_BZIP2 ); | ||
1061 | showopt(OPT_LZMA ); | ||
1062 | showopt(OPT_INCLUDE_FROM ); | ||
1063 | showopt(OPT_EXCLUDE_FROM ); | ||
1064 | showopt(OPT_GZIP ); | ||
1065 | showopt(OPT_XZ ); | ||
1066 | showopt(OPT_COMPRESS ); | ||
1067 | showopt(OPT_NOPRESERVE_TIME ); | ||
1068 | showopt(OPT_STRIP_COMPONENTS); | ||
1069 | showopt(OPT_NORECURSION ); | ||
1070 | showopt(OPT_2COMMAND ); | ||
1071 | showopt(OPT_NUMERIC_OWNER ); | ||
1072 | showopt(OPT_NOPRESERVE_PERM ); | ||
1073 | showopt(OPT_OVERWRITE ); | ||
1074 | showopt(OPT_ANY_COMPRESS ); | ||
1075 | bb_error_msg("base_dir:'%s'", base_dir); | ||
1076 | bb_error_msg("tar_filename:'%s'", tar_filename); | ||
1077 | bb_error_msg("verboseFlag:%d", verboseFlag); | ||
1078 | bb_error_msg("tar_handle->tar__to_command:'%s'", tar_handle->tar__to_command); | ||
1079 | bb_error_msg("tar_handle->tar__strip_components:%u", tar_handle->tar__strip_components); | ||
1080 | return 0; | ||
1081 | # undef showopt | ||
1082 | #endif | ||
1033 | argv += optind; | 1083 | argv += optind; |
1034 | 1084 | ||
1035 | if (verboseFlag) tar_handle->action_header = header_verbose_list; | 1085 | if (verboseFlag) |
1036 | if (verboseFlag == 1) tar_handle->action_header = header_list; | 1086 | tar_handle->action_header = header_verbose_list; |
1087 | if (verboseFlag == 1) | ||
1088 | tar_handle->action_header = header_list; | ||
1037 | 1089 | ||
1038 | if (opt & OPT_EXTRACT) | 1090 | if (opt & OPT_EXTRACT) |
1039 | tar_handle->action_data = data_extract_all; | 1091 | tar_handle->action_data = data_extract_all; |
diff --git a/include/bb_archive.h b/include/bb_archive.h index 2329d025d..10969b567 100644 --- a/include/bb_archive.h +++ b/include/bb_archive.h | |||
@@ -77,6 +77,9 @@ typedef struct archive_handle_t { | |||
77 | off_t offset; | 77 | off_t offset; |
78 | 78 | ||
79 | /* Archiver specific. Can make it a union if it ever gets big */ | 79 | /* Archiver specific. Can make it a union if it ever gets big */ |
80 | #if ENABLE_FEATURE_TAR_LONG_OPTIONS | ||
81 | unsigned tar__strip_components; | ||
82 | #endif | ||
80 | #define PAX_NEXT_FILE 0 | 83 | #define PAX_NEXT_FILE 0 |
81 | #define PAX_GLOBAL 1 | 84 | #define PAX_GLOBAL 1 |
82 | #if ENABLE_TAR || ENABLE_DPKG || ENABLE_DPKG_DEB | 85 | #if ENABLE_TAR || ENABLE_DPKG || ENABLE_DPKG_DEB |
diff --git a/testsuite/tar.tests b/testsuite/tar.tests index 4929f4e49..383a4646c 100755 --- a/testsuite/tar.tests +++ b/testsuite/tar.tests | |||
@@ -53,6 +53,15 @@ dd if=/dev/zero bs=512 count=20 2>/dev/null | tar xvf - 2>&1; echo $? | |||
53 | "" "" | 53 | "" "" |
54 | SKIP= | 54 | SKIP= |
55 | 55 | ||
56 | # "tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input": | ||
57 | # GNU tar 1.26 records as hardlinks: | ||
58 | # input_hard2 -> input_hard1 | ||
59 | # input_hard1 -> input_hard1 (!!!) | ||
60 | # input_dir/file -> input_dir/file | ||
61 | # input -> input | ||
62 | # As of 1.24.0, we don't record last two: for them, nlink==1 | ||
63 | # and we check for "hardlink"ness only files with nlink!=1 | ||
64 | # We also don't use "hrw-r--r--" notation for hardlinks in "tar tv" listing. | ||
56 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES | 65 | optional FEATURE_TAR_CREATE FEATURE_LS_SORTFILES |
57 | testing "tar hardlinks and repeated files" '\ | 66 | testing "tar hardlinks and repeated files" '\ |
58 | rm -rf input_* test.tar 2>/dev/null | 67 | rm -rf input_* test.tar 2>/dev/null |
@@ -64,6 +73,7 @@ chmod -R 644 * | |||
64 | chmod 755 input_dir | 73 | chmod 755 input_dir |
65 | tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input | 74 | tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input |
66 | tar tvf test.tar | sed "s/.*[0-9] input/input/" | 75 | tar tvf test.tar | sed "s/.*[0-9] input/input/" |
76 | rm -rf input_dir | ||
67 | tar xf test.tar 2>&1 | 77 | tar xf test.tar 2>&1 |
68 | echo Ok: $? | 78 | echo Ok: $? |
69 | ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" | 79 | ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/" |