aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/xwrap.c6
-rw-r--r--toys/posix/tar.c21
2 files changed, 18 insertions, 9 deletions
diff --git a/lib/xwrap.c b/lib/xwrap.c
index 1a3ef837..591c9513 100644
--- a/lib/xwrap.c
+++ b/lib/xwrap.c
@@ -525,7 +525,8 @@ void xstat(char *path, struct stat *st)
// Canonicalize path, even to file with one or more missing components at end.
// Returns allocated string for pathname or NULL if doesn't exist
-// exact = 1 file must exist, 0 dir must exist, -1 show theoretical location
+// exact = 1 file must exist, 0 dir must exist, -1 show theoretical location,
+// -2 don't resolve last file
char *xabspath(char *path, int exact)
{
struct string_list *todo, *done = 0;
@@ -570,7 +571,8 @@ char *xabspath(char *path, int exact)
}
// Is this a symlink?
- len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
+ if (exact == -2 && !todo) len = 0;
+ else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf));
if (len>4095) goto error;
// Not a symlink: add to linked list, move dirfd, fail if error
diff --git a/toys/posix/tar.c b/toys/posix/tar.c
index 78ff9a1e..ff129819 100644
--- a/toys/posix/tar.c
+++ b/toys/posix/tar.c
@@ -386,25 +386,32 @@ static void wsettime(char *s, long long sec)
}
// Do pending directory utimes(), NULL to flush all.
-static int dirflush(char *name)
+static int dirflush(char *name, int isdir)
{
char *s = 0, *ss;
// Barf if name not in TT.cwd
if (name) {
- ss = s = xabspath(name, -1);
- if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || *ss!='/')) {
- error_msg("'%s' not under '%s'", name, TT.cwd);
+ if (!(ss = s = xabspath(name, -1-isdir))) {
+ error_msg("'%s' bad symlink", name);
+
+ return 1;
+ }
+ if (TT.cwd[1] && (!strstart(&ss, TT.cwd) || (*ss && *ss!='/'))) {
+ error_msg("'%s' %s not under '%s'", name, s, TT.cwd);
free(s);
return 1;
}
+ // --restrict means first entry extracted is what everything must be under
if (FLAG(restrict)) {
free(TT.cwd);
TT.cwd = strdup(s);
toys.optflags ^= FLAG_restrict;
}
+ // use resolved name so trailing / is stripped
+ if (isdir) unlink(s);
}
// Set deferred utimes() for directories this file isn't under.
@@ -467,14 +474,14 @@ static void extract_to_disk(void)
char *name = TT.hdr.name;
int ala = TT.hdr.mode;
- if (dirflush(name)) {
+ if (dirflush(name, S_ISDIR(ala))) {
if (S_ISREG(ala) && !TT.hdr.link_target) skippy(TT.hdr.size);
return;
}
// create path before file if necessary
- if (strrchr(name, '/') && mkpath(name) && errno !=EEXIST)
+ if (strrchr(name, '/') && mkpath(name) && errno!=EEXIST)
return perror_msg(":%s: can't mkdir", name);
// remove old file, if exists
@@ -895,7 +902,7 @@ void tar_main(void)
}
unpack_tar(hdr);
- dirflush(0);
+ dirflush(0, 0);
// Each time a TT.incl entry is seen it's moved to the end of the list,
// with TT.seen pointing to first seen list entry. Anything between