Mercurial > hg > toybox
view toys/pending/tar.c @ 1572:da1bf31ed322 draft
Tweak the "ignoring return value" fortify workaround for readlinkat.
We zero the buffer and if the link read fails that's left alone, so
it's ok for the symlink not to be there. Unfortunately, typecasting the
return value to (void) doesn't shut up gcc, and having an if(); with the
semicolon on the same line doesn't shut up llvm. (The semicolon on a new
line would, but C does not have significant whitespace and I'm not going
to humor llvm if it plans to start.)
So far, empty curly brackets consistently get the warning to shut up.
author | Rob Landley <rob@landley.net> |
---|---|
date | Mon, 24 Nov 2014 17:23:23 -0600 |
parents | 00c20f410c46 |
children | 41efba077b75 |
line wrap: on
line source
/* 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)z(gzip)O(to-stdout)m(touch)X(exclude-from)*T(files-from)*C(directory):f(file):[!txc]", TOYFLAG_USR|TOYFLAG_BIN)) config TAR bool "tar" default n help usage: tar -[cxtzhmvO] [-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 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, 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", sizeof(tmp.mode)-1, 0); sprintf(tmp.uid, "%0*d", sizeof(tmp.uid)-1, 0); sprintf(tmp.gid, "%0*d", sizeof(tmp.gid)-1, 0); sprintf(tmp.size, "%0*d", sizeof(tmp.size)-1, 0); sprintf(tmp.mtime, "%0*d", 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) { printf("removing leading '%.*s' " "from member names\n",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 strncpy(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 '%d'\n", hname, 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 strncpy(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), major(st->st_rdev)); itoo(hdr.minor, sizeof(hdr.minor), minor(st->st_rdev)); } else { error_msg("unknown file type '%s'"); 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 (node->parent && !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; if (pipe(pipefd) == -1) error_exit("pipe"); signal(SIGPIPE, SIG_IGN); cpid = fork(); if (cpid == -1) perror_exit("fork"); if (!cpid) { /* Child reads from pipe */ char *argv[] = {"gzip", "-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; if (pipe(pipefd) == -1) error_exit("pipe"); 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; if (file_hdr->name[strlen(file_hdr->name)-1] == '/') file_hdr->name[strlen(file_hdr->name)-1] = 0; //Regular file with preceding path if ((s = strrchr(file_hdr->name, '/'))) { if (mkpathat(AT_FDCWD, file_hdr->name, 00, 2) && 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; } chown(file_hdr->name, u, g); } 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; if (pipe(pipefd) == -1) error_exit("pipe"); cpid = fork(); if (cpid == -1) perror_exit("fork"); if (!cpid) { /* Child reads from pipe */ char *argv[] = {"gunzip", "-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; unsigned char *gzMagic; 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 by reading magic CHECK_MAGIC: gzMagic = (unsigned char*)&tar; if ((gzMagic[0] == 0x1f) && (gzMagic[1] == 0x8b) && !lseek(tar_hdl->src_fd, -i, SEEK_CUR)) { 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 = 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, flags = O_RDONLY; struct arg_list *tmp; char **args = toys.optargs; if (!toys.argv[1]) { toys.exithelp++; error_exit(NULL); } 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, flags = O_WRONLY|O_CREAT|O_TRUNC; } if ((toys.optflags & FLAG_f) && strcmp(TT.fname, "-")) fd = xcreate(TT.fname, flags, 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_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 struct dirtree *root = dirtree_add_node(0,tmp->arg,toys.optflags & FLAG_h); if (root) dirtree_handle_callback(root, 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); } }