aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2021-08-07 05:43:13 -0500
committerRob Landley <rob@landley.net>2021-08-07 05:43:13 -0500
commit0b00ea7fb8f5be7089815d731251ee20d6dddaab (patch)
tree5795fb4c0cf3941b4bdcf9d95afce9d5ad238305 /lib
parentffe98246d323fb575058b91be55378555a543bf3 (diff)
downloadtoybox-0b00ea7fb8f5be7089815d731251ee20d6dddaab.tar.gz
Change xabspath() to more granular (flag based) control interface.
Diffstat (limited to 'lib')
-rw-r--r--lib/lib.c6
-rw-r--r--lib/lib.h9
-rw-r--r--lib/xwrap.c110
3 files changed, 63 insertions, 62 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<<!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);