[tar] Fix support for pipes in Fuchsia
- I broke skipblk() when I added support for lseek(). Seeking does not
work on pipes and it's necessary to fall back to sequential reads.
- Pipes will sometimes return short reads. This is legal but unusual.
I updated unarchive() to cleanly handle this condition.
- Also fixed directory creation, which wasn't setting the mode because
of a premature call to mkdirp().
Change-Id: I72e77dd35a1a95afce3c1ef8241cf8f66d320a7d
diff --git a/tar.c b/tar.c
index 7f2eccc..613d562 100644
--- a/tar.c
+++ b/tar.c
@@ -255,7 +255,7 @@
static int
unarchive(char *fname, ssize_t l, char b[BLKSIZ])
{
- char lname[101], *tmp, *p;
+ char lname[101], *p;
long mode, major, minor, type, mtime, uid, gid;
struct header *h = (struct header *)b;
int fd = -1;
@@ -266,9 +266,13 @@
if (remove(fname) < 0 && errno != ENOENT && errno != ENOTEMPTY)
weprintf("remove %s:", fname);
- tmp = estrdup(fname);
- mkdirp(dirname(tmp));
- free(tmp);
+ // tar files normally create the directory chain. This is a fallback
+ // for noncompliant tar files.
+ if (h->type != DIRECTORY) {
+ char* tmp = estrdup(fname);
+ mkdirp(dirname(tmp));
+ free(tmp);
+ }
switch (h->type) {
case REG:
@@ -324,16 +328,30 @@
eprintf("strtol %s: invalid number\n", h->gid);
if (fd != -1) {
+ // Ceiling to BLKSIZ boundary
+ int readsize = (l + (BLKSIZ-1)) & ~(BLKSIZ-1);
char chunk[COPY_CHUNK_SIZE];
- for (; l > 0; l -= COPY_CHUNK_SIZE) {
- // Ceiling to BLKSIZ boundary
- int ceilsize = (MIN(l, COPY_CHUNK_SIZE) + (BLKSIZ-1)) & ~(BLKSIZ-1);
- if (eread(tarfd, chunk, ceilsize) != ceilsize) {
+ int lastread = 0;
+
+ for (; readsize > 0; l -= lastread, readsize -= lastread) {
+ int chunk_size = MIN(readsize, COPY_CHUNK_SIZE);
+ // Short reads are legal, so don't expect to read
+ // everything that was requested.
+ lastread = eread(tarfd, chunk, chunk_size);
+ if (lastread == 0) {
close(fd);
remove(fname);
- eprintf("unexpected end of file reading %s.\n", fname);
+ eprintf("unexpected end of file reading %s.\n",
+ fname);
}
- ewrite(fd, chunk, ceilsize);
+
+ // TODO(jimbe) Remove this "if" block when we better
+ // understand what causes a short read.
+ //if (chunk_size != lastread) {
+ // printf("info: short read %d %d\n", chunk_size,
+ // lastread);
+ //}
+ ewrite(fd, chunk, MIN(l, lastread));
}
close(fd);
}
@@ -361,11 +379,33 @@
static void
skipblk(ssize_t l)
{
- // Ceiling to the next BLKSIZ boundary
+ char b[BLKSIZ];
+ int lastread = 0;
+ // Ceiling to BLKSIZ boundary
int ceilsize = (l + (BLKSIZ-1)) & ~(BLKSIZ-1);
- if (lseek(tarfd, ceilsize, SEEK_CUR) == -1) {
+
+ off_t offset = lseek(tarfd, ceilsize, SEEK_CUR);
+ if (offset >= ceilsize)
+ return;
+ // TODO(jimbe) remove EOPNOTSUPP when US-297 is fixed
+ if (errno != ESPIPE && errno != EOPNOTSUPP) {
eprintf("unexpected end of file.\n");
}
+
+ // This is a pipe, socket or FIFO. Fall back to a sequential read.
+ for (; ceilsize > 0; ceilsize -= lastread) {
+ int chunk_size = MIN(ceilsize, BLKSIZ);
+ lastread = eread(tarfd, b, chunk_size);
+ // TODO(jimbe) Remove this "if" block when we better understand
+ // what causes a short read.
+ //if (chunk_size != lastread) {
+ // printf("info: short read %d %d\n", chunk_size,
+ // lastread);
+ //}
+ if (lastread == 0) {
+ eprintf("unexpected end of file %d.\n", errno);
+ }
+ }
}
static int