From: Daan De Meyer Date: Mon, 1 Dec 2025 22:52:29 +0000 (+0100) Subject: portable: Make extract_now() operate on a dirfd X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e4058c584fec03cf24c0ddbe7b040320ffcd2257;p=thirdparty%2Fsystemd.git portable: Make extract_now() operate on a dirfd Preparation for making portabled support unpriv operation. --- diff --git a/src/portable/portable.c b/src/portable/portable.c index 828ef703cde..78da62e053d 100644 --- a/src/portable/portable.c +++ b/src/portable/portable.c @@ -180,7 +180,8 @@ DEFINE_PRIVATE_HASH_OPS_WITH_VALUE_DESTRUCTOR(portable_metadata_hash_ops, char, static int extract_now( RuntimeScope scope, - const char *where, + int rfd, + const char *image_path, char **matches, const char *image_name, bool path_is_extension, @@ -197,7 +198,7 @@ static int extract_now( const char *os_release_id; int r; - /* Extracts the metadata from a directory tree 'where'. Extracts two kinds of information: the /etc/os-release + /* Extracts the metadata from a directory tree 'dir_fd'. Extracts two kinds of information: the /etc/os-release * data, and all unit files matching the specified expression. Note that this function is called in two very * different but also similar contexts. When the tool gets invoked on a directory tree, we'll process it * directly, and in-process, and thus can return the requested data directly, via 'ret_os_release' and @@ -207,15 +208,15 @@ static int extract_now( * used to send the data to the parent. */ assert(scope < _RUNTIME_SCOPE_MAX); - assert(where); + assert(rfd >= 0); /* First, find os-release/extension-release and send it upstream (or just save it). */ if (path_is_extension) { ImageClass class = IMAGE_SYSEXT; - r = open_extension_release(where, IMAGE_SYSEXT, image_name, relax_extension_release_check, &os_release_path, &os_release_fd); + r = open_extension_release_at(rfd, IMAGE_SYSEXT, image_name, relax_extension_release_check, &os_release_path, &os_release_fd); if (r == -ENOENT) { - r = open_extension_release(where, IMAGE_CONFEXT, image_name, relax_extension_release_check, &os_release_path, &os_release_fd); + r = open_extension_release_at(rfd, IMAGE_CONFEXT, image_name, relax_extension_release_check, &os_release_path, &os_release_fd); if (r >= 0) class = IMAGE_CONFEXT; } @@ -225,7 +226,7 @@ static int extract_now( os_release_id = strjoina((class == IMAGE_SYSEXT) ? "/usr/lib" : "/etc", "/extension-release.d/extension-release.", image_name); } else { os_release_id = "/etc/os-release"; - r = open_os_release(where, &os_release_path, &os_release_fd); + r = open_os_release_at(rfd, &os_release_path, &os_release_fd); } if (r < 0) log_debug_errno(r, @@ -256,7 +257,7 @@ static int extract_now( /* Then, send unit file data to the parent (or/and add it to the hashmap). For that we use our usual unit * discovery logic. Note that we force looking inside of /lib/systemd/system/ for units too, as the * image might have a legacy split-usr layout. */ - r = lookup_paths_init(&paths, scope, LOOKUP_PATHS_SPLIT_USR, where); + r = lookup_paths_init(&paths, scope, LOOKUP_PATHS_SPLIT_USR, /* root_dir= */ NULL); if (r < 0) return log_debug_errno(r, "Failed to acquire lookup paths: %m"); @@ -265,15 +266,19 @@ static int extract_now( return -ENOMEM; STRV_FOREACH(i, paths.search_path) { - _cleanup_free_ char *resolved = NULL; + _cleanup_free_ char *relative = NULL, *resolved = NULL; _cleanup_closedir_ DIR *d = NULL; - r = chase_and_opendir(*i, where, 0, &resolved, &d); + r = chase_and_opendirat(rfd, *i, CHASE_AT_RESOLVE_IN_ROOT, &relative, &d); if (r < 0) { log_debug_errno(r, "Failed to open unit path '%s', ignoring: %m", *i); continue; } + r = chaseat_prefix_root(relative, image_path, &resolved); + if (r < 0) + return r; + FOREACH_DIRENT(de, d, return log_debug_errno(errno, "Failed to read directory: %m")) { _cleanup_(portable_metadata_unrefp) PortableMetadata *m = NULL; _cleanup_freecon_ char *con = NULL; @@ -332,7 +337,7 @@ static int extract_now( return log_debug_errno(r, "Failed to send unit metadata to parent: %m"); } - m = portable_metadata_new(de->d_name, where, con, fd); + m = portable_metadata_new(de->d_name, image_path, con, fd); if (!m) return -ENOMEM; fd = -EBADF; @@ -384,16 +389,28 @@ static int portable_extract_by_path( LOCK_SH, &d); if (r == -EISDIR) { - _cleanup_free_ char *image_name = NULL; - /* We can't turn this into a loop-back block device, and this returns EISDIR? Then this is a directory * tree and not a raw device. It's easy then. */ + _cleanup_free_ char *image_name = NULL; r = path_extract_filename(path, &image_name); if (r < 0) return log_error_errno(r, "Failed to extract image name from path '%s': %m", path); - r = extract_now(scope, path, matches, image_name, path_is_extension, /* relax_extension_release_check= */ false, -1, &os_release, &unit_files); + _cleanup_close_ int rfd = open(path, O_DIRECTORY|O_CLOEXEC); + if (rfd < 0) + return log_error_errno(errno, "Failed to open '%s': %m", path); + + r = extract_now(scope, + rfd, + path, + matches, + image_name, + path_is_extension, + /* relax_extension_release_check= */ false, + /* socket_fd= */ -EBADF, + &os_release, + &unit_files); if (r < 0) return r; @@ -496,7 +513,22 @@ static int portable_extract_by_path( report_errno_and_exit(errno_pipe_fd[1], r); } - r = extract_now(scope, tmpdir, matches, m->image_name, path_is_extension, relax_extension_release_check, seq[1], NULL, NULL); + _cleanup_close_ int rfd = open(tmpdir, O_DIRECTORY|O_CLOEXEC); + if (rfd < 0) { + r = log_debug_errno(errno, "Failed to open '%s': %m", tmpdir); + report_errno_and_exit(errno_pipe_fd[1], r); + } + + r = extract_now(scope, + rfd, + path, + matches, + m->image_name, + path_is_extension, + relax_extension_release_check, + seq[1], + /* ret_os_release= */ NULL, + /* ret_unit_files= */ NULL); report_errno_and_exit(errno_pipe_fd[1], r); }