Skip to content

Commit 9de0f57

Browse files
authored
Merge branch 'desultory:main' into patch-6
2 parents e649687 + 5ff4d6e commit 9de0f57

File tree

12 files changed

+130
-70
lines changed

12 files changed

+130
-70
lines changed

docs/configuration.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,19 +234,27 @@ Additional modules include:
234234
#### ugrd.fs.mounts
235235

236236
* `autodetect_root` (true) Set the root mount parameter based on the current root label or uuid.
237-
* `autodetect_root_dm` (true) Attempt to automatically configure virtual block devices such as LUKS/LVM/MDRAID.
238-
* `autodetect_root_luks` (true) Attempt to automatically configure LUKS mounts for the root device.
239-
* `autodetect_root_lvm` (true) Attempt to automatically configure LVM mounts for the root device.
240-
* `autodetect_root_mdraid` (true) Attempt to automatically configure MDRAID mounts for the root device.
237+
* `autodetect_dm` (true) Attempt to automatically configure virtual block devices such as LUKS/LVM/MDRAID.
238+
* `autodetect_luks` (true) Attempt to automatically configure LUKS mounts for the root device.
239+
* `autodetect_lvm` (true) Attempt to automatically configure LVM mounts for the root device.
240+
* `autodetect_mdraid` (true) Attempt to automatically configure MDRAID mounts for the root device.
241241
* `autodetect_init_mount'` (true) Automatically detect the mountpoint for the init binary, and add it to `late_mounts`.
242242
* `run_dirs` A list of directories to create under `/run/` at runtime
243243

244-
> `autodetect_root` is required for `autodetect_root_<type>` to work.
244+
> `autodetect_root` is required for `autodetect_<type>` to work.
245245
246246
`mounts`: A dictionary containing entries for mounts, with their associated config.
247247

248+
Mounts defined here are mounted before `init_main` is run. This cannot be used for mounts backed by LUKS, LVM, or MDRAID devices, because the backend will not be available when these mounts are attempted.
249+
250+
> `mounts` can be automatically populated by configuring paths as list items in `auto_mounts`.
251+
248252
`mounts.root` is predefined to have a destination of `/target_rootfs` and defines the root filesystem mount, used by `switch_root`.
249253

254+
`late_mounts`: A dictionary containing entries for mounts that should be mounted after `init_main` is run.
255+
256+
> `late_mounts` can be automatically populated by configuring paths as list items in `auto_late_mounts`.
257+
250258
Each mount has the following available parameters:
251259

252260
* `type` (auto) Mount filesystem type.
@@ -285,6 +293,8 @@ label = "extra"
285293

286294
Paths added to `auto_mounts` will be auto-configured to mount before `init_main` is run.
287295

296+
Paths added to `auto_late_mounts` will be auto-configured to mount after `init_main` is run.
297+
288298
#### ugrd.fs.fakeudev
289299

290300
This module is used to create fake udev entries for DM devices.
@@ -314,6 +324,7 @@ Importing this module will run `btrfs device scan` and pull btrfs modules.
314324
* `autodetect_root_subvol` (true) Autodetect the root subvolume, unless `root_subvol` or `subvol_selector` is set. Depends on `hostonly`.
315325
* `root_subvol` - Set the desired root subvolume.
316326
* `_base_mount_path` (/root_base) Sets where the subvolume selector mounts the base filesytem to scan for subvolumes.
327+
* `btrfs_userspace` (true) Add btrfs binary to be able to mount multi-device btrfs partitions.
317328

318329
#### ugrd.fs.resume
319330

examples/luks.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ modules = [
77
]
88

99
# Device mapper autodetection is enabled by default
10-
# autodetect_root_luks = true
10+
# autodetect_luks = true
1111

1212
# Information about the LUKS volume can be manually specified
1313
#[cryptsetup.root]

readme.md

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,19 @@ The original goal of this project was to create an initramfs suitable for decryp
2626
### Auto-detection
2727

