diff --git a/src/basic/mountpoint-util.c b/src/basic/mountpoint-util.c index 7aa14f7cd2..4db4d8162f 100644 --- a/src/basic/mountpoint-util.c +++ b/src/basic/mountpoint-util.c @@ -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 diff --git a/src/basic/mountpoint-util.h b/src/basic/mountpoint-util.h index 8cc966751c..dd27f36c2c 100644 --- a/src/basic/mountpoint-util.h +++ b/src/basic/mountpoint-util.h @@ -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); diff --git a/src/repart/repart.c b/src/repart/repart.c index 53204e94db..501f2cd1e4 100644 --- a/src/repart/repart.c +++ b/src/repart/repart.c @@ -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) diff --git a/src/shared/mkfs-util.c b/src/shared/mkfs-util.c index e45a106ed5..126f570527 100644 --- a/src/shared/mkfs-util.c +++ b/src/shared/mkfs-util.c @@ -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 = diff --git a/src/shared/mkfs-util.h b/src/shared/mkfs-util.h index 7ab78be98a..b23f0cf745 100644 --- a/src/shared/mkfs-util.h +++ b/src/shared/mkfs-util.h @@ -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);