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.
This commit is contained in:
Valentin David
2026-03-12 23:14:23 +01:00
parent ee2c2b1b89
commit 3ac8bd0e97
5 changed files with 45 additions and 16 deletions

2
README
View File

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

View 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())

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

View 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),

View 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);