diff options
Diffstat (limited to 'toys')
-rw-r--r-- | toys/pending/tar.c | 275 |
1 files changed, 125 insertions, 150 deletions
diff --git a/toys/pending/tar.c b/toys/pending/tar.c index 33a40393..97e699b4 100644 --- a/toys/pending/tar.c +++ b/toys/pending/tar.c @@ -54,10 +54,20 @@ GLOBALS( // exc is an argument but inc isn't? struct arg_list *inc, *pass; - void *inodes, *handle; + void *inodes; char *cwd; int fd; - unsigned short offset; // only ever used to calculate 512 byte padding + + // Parsed information about a tar header. + struct { + char *name, *link_target, *uname, *gname; + long long size; + uid_t uid; + gid_t gid; + mode_t mode; + time_t mtime; + dev_t device; + } hdr; ) struct tar_hdr { @@ -66,17 +76,6 @@ struct tar_hdr { prefix[155], padd[12]; }; -// Parsed information about a tar header. -struct file_header { - char *name, *link_target, *uname, *gname; - long long size; - uid_t uid; - gid_t gid; - mode_t mode; - time_t mtime; - dev_t device; -}; - struct inode_list { struct inode_list *next; char *arg; @@ -154,13 +153,6 @@ static int filter(struct arg_list *lst, char *name) static void skippy(long long len) { if (lskip(TT.fd, len)) perror_exit("EOF"); - TT.offset += len; -} - -static void sendlen(int fd, long long len) -{ - xsendfile_len(TT.fd, fd, len); - TT.offset += len; } // actually void **, but automatic typecasting doesn't work with void ** :( @@ -172,7 +164,6 @@ static void alloread(void *buf, int len) *b = xmalloc(len+1); xreadall(TT.fd, *b, len); b[len] = 0; - TT.offset += len; } static void add_file(char **nam, struct stat *st) @@ -287,13 +278,13 @@ static int add_to_tar(struct dirtree *node) } // Does anybody actually use this? -static void extract_to_command(struct file_header *hdr) +static void extract_to_command(void) { int pipefd[2], status = 0; pid_t cpid; xpipe(pipefd); - if (!S_ISREG(hdr->mode)) return; //only regular files are supported. + if (!S_ISREG(TT.hdr.mode)) return; //only regular files are supported. cpid = fork(); if (cpid == -1) perror_exit("fork"); @@ -302,18 +293,18 @@ static void extract_to_command(struct file_header *hdr) char buf[64], *argv[4] = {"sh", "-c", TT.to_command, NULL}; setenv("TAR_FILETYPE", "f", 1); - sprintf(buf, "%0o", hdr->mode); + sprintf(buf, "%0o", TT.hdr.mode); setenv("TAR_MODE", buf, 1); - sprintf(buf, "%ld", (long)hdr->size); + sprintf(buf, "%ld", (long)TT.hdr.size); setenv("TAR_SIZE", buf, 1); - setenv("TAR_FILENAME", hdr->name, 1); - setenv("TAR_UNAME", hdr->uname, 1); - setenv("TAR_GNAME", hdr->gname, 1); - sprintf(buf, "%0o", (int)hdr->mtime); + setenv("TAR_FILENAME", TT.hdr.name, 1); + setenv("TAR_UNAME", TT.hdr.uname, 1); + setenv("TAR_GNAME", TT.hdr.gname, 1); + sprintf(buf, "%0o", (int)TT.hdr.mtime); setenv("TAR_MTIME", buf, 1); - sprintf(buf, "%0o", hdr->uid); + sprintf(buf, "%0o", TT.hdr.uid); setenv("TAR_UID", buf, 1); - sprintf(buf, "%0o", hdr->gid); + sprintf(buf, "%0o", TT.hdr.gid); setenv("TAR_GID", buf, 1); xclose(pipefd[1]); // Close unused write @@ -322,7 +313,7 @@ static void extract_to_command(struct file_header *hdr) xexec(argv); } else { xclose(pipefd[0]); // Close unused read end - sendlen(pipefd[1], hdr->size); + xsendfile_len(TT.fd, pipefd[1], TT.hdr.size); xclose(pipefd[1]); waitpid(cpid, &status, 0); if (WIFSIGNALED(status)) @@ -330,94 +321,89 @@ static void extract_to_command(struct file_header *hdr) } } -static void extract_to_disk(struct file_header *hdr) +static void extract_to_disk(void) { int flags, dst_fd = -1; char *s; struct stat ex; // while not if - flags = strlen(hdr->name); + flags = strlen(TT.hdr.name); if (flags>2) - if (strstr(hdr->name, "/../") || !strcmp(hdr->name, "../") || - !strcmp(hdr->name+flags-3, "/..")) - error_msg("drop %s", hdr->name); + if (strstr(TT.hdr.name, "/../") || !strcmp(TT.hdr.name, "../") || + !strcmp(TT.hdr.name+flags-3, "/..")) + error_msg("drop %s", TT.hdr.name); - if (hdr->name[flags-1] == '/') hdr->name[flags-1] = 0; + if (TT.hdr.name[flags-1] == '/') TT.hdr.name[flags-1] = 0; //Regular file with preceding path - if ((s = strrchr(hdr->name, '/'))) { - if (mkpath(hdr->name) && errno !=EEXIST) { - error_msg(":%s: not created", hdr->name); - return; - } - } + if ((s = strrchr(TT.hdr.name, '/')) && mkpath(TT.hdr.name) && errno !=EEXIST) + return error_msg(":%s: not created", TT.hdr.name); //remove old file, if exists - if (!FLAG(k) && !S_ISDIR(hdr->mode) && !lstat(hdr->name, &ex)) - if (unlink(hdr->name)) - perror_msg("can't remove: %s", hdr->name); + if (!FLAG(k) && !S_ISDIR(TT.hdr.mode) && !lstat(TT.hdr.name, &ex)) + if (unlink(TT.hdr.name)) perror_msg("can't remove: %s", TT.hdr.name); //hard link - if (S_ISREG(hdr->mode) && hdr->link_target) { - if (link(hdr->link_target, hdr->name)) - perror_msg("can't link '%s' -> '%s'",hdr->name, hdr->link_target); + if (S_ISREG(TT.hdr.mode) && TT.hdr.link_target) { + if (link(TT.hdr.link_target, TT.hdr.name)) + perror_msg("can't link '%s' -> '%s'", TT.hdr.name, TT.hdr.link_target); goto COPY; } - switch (hdr->mode & S_IFMT) { + switch (TT.hdr.mode & S_IFMT) { case S_IFREG: flags = O_WRONLY|O_CREAT|O_EXCL; if (FLAG(overwrite)) flags = O_WRONLY|O_CREAT|O_TRUNC; - dst_fd = open(hdr->name, flags, hdr->mode & 07777); - if (dst_fd == -1) perror_msg("%s: can't open", hdr->name); + dst_fd = open(TT.hdr.name, flags, TT.hdr.mode & 07777); + if (dst_fd == -1) perror_msg("%s: can't open", TT.hdr.name); break; case S_IFDIR: - if ((mkdir(hdr->name, hdr->mode) == -1) && errno != EEXIST) - perror_msg("%s: can't create", hdr->name); + if ((mkdir(TT.hdr.name, TT.hdr.mode) == -1) && errno != EEXIST) + perror_msg("%s: can't create", TT.hdr.name); break; case S_IFLNK: - if (symlink(hdr->link_target, hdr->name)) - perror_msg("can't link '%s' -> '%s'",hdr->name, hdr->link_target); + if (symlink(TT.hdr.link_target, TT.hdr.name)) + perror_msg("can't link '%s' -> '%s'", TT.hdr.name, TT.hdr.link_target); break; case S_IFBLK: case S_IFCHR: case S_IFIFO: - if (mknod(hdr->name, hdr->mode, hdr->device)) - perror_msg("can't create '%s'", hdr->name); + if (mknod(TT.hdr.name, TT.hdr.mode, TT.hdr.device)) + perror_msg("can't create '%s'", TT.hdr.name); break; default: - printf("type %o not supported\n", hdr->mode); + printf("type %o not supported\n", TT.hdr.mode); break; } //copy file.... COPY: - sendlen(dst_fd, hdr->size); + xsendfile_len(TT.fd, dst_fd, TT.hdr.size); close(dst_fd); - if (S_ISLNK(hdr->mode)) return; if (!FLAG(o)) { //set ownership..., --no-same-owner, --numeric-owner - uid_t u = hdr->uid; - gid_t g = hdr->gid; + uid_t u = TT.hdr.uid; + gid_t g = TT.hdr.gid; if (!FLAG(numeric_owner)) { - struct group *gr = getgrnam(hdr->gname); - struct passwd *pw = getpwnam(hdr->uname); + struct group *gr = getgrnam(TT.hdr.gname); + struct passwd *pw = getpwnam(TT.hdr.uname); if (pw) u = pw->pw_uid; if (gr) g = gr->gr_gid; } - if (!geteuid() && chown(hdr->name, u, g)) - perror_msg("chown %d:%d '%s'", u, g, hdr->name);; + if (!geteuid() && lchown(TT.hdr.name, u, g)) + perror_msg("chown %d:%d '%s'", u, g, TT.hdr.name);; } - if (FLAG(p)) // || !FLAG(no_same_permissions)) - chmod(hdr->name, hdr->mode); + // || !FLAG(no_same_permissions)) + if (FLAG(p) && !S_ISLNK(TT.hdr.mode)) chmod(TT.hdr.name, TT.hdr.mode); +// TODO: defer directory mtime until we've finished with contents //apply mtime if (!FLAG(m)) { - struct timeval times[2] = {{hdr->mtime, 0},{hdr->mtime, 0}}; - utimes(hdr->name, times); + struct timeval times[2] = {{TT.hdr.mtime, 0},{TT.hdr.mtime, 0}}; + utimes(TT.hdr.name, times); } } @@ -457,74 +443,48 @@ static unsigned long long otoi(char *str, int len) static void unpack_tar(void) { - struct file_header *hdr = TT.handle; struct tar_hdr tar; int i; char *s; for (;;) { // align to next block and read it - if (hdr->size%512) skippy(512-hdr->size%512); -// This is the only consumer of TT.offset -// if (TT.offset&511) skippy(512-TT.offset%512); + if (TT.hdr.size%512) skippy(512-TT.hdr.size%512); i = readall(TT.fd, &tar, 512); if (i != 512) error_exit("read error"); - TT.offset += i; // ensure null temination even of pathological packets tar.padd[0] = 0; - // End of tar if (!*tar.name) return; // can you append a bzip to a gzip _within_ a tarball? Nested compress? // Or compressed data after uncompressed data? if (memcmp(tar.magic, "ustar", 5)) error_exit("bad header"); - if (cksum(&tar) != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("bad cksum"); + TT.hdr.size = otoi(tar.size, sizeof(tar.size)); - hdr->size = otoi(tar.size, sizeof(tar.size)); - - // Write something to the filesystem? - if (tar.type>='0' && tar.type<='7') { - hdr->mode = otoi(tar.mode, sizeof(tar.mode)); - hdr->mode |= (char []){8,8,10,2,6,4,1,8}[tar.type-'0']<<12; - hdr->uid = otoi(tar.uid, sizeof(tar.uid)); - hdr->gid = otoi(tar.gid, sizeof(tar.gid)); - hdr->mtime = otoi(tar.mtime, sizeof(tar.mtime)); - hdr->device = dev_makedev(otoi(tar.major, sizeof(tar.major)), - otoi(tar.minor, sizeof(tar.minor))); - hdr->uname = xstrndup(tar.uname, sizeof(tar.uname)); - hdr->gname = xstrndup(tar.gname, sizeof(tar.gname)); - if (!hdr->link_target && *tar.link) - hdr->link_target = xstrndup(tar.link, sizeof(tar.link)); - if (!hdr->name) - hdr->name = xmprintf("%.*s%s%.*s", (int)sizeof(tar.prefix), tar.prefix, - (!*tar.prefix || tar.prefix[strlen(tar.prefix)-1] == '/') ? "" : "/", - (int)sizeof(tar.name), tar.name); - - // Other headers don't immediately result in creating a new node - } else { - - if (tar.type == 'K') alloread(&hdr->link_target, hdr->size); - else if (tar.type == 'L') alloread(&hdr->name, hdr->size); + // If this header isn't writing something to the filesystem + if (tar.type<'0' || tar.type>'7') { + if (tar.type == 'K') alloread(&TT.hdr.link_target, TT.hdr.size); + else if (tar.type == 'L') alloread(&TT.hdr.name, TT.hdr.size); else if (tar.type == 'x') { char *p, *buf = 0; int i, len, n; // Posix extended record "LEN NAME=VALUE\n" format - alloread(&buf, hdr->size); - for (p = buf; (p-buf)<hdr->size; p += len) { + alloread(&buf, TT.hdr.size); + for (p = buf; (p-buf)<TT.hdr.size; p += len) { if ((i = sscanf(p, "%u path=%n", &len, &n))<1 || len<4 || - len>hdr->size) + len>TT.hdr.size) { error_msg("corrupted extended header"); break; } p[len-1] = 0; if (i == 2) { - hdr->name = xstrdup(p+n); + TT.hdr.name = xstrdup(p+n); break; } } @@ -532,71 +492,86 @@ static void unpack_tar(void) // This could be if (strchr("DMNSVg", tar.type)) but an unknown header // type with trailing contents is unlikely to have a valid type & cksum - // if the contents interpreted as a header - } else skippy(hdr->size); + } else skippy(TT.hdr.size); continue; } + // At this point, we're writing something to the filesystem. Parse fields. + TT.hdr.mode = otoi(tar.mode, sizeof(tar.mode)); + TT.hdr.mode |= (char []){8,8,10,2,6,4,1,8}[tar.type-'0']<<12; + TT.hdr.uid = otoi(tar.uid, sizeof(tar.uid)); + TT.hdr.gid = otoi(tar.gid, sizeof(tar.gid)); + TT.hdr.mtime = otoi(tar.mtime, sizeof(tar.mtime)); + TT.hdr.device = dev_makedev(otoi(tar.major, sizeof(tar.major)), + otoi(tar.minor, sizeof(tar.minor))); + + TT.hdr.uname = xstrndup(tar.uname, sizeof(tar.uname)); + TT.hdr.gname = xstrndup(tar.gname, sizeof(tar.gname)); + if (!TT.hdr.link_target && *tar.link) + TT.hdr.link_target = xstrndup(tar.link, sizeof(tar.link)); + if (!TT.hdr.name) { + i = strnlen(tar.prefix, sizeof(tar.prefix)); + TT.hdr.name = xmprintf("%.*s%s%.*s", i, tar.prefix, + (i && tar.prefix[i-1] != '/') ? "/" : "", + (int)sizeof(tar.name), tar.name); + } + // Directories sometimes recorded as "file with trailing slash" - if (S_ISREG(hdr->mode) && (s = strend(hdr->name, "/"))) { + if (S_ISREG(TT.hdr.mode) && (s = strend(TT.hdr.name, "/"))) { *s = 0; - hdr->mode = (hdr->mode & ~S_IFMT) | S_IFDIR; + TT.hdr.mode = (TT.hdr.mode & ~S_IFMT) | S_IFDIR; } -// TODO: what? - if ((hdr->link_target && *(hdr->link_target)) - || S_ISLNK(hdr->mode) || S_ISDIR(hdr->mode)) - hdr->size = 0; + // Hardlinks, symlinks, and directories do not have contents in archive + // (Neither do fifo, block or char devices, but not testing for that...?) + if ((TT.hdr.link_target && *TT.hdr.link_target) + || S_ISLNK(TT.hdr.mode) || S_ISDIR(TT.hdr.mode)) + TT.hdr.size = 0; // Skip excluded files - if (filter(TT.exc, hdr->name) || - (TT.inc && !filter(TT.inc, hdr->name))) { - skippy(hdr->size); -// TODO: awkward - goto FREE; - } + if (filter(TT.exc, TT.hdr.name) || (TT.inc && !filter(TT.inc, TT.hdr.name))) + skippy(TT.hdr.size); + else { + // TODO: wrong, shouldn't grow endlessly, mark seen TT.inc instead - add_to_list(&TT.pass, xstrdup(hdr->name)); + add_to_list(&TT.pass, xstrdup(TT.hdr.name)); - if (FLAG(t)) { - if (FLAG(v)) { - char perm[11]; - struct tm *lc = localtime((const time_t*)&(hdr->mtime)); + if (FLAG(t)) { + if (FLAG(v)) { + char perm[11]; + struct tm *lc = localtime(&TT.hdr.mtime); - mode_to_string(hdr->mode, perm); - printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ", perm, hdr->uname, - hdr->gname, (long)hdr->size, 1900+lc->tm_year, - 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec); + mode_to_string(TT.hdr.mode, perm); + printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ", perm, + TT.hdr.uname, TT.hdr.gname, (long)TT.hdr.size, 1900+lc->tm_year, + 1+lc->tm_mon, lc->tm_mday, lc->tm_hour, lc->tm_min, lc->tm_sec); + } + printf("%s", TT.hdr.name); + if (TT.hdr.link_target) printf(" -> %s", TT.hdr.link_target); + xputc('\n'); + skippy(TT.hdr.size); + } else { + if (FLAG(v)) printf("%s\n", TT.hdr.name); + if (FLAG(O)) xsendfile_len(TT.fd, 0, TT.hdr.size); + else if (FLAG(to_command)) extract_to_command(); + else extract_to_disk(); } - printf("%s", hdr->name); - if (hdr->link_target) printf(" -> %s", hdr->link_target); - xputc('\n'); - skippy(hdr->size); - } else { - if (FLAG(v)) printf("%s\n", hdr->name); - if (FLAG(O)) sendlen(0, hdr->size); - else if (FLAG(to_command)) extract_to_command(hdr); - else extract_to_disk(hdr); } -FREE: - free(hdr->name); - free(hdr->link_target); - free(hdr->uname); - free(hdr->gname); - memset(hdr, 0, sizeof(struct file_header)); + + free(TT.hdr.name); + free(TT.hdr.link_target); + free(TT.hdr.uname); + free(TT.hdr.gname); + TT.hdr.name = TT.hdr.link_target = 0; } } void tar_main(void) { - struct file_header *hdr; struct arg_list *tmp; char *s, **args = toys.optargs; -// TODO: zap - TT.handle = hdr = xzalloc(sizeof(struct file_header)); - // When extracting to command signal(SIGPIPE, SIG_IGN); |