Make repart more user-friendly (#42345)
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user