aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/pending/tar.c244
1 files changed, 113 insertions, 131 deletions
diff --git a/toys/pending/tar.c b/toys/pending/tar.c
index 5bceb1a3..36553bf2 100644
--- a/toys/pending/tar.c
+++ b/toys/pending/tar.c
@@ -2,36 +2,45 @@
*
* Copyright 2014 Ashwini Kumar <ak.ashwini81@gmail.com>
*
- * USTAR interchange format is of interest in
- * See http://http://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html
+ * For 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
+ *
+ * Toybox will never implement the "pax" command as a matter of policy.
+ *
* For writing to external program
* http://www.gnu.org/software/tar/manual/html_node/Writing-to-an-External-Program.html
+ *
+ * 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, "&(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))
+todo: support .txz
+todo: directory timestamps not set on extract
+todo: extract into chmod 000 directory
+
config TAR
bool "tar"
default n
help
- usage: tar -[cxtjzhmvO] [-X FILE] [-T FILE] [-f TARFILE] [-C DIR]
-
- Create, extract, or list files from a tar file
-
- Operation:
- c Create
- f Name of TARFILE ('-' for stdin/out)
- h Follow symlinks
- j (De)compress using bzip2
- m Don't restore mtime
- t List
- v Verbose
- x Extract
- z (De)compress using gzip
- C Change to DIR before operation
- O Extract to stdout
- exclude=FILE File to exclude
- X File with names to exclude
- T File with names to include
+ 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 Force bzip2 format z Force gzip format
+ O Extract to stdout
+ X File of names to exclude
+ T File of names to include
+ --exclude=FILE File pattern(s) to exclude
*/
#define FOR_tar
@@ -43,8 +52,12 @@ GLOBALS(
char *to_command;
struct arg_list *exc;
+// exc is an argument but inc isn't?
struct arg_list *inc, *pass;
void *inodes, *handle;
+ char *cwd;
+ int fd;
+ unsigned short offset; // only ever used to calculate 512 byte padding
)
struct tar_hdr {
@@ -64,9 +77,7 @@ struct file_header {
};
struct archive_handler {
- int src_fd;
struct file_header file_hdr;
- off_t offset;
void (*extract_handler)(struct archive_handler*);
};
@@ -123,29 +134,29 @@ static struct inode_list *seen_inode(void **list, struct stat *st, char *name)
static void write_longname(struct archive_handler *tar, char *name, char type)
{
- struct tar_hdr tmp;
+ struct tar_hdr tmp[2];
unsigned int sum = 0;
int i, sz = strlen(name) +1;
- char buf[512] = {0,};
-
- memset(&tmp, 0, sizeof(tmp));
- strcpy(tmp.name, "././@LongLink");
- memset(tmp.mode, '0', sizeof(tmp.mode)-1);
- memset(tmp.uid, '0', sizeof(tmp.uid)-1);
- memset(tmp.gid, '0', sizeof(tmp.gid)-1);
- memset(tmp.size, '0', sizeof(tmp.size)-1);
- memset(tmp.mtime, '0', sizeof(tmp.mtime)-1);
- itoo(tmp.size, sizeof(tmp.size), sz);
- tmp.type = type;
- memset(tmp.chksum, ' ', 8);
- strcpy(tmp.magic, "ustar ");
- for (i = 0; i < 512; i++) sum += (unsigned int)((char*)&tmp)[i];
- itoo(tmp.chksum, sizeof(tmp.chksum)-1, sum);
-
- writeall(tar->src_fd, (void*) &tmp, sizeof(tmp));
- //write name to archive
- writeall(tar->src_fd, name, sz);
- if (sz%512) writeall(tar->src_fd, buf, (512-(sz%512)));
+
+ memset(tmp, 0, sizeof(tmp));
+ strcpy(tmp->name, "././@LongLink");
+ memset(tmp->mode, '0', sizeof(tmp->mode)-1);
+ memset(tmp->uid, '0', sizeof(tmp->uid)-1);
+ memset(tmp->gid, '0', sizeof(tmp->gid)-1);
+ itoo(tmp->size, sizeof(tmp->size), sz);
+ memset(tmp->mtime, '0', sizeof(tmp->mtime)-1);
+ tmp->type = type;
+ strcpy(tmp->magic, "ustar ");
+
+ // Calculate checksum
+ memset(tmp->chksum, ' ', 8);
+ for (i = 0; i < 512; i++) sum += ((char *)tmp)[i];
+ itoo(tmp->chksum, sizeof(tmp->chksum)-1, sum);
+
+ // write header and name, padded with NUL to block size
+ xwrite(TT.fd, tmp, sizeof(*tmp));
+ xwrite(TT.fd, name, sz);
+ xwrite(TT.fd, tmp+1, 512-(sz%512));
}
static int filter(struct arg_list *lst, char *name)
@@ -249,7 +260,7 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
for (i = 0; i < 512; i++) sum += ((char*)&hdr)[i];
itoo(hdr.chksum, sizeof(hdr.chksum)-1, sum);
if (FLAG(v)) printf("%s\n",hname);
- writeall(tar->src_fd, (void*)&hdr, 512);
+ xwrite(TT.fd, (void*)&hdr, 512);
//write actual data to archive
if (hdr.type != '0') return; //nothing to write
@@ -257,8 +268,8 @@ static void add_file(struct archive_handler *tar, char **nam, struct stat *st)
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)));
+ copy_in_out(fd, TT.fd, st->st_size);
+ if (st->st_size%512) writeall(TT.fd, buf, (512-(st->st_size%512)));
close(fd);
}
@@ -269,7 +280,7 @@ static int add_to_tar(struct dirtree *node)
struct archive_handler *hdl = (struct archive_handler*)TT.handle;
if (!dirtree_notdotdot(node)) return 0;
- if (!fstat(hdl->src_fd, &st) && st.st_dev == node->st.st_dev
+ if (!fstat(TT.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.f);
return 0;
@@ -297,11 +308,12 @@ static void compress_stream(struct archive_handler *tar_hdl)
char *argv[] = {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
+ dup2(TT.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
+// TODO doesn't this leak pipefd[1]?
+ dup2(pipefd[1], TT.fd); //write to pipe
}
}
@@ -309,8 +321,8 @@ static void extract_to_stdout(struct archive_handler *tar)
{
struct file_header *file_hdr = &tar->file_hdr;
- copy_in_out(tar->src_fd, 0, file_hdr->size);
- tar->offset += file_hdr->size;
+ copy_in_out(TT.fd, 0, file_hdr->size);
+ TT.offset += file_hdr->size;
}
static void extract_to_command(struct archive_handler *tar)
@@ -349,8 +361,8 @@ static void extract_to_command(struct archive_handler *tar)
xexec(argv);
} else {
xclose(pipefd[0]); // Close unused read end
- copy_in_out(tar->src_fd, pipefd[1], file_hdr->size);
- tar->offset += file_hdr->size;
+ copy_in_out(TT.fd, pipefd[1], file_hdr->size);
+ TT.offset += file_hdr->size;
xclose(pipefd[1]);
waitpid(cpid, &status, 0);
if (WIFSIGNALED(status))
@@ -365,14 +377,12 @@ static void extract_to_disk(struct archive_handler *tar)
struct stat ex;
struct file_header *file_hdr = &tar->file_hdr;
+// while not if
flags = strlen(file_hdr->name);
- if (flags>2) {
+ if (flags>2)
if (strstr(file_hdr->name, "/../") || !strcmp(file_hdr->name, "../") ||
!strcmp(file_hdr->name+flags-3, "/.."))
- {
error_msg("drop %s", file_hdr->name);
- }
- }
if (file_hdr->name[flags-1] == '/') file_hdr->name[flags-1] = 0;
//Regular file with preceding path
@@ -423,8 +433,8 @@ static void extract_to_disk(struct archive_handler *tar)
//copy file....
COPY:
- copy_in_out(tar->src_fd, dst_fd, file_hdr->size);
- tar->offset += file_hdr->size;
+ copy_in_out(TT.fd, dst_fd, file_hdr->size);
+ TT.offset += file_hdr->size;
close(dst_fd);
if (S_ISLNK(file_hdr->mode)) return;
@@ -439,7 +449,7 @@ COPY:
if (pw) u = pw->pw_uid;
if (gr) g = gr->gr_gid;
}
- if (chown(file_hdr->name, u, g))
+ if (!geteuid() && chown(file_hdr->name, u, g))
perror_msg("chown %d:%d '%s'", u, g, file_hdr->name);;
}
@@ -464,29 +474,14 @@ static void add_to_list(struct arg_list **llist, char *name)
name[strlen(name)-1] = '\0';
}
-static void add_from_file(struct arg_list **llist, struct arg_list *flist)
+static void file_to_list(char *file, struct arg_list **llist)
{
- char *line = NULL;
-
- while (flist) {
- int fd = 0;
-
- if (strcmp((char *)flist->arg, "-"))
- fd = xopen((char *)flist->arg, O_RDONLY);
-
- while ((line = get_line(fd))) {
- add_to_list(llist, line);
- }
- if (fd) close(fd);
- flist = flist->next;
- }
-}
+ int fd = xopenro(file);
+ char *line = 0;
-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;
+ while ((line = get_line(fd))) add_to_list(llist, xstrdup(line));
+ if (fd) close(fd);
+ free(line);
}
//convert octal to int
@@ -516,12 +511,12 @@ static void extract_stream(struct archive_handler *tar_hdl)
char *argv[] =
{FLAG(z)?"gunzip":"bunzip2", "-cf", "-", NULL};
xclose(pipefd[0]); /* Close unused read*/
- dup2(tar_hdl->src_fd, 0);
+ dup2(TT.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
+ dup2(pipefd[0], TT.fd); //read from pipe
}
}
@@ -529,9 +524,9 @@ static char *process_extended_hdr(struct archive_handler *tar, int size)
{
char *value = NULL, *p, *buf = xzalloc(size+1);
- if (readall(tar->src_fd, buf, size) != size) error_exit("short read");
+ if (readall(TT.fd, buf, size) != size) error_exit("short read");
buf[size] = 0;
- tar->offset += size;
+ TT.offset += size;
p = buf;
while (size) {
@@ -560,17 +555,6 @@ static char *process_extended_hdr(struct archive_handler *tar, int size)
return value;
}
-static void tar_skip(struct archive_handler *tar, int sz)
-{
- int x;
-
- while ((x = lskip(tar->src_fd, sz))) {
- tar->offset += sz - x;
- sz = x;
- }
- tar->offset += sz;
-}
-
static void unpack_tar(struct archive_handler *tar_hdl)
{
struct tar_hdr tar;
@@ -581,12 +565,13 @@ static void unpack_tar(struct archive_handler *tar_hdl)
while (1) {
cksum = 0;
- if (tar_hdl->offset % 512) {
- sz = 512 - tar_hdl->offset % 512;
- tar_skip(tar_hdl, sz);
+ if (TT.offset % 512) {
+ sz = 512 - TT.offset % 512;
+ lskip(TT.fd, sz);
+ TT.offset += sz;
}
- i = readall(tar_hdl->src_fd, &tar, 512);
- tar_hdl->offset += i;
+ i = readall(TT.fd, &tar, 512);
+ TT.offset += i;
if (i != 512) {
if (i >= 2) goto CHECK_MAGIC; //may be a small (<512 byte)zipped file
error_exit("read error");
@@ -601,9 +586,9 @@ static void unpack_tar(struct archive_handler *tar_hdl)
// 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)) {
+ && !lseek(TT.fd, -i, SEEK_CUR)) {
toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
- tar_hdl->offset -= i;
+ TT.offset -= i;
extract_stream(tar_hdl);
continue;
}
@@ -673,14 +658,14 @@ CHECK_MAGIC:
break;
case 'K':
longlink = xzalloc(file_hdr->size +1);
- xread(tar_hdl->src_fd, longlink, file_hdr->size);
- tar_hdl->offset += file_hdr->size;
+ xread(TT.fd, longlink, file_hdr->size);
+ TT.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;
+ xread(TT.fd, longname, file_hdr->size);
+ TT.offset += file_hdr->size;
continue;
case 'D':
case 'M':
@@ -688,7 +673,8 @@ CHECK_MAGIC:
case 'S':
case 'V':
case 'g': // pax global header
- tar_skip(tar_hdl, file_hdr->size);
+ lskip(TT.fd, file_hdr->size);
+ TT.offset += file_hdr->size;
continue;
case 'x': // pax extended header
free(longname);
@@ -737,7 +723,8 @@ CHECK_MAGIC:
if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target);
xputc('\n');
SKIP:
- tar_skip(tar_hdl, file_hdr->size);
+ lskip(TT.fd, file_hdr->size);
+ TT.offset += file_hdr->size;
} else {
if (FLAG(v)) printf("%s\n",file_hdr->name);
tar_hdl->extract_handler(tar_hdl);
@@ -752,30 +739,33 @@ SKIP:
void tar_main(void)
{
struct archive_handler *tar_hdl;
- int fd = 0;
struct arg_list *tmp;
- char **args = toys.optargs;
+ char *s, **args = toys.optargs;
if (!geteuid()) toys.optflags |= FLAG_p;
- for (tmp = TT.exc; tmp; tmp = tmp->next)
- tmp->arg = xstrdup(tmp->arg); //freeing at the end fails otherwise
-
- while (*args) add_to_list(&TT.inc, xstrdup(*args++));
- if (TT.X) add_from_file(&TT.exc, TT.X);
- if (TT.T) add_from_file(&TT.inc, TT.T);
+ // Collect file list
+ while (*args) add_to_list(&TT.inc, *args++);
+ for (;TT.T; TT.T = TT.T->next) file_to_list(TT.T->arg, &TT.inc);
+ for (;TT.X; TT.X = TT.X->next) file_to_list(TT.X->arg, &TT.exc);
+ // Open archive file
if (FLAG(c)) {
if (!TT.inc) error_exit("empty archive");
- fd = 1;
+ TT.fd = 1;
}
if (TT.f && strcmp(TT.f, "-"))
- fd = xcreate(TT.f, fd*(O_WRONLY|O_CREAT|O_TRUNC), 0666);
+ TT.fd = xcreate(TT.f, TT.fd*(O_WRONLY|O_CREAT|O_TRUNC), 0666);
+
+ // Get destination directory
if (TT.C) xchdir(TT.C);
+ TT.cwd = xabspath(s = xgetcwd(), 1);
+ free(s);
- tar_hdl = init_handler();
- tar_hdl->src_fd = fd;
+ tar_hdl = xzalloc(sizeof(struct archive_handler));
+ tar_hdl->extract_handler = extract_to_disk;
+ // Are we reading?
if (FLAG(x) || FLAG(t)) {
if (FLAG(O)) tar_hdl->extract_handler = extract_to_stdout;
if (FLAG(to_command)) {
@@ -796,15 +786,7 @@ void tar_main(void)
dirtree_flagread(tmp->arg, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar);
}
memset(toybuf, 0, 1024);
- writeall(tar_hdl->src_fd, toybuf, 1024);
+ writeall(TT.fd, toybuf, 1024);
seen_inode(&TT.inodes, 0, 0);
}
-
- if (CFG_TOYBOX_FREE) {
- close(tar_hdl->src_fd);
- free(tar_hdl);
- llist_traverse(TT.exc, llist_free_arg);
- llist_traverse(TT.inc, llist_free_arg);
- llist_traverse(TT.pass, llist_free_arg);
- }
}