2828
* Root mount, using `/proc/mounts`. `root=` and `rootflags=` can be used but are not required
29-
* MDRAID auto-configuration for the root mount
30-
* LUKS auto-configuration and validation for the root mount
29+
* MDRAID auto-configuration
30+
* LVM auto-configuration
31+
* LUKS auto-configuration and validation
3132
- LUKS under LVM support
3233
- LUKS under MDRAID support
33-
* LVM based root volumes are auto-mounted
34-
* BTRFS root subvolumes are automatically detected to `root_subvol`
35-
- `subvol_selector` can be used to select a subvolume at boot time
36-
* `/usr` auto-mounting if the init system requires it
37-
* Auto-detection of kernel modules required by the storage device used by the root filesystem
34+
- Detached header support
35+
- YubiKey (OpenPGP smartcard) support
36+
- Recovery using a passprhase using `try_nokey`
37+
- DM-Integrity support
38+
* BTRFS root subvolumes are automatically detected or can be manually set with `root_subvol`
39+
- `subvol_selector` can be used to interactively select a subvolume at boot time
40+
* `/usr`, `/var`, and `/etc` auto-mounting if the init system requires it
41+
* Auto-detection of kernel modules required by storage devices and filesystems
3842
* Init system/target auto-detection
3943

4044
### Validation

src/ugrd/crypto/cryptsetup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def _validate_cryptsetup_config(self, mapped_name: str) -> None:
100100
).exists(): # Make sure the header file exists, it may not be present at build time
101101
self.logger.warning("[%s] Header file not found: %s" % (mapped_name, c_(config["header_file"], "yellow")))
102102
elif not any([config.get("partuuid"), config.get("uuid"), config.get("path")]):
103-
if not self["autodetect_root_luks"]:
103+
if not self["autodetect_luks"]:
104104
raise ValidationError(
105105
"A device uuid, partuuid, or path must be specified for cryptsetup mount: %s" % mapped_name
106106
)

src/ugrd/fs/btrfs.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ def _process_subvol_selector(self, subvol_selector: bool) -> None:
7575
self["paths"] = self["_base_mount_path"]
7676

7777

78+
@contains("btrfs_userspace", message="btrfs_userspace is disabled, will not add btrfs_scan to init.")
7879
def btrfs_scan(self) -> str:
7980
"""scan for new btrfs devices."""
8081
return 'einfo "$(btrfs device scan)"'
@@ -98,6 +99,7 @@ def autodetect_root_subvol(self):
9899

