summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2024-01-13 12:42:42 -0500
committerTavian Barnes <tavianator@tavianator.com>2024-01-13 12:42:42 -0500
commite9588c49d5539ded993f720fc6855d6fa878c997 (patch)
treed827157c37f59335252ba874fd41604984775471
parentd6cae04b54c1d58223e1719101b7c54d348e8d80 (diff)
downloadbfs-e9588c49d5539ded993f720fc6855d6fa878c997.tar.xz
bfstd: New {error,errno}_is_like() functions
We used to have is_nonexistence_error() to consistently treat ENOENT and ENOTDIR the same. Recently, we started considering EFAULT the same as ENAMETOOLONG on DragonFly BSD to work around a kernel bug. Unify both of these behind a more generic interface.
-rw-r--r--src/bfstd.c23
-rw-r--r--src/bfstd.h18
-rw-r--r--src/bftw.c18
-rw-r--r--src/eval.c2
-rw-r--r--src/stat.c2
-rw-r--r--tests/xtouch.c17
6 files changed, 45 insertions, 35 deletions
diff --git a/src/bfstd.c b/src/bfstd.c
index c0e61cb..15e8667 100644
--- a/src/bfstd.c
+++ b/src/bfstd.c
@@ -36,8 +36,27 @@
# include <util.h>
#endif
-bool is_nonexistence_error(int error) {
- return error == ENOENT || errno == ENOTDIR;
+bool error_is_like(int error, int category) {
+ if (error == category) {
+ return true;
+ }
+
+ switch (category) {
+ case ENOENT:
+ return error == ENOTDIR;
+
+#if __DragonFly__
+ // https://twitter.com/tavianator/status/1742991411203485713
+ case ENAMETOOLONG:
+ return error == EFAULT;
+#endif
+ }
+
+ return false;
+}
+
+bool errno_is_like(int category) {
+ return error_is_like(errno, category);
}
char *xdirname(const char *path) {
diff --git a/src/bfstd.h b/src/bfstd.h
index 0fcb892..8953b9b 100644
--- a/src/bfstd.h
+++ b/src/bfstd.h
@@ -45,9 +45,23 @@
// #include <errno.h>
/**
- * Return whether an error code is due to a path not existing.
+ * Check if an error code is "like" another one. For example, ENOTDIR is
+ * like ENOENT because they can both be triggered by non-existent paths.
+ *
+ * @param error
+ * The error code to check.
+ * @param category
+ * The category to test for. Known categories include ENOENT and
+ * ENAMETOOLONG.
+ * @return
+ * Whether the error belongs to the given category.
+ */
+bool error_is_like(int error, int category);
+
+/**
+ * Equivalent to error_is_like(errno, category).
*/
-bool is_nonexistence_error(int error);
+bool errno_is_like(int category);
#include <fcntl.h>
diff --git a/src/bftw.c b/src/bftw.c
index 355cb54..49f07df 100644
--- a/src/bftw.c
+++ b/src/bftw.c
@@ -68,7 +68,7 @@ const struct bfs_stat *bftw_stat(const struct BFTW *ftwbuf, enum bfs_stat_flags
}
} else {
ret = bftw_stat_impl(mutbuf, &mutbuf->stat_cache, BFS_STAT_FOLLOW);
- if (!ret && (flags & BFS_STAT_TRYFOLLOW) && is_nonexistence_error(errno)) {
+ if (!ret && (flags & BFS_STAT_TRYFOLLOW) && errno_is_like(ENOENT)) {
ret = bftw_stat_impl(mutbuf, &mutbuf->lstat_cache, BFS_STAT_NOFOLLOW);
}
}
@@ -81,7 +81,7 @@ const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat
return ftwbuf->lstat_cache.buf;
} else if (ftwbuf->stat_cache.buf) {
return ftwbuf->stat_cache.buf;
- } else if ((flags & BFS_STAT_TRYFOLLOW) && is_nonexistence_error(ftwbuf->stat_cache.error)) {
+ } else if ((flags & BFS_STAT_TRYFOLLOW) && error_is_like(ftwbuf->stat_cache.error, ENOENT)) {
return ftwbuf->lstat_cache.buf;
} else {
return NULL;
@@ -739,22 +739,10 @@ static int bftw_file_open(struct bftw_state *state, struct bftw_file *file, cons
}
int fd = bftw_file_openat(state, file, base, at_path);
- if (fd >= 0) {
+ if (fd >= 0 || !errno_is_like(ENAMETOOLONG)) {
return fd;
}
- switch (errno) {
- case ENAMETOOLONG:
-#if __DragonFly__
- // https://twitter.com/tavianator/status/1742991411203485713
- case EFAULT:
-#endif
- break;
-
- default:
- return -1;
- }
-
// Handle ENAMETOOLONG by manually traversing the path component-by-component
struct bftw_list parents;
SLIST_INIT(&parents);
diff --git a/src/eval.c b/src/eval.c
index 130b366..1a814b3 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -80,7 +80,7 @@ static void eval_error(struct bfs_eval *state, const char *format, ...) {
*/
static bool eval_should_ignore(const struct bfs_eval *state, int error) {
return state->ctx->ignore_races
- && is_nonexistence_error(error)
+ && error_is_like(error, ENOENT)
&& state->ftwbuf->depth > 0;
}
diff --git a/src/stat.c b/src/stat.c
index d7387c6..91aa092 100644
--- a/src/stat.c
+++ b/src/stat.c
@@ -260,7 +260,7 @@ static int bfs_stat_tryfollow(int at_fd, const char *at_path, int at_flags, int
if (ret != 0
&& (bfs_flags & (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW)) == BFS_STAT_TRYFOLLOW
- && is_nonexistence_error(errno))
+ && errno_is_like(ENOENT))
{
at_flags |= AT_SYMLINK_NOFOLLOW;
ret = bfs_stat_explicit(at_fd, at_path, at_flags, x_flags, buf);
diff --git a/tests/xtouch.c b/tests/xtouch.c
index ed8bbee..6099128 100644
--- a/tests/xtouch.c
+++ b/tests/xtouch.c
@@ -68,22 +68,11 @@ static int open_parent(const struct args *args, const char **path) {
goto done;
}
- switch (errno) {
- case ENAMETOOLONG:
-#if __DragonFly__
- // https://twitter.com/tavianator/status/1742991411203485713
- case EFAULT:
-#endif
- break;
-
- case ENOENT:
- if (args->flags & CREATE_PARENTS) {
- break;
- } else {
+ if (errno == ENOENT) {
+ if (!(args->flags & CREATE_PARENTS)) {
goto err;
}
-
- default:
+ } else if (!errno_is_like(ENAMETOOLONG)) {
goto err;
}