cpio: fix a bug where we do not extract zero-sized hardlinks
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 5 Apr 2008 02:44:30 +0000 (02:44 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 5 Apr 2008 02:44:30 +0000 (02:44 -0000)
(spotted at http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=466771).
Add testsuite entry for that, and another one for another bug:
we do not list hardlinks in cpio -t (not fixed).

function                                             old     new   delta
get_header_cpio                                      884     909     +25
static.saved_hardlinks_created                         -       4      +4
static.pending_hardlinks                               4       -      -4
static.inode                                           4       -      -4
cpio_main                                           1122    1060     -62

TODO_config_nommu
archival/Config.in
archival/cpio.c
archival/libunarchive/data_extract_all.c
archival/libunarchive/get_header_cpio.c
archival/libunarchive/seek_by_jump.c
scripts/defconfig

index 42d17314a71ca893a25ef33c678790f77c07c3aa..7fdec69bd630fee8bbbe049bf04376e1742e27ce 100644 (file)
@@ -130,7 +130,6 @@ CONFIG_UNZIP=y
 #
 # Common options for cpio and tar
 #
-CONFIG_FEATURE_UNARCHIVE_TAPE=y
 
 #
 # Common options for dpkg and dpkg_deb
index 28450612ed70389a174d7bf0eafa062956fede6b..35ac40bb791885694ccf99e4ba68194e83cc58e4 100644 (file)
@@ -307,13 +307,6 @@ config UNZIP
 comment "Common options for cpio and tar"
        depends on CPIO || TAR
 
-config FEATURE_UNARCHIVE_TAPE
-       bool "Enable tape drive support"
-       default n
-       depends on CPIO || TAR
-       help
-         I don't think this is needed anymore.
-
 comment "Common options for dpkg and dpkg_deb"
        depends on DPKG || DPKG_DEB
 
index dd9ed756cc9a916a4cb6a68102d486f596b2130a..2919ff8def428e92d93d0175b8b2ba4b444c9163 100644 (file)
@@ -171,9 +171,7 @@ static int cpio_o(void)
                                int fd = xopen(name, O_RDONLY);
                                fflush(stdout);
                                /* We must abort if file got shorter too! */
-                               if (bb_copyfd_size(fd, STDOUT_FILENO, st.st_size) != st.st_size) {
-                                       bb_error_msg_and_die("I/O error or file '%s' was truncated", name);
-                               }
+                               bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size);
                                bytes += st.st_size;
                                close(fd);
                        }
