diff --git a/LICENSE b/LICENSE index 8f1caaf..ab13f3c 100644 --- a/LICENSE +++ b/LICENSE @@ -11,6 +11,7 @@ MIT/X Consortium License © 2012 Kurt H. Maier © 2012 Christoph Lohmann <20h@r-36.net> © 2012 David Galos +© 2012 Enno Boland © 2012 Robert Ransom © 2013 Jakob Kramer diff --git a/Makefile b/Makefile index f825ec4..38f4efa 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,7 @@ SRC = \ sponge.c \ sync.c \ tail.c \ + tar.c \ tee.c \ test.c \ touch.c \ diff --git a/tar.1 b/tar.1 new file mode 100644 index 0000000..414ca4b --- /dev/null +++ b/tar.1 @@ -0,0 +1,27 @@ +.TH TAR 1 sbase\-VERSION +.SH NAME +tar \- create, list or extract a tape archive +.SH SYNOPSIS +.B tar +.BR c | t | x +.RI [ path ] +.SH DESCRIPTION +.B tar +is the standard file archiver. Generally the archices +created with it are further compressed. +.SH OPTIONS +.TP +.B x +extract tarball from stdin +.TP +.B t +list all files in tarball from stdin +.TP +.BI c\ path +creates tarball from +.I path +and prints it to stdout +.SH SEE ALSO +.IR ar (1) +.IR gzip (1) +.IR bzip2 (1) diff --git a/tar.c b/tar.c new file mode 100644 index 0000000..ba4d41a --- /dev/null +++ b/tar.c @@ -0,0 +1,204 @@ +/* See LICENSE file for copyright and license details */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "util.h" + +enum Header { + NAME=0, MODE = 100, UID = 108, GID = 116, SIZE = 124, MTIME = 136, CHKSUM=148, + TYPE = 156, LINK = 157, MAGIC=257, VERS=263, UNAME=265, GNAME=297, MAJ = 329, + MIN = 337, END = 512 +}; + +enum Type { + REG = '0', HARDLINK = '1', SYMLINK = '2', CHARDEV='3', BLOCKDEV='4', + DIRECTORY='5', FIFO='6' +}; + +static int archive(const char *, const struct stat *, int); +static int unarchive(char *, int, char[END]); +static int print(char *, int, char[END]); +static int c(char *); +static int xt(int (*)(char*, int, char[END])); + +static void +usage(void) +{ + eprintf("usage: tar t|x|c [path]\n"); +} + +int +main(int argc, char *argv[]) +{ + if(argc < 2 || argc > 3 || strlen(argv[1]) != 1) + usage(); + switch(argv[1][0]) { + case 'c': + if(argc<3) + usage(); + return c(argv[2]); + case 'x': + return xt(unarchive); + case 't': + return xt(print); + default: + usage(); + } + return EXIT_FAILURE; +} + +int +archive(const char* path, const struct stat* sta, int type) +{ + char b[END]; + FILE *f = NULL; + struct stat st; + struct passwd *pw; + struct group *gr; + mode_t mode; + unsigned sum = 0; + int x; + + lstat(path, &st); + memset(b, 0, END); + snprintf(b+NAME, 100, "%s", path); + snprintf(b+MODE, 8, "%.7o", (unsigned)st.st_mode&0777); + snprintf(b+UID, 8, "%.7o", (unsigned)st.st_uid); + snprintf(b+GID, 8, "%.7o", (unsigned)st.st_gid); + snprintf(b+SIZE, 12, "%.11o", 0); + snprintf(b+MTIME,12, "%.11o", (unsigned)st.st_mtime); + memcpy(b+MAGIC, "ustar", strlen("ustar")+1); + memcpy(b+VERS, "00", strlen("00")); + pw = getpwuid(st.st_uid); + snprintf(b+UNAME, 32, "%s", pw->pw_name); + gr = getgrgid(st.st_gid); + snprintf(b+GNAME, 32, "%s", gr->gr_name); + mode = st.st_mode; + + if(S_ISREG(mode)) { + b[TYPE] = REG; + snprintf(b+SIZE, 12, "%.11o", (unsigned)st.st_size); + f = fopen(path, "r"); + } else if(S_ISDIR(mode)) { + b[TYPE] = DIRECTORY; + } else if(S_ISLNK(mode)) { + b[TYPE] = SYMLINK; + readlink(path, b+LINK, 99); + } else if(S_ISCHR(mode)) { + b[TYPE] = CHARDEV; + snprintf(b+MAJ, 8, "%.7o", (unsigned)major(st.st_dev)); + snprintf(b+MIN, 8, "%.7o", (unsigned)minor(st.st_dev)); + } else if(S_ISBLK(mode)) { + b[TYPE] = BLOCKDEV; + snprintf(b+MAJ, 8, "%.7o", (unsigned)major(st.st_dev)); + snprintf(b+MIN, 8, "%.7o", (unsigned)minor(st.st_dev)); + } else if(S_ISFIFO(mode)) { + b[TYPE] = FIFO; + } + memset(b+CHKSUM, ' ', 8); + for(x = 0; x < END; x++) + sum+=b[x]; + snprintf(b+CHKSUM, 8, "%.7o", sum); + fwrite(b, END, 1, stdout); + if(!f) + return 0; + while((x = fread(b, 1, END, f)) > 0){ + if(x < END) + memset(b+x, 0, END-x); + fwrite(b, END, 1, stdout); + } + fclose(f); + return 0; +} + +int +unarchive(char *fname, int l, char b[END]) +{ + static char lname[101] = {0}; + FILE *f = NULL; + memcpy(lname, b+LINK, 100); + + unlink(fname); + switch(b[TYPE]) { + case REG: + if(!(f = fopen(fname,"w")) || chmod(fname,strtoul(b + MODE,0,8))) + perror(fname); + break; + case HARDLINK: + if(!link(lname,fname)) + perror(fname); + break; + case SYMLINK: + if(!symlink(lname,fname)) + perror(fname); + break; + case DIRECTORY: + if(mkdir(fname,(mode_t) strtoull(b + MODE,0,8))) + perror(fname); + break; + case CHARDEV: + case BLOCKDEV: + if(mknod(fname, (b[TYPE] == '3' ? S_IFCHR : S_IFBLK) | strtoul(b + MODE,0,8), + makedev(strtoul(b + MAJ,0,8), + strtoul(b + MIN,0,8)))) + perror(fname); + break; + case FIFO: + if(mknod(fname, S_IFIFO | strtoul(b + MODE,0,8), 0)) + perror(fname); + break; + default: + fprintf(stderr,"tar: '%c': unsupported filetype\n",b[TYPE]); + } + if(getuid() == 0 && chown(fname, strtoul(b + UID,0,8),strtoul(b + GID,0,8))) + perror(fname); + + for(; l > 0; l -= END){ + fread(b, END, 1, stdin); + if(f) + fwrite(b, MIN(l, 512), 1, f); + } + if(f) + fclose(f); + return 0; +} + +int +print(char *fname, int l, char b[END]) +{ + puts(fname); + for(; l > 0; l -= END) + fread(b, END, 1, stdin); + return 0; +} + +int +c(char *dir) +{ + ftw(dir, archive, 128); /* OPEN_MAX); */ + return EXIT_SUCCESS; +} + +int +xt(int (*fn)(char*, int, char[END])) +{ + int l; + char b[END],fname[101]; + fname[100] = '\0'; + + while(fread(b, END, 1, stdin)){ + if(*b == '\0') + break; + memcpy(fname, b, 100); + l = strtol(b+SIZE, 0, 8); + fn(fname, l, b); + } + return EXIT_SUCCESS; +}