Patch from Jim Gleason <jimg@lineo.com> to fix tar so it no longer breaks hard
authorEric Andersen <andersen@codepoet.org>
Thu, 7 Dec 2000 00:34:58 +0000 (00:34 -0000)
committerEric Andersen <andersen@codepoet.org>
Thu, 7 Dec 2000 00:34:58 +0000 (00:34 -0000)
links, and no longer segfault in a certain wierd case.

Changelog
applets/usage.c
archival/tar.c
tar.c
usage.c

index 83ca4ad87159659f54f998392317e82d2fc0513f..90212119fabb2b4357dad2858148ce61947760eb 100644 (file)
--- a/Changelog
+++ b/Changelog
@@ -43,6 +43,8 @@
        * Larry Doolittle -- \r handled now in echo and tr
        * Matt Kraai -- rewrite of uniq
        * Mark Whitley -- remix of xargs
+       * Jim Gleason <jimg@lineo.com> -- fixed tar so it no longer breaks 
+           hard links.
 
 
         -Erik Andersen
index bd2321fbce3327cb2cc0872f26b06381218d02ef..35d69df42347c1871c818256a30d8deb0c51dc8d 100644 (file)
@@ -1222,8 +1222,7 @@ const char tar_usage[] =
 #endif
        "[-f tarFile] [FILE(s)] ...\n"
 #ifndef BB_FEATURE_TRIVIAL_HELP
-       "\nCreate, extract, or list files from a tar file.  Note that\n"
-       "this version of tar treats hard links as separate files.\n\n"
+       "\nCreate, extract, or list files from a tar file.\n\n"
        "Main operation mode:\n"
 #ifdef BB_FEATURE_TAR_CREATE
        "\tc\t\tcreate\n"
index 7e56fb99ab848f77eeeafdf74bdbadde41e3030e..906fd7eda014e7475a9d1df9f4f774d84da11887 100644 (file)
@@ -769,6 +769,21 @@ endgame:
 
 #ifdef BB_FEATURE_TAR_CREATE
 
+/*
+** writeTarFile(),  writeFileToTarball(), and writeTarHeader() are
+** the only functions that deal with the HardLinkInfo structure.
+** Even these functions use the xxxHardLinkInfo() functions.
+*/
+typedef struct HardLinkInfo HardLinkInfo;
+struct HardLinkInfo
+{
+       HardLinkInfo *next;           /* Next entry in list */
+       dev_t dev;                    /* Device number */
+       ino_t ino;                    /* Inode number */
+       short linkCount;              /* (Hard) Link Count */
+       char name[1];                 /* Start of filename (must be last) */
+};
+
 /* Some info to be carried along when creating a new tarball */
 struct TarBallInfo
 {
@@ -781,10 +796,62 @@ struct TarBallInfo
                                                                         to include the tarball into itself */
        int verboseFlag;              /* Whether to print extra stuff or not */
        char** excludeList;           /* List of files to not include */
+       HardLinkInfo *hlInfoHead;     /* Hard Link Tracking Information */
+       HardLinkInfo *hlInfo;         /* Hard Link Info for the current file */
 };
 typedef struct TarBallInfo TarBallInfo;
 
 
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static void
+addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino,
+               short linkCount, const char *name)
+{
+       /* Note: hlInfoHeadPtr can never be NULL! */
+       HardLinkInfo *hlInfo;
+
+       hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1);
+       if (hlInfo) {
+               hlInfo->next = *hlInfoHeadPtr;
+               *hlInfoHeadPtr = hlInfo;
+               hlInfo->dev = dev;
+               hlInfo->ino = ino;
+               hlInfo->linkCount = linkCount;
+               strcpy(hlInfo->name, name);
+       }
+       return;
+}
+
+static void
+freeHardLinkInfo (HardLinkInfo **hlInfoHeadPtr)
+{
+       HardLinkInfo *hlInfo = NULL;
+       HardLinkInfo *hlInfoNext = NULL;
+
+       if (hlInfoHeadPtr) {
+               hlInfo = *hlInfoHeadPtr;
+               while (hlInfo) {
+                       hlInfoNext = hlInfo->next;
+                       free(hlInfo);
+                       hlInfo = hlInfoNext;
+               }
+               *hlInfoHeadPtr = NULL;
+       }
+       return;
+}
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static HardLinkInfo *
+findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino)
+{
+       while(hlInfo) {
+               if ((ino == hlInfo->ino) && (dev == hlInfo->dev))
+                       break;
+               hlInfo = hlInfo->next;
+       }
+       return(hlInfo);
+}
+
 /* Put an octal string into the specified buffer.
  * The number is zero and space padded and possibly null padded.
  * Returns TRUE if successful.  */ 
@@ -879,8 +946,11 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st
        if (! *header.uname)
                strcpy(header.uname, "root");
 
