diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2006-11-25 23:56:50 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2006-11-25 23:56:50 +0000 |
commit | f709ebefbdf62ba30226a538b19aa4811a8c54b8 (patch) | |
tree | 7b6bbba9c73e1e8a2c10d9cafde28ce49125f914 | |
parent | a0f78c266b49e283f862a642ede1cc1ed439162a (diff) | |
download | busybox-w32-f709ebefbdf62ba30226a538b19aa4811a8c54b8.tar.gz busybox-w32-f709ebefbdf62ba30226a538b19aa4811a8c54b8.tar.bz2 busybox-w32-f709ebefbdf62ba30226a538b19aa4811a8c54b8.zip |
tar: add support for FEATURE_TAR_GNU_EXTENSIONS so than we can save
long names now. We were able to read such tars, but not create.
+275 bytes. Without FEATURE_TAR_GNU_EXTENSIONS: -25 bytes.
We still cannot unpack Linux kernels, but not for long ;)
git-svn-id: svn://busybox.net/trunk/busybox@16670 69ca8d6d-28ef-0310-b511-8ec308f3f277
-rw-r--r-- | archival/tar.c | 123 |
1 files changed, 98 insertions, 25 deletions
diff --git a/archival/tar.c b/archival/tar.c index c870d53c2..bf6786f0a 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -169,6 +169,72 @@ static void putOctal(char *cp, int len, off_t value) | |||
169 | /* Copy the string to the field */ | 169 | /* Copy the string to the field */ |
170 | memcpy(cp, tempString, len); | 170 | memcpy(cp, tempString, len); |
171 | } | 171 | } |
172 | #define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b)) | ||
173 | |||
174 | static void chksum_and_xwrite(int fd, struct TarHeader* hp) | ||
175 | { | ||
176 | const unsigned char *cp; | ||
177 | int chksum, size; | ||
178 | |||
179 | strcpy(hp->magic, "ustar "); | ||
180 | |||
181 | /* Calculate and store the checksum (i.e., the sum of all of the bytes of | ||
182 | * the header). The checksum field must be filled with blanks for the | ||
183 | * calculation. The checksum field is formatted differently from the | ||
184 | * other fields: it has 6 digits, a null, then a space -- rather than | ||
185 | * digits, followed by a null like the other fields... */ | ||
186 | memset(hp->chksum, ' ', sizeof(hp->chksum)); | ||
187 | cp = (const unsigned char *) hp; | ||
188 | chksum = 0; | ||
189 | size = sizeof(*hp); | ||
190 | do { chksum += *cp++; } while (--size); | ||
191 | putOctal(hp->chksum, sizeof(hp->chksum)-1, chksum); | ||
192 | |||
193 | /* Now write the header out to disk */ | ||
194 | xwrite(fd, hp, sizeof(*hp)); | ||
195 | } | ||
196 | |||
197 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
198 | static void writeLongname(int fd, int type, const char *name, int dir) | ||
199 | { | ||
200 | static const struct { | ||
201 | char mode[8]; /* 100-107 */ | ||
202 | char uid[8]; /* 108-115 */ | ||
203 | char gid[8]; /* 116-123 */ | ||
204 | char size[12]; /* 124-135 */ | ||
205 | char mtime[12]; /* 136-147 */ | ||
206 | } prefilled = { | ||
207 | "0000000", | ||
208 | "0000000", | ||
209 | "0000000", | ||
210 | "00000000000", | ||
211 | "00000000000", | ||
212 | }; | ||
213 | struct TarHeader header; | ||
214 | int size; | ||
215 | |||
216 | dir = !!dir; /* normalize: 0/1 */ | ||
217 | size = strlen(name) + 1 + dir; /* GNU tar uses strlen+1 */ | ||
218 | /* + dir: account for possible '/' */ | ||
219 | |||
220 | bzero(&header, sizeof(header)); | ||
221 | strcpy(header.name, "././@LongLink"); | ||
222 | memcpy(header.mode, prefilled.mode, sizeof(prefilled)); | ||
223 | PUT_OCTAL(header.size, size); | ||
224 | header.typeflag = type; | ||
225 | chksum_and_xwrite(fd, &header); | ||
226 | |||
227 | /* Write filename[/] and pad the block. */ | ||
228 | /* dir=0: writes 'name<NUL>', pads */ | ||
229 | /* dir=1: writes 'name', writes '/<NUL>', pads */ | ||
230 | dir *= 2; | ||
231 | xwrite(fd, name, size - dir); | ||
232 | xwrite(fd, "/", dir); | ||
233 | size = (-size) & (TAR_BLOCK_SIZE-1); | ||
234 | bzero(&header, size); | ||
235 | xwrite(fd, &header, size); | ||
236 | } | ||
237 | #endif | ||
172 | 238 | ||
173 | /* Write out a tar header for the specified file/directory/whatever */ | 239 | /* Write out a tar header for the specified file/directory/whatever */ |
174 | void BUG_tar_header_size(void); | 240 | void BUG_tar_header_size(void); |
@@ -176,25 +242,20 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, | |||
176 | const char *header_name, const char *fileName, struct stat *statbuf) | 242 | const char *header_name, const char *fileName, struct stat *statbuf) |
177 | { | 243 | { |
178 | struct TarHeader header; | 244 | struct TarHeader header; |
179 | const unsigned char *cp; | ||
180 | int chksum; | ||
181 | int size; | ||
182 | 245 | ||
183 | if (sizeof(header) != 512) | 246 | if (sizeof(header) != 512) |
184 | BUG_tar_header_size(); | 247 | BUG_tar_header_size(); |
185 | 248 | ||
186 | bzero(&header, sizeof(struct TarHeader)); | 249 | bzero(&header, sizeof(struct TarHeader)); |
187 | 250 | ||
188 | safe_strncpy(header.name, header_name, sizeof(header.name)); | 251 | strncpy(header.name, header_name, sizeof(header.name)); |
189 | 252 | ||
190 | #define PUT_OCTAL(a, b) putOctal((a), sizeof(a), (b)) | ||
191 | /* POSIX says to mask mode with 07777. */ | 253 | /* POSIX says to mask mode with 07777. */ |
192 | PUT_OCTAL(header.mode, statbuf->st_mode & 07777); | 254 | PUT_OCTAL(header.mode, statbuf->st_mode & 07777); |
193 | PUT_OCTAL(header.uid, statbuf->st_uid); | 255 | PUT_OCTAL(header.uid, statbuf->st_uid); |
194 | PUT_OCTAL(header.gid, statbuf->st_gid); | 256 | PUT_OCTAL(header.gid, statbuf->st_gid); |
195 | memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ | 257 | memset(header.size, '0', sizeof(header.size)-1); /* Regular file size is handled later */ |
196 | PUT_OCTAL(header.mtime, statbuf->st_mtime); | 258 | PUT_OCTAL(header.mtime, statbuf->st_mtime); |
197 | strcpy(header.magic, "ustar "); | ||
198 | 259 | ||
199 | /* Enter the user and group names */ | 260 | /* Enter the user and group names */ |
200 | safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); | 261 | safe_strncpy(header.uname, get_cached_username(statbuf->st_uid), sizeof(header.uname)); |
@@ -205,24 +266,36 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, | |||
205 | header.typeflag = LNKTYPE; | 266 | header.typeflag = LNKTYPE; |
206 | strncpy(header.linkname, tbInfo->hlInfo->name, | 267 | strncpy(header.linkname, tbInfo->hlInfo->name, |
207 | sizeof(header.linkname)); | 268 | sizeof(header.linkname)); |
269 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
270 | /* Write out long linkname if needed */ | ||
271 | if (header.linkname[sizeof(header.linkname)-1]) | ||
272 | writeLongname(tbInfo->tarFd, GNULONGLINK, | ||
273 | tbInfo->hlInfo->name, 0); | ||
274 | #endif | ||
208 | } else if (S_ISLNK(statbuf->st_mode)) { | 275 | } else if (S_ISLNK(statbuf->st_mode)) { |
209 | char *lpath = xreadlink(fileName); | 276 | char *lpath = xreadlink(fileName); |
210 | if (!lpath) /* Already printed err msg inside xreadlink() */ | 277 | if (!lpath) /* Already printed err msg inside xreadlink() */ |
211 | return FALSE; | 278 | return FALSE; |
212 | header.typeflag = SYMTYPE; | 279 | header.typeflag = SYMTYPE; |
213 | strncpy(header.linkname, lpath, sizeof(header.linkname)); | 280 | strncpy(header.linkname, lpath, sizeof(header.linkname)); |
281 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
282 | /* Write out long linkname if needed */ | ||
283 | if (header.linkname[sizeof(header.linkname)-1]) | ||
284 | writeLongname(tbInfo->tarFd, GNULONGLINK, lpath, 0); | ||
285 | #else | ||
214 | /* If it is larger than 100 bytes, bail out */ | 286 | /* If it is larger than 100 bytes, bail out */ |
215 | if (header.linkname[sizeof(header.linkname)-1] /* at least 100? */ | 287 | if (header.linkname[sizeof(header.linkname)-1]) { |
216 | && lpath[sizeof(header.linkname)] /* and 101th is also not zero */ | ||
217 | ) { | ||
218 | free(lpath); | 288 | free(lpath); |
219 | bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); | 289 | bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); |
220 | return FALSE; | 290 | return FALSE; |
221 | } | 291 | } |
292 | #endif | ||
222 | free(lpath); | 293 | free(lpath); |
223 | } else if (S_ISDIR(statbuf->st_mode)) { | 294 | } else if (S_ISDIR(statbuf->st_mode)) { |
224 | header.typeflag = DIRTYPE; | 295 | header.typeflag = DIRTYPE; |
225 | strncat(header.name, "/", sizeof(header.name)); | 296 | /* Append '/' only if there is a space for it */ |
297 | if (!header.name[sizeof(header.name)-1]) | ||
298 | header.name[strlen(header.name)] = '/'; | ||
226 | } else if (S_ISCHR(statbuf->st_mode)) { | 299 | } else if (S_ISCHR(statbuf->st_mode)) { |
227 | header.typeflag = CHRTYPE; | 300 | header.typeflag = CHRTYPE; |
228 | PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); | 301 | PUT_OCTAL(header.devmajor, major(statbuf->st_rdev)); |
@@ -247,22 +320,17 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, | |||
247 | bb_error_msg("%s: unknown file type", fileName); | 320 | bb_error_msg("%s: unknown file type", fileName); |
248 | return FALSE; | 321 | return FALSE; |
249 | } | 322 | } |
250 | #undef PUT_OCTAL | ||
251 | 323 | ||
252 | /* Calculate and store the checksum (i.e., the sum of all of the bytes of | 324 | #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS |
253 | * the header). The checksum field must be filled with blanks for the | 325 | /* Write out long name if needed */ |
254 | * calculation. The checksum field is formatted differently from the | 326 | /* (we, like GNU tar, output long linkname *before* long name) */ |
255 | * other fields: it has [6] digits, a null, then a space -- rather than | 327 | if (header.name[sizeof(header.name)-1]) |
256 | * digits, followed by a null like the other fields... */ | 328 | writeLongname(tbInfo->tarFd, GNULONGNAME, |
257 | memset(header.chksum, ' ', sizeof(header.chksum)); | 329 | header_name, S_ISDIR(statbuf->st_mode)); |
258 | cp = (const unsigned char *) &header; | 330 | #endif |
259 | chksum = 0; | ||
260 | size = sizeof(struct TarHeader); | ||
261 | do { chksum += *cp++; } while (--size); | ||
262 | putOctal(header.chksum, sizeof(header.chksum)-1, chksum); | ||
263 | 331 | ||
264 | /* Now write the header out to disk */ | 332 | /* Now write the header out to disk */ |
265 | xwrite(tbInfo->tarFd, &header, sizeof(struct TarHeader)); | 333 | chksum_and_xwrite(tbInfo->tarFd, &header); |
266 | 334 | ||
267 | /* Now do the verbose thing (or not) */ | 335 | /* Now do the verbose thing (or not) */ |
268 | if (tbInfo->verboseFlag) { | 336 | if (tbInfo->verboseFlag) { |
@@ -272,7 +340,9 @@ static int writeTarHeader(struct TarBallInfo *tbInfo, | |||
272 | vbFd = stderr; | 340 | vbFd = stderr; |
273 | /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ | 341 | /* GNU "tar cvvf" prints "extended" listing a-la "ls -l" */ |
274 | /* We don't have such excesses here: for us "v" == "vv" */ | 342 | /* We don't have such excesses here: for us "v" == "vv" */ |
275 | fprintf(vbFd, "%s\n", header.name); | 343 | /* '/' is probably a GNUism */ |
344 | fprintf(vbFd, "%s%s\n", header_name, | ||
345 | S_ISDIR(statbuf->st_mode) ? "/" : ""); | ||
276 | } | 346 | } |
277 | 347 | ||
278 | return TRUE; | 348 | return TRUE; |
@@ -352,10 +422,12 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, | |||
352 | header_name++; | 422 | header_name++; |
353 | } | 423 | } |
354 | 424 | ||
425 | #if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS | ||
355 | if (strlen(fileName) >= NAME_SIZE) { | 426 | if (strlen(fileName) >= NAME_SIZE) { |
356 | bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); | 427 | bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); |
357 | return TRUE; | 428 | return TRUE; |
358 | } | 429 | } |
430 | #endif | ||
359 | 431 | ||
360 | if (header_name[0] == '\0') | 432 | if (header_name[0] == '\0') |
361 | return TRUE; | 433 | return TRUE; |
@@ -384,7 +456,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, | |||
384 | 456 | ||
385 | /* write the file to the archive */ | 457 | /* write the file to the archive */ |
386 | readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); | 458 | readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); |
387 | if (readSize != statbuf->st_size) { | 459 | /* readSize < 0 means that error was already reported */ |
460 | if (readSize != statbuf->st_size && readSize >= 0) { | ||
388 | /* Deadly. We record size into header first, */ | 461 | /* Deadly. We record size into header first, */ |
389 | /* and then write out file. If file shrinks in between, */ | 462 | /* and then write out file. If file shrinks in between, */ |
390 | /* tar will be corrupted. So bail out. */ | 463 | /* tar will be corrupted. So bail out. */ |