| @@ -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" */ |
| } |