summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2026-03-22 16:43:28 +0000
committerLeah Rowe <leah@libreboot.org>2026-03-22 16:50:01 +0000
commitc766c819b320c0b08cc0cb8e7b98b300a72e5806 (patch)
tree1567bad888fd2359a5658dadbf6af286e442591b
parent7694b307b82d58bd6b08876c432e196baea5d0dc (diff)
WIP: fs_resolve_at
yes. mkhtemp is ccoming along nicely Signed-off-by: Leah Rowe <leah@libreboot.org>
-rw-r--r--util/nvmutil/include/common.h2
-rw-r--r--util/nvmutil/lib/file.c153
2 files changed, 155 insertions, 0 deletions
diff --git a/util/nvmutil/include/common.h b/util/nvmutil/include/common.h
index 48de3b97..5803fa90 100644
--- a/util/nvmutil/include/common.h
+++ b/util/nvmutil/include/common.h
@@ -491,6 +491,8 @@ int close_on_eintr(int fd);
int fsync_on_eintr(int fd);
int fs_resolve(const char *path, int flags);
int fs_root_fd(void);
+int fs_mkdir_p_at(int dirfd, const char *path, mode_t mode);
+int fs_resolve_at(int dirfd, const char *path, int flags);
int fs_next_component(const char **p,
char *name, size_t namesz);
int fs_open_component(int dirfd, const char *name,
diff --git a/util/nvmutil/lib/file.c b/util/nvmutil/lib/file.c
index fd93bd6b..875f08a8 100644
--- a/util/nvmutil/lib/file.c
+++ b/util/nvmutil/lib/file.c
@@ -1932,6 +1932,159 @@ fs_root_fd(void)
return open("/", O_RDONLY | O_DIRECTORY | O_CLOEXEC);
}
+/* implementation of: mkdir -p
+ */
+int
+fs_mkdir_p_at(int dirfd, const char *path, mode_t mode)
+{
+ const char *p;
+ char name[256];
+ int nextfd = -1;
+ int saved_errno = errno;
+ int close_errno;
+ int r;
+ int is_last;
+
+ if (dirfd < 0 || path == NULL || *path == '\0') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ p = path;
+
+ for (;;) {
+
+ r = fs_next_component(&p, name, sizeof(name));
+ if (r < 0)
+ goto err;
+ if (r == 0)
+ break;
+
+ is_last = (*p == '\0');
+
+ /* TODO: consider more flags
+ * or make configurable
+ */
+ nextfd = openat(dirfd, name,
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+
+ if (nextfd < 0) {
+
+ if (errno != ENOENT)
+ goto err;
+
+ if (mkdirat(dirfd, name, mode) < 0)
+ goto err;
+
+ /* TODO: consider more flags
+ * or make configurable?
+ */
+ nextfd = openat(dirfd, name,
+ O_RDONLY | O_DIRECTORY | O_CLOEXEC);
+ if (nextfd < 0)
+ goto err;
+ }
+
+ close_errno = errno;
+ (void) close_on_eintr(dirfd);
+ errno = close_errno;
+
+ dirfd = nextfd;
+ nextfd = -1;
+ }
+
+ errno = saved_errno;
+ return 0;
+
+err:
+ saved_errno = errno;
+
+ if (dirfd >= 0)
+ (void) close_on_eintr(dirfd);
+ if (nextfd >= 0)
+ (void) close_on_eintr(nextfd);
+
+ errno = saved_errno;
+ return -1;
+}
+
+/* use in conjunction with fs_resolve. example:
+ * int rootfd = fs_resolve("/safe/root", O_RDONLY | O_DIRECTORY);
+ * now, you can resolve everything relatively, for instance:
+ * fd = fs_resolve_at(rootfd, "file.txt", O_RDONLY);
+ * if a user then tries e.g. ../etc/password ??????
+ * BLOCKED
+ * basically userspace sandboxing, similar to e.g.
+ * openbsd unveil
+ * freebsd capsicum
+ * openat2 RESOLVE_BENEATH
+ * ...but in ****userspace****
+ * no need for chroot (you should still use one in critical code)
+ * cannot escape the relative root that you set.
+ * no dependence on CWD
+ * probably safe in multi-threaded code (with some care)
+ *
+ * ps: you should still use unveil. unveil is awesome.
+ */
+int
+fs_resolve_at(int dirfd, const char *path, int flags)
+{
+ int nextfd = -1;
+ const char *p;
+ char name[256]; /* TODO: make configurable */
+ int saved_errno = errno;
+ int saved_close_errno;
+ int r;
+ int is_last;
+
+ if (dirfd < 0 ||
+ path == NULL ||
+ *path == '\0') {
+
+ errno = EINVAL;
+ return -1;
+ }
+
+ p = path;
+
+ for (;;) {
+
+ r = fs_next_component(&p, name, sizeof(name));
+ if (r < 0)
+ goto err;
+ if (r == 0)
+ break;
+
+ is_last = (*p == '\0');
+
+ nextfd = fs_open_component(dirfd,
+ name, flags, is_last);
+ if (nextfd < 0)
+ goto err;
+
+ saved_close_errno = errno;
+ (void) close_on_eintr(dirfd);
+ errno = saved_close_errno;
+
+ dirfd = nextfd;
+ nextfd = -1;
+ }
+
+ errno = saved_errno;
+ return dirfd;
+
+err:
+ saved_errno = errno;
+
+ if (dirfd >= 0)
+ close(dirfd);
+ if (nextfd >= 0)
+ close(nextfd);
+
+ errno = saved_errno;
+ return -1;
+}
+
int
fs_next_component(const char **p,
char *name, size_t namesz)