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" <