mirror of
https://github.com/systemd/systemd.git
synced 2026-08-05 07:30:30 +00:00
Merge pull request #28556 from DaanDeMeyer/repart
repart: Allow combining CopyBlocks= and CopyFiles=
This commit is contained in:
@@ -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.</para>
|
||||
|
||||
<para>This option cannot be combined with <varname>Format=</varname> or
|
||||
<varname>CopyFiles=</varname>.</para></listitem>
|
||||
the partition exists but is not or only partially populated.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@@ -395,9 +392,7 @@
|
||||
|
||||
<para>Similarly to the behaviour of <varname>CopyBlocks=</varname>, the file system is formatted
|
||||
before the partition is created, ensuring that the partition only ever exists with a fully
|
||||
initialized file system.</para>
|
||||
|
||||
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para></listitem>
|
||||
initialized file system.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
@@ -439,7 +434,10 @@
|
||||
<citerefentry project='man-pages'><refentrytitle>mkfs.xfs</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
due to limitations of its protofile format.</para>
|
||||
|
||||
<para>This option cannot be combined with <varname>CopyBlocks=</varname>.</para>
|
||||
<para>When this option is used in combination with <varname>CopyBlocks=</varname>,
|
||||
<command>systemd-repart</command> will first try the <varname>CopyBlocks=</varname> logic and will
|
||||
only fall back to the <varname>CopyFiles=</varname> logic if the <varname>CopyBlocks=</varname> logic
|
||||
cannot be used.</para>
|
||||
|
||||
<para>When
|
||||
<citerefentry><refentrytitle>systemd-repart</refentrytitle><manvolnum>8</manvolnum></citerefentry>
|
||||
@@ -569,6 +567,15 @@
|
||||
into their original state by removing partitions and creating them anew. Defaults to off.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>OEM=</varname></term>
|
||||
|
||||
<listitem><para>Takes a boolean argument. If specified the partition is marked as an OEM partition.
|
||||
When the <option>--oem</option> is used, only OEM partitions are written to the partition table.
|
||||
Unless configured explicitly with <varname>OEM=</varname>, a partition is an OEM partition if
|
||||
<varname>FactoryReset=no</varname>.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><varname>Flags=</varname></term>
|
||||
|
||||
|
||||
@@ -440,6 +440,14 @@
|
||||
due to missing permissions.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--oem=</option><arg>BOOL</arg></term>
|
||||
|
||||
<listitem><para>Instructs <command>systemd-repart</command> to only include OEM partitions into the
|
||||
image. Takes a boolean and is off by default. OEM partitions can be configured using the
|
||||
<varname>OEM=</varname> setting.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="standard-options.xml" xpointer="help" />
|
||||
<xi:include href="standard-options.xml" xpointer="version" />
|
||||
<xi:include href="standard-options.xml" xpointer="no-pager" />
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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" <<EOF
|
||||
@@ -139,6 +142,8 @@ EOF
|
||||
Type=swap
|
||||
SizeMaxBytes=64M
|
||||
PaddingMinBytes=92M
|
||||
# No OEM=yes here so this should not be considered an OEM partition since FactoryReset=yes.
|
||||
FactoryReset=yes
|
||||
EOF
|
||||
|
||||
systemd-repart --offline="$OFFLINE" \
|
||||
@@ -160,6 +165,26 @@ last-lba: 2097118
|
||||
$imgs/zzz1 : start= 2048, size= 1775576, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\"
|
||||
$imgs/zzz2 : start= 1777624, size= 131072, type=0657FD6D-A4AB-43C4-84E5-0933C84B4F4F, uuid=78C92DB8-3D2B-4823-B0DC-792B78F66F1E, name=\"swap\""
|
||||
|
||||
systemd-repart --offline="$OFFLINE" \
|
||||
--definitions="$defs" \
|
||||
--dry-run=no \
|
||||
--seed="$seed" \
|
||||
--empty=force \
|
||||
--include-partitions=home,swap \
|
||||
--oem=yes \
|
||||
--offline="$OFFLINE" \
|
||||
"$imgs/zzz"
|
||||
|
||||
output=$(sfdisk -d "$imgs/zzz" | grep -v -e 'sector-size' -e '^$')
|
||||
|
||||
assert_eq "$output" "label: gpt
|
||||
label-id: 1D2CE291-7CCE-4F7D-BC83-FDB49AD74EBD
|
||||
device: $imgs/zzz
|
||||
unit: sectors
|
||||
first-lba: 2048
|
||||
last-lba: 2097118
|
||||
$imgs/zzz1 : start= 2048, size= 2095064, type=933AC7E1-2EB4-4F13-B844-0E14E2AEF915, uuid=4980595D-D74A-483A-AA9E-9903879A0EE5, name=\"home-first\", attrs=\"GUID:59\""
|
||||
|
||||
systemd-repart --offline="$OFFLINE" \
|
||||
--definitions="$defs" \
|
||||
--dry-run=no \
|
||||
@@ -268,6 +293,9 @@ Type=linux-generic
|
||||
Label=block-copy
|
||||
UUID=2a1d97e1d0a346cca26eadc643926617
|
||||
CopyBlocks=$imgs/block-copy
|
||||
# Test that these are ignored
|
||||
CopyFiles=abc
|
||||
Format=ext4
|
||||
EOF
|
||||
|
||||
systemd-repart --offline="$OFFLINE" \
|
||||
@@ -903,6 +931,8 @@ CopyFiles=/
|
||||
CopyFiles=/zzz:/
|
||||
CopyFiles=/:/oiu
|
||||
ExcludeFilesTarget=/oiu/usr
|
||||
# Test that this is just ignored
|
||||
CopyBlocks=auto
|
||||
EOF
|
||||
|
||||
tee "$defs/10-usr.conf" <<EOF
|
||||
|
||||
@@ -30,6 +30,7 @@ Before=shutdown.target initrd-switch-root.target
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart={{BINDIR}}/systemd-repart --dry-run=no
|
||||
ImportCredential=repart.*
|
||||
|
||||
# The tool returns 77 if there's no existing GPT partition table
|
||||
SuccessExitStatus=77
|
||||
|
||||
Reference in New Issue
Block a user