diff options
author | Rob Landley <rob@landley.net> | 2021-08-07 05:43:13 -0500 |
---|---|---|
committer | Rob Landley <rob@landley.net> | 2021-08-07 05:43:13 -0500 |
commit | 0b00ea7fb8f5be7089815d731251ee20d6dddaab (patch) | |
tree | 5795fb4c0cf3941b4bdcf9d95afce9d5ad238305 /lib | |
parent | ffe98246d323fb575058b91be55378555a543bf3 (diff) | |
download | toybox-0b00ea7fb8f5be7089815d731251ee20d6dddaab.tar.gz |
Change xabspath() to more granular (flag based) control interface.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/lib.c | 6 | ||||
-rw-r--r-- | lib/lib.h | 9 | ||||
-rw-r--r-- | lib/xwrap.c | 110 |
3 files changed, 63 insertions, 62 deletions
@@ -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; @@ -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<<!todo)) errno = 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 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<<!todo))) missing++; + else if (errno != EINVAL && (flags & (ABS_PATH<<!todo))) goto error; + else if (track) { + if (-1 == (fd = openat(dirfd, new->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); |