static void unzip_extract(zip_header_t *zip_header, int dst_fd) { if (zip_header->formatted.method == 0) { /* Method 0 - stored (not compressed) */ off_t size = zip_header->formatted.ucmpsize; if (size) bb_copyfd_exact_size(zip_fd, dst_fd, size); } else { /* Method 8 - inflate */ transformer_aux_data_t aux; init_transformer_aux_data(&aux); aux.bytes_in = zip_header->formatted.cmpsize; if (inflate_unzip(&aux, zip_fd, dst_fd) < 0) bb_error_msg_and_die("inflate error"); /* Validate decompression - crc */ if (zip_header->formatted.crc32 != (aux.crc32 ^ 0xffffffffL)) { bb_error_msg_and_die("crc error"); } /* Validate decompression - size */ if (zip_header->formatted.ucmpsize != aux.bytes_out) { /* Don't die. Who knows, maybe len calculation * was botched somewhere. After all, crc matched! */ bb_error_msg("bad length"); } } }
int scriptreplay_main(int argc UNUSED_PARAM, char **argv) { const char *script = "typescript"; double delay, factor = 1000000.0; int fd; unsigned long count; FILE *tfp; if (!argv[1]) bb_show_usage(); if (argv[2]) { script = argv[2]; if (argv[3]) factor /= atof(argv[3]); } tfp = xfopen_for_read(argv[1]); fd = xopen(script, O_RDONLY); while (fscanf(tfp, "%lf %lu\n", &delay, &count) == 2) { usleep(delay * factor); bb_copyfd_exact_size(fd, STDOUT_FILENO, count); } if (ENABLE_FEATURE_CLEAN_UP) { close(fd); fclose(tfp); } return EXIT_SUCCESS; }
static int write_ar_header(archive_handle_t *handle) { char *fn; char fn_h[17]; /* 15 + "/" + NUL */ struct stat st; int fd; fn = llist_pop(&handle->accept); if (!fn) return -1; xstat(fn, &st); handle->file_header->mtime = st.st_mtime; handle->file_header->uid = st.st_uid; handle->file_header->gid = st.st_gid; handle->file_header->mode = st.st_mode; handle->file_header->size = st.st_size; handle->file_header->name = fn_h; //TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES... sprintf(fn_h, "%.15s/", bb_basename(fn)); output_ar_header(handle); fd = xopen(fn, O_RDONLY); bb_copyfd_exact_size(fd, handle->src_fd, st.st_size); close(fd); handle->offset += st.st_size; return 0; }
/* * when replacing files in an existing archive, copy from the * original archive those files that are to be left intact */ static void FAST_FUNC copy_data(archive_handle_t *handle) { archive_handle_t *out_handle = handle->ar__out; struct file_header_t *fh = handle->file_header; out_handle->file_header = fh; output_ar_header(out_handle); bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size); out_handle->offset += fh->size; }
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); } } }
static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statbuf, void *userData, int depth UNUSED_PARAM) { struct TarBallInfo *tbInfo = (struct TarBallInfo *) userData; const char *header_name; int inputFileFd = -1; /* Strip leading '/' (must be before memorizing hardlink's name) */ header_name = fileName; while (header_name[0] == '/') { static smallint warned; if (!warned) { bb_error_msg("removing leading '/' from member names"); warned = 1; } header_name++; } if (header_name[0] == '\0') return TRUE; /* It is against the rules to archive a socket */ if (S_ISSOCK(statbuf->st_mode)) { bb_error_msg("%s: socket ignored", fileName); return TRUE; } /* * 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); if (tbInfo->hlInfo == NULL) addHardLinkInfo(&tbInfo->hlInfoHead, statbuf, header_name); } /* It is a bad idea to store the archive we are in the process of creating, * so check the device and inode to be sure that this particular file isn't * the new tarball */ if (tbInfo->statBuf.st_dev == statbuf->st_dev && tbInfo->statBuf.st_ino == statbuf->st_ino ) { bb_error_msg("%s: file is the archive; skipping", fileName); return TRUE; } if (exclude_file(tbInfo->excludeList, header_name)) return SKIP; #if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS if (strlen(header_name) >= NAME_SIZE) { bb_error_msg("names longer than "NAME_SIZE_STR" chars not supported"); return TRUE; } #endif /* Is this a regular file? */ if (tbInfo->hlInfo == NULL && S_ISREG(statbuf->st_mode)) { /* open the file we want to archive, and make sure all is well */ inputFileFd = open_or_warn(fileName, O_RDONLY); if (inputFileFd < 0) { return FALSE; } } /* Add an entry to the tarball */ if (writeTarHeader(tbInfo, header_name, fileName, statbuf) == FALSE) { return FALSE; } /* If it was a regular file, write out the body */ if (inputFileFd >= 0) { size_t readSize; /* Write the file to the archive. */ /* We record size into header first, */ /* and then write out file. If file shrinks in between, */ /* tar will be corrupted. So we don't allow for that. */ /* NB: GNU tar 1.16 warns and pads with zeroes */ /* or even seeks back and updates header */ bb_copyfd_exact_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); ////off_t readSize; ////readSize = bb_copyfd_size(inputFileFd, tbInfo->tarFd, statbuf->st_size); ////if (readSize != statbuf->st_size && readSize >= 0) { //// bb_error_msg_and_die("short read from %s, aborting", fileName); ////} /* Check that file did not grow in between? */ /* if (safe_read(inputFileFd, 1) == 1) warn but continue? */ close(inputFileFd); /* Pad the file up to the tar block size */ /* (a few tricks here in the name of code size) */ readSize = (-(int)statbuf->st_size) & (TAR_BLOCK_SIZE-1); memset(block_buf, 0, readSize); xwrite(tbInfo->tarFd, block_buf, readSize); } return TRUE; }
void FAST_FUNC data_extract_to_command(archive_handle_t *archive_handle) { file_header_t *file_header = archive_handle->file_header; #if 0 /* do we need this? ENABLE_FEATURE_TAR_SELINUX */ char *sctx = archive_handle->tar__sctx[PAX_NEXT_FILE]; 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 ((file_header->mode & S_IFMT) == S_IFREG) { pid_t pid; int p[2], status; char *tar_env[TAR_MAX]; memset(tar_env, 0, sizeof(tar_env)); xpipe(p); pid = BB_MMU ? xfork() : xvfork(); if (pid == 0) { /* Child */ /* str2env(tar_env, TAR_FILETYPE, "f"); - parent should do it once */ oct2env(tar_env, TAR_MODE, file_header->mode); str2env(tar_env, TAR_FILENAME, file_header->name); str2env(tar_env, TAR_REALNAME, file_header->name); #if ENABLE_FEATURE_TAR_UNAME_GNAME str2env(tar_env, TAR_UNAME, file_header->tar__uname); str2env(tar_env, TAR_GNAME, file_header->tar__gname); #endif dec2env(tar_env, TAR_SIZE, file_header->size); dec2env(tar_env, TAR_UID, file_header->uid); dec2env(tar_env, TAR_GID, file_header->gid); close(p[1]); xdup2(p[0], STDIN_FILENO); signal(SIGPIPE, SIG_DFL); execl(archive_handle->tar__to_command_shell, archive_handle->tar__to_command_shell, "-c", archive_handle->tar__to_command, NULL); bb_perror_msg_and_die("can't execute '%s'", archive_handle->tar__to_command_shell); } close(p[0]); /* Our caller is expected to do signal(SIGPIPE, SIG_IGN) * so that we don't die if child don't read all the input: */ bb_copyfd_exact_size(archive_handle->src_fd, p[1], -file_header->size); close(p[1]); if (safe_waitpid(pid, &status, 0) == -1) bb_perror_msg_and_die("waitpid"); if (WIFEXITED(status) && WEXITSTATUS(status)) bb_error_msg_and_die("'%s' returned status %d", archive_handle->tar__to_command, WEXITSTATUS(status)); if (WIFSIGNALED(status)) bb_error_msg_and_die("'%s' terminated on signal %d", archive_handle->tar__to_command, WTERMSIG(status)); if (!BB_MMU) { int i; for (i = 0; i < TAR_MAX; i++) { if (tar_env[i]) bb_unsetenv_and_free(tar_env[i]); } } } #if 0 /* ENABLE_FEATURE_TAR_SELINUX */ if (sctx) /* reset the context after creating an entry */ setfscreatecon(NULL); #endif }
static void unzip_skip(off_t skip) { if (lseek(zip_fd, skip, SEEK_CUR) == (off_t)-1) bb_copyfd_exact_size(zip_fd, -1, skip); }
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 }
/* Return value will become exit code. * It's ok to exit instead of return. */ static NOINLINE int cpio_o(void) { static const char trailer[] ALIGN1 = "TRAILER!!!"; struct name_s { struct name_s *next; char name[1]; }; struct inodes_s { struct inodes_s *next; struct name_s *names; struct stat st; }; struct inodes_s *links = NULL; off_t bytes = 0; /* output bytes count */ while (1) { const char *name; char *line; struct stat st; line = (option_mask32 & CPIO_OPT_NUL_TERMINATED) ? bb_get_chunk_from_file(stdin, NULL) : xmalloc_fgetline(stdin); if (line) { /* Strip leading "./[./]..." from the filename */ name = line; while (name[0] == '.' && name[1] == '/') { while (*++name == '/') continue; } if (!*name) { /* line is empty */ free(line); continue; } if ((option_mask32 & CPIO_OPT_DEREF) ? stat(name, &st) : lstat(name, &st) ) { abort_cpio_o: bb_simple_perror_msg_and_die(name); } if (!(S_ISLNK(st.st_mode) || S_ISREG(st.st_mode))) st.st_size = 0; /* paranoia */ /* Store hardlinks for later processing, dont output them */ if (!S_ISDIR(st.st_mode) && st.st_nlink > 1) { struct name_s *n; struct inodes_s *l; /* Do we have this hardlink remembered? */ l = links; while (1) { if (l == NULL) { /* Not found: add new item to "links" list */ l = xzalloc(sizeof(*l)); l->st = st; l->next = links; links = l; break; } if (l->st.st_ino == st.st_ino) { /* found */ break; } l = l->next; } /* Add new name to "l->names" list */ n = xmalloc(sizeof(*n) + strlen(name)); strcpy(n->name, name); n->next = l->names; l->names = n; free(line); continue; } } else { /* line == NULL: EOF */ next_link: if (links) { /* Output hardlink's data */ st = links->st; name = links->names->name; links->names = links->names->next; /* GNU cpio is reported to emit file data * only for the last instance. Mimic that. */ if (links->names == NULL) links = links->next; else st.st_size = 0; /* NB: we leak links->names and/or links, * this is intended (we exit soon anyway) */ } else { /* If no (more) hardlinks to output, * output "trailer" entry */ name = trailer; /* st.st_size == 0 is a must, but for uniformity * in the output, we zero out everything */ memset(&st, 0, sizeof(st)); /* st.st_nlink = 1; - GNU cpio does this */ } } bytes += printf("070701" "%08X%08X%08X%08X%08X%08X%08X" "%08X%08X%08X%08X" /* GNU cpio uses uppercase hex */ /* strlen+1: */ "%08X" /* chksum: */ "00000000" /* (only for "070702" files) */ /* name,NUL: */ "%s%c", (unsigned)(uint32_t) st.st_ino, (unsigned)(uint32_t) st.st_mode, (unsigned)(uint32_t) st.st_uid, (unsigned)(uint32_t) st.st_gid, (unsigned)(uint32_t) st.st_nlink, (unsigned)(uint32_t) st.st_mtime, (unsigned)(uint32_t) st.st_size, (unsigned)(uint32_t) major(st.st_dev), (unsigned)(uint32_t) minor(st.st_dev), (unsigned)(uint32_t) major(st.st_rdev), (unsigned)(uint32_t) minor(st.st_rdev), (unsigned)(strlen(name) + 1), name, '\0'); bytes = cpio_pad4(bytes); if (st.st_size) { if (S_ISLNK(st.st_mode)) { char *lpath = xmalloc_readlink_or_warn(name); if (!lpath) goto abort_cpio_o; bytes += printf("%s", lpath); free(lpath); } else { /* S_ISREG */ int fd = xopen(name, O_RDONLY); fflush_all(); /* We must abort if file got shorter too! */ bb_copyfd_exact_size(fd, STDOUT_FILENO, st.st_size); bytes += st.st_size; close(fd); } bytes = cpio_pad4(bytes); } if (!line) { if (name != trailer) goto next_link; /* TODO: GNU cpio pads trailer to 512 bytes, do we want that? */ return EXIT_SUCCESS; } free(line); } /* end of "while (1)" */ }
/* If we are reading through a pipe(), or from stdin then we can't lseek, * we must read and discard the data to skip over it. */ void seek_by_read(const archive_handle_t *archive_handle, const unsigned int jump_size) { if (jump_size) bb_copyfd_exact_size(archive_handle->src_fd, -1, jump_size); }
static void unzip_skip(int fd, off_t skip) { bb_copyfd_exact_size(fd, -1, skip); }
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); } } }