extern int unzip_main(int argc, char **argv) { union { unsigned char raw[26]; struct { unsigned short version; /* 0-1 */ unsigned short flags; /* 2-3 */ unsigned short method; /* 4-5 */ unsigned short modtime; /* 6-7 */ unsigned short moddate; /* 8-9 */ unsigned int crc32 __attribute__ ((packed)); /* 10-13 */ unsigned int cmpsize __attribute__ ((packed));; /* 14-17 */ unsigned int ucmpsize __attribute__ ((packed));; /* 18-21 */ unsigned short filename_len; /* 22-23 */ unsigned short extra_len; /* 24-25 */ } formated __attribute__ ((packed)); } zip_header; archive_handle_t *archive_handle; unsigned int total_size = 0; unsigned int total_entries = 0; char *base_dir = NULL; int opt = 0; /* Initialise */ archive_handle = init_handle(); archive_handle->action_data = NULL; archive_handle->action_header = header_list_unzip; while ((opt = getopt(argc, argv, "lnopqd:")) != -1) { switch (opt) { case 'l': /* list */ archive_handle->action_header = header_verbose_list_unzip; archive_handle->action_data = data_skip; break; case 'n': /* never overwright existing files */ break; case 'o': archive_handle->flags = ARCHIVE_EXTRACT_UNCONDITIONAL; break; case 'p': /* extract files to stdout */ archive_handle->action_data = data_extract_to_stdout; break; case 'q': /* Extract files quietly */ archive_handle->action_header = header_skip; break; case 'd': /* Extract files to specified base directory*/ base_dir = optarg; break; #if 0 case 'x': /* Exclude the specified files */ archive_handle->filter = filter_accept_reject_list; break; #endif default: bb_show_usage(); } } if (argc == optind) { bb_show_usage(); } printf("Archive: %s\n", argv[optind]); if (archive_handle->action_header == header_verbose_list_unzip) { printf(" Length Date Time Name\n"); printf(" -------- ---- ---- ----\n"); } if (*argv[optind] == '-') { archive_handle->src_fd = fileno(stdin); archive_handle->seek = seek_by_char; } else { archive_handle->src_fd = bb_xopen(argv[optind++], O_RDONLY); } if ((base_dir) && (chdir(base_dir))) { bb_perror_msg_and_die("Couldnt chdir"); } while (optind < argc) { archive_handle->filter = filter_accept_list; archive_handle->accept = llist_add_to(archive_handle->accept, argv[optind]); optind++; } while (1) { unsigned int magic; int dst_fd; /* TODO Endian issues */ archive_xread_all(archive_handle, &magic, 4); archive_handle->offset += 4; if (magic == ZIP_CDS_MAGIC) { break; } else if (magic != ZIP_FILEHEADER_MAGIC) { bb_error_msg_and_die("Invlaide zip magic"); } /* Read the file header */ archive_xread_all(archive_handle, zip_header.raw, 26); archive_handle->offset += 26; archive_handle->file_header->mode = S_IFREG | 0777; if (zip_header.formated.method != 8) { bb_error_msg_and_die("Unsupported compression method %d\n", zip_header.formated.method); } /* Read filename */ archive_handle->file_header->name = xmalloc(zip_header.formated.filename_len + 1); archive_xread_all(archive_handle, archive_handle->file_header->name, zip_header.formated.filename_len); archive_handle->offset += zip_header.formated.filename_len; archive_handle->file_header->name[zip_header.formated.filename_len] = '\0'; /* Skip extra header bits */ archive_handle->file_header->size = zip_header.formated.extra_len; data_skip(archive_handle); archive_handle->offset += zip_header.formated.extra_len; /* Handle directories */ archive_handle->file_header->mode = S_IFREG | 0777; if (last_char_is(archive_handle->file_header->name, '/')) { archive_handle->file_header->mode ^= S_IFREG; archive_handle->file_header->mode |= S_IFDIR; } /* Data section */ archive_handle->file_header->size = zip_header.formated.cmpsize; if (archive_handle->action_data) { archive_handle->action_data(archive_handle); } else { dst_fd = bb_xopen(archive_handle->file_header->name, O_WRONLY | O_CREAT); inflate(archive_handle->src_fd, dst_fd); close(dst_fd); chmod(archive_handle->file_header->name, archive_handle->file_header->mode); /* Validate decompression - crc */ if (zip_header.formated.crc32 != (gunzip_crc ^ 0xffffffffL)) { bb_error_msg("Invalid compressed data--crc error"); } /* Validate decompression - size */ if (gunzip_bytes_out != zip_header.formated.ucmpsize) { bb_error_msg("Invalid compressed data--length error"); } } /* local file descriptor section */ archive_handle->offset += zip_header.formated.cmpsize; /* This ISNT unix time */ archive_handle->file_header->mtime = zip_header.formated.modtime | (zip_header.formated.moddate << 16); archive_handle->file_header->size = zip_header.formated.ucmpsize; total_size += archive_handle->file_header->size; total_entries++; archive_handle->action_header(archive_handle->file_header); /* Data descriptor section */ if (zip_header.formated.flags & 4) { /* skip over duplicate crc, compressed size and uncompressed size */ unsigned short i; for (i = 0; i != 12; i++) { archive_xread_char(archive_handle); } archive_handle->offset += 12; } } /* Central directory section */ if (archive_handle->action_header == header_verbose_list_unzip) { printf(" -------- -------\n"); printf("%9d %d files\n", total_size, total_entries); } return(EXIT_SUCCESS); }
extern char get_header_tar(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; union { unsigned char raw[512]; struct { char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } formated; } tar; long sum = 0; long i; char *tmp; /* Align header */ data_align(archive_handle, 512); if (archive_xread(archive_handle, tar.raw, 512) != 512) { /* Assume end of file */ return(EXIT_FAILURE); } archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.formated.name[0] == 0) { return(EXIT_SUCCESS); } /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.formated.magic, "ustar", 5) != 0) { #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("Invalid tar magic"); } /* Do checksum on headers */ for (i = 0; i < 148 ; i++) { sum += tar.raw[i]; } sum += ' ' * 8; for (i = 156; i < 512 ; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { bb_error_msg("Invalid tar header checksum"); return(EXIT_FAILURE); } #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } else if (linkname) { file_header->name = linkname; linkname = NULL; } else #endif if (tar.formated.prefix[0] == 0) { file_header->name = strdup(tar.formated.name); } else { file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); } tmp = last_char_is(archive_handle->file_header->name, '/'); if (tmp) { *tmp = '\0'; } file_header->mode = strtol(tar.formated.mode, NULL, 8); file_header->uid = strtol(tar.formated.uid, NULL, 8); file_header->gid = strtol(tar.formated.gid, NULL, 8); file_header->size = strtol(tar.formated.size, NULL, 8); file_header->mtime = strtol(tar.formated.mtime, NULL, 8); file_header->link_name = (tar.formated.linkname[0] != '\0') ? bb_xstrdup(tar.formated.linkname) : NULL; file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8)); if (tar.formated.typeflag == '1') { bb_error_msg("WARNING: Converting hard link to symlink"); file_header->mode |= S_IFLNK; } #if defined CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY || defined CONFIG_FEATURE_TAR_GNU_EXTENSIONS /* Fix mode, used by the old format */ switch (tar.formated.typeflag) { # ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY case 0: case '0': file_header->mode |= S_IFREG; break; case '1': // bb_error_msg("Internal hard link not supported"); break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; # endif # ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS case 'L': { longname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, longname, file_header->size); longname[file_header->size] = '\0'; archive_handle->offset += file_header->size; return(get_header_tar(archive_handle)); } case 'K': { linkname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, linkname, file_header->size); linkname[file_header->size] = '\0'; archive_handle->offset += file_header->size; file_header->name = linkname; return(get_header_tar(archive_handle)); } case 'D': case 'M': case 'N': case 'S': case 'V': bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag); # endif } #endif if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); archive_handle->passed = llist_add_to(archive_handle->passed, archive_handle->file_header->name); } else { data_skip(archive_handle); } archive_handle->offset += file_header->size; return(EXIT_SUCCESS); }
extern char get_header_tar(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; union { /* ustar header, Posix 1003.1 */ unsigned char raw[512]; struct { char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } formated; } tar; long sum = 0; long i; /* Align header */ data_align(archive_handle, 512); if (bb_full_read(archive_handle->src_fd, tar.raw, 512) != 512) { /* Assume end of file */ return(EXIT_FAILURE); } archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.formated.name[0] == 0) { return(EXIT_SUCCESS); } /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.formated.magic, "ustar", 5) != 0) { #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (strncmp(tar.formated.magic, "\0\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("Invalid tar magic"); } /* Do checksum on headers */ for (i = 0; i < 148 ; i++) { sum += tar.raw[i]; } sum += ' ' * 8; for (i = 156; i < 512 ; i++) { sum += tar.raw[i]; } if (sum != strtol(tar.formated.chksum, NULL, 8)) { bb_error_msg("Invalid tar header checksum"); return(EXIT_FAILURE); } #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } else if (linkname) { file_header->name = linkname; linkname = NULL; } else #endif if (tar.formated.prefix[0] == 0) { file_header->name = strdup(tar.formated.name); } else { file_header->name = concat_path_file(tar.formated.prefix, tar.formated.name); } file_header->uid = strtol(tar.formated.uid, NULL, 8); file_header->gid = strtol(tar.formated.gid, NULL, 8); file_header->size = strtol(tar.formated.size, NULL, 8); file_header->mtime = strtol(tar.formated.mtime, NULL, 8); file_header->link_name = (tar.formated.linkname[0] != '\0') ? bb_xstrdup(tar.formated.linkname) : NULL; file_header->device = (dev_t) ((strtol(tar.formated.devmajor, NULL, 8) << 8) + strtol(tar.formated.devminor, NULL, 8)); /* Set bits 0-11 of the files mode */ file_header->mode = 07777 & strtol(tar.formated.mode, NULL, 8); /* Set bits 12-15 of the files mode */ switch (tar.formated.typeflag) { /* busybox identifies hard links as being regular files with 0 size and a link name */ case '1': file_header->mode |= S_IFREG; break; case 'x': case 'g': bb_error_msg_and_die("pax is not tar"); break; case '7': /* Reserved for high performance files, treat as normal file */ case 0: case '0': #ifdef CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY if (last_char_is(file_header->name, '/')) { file_header->mode |= S_IFDIR; } else #endif file_header->mode |= S_IFREG; break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; #ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS case 'L': { longname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, longname, file_header->size); longname[file_header->size] = '\0'; archive_handle->offset += file_header->size; return(get_header_tar(archive_handle)); } case 'K': { linkname = xmalloc(file_header->size + 1); archive_xread_all(archive_handle, linkname, file_header->size); linkname[file_header->size] = '\0'; archive_handle->offset += file_header->size; file_header->name = linkname; return(get_header_tar(archive_handle)); } case 'D': /* GNU dump dir */ case 'M': /* Continuation of multi volume archive*/ case 'N': /* Old GNU for names > 100 characters */ case 'S': /* Sparse file */ case 'V': /* Volume header */ bb_error_msg("Ignoring GNU extension type %c", tar.formated.typeflag); #endif default: bb_error_msg("Unknown typeflag: 0x%x", tar.formated.typeflag); } { /* Strip trailing '/' in directories */ /* Must be done after mode is set as '/' is used to check if its a directory */ char *tmp = last_char_is(file_header->name, '/'); if (tmp) { *tmp = '\0'; } } if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); archive_handle->passed = llist_add_to(archive_handle->passed, file_header->name); } else { data_skip(archive_handle); } archive_handle->offset += file_header->size; free(file_header->link_name); return(EXIT_SUCCESS); }
void data_extract_all(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; int dst_fd; int res; if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) { char *name = xstrdup(file_header->name); bb_make_directory(dirname(name), -1, FILEUTILS_RECUR); free(name); } /* Check if the file already exists */ if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) { /* Remove the existing entry if it exists */ if (((file_header->mode & S_IFMT) != S_IFDIR) && (unlink(file_header->name) == -1) && (errno != ENOENT) ) { bb_perror_msg_and_die("cannot remove old file %s", file_header->name); } } else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) { /* Remove the existing entry if its older than the extracted entry */ struct stat statbuf; if (lstat(file_header->name, &statbuf) == -1) { if (errno != ENOENT) { bb_perror_msg_and_die("cannot stat old file"); } } else if (statbuf.st_mtime <= file_header->mtime) { if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_error_msg("%s not created: newer or " "same age file exists", file_header->name); } data_skip(archive_handle); return; } else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { bb_perror_msg_and_die("cannot remove old file %s", file_header->name); } } /* Handle hard links separately * We identified hard links as regular files of size 0 with a symlink */ if (S_ISREG(file_header->mode) && (file_header->link_target) && (file_header->size == 0) ) { /* hard link */ res = link(file_header->link_target, file_header->name); if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("cannot create %slink " "from %s to %s", "hard", file_header->name, file_header->link_target); } } else { /* Create the filesystem entry */ switch (file_header->mode & S_IFMT) { case S_IFREG: { /* Regular file */ dst_fd = xopen3(file_header->name, O_WRONLY | O_CREAT | O_EXCL, file_header->mode); bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); close(dst_fd); break; } case S_IFDIR: res = mkdir(file_header->name, file_header->mode); if ((res == -1) && (errno != EISDIR) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("cannot make dir %s", file_header->name); } break; case S_IFLNK: /* Symlink */ res = symlink(file_header->link_target, file_header->name); if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("cannot create %slink " "from %s to %s", "sym", file_header->name, file_header->link_target); } break; #ifndef _WIN32 case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: res = mknod(file_header->name, file_header->mode, file_header->device); if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("cannot create node %s", file_header->name); } break; #endif default: bb_error_msg_and_die("unrecognized file type"); } } #ifndef _WIN32 if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_OWN)) { #if ENABLE_FEATURE_TAR_UNAME_GNAME uid_t uid = file_header->uid; gid_t gid = file_header->gid; if (file_header->uname) { struct passwd *pwd = getpwnam(file_header->uname); if (pwd) uid = pwd->pw_uid; } if (file_header->gname) { struct group *grp = getgrnam(file_header->gname); if (grp) gid = grp->gr_gid; } lchown(file_header->name, uid, gid); #else lchown(file_header->name, file_header->uid, file_header->gid); #endif } #endif if ((file_header->mode & S_IFMT) != S_IFLNK) { /* uclibc has no lchmod, glibc is even stranger - * it has lchmod which seems to do nothing! * so we use chmod... */ if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_PERM)) { chmod(file_header->name, file_header->mode); } /* same for utime */ if (archive_handle->flags & ARCHIVE_PRESERVE_DATE) { struct utimbuf t; t.actime = t.modtime = file_header->mtime; utime(file_header->name, &t); } } }
void data_extract_all(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; int dst_fd; int res; if (archive_handle->flags & ARCHIVE_CREATE_LEADING_DIRS) { char *name = bb_xstrdup(file_header->name); bb_make_directory (dirname(name), -1, FILEUTILS_RECUR); free(name); } /* Check if the file already exists */ if (archive_handle->flags & ARCHIVE_EXTRACT_UNCONDITIONAL) { /* Remove the existing entry if it exists */ if (((file_header->mode & S_IFMT) != S_IFDIR) && (unlink(file_header->name) == -1) && (errno != ENOENT)) { bb_perror_msg_and_die("Couldnt remove old file"); } } else if (archive_handle->flags & ARCHIVE_EXTRACT_NEWER) { /* Remove the existing entry if its older than the extracted entry */ struct stat statbuf; if (lstat(file_header->name, &statbuf) == -1) { if (errno != ENOENT) { bb_perror_msg_and_die("Couldnt stat old file"); } } else if (statbuf.st_mtime <= file_header->mtime) { if (!(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_error_msg("%s not created: newer or same age file exists", file_header->name); } data_skip(archive_handle); return; } else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { bb_perror_msg_and_die("Couldnt remove old file %s", file_header->name); } } /* Handle hard links separately * We identified hard links as regular files of size 0 with a symlink */ if (S_ISREG(file_header->mode) && (file_header->link_name) && (file_header->size == 0)) { /* hard link */ res = link(file_header->link_name, file_header->name); if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("Couldnt create hard link"); } } else { /* Create the filesystem entry */ switch(file_header->mode & S_IFMT) { case S_IFREG: { /* Regular file */ dst_fd = bb_xopen(file_header->name, O_WRONLY | O_CREAT | O_EXCL); bb_copyfd_size(archive_handle->src_fd, dst_fd, file_header->size); close(dst_fd); break; } case S_IFDIR: res = mkdir(file_header->name, file_header->mode); if ((errno != EISDIR) && (res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("extract_archive: %s", file_header->name); } break; case S_IFLNK: /* Symlink */ res = symlink(file_header->link_name, file_header->name); if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("Cannot create symlink from %s to '%s'", file_header->name, file_header->link_name); } break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: res = mknod(file_header->name, file_header->mode, file_header->device); if ((res == -1) && !(archive_handle->flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("Cannot create node %s", file_header->name); } break; default: bb_error_msg_and_die("Unrecognised file type"); } } if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_OWN)) { lchown(file_header->name, file_header->uid, file_header->gid); } if (!(archive_handle->flags & ARCHIVE_NOPRESERVE_PERM) && (file_header->mode & S_IFMT) != S_IFLNK) { chmod(file_header->name, file_header->mode); } if (archive_handle->flags & ARCHIVE_PRESERVE_DATE && !S_ISLNK(file_header->mode)) { struct utimbuf t; t.actime = t.modtime = file_header->mtime; utime(file_header->name, &t); } }
extern char get_header_ar(archive_handle_t *archive_handle) { file_header_t *typed = archive_handle->file_header; union { char raw[60]; struct { char name[16]; char date[12]; char uid[6]; char gid[6]; char mode[8]; char size[10]; char magic[2]; } formated; } ar; #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES static char *ar_long_names; static unsigned int ar_long_name_size; #endif /* dont use bb_xread as we want to handle the error ourself */ if (read(archive_handle->src_fd, ar.raw, 60) != 60) { /* End Of File */ return(EXIT_FAILURE); } /* Some ar entries have a trailing '\n' after the previous data entry */ if (ar.raw[0] == '\n') { /* fix up the header, we started reading 1 byte too early */ memmove(ar.raw, &ar.raw[1], 59); ar.raw[59] = bb_xread_char(archive_handle->src_fd); archive_handle->offset++; } archive_handle->offset += 60; /* align the headers based on the header magic */ if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) { bb_error_msg_and_die("Invalid ar header"); } typed->mode = strtol(ar.formated.mode, NULL, 8); typed->mtime = atoi(ar.formated.date); typed->uid = atoi(ar.formated.uid); typed->gid = atoi(ar.formated.gid); typed->size = atoi(ar.formated.size); /* long filenames have '/' as the first character */ if (ar.formated.name[0] == '/') { #ifdef CONFIG_FEATURE_AR_LONG_FILENAMES if (ar.formated.name[1] == '/') { /* If the second char is a '/' then this entries data section * stores long filename for multiple entries, they are stored * in static variable long_names for use in future entries */ ar_long_name_size = typed->size; ar_long_names = xmalloc(ar_long_name_size); bb_xread_all(archive_handle->src_fd, ar_long_names, ar_long_name_size); archive_handle->offset += ar_long_name_size; /* This ar entries data section only contained filenames for other records * they are stored in the static ar_long_names for future reference */ return (get_header_ar(archive_handle)); /* Return next header */ } else if (ar.formated.name[1] == ' ') { /* This is the index of symbols in the file for compilers */ data_skip(archive_handle); archive_handle->offset += typed->size; return (get_header_ar(archive_handle)); /* Return next header */ } else { /* The number after the '/' indicates the offset in the ar data section (saved in variable long_name) that conatains the real filename */ const unsigned int long_offset = atoi(&ar.formated.name[1]); if (long_offset >= ar_long_name_size) { bb_error_msg_and_die("Cant resolve long filename"); } typed->name = bb_xstrdup(ar_long_names + long_offset); } #else bb_error_msg_and_die("long filenames not supported"); #endif } else { /* short filenames */ typed->name = bb_xstrndup(ar.formated.name, 16); } typed->name[strcspn(typed->name, " /")] = '\0'; if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(typed); if (archive_handle->sub_archive) { while (archive_handle->action_data_subarchive(archive_handle->sub_archive) == EXIT_SUCCESS); } else { archive_handle->action_data(archive_handle); } } else { data_skip(archive_handle); } archive_handle->offset += typed->size; /* Set the file pointer to the correct spot, we may have been reading a compressed file */ lseek(archive_handle->src_fd, archive_handle->offset, SEEK_SET); return(EXIT_SUCCESS); }
char get_header_tar(archive_handle_t *archive_handle) { static int end; file_header_t *file_header = archive_handle->file_header; struct { /* ustar header, Posix 1003.1 */ char name[100]; /* 0-99 */ char mode[8]; /* 100-107 */ char uid[8]; /* 108-115 */ char gid[8]; /* 116-123 */ char size[12]; /* 124-135 */ char mtime[12]; /* 136-147 */ char chksum[8]; /* 148-155 */ char typeflag; /* 156-156 */ char linkname[100]; /* 157-256 */ char magic[6]; /* 257-262 */ char version[2]; /* 263-264 */ char uname[32]; /* 265-296 */ char gname[32]; /* 297-328 */ char devmajor[8]; /* 329-336 */ char devminor[8]; /* 337-344 */ char prefix[155]; /* 345-499 */ char padding[12]; /* 500-512 */ } tar; char *cp; int i, sum_u, sum_s, sum; int parse_names; if (sizeof(tar) != 512) BUG_tar_header_size(); #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS again: #endif /* Align header */ data_align(archive_handle, 512); again_after_align: xread(archive_handle->src_fd, &tar, 512); archive_handle->offset += 512; /* If there is no filename its an empty header */ if (tar.name[0] == 0) { if (end) { /* This is the second consecutive empty header! End of archive! * Read until the end to empty the pipe from gz or bz2 */ while (full_read(archive_handle->src_fd, &tar, 512) == 512) /* repeat */; return EXIT_FAILURE; } end = 1; return EXIT_SUCCESS; } end = 0; /* Check header has valid magic, "ustar" is for the proper tar * 0's are for the old tar format */ if (strncmp(tar.magic, "ustar", 5) != 0) { #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY if (memcmp(tar.magic, "\0\0\0\0", 5) != 0) #endif bb_error_msg_and_die("invalid tar magic"); } /* Do checksum on headers. * POSIX says that checksum is done on unsigned bytes, but * Sun and HP-UX gets it wrong... more details in * GNU tar source. */ sum_s = sum_u = ' ' * sizeof(tar.chksum); for (i = 0; i < 148 ; i++) { sum_u += ((unsigned char*)&tar)[i]; sum_s += ((signed char*)&tar)[i]; } for (i = 156; i < 512 ; i++) { sum_u += ((unsigned char*)&tar)[i]; sum_s += ((signed char*)&tar)[i]; } /* This field does not need special treatment (getOctal) */ sum = xstrtoul(tar.chksum, 8); if (sum_u != sum && sum_s != sum) { bb_error_msg_and_die("invalid tar header checksum"); } /* 0 is reserved for high perf file, treat as normal file */ if (!tar.typeflag) tar.typeflag = '0'; parse_names = (tar.typeflag >= '0' && tar.typeflag <= '7'); /* getOctal trashes subsequent field, therefore we call it * on fields in reverse order */ if (tar.devmajor[0]) { unsigned minor = GET_OCTAL(tar.devminor); unsigned major = GET_OCTAL(tar.devmajor); file_header->device = makedev(major, minor); } file_header->link_name = NULL; if (!linkname && parse_names && tar.linkname[0]) { /* we trash magic[0] here, it's ok */ tar.linkname[sizeof(tar.linkname)] = '\0'; file_header->link_name = xstrdup(tar.linkname); /* FIXME: what if we have non-link object with link_name? */ /* Will link_name be free()ed? */ } file_header->mtime = GET_OCTAL(tar.mtime); file_header->size = GET_OCTAL(tar.size); file_header->gid = GET_OCTAL(tar.gid); file_header->uid = GET_OCTAL(tar.uid); /* Set bits 0-11 of the files mode */ file_header->mode = 07777 & GET_OCTAL(tar.mode); file_header->name = NULL; if (!longname && parse_names) { /* we trash mode[0] here, it's ok */ tar.name[sizeof(tar.name)] = '\0'; if (tar.prefix[0]) { /* and padding[0] */ tar.prefix[sizeof(tar.prefix)] = '\0'; file_header->name = concat_path_file(tar.prefix, tar.name); } else file_header->name = xstrdup(tar.name); } /* Set bits 12-15 of the files mode */ /* (typeflag was not trashed because chksum does not use getOctal) */ switch (tar.typeflag) { /* busybox identifies hard links as being regular files with 0 size and a link name */ case '1': file_header->mode |= S_IFREG; break; case '7': /* case 0: */ case '0': #if ENABLE_FEATURE_TAR_OLDGNU_COMPATIBILITY if (last_char_is(file_header->name, '/')) { file_header->mode |= S_IFDIR; } else #endif file_header->mode |= S_IFREG; break; case '2': file_header->mode |= S_IFLNK; break; case '3': file_header->mode |= S_IFCHR; break; case '4': file_header->mode |= S_IFBLK; break; case '5': file_header->mode |= S_IFDIR; break; case '6': file_header->mode |= S_IFIFO; break; #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS case 'L': /* free: paranoia: tar with several consecutive longnames */ free(longname); /* For paranoia reasons we allocate extra NUL char */ longname = xzalloc(file_header->size + 1); /* We read ASCIZ string, including NUL */ xread(archive_handle->src_fd, longname, file_header->size); archive_handle->offset += file_header->size; /* return get_header_tar(archive_handle); */ /* gcc 4.1.1 didn't optimize it into jump */ /* so we will do it ourself, this also saves stack */ goto again; case 'K': free(linkname); linkname = xzalloc(file_header->size + 1); xread(archive_handle->src_fd, linkname, file_header->size); archive_handle->offset += file_header->size; /* return get_header_tar(archive_handle); */ goto again; case 'D': /* GNU dump dir */ case 'M': /* Continuation of multi volume archive */ case 'N': /* Old GNU for names > 100 characters */ case 'S': /* Sparse file */ case 'V': /* Volume header */ #endif case 'g': /* pax global header */ case 'x': { /* pax extended header */ off_t sz; bb_error_msg("warning: skipping header '%c'", tar.typeflag); sz = (file_header->size + 511) & ~(off_t)511; archive_handle->offset += sz; sz >>= 9; /* sz /= 512 but w/o contortions for signed div */ while (sz--) xread(archive_handle->src_fd, &tar, 512); /* return get_header_tar(archive_handle); */ goto again_after_align; } default: bb_error_msg_and_die("unknown typeflag: 0x%x", tar.typeflag); } #if ENABLE_FEATURE_TAR_GNU_EXTENSIONS if (longname) { file_header->name = longname; longname = NULL; } if (linkname) { file_header->link_name = linkname; linkname = NULL; } #endif if (!strncmp(file_header->name, "/../"+1, 3) || strstr(file_header->name, "/../") ) { bb_error_msg_and_die("name with '..' encountered: '%s'", file_header->name); } /* Strip trailing '/' in directories */ /* Must be done after mode is set as '/' is used to check if it's a directory */ cp = last_char_is(file_header->name, '/'); if (archive_handle->filter(archive_handle) == EXIT_SUCCESS) { archive_handle->action_header(archive_handle->file_header); /* Note that we kill the '/' only after action_header() */ /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ if (cp) *cp = '\0'; archive_handle->flags |= ARCHIVE_EXTRACT_QUIET; archive_handle->action_data(archive_handle); llist_add_to(&(archive_handle->passed), file_header->name); } else { data_skip(archive_handle); free(file_header->name); } archive_handle->offset += file_header->size; free(file_header->link_name); /* Do not free(file_header->name)! */ return EXIT_SUCCESS; }
void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; int dst_fd; int res; #if ENABLE_FEATURE_TAR_SELINUX char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; #ifdef __BIONIC__ matchpathcon_init(NULL); #endif if (!sctx) sctx = archive_handle->tar__sctx[PAX_GLOBAL]; if (sctx) { /* setfscreatecon is 4 syscalls, avoid if possible */ setfscreatecon(sctx); free(archive_handle->tar__sctx[PAX_NEXT_FILE]); archive_handle->tar__sctx[PAX_NEXT_FILE] = NULL; } #endif if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { char *slash = strrchr(file_header->name, '/'); if (slash) { *slash = '\0'; bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); *slash = '/'; } } if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) { /* Remove the entry if it exists */ if (!S_ISDIR(file_header->mode)) { /* Is it hardlink? * We encode hard links as regular files of size 0 with a symlink */ if (S_ISREG(file_header->mode) && file_header->link_target && file_header->size == 0 ) { /* Ugly special case: * tar cf t.tar hardlink1 hardlink2 hardlink1 * results in this tarball structure: * hardlink1 * hardlink2 -> hardlink1 * hardlink1 -> hardlink1 <== !!! */ if (strcmp(file_header->link_target, file_header->name) == 0) goto ret; } /* Proceed with deleting */ if (unlink(file_header->name) == -1 && errno != ENOENT ) { bb_perror_msg_and_die("can't remove old file %s", file_header->name); } } } else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) { /* Remove the existing entry if its older than the extracted entry */ struct stat existing_sb; if (lstat(file_header->name, &existing_sb) == -1) { if (errno != ENOENT) { bb_perror_msg_and_die("can't stat old file"); } } else if ((time_t) existing_sb.st_mtime >= (time_t) file_header->mtime) { if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) && !S_ISDIR(file_header->mode) ) { bb_error_msg("%s not created: newer or " "same age file exists", file_header->name); } data_skip(archive_handle); goto ret; } else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { bb_perror_msg_and_die("can't remove old file %s", file_header->name); } } /* Handle hard links separately * We encode hard links as regular files of size 0 with a symlink */ if (S_ISREG(file_header->mode) && file_header->link_target && file_header->size == 0 ) { /* hard link */ res = link(file_header->link_target, file_header->name); if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("can't create %slink " "from %s to %s", "hard", file_header->name, file_header->link_target); } /* Hardlinks have no separate mode/ownership, skip chown/chmod */ goto ret; } /* Create the filesystem entry */ switch (file_header->mode & S_IFMT) { case S_IFREG: { /* Regular file */ char *dst_name; int flags = O_WRONLY | O_CREAT | O_EXCL; if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) flags = O_WRONLY | O_CREAT | O_TRUNC; dst_name = file_header->name; #ifdef ARCHIVE_REPLACE_VIA_RENAME if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) /* rpm-style temp file name */ dst_name = xasprintf("%s;%x", dst_name, (int)getpid()); #endif dst_fd = xopen3(dst_name, flags, file_header->mode ); bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); close(dst_fd); #ifdef ARCHIVE_REPLACE_VIA_RENAME if (archive_handle->ah_flags & ARCHIVE_REPLACE_VIA_RENAME) { xrename(dst_name, file_header->name); free(dst_name); } #endif break; } case S_IFDIR: res = mkdir(file_header->name, file_header->mode); if ((res == -1) && (errno != EISDIR) /* btw, Linux doesn't return this */ && (errno != EEXIST) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("can't make dir %s", file_header->name); } break; case S_IFLNK: /* Symlink */ //TODO: what if file_header->link_target == NULL (say, corrupted tarball?) res = symlink(file_header->link_target, file_header->name); if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("can't create %slink " "from %s to %s", "sym", file_header->name, file_header->link_target); } break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: res = mknod(file_header->name, file_header->mode, file_header->device); if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("can't create node %s", file_header->name); } break; default: bb_error_msg_and_die("unrecognized file type"); } if (!S_ISLNK(file_header->mode)) { if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) { uid_t uid = file_header->uid; gid_t gid = file_header->gid; #if ENABLE_FEATURE_TAR_UNAME_GNAME if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) { if (file_header->tar__uname) { //TODO: cache last name/id pair? struct passwd *pwd = getpwnam(file_header->tar__uname); if (pwd) uid = pwd->pw_uid; } if (file_header->tar__gname) { struct group *grp = getgrnam(file_header->tar__gname); if (grp) gid = grp->gr_gid; } } #endif /* GNU tar 1.15.1 uses chown, not lchown */ chown(file_header->name, uid, gid); } /* uclibc has no lchmod, glibc is even stranger - * it has lchmod which seems to do nothing! * so we use chmod... */ if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) { chmod(file_header->name, file_header->mode); } if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { struct timeval t[2]; t[1].tv_sec = t[0].tv_sec = file_header->mtime; t[1].tv_usec = t[0].tv_usec = 0; utimes(file_header->name, t); } } ret: ; #if ENABLE_FEATURE_TAR_SELINUX if (sctx) { /* reset the context after creating an entry */ setfscreatecon(NULL); } #endif }
void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; int dst_fd; int res; if (archive_handle->ah_flags & ARCHIVE_CREATE_LEADING_DIRS) { char *slash = strrchr(file_header->name, '/'); if (slash) { *slash = '\0'; bb_make_directory(file_header->name, -1, FILEUTILS_RECUR); *slash = '/'; } } if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) { /* Remove the entry if it exists */ if ((!S_ISDIR(file_header->mode)) && (unlink(file_header->name) == -1) && (errno != ENOENT) ) { bb_perror_msg_and_die("can't remove old file %s", file_header->name); } } else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) { /* Remove the existing entry if its older than the extracted entry */ struct stat existing_sb; if (lstat(file_header->name, &existing_sb) == -1) { if (errno != ENOENT) { bb_perror_msg_and_die("can't stat old file"); } } else if (existing_sb.st_mtime >= file_header->mtime) { if (!(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { bb_error_msg("%s not created: newer or " "same age file exists", file_header->name); } data_skip(archive_handle); return; } else if ((unlink(file_header->name) == -1) && (errno != EISDIR)) { bb_perror_msg_and_die("can't remove old file %s", file_header->name); } } /* Handle hard links separately * We identified hard links as regular files of size 0 with a symlink */ if (S_ISREG(file_header->mode) && file_header->link_target && file_header->size == 0 ) { /* hard link */ res = link(file_header->link_target, file_header->name); if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET)) { bb_perror_msg("can't create %slink " "from %s to %s", "hard", file_header->name, file_header->link_target); } } else { /* Create the filesystem entry */ switch (file_header->mode & S_IFMT) { case S_IFREG: { /* Regular file */ int flags = O_WRONLY | O_CREAT | O_EXCL; if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) flags = O_WRONLY | O_CREAT | O_TRUNC; dst_fd = xopen3(file_header->name, flags, file_header->mode ); bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); close(dst_fd); break; } case S_IFDIR: res = mkdir(file_header->name, file_header->mode); if ((res == -1) && (errno != EISDIR) /* btw, Linux doesn't return this */ && (errno != EEXIST) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("can't make dir %s", file_header->name); } break; case S_IFLNK: /* Symlink */ res = symlink(file_header->link_target, file_header->name); if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("can't create %slink " "from %s to %s", "sym", file_header->name, file_header->link_target); } break; case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: res = mknod(file_header->name, file_header->mode, file_header->device); if ((res == -1) && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) ) { bb_perror_msg("can't create node %s", file_header->name); } break; default: bb_error_msg_and_die("unrecognized file type"); } } if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) { #if ENABLE_FEATURE_TAR_UNAME_GNAME if (!(archive_handle->ah_flags & ARCHIVE_NUMERIC_OWNER)) { uid_t uid = file_header->uid; gid_t gid = file_header->gid; if (file_header->tar__uname) { //TODO: cache last name/id pair? struct passwd *pwd = getpwnam(file_header->tar__uname); if (pwd) uid = pwd->pw_uid; } if (file_header->tar__gname) { struct group *grp = getgrnam(file_header->tar__gname); if (grp) gid = grp->gr_gid; } /* GNU tar 1.15.1 uses chown, not lchown */ chown(file_header->name, uid, gid); } else #endif chown(file_header->name, file_header->uid, file_header->gid); } if (!S_ISLNK(file_header->mode)) { /* uclibc has no lchmod, glibc is even stranger - * it has lchmod which seems to do nothing! * so we use chmod... */ if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_PERM)) { chmod(file_header->name, file_header->mode); } /* same for utime */ if (archive_handle->ah_flags & ARCHIVE_RESTORE_DATE) { struct timeval t[2]; t[1].tv_sec = t[0].tv_sec = file_header->mtime; t[1].tv_usec = t[0].tv_usec = 0; utimes(file_header->name, t); } } }