|
2 | 2 | //!
|
3 | 3 | //! Create a merged filesystem tree with the image and mounted configmaps.
|
4 | 4 |
|
| 5 | +use std::io::{BufRead, Write}; |
| 6 | + |
5 | 7 | use anyhow::Ok;
|
6 | 8 | use anyhow::{Context, Result};
|
7 | 9 |
|
@@ -377,3 +379,121 @@ fn test_switch_inplace() -> Result<()> {
|
377 | 379 | assert_eq!(replaced, target_deployment);
|
378 | 380 | Ok(())
|
379 | 381 | }
|
| 382 | + |
| 383 | +/// A workaround for https://github.com/ostreedev/ostree/issues/3193 |
| 384 | +/// as generated by anaconda. |
| 385 | +#[context("Updating /etc/fstab for anaconda+composefs")] |
| 386 | +pub(crate) fn fixup_etc_fstab(root: &Dir) -> Result<()> { |
| 387 | + let fstab_path = "etc/fstab"; |
| 388 | + // Read the old file |
| 389 | + let fd = root |
| 390 | + .open(fstab_path) |
| 391 | + .with_context(|| format!("Opening {fstab_path}")) |
| 392 | + .map(std::io::BufReader::new)?; |
| 393 | + |
| 394 | + // Helper function to possibly change a line from /etc/fstab. |
| 395 | + // Returns Ok(true) if we made a change (and we wrote the modified line) |
| 396 | + // otherwise returns Ok(false) and the caller should write the original line. |
| 397 | + fn edit_fstab_line(line: &str, mut w: impl Write) -> Result<bool> { |
| 398 | + if line.starts_with("#") { |
| 399 | + return Ok(false); |
| 400 | + } |
| 401 | + let parts = line.split_ascii_whitespace().collect::<Vec<_>>(); |
| 402 | + |
| 403 | + let path = parts.get(1); |
| 404 | + let options = parts.get(3); |
| 405 | + if let (Some(&path), Some(&options)) = (path, options) { |
| 406 | + let options = options.split(',').collect::<Vec<_>>(); |
| 407 | + if options.iter().any(|&s| s == "ro") { |
| 408 | + return Ok(false); |
| 409 | + } |
| 410 | + if path != "/" { |
| 411 | + return Ok(false); |
| 412 | + } |
| 413 | + } else { |
| 414 | + tracing::debug!("No path in entry: {line}"); |
| 415 | + return Ok(false); |
| 416 | + }; |
| 417 | + |
| 418 | + // SAFETY: we unpacked the options before. |
| 419 | + // This adds `ro` to the option list |
| 420 | + let options = format!("{},ro", options.unwrap()); |
| 421 | + for (i, part) in parts.into_iter().enumerate() { |
| 422 | + if i > 0 { |
| 423 | + write!(w, " ")?; |
| 424 | + } |
| 425 | + if i == 3 { |
| 426 | + write!(w, "{options}")?; |
| 427 | + } else { |
| 428 | + write!(w, "{part}")? |
| 429 | + } |
| 430 | + } |
| 431 | + writeln!(w)?; |
| 432 | + Ok(true) |
| 433 | + } |
| 434 | + |
| 435 | + // Read the input, and atomically write a modified version |
| 436 | + root.atomic_replace_with(fstab_path, move |mut w| { |
| 437 | + for line in fd.lines() { |
| 438 | + let line = line?; |
| 439 | + if !edit_fstab_line(&line, &mut w)? { |
| 440 | + writeln!(w, "{line}")?; |
| 441 | + } |
| 442 | + } |
| 443 | + Ok(()) |
| 444 | + }) |
| 445 | + .context("Replacing /etc/fstab") |
| 446 | +} |
| 447 | + |
| 448 | +#[test] |
| 449 | +fn test_fixup_etc_fstab_default() -> Result<()> { |
| 450 | + let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?; |
| 451 | + let default = "UUID=f7436547-20ac-43cb-aa2f-eac9632183f6 /boot auto ro 0 0\n"; |
| 452 | + tempdir.create_dir_all("etc")?; |
| 453 | + tempdir.atomic_write("etc/fstab", default)?; |
| 454 | + fixup_etc_fstab(&tempdir).unwrap(); |
| 455 | + assert_eq!(tempdir.read_to_string("etc/fstab")?, default); |
| 456 | + Ok(()) |
| 457 | +} |
| 458 | + |
| 459 | +#[test] |
| 460 | +fn test_fixup_etc_fstab_multi() -> Result<()> { |
| 461 | + let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?; |
| 462 | + let default = "UUID=f7436547-20ac-43cb-aa2f-eac9632183f6 /boot auto ro 0 0\n\ |
| 463 | +UUID=6907-17CA /boot/efi vfat umask=0077,shortname=winnt 0 2\n"; |
| 464 | + tempdir.create_dir_all("etc")?; |
| 465 | + tempdir.atomic_write("etc/fstab", default)?; |
| 466 | + fixup_etc_fstab(&tempdir).unwrap(); |
| 467 | + assert_eq!(tempdir.read_to_string("etc/fstab")?, default); |
| 468 | + Ok(()) |
| 469 | +} |
| 470 | + |
| 471 | +#[test] |
| 472 | +fn test_fixup_etc_fstab_ro() -> Result<()> { |
| 473 | + let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?; |
| 474 | + let default = "UUID=f7436547-20ac-43cb-aa2f-eac9632183f6 /boot auto ro 0 0\n\ |
| 475 | +UUID=1eef9f42-40e3-4bd8-ae20-e9f2325f8b52 / xfs ro 0 0\n\ |
| 476 | +UUID=6907-17CA /boot/efi vfat umask=0077,shortname=winnt 0 2\n"; |
| 477 | + tempdir.create_dir_all("etc")?; |
| 478 | + tempdir.atomic_write("etc/fstab", default)?; |
| 479 | + fixup_etc_fstab(&tempdir).unwrap(); |
| 480 | + assert_eq!(tempdir.read_to_string("etc/fstab")?, default); |
| 481 | + Ok(()) |
| 482 | +} |
| 483 | + |
| 484 | +#[test] |
| 485 | +fn test_fixup_etc_fstab_rw() -> Result<()> { |
| 486 | + let tempdir = cap_std_ext::cap_tempfile::tempdir(cap_std::ambient_authority())?; |
| 487 | + // This case uses `defaults` |
| 488 | + let default = "UUID=f7436547-20ac-43cb-aa2f-eac9632183f6 /boot auto ro 0 0\n\ |
| 489 | +UUID=1eef9f42-40e3-4bd8-ae20-e9f2325f8b52 / xfs defaults 0 0\n\ |
| 490 | +UUID=6907-17CA /boot/efi vfat umask=0077,shortname=winnt 0 2\n"; |
| 491 | + let modified = "UUID=f7436547-20ac-43cb-aa2f-eac9632183f6 /boot auto ro 0 0\n\ |
| 492 | +UUID=1eef9f42-40e3-4bd8-ae20-e9f2325f8b52 / xfs defaults,ro 0 0\n\ |
| 493 | +UUID=6907-17CA /boot/efi vfat umask=0077,shortname=winnt 0 2\n"; |
| 494 | + tempdir.create_dir_all("etc")?; |
| 495 | + tempdir.atomic_write("etc/fstab", default)?; |
| 496 | + fixup_etc_fstab(&tempdir).unwrap(); |
| 497 | + assert_eq!(tempdir.read_to_string("etc/fstab")?, modified); |
| 498 | + Ok(()) |
| 499 | +} |
0 commit comments