index 4df9c09a7575f10f4ba615c7394a1818276a4e06..29a224bbce59e80b42dd3031693a2d0435823bf8 100644 (file)
@@ -20,7 +20,7 @@ void data_extract_all(archive_handle_t *archive_handle)
 
        /* Check if the file already exists */
        if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) {
-               /* Remove the existing entry if it exists */
+               /* Remove the entry if it exists */
                if (((file_header->mode & S_IFMT) != S_IFDIR)
                 && (unlink(file_header->name) == -1)
                 && (errno != ENOENT)
index 3f513551295ceab789f956292a7eef04e4f84c24..b97b53b204e06a9db2e3e0bb0ff024eab352388b 100644 (file)
@@ -8,70 +8,32 @@
 #include "unarchive.h"
 
 typedef struct hardlinks_s {
-       char *name;
-       int inode;
        struct hardlinks_s *next;
+       int inode; /* TODO: must match maj/min too! */
+       int mode ;
+       int mtime; /* These three are useful only in corner case */
+       int uid  ; /* of hardlinks with zero size body */
+       int gid  ;
+       char name[1];
 } hardlinks_t;
 
 char get_header_cpio(archive_handle_t *archive_handle)
 {
        static hardlinks_t *saved_hardlinks = NULL;
-       static unsigned pending_hardlinks = 0;
-       static int inode;
+       static hardlinks_t *saved_hardlinks_created = NULL;
 
        file_header_t *file_header = archive_handle->file_header;
        char cpio_header[110];
-       int namesize;
        char dummy[16];
-       int major, minor, nlink;
-
-       if (pending_hardlinks) { /* Deal with any pending hardlinks */
-               hardlinks_t *tmp, *oldtmp;
-
-               tmp = saved_hardlinks;
-               oldtmp = NULL;
-
-               file_header->link_target = file_header->name;
-               file_header->size = 0;
-
-               while (tmp) {
-                       if (tmp->inode != inode) {
-                               tmp = tmp->next;
-                               continue;
-                       }
-
-                       file_header->name = tmp->name;
-
-                       if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
-                               archive_handle->action_data(archive_handle);
-                               archive_handle->action_header(archive_handle->file_header);
-                       }
-
-                       pending_hardlinks--;
-
-                       oldtmp = tmp;
-                       tmp = tmp->next;
-                       free(oldtmp->name);
-                       free(oldtmp);
-                       if (oldtmp == saved_hardlinks)
-                               saved_hardlinks = tmp;
-               }
-
-               file_header->name = file_header->link_target;
-
-               if (pending_hardlinks > 1) {
-                       bb_error_msg("error resolving hardlink: archive made by GNU cpio 2.0-2.2?");
-               }
-
-               /* No more pending hardlinks, read next file entry */
-               pending_hardlinks = 0;
-       }
+       int namesize;
+       int major, minor, nlink, mode, inode;
+       unsigned size, uid, gid, mtime;
 
        /* There can be padding before archive header */
        data_align(archive_handle, 4);
 
        if (archive_xread_all_eof(archive_handle, (unsigned char*)cpio_header, 110) == 0) {
-               return EXIT_FAILURE;
+               goto create_hardlinks;
        }
        archive_handle->offset += 110;
 
@@ -81,17 +43,19 @@ char get_header_cpio(archive_handle_t *archive_handle)
                bb_error_msg_and_die("unsupported cpio format, use newc or crc");
        }
 
-       {
-               unsigned long tmpsize;
-               sscanf(cpio_header, "%6c%8x%8x%8x%8x%8x%8lx%8lx%16c%8x%8x%8x%8c",
-                   dummy, &inode, (unsigned int*)&file_header->mode,
-                   (unsigned int*)&file_header->uid, (unsigned int*)&file_header->gid,
-                   &nlink, &file_header->mtime, &tmpsize,
-                   dummy, &major, &minor, &namesize, dummy);
-               file_header->size = tmpsize;
-       }
+       sscanf(cpio_header + 6,
+                       "%8x" "%8x" "%8x" "%8x"
+                       "%8x" "%8x" "%8x" /*maj,min:*/ "%16c"
+                       /*rmaj,rmin:*/"%8x" "%8x" "%8x" /*chksum:*/ "%8c",
+                       &inode, &mode, &uid, &gid,
+                       &nlink, &mtime, &size, dummy,
+                       &major, &minor, &namesize, dummy);
+       file_header->mode = mode;
+       file_header->uid = uid;
+       file_header->gid = gid;
+       file_header->mtime = mtime;
+       file_header->size = size;
 
-       free(file_header->name);
        file_header->name = xzalloc(namesize + 1);
        /* Read in filename */
        xread(archive_handle->src_fd, file_header->name, namesize);
@@ -101,25 +65,9 @@ char get_header_cpio(archive_handle_t *archive_handle)
        data_align(archive_handle, 4);
 
        if (strcmp(file_header->name, "TRAILER!!!") == 0) {
-               /* Always round up */
-               printf("%d blocks\n", (int) (archive_handle->offset % 512 ?
-                                            archive_handle->offset / 512 + 1 :
-                                            archive_handle->offset / 512
-                                           ));
-               if (saved_hardlinks) { /* Bummer - we still have unresolved hardlinks */
-                       hardlinks_t *tmp = saved_hardlinks;
-                       hardlinks_t *oldtmp = NULL;
-                       while (tmp) {
-                               bb_error_msg("%s not created: cannot resolve hardlink", tmp->name);
-                               oldtmp = tmp;
-                               tmp = tmp->next;
-                               free(oldtmp->name);
-                               free(oldtmp);
-                       }
-                       saved_hardlinks = NULL;
-                       pending_hardlinks = 0;
-               }
-               return EXIT_FAILURE;
+               /* Always round up. ">> 9" divides by 512 */
+               printf("%"OFF_FMT"u blocks\n", (archive_handle->offset + 511) >> 9);
+               goto create_hardlinks;
        }
 
        if (S_ISLNK(file_header->mode)) {
@@ -130,25 +78,33 @@ char get_header_cpio(archive_handle_t *archive_handle)
        } else {
                file_header->link_target = NULL;
        }
-       if (nlink > 1 && !S_ISDIR(file_header->mode)) {
-               if (file_header->size == 0) { /* Put file on a linked list for later */
-                       hardlinks_t *new = xmalloc(sizeof(hardlinks_t));
+
+// TODO: data_extract_all can't deal with hardlinks to non-files...
+// (should be !S_ISDIR instead of S_ISREG here)
+
+       if (nlink > 1 && S_ISREG(file_header->mode)) {
+               hardlinks_t *new = xmalloc(sizeof(*new) + namesize);
+               new->inode = inode;
+               new->mode  = mode ;
+               new->mtime = mtime;
+               new->uid   = uid  ;
+               new->gid   = gid  ;
+               strcpy(new->name, file_header->name);
+               /* Put file on a linked list for later */
+               if (size == 0) {
                        new->next = saved_hardlinks;
-                       new->inode = inode;
-                       /* name current allocated, freed later */
-                       new->name = file_header->name;
-                       file_header->name = NULL;
                        saved_hardlinks = new;
                        return EXIT_SUCCESS; /* Skip this one */
+                       /* TODO: this breaks cpio -t (it does not show hardlinks) */
                }
-               /* Found the file with data in */
-               pending_hardlinks = nlink;
+               new->next = saved_hardlinks_created;
+               saved_hardlinks_created = new;
        }
        file_header->device = makedev(major, minor);
 
        if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) {
                archive_handle->action_data(archive_handle);
-               archive_handle->action_header(archive_handle->file_header);
+               archive_handle->action_header(file_header);
        } else {
                data_skip(archive_handle);
        }
@@ -156,6 +112,57 @@ char get_header_cpio(archive_handle_t *archive_handle)
        archive_handle->offset += file_header->size;
 
        free(file_header->link_target);
+       free(file_header->name);
+       file_header->link_target = NULL;
+       file_header->name = NULL;
 
        return EXIT_SUCCESS;
+
+ create_hardlinks:
+       free(file_header->link_target);
+       free(file_header->name);
+
+       while (saved_hardlinks) {
+               hardlinks_t *cur;
+               hardlinks_t *make_me = saved_hardlinks;
+               saved_hardlinks = make_me->next;
+
+               memset(file_header, 0, sizeof(*file_header));
+               file_header->name = make_me->name;
+               file_header->mode = make_me->mode;
+               /*file_header->size = 0;*/
+
+               /* Try to find a file we are hardlinked to */
+               cur = saved_hardlinks_created;
+               while (cur) {
+                       /* TODO: must match maj/min too! */
+                       if (cur->inode == make_me->inode) {
+                               file_header->link_target = cur->name;
+                                /* link_target != NULL, size = 0: "I am a hardlink" */
+                               if (archive_handle->filter(archive_handle) == EXIT_SUCCESS)
+                                       archive_handle->action_data(archive_handle);
+                               free(make_me);
+                               goto next_link;
+                       }
+               }
+               /* Oops... no file with such inode was created... do it now
+                * (happens when hardlinked files are empty (zero length)) */
+               file_header->mtime = make_me->mtime;
+               file_header->uid   = make_me->uid  ;
+               file_header->gid   = make_me->gid  ;
+               if (archive_handle->filter(archive_handle) == EXIT_SUCCESS)
+                       archive_handle->action_data(archive_handle);
+               /* Move to the list of created hardlinked files */
+               make_me->next = saved_hardlinks_created;
+               saved_hardlinks_created = make_me;
+ next_link: ;
+       }
+
+       while (saved_hardlinks_created) {
+               hardlinks_t *p = saved_hardlinks_created;
+               saved_hardlinks_created = p->next;
+               free(p);
+       }
+
+       return EXIT_FAILURE; /* "No more files to process" */
 }
index 8b5f3e88760c8700ab2966b1883e0e184234b30f..5288c1d73657714be031a6abced054eede3876f4 100644 (file)
@@ -9,11 +9,9 @@
 void seek_by_jump(const archive_handle_t *archive_handle, unsigned amount)
 {
        if (lseek(archive_handle->src_fd, (off_t) amount, SEEK_CUR) == (off_t) -1) {
-#if ENABLE_FEATURE_UNARCHIVE_TAPE
-               if (errno == ESPIPE) {
+               if (errno == ESPIPE)
                        seek_by_read(archive_handle, amount);
-               } else
-#endif
+               else
                        bb_perror_msg_and_die("seek failure");
        }
 }
index c24d852423566b583cf3b7ce93030c0d2ccdbd37..26cc8d25d3af96af27377ce5833f2cec133b0aee 100644 (file)
@@ -130,7 +130,6 @@ CONFIG_UNZIP=y
 #
 # Common options for cpio and tar
 #
-CONFIG_FEATURE_UNARCHIVE_TAPE=y
 # CONFIG_FEATURE_DEB_TAR_GZ is not set
 # CONFIG_FEATURE_DEB_TAR_BZ2 is not set
 # CONFIG_FEATURE_DEB_TAR_LZMA is not set