Adding tar.
This commit is contained in:
		
							
								
								
									
										1
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								Makefile
									
									
									
									
									
								
							@@ -69,6 +69,7 @@ SRC = \
 | 
			
		||||
	sponge.c   \
 | 
			
		||||
	sync.c     \
 | 
			
		||||
	tail.c     \
 | 
			
		||||
	tar.c      \
 | 
			
		||||
	tee.c      \
 | 
			
		||||
	test.c     \
 | 
			
		||||
	touch.c    \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										62
									
								
								tar.1
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								tar.1
									
									
									
									
									
										Normal file
									
								
							@@ -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)
 | 
			
		||||
							
								
								
									
										298
									
								
								tar.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										298
									
								
								tar.c
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user