diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/xwrap.c | 49 |
1 files changed, 29 insertions, 20 deletions
diff --git a/lib/xwrap.c b/lib/xwrap.c index 562cbaf4..54985db3 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -481,7 +481,7 @@ void xstat(char *path, struct stat *st) char *xabspath(char *path, int exact) { struct string_list *todo, *done = 0; - int try = 9999, dirfd = open("/", 0);; + int try = 9999, dirfd = open("/", 0), missing = 0; char *ret; // If this isn't an absolute path, start with cwd. @@ -492,11 +492,12 @@ char *xabspath(char *path, int exact) free(temp); } else splitpath(path, &todo); - // Iterate through path components + // 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 if (!try--) { errno = ELOOP; goto error; @@ -507,29 +508,37 @@ char *xabspath(char *path, int exact) int x = new->str[1]; free(new); - if (x) { - if (done) free(llist_pop(&done)); - len = 0; - } else continue; + if (!x) continue; + if (done) free(llist_pop(&done)); + len = 0; + + if (missing) missing--; + else { + if (-1 == (x = openat(dirfd, "..", 0))) goto error; + close(dirfd); + dirfd = x; + } + continue; + } // Is this a symlink? - } else len = readlinkat(dirfd, new->str, libbuf, sizeof(libbuf)); - + 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; - char *s = ".."; - - // For .. just move dirfd - if (len) { - // Not a symlink: add to linked list, move dirfd, fail if error - if ((exact || todo) && errno != EINVAL) goto error; - new->next = done; - done = new; - if (errno == EINVAL && !todo) break; - s = new->str; + + new->next = done; + done = new; + if (errno == EINVAL && !todo) break; + if (errno == ENOENT && exact<0) { + missing++; + continue; } - fd = openat(dirfd, s, 0); + if (errno != EINVAL && (exact || todo)) goto error; + + fd = openat(dirfd, new->str, 0); if (fd == -1 && (exact || todo || errno != ENOENT)) goto error; close(dirfd); dirfd = fd; @@ -588,7 +597,7 @@ error: llist_traverse(todo, free); llist_traverse(done, free); - return NULL; + return 0; } void xchdir(char *path) |