diff --git a/man/repart.d.xml b/man/repart.d.xml index a79724a93ea..7e4fe93cfcb 100644 --- a/man/repart.d.xml +++ b/man/repart.d.xml @@ -373,10 +373,7 @@ data is never overwritten. Note that the data is copied in before the partition table is updated, i.e. before the partition actually is persistently created. This provides robustness: it is guaranteed that the partition either doesn't exist or exists fully populated; it is not possible that - the partition exists but is not or only partially populated. - - This option cannot be combined with Format= or - CopyFiles=. + the partition exists but is not or only partially populated. @@ -395,9 +392,7 @@ Similarly to the behaviour of CopyBlocks=, the file system is formatted before the partition is created, ensuring that the partition only ever exists with a fully - initialized file system. - - This option cannot be combined with CopyBlocks=. + initialized file system. @@ -439,7 +434,10 @@ mkfs.xfs8 due to limitations of its protofile format. - This option cannot be combined with CopyBlocks=. + When this option is used in combination with CopyBlocks=, + systemd-repart will first try the CopyBlocks= logic and will + only fall back to the CopyFiles= logic if the CopyBlocks= logic + cannot be used. When systemd-repart8 @@ -569,6 +567,15 @@ into their original state by removing partitions and creating them anew. Defaults to off. + + OEM= + + Takes a boolean argument. If specified the partition is marked as an OEM partition. + When the is used, only OEM partitions are written to the partition table. + Unless configured explicitly with OEM=, a partition is an OEM partition if + FactoryReset=no. + + Flags= diff --git a/man/systemd-repart.xml b/man/systemd-repart.xml index 1799961527d..39912052a4b 100644 --- a/man/systemd-repart.xml +++ b/man/systemd-repart.xml @@ -440,6 +440,14 @@ due to missing permissions. + + BOOL + + Instructs systemd-repart to only include OEM partitions into the + image. Takes a boolean and is off by default. OEM partitions can be configured using the + OEM= setting. + + diff --git a/src/partition/repart.c b/src/partition/repart.c index 6c39ee804c2..9ae8ed4c117 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -24,6 +24,7 @@ #include "conf-files.h" #include "conf-parser.h" #include "constants.h" +#include "creds-util.h" #include "cryptsetup-util.h" #include "device-util.h" #include "devnum-util.h" @@ -153,6 +154,7 @@ static uint64_t arg_sector_size = 0; static ImagePolicy *arg_image_policy = NULL; static Architecture arg_architecture = _ARCHITECTURE_INVALID; static int arg_offline = -1; +static bool arg_oem = false; STATIC_DESTRUCTOR_REGISTER(arg_root, freep); STATIC_DESTRUCTOR_REGISTER(arg_image, freep); @@ -205,6 +207,7 @@ typedef struct Partition { bool dropped; bool factory_reset; + int oem; int32_t priority; uint32_t weight, padding_weight; @@ -350,6 +353,7 @@ static Partition *partition_new(void) { .no_auto = -1, .read_only = -1, .growfs = -1, + .oem = -1, }; return p; @@ -423,9 +427,16 @@ static void partition_foreignize(Partition *p) { p->verity = VERITY_OFF; } +static bool partition_is_oem(const Partition *p) { + return p->oem > 0 || (p->oem < 0 && !p->factory_reset); +} + static bool partition_exclude(const Partition *p) { assert(p); + if (arg_oem && !partition_is_oem(p)) + return true; + if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE) return false; @@ -1631,6 +1642,7 @@ static int partition_read_definition(Partition *p, const char *path, const char { "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs }, { "Partition", "SplitName", config_parse_string, 0, &p->split_name_format }, { "Partition", "Minimize", config_parse_minimize, 0, &p->minimize }, + { "Partition", "OEM", config_parse_tristate, 0, &p->oem }, {} }; int r; @@ -1672,11 +1684,6 @@ static int partition_read_definition(Partition *p, const char *path, const char return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), "Type= not defined, refusing."); - if ((p->copy_blocks_path || p->copy_blocks_auto) && - (p->format || !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories))) - return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), - "Format=/CopyFiles=/MakeDirectories= and CopyBlocks= cannot be combined, refusing."); - if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && streq_ptr(p->format, "swap")) return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL), "Format=swap and CopyFiles= cannot be combined, refusing."); @@ -5387,19 +5394,6 @@ static int resolve_copy_blocks_auto( dev_t devno, found = 0; int r; - /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the - * --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all, - * and restrict block device references in the --image= case to loopback block device we set up. - * - * restrict_devno contain the dev_t of the loop back device we operate on in case of --image=, and - * thus declares which device (and its partition subdevices) we shall limit access to. If - * restrict_devno is zero no device probing access shall be allowed at all (used for --root=) and if - * it is (dev_t) -1 then free access shall be allowed (if neither switch is used). */ - - if (restrict_devno == 0) - return log_error_errno(SYNTHETIC_ERRNO(EPERM), - "Automatic discovery of backing block devices not permitted in --root= mode, refusing."); - /* Handles CopyBlocks=auto, and finds the right source partition to copy from. We look for matching * partitions in the host, using the appropriate directory as key and ensuring that the partition * type matches. */ @@ -5492,17 +5486,13 @@ static int resolve_copy_blocks_auto( found = devno; } - if (found == 0) - return log_error_errno(SYNTHETIC_ERRNO(ENXIO), - "Unable to automatically discover suitable partition to copy blocks from."); - if (ret_devno) *ret_devno = found; if (ret_uuid) *ret_uuid = found_uuid; - return 0; + return found != 0; } static int context_open_copy_block_paths( @@ -5544,9 +5534,35 @@ static int context_open_copy_block_paths( } else if (p->copy_blocks_auto) { dev_t devno = 0; /* Fake initialization to appease gcc. */ + /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get + * outside of the --root=/--image= confinement. Specifically, refuse CopyBlocks= in + * combination with --root= at all, and restrict block device references in the + * --image= case to loopback block device we set up. + * + * restrict_devno contain the dev_t of the loop back device we operate on in case of + * --image=, and thus declares which device (and its partition subdevices) we shall + * limit access to. If restrict_devno is zero no device probing access shall be + * allowed at all (used for --root=) and if it is (dev_t) -1 then free access shall + * be allowed (if neither switch is used). */ + + if (restrict_devno == 0) { + if (!p->format && strv_isempty(p->copy_files) && strv_isempty(p->make_directories)) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), + "Automatic discovery of backing block devices not permitted in --root= mode, refusing."); + + continue; + } + r = resolve_copy_blocks_auto(p->type, p->copy_blocks_root, restrict_devno, &devno, &uuid); if (r < 0) return r; + if (r == 0) { + if (!p->format && strv_isempty(p->copy_files) && strv_isempty(p->make_directories)) + return log_error_errno(SYNTHETIC_ERRNO(ENXIO), + "Unable to automatically discover suitable partition to copy blocks from."); + + continue; + } assert(devno != 0); source_fd = r = device_open_from_devnum(S_IFBLK, devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened); @@ -5684,6 +5700,9 @@ static int context_minimize(Context *context) { if (!p->format) continue; + if (p->copy_blocks_fd >= 0) + continue; + if (p->minimize == MINIMIZE_OFF) continue; @@ -5760,7 +5779,13 @@ static int context_minimize(Context *context) { if (fstype_is_ro(p->format)) { struct stat st; - if (stat(temp, &st) < 0) + assert(fd < 0); + + fd = open(temp, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return log_error_errno(errno, "Failed to open temporary file %s: %m", temp); + + if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to stat temporary file: %m"); log_info("Minimal partition size of %s filesystem of partition %s is %s", @@ -5768,6 +5793,8 @@ static int context_minimize(Context *context) { p->copy_blocks_path = TAKE_PTR(temp); p->copy_blocks_path_is_our_file = true; + p->copy_blocks_fd = TAKE_FD(fd); + p->copy_blocks_size = st.st_size; continue; } @@ -5830,8 +5857,12 @@ static int context_minimize(Context *context) { return r; } + assert(fd >= 0); + p->copy_blocks_path = TAKE_PTR(temp); p->copy_blocks_path_is_our_file = true; + p->copy_blocks_fd = TAKE_FD(fd); + p->copy_blocks_size = fsz; } /* Now that we've done the data partitions, do the verity hash partitions. We do these in a separate @@ -5840,6 +5871,7 @@ static int context_minimize(Context *context) { LIST_FOREACH(partitions, p, context->partitions) { _cleanup_(unlink_and_freep) char *temp = NULL; _cleanup_free_ char *hint = NULL; + _cleanup_close_ int fd = -EBADF; struct stat st; Partition *dp; @@ -5882,7 +5914,11 @@ static int context_minimize(Context *context) { if (r < 0) return r; - if (stat(temp, &st) < 0) + fd = open(temp, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) + return log_error_errno(errno, "Failed to open temporary file %s: %m", temp); + + if (fstat(fd, &st) < 0) return log_error_errno(r, "Failed to stat temporary file: %m"); log_info("Minimal partition size of verity hash partition %s is %s", @@ -5890,6 +5926,8 @@ static int context_minimize(Context *context) { p->copy_blocks_path = TAKE_PTR(temp); p->copy_blocks_path_is_our_file = true; + p->copy_blocks_fd = TAKE_FD(fd); + p->copy_blocks_size = st.st_size; } return 0; @@ -5978,6 +6016,7 @@ static int help(void) { " --sector-size=SIZE Set the logical sector size for the image\n" " --architecture=ARCH Set the generic architecture for the image\n" " --offline=BOOL Whether to build the image offline\n" + " --oem=BOOL Whether to only include OEM partitions\n" "\nSee the %s for details.\n", program_invocation_short_name, ansi_highlight(), @@ -5987,6 +6026,17 @@ static int help(void) { return 0; } +static int parse_credentials(void) { + int r; + + r = read_credential_bool("repart.oem"); + if (r < 0) + return log_error_errno(r, "Failed to read repart.oem credential: %m"); + arg_oem = r; + + return 0; +} + static int parse_argv(int argc, char *argv[]) { enum { @@ -6021,6 +6071,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_SKIP_PARTITIONS, ARG_ARCHITECTURE, ARG_OFFLINE, + ARG_OEM, }; static const struct option options[] = { @@ -6055,6 +6106,7 @@ static int parse_argv(int argc, char *argv[]) { { "sector-size", required_argument, NULL, ARG_SECTOR_SIZE }, { "architecture", required_argument, NULL, ARG_ARCHITECTURE }, { "offline", required_argument, NULL, ARG_OFFLINE }, + { "oem", required_argument, NULL, ARG_OEM }, {} }; @@ -6376,6 +6428,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_OEM: + r = parse_boolean_argument("--oem=", optarg, &arg_oem); + if (r < 0) + return r; + + break; + case '?': return -EINVAL; @@ -6872,6 +6931,10 @@ static int run(int argc, char *argv[]) { log_parse_environment(); log_open(); + r = parse_credentials(); + if (r < 0) + return r; + r = parse_argv(argc, argv); if (r <= 0) return r; @@ -7004,12 +7067,6 @@ static int run(int argc, char *argv[]) { if (r < 0) return r; - /* We might have gotten more copy blocks paths to open during the minimize process, so let's make - * sure we open those as well. These should all be regular files, so don't allow any block devices. */ - r = context_open_copy_block_paths(context, 0); - if (r < 0) - return r; - if (arg_size_auto) { r = determine_auto_size(context); if (r < 0) diff --git a/src/shared/creds-util.c b/src/shared/creds-util.c index 573900f8704..16df01b9ca8 100644 --- a/src/shared/creds-util.c +++ b/src/shared/creds-util.c @@ -22,6 +22,7 @@ #include "memory-util.h" #include "mkdir.h" #include "openssl-util.h" +#include "parse-util.h" #include "path-util.h" #include "random-util.h" #include "sparse-endian.h" @@ -253,6 +254,17 @@ int read_credential_strings_many_internal( return ret; } +int read_credential_bool(const char *name) { + _cleanup_free_ void *data = NULL; + int r; + + r = read_credential(name, &data, NULL); + if (r < 0) + return IN_SET(r, -ENXIO, -ENOENT) ? 0 : r; + + return parse_boolean(data); +} + int get_credential_user_password(const char *username, char **ret_password, bool *ret_is_hashed) { _cleanup_(erase_and_freep) char *creds_password = NULL; _cleanup_free_ char *cn = NULL; diff --git a/src/shared/creds-util.h b/src/shared/creds-util.h index 8fbd61e9fe6..5e39a6a022f 100644 --- a/src/shared/creds-util.h +++ b/src/shared/creds-util.h @@ -43,6 +43,8 @@ int read_credential_strings_many_internal(const char *first_name, char **first_v #define read_credential_strings_many(first_name, first_value, ...) \ read_credential_strings_many_internal(first_name, first_value, __VA_ARGS__, NULL) +int read_credential_bool(const char *name); + typedef enum CredentialSecretFlags { CREDENTIAL_SECRET_GENERATE = 1 << 0, CREDENTIAL_SECRET_WARN_NOT_ENCRYPTED = 1 << 1, diff --git a/test/units/testsuite-58.sh b/test/units/testsuite-58.sh index 13e40bd82ab..f90c3b1b4ef 100755 --- a/test/units/testsuite-58.sh +++ b/test/units/testsuite-58.sh @@ -132,6 +132,9 @@ EOF Type=home Label=home-first Label=home-always-too-long-xxxxxxxxxxxxxx-%v +# Test that OEM=yes makes sure that a partition is OEM even if FactoryReset=yes is set. +FactoryReset=yes +OEM=yes EOF tee "$defs/swap.conf" <