Added support for hard links.
authorewt <devnull@localhost>
Tue, 6 May 1997 17:38:05 +0000 (17:38 +0000)
committerewt <devnull@localhost>
Tue, 6 May 1997 17:38:05 +0000 (17:38 +0000)
CVS patchset: 1597
CVS date: 1997/05/06 17:38:05

lib/cpio.c
lib/cpio.h

index 7bc5541..1c3b7d6 100644 (file)
@@ -32,6 +32,16 @@ struct ourfd {
     int pos;
 };
 
+struct hardLink {
+    char ** files;             /* there are nlink of these */
+    dev_t dev;
+    ino_t inode;
+    int nlink;                 
+    int linksLeft;
+    int createdPath;
+    struct hardLink * next;
+};
+
 struct cpioCrcPhysicalHeader {
     char magic[6];
     char inode[8];
@@ -336,21 +346,63 @@ static int expandDevice(struct ourfd * fd, struct cpioHeader * hdr) {
     return 0;
 }
 
+static void freeLink(struct hardLink * li) {
+    int i;
+
+    for (i = 0; i < li->nlink; i++) {
+       if (li->files[i]) free(li->files[i]);
+    }
+    free(li->files);
+}
+
+static int createLinks(struct hardLink * li, char ** failedFile) {
+    int i;
+    struct stat sb;
+
+    for (i = 0; i < li->nlink; i++) {
+       if (i == li->createdPath) continue;
+       if (!li->files[i]) continue;
+
+       if (!lstat(li->files[i], &sb)) {
+           if (unlink(li->files[i])) {
+               *failedFile = strdup(li->files[i]);
+               return CPIO_UNLINK_FAILED;
+           }
+       }
+
+       if (link(li->files[li->createdPath], li->files[i])) {
+           *failedFile = strdup(li->files[i]);
+           return CPIO_LINK_FAILED;
+       }
+
+       free(li->files[i]);
+       li->files[i] = NULL;
+       li->linksLeft--;
+    }
+
+    return 0;
+}
+
 int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings, 
                       int numMappings, cpioCallback cb, void * cbData,
                       char ** failedFile) {
     struct cpioHeader ch;
     struct ourfd fd;
     int rc = 0;
+    int linkNum = 0;
     struct cpioFileMapping * map = NULL;
     struct cpioFileMapping needle;
     mode_t cpioMode;
     int olderr;
     struct cpioCallbackInfo cbInfo;
+    struct hardLink * links = NULL;
+    struct hardLink * li = NULL;
 
     fd.fd = stream;
     fd.pos = 0;
 
+    *failedFile = NULL;
+
     do {
        if ((rc = getNextHeader(&fd, &ch))) {
            printf("error %d reading header: %s\n", rc, strerror(errno));
@@ -385,31 +437,70 @@ int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings,
                    ch.gid = map->finalGid;
            }
 
-           rc = checkDirectory(ch.path);
-
-           if (!rc) {
-               if (S_ISREG(ch.mode))
-                   rc = expandRegular(&fd, &ch, cb, cbData);
-               else if (S_ISDIR(ch.mode))
-                   rc = createDirectory(ch.path);
-               else if (S_ISLNK(ch.mode))
-                   rc = expandSymlink(&fd, &ch);
-               else if (S_ISFIFO(ch.mode))
-                   rc = expandFifo(&fd, &ch);
-               else if (S_ISCHR(ch.mode) || S_ISBLK(ch.mode))
-                   rc = expandDevice(&fd, &ch);
-               else if (S_ISSOCK(ch.mode)) {
-                   /* should we do something here??? */
-                   rc = 0;
-               } else {
-                   rc = CPIO_INTERNAL;
+           /* This won't get hard linked symlinks right, but I can't seem 
+              to create those anyway */
+
+           if (ch.nlink > 1) {
+               li = links;
+               for (li = links; li; li = li->next) {
+                   if (li->inode == ch.inode && li->dev == ch.dev) break;
+               }
+
+               if (!li) {
+                   li = malloc(sizeof(*li));
+                   li->inode = ch.inode;
+                   li->dev = ch.dev;
+                   li->nlink = ch.nlink;
+                   li->linksLeft = ch.nlink;
+                   li->createdPath = -1;
+                   li->files = calloc(sizeof(char *), li->nlink);
+                   li->next = links;
+                   links = li;
                }
+
+               for (linkNum = 0; linkNum < li->nlink; linkNum++)
+                   if (!li->files[linkNum]) break;
+               li->files[linkNum] = strdup(ch.path);
            }
+               
+           if ((ch.nlink > 1) && S_ISREG(ch.mode) && !ch.size &&
+               li->createdPath == -1) {
+               /* defer file creation */
+           } else if ((ch.nlink > 1) && (li->createdPath != -1)) {
+               createLinks(li, failedFile);
+           } else {
+               rc = checkDirectory(ch.path);
+
+               if (!rc) {
+                   if (S_ISREG(ch.mode))
+                       rc = expandRegular(&fd, &ch, cb, cbData);
+                   else if (S_ISDIR(ch.mode))
+                       rc = createDirectory(ch.path);
+                   else if (S_ISLNK(ch.mode))
+                       rc = expandSymlink(&fd, &ch);
+                   else if (S_ISFIFO(ch.mode))
+                       rc = expandFifo(&fd, &ch);
+                   else if (S_ISCHR(ch.mode) || S_ISBLK(ch.mode))
+                       rc = expandDevice(&fd, &ch);
+                   else if (S_ISSOCK(ch.mode)) {
+                       /* this mimicks cpio but probably isnt' right */
+                       rc = expandFifo(&fd, &ch);
+                   } else {
+                       rc = CPIO_INTERNAL;
+                   }
+               }
+
+               if (!rc)
+                   rc = setInfo(&ch);
 
-           if (!rc)
-               rc = setInfo(&ch);
+               if (ch.nlink > 1) {
+                   li->createdPath = linkNum;
+                   li->linksLeft--;
+                   rc = createLinks(li, failedFile);
+               }
+           }
 
-           if (rc) {
+           if (rc && !*failedFile) {
                *failedFile = strdup(ch.path);
 
                olderr = errno;
@@ -431,5 +522,31 @@ int cpioInstallArchive(gzFile stream, struct cpioFileMapping * mappings,
        free(ch.path);
     } while (1 && !rc);
 
+    li = links;
+    while (li && !rc) {
+       if (li->linksLeft) {
+           if (li->createdPath == -1)
+               rc = CPIO_INTERNAL;
+           else 
+               rc = createLinks(li, failedFile);
+       }
+
+       freeLink(li);
+
+       links = li;
+       li = li->next;
+       free(links);
+       links = li;
+    }
+
+    li = links;
+    /* if an error got us here links will still be eating some memory */
+    while (li) {
+       freeLink(li);
+       links = li;
+       li = li->next;
+       free(links);
+    }
+
     return rc;
 }
index 1f3b1e7..a34299d 100644 (file)
@@ -20,6 +20,7 @@
 #define CPIO_MKDIR_FAILED      (-13  | CPIO_CHECK_ERRNO)
 #define CPIO_MKNOD_FAILED      (-14  | CPIO_CHECK_ERRNO)
 #define CPIO_MKFIFO_FAILED     (-15  | CPIO_CHECK_ERRNO)
+#define CPIO_LINK_FAILED       (-16  | CPIO_CHECK_ERRNO)
 
 /* Don't think this behaves just like standard cpio. It's pretty close, but
    it has some behaviors which are more to RPM's liking. I tried to document