]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
repart: Reuse the backing fd for fdisk
authorValentin David <me@valentindavid.com>
Thu, 12 Mar 2026 22:14:23 +0000 (23:14 +0100)
committerValentin David <me@valentindavid.com>
Tue, 12 May 2026 16:31:21 +0000 (18:31 +0200)
Because fdisk_assign_device tries to open block devices with O_EXCL, when it
does it blocks cryptsetup from using partition block devices for the same
disk.

Since we already have a file descriptor for the device, we can just share it
and use fdisk_assign_device_by_fd instead.

This requires at least libfdisk 2.35 (part of util-linux) which was
released in 2020.

README
meson.build
src/repart/repart.c
src/shared/fdisk-util.c
src/shared/fdisk-util.h

diff --git a/README b/README
index 51a78bd2a1d00feaec901edd058cb5ef510bd7b5..30bb9ee5116d85f076a1ec0450cef77066c708ae 100644 (file)
--- a/README
+++ b/README
@@ -219,7 +219,7 @@ REQUIREMENTS:
         libacl (optional)
         libbpf >= 0.1.0 (optional),
                >= 1.4.0 is required for using GCC as a bpf compiler
-        libfdisk >= 2.32 (from util-linux) (optional)
+        libfdisk >= 2.35 (from util-linux) (optional)
         libselinux >= 2.1.9 (optional)
         libapparmor >= 2.13 (optional)
         libxenctrl >= 4.9 (optional)
index d5405995db22cbcf99da32d607be21cfe15a701f..2e78b359e6ea63ebdff82812779e73cd7743736b 100644 (file)
@@ -1076,7 +1076,7 @@ conf.set10('HAVE_LIBMOUNT', have)
 libmount_cflags = libmount.partial_dependency(includes: true, compile_args: true)
 
 libfdisk = dependency('fdisk',
-                      version : '>= 2.32',
+                      version : '>= 2.35',
                       required : get_option('fdisk'))
 libfdisk_cflags = libfdisk.partial_dependency(includes: true, compile_args: true)
 conf.set10('HAVE_LIBFDISK', libfdisk.found())
