summaryrefslogtreecommitdiff
path: root/archival
diff options
context:
space:
mode:
authorErik Andersen <andersen@codepoet.org>2000-03-24 00:54:46 +0000
committerErik Andersen <andersen@codepoet.org>2000-03-24 00:54:46 +0000
commit1ad302ac903695ef4ba748d3880222c05eeaafef (patch)
treea03bebb080253f4a20f8d5335285a5e717106d36 /archival
parente454fb68a3165ca5cf42c290b4b9dc92dbf9a235 (diff)
downloadbusybox-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.c1163
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};
138typedef struct TarInfo TarInfo; 137typedef 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. */
145static int readTarFile(const char* tarName, int extractFlag, int listFlag, 144static int readTarFile(const char* tarName, int extractFlag, int listFlag,
146 int tostdoutFlag, int verboseFlag); 145 int tostdoutFlag, int verboseFlag);
147static long getOctal(const char *cp, int len); 146
148static 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
256static void 255static void
257tarExtractRegularFile(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) 256tarExtractRegularFile(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
323static void
324fixUpPermissions(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
262static void 336static void
263tarExtractDirectory(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) 337tarExtractDirectory(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
268static void 356static void
269tarExtractHardLink(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) 357tarExtractHardLink(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
274static void 371static void
275tarExtractSymLink(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) 372tarExtractSymLink(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
280static void 397static void
281tarExtractSpecial(TarInfo *header, int extractFlag, int listFlag, int tostdoutFlag, int verboseFlag) 398tarExtractSpecial(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. */
418static 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 */
436static int
437parseTarHeader(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 */
386endgame: 610endgame:
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 */
400static 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 */
417static int
418parseTarHeader(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 */
658static 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 */
717static int
718wantFileName(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 */
764static 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 */
832static 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 */
905static void
906saveRegularFile(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 */
1005static 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 */
1079static 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 */
1135static 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 */
1193static 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