From: Valentin David Date: Thu, 12 Mar 2026 22:14:23 +0000 (+0100) Subject: repart: Reuse the backing fd for fdisk X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=3ac8bd0e97c4e3991c8d79df60549a4aee0f88ae;p=thirdparty%2Fsystemd.git repart: Reuse the backing fd for fdisk 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. --- diff --git a/README b/README index 51a78bd2a1d..30bb9ee5116 100644 --- 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) diff --git a/meson.build b/meson.build index d5405995db2..2e78b359e6e 100644 --- a/meson.build +++ b/meson.build @@ -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()) diff --git a/src/repart/repart.c b/src/repart/repart.c index 168092d43a8..8246b87045c 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -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) diff --git a/src/shared/fdisk-util.c b/src/shared/fdisk-util.c index 5eaa91160ac..00f1bfeaea2 100644 --- a/src/shared/fdisk-util.c +++ b/src/shared/fdisk-util.c @@ -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), diff --git a/src/shared/fdisk-util.h b/src/shared/fdisk-util.h index bdac11c5071..66620c9c1cf 100644 --- a/src/shared/fdisk-util.h +++ b/src/shared/fdisk-util.h @@ -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);