aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-05-09 03:13:37 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-05-09 03:13:37 +0000
commitd362c3db9f259110d485b908cd2874141e736432 (patch)
tree95cab695760fe2fc7e7e40cee6bd6d249ac97619
parent04a9e8a4d398b1aa72deec8b8bfd6ac58ef715c8 (diff)
parent793a3cae970b45acde49093360d746726f25474e (diff)
downloadandroid_external_toybox-lineage-17.0.tar.gz
android_external_toybox-lineage-17.0.tar.bz2
android_external_toybox-lineage-17.0.zip
Snap for 5546586 from 793a3cae970b45acde49093360d746726f25474e to qt-releaselineage-17.0
Change-Id: I8e473e1e4a53cc1069872a0c9807e1ccf0d550a2
-rw-r--r--generated/flags.h22
-rw-r--r--generated/globals.h37
-rw-r--r--generated/help.h2
-rw-r--r--generated/newtoys.h2
-rw-r--r--toys/pending/tar.c1200
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 <DEFAULT: /var/log/messages>\n-f FILE Config file <DEFAULT: /etc/syslog.conf>\n-p Alternative unix domain socket <DEFAULT : /dev/log>\n-n Avoid auto-backgrounding\n-S Smaller output\n-m MARK interval <DEFAULT: 20 minutes> (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 <ak.ashwini81@gmail.com>
*
- * 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; i<TT.hlc; i++) {
- if (st->st_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; 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 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; p += len) {
- i = sscanf(p, "%u path=%n", &len, &n);
- if (i<1 || len<4 || len>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);
}
}