index 168092d43a8eb6019adda75e92e5dd179b217bd5..8246b87045c40a2a2c8be5fe67193feaff8f4a45 100644 (file)
@@ -544,6 +544,7 @@ struct Context {
         uint64_t start, end, total;
 
         struct fdisk_context *fdisk_context;
+        int fdisk_context_fd;
         uint64_t sector_size, grain_size, default_fs_sector_size;
 
         sd_id128_t seed;
@@ -948,6 +949,7 @@ static Context* context_new(
                 .empty = empty,
                 .dry_run = dry_run,
                 .backing_fd = -EBADF,
+                .fdisk_context_fd = -EBADF,
         };
 
         return context;
@@ -977,6 +979,7 @@ static Context* context_free(Context *context) {
 
         if (context->fdisk_context)
                 sym_fdisk_unref_context(context->fdisk_context);
+        safe_close(context->fdisk_context_fd);
 
         safe_close(context->backing_fd);
         if (context->node_is_our_file)
@@ -3162,16 +3165,17 @@ static int find_verity_sibling(Context *context, Partition *p, VerityMode mode,
         return 0;
 }
 
-static int context_open_and_lock_backing_fd(const char *node, int operation, int *backing_fd) {
+static int context_open_and_lock_backing_fd(const char *node, int operation, int mode, int *backing_fd) {
         _cleanup_close_ int fd = -EBADF;
 
         assert(node);
         assert(backing_fd);
+        assert(IN_SET(mode, O_RDONLY, O_RDWR));
 
         if (*backing_fd >= 0)
                 return 0;
 
-        fd = open(node, O_RDONLY|O_CLOEXEC);
+        fd = open(node, mode|O_CLOEXEC);
         if (fd < 0)
                 return log_error_errno(errno, "Failed to open device '%s': %m", node);
 
@@ -3264,7 +3268,7 @@ static int context_copy_from_one(Context *context, const char *src) {
 
         assert(src);
 
-        r = context_open_and_lock_backing_fd(src, LOCK_SH, &fd);
+        r = context_open_and_lock_backing_fd(src, LOCK_SH, O_RDONLY, &fd);
         if (r < 0)
                 return r;
 
@@ -3674,7 +3678,12 @@ static int context_load_fallback_metrics(Context *context) {
         return 1; /* Starting from scratch */
 }
 
+static int context_open_mode(Context *context) {
+        return ASSERT_PTR(context)->dry_run ? O_RDONLY : O_RDWR;
+}
+
 static int context_load_partition_table(Context *context) {
+        _cleanup_close_ int fd = -EBADF;
         _cleanup_(fdisk_unref_contextp) struct fdisk_context *c = NULL;
         _cleanup_(fdisk_unref_tablep) struct fdisk_table *t = NULL;
         uint64_t left_boundary = UINT64_MAX, first_lba, last_lba, nsectors;
@@ -3688,6 +3697,7 @@ static int context_load_partition_table(Context *context) {
         assert(context);
         assert(context->node);
         assert(!context->fdisk_context);
+        assert(context->fdisk_context_fd < 0);
         assert(!context->free_areas);
         assert(context->start == UINT64_MAX);
         assert(context->end == UINT64_MAX);
@@ -3709,6 +3719,7 @@ static int context_load_partition_table(Context *context) {
                 r = context_open_and_lock_backing_fd(
                                 context->node,
                                 context->dry_run ? LOCK_SH : LOCK_EX,
+                                context_open_mode(context),
                                 &context->backing_fd);
                 if (r < 0)
                         return r;
@@ -3739,11 +3750,15 @@ static int context_load_partition_table(Context *context) {
         if (r < 0)
                 return log_error_errno(r, "Failed to set sector size: %m");
 
-        /* libfdisk doesn't have an API to operate on arbitrary fds, hence reopen the fd going via the
-         * /proc/self/fd/ magic path if we have an existing fd. Open the original file otherwise. */
-        r = sym_fdisk_assign_device(
+        if (context->backing_fd < 0) {
+                fd = open(context->node, context_open_mode(context)|O_CLOEXEC);
+                if (fd < 0)
+                        return log_error_errno(errno, "Failed to open backing node '%s': %m", context->node);
+        }
+        r = sym_fdisk_assign_device_by_fd(
                         c,
-                        context->backing_fd >= 0 ? FORMAT_PROC_FD_PATH(context->backing_fd) : context->node,
+                        context->backing_fd >= 0 ? context->backing_fd : fd,
+                        context->node,
                         context->dry_run);
         if (r == -EINVAL && arg_size_auto) {
                 struct stat st;
@@ -3752,7 +3767,7 @@ static int context_load_partition_table(Context *context) {
                  * it if automatic sizing is requested. */
 
                 if (context->backing_fd < 0)
-                        r = stat(context->node, &st);
+                        r = fstat(fd, &st);
                 else
                         r = fstat(context->backing_fd, &st);
                 if (r < 0)
@@ -3775,6 +3790,7 @@ static int context_load_partition_table(Context *context) {
                 /* If we have no fd referencing the device yet, make a copy of the fd now, so that we have one */
                 r = context_open_and_lock_backing_fd(FORMAT_PROC_FD_PATH(sym_fdisk_get_devfd(c)),
                                                      context->dry_run ? LOCK_SH : LOCK_EX,
+                                                     context_open_mode(context),
                                                      &context->backing_fd);
                 if (r < 0)
                         return r;
@@ -4047,6 +4063,7 @@ add_initial_free_area:
         context->default_fs_sector_size = fs_secsz;
         context->grain_size = grainsz;
         context->fdisk_context = TAKE_PTR(c);
+        context->fdisk_context_fd = TAKE_FD(fd);
 
         return from_scratch;
 }
@@ -4103,6 +4120,7 @@ static void context_unload_partition_table(Context *context) {
                 sym_fdisk_unref_context(context->fdisk_context);
                 context->fdisk_context = NULL;
         }
+        context->fdisk_context_fd = safe_close(context->fdisk_context_fd);
 
         context_free_free_areas(context);
 }
@@ -10433,13 +10451,21 @@ static int acquire_root_devno(
         assert(ret);
         assert(ret_fd);
 
-        fd = chase_and_open(p, root, CHASE_PREFIX_ROOT, mode, &found_path);
+        fd = chase_and_open(p, root, CHASE_PREFIX_ROOT, (mode & ~O_ACCMODE_STRICT) | O_RDONLY, &found_path);
         if (fd < 0)
                 return fd;
 
         if (fstat(fd, &st) < 0)
                 return -errno;
 
+        if ((S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) && (mode & O_ACCMODE_STRICT) != O_RDONLY) {
+                _cleanup_close_ int new_fd = fd_reopen(fd, mode);
+                if (new_fd < 0)
+                        return new_fd;
+
+                close_and_replace(fd, new_fd);
+        }
+
         if (S_ISREG(st.st_mode)) {
                 *ret = TAKE_PTR(found_path);
                 *ret_fd = TAKE_FD(fd);
@@ -10499,7 +10525,7 @@ static int acquire_root_devno(
 
 static int find_root(Context *context) {
         _cleanup_free_ char *device = NULL;
-        int r;
+        int r, open_flags = O_CLOEXEC|context_open_mode(context);
 
         assert(context);
 
@@ -10515,7 +10541,7 @@ static int find_root(Context *context) {
                         if (!s)
                                 return log_oom();
 
-                        fd = xopenat_full(AT_FDCWD, arg_node, O_RDONLY|O_CREAT|O_EXCL|O_CLOEXEC|O_NOFOLLOW, XO_NOCOW, 0666);
+                        fd = xopenat_full(AT_FDCWD, arg_node, open_flags|O_CREAT|O_EXCL|O_NOFOLLOW, XO_NOCOW, 0666);
                         if (fd < 0)
                                 return log_error_errno(fd, "Failed to create '%s': %m", arg_node);
 
@@ -10527,7 +10553,7 @@ static int find_root(Context *context) {
 
                 /* Note that we don't specify a root argument here: if the user explicitly configured a node
                  * we'll take it relative to the host, not the image */
-                r = acquire_root_devno(arg_node, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
+                r = acquire_root_devno(arg_node, NULL, open_flags, &context->node, &context->backing_fd);
                 if (r == -EUCLEAN)
                         return btrfs_log_dev_root(LOG_ERR, r, arg_node);
                 if (r < 0)
@@ -10549,7 +10575,7 @@ static int find_root(Context *context) {
 
                 FOREACH_STRING(p, "/", "/usr") {
 
-                        r = acquire_root_devno(p, arg_root, O_RDONLY|O_DIRECTORY|O_CLOEXEC, &context->node,
+                        r = acquire_root_devno(p, arg_root, open_flags|O_DIRECTORY, &context->node,
                                                &context->backing_fd);
                         if (r < 0) {
                                 if (r == -EUCLEAN)
@@ -10562,7 +10588,7 @@ static int find_root(Context *context) {
         } else if (r < 0)
                 return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m");
         else {
-                r = acquire_root_devno(device, NULL, O_RDONLY|O_CLOEXEC, &context->node, &context->backing_fd);
+                r = acquire_root_devno(device, NULL, open_flags, &context->node, &context->backing_fd);
                 if (r == -EUCLEAN)
                         return btrfs_log_dev_root(LOG_ERR, r, device);
                 if (r < 0)
index 5eaa91160acd46018d1b394947aade63caee1716..00f1bfeaea2468a86c4824ab8430d84cf8f23ffd 100644 (file)
@@ -23,6 +23,7 @@ DLSYM_PROTOTYPE(fdisk_apply_table) = NULL;
 DLSYM_PROTOTYPE(fdisk_ask_get_type) = NULL;
 DLSYM_PROTOTYPE(fdisk_ask_string_set_result) = NULL;
 DLSYM_PROTOTYPE(fdisk_assign_device) = NULL;
+DLSYM_PROTOTYPE(fdisk_assign_device_by_fd) = NULL;
 DLSYM_PROTOTYPE(fdisk_create_disklabel) = NULL;
 DLSYM_PROTOTYPE(fdisk_delete_partition) = NULL;
 DLSYM_PROTOTYPE(fdisk_get_devfd) = NULL;
@@ -97,6 +98,7 @@ int dlopen_fdisk(int log_level) {
                         DLSYM_ARG(fdisk_ask_get_type),
                         DLSYM_ARG(fdisk_ask_string_set_result),
                         DLSYM_ARG(fdisk_assign_device),
+                        DLSYM_ARG(fdisk_assign_device_by_fd),
                         DLSYM_ARG(fdisk_create_disklabel),
                         DLSYM_ARG(fdisk_delete_partition),
                         DLSYM_ARG(fdisk_get_devfd),
index bdac11c5071aa5617165919ade31e9424a260df9..66620c9c1cf50b3e82640efe06060f590014378c 100644 (file)
@@ -14,6 +14,7 @@ extern DLSYM_PROTOTYPE(fdisk_apply_table);
 extern DLSYM_PROTOTYPE(fdisk_ask_get_type);
 extern DLSYM_PROTOTYPE(fdisk_ask_string_set_result);
 extern DLSYM_PROTOTYPE(fdisk_assign_device);
+extern DLSYM_PROTOTYPE(fdisk_assign_device_by_fd);
 extern DLSYM_PROTOTYPE(fdisk_create_disklabel);
 extern DLSYM_PROTOTYPE(fdisk_delete_partition);
 extern DLSYM_PROTOTYPE(fdisk_get_devfd);