/* tar.c - create/extract archives * * Copyright 2014 Ashwini Kumar <ak.ashwini81@gmail.com> * * USTAR interchange format is of interest in * See http://http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html * For writing to external program * http://www.gnu.org/software/tar/manual/html_node/Writing-to-an-External-Program.html USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(to-command):o(no-same-owner)p(same-permissions)k(keep-old)c(create)|h(dereference)x(extract)|t(list)|v(verbose)j(bzip2)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc][!jz]", TOYFLAG_USR|TOYFLAG_BIN)) config TAR bool "tar" default n help usage: tar -[cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR] Create, extract, or list files from a tar file Operation: c Create f Name of TARFILE ('-' for stdin/out) h Follow symlinks j (De)compress using bzip2 m Don't restore mtime t List v Verbose x Extract z (De)compress using gzip C Change to DIR before operation O Extract to stdout exclude=FILE File to exclude X File with names to exclude T File with names to include */ #define FOR_tar #include "toys.h" GLOBALS( char *fname; char *dir; struct arg_list *inc_file; struct arg_list *exc_file; char *tocmd; struct arg_list *exc; struct arg_list *inc, *pass; void *inodes, *handle; ) struct tar_hdr { char name[100], mode[8], uid[8], gid[8],size[12], mtime[12], chksum[8], type, link[100], magic[8], uname[32], gname[32], major[8], minor[8], prefix[155], padd[12]; }; struct file_header { char *name, *link_target, *uname, *gname; off_t size; uid_t uid; gid_t gid; mode_t mode; time_t mtime; dev_t device; }; struct archive_handler { int src_fd; struct file_header file_hdr; off_t offset; void (*extract_handler)(struct archive_handler*); }; struct inode_list { struct inode_list *next; char *arg; ino_t ino; dev_t dev; }; static void copy_in_out(int src, int dst, off_t size) { int i, rd, rem = size%512, cnt; cnt = size/512 + (rem?1:0); for (i = 0; i < cnt; i++) { rd = (i == cnt-1 && rem) ? rem : 512; xreadall(src, toybuf, rd); writeall(dst, toybuf, rd); } } //convert to octal static void itoo(char *str, int len, off_t val) { char *t, tmp[sizeof(off_t)*3+1]; int cnt = sprintf(tmp, "%0*llo", len, (unsigned long long)val); t = tmp + cnt - len; if (*t == '0') t++; memcpy(str, t, len); } static struct inode_list *seen_inode(void **list, struct stat *st, char *name) { if (!st) llist_traverse(*list, llist_free_arg); else if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) { struct inode_list *new; for (new = *list; new; new = new->next) if(new->ino == st->st_ino && new->dev == st->st_dev) return new; new = xzalloc(sizeof(*new)); new->ino = st->st_ino; new->dev = st->st_dev; new->arg = xstrdup(name); new->next = *list; *list = new; } return 0; } static void write_longname(struct archive_handler *tar, char *name, char type) { struct tar_hdr tmp; unsigned int sum = 0; int i, sz = strlen(name) +1; char buf[512] = {0,}; memset(&tmp, 0, sizeof(tmp)); strcpy(tmp.name, "././@LongLink"); sprintf(tmp.mode, "%0*d", (int)sizeof(tmp.mode)-1, 0); sprintf(tmp.uid, "%0*d", (int)sizeof(tmp.uid)-1, 0); sprintf(tmp.gid, "%0*d", (int)sizeof(tmp.gid)-1, 0); sprintf(tmp.size, "%0*d", (int)sizeof(tmp.size)-1, 0); sprintf(tmp.mtime, "%0*d", (int)sizeof(tmp.mtime)-1, 0); itoo(tmp.size, sizeof(tmp.size), sz); tmp.type = type; memset(tmp.chksum, ' ', 8); strcpy(tmp.magic, "ustar "); for (i= 0; i < 512; i++) sum += (unsigned int)((char*)&tmp)[i]; itoo(tmp.chksum, sizeof(tmp.chksum)-1, sum); writeall(tar->src_fd, (void*) &tmp, sizeof(tmp)); //write name to archive writeall(tar->src_fd, name, sz); if (sz%512) writeall(tar->src_fd, buf, (512-(sz%512))); } static int filter(struct arg_list *lst, char *name) { struct arg_list *cur; for (cur = lst; cur; cur = cur->next) if (!fnmatch(cur->arg, name, 1<<3)) return 1; return 0; } static void add_file(struct archive_handler *tar, char **nam, struct stat *st) { struct tar_hdr hdr; struct passwd *pw; struct group *gr; struct inode_list *node; int i, fd =-1; char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,}; unsigned int sum = 0; static int warn = 1; for (p = name; *p; p++) if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.exc, p)) return; if (S_ISDIR(st->st_mode) && name[strlen(name)-1] != '/') { lnk = xmprintf("%s/",name); free(name); *nam = name = lnk; } hname = name; //remove leading '/' or relative path '../' component if (*hname == '/') hname++; if (!*hname) return; while ((c = strstr(hname, "../"))) hname = c + 3; if (warn && hname != name) { fprintf(stderr, "removing leading '%.*s' " "from member names\n", (int)(hname-name), name); warn = 0; } memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.name, hname, sizeof(hdr.name)); itoo(hdr.mode, sizeof(hdr.mode), st->st_mode &07777); itoo(hdr.uid, sizeof(hdr.uid), st->st_uid); itoo(hdr.gid, sizeof(hdr.gid), st->st_gid); itoo(hdr.size, sizeof(hdr.size), 0); //set size later itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime); for (i=0; i<sizeof(hdr.chksum); i++) hdr.chksum[i] = ' '; if ((node = seen_inode(&TT.inodes, st, hname))) { //this is a hard link hdr.type = '1'; if (strlen(node->arg) > sizeof(hdr.link)) write_longname(tar, hname, 'K'); //write longname LINK xstrncpy(hdr.link, node->arg, sizeof(hdr.link)); } else if (S_ISREG(st->st_mode)) { hdr.type = '0'; if (st->st_size <= (off_t)0777777777777LL) itoo(hdr.size, sizeof(hdr.size), st->st_size); else { error_msg("can't store file '%s' of size '%lld'\n", hname, (unsigned long long)st->st_size); return; } } else if (S_ISLNK(st->st_mode)) { hdr.type = '2'; //'K' long link if (!(lnk = xreadlink(name))) { perror_msg("readlink"); return; } if (strlen(lnk) > sizeof(hdr.link)) write_longname(tar, hname, 'K'); //write longname LINK xstrncpy(hdr.link, lnk, sizeof(hdr.link)); free(lnk); } else if (S_ISDIR(st->st_mode)) hdr.type = '5'; else if (S_ISFIFO(st->st_mode)) hdr.type = '6'; else if (S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode)) { hdr.type = (S_ISCHR(st->st_mode))?'3':'4'; itoo(hdr.major, sizeof(hdr.major), dev_major(st->st_rdev)); itoo(hdr.minor, sizeof(hdr.minor), dev_minor(st->st_rdev)); } else { error_msg("unknown file type '%o'", st->st_mode & S_IFMT); return; } if (strlen(hname) > sizeof(hdr.name)) write_longname(tar, hname, 'L'); //write longname NAME strcpy(hdr.magic, "ustar "); if ((pw = getpwuid(st->st_uid))) snprintf(hdr.uname, sizeof(hdr.uname), "%s", pw->pw_name); else snprintf(hdr.uname, sizeof(hdr.uname), "%d", st->st_uid); if ((gr = getgrgid(st->st_gid))) snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name); else snprintf(hdr.gname, sizeof(hdr.gname), "%d", st->st_gid); //calculate chksum. for (i= 0; i < 512; i++) sum += (unsigned int)((char*)&hdr)[i]; itoo(hdr.chksum, sizeof(hdr.chksum)-1, sum); if (toys.optflags & FLAG_v) printf("%s\n",hname); writeall(tar->src_fd, (void*)&hdr, 512); //write actual data to archive if (hdr.type != '0') return; //nothing to write if ((fd = open(name, O_RDONLY)) < 0) { perror_msg("can't open '%s'", name); return; } copy_in_out(fd, tar->src_fd, st->st_size); if (st->st_size%512) writeall(tar->src_fd, buf, (512-(st->st_size%512))); close(fd); } static int add_to_tar(struct dirtree *node) { struct stat st; char *path; struct archive_handler *hdl = (struct archive_handler*)TT.handle; if (!fstat(hdl->src_fd, &st) && st.st_dev == node->st.st_dev && st.st_ino == node->st.st_ino) { error_msg("'%s' file is the archive; not dumped", TT.fname); return ((DIRTREE_RECURSE | ((toys.optflags & FLAG_h)?DIRTREE_SYMFOLLOW:0))); } if (!dirtree_notdotdot(node)) return 0; path = dirtree_path(node, 0); add_file(hdl, &path, &(node->st)); //path may be modified free(path); if (toys.optflags & FLAG_no_recursion) return 0; return ((DIRTREE_RECURSE | ((toys.optflags & FLAG_h)?DIRTREE_SYMFOLLOW:0))); } static void compress_stream(struct archive_handler *tar_hdl) { int pipefd[2]; pid_t cpid; xpipe(pipefd); signal(SIGPIPE, SIG_IGN); cpid = fork(); if (cpid == -1) perror_exit("fork"); if (!cpid) { /* Child reads from pipe */ char *argv[] = {(toys.optflags&FLAG_z)?"gzip":"bzip2", "-f", NULL}; xclose(pipefd[1]); /* Close unused write*/ dup2(pipefd[0], 0); dup2(tar_hdl->src_fd, 1); //write to tar fd xexec(argv); } else { xclose(pipefd[0]); /* Close unused read end */ dup2(pipefd[1], tar_hdl->src_fd); //write to pipe } } static void extract_to_stdout(struct archive_handler *tar) { struct file_header *file_hdr = &tar->file_hdr; copy_in_out(tar->src_fd, 0, file_hdr->size); tar->offset += file_hdr->size; } static void extract_to_command(struct archive_handler *tar) { int pipefd[2], status = 0; pid_t cpid; struct file_header *file_hdr = &tar->file_hdr; xpipe(pipefd); if (!S_ISREG(file_hdr->mode)) return; //only regular files are supported. cpid = fork(); if (cpid == -1) perror_exit("fork"); if (!cpid) { // Child reads from pipe char buf[64], *argv[4] = {"sh", "-c", TT.tocmd, NULL}; setenv("TAR_FILETYPE", "f", 1); sprintf(buf, "%0o", file_hdr->mode); setenv("TAR_MODE", buf, 1); sprintf(buf, "%ld", (long)file_hdr->size); setenv("TAR_SIZE", buf, 1); setenv("TAR_FILENAME", file_hdr->name, 1); setenv("TAR_UNAME", file_hdr->uname, 1); setenv("TAR_GNAME", file_hdr->gname, 1); sprintf(buf, "%0o", (int)file_hdr->mtime); setenv("TAR_MTIME", buf, 1); sprintf(buf, "%0o", file_hdr->uid); setenv("TAR_UID", buf, 1); sprintf(buf, "%0o", file_hdr->gid); setenv("TAR_GID", buf, 1); xclose(pipefd[1]); // Close unused write dup2(pipefd[0], 0); signal(SIGPIPE, SIG_DFL); xexec(argv); } else { xclose(pipefd[0]); // Close unused read end copy_in_out(tar->src_fd, pipefd[1], file_hdr->size); tar->offset += file_hdr->size; xclose(pipefd[1]); waitpid(cpid, &status, 0); if (WIFSIGNALED(status)) xprintf("tar : %d: child returned %d\n", cpid, WTERMSIG(status)); } } static void extract_to_disk(struct archive_handler *tar) { int flags, dst_fd = -1; char *s; struct stat ex; struct file_header *file_hdr = &tar->file_hdr; flags = strlen(file_hdr->name); if (flags>2) { if (strstr(file_hdr->name, "/../") || !strcmp(file_hdr->name, "../") || !strcmp(file_hdr->name+flags-3, "/..")) { error_msg("drop %s", file_hdr->name); } } if (file_hdr->name[flags-1] == '/') file_hdr->name[flags-1] = 0; //Regular file with preceding path if ((s = strrchr(file_hdr->name, '/'))) { if (mkpath(file_hdr->name) && errno !=EEXIST) { error_msg(":%s: not created", file_hdr->name); return; } } //remove old file, if exists if (!(toys.optflags & FLAG_k) && !S_ISDIR(file_hdr->mode) && !lstat( file_hdr->name, &ex)) { if (unlink(file_hdr->name)) { perror_msg("can't remove: %s",file_hdr->name); } } //hard link if (S_ISREG(file_hdr->mode) && file_hdr->link_target) { if (link(file_hdr->link_target, file_hdr->name)) perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target); goto COPY; } switch (file_hdr->mode & S_IFMT) { case S_IFREG: flags = O_WRONLY|O_CREAT|O_EXCL; if (toys.optflags & FLAG_overwrite) flags = O_WRONLY|O_CREAT|O_TRUNC; dst_fd = open(file_hdr->name, flags, file_hdr->mode & 07777); if (dst_fd == -1) perror_msg("%s: can't open", file_hdr->name); break; case S_IFDIR: if ((mkdir(file_hdr->name, file_hdr->mode) == -1) && errno != EEXIST) perror_msg("%s: can't create", file_hdr->name); break; case S_IFLNK: if (symlink(file_hdr->link_target, file_hdr->name)) perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target); break; case S_IFBLK: case S_IFCHR: case S_IFIFO: if (mknod(file_hdr->name, file_hdr->mode, file_hdr->device)) perror_msg("can't create '%s'", file_hdr->name); break; default: printf("type not yet supported\n"); break; } //copy file.... COPY: copy_in_out(tar->src_fd, dst_fd, file_hdr->size); tar->offset += file_hdr->size; close(dst_fd); if (S_ISLNK(file_hdr->mode)) return; if (!(toys.optflags & FLAG_o)) { //set ownership..., --no-same-owner, --numeric-owner uid_t u = file_hdr->uid; gid_t g = file_hdr->gid; if (!(toys.optflags & FLAG_numeric_owner)) { struct group *gr = getgrnam(file_hdr->gname); struct passwd *pw = getpwnam(file_hdr->uname); if (pw) u = pw->pw_uid; if (gr) g = gr->gr_gid; } if (chown(file_hdr->name, u, g)) perror_msg("chown %d:%d '%s'", u, g, file_hdr->name);; } if (toys.optflags & FLAG_p) // || !(toys.optflags & FLAG_no_same_permissions)) chmod(file_hdr->name, file_hdr->mode); //apply mtime if (!(toys.optflags & FLAG_m)) { struct timeval times[2] = {{file_hdr->mtime, 0},{file_hdr->mtime, 0}}; utimes(file_hdr->name, times); } } static void add_to_list(struct arg_list **llist, char *name) { struct arg_list **list = llist; while (*list) list=&((*list)->next); *list = xzalloc(sizeof(struct arg_list)); (*list)->arg = name; if ((name[strlen(name)-1] == '/') && strlen(name) != 1) name[strlen(name)-1] = '\0'; } static void add_from_file(struct arg_list **llist, struct arg_list *flist) { char *line = NULL; while (flist) { int fd = 0; if (strcmp((char *)flist->arg, "-")) fd = xopen((char *)flist->arg, O_RDONLY); while ((line = get_line(fd))) { add_to_list(llist, line); } if (fd) close(fd); flist = flist->next; } } static struct archive_handler *init_handler() { struct archive_handler *tar_hdl = xzalloc(sizeof(struct archive_handler)); tar_hdl->extract_handler = extract_to_disk; return tar_hdl; } //convert octal to int static int otoi(char *str, int len) { long val; char *endp, inp[len+1]; //1 for NUL termination memcpy(inp, str, len); inp[len] = '\0'; //nul-termination made sure val = strtol(inp, &endp, 8); if (*endp && *endp != ' ') error_exit("invalid param"); return (int)val; } static void extract_stream(struct archive_handler *tar_hdl) { int pipefd[2]; pid_t cpid; xpipe(pipefd); cpid = fork(); if (cpid == -1) perror_exit("fork"); if (!cpid) { /* Child reads from pipe */ char *argv[] = {(toys.optflags&FLAG_z)?"gunzip":"bunzip2", "-cf", "-", NULL}; xclose(pipefd[0]); /* Close unused read*/ dup2(tar_hdl->src_fd, 0); dup2(pipefd[1], 1); //write to pipe xexec(argv); } else { xclose(pipefd[1]); /* Close unused read end */ dup2(pipefd[0], tar_hdl->src_fd); //read from pipe } } static char *process_extended_hdr(struct archive_handler *tar, int size) { char *value = NULL, *p, *buf = xzalloc(size+1); if (readall(tar->src_fd, buf, size) != size) error_exit("short read"); buf[size] = 0; tar->offset += size; p = buf; while (size) { char *key; int len, n; // extended records are of the format: "LEN NAME=VALUE\n" sscanf(p, "%d %n", &len, &n); key = p + n; p += len; size -= len; p[-1] = 0; if (size < 0) { error_msg("corrupted extended header"); break; } len = strlen("path="); if (!strncmp(key, "path=", len)) { value = key + strlen("path="); break; } } if (value) value = xstrdup(value); free(buf); return value; } static void tar_skip(struct archive_handler *tar, int sz) { int x; while ((x = lskip(tar->src_fd, sz))) { tar->offset += sz - x; sz = x; } tar->offset += sz; } static void unpack_tar(struct archive_handler *tar_hdl) { struct tar_hdr tar; struct file_header *file_hdr; int i, j, maj, min, sz, e = 0; unsigned int cksum; char *longname = NULL, *longlink = NULL; while (1) { cksum = 0; if (tar_hdl->offset % 512) { sz = 512 - tar_hdl->offset % 512; tar_skip(tar_hdl, sz); } i = readall(tar_hdl->src_fd, &tar, 512); tar_hdl->offset += i; if (i != 512) { if (i >= 2) goto CHECK_MAGIC; //may be a small (<512 byte)zipped file error_exit("read error"); } if (!tar.name[0]) { if (e) return; //end of tar 2 empty blocks e = 1;//empty jump to next block continue; } if (strncmp(tar.magic, "ustar", 5)) { // Try detecting .gz or .bz2 by looking for their magic. CHECK_MAGIC: if ((!strncmp(tar.name, "\x1f\x8b", 2) || !strncmp(tar.name, "BZh", 3)) && !lseek(tar_hdl->src_fd, -i, SEEK_CUR)) { toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z; tar_hdl->offset -= i; extract_stream(tar_hdl); continue; } error_exit("invalid tar format"); } for (j = 0; j<148; j++) cksum += (unsigned int)((char*)&tar)[j]; for (j = 156; j<500; j++) cksum += (unsigned int)((char*)&tar)[j]; //cksum field itself treated as ' ' for ( j= 0; j<8; j++) cksum += (unsigned int)' '; if (cksum != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("wrong cksum"); file_hdr = &tar_hdl->file_hdr; memset(file_hdr, 0, sizeof(struct file_header)); file_hdr->mode = otoi(tar.mode, sizeof(tar.mode)); file_hdr->uid = otoi(tar.uid, sizeof(tar.uid)); file_hdr->gid = otoi(tar.gid, sizeof(tar.gid)); file_hdr->size = otoi(tar.size, sizeof(tar.size)); file_hdr->mtime = otoi(tar.mtime, sizeof(tar.mtime)); file_hdr->uname = xstrdup(tar.uname); file_hdr->gname = xstrdup(tar.gname); maj = otoi(tar.major, sizeof(tar.major)); min = otoi(tar.minor, sizeof(tar.minor)); file_hdr->device = dev_makedev(maj, min); if (tar.type <= '7') { if (tar.link[0]) { sz = sizeof(tar.link); file_hdr->link_target = xmalloc(sz + 1); memcpy(file_hdr->link_target, tar.link, sz); file_hdr->link_target[sz] = '\0'; } file_hdr->name = xzalloc(256);// pathname supported size if (tar.prefix[0]) { memcpy(file_hdr->name, tar.prefix, sizeof(tar.prefix)); sz = strlen(file_hdr->name); if (file_hdr->name[sz-1] != '/') file_hdr->name[sz] = '/'; } sz = strlen(file_hdr->name); memcpy(file_hdr->name + sz, tar.name, sizeof(tar.name)); if (file_hdr->name[255]) error_exit("filename too long"); } switch (tar.type) { // case '\0': case '0': case '7': case '1': //Hard Link file_hdr->mode |= S_IFREG; break; case '2': file_hdr->mode |= S_IFLNK; break; case '3': file_hdr->mode |= S_IFCHR; break; case '4': file_hdr->mode |= S_IFBLK; break; case '5': file_hdr->mode |= S_IFDIR; break; case '6': file_hdr->mode |= S_IFIFO; break; case 'K': longlink = xzalloc(file_hdr->size +1); xread(tar_hdl->src_fd, longlink, file_hdr->size); tar_hdl->offset += file_hdr->size; continue; case 'L': free(longname); longname = xzalloc(file_hdr->size +1); xread(tar_hdl->src_fd, longname, file_hdr->size); tar_hdl->offset += file_hdr->size; continue; case 'D': case 'M': case 'N': case 'S': case 'V': case 'g': // pax global header tar_skip(tar_hdl, file_hdr->size); continue; case 'x': // pax extended header free(longname); longname = process_extended_hdr(tar_hdl, file_hdr->size); continue; default: break; } if (longname) { free(file_hdr->name); file_hdr->name = longname; longname = NULL; } if (longlink) { free(file_hdr->link_target); file_hdr->link_target = longlink; longlink = NULL; } if ((file_hdr->mode & S_IFREG) && file_hdr->name[strlen(file_hdr->name)-1] == '/') { file_hdr->name[strlen(file_hdr->name)-1] = '\0'; file_hdr->mode &= ~S_IFREG; file_hdr->mode |= S_IFDIR; } if ((file_hdr->link_target && *(file_hdr->link_target)) || S_ISLNK(file_hdr->mode) || S_ISDIR(file_hdr->mode)) file_hdr->size = 0; if (filter(TT.exc, file_hdr->name) || (TT.inc && !filter(TT.inc, file_hdr->name))) goto SKIP; add_to_list(&TT.pass, xstrdup(file_hdr->name)); if (toys.optflags & FLAG_t) { if (toys.optflags & FLAG_v) { char perm[11]; struct tm *lc = localtime((const time_t*)&(file_hdr->mtime)); mode_to_string(file_hdr->mode, perm); printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ",perm,file_hdr->uname, file_hdr->gname, (long)file_hdr->size, 1900+lc->tm_year, 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec); } printf("%s",file_hdr->name); if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target); xputc('\n'); SKIP: tar_skip(tar_hdl, file_hdr->size); } else { if (toys.optflags & FLAG_v) printf("%s\n",file_hdr->name); tar_hdl->extract_handler(tar_hdl); } free(file_hdr->name); free(file_hdr->link_target); free(file_hdr->uname); free(file_hdr->gname); } } void tar_main(void) { struct archive_handler *tar_hdl; int fd = 0; struct arg_list *tmp; char **args = toys.optargs; if (!geteuid()) toys.optflags |= FLAG_p; for (tmp = TT.exc; tmp; tmp = tmp->next) tmp->arg = xstrdup(tmp->arg); //freeing at the end fails otherwise while(*args) add_to_list(&TT.inc, xstrdup(*args++)); if (toys.optflags & FLAG_X) add_from_file(&TT.exc, TT.exc_file); if (toys.optflags & FLAG_T) add_from_file(&TT.inc, TT.inc_file); if (toys.optflags & FLAG_c) { if (!TT.inc) error_exit("empty archive"); fd = 1; } if ((toys.optflags & FLAG_f) && strcmp(TT.fname, "-")) fd = xcreate(TT.fname, fd*(O_WRONLY|O_CREAT|O_TRUNC), 0666); if (toys.optflags & FLAG_C) xchdir(TT.dir); tar_hdl = init_handler(); tar_hdl->src_fd = fd; if ((toys.optflags & FLAG_x) || (toys.optflags & FLAG_t)) { if (toys.optflags & FLAG_O) tar_hdl->extract_handler = extract_to_stdout; if (toys.optflags & FLAG_to_command) { signal(SIGPIPE, SIG_IGN); //will be using pipe between child & parent tar_hdl->extract_handler = extract_to_command; } if (toys.optflags & FLAG_z) extract_stream(tar_hdl); unpack_tar(tar_hdl); for (tmp = TT.inc; tmp; tmp = tmp->next) if (!filter(TT.exc, tmp->arg) && !filter(TT.pass, tmp->arg)) error_msg("'%s' not in archive", tmp->arg); } else if (toys.optflags & FLAG_c) { //create the tar here. if (toys.optflags & (FLAG_j|FLAG_z)) compress_stream(tar_hdl); for (tmp = TT.inc; tmp; tmp = tmp->next) { TT.handle = tar_hdl; //recurse thru dir and add files to archive dirtree_flagread(tmp->arg, DIRTREE_SYMFOLLOW*!!(toys.optflags&FLAG_h), add_to_tar); } memset(toybuf, 0, 1024); writeall(tar_hdl->src_fd, toybuf, 1024); seen_inode(&TT.inodes, 0, 0); } if (CFG_TOYBOX_FREE) { close(tar_hdl->src_fd); free(tar_hdl); llist_traverse(TT.exc, llist_free_arg); llist_traverse(TT.inc, llist_free_arg); llist_traverse(TT.pass, llist_free_arg); } }