From 793a3cae970b45acde49093360d746726f25474e Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Tue, 7 May 2019 10:52:13 -0700 Subject: Revert all recent tar changes for Q. Q branched at an awkward time for tar, right in the middle of a rewrite. Keep the other toybox changes since then, but put tar back to 168bfe5382c5a5034b7e208b3253f292b24999ec:toys/pending/tar.c. Bug: http://b/132097981 Test: treehugger Change-Id: I7c09236212757ae3277a3f0ad2aa995e99929e39 Merged-In: I816e199e29c09ad76955e76cc722cd2639bf49cc --- generated/flags.h | 22 +- generated/globals.h | 37 +- generated/help.h | 2 +- generated/newtoys.h | 2 +- toys/pending/tar.c | 1200 +++++++++++++++++++++++++++------------------------ 5 files changed, 658 insertions(+), 605 deletions(-) diff --git a/generated/flags.h b/generated/flags.h index 7019fc94..0255f55c 100644 --- a/generated/flags.h +++ b/generated/flags.h @@ -2742,9 +2742,9 @@ #undef FLAG_f #endif -// tar &(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(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] &(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(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] +// 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] &(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] #undef OPTSTR_tar -#define OPTSTR_tar "&(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(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]" +#define OPTSTR_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]" #ifdef CLEANUP_tar #undef CLEANUP_tar #undef FOR_tar @@ -2765,15 +2765,11 @@ #undef FLAG_p #undef FLAG_o #undef FLAG_to_command -#undef FLAG_owner -#undef FLAG_group -#undef FLAG_mtime #undef FLAG_exclude #undef FLAG_overwrite #undef FLAG_no_same_permissions #undef FLAG_numeric_owner #undef FLAG_no_recursion -#undef FLAG_full_time #endif // taskset <1^pa <1^pa @@ -5662,15 +5658,11 @@ #define FLAG_p (1<<14) #define FLAG_o (1<<15) #define FLAG_to_command (1<<16) -#define FLAG_owner (1<<17) -#define FLAG_group (1<<18) -#define FLAG_mtime (1<<19) -#define FLAG_exclude (1<<20) -#define FLAG_overwrite (1<<21) -#define FLAG_no_same_permissions (1<<22) -#define FLAG_numeric_owner (1<<23) -#define FLAG_no_recursion (1<<24) -#define FLAG_full_time (1<<25) +#define FLAG_exclude (1<<17) +#define FLAG_overwrite (1<<18) +#define FLAG_no_same_permissions (1<<19) +#define FLAG_numeric_owner (1<<20) +#define FLAG_no_recursion (1<<21) #endif #ifdef FOR_taskset diff --git a/generated/globals.h b/generated/globals.h index 8f40ea18..82089a4d 100644 --- a/generated/globals.h +++ b/generated/globals.h @@ -803,34 +803,15 @@ struct syslogd_data { // toys/pending/tar.c struct tar_data { - char *f, *C; - struct arg_list *T, *X; - char *to_command, *owner, *group, *mtime; - struct arg_list *exclude; - - struct double_list *incl, *excl, *seen; - struct string_list *dirs; - char *cwd; - int fd, ouid, ggid, hlc, warn, adev, aino; - time_t mtt; - - // hardlinks seen so far (hlc many) - struct { - char *arg; - ino_t ino; - dev_t dev; - } *hlx; - - // Parsed information about a tar header. - struct tar_header { - char *name, *link_target, *uname, *gname; - long long size; - uid_t uid; - gid_t gid; - mode_t mode; - time_t mtime; - dev_t device; - } hdr; + 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; }; // toys/pending/tcpsvd.c diff --git a/generated/help.h b/generated/help.h index f4728dcc..fbc1fb37 100644 --- a/generated/help.h +++ b/generated/help.h @@ -340,7 +340,7 @@ #define HELP_tcpsvd "usage: tcpsvd [-hEv] [-c N] [-C N[:MSG]] [-b N] [-u User] [-l Name] IP Port Prog\nusage: udpsvd [-hEv] [-c N] [-u User] [-l Name] IP Port Prog\n\nCreate TCP/UDP socket, bind to IP:PORT and listen for incoming connection.\nRun PROG for each connection.\n\nIP IP to listen on, 0 = all\nPORT Port to listen on\nPROG ARGS Program to run\n-l NAME Local hostname (else looks up local hostname in DNS)\n-u USER[:GRP] Change to user/group after bind\n-c N Handle up to N (> 0) connections simultaneously\n-b N (TCP Only) Allow a backlog of approximately N TCP SYNs\n-C N[:MSG] (TCP Only) Allow only up to N (> 0) connections from the same IP\n New connections from this IP address are closed\n immediately. MSG is written to the peer before close\n-h Look up peer's hostname\n-E Don't set up environment variables\n-v Verbose\n\n" -#define HELP_tar "usage: tar [-cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR]\n\nCreate, extract, or list files in a .tar (or compressed t?z) file.\n\nOptions:\nc Create x Extract t Test\nf Name of TARFILE C Change to DIR first v Verbose: show filenames\no Ignore owner h Follow symlinks m Ignore mtime\nj bzip2 compression z gzip compression\nO Extract to stdout X exclude names in FILE T include names in FILE\n--exclude=FILE File pattern(s) to exclude\n\n" +#define HELP_tar "usage: tar -[cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR]\n\nCreate, extract, or list files from a tar file\n\nOperation:\nc Create\nf Name of TARFILE ('-' for stdin/out)\nh Follow symlinks\nj (De)compress using bzip2\nm Don't restore mtime\nt List\nv Verbose\nx Extract\nz (De)compress using gzip\nC Change to DIR before operation\nO Extract to stdout\nexclude=FILE File to exclude\nX File with names to exclude\nT File with names to include\n\n" #define HELP_syslogd "usage: syslogd [-a socket] [-O logfile] [-f config file] [-m interval]\n [-p socket] [-s SIZE] [-b N] [-R HOST] [-l N] [-nSLKD]\n\nSystem logging utility\n\n-a Extra unix socket for listen\n-O FILE Default log file \n-f FILE Config file \n-p Alternative unix domain socket \n-n Avoid auto-backgrounding\n-S Smaller output\n-m MARK interval (RANGE: 0 to 71582787)\n-R HOST Log to IP or hostname on PORT (default PORT=514/UDP)\"\n-L Log locally and via network (default is network only if -R)\"\n-s SIZE Max size (KB) before rotation (default:200KB, 0=off)\n-b N rotated logs to keep (default:1, max=99, 0=purge)\n-K Log to kernel printk buffer (use dmesg to read it)\n-l N Log only messages more urgent than prio(default:8 max:8 min:1)\n-D Drop duplicates\n\n" diff --git a/generated/newtoys.h b/generated/newtoys.h index e8205572..fd6e7163 100644 --- a/generated/newtoys.h +++ b/generated/newtoys.h @@ -246,7 +246,7 @@ USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN)) USE_SYSLOGD(NEWTOY(syslogd,">0l#<1>8=8R:b#<0>99=1s#<0=200m#<0>71582787=20O:p:f:a:nSKLD", TOYFLAG_SBIN|TOYFLAG_STAYROOT)) USE_TAC(NEWTOY(tac, NULL, TOYFLAG_USR|TOYFLAG_BIN)) USE_TAIL(NEWTOY(tail, "?fc-n-[-cn]", TOYFLAG_USR|TOYFLAG_BIN)) -USE_TAR(NEWTOY(tar, "&(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(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)) +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)) USE_TASKSET(NEWTOY(taskset, "<1^pa", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) USE_TCPSVD(NEWTOY(tcpsvd, "^<3c#=30<1C:b#=20<0u:l:hEv", TOYFLAG_USR|TOYFLAG_BIN)) USE_TEE(NEWTOY(tee, "ia", TOYFLAG_USR|TOYFLAG_BIN)) diff --git a/toys/pending/tar.c b/toys/pending/tar.c index bff8b540..c2946225 100644 --- a/toys/pending/tar.c +++ b/toys/pending/tar.c @@ -2,73 +2,51 @@ * * Copyright 2014 Ashwini Kumar * - * For the command, see - * http://pubs.opengroup.org/onlinepubs/007908799/xcu/tar.html - * For the modern file format, see - * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13_06 - * https://en.wikipedia.org/wiki/Tar_(computing)#File_format - * https://www.gnu.org/software/tar/manual/html_node/Tar-Internals.html - * + * 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 - * - * Toybox will never implement the "pax" command as a matter of policy. - * - * Why --exclude pattern but no --include? tar cvzf a.tgz dir --include '*.txt' - * Extract into dir same as filename, --restrict? "Tarball is splodey" - * -USE_TAR(NEWTOY(tar, "&(full-time)(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(mtime):(group):(owner):(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)) +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 in a .tar (or compressed t?z) file. - - Options: - c Create x Extract t Test - f Name of TARFILE C Change to DIR first v Verbose: show filenames - o Ignore owner h Follow symlinks m Ignore mtime - j bzip2 compression z gzip compression - O Extract to stdout X exclude names in FILE T include names in FILE - --exclude=FILE File pattern(s) to exclude + 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 *f, *C; - struct arg_list *T, *X; - char *to_command, *owner, *group, *mtime; - struct arg_list *exclude; - - struct double_list *incl, *excl, *seen; - struct string_list *dirs; - char *cwd; - int fd, ouid, ggid, hlc, warn, adev, aino; - time_t mtt; - - // hardlinks seen so far (hlc many) - struct { - char *arg; - ino_t ino; - dev_t dev; - } *hlx; - - // Parsed information about a tar header. - struct tar_header { - char *name, *link_target, *uname, *gname; - long long size; - uid_t uid; - gid_t gid; - mode_t mode; - time_t mtime; - dev_t device; - } hdr; + 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 { @@ -77,264 +55,293 @@ struct tar_hdr { prefix[155], padd[12]; }; -// convert to int to octal (or base-256) -static void itoo(char *str, int len, unsigned long long val) +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) { - // Do we need binary encoding? - if (!(val>>(3*(len-1)))) sprintf(str, "%0*llo", len-1, val); - else { - *str = 128; - while (--len) *++str = val>>(3*len); + 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); } } -#define ITOO(x, y) itoo(x, sizeof(x), y) -// convert octal (or base-256) to int -static unsigned long long otoi(char *str, unsigned len) +//convert to octal +static void itoo(char *str, int len, off_t val) { - unsigned long long val = 0; - - // When tar value too big or octal, use binary encoding with high bit set - if (128&*str) while (--len) val = (val<<8)+*++str; - else { - while (len && *str>='0' && *str<='7') val = val*8+*str++-'0', len--; - if (len && *str && *str != ' ') error_exit("bad header"); - } + char *t, tmp[sizeof(off_t)*3+1]; + int cnt = sprintf(tmp, "%0*llo", len, (unsigned long long)val); - return val; + t = tmp + cnt - len; + if (*t == '0') t++; + memcpy(str, t, len); } -// Calculate packet checksum, with cksum field treated as 8 spaces -static unsigned cksum(void *data) +static struct inode_list *seen_inode(void **list, struct stat *st, char *name) { - unsigned i, cksum = 8*' '; - - for (i = 0; i<500; i += (i==147) ? 9 : 1) cksum += ((char *)data)[i]; - - return cksum; + 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(char *name, char type) +static void write_longname(struct archive_handler *tar, char *name, char type) { struct tar_hdr tmp; - int sz = strlen(name) +1; + unsigned int sum = 0; + int i, sz = strlen(name) +1; + char buf[512] = {0,}; memset(&tmp, 0, sizeof(tmp)); strcpy(tmp.name, "././@LongLink"); - ITOO(tmp.uid, 0); - ITOO(tmp.gid, 0); - ITOO(tmp.size, sz); - ITOO(tmp.mtime, 0); + 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); - // Historical nonsense to match other implementations. Never used. - ITOO(tmp.mode, 0644); - strcpy(tmp.uname, "root"); - strcpy(tmp.gname, "root"); - - // Calculate checksum. Since 512*255 = 0377000 in octal, this can never - // use more than 6 digits. The last byte is ' ' or historical reasons. - itoo(tmp.chksum, sizeof(tmp.chksum)-1, cksum(&tmp)); - tmp.chksum[7] = ' '; - - // write header and name, padded with NUL to block size - xwrite(TT.fd, &tmp, 512); - xwrite(TT.fd, name, sz); - if (sz%512) xwrite(TT.fd, toybuf, 512-(sz%512)); + 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 struct double_list *filter(struct double_list *lst, char *name) +static int filter(struct arg_list *lst, char *name) { - struct double_list *end = lst; - - if (lst) - do if (!fnmatch(lst->data, name, 1<<3)) return lst; - while (end != (lst = lst->next)); + struct arg_list *cur; + for (cur = lst; cur; cur = cur->next) + if (!fnmatch(cur->arg, name, 1<<3)) return 1; return 0; } -static void skippy(long long len) +static void add_file(struct archive_handler *tar, char **nam, struct stat *st) { - if (lskip(TT.fd, len)) perror_exit("EOF"); -} - -// allocate and read data from TT.fd -static void alloread(void *buf, int len) -{ - // actually void **, but automatic typecasting doesn't work with void ** :( - void **b = buf; - - free(*b); - *b = xmalloc(len+1); - xreadall(TT.fd, *b, len); - b[len] = 0; -} - -static int add_to_tar(struct dirtree *node) -{ - struct stat *st = &(node->st); struct tar_hdr hdr; - struct passwd *pw = pw; - struct group *gr = gr; + struct passwd *pw; + struct group *gr; + struct inode_list *node; int i, fd =-1; - char *c, *p, *name, *lnk = lnk, *hname; - - if (!dirtree_notdotdot(node)) return 0; - if (TT.adev == st->st_dev && TT.aino == st->st_ino) { - error_msg("'%s' file is the archive; not dumped", node->name); - return 0; - } - - i = 1; - name = dirtree_path(node, &i); + char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,}; + unsigned int sum = 0; + static int warn = 1; - // exclusion defaults to --no-anchored and --wildcards-match-slash for (p = name; *p; p++) - if ((p == name || p[-1] == '/') && *p != '/' && filter(TT.excl, p)) - goto done; - - // The 1 extra byte from dirtree_path() - if (S_ISDIR(st->st_mode) && name[i-1] != '/') strcat(name, "/"); - - // remove leading / and any .. entries from saved name - for (hname = name; *hname == '/'; hname++); - for (c = hname;;) { - if (!(c = strstr(c, ".."))) break; - if (c == hname || c[-1] == '/') { - if (!c[2]) goto done; - if (c[2]=='/') c = hname = c+3; - } else c+= 2; - } - if (!*hname) goto done; + if ((p == name || p[-1] == '/') && *p != '/' + && filter(TT.exc, p)) return; - if (TT.warn && hname != name) { - fprintf(stderr, "removing leading '%.*s' from member names\n", - (int)(hname-name), name); - TT.warn = 0; + 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; } - - if (TT.owner) st->st_uid = TT.ouid; - if (TT.group) st->st_gid = TT.ggid; - if (TT.mtime) st->st_mtime = TT.mtt; memset(&hdr, 0, sizeof(hdr)); strncpy(hdr.name, hname, sizeof(hdr.name)); - ITOO(hdr.mode, st->st_mode &07777); - ITOO(hdr.uid, st->st_uid); - ITOO(hdr.gid, st->st_gid); - ITOO(hdr.size, 0); //set size later - ITOO(hdr.mtime, st->st_mtime); - strcpy(hdr.magic, "ustar "); - - // Hard link or symlink? i=0 neither, i=1 hardlink, i=2 symlink - - // Are there hardlinks to a non-directory entry? - if (st->st_nlink>1 && !S_ISDIR(st->st_mode)) { - // Have we seen this dev&ino before? - for (i = 0; ist_ino == TT.hlx[i].ino && st->st_dev == TT.hlx[i].dev) - break; - } - if (i != TT.hlc) { - lnk = TT.hlx[i].arg; - i = 1; - } else { - // first time we've seen it. Store as normal file, but remember it. - if (!(TT.hlc&255)) TT.hlx = xrealloc(TT.hlx, TT.hlc+256); - TT.hlx[TT.hlc].arg = xstrdup(hname); - TT.hlx[TT.hlc].ino = st->st_ino; - TT.hlx[TT.hlc].dev = st->st_dev; - TT.hlc++; - i = 0; + 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; iarg) > 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 i = 0; - - // !i because hardlink to a symlink is a thing. - if (!i && S_ISLNK(st->st_mode)) { - i = 2; - lnk = xreadlink(name); - } - - // Handle file types - if (i) { - hdr.type = '0'+i; - if (i==2 && !(lnk = xreadlink(name))) { + } else if (S_ISLNK(st->st_mode)) { + hdr.type = '2'; //'K' long link + if (!(lnk = xreadlink(name))) { perror_msg("readlink"); - goto done; + return; } - if (strlen(lnk) > sizeof(hdr.link)) write_longname(lnk, 'K'); - strncpy(hdr.link, lnk, sizeof(hdr.link)); - if (i) free(lnk); - } else if (S_ISREG(st->st_mode)) { - hdr.type = '0'; - ITOO(hdr.size, st->st_size); - } else if (S_ISDIR(st->st_mode)) hdr.type = '5'; + 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, dev_major(st->st_rdev)); - ITOO(hdr.minor, dev_minor(st->st_rdev)); + 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); - goto done; + 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); +} - if (strlen(hname) > sizeof(hdr.name)) write_longname(hname, 'L'); - if (!FLAG(numeric_owner)) { - if (TT.owner || (pw = bufgetpwuid(st->st_uid))) - strncpy(hdr.uname, TT.owner ? TT.owner : pw->pw_name, sizeof(hdr.uname)); - if (TT.group || (gr = bufgetgrgid(st->st_gid))) - strncpy(hdr.gname, TT.group ? TT.group : gr->gr_name, sizeof(hdr.gname)); +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))); } - itoo(hdr.chksum, sizeof(hdr.chksum)-1, cksum(&hdr)); - hdr.chksum[7] = ' '; + 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; - if (FLAG(v)) printf("%s\n", hname); + xpipe(pipefd); - // Write header and data to archive - xwrite(TT.fd, &hdr, 512); - if (hdr.type == '0') { - if ((fd = open(name, O_RDONLY)) < 0) perror_msg("can't open '%s'", name); - else { - xsendfile_pad(fd, TT.fd, st->st_size); - if (st->st_size%512) writeall(TT.fd, toybuf, (512-(st->st_size%512))); - close(fd); - } + 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 } -done: - free(name); +} + +static void extract_to_stdout(struct archive_handler *tar) +{ + struct file_header *file_hdr = &tar->file_hdr; - return (DIRTREE_RECURSE|(FLAG(h)?DIRTREE_SYMFOLLOW:0))*!FLAG(no_recursion); + copy_in_out(tar->src_fd, 0, file_hdr->size); + tar->offset += file_hdr->size; } -// Does anybody actually use this? -static void extract_to_command(void) +static void extract_to_command(struct archive_handler *tar) { int pipefd[2], status = 0; pid_t cpid; - - if (!S_ISREG(TT.hdr.mode)) return; //only regular files are supported. + struct file_header *file_hdr = &tar->file_hdr; xpipe(pipefd); - if (!(cpid = xfork())) { // Child reads from pipe - char buf[64], *argv[4] = {"sh", "-c", TT.to_command, NULL}; + 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", TT.hdr.mode); + sprintf(buf, "%0o", file_hdr->mode); setenv("TAR_MODE", buf, 1); - sprintf(buf, "%ld", (long)TT.hdr.size); + sprintf(buf, "%ld", (long)file_hdr->size); setenv("TAR_SIZE", buf, 1); - setenv("TAR_FILENAME", TT.hdr.name, 1); - setenv("TAR_UNAME", TT.hdr.uname, 1); - setenv("TAR_GNAME", TT.hdr.gname, 1); - sprintf(buf, "%0llo", (long long)TT.hdr.mtime); + 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", TT.hdr.uid); + sprintf(buf, "%0o", file_hdr->uid); setenv("TAR_UID", buf, 1); - sprintf(buf, "%0o", TT.hdr.gid); + sprintf(buf, "%0o", file_hdr->gid); setenv("TAR_GID", buf, 1); xclose(pipefd[1]); // Close unused write @@ -343,7 +350,8 @@ static void extract_to_command(void) xexec(argv); } else { xclose(pipefd[0]); // Close unused read end - xsendfile_len(TT.fd, pipefd[1], TT.hdr.size); + 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)) @@ -351,385 +359,457 @@ static void extract_to_command(void) } } -// Do pending directory utimes(), NULL to flush all. -static int dirflush(char *name) +static void extract_to_disk(struct archive_handler *tar) { - char *s = s, *ss; + 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); + } + } - // Barf if name not in TT.cwd - if (name) { - ss = s = xabspath(name, -1); - if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || *ss!='/')) { - error_msg("'%s' not under '%s'", name, TT.cwd); - free(s); + 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; + } + } - return 1; + //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); } } - // Set deferred utimes() for directories this file isn't under. - // (Files must be depth-first ordered in tarball for this to matter.) - while (TT.dirs) { - long long ll = *(long long *)TT.dirs->str; - struct timeval times[2] = {{ll, 0},{ll, 0}}; + //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; + } - // If next file is under (or equal to) this dir, keep waiting - if (name && strstart(&ss, ss = s) && (!*ss || *ss=='/')) break; + 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; + } - if (utimes(TT.dirs->str+sizeof(long long), times)) - perror_msg("utimes %lld %s", ll, - TT.dirs->str+sizeof(long long)); - free(llist_pop(&TT.dirs)); + //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);; } - free(s); - // name was under TT.cwd - return 0; + 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 extract_to_disk(void) +static void add_to_list(struct arg_list **llist, char *name) { - char *name = TT.hdr.name; - int ala = TT.hdr.mode; + struct arg_list **list = llist; - if (dirflush(name)) { - if (S_ISREG(ala) && !TT.hdr.link_target) skippy(TT.hdr.size); - - return; - } + 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'; +} - // create path before file if necessary - if (strrchr(name, '/') && mkpath(name) && errno !=EEXIST) - return perror_msg(":%s: can't mkdir", name); +static void add_from_file(struct arg_list **llist, struct arg_list *flist) +{ + char *line = NULL; - // remove old file, if exists - if (!FLAG(k) && !S_ISDIR(ala) && unlink(name) && errno!=ENOENT) - return perror_msg("can't remove: %s", name); + while (flist) { + int fd = 0; - if (S_ISREG(ala)) { - // hardlink? - if (TT.hdr.link_target) { - if (link(TT.hdr.link_target, name)) - return perror_msg("can't link '%s' -> '%s'", name, TT.hdr.link_target); - // write contents - } else { - int fd = xcreate(name, O_WRONLY|O_CREAT|(FLAG(overwrite)?O_TRUNC:O_EXCL), - WARN_ONLY|(ala & 07777)); - if (fd != -1) { - xsendfile_len(TT.fd, fd, TT.hdr.size); - close(fd); - } - } - } else if (S_ISDIR(ala)) { - if ((mkdir(name, 0700) == -1) && errno != EEXIST) - return perror_msg("%s: can't create", TT.hdr.name); - } else if (S_ISLNK(ala)) { - if (symlink(TT.hdr.link_target, TT.hdr.name)) - return perror_msg("can't link '%s' -> '%s'", name, TT.hdr.link_target); - } else if (mknod(name, ala, TT.hdr.device)) - return perror_msg("can't create '%s'", name); - - - // Set ownership - if (!FLAG(o) && !geteuid()) { - int u = TT.hdr.uid, g = TT.hdr.gid; - - if (TT.owner) TT.hdr.uid = TT.ouid; - else if (!FLAG(numeric_owner) && *TT.hdr.uname) { - struct passwd *pw = getpwnam(TT.hdr.uname); - if (pw && (TT.owner || !FLAG(numeric_owner))) TT.hdr.uid = pw->pw_uid; - } + if (strcmp((char *)flist->arg, "-")) + fd = xopen((char *)flist->arg, O_RDONLY); - if (TT.group) TT.hdr.gid = TT.ggid; - else if (!FLAG(numeric_owner) && *TT.hdr.uname) { - struct group *gr = getgrnam(TT.hdr.gname); - if (gr) TT.hdr.gid = gr->gr_gid; + while ((line = get_line(fd))) { + add_to_list(llist, line); } - - if (lchown(name, u, g)) perror_msg("chown %d:%d '%s'", u, g, name);; + if (fd) close(fd); + flist = flist->next; } +} - // || !FLAG(no_same_permissions)) - if (FLAG(p) && !S_ISLNK(ala)) chmod(TT.hdr.name, ala); +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; +} - // Apply mtime. - if (!FLAG(m)) { - if (S_ISDIR(ala)) { - struct string_list *sl; +//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; +} - // Writing files into a directory changes directory timestamps, so - // defer mtime updates until contents written. +static void extract_stream(struct archive_handler *tar_hdl) +{ + int pipefd[2]; + pid_t cpid; - sl = xmalloc(sizeof(struct string_list)+sizeof(long long)+strlen(name)+1); - *(long long *)sl->str = TT.hdr.mtime; - strcpy(sl->str+sizeof(long long), name); - sl->next = TT.dirs; - TT.dirs = sl; - } else { - struct timeval times[2] = {{TT.hdr.mtime, 0},{TT.hdr.mtime, 0}}; - utimes(TT.hdr.name, times); - } + 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 void unpack_tar(void) +static char *process_extended_hdr(struct archive_handler *tar, int size) { - struct double_list *walk, *delete; - struct tar_hdr tar; - int i, and = 0; - unsigned maj, min; - char *s; + 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; +} - for (;;) { - // align to next block and read it - if (TT.hdr.size%512) skippy(512-TT.hdr.size%512); - i = readall(TT.fd, &tar, 512); +static void tar_skip(struct archive_handler *tar, int sz) +{ + int x; - if (i && i != 512) error_exit("read error"); + while ((x = lskip(tar->src_fd, sz))) { + tar->offset += sz - x; + sz = x; + } + tar->offset += sz; +} - // Two consecutive empty headers ends tar even if there's more data - if (!i || !*tar.name) { - if (!i || and++) { - dirflush(0); - return; - } - TT.hdr.size = 0; - continue; +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"); } - // ensure null temination even of pathological packets - tar.padd[0] = and = 0; - - // Is this a valid Unix Standard TAR header? - if (memcmp(tar.magic, "ustar", 5)) error_exit("bad header"); - if (cksum(&tar) != otoi(tar.chksum, sizeof(tar.chksum))) - error_exit("bad cksum"); - TT.hdr.size = otoi(tar.size, sizeof(tar.size)); - - // If this header isn't writing something to the filesystem - if (tar.type<'0' || tar.type>'7') { - - // Long name extension header? - if (tar.type == 'K') alloread(&TT.hdr.link_target, TT.hdr.size); - else if (tar.type == 'L') alloread(&TT.hdr.name, TT.hdr.size); - else if (tar.type == 'x') { - char *p, *buf = 0; - int i, len, n; - - // Posix extended record "LEN NAME=VALUE\n" format - alloread(&buf, TT.hdr.size); - for (p = buf; (p-buf)TT.hdr.size) { - error_msg("bad header"); - break; - } - p[len-1] = 0; - if (i == 2) { - TT.hdr.name = xstrdup(p+n); - break; - } - } - free(buf); - - // Ignore everything else. - } else skippy(TT.hdr.size); + 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"); + } - // At this point, we have something to output. Convert metadata. - TT.hdr.mode = otoi(tar.mode, sizeof(tar.mode)); - TT.hdr.mode |= (char []){8,8,10,2,6,4,1,8}[tar.type-'0']<<12; - TT.hdr.uid = otoi(tar.uid, sizeof(tar.uid)); - TT.hdr.gid = otoi(tar.gid, sizeof(tar.gid)); - TT.hdr.mtime = otoi(tar.mtime, sizeof(tar.mtime)); + 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)); - TT.hdr.device = dev_makedev(maj, min); - - TT.hdr.uname = xstrndup(TT.owner ? TT.owner : tar.uname, sizeof(tar.uname)); - TT.hdr.gname = xstrndup(TT.group ? TT.group : tar.gname, sizeof(tar.gname)); + 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'; + } - if (TT.owner) TT.hdr.uid = TT.ouid; - else if (!FLAG(numeric_owner)) { - struct passwd *pw = getpwnam(TT.hdr.uname); - if (pw && (TT.owner || !FLAG(numeric_owner))) TT.hdr.uid = pw->pw_uid; + 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"); } - if (TT.group) TT.hdr.gid = TT.ggid; - else if (!FLAG(numeric_owner)) { - struct group *gr = getgrnam(TT.hdr.gname); - if (gr) TT.hdr.gid = gr->gr_gid; + 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 (!TT.hdr.link_target && *tar.link) - TT.hdr.link_target = xstrndup(tar.link, sizeof(tar.link)); - if (!TT.hdr.name) { - // Glue prefix and name fields together with / if necessary - i = strnlen(tar.prefix, sizeof(tar.prefix)); - TT.hdr.name = xmprintf("%.*s%s%.*s", i, tar.prefix, - (i && tar.prefix[i-1] != '/') ? "/" : "", - (int)sizeof(tar.name), tar.name); + if (longname) { + free(file_hdr->name); + file_hdr->name = longname; + longname = NULL; } - - // Old broken tar recorded dir as "file with trailing slash" - if (S_ISREG(TT.hdr.mode) && (s = strend(TT.hdr.name, "/"))) { - *s = 0; - TT.hdr.mode = (TT.hdr.mode & ~S_IFMT) | S_IFDIR; + if (longlink) { + free(file_hdr->link_target); + file_hdr->link_target = longlink; + longlink = NULL; } - // Non-regular files don't have contents stored in archive. - if ((TT.hdr.link_target && *TT.hdr.link_target) || !S_ISREG(TT.hdr.mode)) - TT.hdr.size = 0; - - // Files are seen even if excluded, so check them here. - // TT.seen points to first seen entry in TT.incl, or NULL if none yet. - if ((delete = filter(TT.incl, TT.hdr.name)) && TT.incl != TT.seen) { - if (!TT.seen) TT.seen = delete; - - // Move seen entry to end of list. - if (TT.incl == delete) TT.incl = TT.incl->next; - else for (walk = TT.incl; walk != TT.seen; walk = walk->next) { - if (walk == delete) { - dlist_pop(&walk); - dlist_add_nomalloc(&TT.incl, delete); - } - } + 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; } - // Skip excluded files - if (filter(TT.excl, TT.hdr.name) || (TT.incl && !delete)) - skippy(TT.hdr.size); - else if (FLAG(t)) { - if (FLAG(v)) { - struct tm *lc = localtime(TT.mtime ? &TT.mtt : &TT.hdr.mtime); - char perm[12], gname[12]; - - mode_to_string(TT.hdr.mode, perm); - printf("%s", perm); - sprintf(perm, "%u", TT.hdr.uid); - sprintf(gname, "%u", TT.hdr.gid); - printf(" %s/%s ", *TT.hdr.uname ? TT.hdr.uname : perm, - *TT.hdr.gname ? TT.hdr.gname : gname); - if (tar.type=='3' || tar.type=='4') printf("%u,%u", maj, min); - else printf("%9lld", (long long)TT.hdr.size); - sprintf(perm, ":%02d", lc->tm_sec); - printf(" %d-%02d-%02d %02d:%02d%s ", 1900+lc->tm_year, 1+lc->tm_mon, - lc->tm_mday, lc->tm_hour, lc->tm_min, FLAG(full_time) ? perm : ""); + 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", TT.hdr.name); - if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target); + printf("%s",file_hdr->name); + if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target); xputc('\n'); - skippy(TT.hdr.size); +SKIP: + tar_skip(tar_hdl, file_hdr->size); } else { - if (FLAG(v)) printf("%s\n", TT.hdr.name); - if (FLAG(O)) xsendfile_len(TT.fd, 0, TT.hdr.size); - else if (FLAG(to_command)) extract_to_command(); - else extract_to_disk(); + if (toys.optflags & FLAG_v) printf("%s\n",file_hdr->name); + tar_hdl->extract_handler(tar_hdl); } - - free(TT.hdr.name); - free(TT.hdr.link_target); - free(TT.hdr.uname); - free(TT.hdr.gname); - TT.hdr.name = TT.hdr.link_target = 0; + free(file_hdr->name); + free(file_hdr->link_target); + free(file_hdr->uname); + free(file_hdr->gname); } } -// Add copy of filename to TT.incl or TT.excl, minus trailing \n and / -static void trim_list(char **pline, long len) -{ - char *n = strdup(*pline); - int i = strlen(n); - - dlist_add(TT.X ? &TT.excl : &TT.incl, n); - if (i && n[i-1]=='\n') i--; - while (i && n[i-1] == '/') i--; - n[i] = 0; -} - void tar_main(void) { - char *s, **args = toys.optargs; - - // When extracting to command - signal(SIGPIPE, SIG_IGN); + struct archive_handler *tar_hdl; + int fd = 0; + struct arg_list *tmp; + char **args = toys.optargs; if (!geteuid()) toys.optflags |= FLAG_p; - if (TT.owner) TT.ouid = xgetuid(TT.owner); - if (TT.group) TT.ggid = xgetgid(TT.group); - if (TT.mtime) xparsedate(TT.mtime, &TT.mtt, (void *)&s, 1); - - // Collect file list. Note: trim_list appends to TT.incl when !TT.X - for (;TT.X; TT.X = TT.X->next) do_lines(xopenro(TT.X->arg), '\n', trim_list); - for (args = toys.optargs; *args; args++) trim_list(args, strlen(*args)); - for (;TT.T; TT.T = TT.T->next) do_lines(xopenro(TT.T->arg), '\n', trim_list); - - // Open archive file - if (FLAG(c)) { - if (!TT.incl) error_exit("empty archive"); - TT.fd = 1; - } - if (TT.f && strcmp(TT.f, "-")) - TT.fd = xcreate(TT.f, TT.fd*(O_WRONLY|O_CREAT|O_TRUNC), 0666); - - // grab archive inode - { - struct stat st; - - if (!fstat(TT.fd, &st)) { - TT.aino = st.st_ino; - TT.adev = st.st_dev; - } - } - - // Get destination directory - if (TT.C) xchdir(TT.C); - TT.cwd = xabspath(s = xgetcwd(), 1); - free(s); - // Are we reading? - if (FLAG(x)||FLAG(t)) { - if (FLAG(j)||FLAG(z)) { - int pipefd[2] = {TT.fd, -1}; + for (tmp = TT.exc; tmp; tmp = tmp->next) + tmp->arg = xstrdup(tmp->arg); //freeing at the end fails otherwise - xpopen_both((char *[]){FLAG(z)?"gunzip":"bunzip2", "-cf", "-", NULL}, - pipefd); - close(TT.fd); - TT.fd = pipefd[1]; - } + 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); - unpack_tar(); - if (TT.seen != TT.incl) { - if (!TT.seen) TT.seen = TT.incl; - while (TT.incl != TT.seen) { - error_msg("'%s' not in archive", TT.incl->data); - TT.incl = TT.incl->next; - } + 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; } - - // are we writing? (Don't have to test flag here, one of 3 must be set) - } else { - struct double_list *dl = TT.incl; - - if (FLAG(j)||FLAG(z)) { - int pipefd[2] = {-1, TT.fd}; - - xpopen_both((char *[]){FLAG(z)?"gzip":"bzip2", "-f", NULL}, pipefd); - close(TT.fd); - TT.fd = pipefd[0]; + 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); } - do { - TT.warn = 1; - dirtree_flagread(dl->data, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar); - } while (TT.incl != (dl = dl->next)); - - writeall(TT.fd, toybuf, 1024); + memset(toybuf, 0, 1024); + writeall(tar_hdl->src_fd, toybuf, 1024); + seen_inode(&TT.inodes, 0, 0); } if (CFG_TOYBOX_FREE) { - while(TT.hlc) free(TT.hlx[--TT.hlc].arg); - free(TT.hlx); - close(TT.fd); + 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); } } -- cgit v1.2.3