diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2021-01-28 10:54:32 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2021-01-28 15:00:23 -0500 |
commit | 2f4d7858e09437962887c4fb943b30a1e07449d0 (patch) | |
tree | f72ccca6eb476c0aeaf821818c0825fafbd39cfe | |
parent | ad058cc871a51f1da2dba0a179891f0b96be2c0d (diff) | |
download | bfs-2f4d7858e09437962887c4fb943b30a1e07449d0.tar.xz |
bftw: Avoid allocating when handling ENAMETOOLONG
-rw-r--r-- | bftw.c | 82 |
1 files changed, 30 insertions, 52 deletions
@@ -336,32 +336,6 @@ static struct bftw_file *bftw_file_new(struct bftw_cache *cache, struct bftw_fil } /** - * Get the appropriate (fd, path) pair for the *at() family of functions. - * - * @param file - * The file being accessed. - * @param[out] at_fd - * Will hold the appropriate file descriptor to use. - * @param[in,out] at_path - * Will hold the appropriate path to use. - * @return The closest open ancestor file. - */ -static struct bftw_file *bftw_file_base(struct bftw_file *file, int *at_fd, const char **at_path) { - struct bftw_file *base = file; - - do { - base = base->parent; - } while (base && base->fd < 0); - - if (base) { - *at_fd = base->fd; - *at_path += bftw_child_nameoff(base); - } - - return base; -} - -/** * Open a bftw_file relative to another one. * * @param cache @@ -377,9 +351,15 @@ static struct bftw_file *bftw_file_base(struct bftw_file *file, int *at_fd, cons * @return * The opened file descriptor, or negative on error. */ -static int bftw_file_openat(struct bftw_cache *cache, struct bftw_file *file, const struct bftw_file *base, int at_fd, const char *at_path) { +static int bftw_file_openat(struct bftw_cache *cache, struct bftw_file *file, const struct bftw_file *base, const char *at_path) { assert(file->fd < 0); + int at_fd = AT_FDCWD; + if (base) { + at_fd = base->fd; + assert(at_fd >= 0); + } + int flags = O_RDONLY | O_CLOEXEC | O_DIRECTORY; int fd = openat(at_fd, at_path, flags); @@ -414,46 +394,44 @@ static int bftw_file_openat(struct bftw_cache *cache, struct bftw_file *file, co * The opened file descriptor, or negative on error. */ static int bftw_file_open(struct bftw_cache *cache, struct bftw_file *file, const char *path) { - int at_fd = AT_FDCWD; + // Find the nearest open ancestor + struct bftw_file *base = file; + do { + base = base->parent; + } while (base && base->fd < 0); + const char *at_path = path; - struct bftw_file *base = bftw_file_base(file, &at_fd, &at_path); + if (base) { + at_path += bftw_child_nameoff(base); + } - int fd = bftw_file_openat(cache, file, base, at_fd, at_path); + int fd = bftw_file_openat(cache, file, base, at_path); if (fd >= 0 || errno != ENAMETOOLONG) { return fd; } // Handle ENAMETOOLONG by manually traversing the path component-by-component - // -1 to include the root, which has depth == 0 - size_t offset = base ? base->depth : (size_t)-1; - size_t levels = file->depth - offset; - if (levels < 2) { - return fd; - } - - struct bftw_file **parents = malloc(levels * sizeof(*parents)); - if (!parents) { - return fd; - } - - struct bftw_file *parent = file; - for (size_t i = levels; i-- > 0;) { - parents[i] = parent; - parent = parent->parent; + // Use the ->next linked list to temporarily hold the reversed parent + // chain between base and file + struct bftw_file *cur; + for (cur = file; cur->parent != base; cur = cur->parent) { + cur->parent->next = cur; } - for (size_t i = 0; i < levels; ++i) { - fd = bftw_file_openat(cache, parents[i], base, at_fd, parents[i]->name); - if (fd < 0) { + // Open the files in the chain one by one + for (base = cur; base; base = base->next) { + fd = bftw_file_openat(cache, base, base->parent, base->name); + if (fd < 0 || base == file) { break; } + } - base = parents[i]; - at_fd = fd; + // Clear out the linked list + for (struct bftw_file *next = cur->next; cur != file; cur = next, next = next->next) { + cur->next = NULL; } - free(parents); return fd; } |