-       /* WARNING/NOTICE: I break Hard Links */
-       if (S_ISLNK(statbuf->st_mode)) {
+       if (tbInfo->hlInfo) {
+               /* This is a hard link */
+               header.typeflag = LNKTYPE;
+               strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname));
+       } else if (S_ISLNK(statbuf->st_mode)) {
                int link_size=0;
                char buffer[BUFSIZ];
                header.typeflag  = SYMTYPE;
@@ -948,6 +1018,22 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void*
 {
        struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData;
 
+       /*
+       ** Check to see if we are dealing with a hard link.
+       ** If so -
+       ** Treat the first occurance of a given dev/inode as a file while
+       ** treating any additional occurances as hard links.  This is done
+       ** by adding the file information to the HardLinkInfo linked list.
+       */
+       tbInfo->hlInfo = NULL;
+       if (statbuf->st_nlink > 1) {
+               tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf->st_dev, 
+                               statbuf->st_ino);
+               if (tbInfo->hlInfo == NULL)
+                       addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev,
+                                       statbuf->st_ino, statbuf->st_nlink, fileName);
+       }
+
        /* It is against the rules to archive a socket */
        if (S_ISSOCK(statbuf->st_mode)) {
                errorMsg("%s: socket ignored\n", fileName);
@@ -973,7 +1059,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void*
        } 
 
        /* Now, if the file is a regular file, copy it out to the tarball */
-       if (S_ISREG(statbuf->st_mode)) {
+       if ((tbInfo->hlInfo == NULL)
+       &&  (S_ISREG(statbuf->st_mode))) {
                int  inputFileFd;
                char buffer[BUFSIZ];
                ssize_t size=0, readSize=0;
@@ -1015,6 +1102,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
        ssize_t size;
        struct TarBallInfo tbInfo;
        tbInfo.verboseFlag = verboseFlag;
+       tbInfo.hlInfoHead = NULL;
 
        /* Make sure there is at least one file to tar up.  */
        if (*argv == NULL)
@@ -1027,6 +1115,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
                tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (tbInfo.tarFd < 0) {
                errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno));
+               freeHardLinkInfo(&tbInfo.hlInfoHead);
                return ( FALSE);
        }
        tbInfo.excludeList=excludeList;
@@ -1061,8 +1150,10 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
        close(tarFd);
        if (errorFlag == TRUE) {
                errorMsg("Error exit delayed from previous errors\n");
+               freeHardLinkInfo(&tbInfo.hlInfoHead);
                return(FALSE);
        }
+       freeHardLinkInfo(&tbInfo.hlInfoHead);
        return( TRUE);
 }
 
diff --git a/tar.c b/tar.c
index 7e56fb99ab848f77eeeafdf74bdbadde41e3030e..906fd7eda014e7475a9d1df9f4f774d84da11887 100644 (file)
--- a/tar.c
+++ b/tar.c
@@ -769,6 +769,21 @@ endgame:
 
 #ifdef BB_FEATURE_TAR_CREATE
 
+/*
+** writeTarFile(),  writeFileToTarball(), and writeTarHeader() are
+** the only functions that deal with the HardLinkInfo structure.
+** Even these functions use the xxxHardLinkInfo() functions.
+*/
+typedef struct HardLinkInfo HardLinkInfo;
+struct HardLinkInfo
+{
+       HardLinkInfo *next;           /* Next entry in list */
+       dev_t dev;                    /* Device number */
+       ino_t ino;                    /* Inode number */
+       short linkCount;              /* (Hard) Link Count */
+       char name[1];                 /* Start of filename (must be last) */
+};
+
 /* Some info to be carried along when creating a new tarball */
 struct TarBallInfo
 {
@@ -781,10 +796,62 @@ struct TarBallInfo
                                                                         to include the tarball into itself */
        int verboseFlag;              /* Whether to print extra stuff or not */
        char** excludeList;           /* List of files to not include */
+       HardLinkInfo *hlInfoHead;     /* Hard Link Tracking Information */
+       HardLinkInfo *hlInfo;         /* Hard Link Info for the current file */
 };
 typedef struct TarBallInfo TarBallInfo;
 
 
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static void
+addHardLinkInfo (HardLinkInfo **hlInfoHeadPtr, dev_t dev, ino_t ino,
+               short linkCount, const char *name)
+{
+       /* Note: hlInfoHeadPtr can never be NULL! */
+       HardLinkInfo *hlInfo;
+
+       hlInfo = (HardLinkInfo *)xmalloc(sizeof(HardLinkInfo)+strlen(name)+1);
+       if (hlInfo) {
+               hlInfo->next = *hlInfoHeadPtr;
+               *hlInfoHeadPtr = hlInfo;
+               hlInfo->dev = dev;
+               hlInfo->ino = ino;
+               hlInfo->linkCount = linkCount;
+               strcpy(hlInfo->name, name);
+       }
+       return;
+}
+
+static void
+freeHardLinkInfo (HardLinkInfo **hlInfoHeadPtr)
+{
+       HardLinkInfo *hlInfo = NULL;
+       HardLinkInfo *hlInfoNext = NULL;
+
+       if (hlInfoHeadPtr) {
+               hlInfo = *hlInfoHeadPtr;
+               while (hlInfo) {
+                       hlInfoNext = hlInfo->next;
+                       free(hlInfo);
+                       hlInfo = hlInfoNext;
+               }
+               *hlInfoHeadPtr = NULL;
+       }
+       return;
+}
+
+/* Might be faster (and bigger) if the dev/ino were stored in numeric order;) */
+static HardLinkInfo *
+findHardLinkInfo (HardLinkInfo *hlInfo, dev_t dev, ino_t ino)
+{
+       while(hlInfo) {
+               if ((ino == hlInfo->ino) && (dev == hlInfo->dev))
+                       break;
+               hlInfo = hlInfo->next;
+       }
+       return(hlInfo);
+}
+
 /* Put an octal string into the specified buffer.
  * The number is zero and space padded and possibly null padded.
  * Returns TRUE if successful.  */ 
