vpick: take separate root_fd and dir_fd arguments

Mirror how chaseat() works these days: instead of a single toplevel_fd that
serves as both the root (chroot) boundary and the directory that resolution
starts from, path_pick() now takes a separate root_fd and dir_fd. This lets
callers resolve a path relative to a specific directory fd while confining
symlink and absolute-path resolution to a root directory fd.

All existing callers are updated to pass the same fd for both, preserving
their current behaviour.
This commit is contained in:
Lennart Poettering
2026-06-24 12:59:55 +02:00
parent 8168aca9fb
commit 95a372a514
9 changed files with 109 additions and 83 deletions
+2
View File
@@ -956,6 +956,7 @@ int image_find(RuntimeScope scope,
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick(root,
rfd,
rfd,
fname_path, /* This has to be the unresolved entry with the .v suffix */
&filter,
@@ -1177,6 +1178,7 @@ int image_discover(
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick(root,
rfd,
rfd,
fname_path, /* This has to be the unresolved entry with the .v suffix */
&filter,
+1 -1
View File
@@ -139,7 +139,7 @@ static int mstack_load_one(MStack *mstack, const char *dir, int dir_fd, const ch
};
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick(dir, dir_fd, fname, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE, &result);
r = path_pick(dir, dir_fd, dir_fd, fname, &filter, /* n_filters= */ 1, PICK_ARCHITECTURE, &result);
if (r < 0)
return log_debug_errno(r, "Failed to resolve '%s' directory: %m", fname);
if (r == 0)
+52 -38
View File
@@ -176,8 +176,9 @@ static int errno_from_mode(uint32_t type_mask, mode_t found) {
}
static int pin_choice(
const char *toplevel_path,
int toplevel_fd,
const char *root_path,
int root_fd,
int dir_fd,
const char *inode_path,
int _inode_fd, /* we always take ownership of the fd, even on failure */
unsigned tries_left,
@@ -190,14 +191,15 @@ static int pin_choice(
_cleanup_free_ char *resolved_path = NULL;
int r;
assert(wildcard_fd_is_valid(toplevel_fd));
assert(wildcard_fd_is_valid(root_fd));
assert(wildcard_fd_is_valid(dir_fd));
assert(inode_path);
assert(filter);
assert(ret);
if (inode_fd < 0 || FLAGS_SET(flags, PICK_RESOLVE)) {
r = chaseat(toplevel_fd,
toplevel_fd,
r = chaseat(root_fd,
dir_fd,
inode_path,
/* flags= */ 0,
FLAGS_SET(flags, PICK_RESOLVE) ? &resolved_path : NULL,
@@ -212,11 +214,11 @@ static int pin_choice(
struct stat st;
if (fstat(inode_fd, &st) < 0)
return log_debug_errno(errno, "Failed to stat discovered inode '%s%s': %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
empty_to_root(root_path), skip_leading_slash(inode_path));
if (filter->type_mask != 0 && !BIT_SET(filter->type_mask, IFTODT(st.st_mode))) {
log_debug("Inode '%s/%s' has wrong type, found '%s'.",
empty_to_root(toplevel_path), skip_leading_slash(inode_path),
empty_to_root(root_path), skip_leading_slash(inode_path),
inode_type_to_string(st.st_mode));
*ret = PICK_RESULT_NULL;
return 0;
@@ -311,8 +313,9 @@ static bool architecture_matches(const PickFilter *filter, Architecture a) {
}
static int make_choice(
const char *toplevel_path,
int toplevel_fd,
const char *root_path,
int root_fd,
int dir_fd,
const char *inode_path,
int _inode_fd, /* we always take ownership of the fd, even on failure */
const PickFilter *filter,
@@ -322,13 +325,14 @@ static int make_choice(
_cleanup_close_ int inode_fd = TAKE_FD(_inode_fd);
int r;
assert(wildcard_fd_is_valid(toplevel_fd));
assert(wildcard_fd_is_valid(root_fd));
assert(wildcard_fd_is_valid(dir_fd));
assert(inode_path);
assert(filter);
assert(ret);
if (inode_fd < 0) {
r = chaseat(toplevel_fd, toplevel_fd, inode_path, /* flags= */ 0, NULL, &inode_fd);
r = chaseat(root_fd, dir_fd, inode_path, /* flags= */ 0, NULL, &inode_fd);
if (r < 0)
return r;
}
@@ -345,18 +349,19 @@ static int make_choice(
return log_oom_debug();
_cleanup_close_ int object_fd = -EBADF;
r = chaseat(toplevel_fd, toplevel_fd, p, /* flags= */ 0, &object_path, &object_fd);
r = chaseat(root_fd, dir_fd, p, /* flags= */ 0, &object_path, &object_fd);
if (r == -ENOENT) {
*ret = PICK_RESULT_NULL;
return 0;
}
if (r < 0)
return log_debug_errno(r, "Failed to open '%s/%s': %m",
empty_to_root(toplevel_path), skip_leading_slash(p));
empty_to_root(root_path), skip_leading_slash(p));
return pin_choice(
toplevel_path,
toplevel_fd,
root_path,
root_fd,
dir_fd,
FLAGS_SET(flags, PICK_RESOLVE) ? object_path : p,
TAKE_FD(object_fd), /* unconditionally pass ownership of the fd */
/* tries_left= */ UINT_MAX,
@@ -371,16 +376,16 @@ static int make_choice(
/* Underspecified, so we do our enumeration dance */
/* Convert O_PATH to a regular directory fd */
_cleanup_close_ int dir_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (dir_fd < 0)
return log_debug_errno(dir_fd, "Failed to reopen '%s/%s' as directory: %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
_cleanup_close_ int enumerate_fd = fd_reopen(inode_fd, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (enumerate_fd < 0)
return log_debug_errno(enumerate_fd, "Failed to reopen '%s/%s' as directory: %m",
empty_to_root(root_path), skip_leading_slash(inode_path));
_cleanup_free_ DirectoryEntries *de = NULL;
r = readdir_all(dir_fd, 0, &de);
r = readdir_all(enumerate_fd, 0, &de);
if (r < 0)
return log_debug_errno(r, "Failed to read directory '%s/%s': %m",
empty_to_root(toplevel_path), skip_leading_slash(inode_path));
empty_to_root(root_path), skip_leading_slash(inode_path));
_cleanup_(pick_result_done) PickResult best = PICK_RESULT_NULL;
@@ -457,8 +462,9 @@ static int make_choice(
return log_oom_debug();
_cleanup_(pick_result_done) PickResult found = PICK_RESULT_NULL;
r = pin_choice(toplevel_path,
toplevel_fd,
r = pin_choice(root_path,
root_fd,
dir_fd,
p,
/* _inode_fd= */ -EBADF,
found_tries_left,
@@ -501,8 +507,9 @@ static int make_choice(
}
static int path_pick_one(
const char *toplevel_path,
int toplevel_fd,
const char *root_path,
int root_fd,
int dir_fd,
const char *path,
const PickFilter *filter,
PickFlags flags,
@@ -513,7 +520,8 @@ static int path_pick_one(
uint32_t filter_type_mask;
int r;
assert(wildcard_fd_is_valid(toplevel_fd));
assert(wildcard_fd_is_valid(root_fd));
assert(wildcard_fd_is_valid(dir_fd));
assert(path);
assert(filter);
assert(ret);
@@ -537,8 +545,9 @@ static int path_pick_one(
/* Explicit basename specified, then shortcut things and do .v mode regardless of the path name. */
if (filter->basename)
return make_choice(
toplevel_path,
toplevel_fd,
root_path,
root_fd,
dir_fd,
path,
/* inode_fd= */ -EBADF,
filter,
@@ -623,8 +632,9 @@ static int path_pick_one(
}
return make_choice(
toplevel_path,
toplevel_fd,
root_path,
root_fd,
dir_fd,
enumeration_path,
/* inode_fd= */ -EBADF,
&(const PickFilter) {
@@ -640,8 +650,9 @@ static int path_pick_one(
bypass:
/* Don't make any choice, but just use the passed path literally */
return pin_choice(
toplevel_path,
toplevel_fd,
root_path,
root_fd,
dir_fd,
path,
/* inode_fd= */ -EBADF,
/* tries_left= */ UINT_MAX,
@@ -651,8 +662,9 @@ bypass:
ret);
}
int path_pick(const char *toplevel_path,
int toplevel_fd,
int path_pick(const char *root_path,
int root_fd,
int dir_fd,
const char *path,
const PickFilter filters[],
size_t n_filters,
@@ -662,7 +674,8 @@ int path_pick(const char *toplevel_path,
_cleanup_(pick_result_done) PickResult best = PICK_RESULT_NULL;
int r;
assert(wildcard_fd_is_valid(toplevel_fd));
assert(wildcard_fd_is_valid(root_fd));
assert(wildcard_fd_is_valid(dir_fd));
assert(path);
assert(filters || n_filters == 0);
assert(ret);
@@ -671,7 +684,7 @@ int path_pick(const char *toplevel_path,
for (size_t i = 0; i < n_filters; i++) {
_cleanup_(pick_result_done) PickResult result = PICK_RESULT_NULL;
r = path_pick_one(toplevel_path, toplevel_fd, path, &filters[i], flags, &result);
r = path_pick_one(root_path, root_fd, dir_fd, path, &filters[i], flags, &result);
if (r < 0)
return r;
if (r == 0)
@@ -717,8 +730,9 @@ int path_pick_update_warn(
/* This updates the first argument if needed! */
r = path_pick(/* toplevel_path= */ NULL,
/* toplevel_fd= */ AT_FDCWD,
r = path_pick(/* root_path= */ NULL,
/* root_fd= */ AT_FDCWD,
/* dir_fd= */ AT_FDCWD,
*path,
filters,
n_filters,
+3 -2
View File
@@ -45,8 +45,9 @@ void pick_result_done(PickResult *p);
int pick_result_compare(const PickResult *a, const PickResult *b, PickFlags flags);
int path_pick(const char *toplevel_path,
int toplevel_fd,
int path_pick(const char *root_path,
int root_fd,
int dir_fd,
const char *path,
const PickFilter filters[],
size_t n_filters,