diff options
author | Erik Andersen <andersen@codepoet.org> | 2000-03-24 00:54:46 +0000 |
---|---|---|
committer | Erik Andersen <andersen@codepoet.org> | 2000-03-24 00:54:46 +0000 |
commit | 1ad302ac903695ef4ba748d3880222c05eeaafef (patch) | |
tree | a03bebb080253f4a20f8d5335285a5e717106d36 /archival | |
parent | e454fb68a3165ca5cf42c290b4b9dc92dbf9a235 (diff) | |
download | busybox-w32-1ad302ac903695ef4ba748d3880222c05eeaafef.tar.gz busybox-w32-1ad302ac903695ef4ba748d3880222c05eeaafef.tar.bz2 busybox-w32-1ad302ac903695ef4ba748d3880222c05eeaafef.zip |
The new tar for busybox is now done, and works just fine
for extracting files. Creation of tarballs is next...
-Erik
Diffstat (limited to 'archival')
-rw-r--r-- | archival/tar.c | 1163 |
1 files changed, 271 insertions, 892 deletions
diff --git a/archival/tar.c b/archival/tar.c index 99f166c3e..f7b789f7c 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -1,18 +1,9 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | 1 | /* vi: set sw=4 ts=4: */ |
2 | /* | 2 | /* |
3 | * Mini tar implementation for busybox based on code taken from sash. | 3 | * Mini tar implementation for busybox |
4 | * | 4 | * |
5 | * Copyright (c) 1999 by David I. Bell | 5 | * Copyright (C) 1999 by Lineo, inc. |
6 | * Permission is granted to use, distribute, or modify this source, | 6 | * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> |
7 | * provided that this copyright notice remains intact. | ||
8 | * | ||
9 | * Permission to distribute this code under the GPL has been granted. | ||
10 | * | ||
11 | * Modified for busybox by Erik Andersen <andersee@debian.org> | ||
12 | * Adjusted to grok stdin/stdout options. | ||
13 | * | ||
14 | * Modified to handle device special files by Matt Porter | ||
15 | * <porter@debian.org> | ||
16 | * | 7 | * |
17 | * This program is free software; you can redistribute it and/or modify | 8 | * This program is free software; you can redistribute it and/or modify |
18 | * it under the terms of the GNU General Public License as published by | 9 | * it under the terms of the GNU General Public License as published by |
@@ -32,6 +23,9 @@ | |||
32 | 23 | ||
33 | 24 | ||
34 | #include "internal.h" | 25 | #include "internal.h" |
26 | #define BB_DECLARE_EXTERN | ||
27 | #define bb_need_io_error | ||
28 | #include "messages.c" | ||
35 | #include <stdio.h> | 29 | #include <stdio.h> |
36 | #include <dirent.h> | 30 | #include <dirent.h> |
37 | #include <errno.h> | 31 | #include <errno.h> |
@@ -69,6 +63,10 @@ static const char tar_usage[] = | |||
69 | 63 | ||
70 | 64 | ||
71 | /* Tar file constants */ | 65 | /* Tar file constants */ |
66 | #ifndef MAJOR | ||
67 | #define MAJOR(dev) (((dev)>>8)&0xff) | ||
68 | #define MINOR(dev) ((dev)&0xff) | ||
69 | #endif | ||
72 | 70 | ||
73 | 71 | ||
74 | /* POSIX tar Header Block, from POSIX 1003.1-1990 */ | 72 | /* POSIX tar Header Block, from POSIX 1003.1-1990 */ |
@@ -133,7 +131,8 @@ struct TarInfo | |||
133 | time_t mtime; /* Last-modified time */ | 131 | time_t mtime; /* Last-modified time */ |
134 | enum TarFileType type; /* Regular, directory, link, etc */ | 132 | enum TarFileType type; /* Regular, directory, link, etc */ |
135 | char * linkname; /* Name for symbolic and hard links */ | 133 | char * linkname; /* Name for symbolic and hard links */ |
136 | dev_t device; /* Special device for mknod() */ | 134 | long devmajor; /* Major number for special device */ |
135 | long devminor; /* Minor number for special device */ | ||
137 | }; | 136 | }; |
138 | typedef struct TarInfo TarInfo; | 137 | typedef struct TarInfo TarInfo; |
139 | 138 | ||
@@ -144,8 +143,8 @@ static const unsigned long TarChecksumOffset = (const unsigned long)&(((TarHeade | |||
144 | /* Local procedures to restore files from a tar file. */ | 143 | /* Local procedures to restore files from a tar file. */ |
145 | static int readTarFile(const char* tarName, int extractFlag, int listFlag, | 144 | static int readTarFile(const char* tarName, int extractFlag, int listFlag, |
146 | int tostdoutFlag, int verboseFlag); | 145 | int tostdoutFlag, int verboseFlag); |
147 | static long getOctal(const char *cp, int len); | 146 | |
148 | static int parseTarHeader(struct TarHeader *rawHeader, struct TarInfo *header); | 147 | |
149 | 148 | ||
150 | #ifdef BB_FEATURE_TAR_CREATE | 149 | #ifdef BB_FEATURE_TAR_CREATE |
151 | /* | 150 | /* |
@@ -254,35 +253,216 @@ extern int tar_main(int argc, char **argv) | |||
254 | } | 253 | } |
255 | 254 | ||
256 | static void | 255 | static void |
257 | tarExtractRegularFile(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) | 256 | tarExtractRegularFile(TarInfo *header, int extractFlag, int tostdoutFlag) |
257 | { | ||
258 | size_t writeSize; | ||
259 | size_t readSize; | ||
260 | size_t actualWriteSz; | ||
261 | char buffer[BUFSIZ]; | ||
262 | size_t size = header->size; | ||
263 | int outFd=fileno(stdout); | ||
264 | |||
265 | /* Open the file to be written, if a file is supposed to be written */ | ||
266 | if (extractFlag==TRUE && tostdoutFlag==FALSE) { | ||
267 | if ((outFd=open(header->name, O_CREAT|O_TRUNC|O_WRONLY, header->mode & ~S_IFMT)) < 0) | ||
268 | errorMsg(io_error, header->name, strerror(errno)); | ||
269 | /* Create the path to the file, just in case it isn't there... | ||
270 | * This should not screw up path permissions or anything. */ | ||
271 | createPath(header->name, 0777); | ||
272 | } | ||
273 | |||
274 | /* Write out the file, if we are supposed to be doing that */ | ||
275 | while ( size > 0 ) { | ||
276 | actualWriteSz=0; | ||
277 | if ( size > sizeof(buffer) ) | ||
278 | writeSize = readSize = sizeof(buffer); | ||
279 | else { | ||
280 | int mod = size % 512; | ||
281 | if ( mod != 0 ) | ||
282 | readSize = size + (512 - mod); | ||
283 | else | ||
284 | readSize = size; | ||
285 | writeSize = size; | ||
286 | } | ||
287 | if ( (readSize = fullRead(header->tarFd, buffer, readSize)) <= 0 ) { | ||
288 | /* Tarball seems to have a problem */ | ||
289 | errorMsg("Error reading tarfile: %s", strerror(errno)); | ||
290 | return; | ||
291 | } | ||
292 | if ( readSize < writeSize ) | ||
293 | writeSize = readSize; | ||
294 | |||
295 | /* Write out the file, if we are supposed to be doing that */ | ||
296 | if (extractFlag==TRUE) { | ||
297 | |||
298 | if ((actualWriteSz=fullWrite(outFd, buffer, writeSize)) != writeSize ) { | ||
299 | /* Output file seems to have a problem */ | ||
300 | errorMsg(io_error, header->name, strerror(errno)); | ||
301 | return; | ||
302 | } | ||
303 | } | ||
304 | |||
305 | size -= actualWriteSz; | ||
306 | } | ||
307 | |||
308 | /* Now we are done writing the file out, so try | ||
309 | * and fix up the permissions and whatnot */ | ||
310 | if (extractFlag==TRUE && tostdoutFlag==FALSE) { | ||
311 | struct utimbuf t; | ||
312 | /* Now set permissions etc for the new file */ | ||
313 | fchown(outFd, header->uid, header->gid); | ||
314 | fchmod(outFd, header->mode & ~S_IFMT); | ||
315 | close(outFd); | ||
316 | /* File must be closed before trying to change the date */ | ||
317 | t.actime = time(0); | ||
318 | t.modtime = header->mtime; | ||
319 | utime(header->name, &t); | ||
320 | } | ||
321 | } | ||
322 | |||
323 | static void | ||
324 | fixUpPermissions(TarInfo *header) | ||
258 | { | 325 | { |
259 | return; | 326 | struct utimbuf t; |
327 | /* Now set permissions etc for the new file */ | ||
328 | chown(header->name, header->uid, header->gid); | ||
329 | chmod(header->name, header->mode); | ||
330 | /* Reset the time */ | ||
331 | t.actime = time(0); | ||
332 | t.modtime = header->mtime; | ||
333 | utime(header->name, &t); | ||
260 | } | 334 | } |
261 | 335 | ||
262 | static void | 336 | static void |
263 | tarExtractDirectory(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) | 337 | tarExtractDirectory(TarInfo *header, int extractFlag, int tostdoutFlag) |
264 | { | 338 | { |
265 | return; | 339 | |
340 | if (extractFlag==FALSE || tostdoutFlag==TRUE) | ||
341 | return; | ||
342 | |||
343 | if (createPath(header->name, header->mode) != TRUE) { | ||
344 | errorMsg("Error creating directory '%s': %s", header->name, strerror(errno)); | ||
345 | return; | ||
346 | } | ||
347 | /* make the final component, just in case it was | ||
348 | * omitted by createPath() (which will skip the | ||
349 | * directory if it doesn't have a terminating '/') */ | ||
350 | mkdir(header->name, header->mode); | ||
351 | |||
352 | /* Now set permissions etc for the new directory */ | ||
353 | fixUpPermissions(header); | ||
266 | } | 354 | } |
267 | 355 | ||
268 | static void | 356 | static void |
269 | tarExtractHardLink(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) | 357 | tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag) |
270 | { | 358 | { |
271 | return; | 359 | if (extractFlag==FALSE || tostdoutFlag==TRUE) |
360 | return; | ||
361 | |||
362 | if (link(header->linkname, header->name) < 0) { | ||
363 | errorMsg("Error creating hard link '%s': %s", header->linkname, strerror(errno)); | ||
364 | return; | ||
365 | } | ||
366 | |||
367 | /* Now set permissions etc for the new directory */ | ||
368 | fixUpPermissions(header); | ||
272 | } | 369 | } |
273 | 370 | ||
274 | static void | 371 | static void |
275 | tarExtractSymLink(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) | 372 | tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag) |
276 | { | 373 | { |
277 | return; | 374 | if (extractFlag==FALSE || tostdoutFlag==TRUE) |
375 | return; | ||
376 | |||
377 | #ifdef S_ISLNK | ||
378 | if (symlink(header->linkname, header->name) < 0) { | ||
379 | errorMsg("Error creating symlink '%s': %s", header->linkname, strerror(errno)); | ||
380 | return; | ||
381 | } | ||
382 | /* Try to change ownership of the symlink. | ||
383 | * If libs doesn't support that, don't bother. | ||
384 | * Changing the pointed-to-file is the Wrong Thing(tm). | ||
385 | */ | ||
386 | #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) | ||
387 | lchown(header->name, header->uid, header->gid); | ||
388 | #endif | ||
389 | |||
390 | /* Do not change permissions or date on symlink, | ||
391 | * since it changes the pointed to file instead. duh. */ | ||
392 | #else | ||
393 | fprintf(stderr, "Cannot create symbolic links\n"); | ||
394 | #endif | ||
278 | } | 395 | } |
279 | 396 | ||
280 | static void | 397 | static void |
281 | tarExtractSpecial(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) | 398 | tarExtractSpecial(TarInfo *header, int extractFlag, int tostdoutFlag) |
282 | { | 399 | { |
283 | return; | 400 | if (extractFlag==FALSE || tostdoutFlag==TRUE) |
401 | return; | ||
402 | |||
403 | if (S_ISCHR(header->mode) || S_ISBLK(header->mode) || S_ISSOCK(header->mode)) { | ||
404 | mknod(header->name, header->mode, makedev(header->devmajor, header->devminor)); | ||
405 | } else if (S_ISFIFO(header->mode)) { | ||
406 | mkfifo(header->name, header->mode); | ||
407 | } else { | ||
408 | open(header->name, O_WRONLY | O_CREAT | O_TRUNC, header->mode); | ||
409 | } | ||
410 | |||
411 | /* Now set permissions etc for the new directory */ | ||
412 | fixUpPermissions(header); | ||
284 | } | 413 | } |
285 | 414 | ||
415 | /* Read an octal value in a field of the specified width, with optional | ||
416 | * spaces on both sides of the number and with an optional null character | ||
417 | * at the end. Returns -1 on an illegal format. */ | ||
418 | static long getOctal(const char *cp, int size) | ||
419 | { | ||
420 | long val = 0; | ||
421 | |||
422 | for(;(size > 0) && (*cp == ' '); cp++, size--); | ||
423 | if ((size == 0) || !isOctal(*cp)) | ||
424 | return -1; | ||
425 | for(; (size > 0) && isOctal(*cp); size--) { | ||
426 | val = val * 8 + *cp++ - '0'; | ||
427 | } | ||
428 | for (;(size > 0) && (*cp == ' '); cp++, size--); | ||
429 | if ((size > 0) && *cp) | ||
430 | return -1; | ||
431 | return val; | ||
432 | } | ||
433 | |||
434 | |||
435 | /* Parse the tar header and fill in the nice struct with the details */ | ||
436 | static int | ||
437 | parseTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) | ||
438 | { | ||
439 | int i; | ||
440 | long chksum, sum; | ||
441 | unsigned char *s = (unsigned char *)rawHeader; | ||
442 | |||
443 | header->name = rawHeader->name; | ||
444 | header->mode = getOctal(rawHeader->mode, sizeof(rawHeader->mode)); | ||
445 | header->uid = getOctal(rawHeader->uid, sizeof(rawHeader->uid)); | ||
446 | header->gid = getOctal(rawHeader->gid, sizeof(rawHeader->gid)); | ||
447 | header->size = getOctal(rawHeader->size, sizeof(rawHeader->size)); | ||
448 | header->mtime = getOctal(rawHeader->mtime, sizeof(rawHeader->mtime)); | ||
449 | chksum = getOctal(rawHeader->chksum, sizeof(rawHeader->chksum)); | ||
450 | header->type = rawHeader->typeflag; | ||
451 | header->linkname = rawHeader->linkname; | ||
452 | header->devmajor = getOctal(rawHeader->devmajor, sizeof(rawHeader->devmajor)); | ||
453 | header->devminor = getOctal(rawHeader->devminor, sizeof(rawHeader->devminor)); | ||
454 | |||
455 | /* Check the checksum */ | ||
456 | sum = ' ' * sizeof(rawHeader->chksum); | ||
457 | for ( i = TarChecksumOffset; i > 0; i-- ) | ||
458 | sum += *s++; | ||
459 | s += sizeof(rawHeader->chksum); | ||
460 | for ( i = (512 - TarChecksumOffset - sizeof(rawHeader->chksum)); i > 0; i-- ) | ||
461 | sum += *s++; | ||
462 | if (sum == chksum ) | ||
463 | return ( TRUE); | ||
464 | return( FALSE); | ||
465 | } | ||
286 | 466 | ||
287 | 467 | ||
288 | /* | 468 | /* |
@@ -296,6 +476,8 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, | |||
296 | int errorFlag=FALSE; | 476 | int errorFlag=FALSE; |
297 | TarHeader rawHeader; | 477 | TarHeader rawHeader; |
298 | TarInfo header; | 478 | TarInfo header; |
479 | int alreadyWarned=FALSE; | ||
480 | //int skipFileFlag=FALSE; | ||
299 | 481 | ||
300 | /* Open the tar file for reading. */ | 482 | /* Open the tar file for reading. */ |
301 | if (!strcmp(tarName, "-")) | 483 | if (!strcmp(tarName, "-")) |
@@ -307,9 +489,14 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, | |||
307 | return ( FALSE); | 489 | return ( FALSE); |
308 | } | 490 | } |
309 | 491 | ||
310 | /* Read the tar file */ | 492 | /* Set the umask for this process so it doesn't |
493 | * screw up permission setting for us later. */ | ||
494 | umask(0); | ||
495 | |||
496 | /* Read the tar file, and iterate over it one file at a time */ | ||
311 | while ( (status = fullRead(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { | 497 | while ( (status = fullRead(tarFd, (char*)&rawHeader, TAR_BLOCK_SIZE)) == TAR_BLOCK_SIZE ) { |
312 | /* Now see if the header looks ok */ | 498 | |
499 | /* First, try to read the header */ | ||
313 | if ( parseTarHeader(&rawHeader, &header) == FALSE ) { | 500 | if ( parseTarHeader(&rawHeader, &header) == FALSE ) { |
314 | close( tarFd); | 501 | close( tarFd); |
315 | if ( *(header.name) == '\0' ) { | 502 | if ( *(header.name) == '\0' ) { |
@@ -322,26 +509,64 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, | |||
322 | } | 509 | } |
323 | if ( *(header.name) == '\0' ) | 510 | if ( *(header.name) == '\0' ) |
324 | goto endgame; | 511 | goto endgame; |
325 | 512 | ||
326 | if (extractFlag == FALSE) { | 513 | /* Check for and relativify any absolute paths */ |
327 | if (verboseFlag == TRUE) { | 514 | if ( *(header.name) == '/' ) { |
328 | printf("%s %3d/%-d ", modeString(header.mode), header.uid, header.gid); | 515 | |
329 | if (header.type==CHRTYPE || header.type==BLKTYPE) | 516 | while (*(header.name) == '/') |
330 | printf("%4d,%4d %s ", MAJOR(header.device), | 517 | ++*(header.name); |
331 | MINOR(header.device), timeString(header.mtime)); | 518 | |
332 | else | 519 | if (alreadyWarned == FALSE) { |
333 | printf("%9ld %s ", header.size, timeString(header.mtime)); | 520 | errorMsg("Absolute path detected, removing leading slashes\n"); |
521 | alreadyWarned = TRUE; | ||
334 | } | 522 | } |
335 | printf("%s", header.name); | 523 | } |
524 | |||
525 | /* Special treatment if the list (-t) flag is on */ | ||
526 | if (verboseFlag == TRUE && extractFlag == FALSE) { | ||
527 | int len, len1; | ||
528 | char buf[35]; | ||
529 | struct tm *tm = localtime (&(header.mtime)); | ||
336 | 530 | ||
531 | len=printf("%s %d/%-d ", modeString(header.mode), header.uid, header.gid); | ||
532 | if (header.type==CHRTYPE || header.type==BLKTYPE) { | ||
533 | len1=snprintf(buf, sizeof(buf), "%ld,%-ld ", | ||
534 | header.devmajor, header.devminor); | ||
535 | } else { | ||
536 | len1=snprintf(buf, sizeof(buf), "%d ", header.size); | ||
537 | } | ||
538 | /* Jump through some hoops to make the columns match up */ | ||
539 | for(;(len+len1)<31;len++) | ||
540 | printf(" "); | ||
541 | printf(buf); | ||
542 | |||
543 | /* Use ISO 8610 time format */ | ||
544 | if (tm) { | ||
545 | printf ("%04d-%02d-%02d %02d:%02d:%02d ", | ||
546 | tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, | ||
547 | tm->tm_hour, tm->tm_min, tm->tm_sec); | ||
548 | } | ||
549 | } | ||
550 | /* List contents if we are supposed to do that */ | ||
551 | if (verboseFlag == TRUE || listFlag == TRUE) { | ||
552 | /* Now the normal listing */ | ||
553 | printf("%s", header.name); | ||
554 | /* If this is a link, say so */ | ||
337 | if (header.type==LNKTYPE) | 555 | if (header.type==LNKTYPE) |
338 | printf(" (link to \"%s\")", hp->linkName); | 556 | printf(" link to %s", header.linkname); |
339 | else if (header.type==SYMTYPE) | 557 | else if (header.type==SYMTYPE) |
340 | printf(" (symlink to \"%s\")", hp->linkName); | 558 | printf(" -> %s", header.linkname); |
341 | printf("\n"); | 559 | printf("\n"); |
342 | continue; | ||
343 | } | 560 | } |
344 | 561 | ||
562 | #if 0 | ||
563 | /* See if we want to restore this file or not */ | ||
564 | skipFileFlag=FALSE; | ||
565 | if (wantFileName(outName) == FALSE) { | ||
566 | skipFileFlag = TRUE; | ||
567 | } | ||
568 | #endif | ||
569 | |||
345 | /* If we got here, we can be certain we have a legitimate | 570 | /* If we got here, we can be certain we have a legitimate |
346 | * header to work with. So work with it. */ | 571 | * header to work with. So work with it. */ |
347 | switch ( header.type ) { | 572 | switch ( header.type ) { |
@@ -350,29 +575,28 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, | |||
350 | /* If the name ends in a '/' then assume it is | 575 | /* If the name ends in a '/' then assume it is |
351 | * supposed to be a directory, and fall through */ | 576 | * supposed to be a directory, and fall through */ |
352 | if (header.name[strlen(header.name)-1] != '/') { | 577 | if (header.name[strlen(header.name)-1] != '/') { |
353 | tarExtractRegularFile(&header, extractFlag, listFlag, tostdoutFlag, verboseFlag); | 578 | tarExtractRegularFile(&header, extractFlag, tostdoutFlag); |
354 | break; | 579 | break; |
355 | } | 580 | } |
356 | case DIRTYPE: | 581 | case DIRTYPE: |
357 | tarExtractDirectory( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag); | 582 | tarExtractDirectory( &header, extractFlag, tostdoutFlag); |
358 | break; | 583 | break; |
359 | case LNKTYPE: | 584 | case LNKTYPE: |
360 | tarExtractHardLink( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag); | 585 | tarExtractHardLink( &header, extractFlag, tostdoutFlag); |
361 | break; | 586 | break; |
362 | case SYMTYPE: | 587 | case SYMTYPE: |
363 | tarExtractSymLink( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag); | 588 | tarExtractSymLink( &header, extractFlag, tostdoutFlag); |
364 | break; | 589 | break; |
365 | case CHRTYPE: | 590 | case CHRTYPE: |
366 | case BLKTYPE: | 591 | case BLKTYPE: |
367 | case FIFOTYPE: | 592 | case FIFOTYPE: |
368 | tarExtractSpecial( &header, extractFlag, listFlag, tostdoutFlag, verboseFlag); | 593 | tarExtractSpecial( &header, extractFlag, tostdoutFlag); |
369 | break; | 594 | break; |
370 | default: | 595 | default: |
371 | close( tarFd); | 596 | close( tarFd); |
372 | return( FALSE); | 597 | return( FALSE); |
373 | } | 598 | } |
374 | } | 599 | } |
375 | |||
376 | close(tarFd); | 600 | close(tarFd); |
377 | if (status > 0) { | 601 | if (status > 0) { |
378 | /* Bummer - we read a partial header */ | 602 | /* Bummer - we read a partial header */ |
@@ -382,7 +606,7 @@ static int readTarFile(const char* tarName, int extractFlag, int listFlag, | |||
382 | else | 606 | else |
383 | return( status); | 607 | return( status); |
384 | 608 | ||
385 | /* Stuff we do when we know we are done with the file */ | 609 | /* Stuff to do when we are done */ |
386 | endgame: | 610 | endgame: |
387 | close( tarFd); | 611 | close( tarFd); |
388 | if ( *(header.name) == '\0' ) { | 612 | if ( *(header.name) == '\0' ) { |
@@ -392,848 +616,3 @@ endgame: | |||
392 | return( FALSE); | 616 | return( FALSE); |
393 | } | 617 | } |
394 | 618 | ||
395 | /* | ||
396 | * Read an octal value in a field of the specified width, with optional | ||
397 | * spaces on both sides of the number and with an optional null character | ||
398 | * at the end. Returns -1 on an illegal format. | ||
399 | */ | ||
400 | static long getOctal(const char *cp, int size) | ||
401 | { | ||
402 | long val = 0; | ||
403 | |||
404 | for(;(size > 0) && (*cp == ' '); cp++, size--); | ||
405 | if ((size == 0) || !isOctal(*cp)) | ||
406 | return -1; | ||
407 | for(; (size > 0) && isOctal(*cp); size--) { | ||
408 | val = val * 8 + *cp++ - '0'; | ||
409 | } | ||
410 | for (;(size > 0) && (*cp == ' '); cp++, size--); | ||
411 | if ((size > 0) && *cp) | ||
412 | return -1; | ||
413 | return val; | ||
414 | } | ||
415 | |||
416 | /* Parse the tar header and fill in the nice struct with the details */ | ||
417 | static int | ||
418 | parseTarHeader(struct TarHeader *rawHeader, struct TarInfo *header) | ||
419 | { | ||
420 | int i; | ||
421 | long chksum, sum; | ||
422 | unsigned char *s = (unsigned char *)rawHeader; | ||
423 | |||
424 | header->name = rawHeader->name; | ||
425 | header->mode = getOctal(rawHeader->mode, sizeof(rawHeader->mode)); | ||
426 | header->uid = getOctal(rawHeader->uid, sizeof(rawHeader->uid)); | ||
427 | header->gid = getOctal(rawHeader->gid, sizeof(rawHeader->gid)); | ||
428 | header->size = getOctal(rawHeader->size, sizeof(rawHeader->size)); | ||
429 | header->mtime = getOctal(rawHeader->mtime, sizeof(rawHeader->mtime)); | ||
430 | chksum = getOctal(rawHeader->chksum, sizeof(rawHeader->chksum)); | ||
431 | header->type = rawHeader->typeflag; | ||
432 | header->linkname = rawHeader->linkname; | ||
433 | header->device = MAJOR(getOctal(rawHeader->devmajor, sizeof(rawHeader->devmajor))) | | ||
434 | MINOR(getOctal(rawHeader->devminor, sizeof(rawHeader->devminor))); | ||
435 | |||
436 | /* Check the checksum */ | ||
437 | sum = ' ' * sizeof(rawHeader->chksum); | ||
438 | for ( i = TarChecksumOffset; i > 0; i-- ) | ||
439 | sum += *s++; | ||
440 | s += sizeof(rawHeader->chksum); | ||
441 | for ( i = (512 - TarChecksumOffset - sizeof(rawHeader->chksum)); i > 0; i-- ) | ||
442 | sum += *s++; | ||
443 | if (sum == chksum ) | ||
444 | return ( TRUE); | ||
445 | return( FALSE); | ||
446 | } | ||
447 | |||
448 | #if 0 | ||
449 | if ((header->mode < 0) || (header->uid < 0) || | ||
450 | (header->gid < 0) || (header->size < 0)) { | ||
451 | errorMsg(stderr, "Bad tar header, skipping\n"); | ||
452 | return( FALSE); | ||
453 | } | ||
454 | |||
455 | badHeader = FALSE; | ||
456 | skipFileFlag = FALSE; | ||
457 | devFileFlag = FALSE; | ||
458 | |||
459 | /* | ||
460 | * Check for the file modes. | ||
461 | */ | ||
462 | hardLink = ((hp->typeFlag == TAR_TYPE_HARD_LINK) || | ||
463 | (hp->typeFlag == TAR_TYPE_HARD_LINK - '0')); | ||
464 | |||
465 | softLink = ((hp->typeFlag == TAR_TYPE_SOFT_LINK) || | ||
466 | (hp->typeFlag == TAR_TYPE_SOFT_LINK - '0')); | ||
467 | |||
468 | /* | ||
469 | * Check for a directory. | ||
470 | */ | ||
471 | if (outName[strlen(outName) - 1] == '/') | ||
472 | mode |= S_IFDIR; | ||
473 | |||
474 | /* | ||
475 | * Check for absolute paths in the file. | ||
476 | * If we find any, then warn the user and make them relative. | ||
477 | */ | ||
478 | if (*outName == '/') { | ||
479 | while (*outName == '/') | ||
480 | outName++; | ||
481 | |||
482 | if (warnedRoot == FALSE) { | ||
483 | fprintf(stderr, | ||
484 | "Absolute path detected, removing leading slashes\n"); | ||
485 | } | ||
486 | |||
487 | warnedRoot = TRUE; | ||
488 | } | ||
489 | |||
490 | /* | ||
491 | * See if we want this file to be restored. | ||
492 | * If not, then set up to skip it. | ||
493 | */ | ||
494 | if (wantFileName(outName, argc, argv) == FALSE) { | ||
495 | if (!hardLink && !softLink && (S_ISREG(mode) || S_ISCHR(mode) | ||
496 | || S_ISBLK(mode) || S_ISSOCK(mode) | ||
497 | || S_ISFIFO(mode))) { | ||
498 | inHeader = (size == 0) ? TRUE : FALSE; | ||
499 | dataCc = size; | ||
500 | } | ||
501 | |||
502 | skipFileFlag = TRUE; | ||
503 | |||
504 | return; | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * This file is to be handled. | ||
509 | * If we aren't extracting then just list information about the file. | ||
510 | */ | ||
511 | if (extractFlag == FALSE) { | ||
512 | if (verboseFlag == TRUE) { | ||
513 | printf("%s %3d/%-d ", modeString(mode), uid, gid); | ||
514 | if (S_ISCHR(mode) || S_ISBLK(mode)) | ||
515 | printf("%4d,%4d %s ", major, minor, timeString(mtime)); | ||
516 | else | ||
517 | printf("%9ld %s ", size, timeString(mtime)); | ||
518 | } | ||
519 | printf("%s", outName); | ||
520 | |||
521 | if (hardLink) | ||
522 | printf(" (link to \"%s\")", hp->linkName); | ||
523 | else if (softLink) | ||
524 | printf(" (symlink to \"%s\")", hp->linkName); | ||
525 | else if (S_ISREG(mode) || S_ISCHR(mode) || S_ISBLK(mode) || | ||
526 | S_ISSOCK(mode) || S_ISFIFO(mode)) { | ||
527 | inHeader = (size == 0) ? TRUE : FALSE; | ||
528 | dataCc = size; | ||
529 | } | ||
530 | |||
531 | printf("\n"); | ||
532 | |||
533 | return; | ||
534 | } | ||
535 | |||
536 | /* | ||
537 | * We really want to extract the file. | ||
538 | */ | ||
539 | if (verboseFlag == TRUE) | ||
540 | printf("x %s\n", outName); | ||
541 | |||
542 | if (hardLink) { | ||
543 | if (link(hp->linkName, outName) < 0) { | ||
544 | perror(outName); | ||
545 | return; | ||
546 | } | ||
547 | /* Set the file time */ | ||
548 | utb.actime = mtime; | ||
549 | utb.modtime = mtime; | ||
550 | utime(outName, &utb); | ||
551 | /* Set the file permissions */ | ||
552 | chown(outName, uid, gid); | ||
553 | chmod(outName, mode); | ||
554 | return; | ||
555 | } | ||
556 | |||
557 | if (softLink) { | ||
558 | #ifdef S_ISLNK | ||
559 | if (symlink(hp->linkName, outName) < 0) { | ||
560 | perror(outName); | ||
561 | return; | ||
562 | } | ||
563 | /* Try to change ownership of the symlink. | ||
564 | * If libs doesn't support that, don't bother. | ||
565 | * Changing the pointed-to file is the Wrong Thing(tm). | ||
566 | */ | ||
567 | #if (__GLIBC__ >= 2) && (__GLIBC_MINOR__ >= 1) | ||
568 | lchown(outName, uid, gid); | ||
569 | #endif | ||
570 | |||
571 | /* Do not change permissions or date on symlink, | ||
572 | * since it changes the pointed to file instead. duh. */ | ||
573 | #else | ||
574 | fprintf(stderr, "Cannot create symbolic links\n"); | ||
575 | #endif | ||
576 | return; | ||
577 | } | ||
578 | |||
579 | /* Set the umask for this process so it doesn't | ||
580 | * screw things up. */ | ||
581 | umask(0); | ||
582 | |||
583 | /* | ||
584 | * If the file is a directory, then just create the path. | ||
585 | */ | ||
586 | if (S_ISDIR(mode)) { | ||
587 | if (createPath(outName, mode) == TRUE) { | ||
588 | /* make the final component, just in case it was | ||
589 | * omitted by createPath() (which will skip the | ||
590 | * directory if it doesn't have a terminating '/') | ||
591 | */ | ||
592 | mkdir(outName, mode); | ||
593 | |||
594 | /* Set the file time */ | ||
595 | utb.actime = mtime; | ||
596 | utb.modtime = mtime; | ||
597 | utime(outName, &utb); | ||
598 | /* Set the file permissions */ | ||
599 | chown(outName, uid, gid); | ||
600 | chmod(outName, mode); | ||
601 | return; | ||
602 | } | ||
603 | return; | ||
604 | } | ||
605 | |||
606 | /* | ||
607 | * There is a file to write. | ||
608 | * First create the path to it if necessary with default permissions. | ||
609 | */ | ||
610 | createPath(outName, 0777); | ||
611 | |||
612 | inHeader = (size == 0) ? TRUE : FALSE; | ||
613 | dataCc = size; | ||
614 | |||
615 | /* | ||
616 | * Start the output file. | ||
617 | */ | ||
618 | if (tostdoutFlag == TRUE) | ||
619 | outFd = fileno(stdout); | ||
620 | else { | ||
621 | if (S_ISCHR(mode) || S_ISBLK(mode) || S_ISSOCK(mode)) { | ||
622 | devFileFlag = TRUE; | ||
623 | outFd = mknod(outName, mode, makedev(major, minor)); | ||
624 | } else if (S_ISFIFO(mode)) { | ||
625 | devFileFlag = TRUE; | ||
626 | outFd = mkfifo(outName, mode); | ||
627 | } else { | ||
628 | outFd = open(outName, O_WRONLY | O_CREAT | O_TRUNC, mode); | ||
629 | } | ||
630 | if (outFd < 0) { | ||
631 | perror(outName); | ||
632 | skipFileFlag = TRUE; | ||
633 | return; | ||
634 | } | ||
635 | /* Set the file time */ | ||
636 | utb.actime = mtime; | ||
637 | utb.modtime = mtime; | ||
638 | utime(outName, &utb); | ||
639 | /* Set the file permissions */ | ||
640 | chown(outName, uid, gid); | ||
641 | chmod(outName, mode); | ||
642 | } | ||
643 | |||
644 | |||
645 | /* | ||
646 | * If the file is empty, then that's all we need to do. | ||
647 | */ | ||
648 | if (size == 0 && (tostdoutFlag == FALSE) && (devFileFlag == FALSE)) { | ||
649 | close(outFd); | ||
650 | outFd = -1; | ||
651 | } | ||
652 | } | ||
653 | |||
654 | |||
655 | /* | ||
656 | * Handle a data block of some specified size that was read. | ||
657 | */ | ||
658 | static void readData(const char *cp, int count) | ||
659 | { | ||
660 | /* | ||
661 | * Reduce the amount of data left in this file. | ||
662 | * If there is no more data left, then we need to read | ||
663 | * the header again. | ||
664 | */ | ||
665 | dataCc -= count; | ||
666 | |||
667 | if (dataCc <= 0) | ||
668 | inHeader = TRUE; | ||
669 | |||
670 | /* | ||
671 | * If we aren't extracting files or this file is being | ||
672 | * skipped then do nothing more. | ||
673 | */ | ||
674 | if (extractFlag == FALSE || skipFileFlag == TRUE) | ||
675 | return; | ||
676 | |||
677 | /* | ||
678 | * Write the data to the output file. | ||
679 | */ | ||
680 | if (fullWrite(outFd, cp, count) < 0) { | ||
681 | perror(outName); | ||
682 | if (tostdoutFlag == FALSE) { | ||
683 | close(outFd); | ||
684 | outFd = -1; | ||
685 | } | ||
686 | skipFileFlag = TRUE; | ||
687 | return; | ||
688 | } | ||
689 | |||
690 | /* | ||
691 | * Check if we are done writing to the file now. | ||
692 | */ | ||
693 | if (dataCc <= 0 && tostdoutFlag == FALSE) { | ||
694 | struct utimbuf utb; | ||
695 | |||
696 | if (close(outFd)) | ||
697 | perror(outName); | ||
698 | |||
699 | /* Set the file time */ | ||
700 | utb.actime = mtime; | ||
701 | utb.modtime = mtime; | ||
702 | utime(outName, &utb); | ||
703 | /* Set the file permissions */ | ||
704 | chown(outName, uid, gid); | ||
705 | chmod(outName, mode); | ||
706 | |||
707 | outFd = -1; | ||
708 | } | ||
709 | } | ||
710 | |||
711 | |||
712 | /* | ||
713 | * See if the specified file name belongs to one of the specified list | ||
714 | * of path prefixes. An empty list implies that all files are wanted. | ||
715 | * Returns TRUE if the file is selected. | ||
716 | */ | ||
717 | static int | ||
718 | wantFileName(const char *fileName, int argc, char **argv) | ||
719 | { | ||
720 | const char *pathName; | ||
721 | int fileLength; | ||
722 | int pathLength; | ||
723 | |||
724 | /* | ||
725 | * If there are no files in the list, then the file is wanted. | ||
726 | */ | ||
727 | if (argc == 0) | ||
728 | return TRUE; | ||
729 | |||
730 | fileLength = strlen(fileName); | ||
731 | |||
732 | /* | ||
733 | * Check each of the test paths. | ||
734 | */ | ||
735 | while (argc-- > 0) { | ||
736 | pathName = *argv++; | ||
737 | |||
738 | pathLength = strlen(pathName); | ||
739 | |||
740 | if (fileLength < pathLength) | ||
741 | continue; | ||
742 | |||
743 | if (memcmp(fileName, pathName, pathLength) != 0) | ||
744 | continue; | ||
745 | |||
746 | if ((fileLength == pathLength) || (fileName[pathLength] == '/')) { | ||
747 | return TRUE; | ||
748 | } | ||
749 | } | ||
750 | |||
751 | return FALSE; | ||
752 | } | ||
753 | |||
754 | |||
755 | /* From here to the end of the file is the tar writing stuff. | ||
756 | * If you do not have BB_FEATURE_TAR_CREATE defined, this will | ||
757 | * not be built. | ||
758 | * */ | ||
759 | #ifdef BB_FEATURE_TAR_CREATE | ||
760 | |||
761 | /* | ||
762 | * Write a tar file containing the specified files. | ||
763 | */ | ||
764 | static void writeTarFile(int argc, char **argv) | ||
765 | { | ||
766 | struct stat statbuf; | ||
767 | |||
768 | /* | ||
769 | * Make sure there is at least one file specified. | ||
770 | */ | ||
771 | if (argc <= 0) { | ||
772 | fprintf(stderr, "No files specified to be saved\n"); | ||
773 | errorFlag = TRUE; | ||
774 | } | ||
775 | |||
776 | /* | ||
777 | * Create the tar file for writing. | ||
778 | */ | ||
779 | if ((tarName == NULL) || !strcmp(tarName, "-")) { | ||
780 | tostdoutFlag = TRUE; | ||
781 | tarFd = fileno(stdout); | ||
782 | } else | ||
783 | tarFd = open(tarName, O_WRONLY | O_CREAT | O_TRUNC, 0666); | ||
784 | |||
785 | if (tarFd < 0) { | ||
786 | perror(tarName); | ||
787 | errorFlag = TRUE; | ||
788 | return; | ||
789 | } | ||
790 | |||
791 | /* | ||
792 | * Get the device and inode of the tar file for checking later. | ||
793 | */ | ||
794 | if (fstat(tarFd, &statbuf) < 0) { | ||
795 | perror(tarName); | ||
796 | errorFlag = TRUE; | ||
797 | goto done; | ||
798 | } | ||
799 | |||
800 | tarDev = statbuf.st_dev; | ||
801 | tarInode = statbuf.st_ino; | ||
802 | |||
803 | /* | ||
804 | * Append each file name into the archive file. | ||
805 | * Follow symbolic links for these top level file names. | ||
806 | */ | ||
807 | while (errorFlag == FALSE && (argc-- > 0)) { | ||
808 | saveFile(*argv++, FALSE); | ||
809 | } | ||
810 | |||
811 | /* | ||
812 | * Now write an empty block of zeroes to end the archive. | ||
813 | */ | ||
814 | writeTarBlock("", 1); | ||
815 | |||
816 | |||
817 | done: | ||
818 | /* | ||
819 | * Close the tar file and check for errors if it was opened. | ||
820 | */ | ||
821 | if ((tostdoutFlag == FALSE) && (tarFd >= 0) && (close(tarFd) < 0)) | ||
822 | perror(tarName); | ||
823 | } | ||
824 | |||
825 | /* | ||
826 | * Save one file into the tar file. | ||
827 | * If the file is a directory, then this will recursively save all of | ||
828 | * the files and directories within the directory. The seeLinks | ||
829 | * flag indicates whether or not we want to see symbolic links as | ||
830 | * they really are, instead of blindly following them. | ||
831 | */ | ||
832 | static void saveFile(const char *fileName, int seeLinks) | ||
833 | { | ||
834 | int status; | ||
835 | struct stat statbuf; | ||
836 | |||
837 | if (verboseFlag == TRUE) | ||
838 | printf("a %s\n", fileName); | ||
839 | |||
840 | /* | ||
841 | * Check that the file name will fit in the header. | ||
842 | */ | ||
843 | if (strlen(fileName) >= TAR_NAME_SIZE) { | ||
844 | fprintf(stderr, "%s: File name is too long\n", fileName); | ||
845 | |||
846 | return; | ||
847 | } | ||
848 | |||
849 | /* | ||
850 | * Find out about the file. | ||
851 | */ | ||
852 | #ifdef S_ISLNK | ||
853 | if (seeLinks == TRUE) | ||
854 | status = lstat(fileName, &statbuf); | ||
855 | else | ||
856 | #endif | ||
857 | status = stat(fileName, &statbuf); | ||
858 | |||
859 | if (status < 0) { | ||
860 | perror(fileName); | ||
861 | |||
862 | return; | ||
863 | } | ||
864 | |||
865 | /* | ||
866 | * Make sure we aren't trying to save our file into itself. | ||
867 | */ | ||
868 | if ((statbuf.st_dev == tarDev) && (statbuf.st_ino == tarInode)) { | ||
869 | fprintf(stderr, "Skipping saving of archive file itself\n"); | ||
870 | |||
871 | return; | ||
872 | } | ||
873 | |||
874 | /* | ||
875 | * Check the type of file. | ||
876 | */ | ||
877 | mode = statbuf.st_mode; | ||
878 | |||
879 | if (S_ISDIR(mode)) { | ||
880 | saveDirectory(fileName, &statbuf); | ||
881 | |||
882 | return; | ||
883 | } | ||
884 | if (S_ISREG(mode)) { | ||
885 | saveRegularFile(fileName, &statbuf); | ||
886 | |||
887 | return; | ||
888 | } | ||
889 | |||
890 | /* Some day add support for tarring these up... but not today. :) */ | ||
891 | // if (S_ISLNK(mode) || S_ISFIFO(mode) || S_ISBLK(mode) || S_ISCHR (mode) ) { | ||
892 | // fprintf (stderr, "%s: This version of tar can't store this type of file\n", fileName); | ||
893 | // } | ||
894 | |||
895 | /* | ||
896 | * The file is a strange type of file, ignore it. | ||
897 | */ | ||
898 | fprintf(stderr, "%s: not a directory or regular file\n", fileName); | ||
899 | } | ||
900 | |||
901 | |||
902 | /* | ||
903 | * Save a regular file to the tar file. | ||
904 | */ | ||
905 | static void | ||
906 | saveRegularFile(const char *fileName, const struct stat *statbuf) | ||
907 | { | ||
908 | int sawEof; | ||
909 | int fileFd; | ||
910 | int cc; | ||
911 | int dataCount; | ||
912 | long fullDataCount; | ||
913 | char data[TAR_BLOCK_SIZE * 16]; | ||
914 | |||
915 | /* | ||
916 | * Open the file for reading. | ||
917 | */ | ||
918 | fileFd = open(fileName, O_RDONLY); | ||
919 | |||
920 | if (fileFd < 0) { | ||
921 | perror(fileName); | ||
922 | |||
923 | return; | ||
924 | } | ||
925 | |||
926 | /* | ||
927 | * Write out the header for the file. | ||
928 | */ | ||
929 | writeHeader(fileName, statbuf); | ||
930 | |||
931 | /* | ||
932 | * Write the data blocks of the file. | ||
933 | * We must be careful to write the amount of data that the stat | ||
934 | * buffer indicated, even if the file has changed size. Otherwise | ||
935 | * the tar file will be incorrect. | ||
936 | */ | ||
937 | fullDataCount = statbuf->st_size; | ||
938 | sawEof = FALSE; | ||
939 | |||
940 | while (fullDataCount > 0) { | ||
941 | /* | ||
942 | * Get the amount to write this iteration which is | ||
943 | * the minumum of the amount left to write and the | ||
944 | * buffer size. | ||
945 | */ | ||
946 | dataCount = sizeof(data); | ||
947 | |||
948 | if (dataCount > fullDataCount) | ||
949 | dataCount = (int) fullDataCount; | ||
950 | |||
951 | /* | ||
952 | * Read the data from the file if we haven't seen the | ||
953 | * end of file yet. | ||
954 | */ | ||
955 | cc = 0; | ||
956 | |||
957 | if (sawEof == FALSE) { | ||
958 | cc = fullRead(fileFd, data, dataCount); | ||
959 | |||
960 | if (cc < 0) { | ||
961 | perror(fileName); | ||
962 | |||
963 | (void) close(fileFd); | ||
964 | errorFlag = TRUE; | ||
965 | |||
966 | return; | ||
967 | } | ||
968 | |||
969 | /* | ||
970 | * If the file ended too soon, complain and set | ||
971 | * a flag so we will zero fill the rest of it. | ||
972 | */ | ||
973 | if (cc < dataCount) { | ||
974 | fprintf(stderr, "%s: Short read - zero filling", fileName); | ||
975 | |||
976 | sawEof = TRUE; | ||
977 | } | ||
978 | } | ||
979 | |||
980 | /* | ||
981 | * Zero fill the rest of the data if necessary. | ||
982 | */ | ||
983 | if (cc < dataCount) | ||
984 | memset(data + cc, 0, dataCount - cc); | ||
985 | |||
986 | /* | ||
987 | * Write the buffer to the TAR file. | ||
988 | */ | ||
989 | writeTarBlock(data, dataCount); | ||
990 | |||
991 | fullDataCount -= dataCount; | ||
992 | } | ||
993 | |||
994 | /* | ||
995 | * Close the file. | ||
996 | */ | ||
997 | if ((tostdoutFlag == FALSE) && close(fileFd) < 0) | ||
998 | fprintf(stderr, "%s: close: %s\n", fileName, strerror(errno)); | ||
999 | } | ||
1000 | |||
1001 | |||
1002 | /* | ||
1003 | * Save a directory and all of its files to the tar file. | ||
1004 | */ | ||
1005 | static void saveDirectory(const char *dirName, const struct stat *statbuf) | ||
1006 | { | ||
1007 | DIR *dir; | ||
1008 | struct dirent *entry; | ||
1009 | int needSlash; | ||
1010 | char fullName[PATH_MAX + 1]; | ||
1011 | |||
1012 | /* | ||
1013 | * Construct the directory name as used in the tar file by appending | ||
1014 | * a slash character to it. | ||
1015 | */ | ||
1016 | strcpy(fullName, dirName); | ||
1017 | strcat(fullName, "/"); | ||
1018 | |||
1019 | /* | ||
1020 | * Write out the header for the directory entry. | ||
1021 | */ | ||
1022 | writeHeader(fullName, statbuf); | ||
1023 | |||
1024 | /* | ||
1025 | * Open the directory. | ||
1026 | */ | ||
1027 | dir = opendir(dirName); | ||
1028 | |||
1029 | if (dir == NULL) { | ||
1030 | fprintf(stderr, "Cannot read directory \"%s\": %s\n", | ||
1031 | dirName, strerror(errno)); | ||
1032 | |||
1033 | return; | ||
1034 | } | ||
1035 | |||
1036 | /* | ||
1037 | * See if a slash is needed. | ||
1038 | */ | ||
1039 | needSlash = (*dirName && (dirName[strlen(dirName) - 1] != '/')); | ||
1040 | |||
1041 | /* | ||
1042 | * Read all of the directory entries and check them, | ||
1043 | * except for the current and parent directory entries. | ||
1044 | */ | ||
1045 | while (errorFlag == FALSE && ((entry = readdir(dir)) != NULL)) { | ||
1046 | if ((strcmp(entry->d_name, ".") == 0) || | ||
1047 | (strcmp(entry->d_name, "..") == 0)) { | ||
1048 | continue; | ||
1049 | } | ||
1050 | |||
1051 | /* | ||
1052 | * Build the full path name to the file. | ||
1053 | */ | ||
1054 | strcpy(fullName, dirName); | ||
1055 | |||
1056 | if (needSlash) | ||
1057 | strcat(fullName, "/"); | ||
1058 | |||
1059 | strcat(fullName, entry->d_name); | ||
1060 | |||
1061 | /* | ||
1062 | * Write this file to the tar file, noticing whether or not | ||
1063 | * the file is a symbolic link. | ||
1064 | */ | ||
1065 | saveFile(fullName, TRUE); | ||
1066 | } | ||
1067 | |||
1068 | /* | ||
1069 | * All done, close the directory. | ||
1070 | */ | ||
1071 | closedir(dir); | ||
1072 | } | ||
1073 | |||
1074 | |||
1075 | /* | ||
1076 | * Write a tar header for the specified file name and status. | ||
1077 | * It is assumed that the file name fits. | ||
1078 | */ | ||
1079 | static void writeHeader(const char *fileName, const struct stat *statbuf) | ||
1080 | { | ||
1081 | long checkSum; | ||
1082 | const unsigned char *cp; | ||
1083 | int len; | ||
1084 | TarHeader header; | ||
1085 | |||
1086 | /* | ||
1087 | * Zero the header block in preparation for filling it in. | ||
1088 | */ | ||
1089 | memset((char *) &header, 0, sizeof(header)); | ||
1090 | |||
1091 | /* | ||
1092 | * Fill in the header. | ||
1093 | */ | ||
1094 | strcpy(header.name, fileName); | ||
1095 | |||
1096 | strncpy(header.magic, TAR_MAGIC, sizeof(header.magic)); | ||
1097 | strncpy(header.version, TAR_VERSION, sizeof(header.version)); | ||
1098 | |||
1099 | putOctal(header.mode, sizeof(header.mode), statbuf->st_mode & 0777); | ||
1100 | putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); | ||
1101 | putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); | ||
1102 | putOctal(header.size, sizeof(header.size), statbuf->st_size); | ||
1103 | putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); | ||
1104 | |||
1105 | header.typeFlag = TAR_TYPE_REGULAR; | ||
1106 | |||
1107 | /* | ||
1108 | * Calculate and store the checksum. | ||
1109 | * This is the sum of all of the bytes of the header, | ||
1110 | * with the checksum field itself treated as blanks. | ||
1111 | */ | ||
1112 | memset(header.checkSum, ' ', sizeof(header.checkSum)); | ||
1113 | |||
1114 | cp = (const unsigned char *) &header; | ||
1115 | len = sizeof(header); | ||
1116 | checkSum = 0; | ||
1117 | |||
1118 | while (len-- > 0) | ||
1119 | checkSum += *cp++; | ||
1120 | |||
1121 | putOctal(header.checkSum, sizeof(header.checkSum), checkSum); | ||
1122 | |||
1123 | /* | ||
1124 | * Write the tar header. | ||
1125 | */ | ||
1126 | writeTarBlock((const char *) &header, sizeof(header)); | ||
1127 | } | ||
1128 | |||
1129 | |||
1130 | /* | ||
1131 | * Write data to one or more blocks of the tar file. | ||
1132 | * The data is always padded out to a multiple of TAR_BLOCK_SIZE. | ||
1133 | * The errorFlag static variable is set on an error. | ||
1134 | */ | ||
1135 | static void writeTarBlock(const char *buf, int len) | ||
1136 | { | ||
1137 | int partialLength; | ||
1138 | int completeLength; | ||
1139 | char fullBlock[TAR_BLOCK_SIZE]; | ||
1140 | |||
1141 | /* | ||
1142 | * If we had a write error before, then do nothing more. | ||
1143 | */ | ||
1144 | if (errorFlag == TRUE) | ||
1145 | return; | ||
1146 | |||
1147 | /* | ||
1148 | * Get the amount of complete and partial blocks. | ||
1149 | */ | ||
1150 | partialLength = len % TAR_BLOCK_SIZE; | ||
1151 | completeLength = len - partialLength; | ||
1152 | |||
1153 | /* | ||
1154 | * Write all of the complete blocks. | ||
1155 | */ | ||
1156 | if ((completeLength > 0) && !fullWrite(tarFd, buf, completeLength)) { | ||
1157 | perror(tarName); | ||
1158 | |||
1159 | errorFlag = TRUE; | ||
1160 | |||
1161 | return; | ||
1162 | } | ||
1163 | |||
1164 | /* | ||
1165 | * If there are no partial blocks left, we are done. | ||
1166 | */ | ||
1167 | if (partialLength == 0) | ||
1168 | return; | ||
1169 | |||
1170 | /* | ||
1171 | * Copy the partial data into a complete block, and pad the rest | ||
1172 | * of it with zeroes. | ||
1173 | */ | ||
1174 | memcpy(fullBlock, buf + completeLength, partialLength); | ||
1175 | memset(fullBlock + partialLength, 0, TAR_BLOCK_SIZE - partialLength); | ||
1176 | |||
1177 | /* | ||
1178 | * Write the last complete block. | ||
1179 | */ | ||
1180 | if (!fullWrite(tarFd, fullBlock, TAR_BLOCK_SIZE)) { | ||
1181 | perror(tarName); | ||
1182 | |||
1183 | errorFlag = TRUE; | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | |||
1188 | /* | ||
1189 | * Put an octal string into the specified buffer. | ||
1190 | * The number is zero and space padded and possibly null padded. | ||
1191 | * Returns TRUE if successful. | ||
1192 | */ | ||
1193 | static int putOctal(char *cp, int len, long value) | ||
1194 | { | ||
1195 | int tempLength; | ||
1196 | char *tempString; | ||
1197 | char tempBuffer[32]; | ||
1198 | |||
1199 | /* | ||
1200 | * Create a string of the specified length with an initial space, | ||
1201 | * leading zeroes and the octal number, and a trailing null. | ||
1202 | */ | ||
1203 | tempString = tempBuffer; | ||
1204 | |||
1205 | sprintf(tempString, " %0*lo", len - 2, value); | ||
1206 | |||
1207 | tempLength = strlen(tempString) + 1; | ||
1208 | |||
1209 | /* | ||
1210 | * If the string is too large, suppress the leading space. | ||
1211 | */ | ||
1212 | if (tempLength > len) { | ||
1213 | tempLength--; | ||
1214 | tempString++; | ||
1215 | } | ||
1216 | |||
1217 | /* | ||
1218 | * If the string is still too large, suppress the trailing null. | ||
1219 | */ | ||
1220 | if (tempLength > len) | ||
1221 | tempLength--; | ||
1222 | |||
1223 | /* | ||
1224 | * If the string is still too large, fail. | ||
1225 | */ | ||
1226 | if (tempLength > len) | ||
1227 | return FALSE; | ||
1228 | |||
1229 | /* | ||
1230 | * Copy the string to the field. | ||
1231 | */ | ||
1232 | memcpy(cp, tempString, len); | ||
1233 | |||
1234 | return TRUE; | ||
1235 | } | ||
1236 | #endif | ||
1237 | |||
1238 | /* END CODE */ | ||
1239 | #endif | ||