99100
@contains("subvol_selector", message="subvol_selector is not enabled, skipping.")
100101
@unset("root_subvol", message="root_subvol is set, skipping.")
102+
@contains("btrfs_userspace", message="btrfs_userspace is not enabled, skipping.")
101103
def select_subvol(self) -> str:
102104
"""Returns a POSIX shell script to list subvolumes on the root volume."""
103105
return f"""
@@ -134,3 +136,8 @@ def set_root_subvol(self) -> str:
134136
"""Adds the root_subvol to the root_mount options."""
135137
_validate_root_subvol(self)
136138
return f"""setvar root_extra_options ',subvol={self["root_subvol"]}'"""
139+
140+
@contains("btrfs_userspace", message="btrfs_userspace is disabled, skipping adding btrfs to binaries list.", log_level=30)
141+
def pull_btrfs_userspace(self):
142+
self.logger.debug("Adding btrfs to binaries list.")
143+
self["binaries"] = "btrfs"

src/ugrd/fs/btrfs.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
binaries = [ "btrfs" ]
21
kmod_init = [ "btrfs", "crc32c" ] # crc32c is a softdep but more or less required
32

43
_base_mount_path = "/root_base"
54
subvol_selector = false
65
autodetect_root_subvol = true
6+
btrfs_userspace = true
77

88
[imports.config_processing]
99
"ugrd.fs.btrfs" = [ "_process_root_subvol", "_process_subvol_selector" ]
1010

1111
[imports.build_pre]
1212
"ugrd.fs.btrfs" = [ "autodetect_root_subvol" ]
1313

14+
[imports.build_late]
15+
"ugrd.fs.btrfs" = [ "pull_btrfs_userspace" ]
16+
1417
[imports.init_mount]
1518
"ugrd.fs.btrfs" = [ "btrfs_scan", "set_root_subvol", "select_subvol" ]
1619

@@ -28,3 +31,4 @@ _base_mount_path = "Path" # Set the mount point for the root filesystem when us
2831
root_subvol = "str" # Set the default btrfs subvolume for the root filesystem
2932
subvol_selector = "bool" # Select a btrfs subvolume for the root partition at runtime
3033
autodetect_root_subvol = "bool" # Automatically detect the root subvolume
34+
btrfs_userspace = "bool" # Add btrfs binary to be able to mount multi-device btrfs partitions

src/ugrd/fs/mounts.py

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__author__ = "desultory"
2-
__version__ = "7.2.1"
2+
__version__ = "7.3.0"
33

44
from pathlib import Path
55
from re import search
@@ -436,7 +436,7 @@ def get_virtual_block_info(self):
436436
sys_block = Path("/sys/devices/virtual/block")
437437

438438
if not sys_block.exists():
439-
self["autodetect_root_dm"] = False
439+
self["autodetect_dm"] = False
440440
return self.logger.warning("Virtual block devices unavailable, disabling device mapper autodetection.")
441441

442442
devices = []
@@ -450,7 +450,7 @@ def get_virtual_block_info(self):
450450
devices.append(part)
451451

452452
if not devices:
453-
self["autodetect_root_dm"] = False
453+
self["autodetect_dm"] = False
454454
return self.logger.warning("No virtual block devices found, disabling device mapper autodetection.")
455455

456456
for virt_dev in devices:
@@ -541,6 +541,17 @@ def _autodetect_dm(self, mountpoint, device=None) -> None:
541541
major, minor = _get_device_id(source_device)
542542
self.logger.debug("[%s] Major: %s, Minor: %s" % (source_device, major, minor))
543543

544+
# If the mountpoint is in auto_mounts, log a big error about it being prone to failure, allow
545+
if mountpoint in self["auto_mounts"]:
546+
self.logger.error(f"Found a device mapper mount in auto_mounts: {c_(mountpoint, 'yellow', bold=True)}")
547+
self.logger.warning(
548+
"auto_mounts is used for mounts before LVM/LUKS init (during mount_fstab). Device mapper mounts defined here may fail to activate and stop the boot process!"
549+
)
550+
if self["validate"]:
551+
raise ValidationError(
552+
f"Device mapper mount found in auto_mounts, auto_mounts cannot be device mapper based: {c_(mountpoint, 'red', bold=True)}"
553+
)
554+
544555
# Get the virtual block device name using the major/minor
545556
for name, info in self["_vblk_info"].items():
546557
if info["major"] == str(major) and info["minor"] == str(minor):
@@ -559,7 +570,9 @@ def _autodetect_dm(self, mountpoint, device=None) -> None:
559570
# If the slave source is a CRYPT-SUBDEV device, use its slave instead
560571
if self["_vblk_info"].get(slave_source, {}).get("uuid", "").startswith("CRYPT-SUBDEV"):
561572
slave_source = self["_vblk_info"][slave_source]["slaves"][0]
562-
self.logger.info(f"[{c_(dev_name, 'blue')}] Slave is a CRYPT-SUBDEV, using its slave instead: {c_(slave_source, 'cyan')}")
573+
self.logger.info(
574+
f"[{c_(dev_name, 'blue')}] Slave is a CRYPT-SUBDEV, using its slave instead: {c_(slave_source, 'cyan')}"
575+
)
563576
# Add the kmod for it
564577
self.logger.info(f"[{c_(dev_name, 'blue')}] Adding kmod for CRYPT-SUBDEV: {c_('dm-crypt', 'magenta')}")
565578
self["_kmod_auto"] = ["dm_integrity", "authenc"]
@@ -624,7 +637,7 @@ def _autodetect_dm(self, mountpoint, device=None) -> None:
624637
self.logger.debug("Slave does not appear to be a DM device: %s" % slave)
625638

626639

627-
@contains("autodetect_root_raid", "Skipping RAID autodetection, autodetect_root_raid is disabled.", log_level=30)
640+
@contains("autodetect_raid", "Skipping RAID autodetection, autodetect_raid is disabled.", log_level=30)
628641
@contains("hostonly", "Skipping RAID autodetection, hostonly mode is disabled.", log_level=30)
629642
def autodetect_raid(self, source_dev, dm_name, blkid_info) -> None:
630643
"""Autodetects MD RAID mounts and sets the raid config.
@@ -641,7 +654,7 @@ def autodetect_raid(self, source_dev, dm_name, blkid_info) -> None:
641654
raise AutodetectError("[%s] Failed to autodetect MDRAID level: %s" % (dm_name, blkid_info))
642655

643656

644-
@contains("autodetect_root_lvm", "Skipping LVM autodetection, autodetect_root_lvm is disabled.", log_level=20)
657+
@contains("autodetect_lvm", "Skipping LVM autodetection, autodetect_lvm is disabled.", log_level=20)
645658
@contains("hostonly", "Skipping LVM autodetection, hostonly mode is disabled.", log_level=30)
646659
def autodetect_lvm(self, source_dev, dm_num, blkid_info) -> None:
647660
"""Autodetects LVM mounts and sets the lvm config."""
@@ -664,7 +677,7 @@ def autodetect_lvm(self, source_dev, dm_num, blkid_info) -> None:
664677
self["lvm"] = {source_dev.name: lvm_config}
665678

666679

