diff options
Diffstat (limited to 'tar.c')
-rw-r--r-- | tar.c | 179 |
1 files changed, 119 insertions, 60 deletions
@@ -56,7 +56,8 @@ | |||
56 | 56 | ||
57 | static const char tar_usage[] = | 57 | static const char tar_usage[] = |
58 | "tar -[cxtvOf] [tarFileName] [FILE] ...\n\n" | 58 | "tar -[cxtvOf] [tarFileName] [FILE] ...\n\n" |
59 | "Create, extract, or list files from a tar file.\n\n" | 59 | "Create, extract, or list files from a tar file. Note that\n" |
60 | "this version of tar packs hard links as separate files.\n\n" | ||
60 | "Options:\n" | 61 | "Options:\n" |
61 | 62 | ||
62 | "\tc=create, x=extract, t=list contents, v=verbose,\n" | 63 | "\tc=create, x=extract, t=list contents, v=verbose,\n" |
@@ -110,10 +111,10 @@ typedef struct TarHeader TarHeader; | |||
110 | 111 | ||
111 | /* A few useful constants */ | 112 | /* A few useful constants */ |
112 | #define TAR_MAGIC "ustar" /* ustar and a null */ | 113 | #define TAR_MAGIC "ustar" /* ustar and a null */ |
113 | #define TAR_VERSION "00" /* 00 and no null */ | 114 | //#define TAR_VERSION "00" /* 00 and no null */ |
115 | #define TAR_VERSION " " /* Be compatable with old GNU format */ | ||
114 | #define TAR_MAGIC_LEN 6 | 116 | #define TAR_MAGIC_LEN 6 |
115 | #define TAR_VERSION_LEN 2 | 117 | #define TAR_VERSION_LEN 2 |
116 | #define TAR_NAME_LEN 100 | ||
117 | #define TAR_BLOCK_SIZE 512 | 118 | #define TAR_BLOCK_SIZE 512 |
118 | 119 | ||
119 | /* A nice enum with all the possible tar file content types */ | 120 | /* A nice enum with all the possible tar file content types */ |
@@ -366,7 +367,8 @@ tarExtractHardLink(TarInfo *header, int extractFlag, int tostdoutFlag) | |||
366 | return; | 367 | return; |
367 | 368 | ||
368 | if (link(header->linkname, header->name) < 0) { | 369 | if (link(header->linkname, header->name) < 0) { |
369 | errorMsg("Error creating hard link '%s': %s\n", header->linkname, strerror(errno)); | 370 | errorMsg("Error creating hard link '%s' to '%s': %s\n", |
371 | header->name, header->linkname, strerror(errno)); | ||
370 | return; | 372 | return; |
371 | } | 373 | } |
372 | 374 | ||
@@ -382,7 +384,8 @@ tarExtractSymLink(TarInfo *header, int extractFlag, int tostdoutFlag) | |||
382 | 384 | ||
383 | #ifdef S_ISLNK | 385 | #ifdef S_ISLNK |
384 | if (symlink(header->linkname, header->name) < 0) { | 386 | if (symlink(header->linkname, header->name) < 0) { |
385 | errorMsg("Error creating symlink '%s': %s\n", header->linkname, strerror(errno)); | 387 | errorMsg("Error creating symlink '%s' to '%s': %s\n", |
388 | header->name, header->linkname, strerror(errno)); | ||
386 | return; | 389 | return; |
387 | } | 390 | } |
388 | /* Try to change ownership of the symlink. | 391 | /* Try to change ownership of the symlink. |
@@ -644,18 +647,15 @@ typedef struct TarBallInfo TarBallInfo; | |||
644 | static int putOctal (char *cp, int len, long value) | 647 | static int putOctal (char *cp, int len, long value) |
645 | { | 648 | { |
646 | int tempLength; | 649 | int tempLength; |
647 | char *tempString; | ||
648 | char tempBuffer[32]; | 650 | char tempBuffer[32]; |
651 | char *tempString = tempBuffer; | ||
649 | 652 | ||
650 | /* Create a string of the specified length with an initial space, | 653 | /* Create a string of the specified length with an initial space, |
651 | * leading zeroes and the octal number, and a trailing null. */ | 654 | * leading zeroes and the octal number, and a trailing null. */ |
652 | tempString = tempBuffer; | 655 | sprintf (tempString, "%0*lo", len - 1, value); |
653 | |||
654 | sprintf (tempString, " %0*lo", len - 2, value); | ||
655 | |||
656 | tempLength = strlen (tempString) + 1; | ||
657 | 656 | ||
658 | /* If the string is too large, suppress the leading space. */ | 657 | /* If the string is too large, suppress the leading space. */ |
658 | tempLength = strlen (tempString) + 1; | ||
659 | if (tempLength > len) { | 659 | if (tempLength > len) { |
660 | tempLength--; | 660 | tempLength--; |
661 | tempString++; | 661 | tempString++; |
@@ -677,10 +677,14 @@ static int putOctal (char *cp, int len, long value) | |||
677 | 677 | ||
678 | /* Write out a tar header for the specified file/directory/whatever */ | 678 | /* Write out a tar header for the specified file/directory/whatever */ |
679 | static int | 679 | static int |
680 | writeTarHeader(struct TarHeader *header, const char *fileName, struct stat *statbuf) | 680 | writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *statbuf) |
681 | { | 681 | { |
682 | //int i; | 682 | long chksum=0; |
683 | //long chksum, sum; | 683 | struct TarHeader header; |
684 | const unsigned char *cp = (const unsigned char *) &header; | ||
685 | ssize_t size = sizeof(struct TarHeader); | ||
686 | |||
687 | memset( &header, 0, size); | ||
684 | 688 | ||
685 | if (*fileName=='/') { | 689 | if (*fileName=='/') { |
686 | static int alreadyWarned=FALSE; | 690 | static int alreadyWarned=FALSE; |
@@ -688,66 +692,88 @@ writeTarHeader(struct TarHeader *header, const char *fileName, struct stat *stat | |||
688 | errorMsg("tar: Removing leading '/' from member names\n"); | 692 | errorMsg("tar: Removing leading '/' from member names\n"); |
689 | alreadyWarned=TRUE; | 693 | alreadyWarned=TRUE; |
690 | } | 694 | } |
691 | strcpy(header->name, fileName+1); | 695 | strcpy(header.name, fileName+1); |
692 | } | 696 | } |
693 | else { | 697 | else { |
694 | strcpy(header->name, fileName); | 698 | strcpy(header.name, fileName); |
695 | } | 699 | } |
696 | putOctal(header->mode, sizeof(header->mode), statbuf->st_mode & 0777); | 700 | putOctal(header.mode, sizeof(header.mode), statbuf->st_mode); |
697 | putOctal(header->uid, sizeof(header->uid), statbuf->st_uid); | 701 | putOctal(header.uid, sizeof(header.uid), statbuf->st_uid); |
698 | putOctal(header->gid, sizeof(header->gid), statbuf->st_gid); | 702 | putOctal(header.gid, sizeof(header.gid), statbuf->st_gid); |
699 | putOctal(header->size, sizeof(header->size), statbuf->st_size); | 703 | putOctal(header.size, sizeof(header.size), 0); /* Regular file size is handled later */ |
700 | putOctal(header->mtime, sizeof(header->mtime), statbuf->st_mtime); | 704 | putOctal(header.mtime, sizeof(header.mtime), statbuf->st_mtime); |
701 | 705 | strncpy(header.magic, TAR_MAGIC TAR_VERSION, | |
706 | TAR_MAGIC_LEN + TAR_VERSION_LEN ); | ||
707 | |||
708 | my_getpwuid(header.uname, statbuf->st_uid); | ||
709 | /* Put some sort of sane fallback in place... */ | ||
710 | if (! *header.uname) | ||
711 | strncpy(header.uname, "root", 5); | ||
712 | my_getgrgid(header.gname, statbuf->st_gid); | ||
713 | if (! *header.uname) | ||
714 | strncpy(header.uname, "root", 5); | ||
715 | |||
716 | // FIXME: (or most likely not) I break Hard Links | ||
702 | if (S_ISLNK(statbuf->st_mode)) { | 717 | if (S_ISLNK(statbuf->st_mode)) { |
703 | header->typeflag = LNKTYPE; | 718 | char buffer[BUFSIZ]; |
704 | // TODO -- Handle SYMTYPE | 719 | header.typeflag = SYMTYPE; |
720 | if ( readlink(fileName, buffer, sizeof(buffer) - 1) < 0) { | ||
721 | errorMsg("Error reading symlink '%s': %s\n", header.name, strerror(errno)); | ||
722 | return ( FALSE); | ||
723 | } | ||
724 | strncpy(header.linkname, buffer, sizeof(header.linkname)); | ||
705 | } else if (S_ISDIR(statbuf->st_mode)) { | 725 | } else if (S_ISDIR(statbuf->st_mode)) { |
706 | header->typeflag = DIRTYPE; | 726 | header.typeflag = DIRTYPE; |
707 | strncat(header->name, "/", sizeof(header->name)); | 727 | strncat(header.name, "/", sizeof(header.name)); |
708 | } else if (S_ISCHR(statbuf->st_mode)) { | 728 | } else if (S_ISCHR(statbuf->st_mode)) { |
709 | header->typeflag = CHRTYPE; | 729 | header.typeflag = CHRTYPE; |
710 | putOctal(header->devmajor, sizeof(header->devmajor), MAJOR(statbuf->st_rdev)); | 730 | putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev)); |
711 | putOctal(header->devminor, sizeof(header->devminor), MINOR(statbuf->st_rdev)); | 731 | putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev)); |
712 | } else if (S_ISBLK(statbuf->st_mode)) { | 732 | } else if (S_ISBLK(statbuf->st_mode)) { |
713 | header->typeflag = BLKTYPE; | 733 | header.typeflag = BLKTYPE; |
714 | putOctal(header->devmajor, sizeof(header->devmajor), MAJOR(statbuf->st_rdev)); | 734 | putOctal(header.devmajor, sizeof(header.devmajor), MAJOR(statbuf->st_rdev)); |
715 | putOctal(header->devminor, sizeof(header->devminor), MINOR(statbuf->st_rdev)); | 735 | putOctal(header.devminor, sizeof(header.devminor), MINOR(statbuf->st_rdev)); |
716 | } else if (S_ISFIFO(statbuf->st_mode)) { | 736 | } else if (S_ISFIFO(statbuf->st_mode)) { |
717 | header->typeflag = FIFOTYPE; | 737 | header.typeflag = FIFOTYPE; |
718 | } else if (S_ISLNK(statbuf->st_mode)) { | 738 | } else if (S_ISREG(statbuf->st_mode)) { |
719 | header->typeflag = LNKTYPE; | 739 | header.typeflag = REGTYPE; |
720 | } else if (S_ISLNK(statbuf->st_mode)) { | 740 | putOctal(header.size, sizeof(header.size), statbuf->st_size); |
721 | header->typeflag = REGTYPE; | ||
722 | } else { | 741 | } else { |
742 | errorMsg("tar: %s: Unknown file type\n", fileName); | ||
723 | return ( FALSE); | 743 | return ( FALSE); |
724 | } | 744 | } |
725 | return ( TRUE); | ||
726 | |||
727 | #if 0 | ||
728 | header->linkname = rawHeader->linkname; | ||
729 | header->devmajor = getOctal(rawHeader->devmajor, sizeof(rawHeader->devmajor)); | ||
730 | header->devminor = getOctal(rawHeader->devminor, sizeof(rawHeader->devminor)); | ||
731 | 745 | ||
732 | /* Write out the checksum */ | 746 | /* Calculate and store the checksum (i.e. the sum of all of the bytes of |
733 | chksum = getOctal(rawHeader->chksum, sizeof(rawHeader->chksum)); | 747 | * the header). The checksum field must be filled with blanks for the |
748 | * calculation. The checksum field is formatted differently from the | ||
749 | * other fields: it has [6] digits, a null, then a space -- rather than | ||
750 | * digits, followed by a null like the other fields... */ | ||
751 | memset(header.chksum, ' ', sizeof(header.chksum)); | ||
752 | cp = (const unsigned char *) &header; | ||
753 | while (size-- > 0) | ||
754 | chksum += *cp++; | ||
755 | putOctal(header.chksum, 7, chksum); | ||
756 | |||
757 | /* Now write the header out to disk */ | ||
758 | if ((size=fullWrite(tbInfo->tarFd, (char*)&header, sizeof(struct TarHeader))) < 0) { | ||
759 | errorMsg(io_error, fileName, strerror(errno)); | ||
760 | return ( FALSE); | ||
761 | } | ||
762 | /* Pad the header up to the tar block size */ | ||
763 | for (; size<TAR_BLOCK_SIZE; size++) { | ||
764 | write(tbInfo->tarFd, "\0", 1); | ||
765 | } | ||
766 | /* Now do the verbose thing (or not) */ | ||
767 | if (tbInfo->verboseFlag==TRUE) | ||
768 | fprintf(stdout, "%s\n", header.name); | ||
734 | 769 | ||
735 | return ( TRUE); | 770 | return ( TRUE); |
736 | #endif | ||
737 | } | 771 | } |
738 | 772 | ||
739 | 773 | ||
740 | static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) | 774 | static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) |
741 | { | 775 | { |
742 | int inputFileFd; | ||
743 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; | 776 | struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData; |
744 | char header[sizeof(struct TarHeader)]; | ||
745 | |||
746 | /* First open the file we want to archive, and make sure all is well */ | ||
747 | if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { | ||
748 | errorMsg("tar: %s: Cannot open: %s\n", fileName, strerror(errno)); | ||
749 | return( TRUE); | ||
750 | } | ||
751 | 777 | ||
752 | /* It is against the rules to archive a socket */ | 778 | /* It is against the rules to archive a socket */ |
753 | if (S_ISSOCK(statbuf->st_mode)) { | 779 | if (S_ISSOCK(statbuf->st_mode)) { |
@@ -764,13 +790,41 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* | |||
764 | return( TRUE); | 790 | return( TRUE); |
765 | } | 791 | } |
766 | 792 | ||
767 | memset( header, 0, sizeof(struct TarHeader)); | 793 | if (writeTarHeader(tbInfo, fileName, statbuf)==FALSE) { |
768 | if (writeTarHeader((struct TarHeader *)header, fileName, statbuf)==FALSE) { | 794 | return( FALSE); |
769 | dprintf(tbInfo->tarFd, "%s", header); | ||
770 | } | 795 | } |
771 | /* Now do the verbose thing (or not) */ | 796 | |
772 | if (tbInfo->verboseFlag==TRUE) | 797 | /* Now, if the file is a regular file, copy it out to the tarball */ |
773 | fprintf(stdout, "%s\n", ((struct TarHeader *)header)->name); | 798 | if (S_ISREG(statbuf->st_mode)) { |
799 | int inputFileFd; | ||
800 | char buffer[BUFSIZ]; | ||
801 | ssize_t size=0, readSize=0; | ||
802 | |||
803 | /* open the file we want to archive, and make sure all is well */ | ||
804 | if ((inputFileFd = open(fileName, O_RDONLY)) < 0) { | ||
805 | errorMsg("tar: %s: Cannot open: %s\n", fileName, strerror(errno)); | ||
806 | return( FALSE); | ||
807 | } | ||
808 | |||
809 | /* write the file to the archive */ | ||
810 | while ( (size = fullRead(inputFileFd, buffer, sizeof(buffer))) > 0 ) { | ||
811 | if (fullWrite(tbInfo->tarFd, buffer, size) != size ) { | ||
812 | /* Output file seems to have a problem */ | ||
813 | errorMsg(io_error, fileName, strerror(errno)); | ||
814 | return( FALSE); | ||
815 | } | ||
816 | readSize+=size; | ||
817 | } | ||
818 | if (size == -1) { | ||
819 | errorMsg(io_error, fileName, strerror(errno)); | ||
820 | return( FALSE); | ||
821 | } | ||
822 | /* Pad the file up to the tar block size */ | ||
823 | for (; (readSize%TAR_BLOCK_SIZE) != 0; readSize++) { | ||
824 | write(tbInfo->tarFd, "\0", 1); | ||
825 | } | ||
826 | close( inputFileFd); | ||
827 | } | ||
774 | 828 | ||
775 | return( TRUE); | 829 | return( TRUE); |
776 | } | 830 | } |
@@ -780,6 +834,7 @@ static int writeTarFile(const char* tarName, int tostdoutFlag, | |||
780 | { | 834 | { |
781 | int tarFd=-1; | 835 | int tarFd=-1; |
782 | int errorFlag=FALSE; | 836 | int errorFlag=FALSE; |
837 | ssize_t size; | ||
783 | //int skipFileFlag=FALSE; | 838 | //int skipFileFlag=FALSE; |
784 | struct TarBallInfo tbInfo; | 839 | struct TarBallInfo tbInfo; |
785 | tbInfo.verboseFlag = verboseFlag; | 840 | tbInfo.verboseFlag = verboseFlag; |
@@ -814,6 +869,10 @@ static int writeTarFile(const char* tarName, int tostdoutFlag, | |||
814 | errorFlag = TRUE; | 869 | errorFlag = TRUE; |
815 | } | 870 | } |
816 | } | 871 | } |
872 | /* Write two empty blocks to the end of the archive */ | ||
873 | for (size=0; size<(2*TAR_BLOCK_SIZE); size++) { | ||
874 | write(tbInfo.tarFd, "\0", 1); | ||
875 | } | ||
817 | /* Hang up the tools, close up shop, head home */ | 876 | /* Hang up the tools, close up shop, head home */ |
818 | close(tarFd); | 877 | close(tarFd); |
819 | if (errorFlag == TRUE) { | 878 | if (errorFlag == TRUE) { |