@@ -879,8 +946,11 @@ writeTarHeader(struct TarBallInfo *tbInfo, const char *fileName, struct stat *st
        if (! *header.uname)
                strcpy(header.uname, "root");
 
-       /* WARNING/NOTICE: I break Hard Links */
-       if (S_ISLNK(statbuf->st_mode)) {
+       if (tbInfo->hlInfo) {
+               /* This is a hard link */
+               header.typeflag = LNKTYPE;
+               strncpy(header.linkname, tbInfo->hlInfo->name, sizeof(header.linkname));
+       } else if (S_ISLNK(statbuf->st_mode)) {
                int link_size=0;
                char buffer[BUFSIZ];
                header.typeflag  = SYMTYPE;
@@ -948,6 +1018,22 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void*
 {
        struct TarBallInfo *tbInfo = (struct TarBallInfo *)userData;
 
+       /*
+       ** Check to see if we are dealing with a hard link.
+       ** If so -
+       ** Treat the first occurance of a given dev/inode as a file while
+       ** treating any additional occurances as hard links.  This is done
+       ** by adding the file information to the HardLinkInfo linked list.
+       */
+       tbInfo->hlInfo = NULL;
+       if (statbuf->st_nlink > 1) {
+               tbInfo->hlInfo = findHardLinkInfo(tbInfo->hlInfoHead, statbuf->st_dev, 
+                               statbuf->st_ino);
+               if (tbInfo->hlInfo == NULL)
+                       addHardLinkInfo (&tbInfo->hlInfoHead, statbuf->st_dev,
+                                       statbuf->st_ino, statbuf->st_nlink, fileName);
+       }
+
        /* It is against the rules to archive a socket */
        if (S_ISSOCK(statbuf->st_mode)) {
                errorMsg("%s: socket ignored\n", fileName);
@@ -973,7 +1059,8 @@ static int writeFileToTarball(const char *fileName, struct stat *statbuf, void*
        } 
 
        /* Now, if the file is a regular file, copy it out to the tarball */
-       if (S_ISREG(statbuf->st_mode)) {
+       if ((tbInfo->hlInfo == NULL)
+       &&  (S_ISREG(statbuf->st_mode))) {
                int  inputFileFd;
                char buffer[BUFSIZ];
                ssize_t size=0, readSize=0;
@@ -1015,6 +1102,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
        ssize_t size;
        struct TarBallInfo tbInfo;
        tbInfo.verboseFlag = verboseFlag;
+       tbInfo.hlInfoHead = NULL;
 
        /* Make sure there is at least one file to tar up.  */
        if (*argv == NULL)
@@ -1027,6 +1115,7 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
                tbInfo.tarFd = open (tarName, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (tbInfo.tarFd < 0) {
                errorMsg( "Error opening '%s': %s\n", tarName, strerror(errno));
+               freeHardLinkInfo(&tbInfo.hlInfoHead);
                return ( FALSE);
        }
        tbInfo.excludeList=excludeList;
@@ -1061,8 +1150,10 @@ static int writeTarFile(const char* tarName, int verboseFlag, char **argv,
        close(tarFd);
        if (errorFlag == TRUE) {
                errorMsg("Error exit delayed from previous errors\n");
+               freeHardLinkInfo(&tbInfo.hlInfoHead);
                return(FALSE);
        }
+       freeHardLinkInfo(&tbInfo.hlInfoHead);
        return( TRUE);
 }
 
diff --git a/usage.c b/usage.c
index bd2321fbce3327cb2cc0872f26b06381218d02ef..35d69df42347c1871c818256a30d8deb0c51dc8d 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -1222,8 +1222,7 @@ const char tar_usage[] =
 #endif
        "[-f tarFile] [FILE(s)] ...\n"
 #ifndef BB_FEATURE_TRIVIAL_HELP
-       "\nCreate, extract, or list files from a tar file.  Note that\n"
-       "this version of tar treats hard links as separate files.\n\n"
+       "\nCreate, extract, or list files from a tar file.\n\n"
        "Main operation mode:\n"
 #ifdef BB_FEATURE_TAR_CREATE
        "\tc\t\tcreate\n"