Make repart more user-friendly (#42345)

This commit is contained in:
Luca Boccassi
2026-06-03 20:22:48 +01:00
committed by GitHub
5 changed files with 131 additions and 59 deletions
+16
View File
@@ -485,6 +485,22 @@ bool fstype_can_fmask_dmask(const char *fstype) {
return streq(fstype, "vfat") || (mount_option_supported(fstype, "fmask", "0177") > 0 && mount_option_supported(fstype, "dmask", "0077") > 0);
}
bool fstype_can_ownership(const char *fstype) {
/* File systems which are not known to not support uid/gid ownership.
* For some types, this can be a bit murky. So just exclude the ones that for sure
* don't support with the current implementations in Linux. */
return !STR_IN_SET(ASSERT_PTR(fstype),
"adfs",
"exfat",
"fat",
"hfs",
"hpfs",
"msdos",
"ntfs",
"vfat");
}
bool fstype_can_uid_gid(const char *fstype) {
/* All file systems that have a uid=/gid= mount option that fixates the owners of all files and
* directories, current and future. Note that this does *not* ask the kernel via
+1
View File
@@ -66,6 +66,7 @@ bool fstype_is_api_vfs(const char *fstype);
bool fstype_is_blockdev_backed(const char *fstype);
bool fstype_is_ro(const char *fsype);
bool fstype_can_discard(const char *fstype);
bool fstype_can_ownership(const char *fstype);
bool fstype_can_uid_gid(const char *fstype);
bool fstype_can_fmask_dmask(const char *fstype);
+60 -13
View File
@@ -1269,7 +1269,7 @@ static uint64_t partition_max_padding(const Partition *p) {
return p->suppressing ? MIN(p->padding_max, p->suppressing->padding_max) : p->padding_max;
}
static uint64_t partition_min_size_with_padding(Context *context, const Partition *p) {
static uint64_t partition_min_size_with_padding(const Context *context, const Partition *p) {
uint64_t sz;
/* Calculate the disk space we need for this partition plus any free space coming after it. This
@@ -1300,7 +1300,7 @@ static uint64_t free_area_available(const FreeArea *a) {
return a->size - a->allocated;
}
static uint64_t free_area_current_end(Context *context, const FreeArea *a) {
static uint64_t free_area_current_end(const Context *context, const FreeArea *a) {
assert(context);
assert(a);
@@ -1316,7 +1316,7 @@ static uint64_t free_area_current_end(Context *context, const FreeArea *a) {
return round_up_size(a->after->offset + a->after->current_size, context->grain_size) + free_area_available(a);
}
static uint64_t free_area_min_end(Context *context, const FreeArea *a) {
static uint64_t free_area_min_end(const Context *context, const FreeArea *a) {
assert(context);
assert(a);
@@ -1330,7 +1330,7 @@ static uint64_t free_area_min_end(Context *context, const FreeArea *a) {
return round_up_size(a->after->offset + partition_min_size_with_padding(context, a->after), context->grain_size);
}
static uint64_t free_area_available_for_new_partitions(Context *context, const FreeArea *a) {
static uint64_t free_area_available_for_new_partitions(const Context *context, const FreeArea *a) {
assert(context);
assert(a);
@@ -1349,7 +1349,7 @@ static int free_area_compare(FreeArea *const *a, FreeArea *const*b, Context *con
free_area_available_for_new_partitions(context, *b));
}
static uint64_t charge_size(Context *context, uint64_t total, uint64_t amount) {
static uint64_t charge_size(const Context *context, uint64_t total, uint64_t amount) {
assert(context);
/* Subtract the specified amount from total, rounding up to multiple of 4K if there's room */
assert(amount <= total);
@@ -1478,7 +1478,7 @@ static uint32_t partition_padding_weight(const Partition *p) {
return p->suppressing ? p->suppressing->padding_weight : p->padding_weight;
}
static int context_sum_weights(Context *context, FreeArea *a, uint64_t *ret) {
static int context_sum_weights(const Context *context, const FreeArea *a, uint64_t *ret) {
uint64_t weight_sum = 0;
assert(context);
@@ -1542,8 +1542,8 @@ typedef enum GrowPartitionPhase {
} GrowPartitionPhase;
static bool context_grow_partitions_phase(
Context *context,
FreeArea *a,
const Context *context,
const FreeArea *a,
GrowPartitionPhase phase,
uint64_t *span,
uint64_t *weight_sum) {
@@ -4926,7 +4926,7 @@ static int context_discard_gap_after(Context *context, Partition *p) {
return 0;
}
static bool partition_defer(Context *c, const Partition *p) {
static bool partition_defer(const Context *c, const Partition *p) {
assert(c);
assert(p);
@@ -6712,6 +6712,8 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
return log_oom();
}
bool copy_ownership = fstype_can_ownership(p->format);
/* copy_tree_at() automatically copies the permissions of source directories to target directories if
* it created them. However, the root directory is created by us, so we have to manually take care
* that it is initialized. We use the first source directory targeting "/" as the metadata source for
@@ -6733,12 +6735,16 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
return log_error_errno(sfd, "Failed to open source file '%s%s': %m", strempty(arg_copy_source), line->source);
(void) copy_xattr(sfd, NULL, rfd, NULL, COPY_ALL_XATTRS);
(void) copy_access(sfd, rfd);
if (copy_ownership)
(void) copy_access(sfd, rfd);
(void) copy_times(sfd, rfd, 0);
break;
}
uid_t uid = copy_ownership ? UID_INVALID : getuid();
gid_t gid = copy_ownership ? GID_INVALID : getgid();
FOREACH_ARRAY(line, copy_files, n_copy_files) {
_cleanup_hashmap_free_ Hashmap *denylist = NULL;
_cleanup_hashmap_free_ Hashmap *subvolumes_by_source_inode = NULL;
@@ -6795,14 +6801,14 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
r = copy_tree_at(
sfd, ".",
pfd, fn,
UID_INVALID, GID_INVALID,
uid, gid,
line->flags,
denylist, subvolumes_by_source_inode);
} else
r = copy_tree_at(
sfd, ".",
tfd, ".",
UID_INVALID, GID_INVALID,
uid, gid,
line->flags,
denylist, subvolumes_by_source_inode);
if (r < 0)
@@ -6849,7 +6855,8 @@ static int do_copy_files(Context *context, Partition *p, const char *root) {
return log_error_errno(r, "Failed to copy '%s' to '%s%s': %m", line->source, strempty(arg_copy_source), line->target);
(void) copy_xattr(sfd, NULL, tfd, NULL, COPY_ALL_XATTRS);
(void) copy_access(sfd, tfd);
if (copy_ownership)
(void) copy_access(sfd, tfd);
(void) copy_times(sfd, tfd, 0);
if (ts != USEC_INFINITY) {
@@ -7364,6 +7371,42 @@ static void context_btrfs_replace_back(Context *context) {
}
}
static int check_mkfs(const Context *context) {
int r;
assert(context);
LIST_FOREACH(partitions, p, context->partitions) {
if (p->dropped)
continue;
if (PARTITION_EXISTS(p)) /* Never format existing partitions */
continue;
if (!p->format)
continue;
if (partition_defer(context, p))
continue;
/* For offline signing case */
if (!set_isempty(arg_verity_settings) && partition_designator_is_verity_sig(p->type.designator))
continue;
/* Minimized partitions will use the copy blocks logic so skip those here. */
if (p->copy_blocks_fd >= 0)
continue;
/* We don't yet quite know if have_root= will be true, so just pass -1 which
* means "not sure". */
r = mkfs_find_or_warn(p->format, /* have_root= */ -1, /* ret= */ NULL);
if (r < 0)
return r;
}
return 0;
}
static int context_mkfs(Context *context) {
int r;
@@ -11674,6 +11717,10 @@ static int run(int argc, char *argv[]) {
return r;
context->from_scratch = r > 0; /* Starting from scratch */
r = check_mkfs(context);
if (r < 0)
return r;
if (arg_can_factory_reset) {
r = context_can_factory_reset(context);
if (r < 0)
+53 -46
View File
@@ -39,6 +39,56 @@ int mkfs_exists(const char *fstype) {
return true;
}
int mkfs_find_or_warn(const char *fstype, int have_root, char **ret) {
int r;
assert(fstype);
if (fstype_is_ro(fstype) && have_root == 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Cannot generate read-only filesystem %s without a source tree.", fstype);
const char *bin = NULL;
if (streq(fstype, "swap")) {
if (have_root > 0)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A swap filesystem can't be populated, refusing");
bin = "mkswap";
} else if (streq(fstype, "squashfs"))
bin = "mksquashfs";
else if (streq(fstype, "erofs"))
bin = "mkfs.erofs";
else if (fstype_is_ro(fstype))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Don't know how to create read-only file system '%s', refusing.", fstype);
if (bin) {
r = find_executable(bin, ret);
if (r == -ENOENT)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "%s binary not available.", bin);
if (r < 0)
return log_error_errno(r, "Failed to determine whether %s binary exists: %m", bin);
return 0;
}
if (have_root > 0 && !mkfs_supports_root_option(fstype))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Populating with source tree is not supported for %s", fstype);
r = mkfs_exists(fstype);
if (r < 0)
return log_error_errno(r, "Failed to determine whether mkfs binary for %s exists: %m", fstype);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs binary for %s not available.", fstype);
if (ret) {
char *mkfs = strjoin("mkfs.", fstype);
if (!mkfs)
return log_oom();
*ret = mkfs;
}
return 0;
}
int mkfs_supports_root_option(const char *fstype) {
return fstype_is_ro(fstype) || STR_IN_SET(fstype, "ext2", "ext3", "ext4", "btrfs", "vfat", "xfs");
}
@@ -190,52 +240,9 @@ int make_filesystem(
assert(fstype);
assert(label);
if (fstype_is_ro(fstype) && !root)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Cannot generate read-only filesystem %s without a source tree.",
fstype);
if (streq(fstype, "swap")) {
if (root)
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"A swap filesystem can't be populated, refusing");
r = find_executable("mkswap", &mkfs);
if (r == -ENOENT)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkswap binary not available.");
if (r < 0)
return log_error_errno(r, "Failed to determine whether mkswap binary exists: %m");
} else if (streq(fstype, "squashfs")) {
r = find_executable("mksquashfs", &mkfs);
if (r == -ENOENT)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mksquashfs binary not available.");
if (r < 0)
return log_error_errno(r, "Failed to determine whether mksquashfs binary exists: %m");
} else if (streq(fstype, "erofs")) {
r = find_executable("mkfs.erofs", &mkfs);
if (r == -ENOENT)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs.erofs binary not available.");
if (r < 0)
return log_error_errno(r, "Failed to determine whether mkfs.erofs binary exists: %m");
} else if (fstype_is_ro(fstype)) {
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Don't know how to create read-only file system '%s', refusing.",
fstype);
} else {
if (root && !mkfs_supports_root_option(fstype))
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Populating with source tree is not supported for %s", fstype);
r = mkfs_exists(fstype);
if (r < 0)
return log_error_errno(r, "Failed to determine whether mkfs binary for %s exists: %m", fstype);
if (r == 0)
return log_error_errno(SYNTHETIC_ERRNO(EPROTONOSUPPORT), "mkfs binary for %s is not available.", fstype);
mkfs = strjoin("mkfs.", fstype);
if (!mkfs)
return log_oom();
}
r = mkfs_find_or_warn(fstype, /* have_root= */ !!root, &mkfs);
if (r < 0)
return r;
if (STR_IN_SET(fstype, "ext2", "ext3", "ext4", "xfs", "swap")) {
size_t max_len =
+1
View File
@@ -12,6 +12,7 @@ typedef enum MakeFilesystemFlags {
} MakeFileSystemFlags;
int mkfs_exists(const char *fstype);
int mkfs_find_or_warn(const char *fstype, int have_root, char **ret);
int mkfs_supports_root_option(const char *fstype);