diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml
index 69788baab4..d3724e7e9d 100644
--- a/man/systemd-gpt-auto-generator.xml
+++ b/man/systemd-gpt-auto-generator.xml
@@ -322,16 +322,20 @@
rootflags=
When root= is used with the special value
- gpt-auto, full automatic discovery of the root
- partition based on the GPT partition type is enabled. Any other value disables this
- logic.
+ gpt-auto, full automatic discovery of the root partition based on the GPT
+ partition type is enabled. Use of the root partition is delayed until factory reset mode is left, in
+ case it is enabled during the current boot. See Factory
+ Reset for more information on that. If gpt-auto-force is specified
+ automatic discovery of the root partition is enabled, ignoring any factory reset mode. Any other
+ value disables this logic.
If root= is not specified at all on the kernel command line automatic
- discovery of the root partition via the boot loader reported ESP is also enabled, however in this
- case discovery based on the loopback block device .lo_name field is not enabled.
+ discovery of the root partition via the ESP reported by the boot loader is also enabled (taking
+ factory reset state into account), however in this case discovery based on the loopback block device
+ .lo_name field is not enabled.
- The rootfstype= and rootflags= are used to select the
- file system type and options when the root file system is automatically discovered.
+ The rootfstype= and rootflags= options are used to select
+ the file system type and options when the root file system is automatically discovered.
diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c
index 6693333c4b..7e85d476e4 100644
--- a/src/fstab-generator/fstab-generator.c
+++ b/src/fstab-generator/fstab-generator.c
@@ -1119,7 +1119,7 @@ static bool validate_root_or_usr_mount_source(const char *what, const char *swit
return false;
}
- if (streq(what, "gpt-auto")) {
+ if (STR_IN_SET(what, "gpt-auto", "gpt-auto-force")) {
/* This is handled by gpt-auto-generator */
log_debug("Skipping %s directory handling, as gpt-auto was requested.", switch_name);
return false;
diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c
index 1b59fecf9e..6c2fbe1781 100644
--- a/src/gpt-auto-generator/gpt-auto-generator.c
+++ b/src/gpt-auto-generator/gpt-auto-generator.c
@@ -17,6 +17,7 @@
#include "dissect-image.h"
#include "dropin.h"
#include "efi-loader.h"
+#include "factory-reset.h"
#include "fd-util.h"
#include "fileio.h"
#include "fs-util.h"
@@ -37,9 +38,16 @@
#include "unit-name.h"
#include "virt.h"
+typedef enum GptAutoRoot {
+ GPT_AUTO_ROOT_UNSPECIFIED, /* no root= specified */
+ GPT_AUTO_ROOT_OFF, /* root= set to something else */
+ GPT_AUTO_ROOT_ON, /* root= set explicitly to "gpt-auto" */
+ GPT_AUTO_ROOT_FORCE, /* root= set explicitly to "gpt-auto-force" → ignores factory reset mode */
+} GptAutoRoot;
+
static const char *arg_dest = NULL;
static bool arg_enabled = true;
-static int arg_root_enabled = -1; /* tristate */
+static GptAutoRoot arg_auto_root = GPT_AUTO_ROOT_UNSPECIFIED;
static bool arg_swap_enabled = true;
static char *arg_root_fstype = NULL;
static char *arg_root_options = NULL;
@@ -47,7 +55,6 @@ static int arg_root_rw = -1;
static ImagePolicy *arg_image_policy = NULL;
STATIC_DESTRUCTOR_REGISTER(arg_image_policy, image_policy_freep);
-
STATIC_DESTRUCTOR_REGISTER(arg_root_fstype, freep);
STATIC_DESTRUCTOR_REGISTER(arg_root_options, freep);
@@ -661,7 +668,19 @@ static int add_root_cryptsetup(void) {
/* If a device /dev/gpt-auto-root-luks appears, then make it pull in systemd-cryptsetup-root.service, which
* sets it up, and causes /dev/gpt-auto-root to appear which is all we are looking for. */
- return add_cryptsetup("root", "/dev/gpt-auto-root-luks", arg_root_options, /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
+ const char *bdev = "/dev/gpt-auto-root-luks";
+
+ if (arg_auto_root == GPT_AUTO_ROOT_FORCE) {
+ /* Similar logic as in add_root_mount(), see below */
+ FactoryResetMode f = factory_reset_mode();
+ if (f < 0)
+ log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
+
+ if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
+ bdev = "/dev/gpt-auto-root-luks-ignore-factory-reset";
+ }
+
+ return add_cryptsetup("root", bdev, arg_root_options, /* rw= */ true, /* require= */ false, /* measure= */ true, NULL);
#else
return 0;
#endif
@@ -674,11 +693,11 @@ static int add_root_mount(void) {
int r;
/* Explicitly disabled? Then exit immediately */
- if (arg_root_enabled == 0)
+ if (arg_auto_root == GPT_AUTO_ROOT_OFF)
return 0;
/* Neither explicitly enabled nor disabled? Then decide based on the EFI partition variables to be set */
- if (arg_root_enabled < 0) {
+ if (arg_auto_root == GPT_AUTO_ROOT_UNSPECIFIED) {
if (!is_efi_boot()) {
log_debug("Not an EFI boot, not creating root mount.");
return 0;
@@ -695,10 +714,27 @@ static int add_root_mount(void) {
}
/* OK, we shall look for a root device, so let's wait for a root device to show up. A udev rule will
- * create the link for us under the right name. */
+ * create the link for us under the right name.
+ *
+ * There are two distinct names: the /dev/gpt-auto-root-ignore-factory-reset symlink is created for
+ * the root partition if factory reset mode is enabled or complete, and the /dev/gpt-auto-root
+ * symlink is only created if factory reset mode is off or already complete (thus taking factory
+ * reset state into account). In scenarios where the root disk is partially reformatted during
+ * factory reset the latter is the link to use, otherwise the former (so that we don't accidentally
+ * mount a root partition too early that is about to be wiped and replaced by another one). */
+
+ const char *bdev = "/dev/gpt-auto-root";
+ if (arg_auto_root == GPT_AUTO_ROOT_FORCE) {
+ FactoryResetMode f = factory_reset_mode();
+ if (f < 0)
+ log_warning_errno(f, "Failed to determine whether we are in factory reset mode, assuming not: %m");
+
+ if (IN_SET(f, FACTORY_RESET_ON, FACTORY_RESET_COMPLETE))
+ bdev = "/dev/gpt-auto-root-ignore-factory-reset";
+ }
if (in_initrd()) {
- r = generator_write_initrd_root_device_deps(arg_dest, "/dev/gpt-auto-root");
+ r = generator_write_initrd_root_device_deps(arg_dest, bdev);
if (r < 0)
return 0;
@@ -727,7 +763,7 @@ static int add_root_mount(void) {
return add_mount(
"root",
- "/dev/gpt-auto-root",
+ bdev,
in_initrd() ? "/sysroot" : "/",
arg_root_fstype,
/* rw= */ arg_root_rw > 0,
@@ -909,15 +945,18 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
if (proc_cmdline_value_missing(key, value))
return 0;
- /* Disable root disk logic if there's a root= value
- * specified (unless it happens to be "gpt-auto") */
+ /* Disable root disk logic if there's a root= value specified (unless it happens to be
+ * "gpt-auto" or "gpt-auto-force") */
if (streq(value, "gpt-auto")) {
- arg_root_enabled = true;
- log_debug("Enabling root partition auto-detection, root= is explicitly set to 'gpt_auto'.");
+ arg_auto_root = GPT_AUTO_ROOT_ON;
+ log_debug("Enabling root partition auto-detection (respecting factory reset mode), root= is explicitly set to 'gpt-auto'.");
+ } else if (streq(value, "gpt-auto-force")) {
+ arg_auto_root = GPT_AUTO_ROOT_FORCE;
+ log_debug("Enabling root partition auto-detection (ignoring factory reset mode), root= is explicitly set to 'gpt-auto-force'.");
} else {
- arg_root_enabled = false;
- log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto'.");
+ arg_auto_root = GPT_AUTO_ROOT_OFF;
+ log_debug("Disabling root partition auto-detection, root= is neither unset, nor set to 'gpt-auto' or 'gpt-auto-force'.");
}
} else if (streq(key, "roothash")) {
@@ -927,7 +966,7 @@ static int parse_proc_cmdline_item(const char *key, const char *value, void *dat
/* Disable root disk logic if there's roothash= defined (i.e. verity enabled) */
- arg_root_enabled = false;
+ arg_auto_root = GPT_AUTO_ROOT_OFF;
log_debug("Disabling root partition auto-detection, roothash= is set.");
} else if (streq(key, "rootfstype")) {