From 3d957c87b7525c3bfefddfc9aa2c24f04995275d Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Thu, 7 Dec 2000 00:34:58 +0000 Subject: [PATCH] Patch from Jim Gleason to fix tar so it no longer breaks hard links, and no longer segfault in a certain wierd case. --- Changelog | 2 + applets/usage.c | 3 +- archival/tar.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++-- tar.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++-- usage.c | 3 +- 5 files changed, 192 insertions(+), 10 deletions(-) diff --git a/Changelog b/Changelog index 83ca4ad87..90212119f 100644 --- 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 -- fixed tar so it no longer breaks + hard links. -Erik Andersen diff --git a/applets/usage.c b/applets/usage.c index bd2321fbc..35d69df42 100644 --- a/applets/usage.c +++ b/applets/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" diff --git a/archival/tar.c b/archival/tar.c index 7e56fb99a..906fd7eda 100644 --- a/archival/tar.c +++ b/archival/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/tar.c b/tar.c index 7e56fb99a..906fd7eda 100644 --- 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 bd2321fbc..35d69df42 100644 --- 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" -- 2.25.1