667-
@contains("autodetect_root_luks", "Skipping LUKS autodetection, autodetect_root_luks is disabled.", log_level=30)
680+
@contains("autodetect_luks", "Skipping LUKS autodetection, autodetect_luks is disabled.", log_level=30)
668681
@contains("hostonly", "Skipping LUKS autodetection, hostonly mode is disabled.", log_level=30)
669682
def autodetect_luks(self, source_dev, dm_num, blkid_info) -> None:
670683
"""Autodetects LUKS mounts and sets the cryptsetup config."""
@@ -747,22 +760,10 @@ def autodetect_root(self) -> None:
747760
raise AutodetectError(
748761
"Root mount not found in host mounts.\nCurrent mounts: %s" % pretty_print(self["_mounts"])
749762
)
750-
root_dev = _autodetect_mount(self, "/")
751-
if self["autodetect_root_dm"]:
752-
if self["mounts"]["root"]["type"] == "btrfs":
753-
from ugrd.fs.btrfs import _get_btrfs_mount_devices
754-
755-
# Btrfs volumes may be backed by multiple dm devices
756-
for device in _get_btrfs_mount_devices(self, "/", root_dev):
757-
_autodetect_dm(self, "/", device)
758-
elif self["mounts"]["root"]["type"] == "zfs":
759-
for device in get_zpool_info(self, root_dev)["devices"]:
760-
_autodetect_dm(self, "/", device)
761-
else:
762-
_autodetect_dm(self, "/")
763+
_autodetect_mount(self, "/")
763764

764765

