Adding tar.
This commit is contained in:
parent
9ac01f59be
commit
2c75eb98d9
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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user