tar: Use raw I/O instead of standard file streams

As part of refactoring tar to add support for compression through
gzip/bzip2, it makes sense to avoid intermixing file stream I/O with
raw I/O.
This commit is contained in:
sin 2015-04-23 15:20:10 +01:00
parent fb1595a69c
commit ab267a87eb
1 changed files with 70 additions and 54 deletions

124
tar.c
View File

@ -3,6 +3,7 @@
#include <sys/time.h> #include <sys/time.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <grp.h> #include <grp.h>
#include <libgen.h> #include <libgen.h>
#include <pwd.h> #include <pwd.h>
@ -47,8 +48,7 @@ enum Type {
BLOCKDEV = '4', DIRECTORY = '5', FIFO = '6' BLOCKDEV = '4', DIRECTORY = '5', FIFO = '6'
}; };
static FILE *tarfile; static int tarfd;
static char *tarfilename;
static ino_t tarinode; static ino_t tarinode;
static dev_t tardev; static dev_t tardev;
@ -59,6 +59,7 @@ static struct ent {
char *name; char *name;
time_t mtime; time_t mtime;
} *ents; } *ents;
static size_t entlen; static size_t entlen;
static void static void
@ -80,8 +81,8 @@ popent(void)
return NULL; return NULL;
} }
static FILE * static int
decomp(FILE *fp) decomp(int fd)
{ {
int fds[2]; int fds[2];
char *tool; char *tool;
@ -93,7 +94,7 @@ decomp(FILE *fp)
case -1: case -1:
eprintf("fork:"); eprintf("fork:");
case 0: case 0:
dup2(fileno(fp), 0); dup2(fd, 0);
dup2(fds[1], 1); dup2(fds[1], 1);
close(fds[0]); close(fds[0]);
close(fds[1]); close(fds[1]);
@ -104,8 +105,32 @@ decomp(FILE *fp)
_exit(1); _exit(1);
} }
close(fds[1]); close(fds[1]);
return fds[0];
}
return fdopen(fds[0], "r"); static ssize_t
eread(int fd, void *buf, size_t n)
{
ssize_t r;
again:
r = read(fd, buf, n);
if (r < 0) {
if (errno == EINTR)
goto again;
eprintf("read:");
}
return r;
}
static ssize_t
ewrite(int fd, const void *buf, size_t n)
{
ssize_t r;
if ((r = write(fd, buf, n)) != n)
eprintf("write:");
return r;
} }
static void static void
@ -118,14 +143,14 @@ putoctal(char *dst, unsigned num, int size)
static int static int
archive(const char *path) archive(const char *path)
{ {
FILE *f = NULL; unsigned char b[BLKSIZ];
struct group *gr; struct group *gr;
struct header *h; struct header *h;
struct passwd *pw; struct passwd *pw;
struct stat st; struct stat st;
size_t chksum, x; size_t chksum, x;
ssize_t l, r; ssize_t l, r;
unsigned char b[BLKSIZ]; int fd = -1;
if (lstat(path, &st) < 0) { if (lstat(path, &st) < 0) {
weprintf("lstat %s:", path); weprintf("lstat %s:", path);
@ -154,7 +179,9 @@ archive(const char *path)
if (S_ISREG(st.st_mode)) { if (S_ISREG(st.st_mode)) {
h->type = REG; h->type = REG;
putoctal(h->size, (unsigned)st.st_size, sizeof(h->size)); putoctal(h->size, (unsigned)st.st_size, sizeof(h->size));
f = fopen(path, "r"); fd = open(path, O_RDONLY);
if (fd < 0)
eprintf("open %s:", path);
} else if (S_ISDIR(st.st_mode)) { } else if (S_ISDIR(st.st_mode)) {
h->type = DIRECTORY; h->type = DIRECTORY;
} else if (S_ISLNK(st.st_mode)) { } else if (S_ISLNK(st.st_mode)) {
@ -174,18 +201,15 @@ archive(const char *path)
for (x = 0, chksum = 0; x < sizeof(*h); x++) for (x = 0, chksum = 0; x < sizeof(*h); x++)
chksum += b[x]; chksum += b[x];
putoctal(h->chksum, chksum, sizeof(h->chksum)); putoctal(h->chksum, chksum, sizeof(h->chksum));
ewrite(tarfd, b, BLKSIZ);
if (fwrite(b, BLKSIZ, 1, tarfile) != 1) if (fd != -1) {
eprintf("fwrite:"); while ((l = eread(fd, b, BLKSIZ)) > 0) {
if (f) {
while ((l = fread(b, 1, BLKSIZ, f)) > 0) {
if (l < BLKSIZ) if (l < BLKSIZ)
memset(b + l, 0, BLKSIZ - l); memset(b + l, 0, BLKSIZ - l);
if (fwrite(b, BLKSIZ, 1, tarfile) != 1) ewrite(tarfd, b, BLKSIZ);
eprintf("fwrite:");
} }
efshut(f, path); close(fd);
} }
return 0; return 0;
@ -194,10 +218,10 @@ archive(const char *path)
static int static int
unarchive(char *fname, ssize_t l, char b[BLKSIZ]) unarchive(char *fname, ssize_t l, char b[BLKSIZ])
{ {
FILE *f = NULL;
struct header *h = (struct header *)b;
long mode, major, minor, type, mtime, uid, gid;
char lname[101], *tmp, *p; char lname[101], *tmp, *p;
long mode, major, minor, type, mtime, uid, gid;
struct header *h = (struct header *)b;
int fd = -1;
if (!mflag && ((mtime = strtol(h->mtime, &p, 8)) < 0 || *p != '\0')) if (!mflag && ((mtime = strtol(h->mtime, &p, 8)) < 0 || *p != '\0'))
eprintf("strtol %s: invalid number\n", h->mtime); eprintf("strtol %s: invalid number\n", h->mtime);
@ -213,10 +237,11 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ])
case AREG: case AREG:
if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0') if ((mode = strtol(h->mode, &p, 8)) < 0 || *p != '\0')
eprintf("strtol %s: invalid number\n", h->mode); eprintf("strtol %s: invalid number\n", h->mode);
if (!(f = fopen(fname, "w"))) fd = open(fname, O_WRONLY | O_TRUNC | O_CREAT, 0644);
eprintf("fopen %s:", fname); if (fd < 0)
if (chmod(fname, mode) < 0) eprintf("open %s:", fname);
eprintf("chmod %s:", fname); if (fchmod(fd, mode) < 0)
eprintf("fchmod %s:", fname);
break; break;
case HARDLINK: case HARDLINK:
case SYMLINK: case SYMLINK:
@ -262,14 +287,12 @@ unarchive(char *fname, ssize_t l, char b[BLKSIZ])
if (!getuid() && chown(fname, uid, gid)) if (!getuid() && chown(fname, uid, gid))
weprintf("chown %s:", fname); weprintf("chown %s:", fname);
for (; l > 0; l -= BLKSIZ) { if (fd != -1) {
if (fread(b, BLKSIZ, 1, tarfile) != 1) for (; l > 0; l -= BLKSIZ)
eprintf("fread %s:", tarfilename); if (eread(tarfd, b, BLKSIZ) > 0)
if (f && fwrite(b, MIN(l, BLKSIZ), 1, f) != 1) ewrite(fd, b, MIN(l, BLKSIZ));
eprintf("fwrite %s:", fname); close(fd);
} }
if (f)
fshut(f, fname);
pushent(fname, mtime); pushent(fname, mtime);
return 0; return 0;
@ -281,8 +304,8 @@ skipblk(ssize_t l)
char b[BLKSIZ]; char b[BLKSIZ];
for (; l > 0; l -= BLKSIZ) for (; l > 0; l -= BLKSIZ)
if (fread(b, BLKSIZ, 1, tarfile) != 1) if (!eread(tarfd, b, BLKSIZ))
eprintf("fread %s:", tarfilename); break;
} }
static int static int
@ -339,7 +362,7 @@ xt(int argc, char *argv[], int (*fn)(char *, ssize_t, char[BLKSIZ]))
long size; long size;
int i, n; int i, n;
while (fread(b, BLKSIZ, 1, tarfile) == 1 && *h->name) { while (eread(tarfd, b, BLKSIZ) > 0 && h->name[0]) {
sanitize(h), n = 0; sanitize(h), n = 0;
/* small dance around non-null terminated fields */ /* small dance around non-null terminated fields */
@ -371,8 +394,6 @@ xt(int argc, char *argv[], int (*fn)(char *, ssize_t, char[BLKSIZ]))
fn(fname, size, b); fn(fname, size, b);
} }
if (ferror(tarfile))
eprintf("fread %s:", tarfilename);
if (!mflag) { if (!mflag) {
while ((ent = popent())) { while ((ent = popent())) {
@ -397,11 +418,11 @@ usage(void)
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
FILE *fp;
struct recursor r = { .fn = c, .hist = NULL, .depth = 0, .maxdepth = 0, struct recursor r = { .fn = c, .hist = NULL, .depth = 0, .maxdepth = 0,
.follow = 'P', .flags = DIRFIRST }; .follow = 'P', .flags = DIRFIRST };
struct stat st; struct stat st;
char *file = NULL, *dir = ".", mode = '\0'; char *file = NULL, *dir = ".", mode = '\0';
int fd;
ARGBEGIN { ARGBEGIN {
case 'x': case 'x':
@ -437,19 +458,17 @@ main(int argc, char *argv[])
switch (mode) { switch (mode) {
case 'c': case 'c':
tarfd = 1;
if (file) { if (file) {
if (!(fp = fopen(file, "w"))) tarfd = open(file, O_WRONLY | O_TRUNC | O_CREAT, 0644);
eprintf("fopen %s:", file); if (tarfd < 0)
eprintf("open %s:", file);
if (lstat(file, &st) < 0) if (lstat(file, &st) < 0)
eprintf("lstat %s:", file); eprintf("lstat %s:", file);
tarinode = st.st_ino; tarinode = st.st_ino;
tardev = st.st_dev; tardev = st.st_dev;
tarfile = fp;
tarfilename = file;
} else {
tarfile = stdout;
tarfilename = "<stdout>";
} }
if (chdir(dir) < 0) if (chdir(dir) < 0)
eprintf("chdir %s:", dir); eprintf("chdir %s:", dir);
for (; *argv; argc--, argv++) for (; *argv; argc--, argv++)
@ -457,22 +476,19 @@ main(int argc, char *argv[])
break; break;
case 't': case 't':
case 'x': case 'x':
tarfd = 0;
if (file) { if (file) {
if (!(fp = fopen(file, "r"))) tarfd = open(file, O_RDONLY);
eprintf("fopen %s:", file); if (tarfd < 0)
} else { eprintf("open %s:", file);
fp = stdin;
} }
tarfilename = file;
switch (filtermode) { switch (filtermode) {
case 'j': case 'j':
case 'z': case 'z':
tarfile = decomp(fp); fd = tarfd;
break; tarfd = decomp(tarfd);
default: close(fd);
tarfile = fp;
break; break;
} }