765-
def _autodetect_mount(self, mountpoint, mount_class="mounts", missing_ok=False) -> str:
766+
def _autodetect_mount(self, mountpoint, mount_class="mounts", missing_ok=False) -> None:
766767
"""Sets mount config for the specified mountpoint, in the specified mount class.
767768
768769
Returns the "real" device path for the mountpoint.
@@ -815,7 +816,7 @@ def _autodetect_mount(self, mountpoint, mount_class="mounts", missing_ok=False)
815816
# Inherit mount options from the host mount for certain mount types
816817
if fs_type in MOUNT_INHERIT_OPTIONS:
817818
mount_options = self["_mounts"][mountpoint].get("options", ["ro"])
818-
if 'rw' in mount_options:
819+
if "rw" in mount_options:
819820
mount_options.pop(mount_options.index("rw")) # Remove rw option if it exists
820821
else: # For standard mounts, default ro
821822
mount_options = ["ro"]
@@ -840,8 +841,21 @@ def _autodetect_mount(self, mountpoint, mount_class="mounts", missing_ok=False)
840841
if fs_type == "zfs":
841842
mount_config[mount_name]["path"] = mount_device
842843

844+
# Run device mapper autodetection if enabled
845+
if self["autodetect_dm"]:
846+
if fs_type == "btrfs":
847+
from ugrd.fs.btrfs import _get_btrfs_mount_devices
848+
849+
# Btrfs volumes may be backed by multiple dm devices
850+
for device in _get_btrfs_mount_devices(self, mountpoint, mount_device):
851+
_autodetect_dm(self, mountpoint, mount_device)
852+
elif fs_type == "zfs":
853+
for device in get_zpool_info(self, mount_device)["devices"]:
854+
_autodetect_dm(self, mountpoint, mount_device)
855+
else:
856+
_autodetect_dm(self, mountpoint)
857+
843858
self[mount_class] = mount_config
844-
return mount_device
845859

846860

847861
@contains("auto_mounts", "Skipping auto mounts, auto_mounts is empty.", log_level=10)
@@ -852,6 +866,14 @@ def autodetect_mounts(self) -> None:
852866
_autodetect_mount(self, mountpoint)
853867

854868

869+
@contains("auto_late_mounts", "Skipping auto late mounts, auto_late_mounts is empty.", log_level=10)
870+
@contains("hostonly", "Skipping late mount autodetection, hostonly mode is disabled.", log_level=30)
871+
def autodetect_late_mounts(self) -> None:
872+
"""Configured the late_mounts config for a device based on the host mount config."""
873+
for mountpoint in self["auto_late_mounts"]:
874+
_autodetect_mount(self, mountpoint, mount_class="late_mounts")
875+
876+
855877
def mount_base(self) -> list[str]:
856878
"""Generates mount commands for the base mounts.
857879
Must be run before variables are used, as it creates the /run/ugrd directory.
@@ -906,7 +928,9 @@ def mount_fstab(self) -> list[str]:
906928
mount_retries sets the number of times to retry the mount, infinite otherwise.
907929
"""
908930
if not self._get_build_path("/etc/fstab").exists():
909-
return self.logger.info("No initramfs fstab found, skipping mount_fstab. If non-root storage devices are not needed at boot, this is fine.")
931+
return self.logger.info(
932+
"No initramfs fstab found, skipping mount_fstab. If non-root storage devices are not needed at boot, this is fine."
933+
)
910934

911935
out = [
912936
'einfo "Attempting to mount all filesystems."',
@@ -1039,8 +1063,12 @@ def export_mount_info(self) -> None:
10391063
self.logger.critical(f"Failed to get source info for the root mount: {e}")
10401064
if not self["hostonly"]:
10411065
self.logger.info("Root mount infomrmation can be defined under the '[mounts.root]' section.")
1042-
raise ValidationError("Root mount source information is not set, when hostonly mode is disabled, it must be manually defined.")
1043-
raise ValidationError("Root mount source information is not set even though hostonly mode is enabled. Please report a bug.")
1066+
raise ValidationError(
1067+
"Root mount source information is not set. When hostonly mode is disabled, it must be manually defined."
1068+
)
1069+
raise ValidationError(
1070+
"Root mount source information is not set even though hostonly mode is enabled. Please report a bug."
1071+
)
10441072
self["exports"]["MOUNTS_ROOT_TYPE"] = self["mounts"]["root"].get("type", "auto")
10451073
self["exports"]["MOUNTS_ROOT_OPTIONS"] = ",".join(self["mounts"]["root"]["options"])
10461074
self["exports"]["MOUNTS_ROOT_TARGET"] = self["mounts"]["root"]["destination"]

src/ugrd/fs/mounts.toml

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ run_dirs = [ "ugrd" ]
99

1010
mount_timeout = 1
1111
autodetect_root = true
12-
autodetect_root_dm = true
13-
autodetect_root_luks = true
14-
autodetect_root_lvm = true
15-
autodetect_root_raid = true
12+
autodetect_dm = true
13+
autodetect_luks = true
14+
autodetect_lvm = true
15+
autodetect_raid = true
1616
autodetect_init_mount = true
1717

1818
[imports.config_processing]
@@ -22,7 +22,7 @@ autodetect_init_mount = true
2222

2323
[imports.build_enum]
2424
"ugrd.fs.mounts" = [ "get_mounts_info", "get_virtual_block_info", "get_blkid_info", "get_zpool_info",
25-
"autodetect_root", "autodetect_mounts", "autodetect_init_mount" ]
25+
"autodetect_root", "autodetect_mounts", "autodetect_late_mounts", "autodetect_init_mount" ]
2626

2727
[imports.build_tasks]
2828
"ugrd.fs.mounts" = [ "export_mount_info" ]
@@ -58,14 +58,15 @@ mount_devpts = "bool" # Whether or not to mount devpts
5858
run_dirs = "NoDupFlatList" # A list of directories to be created under /run
5959
late_mounts = "dict" # Like mounts, but run after the root is mounted
6060
auto_mounts = "NoDupFlatList" # A list of mounts to be automatically added to the mounts list
61+
auto_late_mounts = "NoDupFlatList" # A list of mounts to be automatically added to the late mounts list
6162
mount_timeout = "float" # The time to wait between mount attempts
6263
mount_retries = "int" # The number of times to re-attempt mounting the fstab, infinite if not set
6364
mount_cmd = "str" # The mount command called by mount_root, can be overridden
6465
autodetect_root = "bool" # Add the autodetect_root property, if defined, the root mount will be autodetected
65-
autodetect_root_dm = "bool" # Whether or not to try to autodetect device-mapper partitions
66-
autodetect_root_luks = "bool" # Whether or not to try to autodetect LUKS partitions
67-
autodetect_root_lvm = "bool" # Whether or not to try to autodetect LVM partitions
68-
autodetect_root_raid = "bool" # Whether or not to try to autodetect MDRAID partitions
66+
autodetect_dm = "bool" # Whether or not to try to autodetect device-mapper partitions
67+
autodetect_luks = "bool" # Whether or not to try to autodetect LUKS partitions
68+
autodetect_lvm = "bool" # Whether or not to try to autodetect LVM partitions
69+
autodetect_raid = "bool" # Whether or not to try to autodetect MDRAID partitions
6970
autodetect_init_mount = "bool" # Adds a late_mount for the init target if it exists under a mount on the host
7071
no_fsck = "bool" # Whether or not to skip fsck on the root device when applicable
7172
_mounts = "dict" # The mounts information

0 commit comments

Comments
 (0)