-
Notifications
You must be signed in to change notification settings - Fork 159
install: add target_root_path for RootSetup
#1752
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
f84122b
1a919d9
124370c
65b5493
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -769,6 +769,17 @@ async fn initialize_ostree_root(state: &State, root_setup: &RootSetup) -> Result | |
| // Another implementation: https://github.com/coreos/coreos-assembler/blob/3cd3307904593b3a131b81567b13a4d0b6fe7c90/src/create_disk.sh#L295 | ||
| crate::lsm::ensure_dir_labeled(rootfs_dir, "", Some("/".into()), 0o755.into(), sepolicy)?; | ||
|
|
||
| // If we're installing alongside existing ostree and there's a separate boot partition, | ||
| // we need to mount it to the sysroot's /boot so ostree can write bootloader entries there | ||
| if has_ostree && root_setup.boot.is_some() { | ||
| if let Some(boot) = &root_setup.boot { | ||
| let source_boot = &boot.source; | ||
| let target_boot = root_setup.physical_root_path.join(BOOT); | ||
| tracing::debug!("Mount {source_boot} to {target_boot} on ostree"); | ||
| bootc_mount::mount(source_boot, &target_boot)?; | ||
| } | ||
| } | ||
|
|
||
| // And also label /boot AKA xbootldr, if it exists | ||
| if rootfs_dir.try_exists("boot")? { | ||
| crate::lsm::ensure_dir_labeled(rootfs_dir, "boot", None, 0o755.into(), sepolicy)?; | ||
|
|
@@ -1095,6 +1106,8 @@ pub(crate) struct RootSetup { | |
| pub(crate) physical_root_path: Utf8PathBuf, | ||
| /// Directory file descriptor for the above physical root. | ||
| pub(crate) physical_root: Dir, | ||
| /// Target root path /target. | ||
| pub(crate) target_root_path: Option<Utf8PathBuf>, | ||
| pub(crate) rootfs_uuid: Option<String>, | ||
| /// True if we should skip finalizing | ||
| skip_finalize: bool, | ||
|
|
@@ -1549,7 +1562,10 @@ async fn install_with_sysroot( | |
| Bootloader::Grub => { | ||
| crate::bootloader::install_via_bootupd( | ||
| &rootfs.device_info, | ||
| &rootfs.physical_root_path, | ||
| &rootfs | ||
| .target_root_path | ||
| .clone() | ||
| .unwrap_or(rootfs.physical_root_path.clone()), | ||
| &state.config_opts, | ||
| Some(&deployment_path.as_str()), | ||
| )?; | ||
|
|
@@ -1896,30 +1912,61 @@ fn remove_all_in_dir_no_xdev(d: &Dir, mount_err: bool) -> Result<()> { | |
| anyhow::Ok(()) | ||
| } | ||
|
|
||
| #[context("Removing boot directory content except loader dir on ostree")] | ||
| fn remove_all_except_loader_dirs(bootdir: &Dir, is_ostree: bool) -> Result<()> { | ||
| let entries = bootdir | ||
| .entries() | ||
| .context("Reading boot directory entries")?; | ||
|
|
||
| for entry in entries { | ||
| let entry = entry.context("Reading directory entry")?; | ||
| let file_name = entry.file_name(); | ||
| let file_name = if let Some(n) = file_name.to_str() { | ||
| n | ||
| } else { | ||
| anyhow::bail!("Invalid non-UTF8 filename: {file_name:?} in /boot"); | ||
| }; | ||
|
|
||
| // TODO: Preserve basically everything (including the bootloader entries | ||
| // on non-ostree) by default until the very end of the install. And ideally | ||
| // make the "commit" phase an optional step after. | ||
| if is_ostree && file_name.starts_with("loader") { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This also relates to an issue (TODO link) where we should support preserving basically everything (including the bootloader entries on non-ostree) by default until the very end of the install. And ideally make the "commit" phase an optional step after. This one also textually conflicts with #1727
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think you meant preserve everything under /boot & /boot/efi, then clean them before install bootloader, is this right?
A little confused, this requires empty rootfs when |
||
| continue; | ||
| } | ||
|
|
||
| let etype = entry.file_type()?; | ||
| if etype == FileType::dir() { | ||
| // Open the directory and remove its contents | ||
| if let Some(subdir) = bootdir.open_dir_noxdev(&file_name)? { | ||
| remove_all_in_dir_no_xdev(&subdir, false) | ||
| .with_context(|| format!("Removing directory contents: {}", file_name))?; | ||
| bootdir.remove_dir(&file_name)?; | ||
| } | ||
| } else { | ||
| bootdir | ||
| .remove_file_optional(&file_name) | ||
| .with_context(|| format!("Removing file: {}", file_name))?; | ||
| } | ||
| } | ||
| Ok(()) | ||
| } | ||
|
|
||
| #[context("Removing boot directory content")] | ||
| fn clean_boot_directories(rootfs: &Dir, is_ostree: bool) -> Result<()> { | ||
| let bootdir = | ||
| crate::utils::open_dir_remount_rw(rootfs, BOOT.into()).context("Opening /boot")?; | ||
|
|
||
| if is_ostree { | ||
| // On ostree systems, the boot directory already has our desired format, we should only | ||
| // remove the bootupd-state.json file to avoid bootupctl complaining it already exists. | ||
| bootdir | ||
| .remove_file_optional("bootupd-state.json") | ||
| .context("removing bootupd-state.json")?; | ||
| } else { | ||
| // This should not remove /boot/efi note. | ||
| remove_all_in_dir_no_xdev(&bootdir, false).context("Emptying /boot")?; | ||
| // TODO: Discover the ESP the same way bootupd does it; we should also | ||
| // support not wiping the ESP. | ||
| if ARCH_USES_EFI { | ||
| if let Some(efidir) = bootdir | ||
| .open_dir_optional(crate::bootloader::EFI_DIR) | ||
| .context("Opening /boot/efi")? | ||
| { | ||
| remove_all_in_dir_no_xdev(&efidir, false) | ||
| .context("Emptying EFI system partition")?; | ||
| } | ||
| // This should not remove /boot/efi note. | ||
| remove_all_except_loader_dirs(&bootdir, is_ostree).context("Emptying /boot")?; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm looks like this is going to conflict with #1727 |
||
|
|
||
| // TODO: Discover the ESP the same way bootupd does it; we should also | ||
| // support not wiping the ESP. | ||
| if ARCH_USES_EFI { | ||
| if let Some(efidir) = bootdir | ||
| .open_dir_optional(crate::bootloader::EFI_DIR) | ||
| .context("Opening /boot/efi")? | ||
| { | ||
| remove_all_in_dir_no_xdev(&efidir, false).context("Emptying EFI system partition")?; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -2056,6 +2103,18 @@ pub(crate) async fn install_to_filesystem( | |
| .context("Mounting host / to {ALONGSIDE_ROOT_MOUNT}")?; | ||
| } | ||
|
|
||
| let target_root_path = fsopts.root_path.clone(); | ||
| // Get a file descriptor for the root path /target | ||
| let target_rootfs_fd = | ||
| Dir::open_ambient_dir(&target_root_path, cap_std::ambient_authority()) | ||
| .with_context(|| format!("Opening target root directory {target_root_path}"))?; | ||
|
|
||
| tracing::debug!("Target root filesystem: {target_root_path}"); | ||
|
|
||
| if let Some(false) = target_rootfs_fd.is_mountpoint(".")? { | ||
| anyhow::bail!("Not a mountpoint: {target_root_path}"); | ||
| } | ||
|
|
||
| // Check that the target is a directory | ||
| { | ||
| let root_path = &fsopts.root_path; | ||
|
|
@@ -2069,10 +2128,7 @@ pub(crate) async fn install_to_filesystem( | |
|
|
||
| // Check to see if this happens to be the real host root | ||
| if !fsopts.acknowledge_destructive { | ||
| let root_path = &fsopts.root_path; | ||
| let rootfs_fd = Dir::open_ambient_dir(root_path, cap_std::ambient_authority()) | ||
| .with_context(|| format!("Opening target root directory {root_path}"))?; | ||
| warn_on_host_root(&rootfs_fd)?; | ||
| warn_on_host_root(&target_rootfs_fd)?; | ||
| } | ||
|
|
||
| // If we're installing to an ostree root, then find the physical root from | ||
|
|
@@ -2088,7 +2144,8 @@ pub(crate) async fn install_to_filesystem( | |
| }; | ||
|
|
||
| // Get a file descriptor for the root path | ||
| let rootfs_fd = { | ||
| // It will be /target/sysroot on ostree OS, or will be /target | ||
| let rootfs_fd = if is_already_ostree { | ||
| let root_path = &fsopts.root_path; | ||
| let rootfs_fd = Dir::open_ambient_dir(&fsopts.root_path, cap_std::ambient_authority()) | ||
| .with_context(|| format!("Opening target root directory {root_path}"))?; | ||
|
|
@@ -2099,6 +2156,8 @@ pub(crate) async fn install_to_filesystem( | |
| anyhow::bail!("Not a mountpoint: {root_path}"); | ||
| } | ||
| rootfs_fd | ||
| } else { | ||
| target_rootfs_fd.try_clone()? | ||
| }; | ||
|
|
||
| match fsopts.replace { | ||
|
|
@@ -2108,7 +2167,18 @@ pub(crate) async fn install_to_filesystem( | |
| tokio::task::spawn_blocking(move || remove_all_in_dir_no_xdev(&rootfs_fd, true)) | ||
| .await??; | ||
| } | ||
| Some(ReplaceMode::Alongside) => clean_boot_directories(&rootfs_fd, is_already_ostree)?, | ||
| Some(ReplaceMode::Alongside) => { | ||
| // On existing ostree OS like FCOS, esp is not mounted after booted, | ||
| // need to find esp and mount before clean | ||
| if ARCH_USES_EFI { | ||
| crate::bootloader::mount_esp_part( | ||
| &target_rootfs_fd, | ||
| &target_root_path, | ||
| &rootfs_fd, | ||
| )?; | ||
| } | ||
| clean_boot_directories(&target_rootfs_fd, is_already_ostree)? | ||
| } | ||
| None => require_empty_rootdir(&rootfs_fd)?, | ||
| } | ||
|
|
||
|
|
@@ -2153,7 +2223,7 @@ pub(crate) async fn install_to_filesystem( | |
|
|
||
| let boot_is_mount = { | ||
| let root_dev = rootfs_fd.dir_metadata()?.dev(); | ||
| let boot_dev = rootfs_fd | ||
| let boot_dev = target_rootfs_fd | ||
| .symlink_metadata_optional(BOOT)? | ||
| .ok_or_else(|| { | ||
| anyhow!("No /{BOOT} directory found in root; this is is currently required") | ||
|
|
@@ -2164,9 +2234,10 @@ pub(crate) async fn install_to_filesystem( | |
| }; | ||
| // Find the UUID of /boot because we need it for GRUB. | ||
| let boot_uuid = if boot_is_mount { | ||
| let boot_path = fsopts.root_path.join(BOOT); | ||
| let boot_path = target_root_path.join(BOOT); | ||
| tracing::debug!("boot_path={boot_path}"); | ||
| let u = bootc_mount::inspect_filesystem(&boot_path) | ||
| .context("Inspecting /{BOOT}")? | ||
| .with_context(|| format!("Inspecting /{BOOT}"))? | ||
| .uuid | ||
| .ok_or_else(|| anyhow!("No UUID found for /{BOOT}"))?; | ||
| Some(u) | ||
|
|
@@ -2247,6 +2318,7 @@ pub(crate) async fn install_to_filesystem( | |
| device_info, | ||
| physical_root_path: fsopts.root_path, | ||
| physical_root: rootfs_fd, | ||
| target_root_path: Some(target_root_path.clone()), | ||
| rootfs_uuid: inspect.uuid.clone(), | ||
| boot, | ||
| kargs, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing I don't quite understand is why other cases don't hit this. I feel like there might be a different bug somewhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On ostree env, the bootloader entries are written under
/sysroot/boot, this is why mount/bootto/sysroot/boot.