commit 2c75eb98d9b4623f787a3c9bc97c6f29a0f4d59c
parent 9ac01f59be72a4f1c922774651e4fd715b1e065e
Author: David Galos <galosd83@students.rowan.edu>
Date: Thu, 18 Jul 2013 11:15:35 -0400
Adding tar.
Diffstat:
M | Makefile | | | 1 | + |
A | tar.1 | | | 62 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | tar.c | | | 298 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 361 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -69,6 +69,7 @@ SRC = \
sponge.c \
sync.c \
tail.c \
+ tar.c \
tee.c \
test.c \
touch.c \
diff --git a/tar.1 b/tar.1
@@ -0,0 +1,62 @@
+.TH TAR 1 sbase\-VERSION
+.SH NAME
+tar \- create, list or extract a tape archive
+.SH SYNOPSIS
+.B tar
+.RB [ \-f
+.IR tarfile]
+.RB [ \-C
+.IR dir ]
+.RB [ - ] x | t
+
+.B tar
+.RB [ \-f
+.IR tarfile]
+.RB [ \-C
+.IR dir ]
+.RB [ - ] c
+.I dir
+
+.B tar
+.RB [ \-C
+.IR dir ]
+.B cf
+.I tarfile
+.I dir
+
+.B tar
+.RB [ \-C
+.IR dir ]
+.B x|tf
+.I tarfile
+
+.SH DESCRIPTION
+.B tar
+is the standard file archiver. Generally the archives
+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
+.TP
+.BI f\ tarfile
+Make
+.I tarfile
+be the archive, rather than stdin or stdout.
+.TP
+.BI C\ dir
+Change dierctory to
+.I dir
+before beginning.
+.SH SEE ALSO
+.IR ar (1)
+.IR gzip (1)
+.IR bzip2 (1)
diff --git a/tar.c b/tar.c
@@ -0,0 +1,298 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <ftw.h>
+#include <grp.h>
+#include <pwd.h>
+#include "util.h"
+
+typedef struct Header Header;
+struct Header {
+ char name[100];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char type;
+ char link[100];
+ char magic[6];
+ char version[2];
+ char uname[32];
+ char gname[32];
+ char major[8];
+ char minor[8];
+};
+
+enum {
+ Blksiz = 512
+};
+
+enum Type {
+ REG = '0', HARDLINK = '1', SYMLINK = '2', CHARDEV = '3',
+ BLOCKDEV = '4', DIRECTORY = '5', FIFO = '6'
+};
+
+static void putoctal(char *, unsigned, int);
+static int strlcpy(char *, const char *, int n);
+static int archive(const char *, const struct stat *, int);
+static int unarchive(char *, int, char[Blksiz]);
+static int print(char *, int , char[Blksiz]);
+static void c(char *);
+static void xt(int (*)(char*, int, char[Blksiz]));
+
+static FILE *tarfile;
+
+static void
+usage(void)
+{
+ eprintf("usage: tar [-f tarfile] [-C dir] [-]x|t\n"
+ " tar [-f tarfile] [-C dir] [-]c dir\n"
+ " tar [-C dir] cf tarfile dir\n"
+ " tar [-C dir] x|tf tarfile\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *file, *dir, *ap;
+ char mode = '\0';
+
+ ARGBEGIN {
+ case 'x':
+ case 'c':
+ case 't':
+ if(mode)
+ usage();
+ mode = ARGC();
+ break;
+ case 'C':
+ dir = EARGF(usage());
+ break;
+ case 'f':
+ file = EARGF(usage());
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ if(!mode) {
+ if(argc < 1)
+ usage();
+
+ for(ap = argv[0]; *ap; ap++) {
+ switch(*ap) {
+ case 'x':
+ case 'c':
+ case 't':
+ if(mode)
+ usage();
+ mode = *ap;
+ break;
+ case 'f':
+ if(argc < 2)
+ usage();
+ argc--, argv++;
+ file = argv[0];
+ break;
+ case 'C':
+ if(argc < 2)
+ usage();
+ argc--, argv++;
+ dir = argv[0];
+ break;
+ default:
+ usage();
+ }
+ }
+ argc--, argv++;
+ }
+
+ if(!mode || argc != (mode == 'c'))
+ usage();
+
+ if(file) {
+ tarfile = fopen(file, (mode == 'c') ? "wb" : "rb");
+ if(!tarfile)
+ eprintf("tar: open '%s':", file);
+ } else {
+ tarfile = (mode == 'c') ? stdout : stdin;
+ }
+
+ if(dir)
+ chdir(dir);
+
+ if(mode == 'c') {
+ c(argv[0]);
+ } else {
+ xt((mode == 'x') ? unarchive : print);
+ }
+
+ return 0;
+}
+
+void
+putoctal(char *dst, unsigned num, int n)
+{
+ snprintf(dst, n, "%.*o", n-1, num);
+}
+
+int
+strlcpy(char *dst, const char *src, int n)
+{
+ return snprintf(dst, n, "%s", src);
+}
+
+int
+archive(const char* path, const struct stat* sta, int type)
+{
+ unsigned char b[Blksiz];
+ unsigned chksum;
+ int l, x;
+ Header *h = (void*)b;
+ FILE *f = NULL;
+ struct stat st;
+ struct passwd *pw;
+ struct group *gr;
+ mode_t mode;
+
+ lstat(path, &st);
+ pw = getpwuid(st.st_uid);
+ gr = getgrgid(st.st_gid);
+
+ memset(b, 0, sizeof b);
+ strlcpy (h->name, path, sizeof h->name);
+ putoctal(h->mode, (unsigned)st.st_mode&0777, sizeof h->mode);
+ putoctal(h->uid, (unsigned)st.st_uid, sizeof h->uid);
+ putoctal(h->gid, (unsigned)st.st_gid, sizeof h->gid);
+ putoctal(h->size, 0, sizeof h->size);
+ putoctal(h->mtime, (unsigned)st.st_mtime, sizeof h->mtime);
+ memcpy(h->magic, "ustar", sizeof h->magic);
+ memcpy(h->version, "00", sizeof h->version);
+ strlcpy(h->uname, pw->pw_name, sizeof h->uname);
+ strlcpy(h->gname, gr->gr_name, sizeof h->gname);
+
+ mode = st.st_mode;
+ if(S_ISREG(mode)) {
+ h->type = REG;
+ putoctal(h->size, (unsigned)st.st_size, sizeof h->size);
+ f = fopen(path, "r");
+ } else if(S_ISDIR(mode)) {
+ h->type = DIRECTORY;
+ } else if(S_ISLNK(mode)) {
+ h->type = SYMLINK;
+ readlink(path, h->link, (sizeof h->link)-1);
+ } else if(S_ISCHR(mode) || S_ISBLK(mode)) {
+ h->type = S_ISCHR(mode) ? CHARDEV : BLOCKDEV;
+ putoctal(h->major, (unsigned)major(st.st_dev), sizeof h->major);
+ putoctal(h->minor, (unsigned)minor(st.st_dev), sizeof h->minor);
+ } else if(S_ISFIFO(mode)) {
+ h->type = FIFO;
+ }
+
+ memset(h->chksum, ' ', sizeof h->chksum);
+ for(x = 0, chksum = 0; x < sizeof *h; x++)
+ chksum += b[x];
+ putoctal(h->chksum, chksum, sizeof h->chksum);
+
+ fwrite(b, Blksiz, 1, tarfile);
+ if(!f)
+ return 0;
+ while((l = fread(b, 1, Blksiz, f)) > 0) {
+ if(l < Blksiz)
+ memset(b+l, 0, Blksiz-l);
+ fwrite(b, Blksiz, 1, tarfile);
+ }
+ fclose(f);
+ return 0;
+}
+
+int
+unarchive(char *fname, int l, char b[Blksiz])
+{
+ char lname[101];
+ FILE *f = NULL;
+ unsigned long mode, major, minor, type;
+ Header *h = (void*)b;
+
+ unlink(fname);
+ switch(h->type) {
+ case REG:
+ mode = strtoul(h->mode, 0, 8);
+ if(!(f = fopen(fname, "w")) || chmod(fname, mode))
+ perror(fname);
+ break;
+ case HARDLINK:
+ case SYMLINK:
+ strlcpy(lname, h->link, sizeof lname);
+ if(!((h->type == HARDLINK) ? link : symlink)(lname, fname))
+ perror(fname);
+ break;
+ case DIRECTORY:
+ mode = strtoul(h->mode, 0, 8);
+ if(mkdir(fname, (mode_t)mode))
+ perror(fname);
+ break;
+ case CHARDEV:
+ case BLOCKDEV:
+ mode = strtoul(h->mode, 0, 8);
+ major = strtoul(h->major, 0, 8);
+ minor = strtoul(h->mode, 0, 8);
+ type = (h->type == CHARDEV) ? S_IFCHR : S_IFBLK;
+ if(mknod(fname, type | mode, makedev(major, minor)))
+ perror(fname);
+ break;
+ case FIFO:
+ mode = strtoul(h->mode, 0, 8);
+ if(mknod(fname, S_IFIFO | mode, 0))
+ perror(fname);
+ break;
+ default:
+ fprintf(stderr, "usupported tarfiletype %c\n", h->type);
+ }
+ if(getuid() == 0 && chown(fname, strtoul(h->uid, 0, 8),
+ strtoul(h->gid, 0, 8)))
+ perror(fname);
+
+ for(; l > 0; l -= Blksiz) {
+ fread(b, Blksiz, 1, tarfile);
+ if(f)
+ fwrite(b, MIN(l, 512), 1, f);
+ }
+ if(f)
+ fclose(f);
+ return 0;
+}
+
+int
+print(char * fname, int l, char b[Blksiz])
+{
+ puts(fname);
+ for(; l > 0; l -= Blksiz)
+ fread(b, Blksiz, 1, tarfile);
+ return 0;
+}
+
+void
+c(char * dir)
+{
+ ftw(dir, archive, FOPEN_MAX);
+}
+
+void
+xt(int (*fn)(char*, int, char[Blksiz]))
+{
+ char b[Blksiz], fname[101];
+ Header *h = (void*)b;
+
+ while(fread(b, Blksiz, 1, tarfile) && h->name[0] != '\0') {
+ strlcpy(fname, h->name, sizeof fname);
+ fn(fname, strtol(h->size, 0, 8), b);
+ }
+}