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 /archival/libarchive | |
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>
Diffstat (limited to 'archival/libarchive')
-rw-r--r-- | archival/libarchive/data_extract_all.c | 108 | ||||
-rw-r--r-- | archival/libarchive/get_header_tar.c | 1 |
2 files changed, 77 insertions, 32 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 */ |