aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--toys/pending/tar.c361
1 files changed, 164 insertions, 197 deletions
diff --git a/toys/pending/tar.c b/toys/pending/tar.c
index 6bb2b0e9..1f7acebf 100644
--- a/toys/pending/tar.c
+++ b/toys/pending/tar.c
@@ -115,11 +115,20 @@ static struct inode_list *seen_inode(void **list, struct stat *st, char *name)
return 0;
}
+// Calculate packet checksum, with cksum field treated as 8 spaces
+static unsigned cksum(void *data)
+{
+ unsigned i, cksum = 8*' ';
+
+ for (i = 0; i<500; i += (i==147) ? 9 : 1) cksum += ((char *)data)[i];
+
+ return cksum;
+}
+
static void write_longname(char *name, char type)
{
struct tar_hdr tmp[2];
- unsigned int sum = 0;
- int i, sz = strlen(name) +1;
+ int sz = strlen(name) +1;
memset(tmp, 0, sizeof(tmp));
strcpy(tmp->name, "././@LongLink");
@@ -132,10 +141,7 @@ static void write_longname(char *name, char type)
strcpy(tmp->magic, "ustar ");
// Calculate checksum
- memset(tmp->chksum, ' ', 8);
- for (i = 0; i < 512; i++) sum += ((char *)tmp)[i];
-// TODO: why is this -1 when the rest aren't?
- itoo(tmp->chksum, sizeof(tmp->chksum)-1, sum);
+ itoo(tmp->chksum, sizeof(tmp->chksum), cksum(&tmp));
// write header and name, padded with NUL to block size
xwrite(TT.fd, tmp, sizeof(*tmp));
@@ -170,6 +176,7 @@ static void alloread(void *buf, int len)
free(*b);
*b = xmalloc(len+1);
xreadall(TT.fd, *b, len);
+ b[len] = 0;
TT.offset += len;
}
@@ -181,7 +188,6 @@ static void add_file(char **nam, struct stat *st)
struct inode_list *node = node;
int i, fd =-1;
char *c, *p, *name = *nam, *lnk, *hname, buf[512] = {0,};
- unsigned int sum = 0;
static int warn = 1;
for (p = name; *p; p++)
@@ -210,7 +216,6 @@ static void add_file(char **nam, struct stat *st)
itoo(hdr.gid, sizeof(hdr.gid), st->st_gid);
itoo(hdr.size, sizeof(hdr.size), 0); //set size later
itoo(hdr.mtime, sizeof(hdr.mtime), st->st_mtime);
- memset(hdr.chksum, ' ', sizeof(hdr.chksum));
// Hard link or symlink?
i = !!S_ISLNK(st->st_mode);
@@ -252,9 +257,7 @@ static void add_file(char **nam, struct stat *st)
snprintf(hdr.gname, sizeof(hdr.gname), "%s", gr->gr_name);
else sprintf(hdr.gname, "%d", st->st_gid);
- //calculate chksum.
- for (i = 0; i < 512; i++) sum += ((char*)&hdr)[i];
- itoo(hdr.chksum, sizeof(hdr.chksum)-1, sum);
+ itoo(hdr.chksum, sizeof(hdr.chksum)-1, cksum(&hdr));
if (FLAG(v)) printf("%s\n",hname);
xwrite(TT.fd, (void*)&hdr, 512);
@@ -306,10 +309,10 @@ static void extract_to_command(struct archive_handler *tar)
{
int pipefd[2], status = 0;
pid_t cpid;
- struct file_header *file_hdr = &tar->file_hdr;
+ struct file_header *hdr = &tar->file_hdr;
xpipe(pipefd);
- if (!S_ISREG(file_hdr->mode)) return; //only regular files are supported.
+ if (!S_ISREG(hdr->mode)) return; //only regular files are supported.
cpid = fork();
if (cpid == -1) perror_exit("fork");
@@ -318,18 +321,18 @@ static void extract_to_command(struct archive_handler *tar)
char buf[64], *argv[4] = {"sh", "-c", TT.to_command, NULL};
setenv("TAR_FILETYPE", "f", 1);
- sprintf(buf, "%0o", file_hdr->mode);
+ sprintf(buf, "%0o", hdr->mode);
setenv("TAR_MODE", buf, 1);
- sprintf(buf, "%ld", (long)file_hdr->size);
+ sprintf(buf, "%ld", (long)hdr->size);
setenv("TAR_SIZE", buf, 1);
- setenv("TAR_FILENAME", file_hdr->name, 1);
- setenv("TAR_UNAME", file_hdr->uname, 1);
- setenv("TAR_GNAME", file_hdr->gname, 1);
- sprintf(buf, "%0o", (int)file_hdr->mtime);
+ 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_MTIME", buf, 1);
- sprintf(buf, "%0o", file_hdr->uid);
+ sprintf(buf, "%0o", hdr->uid);
setenv("TAR_UID", buf, 1);
- sprintf(buf, "%0o", file_hdr->gid);
+ sprintf(buf, "%0o", hdr->gid);
setenv("TAR_GID", buf, 1);
xclose(pipefd[1]); // Close unused write
@@ -338,7 +341,7 @@ static void extract_to_command(struct archive_handler *tar)
xexec(argv);
} else {
xclose(pipefd[0]); // Close unused read end
- sendlen(pipefd[1], file_hdr->size);
+ sendlen(pipefd[1], hdr->size);
xclose(pipefd[1]);
waitpid(cpid, &status, 0);
if (WIFSIGNALED(status))
@@ -351,90 +354,90 @@ static void extract_to_disk(struct archive_handler *tar)
int flags, dst_fd = -1;
char *s;
struct stat ex;
- struct file_header *file_hdr = &tar->file_hdr;
+ struct file_header *hdr = &tar->file_hdr;
// while not if
- flags = strlen(file_hdr->name);
+ flags = strlen(hdr->name);
if (flags>2)
- if (strstr(file_hdr->name, "/../") || !strcmp(file_hdr->name, "../") ||
- !strcmp(file_hdr->name+flags-3, "/.."))
- error_msg("drop %s", file_hdr->name);
+ if (strstr(hdr->name, "/../") || !strcmp(hdr->name, "../") ||
+ !strcmp(hdr->name+flags-3, "/.."))
+ error_msg("drop %s", hdr->name);
- if (file_hdr->name[flags-1] == '/') file_hdr->name[flags-1] = 0;
+ if (hdr->name[flags-1] == '/') hdr->name[flags-1] = 0;
//Regular file with preceding path
- if ((s = strrchr(file_hdr->name, '/'))) {
- if (mkpath(file_hdr->name) && errno !=EEXIST) {
- error_msg(":%s: not created", file_hdr->name);
+ if ((s = strrchr(hdr->name, '/'))) {
+ if (mkpath(hdr->name) && errno !=EEXIST) {
+ error_msg(":%s: not created", hdr->name);
return;
}
}
//remove old file, if exists
- if (!FLAG(k) && !S_ISDIR(file_hdr->mode) && !lstat(file_hdr->name, &ex))
- if (unlink(file_hdr->name))
- perror_msg("can't remove: %s", file_hdr->name);
+ if (!FLAG(k) && !S_ISDIR(hdr->mode) && !lstat(hdr->name, &ex))
+ if (unlink(hdr->name))
+ perror_msg("can't remove: %s", hdr->name);
//hard link
- if (S_ISREG(file_hdr->mode) && file_hdr->link_target) {
- if (link(file_hdr->link_target, file_hdr->name))
- perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target);
+ 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);
goto COPY;
}
- switch (file_hdr->mode & S_IFMT) {
+ switch (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(file_hdr->name, flags, file_hdr->mode & 07777);
- if (dst_fd == -1) perror_msg("%s: can't open", file_hdr->name);
+ dst_fd = open(hdr->name, flags, hdr->mode & 07777);
+ if (dst_fd == -1) perror_msg("%s: can't open", hdr->name);
break;
case S_IFDIR:
- if ((mkdir(file_hdr->name, file_hdr->mode) == -1) && errno != EEXIST)
- perror_msg("%s: can't create", file_hdr->name);
+ if ((mkdir(hdr->name, hdr->mode) == -1) && errno != EEXIST)
+ perror_msg("%s: can't create", hdr->name);
break;
case S_IFLNK:
- if (symlink(file_hdr->link_target, file_hdr->name))
- perror_msg("can't link '%s' -> '%s'",file_hdr->name, file_hdr->link_target);
+ if (symlink(hdr->link_target, hdr->name))
+ perror_msg("can't link '%s' -> '%s'",hdr->name, hdr->link_target);
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
- if (mknod(file_hdr->name, file_hdr->mode, file_hdr->device))
- perror_msg("can't create '%s'", file_hdr->name);
+ if (mknod(hdr->name, hdr->mode, hdr->device))
+ perror_msg("can't create '%s'", hdr->name);
break;
default:
- printf("type %o not supported\n", file_hdr->mode);
+ printf("type %o not supported\n", hdr->mode);
break;
}
//copy file....
COPY:
- sendlen(dst_fd, file_hdr->size);
+ sendlen(dst_fd, hdr->size);
close(dst_fd);
- if (S_ISLNK(file_hdr->mode)) return;
+ if (S_ISLNK(hdr->mode)) return;
if (!FLAG(o)) {
//set ownership..., --no-same-owner, --numeric-owner
- uid_t u = file_hdr->uid;
- gid_t g = file_hdr->gid;
+ uid_t u = hdr->uid;
+ gid_t g = hdr->gid;
if (!FLAG(numeric_owner)) {
- struct group *gr = getgrnam(file_hdr->gname);
- struct passwd *pw = getpwnam(file_hdr->uname);
+ struct group *gr = getgrnam(hdr->gname);
+ struct passwd *pw = getpwnam(hdr->uname);
if (pw) u = pw->pw_uid;
if (gr) g = gr->gr_gid;
}
- if (!geteuid() && chown(file_hdr->name, u, g))
- perror_msg("chown %d:%d '%s'", u, g, file_hdr->name);;
+ if (!geteuid() && chown(hdr->name, u, g))
+ perror_msg("chown %d:%d '%s'", u, g, hdr->name);;
}
if (FLAG(p)) // || !FLAG(no_same_permissions))
- chmod(file_hdr->name, file_hdr->mode);
+ chmod(hdr->name, hdr->mode);
//apply mtime
if (!FLAG(m)) {
- struct timeval times[2] = {{file_hdr->mtime, 0},{file_hdr->mtime, 0}};
- utimes(file_hdr->name, times);
+ struct timeval times[2] = {{hdr->mtime, 0},{hdr->mtime, 0}};
+ utimes(hdr->name, times);
}
}
@@ -460,190 +463,147 @@ static void file_to_list(char *file, struct arg_list **llist)
}
//convert octal to int
-static int otoi(char *str, int len)
+static unsigned long long otoi(char *str, int len)
{
- long val;
- char *endp, inp[len+1]; //1 for NUL termination
-
- memcpy(inp, str, len);
- inp[len] = '\0'; //nul-termination made sure
- val = strtol(inp, &endp, 8);
- if (*endp && *endp != ' ') error_exit("invalid param");
- return (int)val;
-}
+ unsigned long long val;
-static char *process_extended_hdr(struct archive_handler *tar, int size)
-{
- char *value = NULL, *p, *buf = 0;
-
- alloread(&buf, size);
- buf[size] = 0;
- p = buf;
-
- while (size) {
- char *key;
- int len, n;
-
- // extended records are of the format: "LEN NAME=VALUE\n"
- sscanf(p, "%d %n", &len, &n);
- key = p + n;
- p += len;
- size -= len;
- p[-1] = 0;
- if (size < 0) {
- error_msg("corrupted extended header");
- break;
- }
+// todo: base-256 encoding, just do it symmetrically for all fields
+ str[len-1] = 0;
+ val = strtoull(str, &str, 8);
+ if (*str && *str != ' ') error_exit("bad header");
- len = strlen("path=");
- if (!strncmp(key, "path=", len)) {
- value = key + strlen("path=");
- break;
- }
- }
- if (value) value = xstrdup(value);
- free(buf);
- return value;
+ return val;
}
static void unpack_tar(void)
{
struct archive_handler *tar_hdl = TT.handle;
+ struct file_header *hdr = &tar_hdl->file_hdr;
struct tar_hdr tar;
- struct file_header *file_hdr;
- int i, j, maj, min, sz, e = 0;
- unsigned int cksum;
+ 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);
- i = readall(TT.fd, &tar, 512);
- if (i != 512) {
- if (i >= 2) goto CHECK_MAGIC; //may be a small (<512 byte)zipped file
- error_exit("read error");
- }
+// if (TT.offset&511) skippy(512-TT.offset%512);
- if (!tar.name[0]) {
- if (e) return; //end of tar 2 empty blocks
- e = 1;//empty jump to next block
-// never clear e, so they don't have to be _consecutive_???
- continue;
- }
-// can you append a bzip to a gzip _within_ a tarball? Nested compress?
-// Or compressed data after uncompressed data?
- if (strncmp(tar.magic, "ustar", 5)) {
- // Try detecting .gz or .bz2 by looking for their magic.
-CHECK_MAGIC:
- if ((!strncmp(tar.name, "\x1f\x8b", 2) || !strncmp(tar.name, "BZh", 3))
- && !lseek(TT.fd, -i, SEEK_CUR)) {
- toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
- extract_stream(tar_hdl);
- continue;
- }
- error_exit("invalid tar format");
- }
-// moved this down here so it doesn't have to be undone
+ 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;
- // Calculate checksum, with cksum field treated as eight spaces
- cksum = 8*' ';
- for (j = 0; j<500; j += (j==147) ? 9 : 1) cksum += ((char*)&tar)[j];
-
- if (cksum != otoi(tar.chksum, sizeof(tar.chksum))) error_exit("bad cksum");
-
- file_hdr = &tar_hdl->file_hdr;
- memset(file_hdr, 0, sizeof(struct file_header));
- file_hdr->mode = otoi(tar.mode, sizeof(tar.mode));
- file_hdr->uid = otoi(tar.uid, sizeof(tar.uid));
- file_hdr->gid = otoi(tar.gid, sizeof(tar.gid));
- file_hdr->size = otoi(tar.size, sizeof(tar.size));
- file_hdr->mtime = otoi(tar.mtime, sizeof(tar.mtime));
- file_hdr->uname = xstrdup(tar.uname);
- file_hdr->gname = xstrdup(tar.gname);
- maj = otoi(tar.major, sizeof(tar.major));
- min = otoi(tar.minor, sizeof(tar.minor));
- file_hdr->device = dev_makedev(maj, min);
-
- if (tar.type <= '7') {
- if (tar.link[0]) {
- sz = sizeof(tar.link);
- file_hdr->link_target = xmalloc(sz + 1);
- memcpy(file_hdr->link_target, tar.link, sz);
- file_hdr->link_target[sz] = '\0';
- }
+ // End of tar
+ if (!*tar.name) return;
- file_hdr->name = xzalloc(256);// pathname supported size
- if (tar.prefix[0]) {
- memcpy(file_hdr->name, tar.prefix, sizeof(tar.prefix));
- sz = strlen(file_hdr->name);
- if (file_hdr->name[sz-1] != '/') file_hdr->name[sz] = '/';
- }
- sz = strlen(file_hdr->name);
- memcpy(file_hdr->name + sz, tar.name, sizeof(tar.name));
-// TODO: won't the vfs tell us?
- if (file_hdr->name[255]) error_exit("filename too long");
- }
+// 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");
+
+ 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 {
- // Headers that don't immediately result in creating a new node
- if (strchr("KLxDMNSVg", tar.type)) {
- if (tar.type == 'K') alloread(&file_hdr->link_target, file_hdr->size);
- else if (tar.type == 'L') alloread(&file_hdr->name, file_hdr->size);
- else if (tar.type == 'x') process_extended_hdr(tar_hdl, file_hdr->size);
- else skippy(file_hdr->size);
+ if (tar.type == 'K') alloread(&hdr->link_target, hdr->size);
+ else if (tar.type == 'L') alloread(&hdr->name, 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) {
+ if ((i = sscanf(p, "%u path=%n", &len, &n))<1 || len<4 ||
+ len>hdr->size)
+ {
+ error_msg("corrupted extended header");
+ break;
+ }
+ p[len-1] = 0;
+ if (i == 2) {
+ hdr->name = xstrdup(p+n);
+ break;
+ }
+ }
+ free(buf);
+
+ // 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);
continue;
}
- // Specified file type?
- if (isdigit(tar.type))
- file_hdr->mode |= (char []){8,8,10,2,6,4,1,8,0,0}[tar.type-'0']<<12;
-
// Directories sometimes recorded as "file with trailing slash"
- if (S_ISREG(file_hdr->mode) && (s = strend(file_hdr->name, "/"))) {
+ if (S_ISREG(hdr->mode) && (s = strend(hdr->name, "/"))) {
*s = 0;
- file_hdr->mode = (file_hdr->mode & ~S_IFMT) | S_IFDIR;
+ hdr->mode = (hdr->mode & ~S_IFMT) | S_IFDIR;
}
// TODO: what?
- if ((file_hdr->link_target && *(file_hdr->link_target))
- || S_ISLNK(file_hdr->mode) || S_ISDIR(file_hdr->mode))
- file_hdr->size = 0;
+ if ((hdr->link_target && *(hdr->link_target))
+ || S_ISLNK(hdr->mode) || S_ISDIR(hdr->mode))
+ hdr->size = 0;
// Skip excluded files
- if (filter(TT.exc, file_hdr->name) ||
- (TT.inc && !filter(TT.inc, file_hdr->name))) {
- skippy(file_hdr->size);
+ if (filter(TT.exc, hdr->name) ||
+ (TT.inc && !filter(TT.inc, hdr->name))) {
+ skippy(hdr->size);
// TODO: awkward
goto FREE;
}
// TODO: wrong, shouldn't grow endlessly, mark seen TT.inc instead
- add_to_list(&TT.pass, xstrdup(file_hdr->name));
+ add_to_list(&TT.pass, xstrdup(hdr->name));
if (FLAG(t)) {
if (FLAG(v)) {
char perm[11];
- struct tm *lc = localtime((const time_t*)&(file_hdr->mtime));
+ struct tm *lc = localtime((const time_t*)&(hdr->mtime));
- mode_to_string(file_hdr->mode, perm);
- printf("%s %s/%s %9ld %d-%02d-%02d %02d:%02d:%02d ",perm,file_hdr->uname,
- file_hdr->gname, (long)file_hdr->size, 1900+lc->tm_year,
+ 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);
}
- printf("%s",file_hdr->name);
- if (file_hdr->link_target) printf(" -> %s",file_hdr->link_target);
+ printf("%s", hdr->name);
+ if (hdr->link_target) printf(" -> %s", hdr->link_target);
xputc('\n');
- skippy(file_hdr->size);
+ skippy(hdr->size);
} else {
- if (FLAG(v)) printf("%s\n",file_hdr->name);
+ if (FLAG(v)) printf("%s\n", hdr->name);
tar_hdl->extract_handler(tar_hdl);
}
FREE:
- free(file_hdr->name);
- free(file_hdr->link_target);
- free(file_hdr->uname);
- free(file_hdr->gname);
+ free(hdr->name);
+ free(hdr->link_target);
+ free(hdr->uname);
+ free(hdr->gname);
+ memset(hdr, 0, sizeof(struct file_header));
}
}
@@ -686,6 +646,14 @@ void tar_main(void)
if (FLAG(to_command)) tar_hdl->extract_handler = extract_to_command;
// TODO: autodtect
+
+// Try detecting .gz or .bz2 by looking for their magic.
+// if ((!memcmp(tar.name, "\x1f\x8b", 2) || !memcmp(tar.name, "BZh", 3))
+// && !lseek(TT.fd, -i, SEEK_CUR)) {
+// toys.optflags |= (*tar.name == 'B') ? FLAG_j : FLAG_z;
+// extract_stream(tar_hdl);
+// continue;
+// }
if (FLAG(j)||FLAG(z)) extract_stream(tar_hdl);
unpack_tar();
@@ -703,7 +671,6 @@ void tar_main(void)
close(TT.fd);
TT.fd = pipefd[0];
}
-// TODO cowardly empty archive
for (tmp = TT.inc; tmp; tmp = tmp->next)
dirtree_flagread(tmp->arg, FLAG(h)?DIRTREE_SYMFOLLOW:0, add_to_tar);