links, and no longer segfault in a certain wierd case.
* 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
#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"
#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
{
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. */
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;
{
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);
}
/* 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;
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)
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;
close(tarFd);
if (errorFlag == TRUE) {
errorMsg("Error exit delayed from previous errors\n");
+ freeHardLinkInfo(&tbInfo.hlInfoHead);
return(FALSE);
}
+ freeHardLinkInfo(&tbInfo.hlInfoHead);
return( TRUE);
}
#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
{
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. */
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;
{
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);
}
/* 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;
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)
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;
close(tarFd);
if (errorFlag == TRUE) {
errorMsg("Error exit delayed from previous errors\n");
+ freeHardLinkInfo(&tbInfo.hlInfoHead);
return(FALSE);
}
+ freeHardLinkInfo(&tbInfo.hlInfoHead);
return( TRUE);
}
#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"