aboutsummaryrefslogtreecommitdiff
path: root/tar.c
diff options
context:
space:
mode:
Diffstat (limited to 'tar.c')
-rw-r--r--tar.c179
1 files changed, 119 insertions, 60 deletions
diff --git a/tar.c b/tar.c
index f9b3e1813..979821ec0 100644
--- a/tar.c
+++ b/tar.c
@@ -56,7 +56,8 @@
56 56
57static const char tar_usage[] = 57static 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;
644static int putOctal (char *cp, int len, long value) 647static 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 */
679static int 679static int
680writeTarHeader(struct TarHeader *header, const char *fileName, struct stat *statbuf) 680writeTarHeader(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
740static int writeFileToTarball(const char *fileName, struct stat *statbuf, void* userData) 774static 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) {