From 0b00ea7fb8f5be7089815d731251ee20d6dddaab Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Sat, 7 Aug 2021 05:43:13 -0500 Subject: [PATCH] Change xabspath() to more granular (flag based) control interface. --- lib/lib.c | 6 +-- lib/lib.h | 9 +++- lib/xwrap.c | 110 ++++++++++++++++++++---------------------- toys/other/losetup.c | 4 +- toys/other/readlink.c | 9 ++-- toys/posix/tar.c | 4 +- 6 files changed, 72 insertions(+), 70 deletions(-) diff --git a/lib/lib.c b/lib/lib.c index 49fd5dd2..81c89650 100644 --- a/lib/lib.c +++ b/lib/lib.c @@ -1068,7 +1068,7 @@ char *getbasename(char *name) // Return pointer to xabspath(file) if file is under dir, else 0 char *fileunderdir(char *file, char *dir) { - char *s1 = xabspath(dir, 1), *s2 = xabspath(file, -1), *ss = s2; + char *s1 = xabspath(dir, ABS_FILE), *s2 = xabspath(file, 0), *ss = s2; int rc = s1 && s2 && strstart(&ss, s1) && (!s1[1] || s2[strlen(s1)] == '/'); free(s1); @@ -1083,8 +1083,8 @@ char *relative_path(char *from, char *to) char *s, *ret = 0; int i, j, k; - if (!(from = xabspath(from, -1))) return 0; - if (!(to = xabspath(to, -1))) goto error; + if (!(from = xabspath(from, 0))) return 0; + if (!(to = xabspath(to, 0))) goto error; // skip common directories from root for (i = j = 0; from[i] && from[i] == to[i]; i++) if (to[i] == '/') j = i+1; diff --git a/lib/lib.h b/lib/lib.h index 7484eb8f..b7c6446e 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -90,8 +90,7 @@ struct dirtree { char *symlink; int dirfd; struct stat st; - char again; - char name[]; + char again, name[]; }; int isdotdot(char *name); @@ -115,6 +114,12 @@ void show_help(FILE *out, int full); #define WARN_ONLY (1<<31) // don't exit, just warn #define LOOPFILES_ANYWAY (1<<30) // call function with fd -1 +// xabspath flags +#define ABS_PATH 1 // all but last path component must exist +#define ABS_FILE 2 // last path component must exist +#define ABS_KEEP 4 // don't resolve symlinks in path to last component +#define ABS_LAST 8 // don't resolve symlink in last path component + // xwrap.c void xstrncpy(char *dest, char *src, size_t size); void xstrncat(char *dest, char *src, size_t size); diff --git a/lib/xwrap.c b/lib/xwrap.c index a58707cc..7296f580 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -548,74 +548,73 @@ 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, -// -2 don't resolve last file -char *xabspath(char *path, int exact) +// Returns allocated string for pathname or NULL if doesn't exist. Flags are: +// ABS_PATH:path to last component must exist ABS_FILE: whole path must exist +// ABS_KEEP:keep symlinks in path ABS_LAST: keep symlink at end of path +char *xabspath(char *path, int flags) { - struct string_list *todo, *done = 0; - int try = 9999, dirfd = open("/", O_PATH), missing = 0; - char *ret; + struct string_list *todo, *done = 0, *new, **tail; + int fd, track, len, try = 9999, dirfd = -1, missing = 0; + char *str; - // If this isn't an absolute path, start with cwd. - if (*path != '/') { - char *temp = xgetcwd(); + // If the last file must exist, path to it must exist. + if (flags&ABS_FILE) flags |= ABS_PATH; + // If we don't resolve path's symlinks, don't resolve last symlink. + if (flags&ABS_KEEP) flags |= ABS_LAST; - splitpath(path, splitpath(temp, &todo)); - free(temp); + // If this isn't an absolute path, start with cwd or $PWD. + if (*path != '/') { + if ((flags & ABS_KEEP) && (str = getenv("PWD"))) + splitpath(path, splitpath(str, &todo)); + else { + splitpath(path, splitpath(str = xgetcwd(), &todo)); + free(str); + } } else splitpath(path, &todo); // Iterate through path components in todo, prepend processed ones to done. while (todo) { - struct string_list *new = llist_pop(&todo), **tail; - ssize_t len; - - // Eventually break out of endless loops + // break out of endless symlink loops if (!try--) { errno = ELOOP; goto error; } - // Removable path componenents. - if (!strcmp(new->str, ".") || !strcmp(new->str, "..")) { - int x = new->str[1]; - + // Remove . or .. component, tracking dirfd back up tree as necessary + str = (new = llist_pop(&todo))->str; + // track dirfd if this component must exist or we're resolving symlinks + track = ((flags>>!todo) & (ABS_PATH|ABS_KEEP)) ^ ABS_KEEP; + if (!done && track) dirfd = open("/", O_PATH); + if (*str=='.' && !str[1+((fd = str[1])=='.')]) { free(new); - if (!x) continue; - if (done) free(llist_pop(&done)); - len = 0; - - if (missing) missing--; - else { - if (-1 == (x = openat(dirfd, "..", O_PATH))) goto error; - close(dirfd); - dirfd = x; + if (fd) { + if (done) free(llist_pop(&done)); + if (missing) missing--; + else if (track) { + if (-1 == (fd = openat(dirfd, "..", O_PATH))) goto error; + close(dirfd); + dirfd = fd; + } } continue; } // Is this a symlink? - if (exact == -2 && !todo) len = 0; + if (flags & (ABS_KEEP<str, libbuf, sizeof(libbuf)); if (len>4095) goto error; // Not a symlink: add to linked list, move dirfd, fail if error if (len<1) { - int fd; - new->next = done; done = new; - if (errno == EINVAL && !todo) break; - if (errno == ENOENT && exact<0) { - missing++; - continue; + if (errno == ENOENT && !(flags & (ABS_PATH<str, O_PATH))) goto error; + close(dirfd); + dirfd = fd; } - if (errno != EINVAL && (exact || todo)) goto error; - - fd = openat(dirfd, new->str, O_PATH); - if (fd == -1 && (exact || todo || errno != ENOENT)) goto error; - close(dirfd); - dirfd = fd; continue; } @@ -623,13 +622,13 @@ char *xabspath(char *path, int exact) libbuf[len] = 0; if (*libbuf == '/') { llist_traverse(done, free); - done=0; + done = 0; close(dirfd); - dirfd = open("/", O_PATH); + dirfd = -1; } free(new); - // prepend components of new path. Note symlink to "/" will leave new NULL + // prepend components of new path. Note symlink to "/" will leave new = NULL tail = splitpath(libbuf, &new); // symlink to "/" will return null and leave tail alone @@ -638,11 +637,10 @@ char *xabspath(char *path, int exact) todo = new; } } - close(dirfd); - - // At this point done has the path, in reverse order. Reverse list while - // calculating buffer length. + xclose(dirfd); + // At this point done has the path, in reverse order. Reverse list + // (into todo) while calculating buffer length. try = 2; while (done) { struct string_list *temp = llist_pop(&done); @@ -654,20 +652,18 @@ char *xabspath(char *path, int exact) } // Assemble return buffer - - ret = xmalloc(try); - *ret = '/'; - ret [try = 1] = 0; + *(str = xmalloc(try)) = '/'; + str[try = 1] = 0; while (todo) { - if (try>1) ret[try++] = '/'; - try = stpcpy(ret+try, todo->str) - ret; + if (try>1) str[try++] = '/'; + try = stpcpy(str+try, todo->str) - str; free(llist_pop(&todo)); } - return ret; + return str; error: - close(dirfd); + xclose(dirfd); llist_traverse(todo, free); llist_traverse(done, free); diff --git a/toys/other/losetup.c b/toys/other/losetup.c index 7b8aed4f..ce84c1ef 100644 --- a/toys/other/losetup.c +++ b/toys/other/losetup.c @@ -103,9 +103,9 @@ static int loopback_setup(char *device, char *file) } // Associate file with this device? } else if (file) { - char *f_path = xabspath(file, 1); + char *f_path = xabspath(file, ABS_PATH); - if (!f_path) perror_exit("file"); // already opened, but if deleted since... + if (!f_path) perror_exit("%s", file); // already opened but if deleted since if (ioctl(lfd, LOOP_SET_FD, ffd)) { free(f_path); if (racy && errno == EBUSY) return 1; diff --git a/toys/other/readlink.c b/toys/other/readlink.c index bae09536..55234ccf 100644 --- a/toys/other/readlink.c +++ b/toys/other/readlink.c @@ -2,6 +2,7 @@ * * Copyright 2007 Rob Landley +// -ef positions match ABS_FILE ABS_PATH USE_READLINK(NEWTOY(readlink, "<1nqmef(canonicalize)[-mef]", TOYFLAG_USR|TOYFLAG_BIN)) USE_REALPATH(NEWTOY(realpath, "<1", TOYFLAG_USR|TOYFLAG_BIN)) @@ -40,14 +41,14 @@ void readlink_main(void) for (arg = toys.optargs; *arg; arg++) { // Calculating full canonical path? - // Take advantage of flag positions to calculate m = -1, f = 0, e = 1 + // Take advantage of flag positions: m = 0, f = ABS_PATH, e = ABS_FILE if (toys.optflags & (FLAG_f|FLAG_e|FLAG_m)) - s = xabspath(*arg, (toys.optflags&(FLAG_f|FLAG_e))-1); + s = xabspath(*arg, toys.optflags&(FLAG_f|FLAG_e)); else s = xreadlink(*arg); if (s) { - if (!FLAG(q)) xprintf(FLAG(n) ? "%s" : "%s\n", s); - if (CFG_TOYBOX_FREE) free(s); + if (!FLAG(q)) xprintf("%s%s", s, (FLAG(n) && !arg[1]) ? "" : "\n"); + free(s); } else toys.exitval = 1; } } diff --git a/toys/posix/tar.c b/toys/posix/tar.c index c4fb4fa2..77253597 100644 --- a/toys/posix/tar.c +++ b/toys/posix/tar.c @@ -399,7 +399,7 @@ static int dirflush(char *name, int isdir) // Barf if name not in TT.cwd if (name) { - if (!(ss = s = xabspath(name, -1-isdir))) { + if (!(ss = s = xabspath(name, isdir ? ABS_LAST : 0))) { error_msg("'%s' bad symlink", name); return 1; @@ -834,7 +834,7 @@ void tar_main(void) } // Get destination directory - TT.cwd = xabspath(s = xgetcwd(), 1); + TT.cwd = xabspath(s = xgetcwd(), ABS_PATH); free(s); // Remember archive inode so we don't overwrite it or add it to itself -- 2.39.2