WIP: fs_resolve_at

yes. mkhtemp is ccoming along nicely

Signed-off-by: Leah Rowe <leah@libreboot.org>
This commit is contained in:
Leah Rowe
2026-03-22 16:43:28 +00:00
parent 7694b307b8
commit c766c819b3
2 changed files with 155 additions and 0 deletions

View File

@@ -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,

View File

@@ -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)