diff options
-rw-r--r-- | toys/pending/tar.c | 146 |
1 files changed, 86 insertions, 60 deletions
diff --git a/toys/pending/tar.c b/toys/pending/tar.c index 8fb047bf..4f9125f6 100644 --- a/toys/pending/tar.c +++ b/toys/pending/tar.c @@ -18,7 +18,7 @@ * Extract into dir same as filename, --restrict? "Tarball is splodey" * -USE_TAR(NEWTOY(tar, "&(no-recursion)(numeric-owner)(no-same-permissions)(overwrite)(exclude)*(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)*(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)) config TAR bool "tar" @@ -43,14 +43,21 @@ config TAR GLOBALS( char *f, *C; struct arg_list *T, *X; - char *to_command, *owner, *group; + char *to_command, *owner, *group, *mtime; struct arg_list *exclude; struct double_list *incl, *excl, *seen; struct string_list *dirs; - void *inodes; char *cwd; - int fd, ouid, ggid; + int fd, ouid, ggid, hlc, warn; + 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 { @@ -82,7 +89,7 @@ static void itoo(char *str, int len, unsigned long long val) } #define ITOO(x, y) itoo(x, sizeof(x), y) -//convert octal (or base-256) to int +// convert octal (or base-256) to int static unsigned long long otoi(char *str, unsigned len) { unsigned long long val = 0; @@ -105,25 +112,6 @@ struct inode_list { dev_t dev; }; -static struct inode_list *seen_inode(void **list, struct stat *st, char *name) -{ - 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; -} - // Calculate packet checksum, with cksum field treated as 8 spaces static unsigned cksum(void *data) { @@ -176,9 +164,10 @@ static void skippy(long long len) if (lskip(TT.fd, len)) perror_exit("EOF"); } -// actually void **, but automatic typecasting doesn't work with void ** :( +// 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); @@ -195,8 +184,8 @@ static void add_file(char **nam, struct stat *st) struct inode_list *node = node; int i, fd =-1; char *c, *p, *name = *nam, *lnk, *hname; - 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)) return; @@ -205,19 +194,27 @@ static void add_file(char **nam, struct stat *st) free(name); *nam = name = lnk; } - hname = name; - //remove leading '/' or relative path '../' component - if (*hname == '/') hname++; + + // 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]) return; + if (c[2]=='/') c = hname = c+3; + } else c+= 2; + } if (!*hname) return; - while ((c = strstr(hname, "../"))) hname = c + 3; - if (warn && hname != name) { + + if (TT.warn && hname != name) { fprintf(stderr, "removing leading '%.*s' from member names\n", (int)(hname-name), name); - warn = 0; + TT.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)); @@ -227,10 +224,26 @@ static void add_file(char **nam, struct stat *st) ITOO(hdr.size, 0); //set size later ITOO(hdr.mtime, st->st_mtime); - // Hard link or symlink? - i = !!S_ISLNK(st->st_mode); - if (i || (node = seen_inode(&TT.inodes, st, hname))) { - hdr.type = '1'+i; + // Hard link or symlink? i=0 neither, i=1 hardlink, i=2 symlink + if (S_ISLNK(st->st_mode)) i = 2; + else if (st->st_nlink>1 && !S_ISDIR(st->st_mode)) { + for (i = 0; i<TT.hlc; i++) + if (st->st_ino == TT.hlx[i].ino && st->st_dev == TT.hlx[i].dev) + break; + if (i != TT.hlc) i = 1; + else { + i = 0; + 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++; + } + } else i = 0; + + // Handle file types + if (i) { + hdr.type = '0'+i; if (!(lnk = i ? xreadlink(name) : node->arg)) return perror_msg("readlink"); if (strlen(lnk) > sizeof(hdr.link)) write_longname(lnk, 'K'); strncpy(hdr.link, lnk, sizeof(hdr.link)); @@ -262,15 +275,13 @@ static void add_file(char **nam, struct stat *st) itoo(hdr.chksum, sizeof(hdr.chksum)-1, cksum(&hdr)); hdr.chksum[7] = ' '; - if (FLAG(v)) printf("%s\n",hname); - xwrite(TT.fd, (void*)&hdr, 512); + if (FLAG(v)) printf("%s\n", hname); - //write actual data to archive + // Write header and data to archive + xwrite(TT.fd, &hdr, 512); if (hdr.type != '0') return; //nothing to write - if ((fd = open(name, O_RDONLY)) < 0) { - perror_msg("can't open '%s'", name); - return; - } + if ((fd = open(name, O_RDONLY)) < 0) + return perror_msg("can't open '%s'", name); xsendfile_pad(fd, TT.fd, st->st_size); if (st->st_size%512) writeall(TT.fd, toybuf, (512-(st->st_size%512))); close(fd); @@ -315,7 +326,7 @@ static void extract_to_command(void) setenv("TAR_FILENAME", TT.hdr.name, 1); setenv("TAR_UNAME", TT.hdr.uname, 1); setenv("TAR_GNAME", TT.hdr.gname, 1); - sprintf(buf, "%0o", (int)TT.hdr.mtime); + sprintf(buf, "%0llo", (long long)TT.hdr.mtime); setenv("TAR_MTIME", buf, 1); sprintf(buf, "%0o", TT.hdr.uid); setenv("TAR_UID", buf, 1); @@ -336,7 +347,6 @@ static void extract_to_command(void) } } - // Do pending directory utimes(), NULL to flush all. static int dirflush(char *name) { @@ -345,8 +355,8 @@ static int dirflush(char *name) // Barf if name not in TT.cwd if (name) { ss = s = xabspath(name, -1); - if (TT.cwd && (!strstart(&ss, TT.cwd) || (*ss && *ss!='/'))) { - error_msg("'%s' not under '%s'", ss, TT.cwd); + if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || *ss!='/')) { + error_msg("'%s' not under '%s'", name, TT.cwd); free(s); return 1; @@ -359,10 +369,15 @@ static int dirflush(char *name) long long ll = *(long long *)TT.dirs->str; struct timeval times[2] = {{ll, 0},{ll, 0}}; + // If next file is under (or equal to) this dir, keep waiting if (name && strstart(&ss, ss = s) && (!*ss || *ss=='/')) break; - if (utimes(TT.dirs->str+sizeof(long long), times)) perror_msg("utimes %lld %s", *(long long *)TT.dirs->str, TT.dirs->str+sizeof(long long)); + + 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)); } + free(s); // name was under TT.cwd return 0; @@ -458,6 +473,7 @@ static void unpack_tar(void) struct double_list *walk, *delete; struct tar_hdr tar; int i, and = 0; + unsigned maj, min; char *s; for (;;) { @@ -524,8 +540,9 @@ static void unpack_tar(void) 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)); - TT.hdr.device = dev_makedev(otoi(tar.major, sizeof(tar.major)), - otoi(tar.minor, sizeof(tar.minor))); + 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)); @@ -569,14 +586,16 @@ static void unpack_tar(void) skippy(TT.hdr.size); else if (FLAG(t)) { if (FLAG(v)) { - struct tm *lc = localtime(&TT.hdr.mtime); + struct tm *lc = localtime(TT.mtime ? &TT.mtt : &TT.hdr.mtime); char perm[11]; mode_to_string(TT.hdr.mode, perm); - printf("%s %s/%s %9lld %d-%02d-%02d %02d:%02d:%02d ", perm, - TT.hdr.uname, TT.hdr.gname, (long long)TT.hdr.size, - 1900+lc->tm_year, 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, - lc->tm_min, lc->tm_sec); + printf("%s %s/%s ", perm, TT.owner ? TT.owner : TT.hdr.uname, + TT.group ? TT.group : TT.hdr.gname); + if (tar.type=='3' || tar.type=='4') printf("%u,%u", maj, min); + else printf("%9lld", (long long)TT.hdr.size); + printf(" %d-%02d-%02d %02d:%02d:%02d ", 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); @@ -619,6 +638,7 @@ void tar_main(void) 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); @@ -635,8 +655,7 @@ void tar_main(void) // Get destination directory if (TT.C) xchdir(TT.C); - s = xgetcwd(); - TT.cwd = (strcmp(s, "/")) ? xabspath(s = xgetcwd(), 1) : 0; + TT.cwd = xabspath(s = xgetcwd(), 1); free(s); // Are we reading? @@ -659,7 +678,7 @@ void tar_main(void) } } - // are we writing? (Don't have to test flag here one of 3 must be set) + // are we writing? (Don't have to test flag here, one of 3 must be set) } else { struct double_list *dl = TT.incl; @@ -670,9 +689,16 @@ void tar_main(void) close(TT.fd); TT.fd = pipefd[0]; } - do dirtree_flagread(dl->data, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar); - while (TT.incl != (dl = dl->next)); + 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); } + + if (CFG_TOYBOX_FREE) { + free (TT.hlx); + close(TT.fd); + } } |