FlashSystems / ukibak

Unified Kernel Image backup utility
BSD 3-Clause "New" or "Revised" License
2 stars 0 forks source link

Inaccurate Handling of '-e' Command Line Option and 'UKIBAK_ESP' Environment Variable in ukibak #1

Closed mib1982 closed 9 months ago

mib1982 commented 9 months ago

Description: When running ukibak with the '-e /efi' command line option or the 'UKIBAK_ESP' environment variable set to '/efi', the program fails to locate the source image '/EFI/Linux/arch-linux.efi', despite its presence at '/efi/EFI/Linux/arch-linux.efi'. This issue persists regardless of using the command line option or the environment variable.

Steps to Reproduce:

Set the 'UKIBAK_ESP' environment variable to '/efi'.
- or - Execute ukibak using the command line option '-e /efi'.
Observe the error message: "ERROR [ukibak] Source image '/EFI/Linux/arch-linux.efi' not found."

Expected Behavior: The program should recognize the specified EFI partition mount point (either through the '-e' option or the 'UKIBAK_ESP' variable) and locate the source image at the provided path.

Actual Behavior: The program outputs an error message indicating it cannot find the source image, suggesting it does not correctly process the provided mount point path.

Additional Information:

Operating System: Arch Linux with Linux-zen 6.7.0-zen3-1-zen
ukibak Version: 1.0.0

Attempted Troubleshooting: I attempted different values for invoking ukibak with the command line option -e, including the whole path to the uki-image.

FlashSystems commented 9 months ago

Hello @mib1982, thank you for your well written bug report. Unfortunately I'm unable to reproduce the problem. I tried the steps you described on two different Arch Linux installations and on both the -e switch and the UKIBAK_ESP environment variable are working flawlessly.

Looking at your observed behavior, it looks to me that only the first / of the option is used by ukibak. Could you please check the following things:

  1. /efi is the default value for the mount point of the EFI partition. If your EFI partition is mounted as /efi it should work without using the -e switch or the UKIBAK_ESP variable. Does ukibak work if you call it without a command line option or set environment variable?
  2. Could you please call ukibak with the -d switch set and post the result here?
d-tux commented 9 months ago

Hi,

I had the same issue @mib1982 mentioned, so I had a quick look and I believe the issue lies with use of the Path#join method: since the loader image name in the EFI variable will always be absolute, joining it with the EFI path doesn't do anything, because joining anything with an absolute path returns that exact path; as shown by the example in the Path#join documentation:

assert_eq!(Path::new("/etc").join("/bin/sh"), PathBuf::from("/bin/sh"));

My Rust-fu is pretty weak, but I managed to write some naive code that appears to fix it; which may be useful, so here goes:

--- a/src/main.rs
+++ b/src/main.rs
@@ -236,12 +236,18 @@ fn backup_uki(args: &Args) -> Result<(), Error> {
     // Select a path from the list.
     let esp_path = get_esp_path_from_list(&esp_paths, args.force)?;

-    let active_esp: PathBuf = get_loader_image_name(&PathBuf::from(&args.efivarfs))?.into();
+    let mut active_esp: PathBuf = get_loader_image_name(&PathBuf::from(&args.efivarfs))?.into();
     debug!("Active loader image name is '{}'.", active_esp.display());

+    if active_esp.is_absolute() {
+        active_esp = active_esp.strip_prefix("/").unwrap().to_path_buf()
+    }
+
     // Combine the relative path of the active ESP with the mountpoint of the ESP
     // specified on the command line (or the default).
     let source_path = esp_path.join(active_esp);
+   
+    debug!("Active loader image source path is '{}'.", source_path.display());
     if !source_path.exists() {
         Err(Error::SourceNotFound(source_path.clone()))?;
     }

Thanks for creating this project BTW!

mib1982 commented 9 months ago

Hello @FlashSystems, thank you for taking the time to look into this issue.

First, here is an excerpt of /etc/fstab showing the relevant line for /efi: /dev/sda2 /efi vfat umask=0077 0 2

  1. Here is the output of ukibak if I call it without a command line option and no environmental variable set.

-> # ukibak ERROR [ukibak] Source image '/EFI/Linux/arch-linux.efi' not found.

  1. Here is the output of ukibak -d :

-> # ukibak -d DEBUG [ukibak] Active loader image name is '/EFI/Linux/arch-linux.efi'. ERROR [ukibak] Source image '/EFI/Linux/arch-linux.efi' not found.

I assume it takes the active loader image name somewhere from /sys/firmware/efi/efivars/, but it doesn't put it in relation to the mount-point of the EFI System Partition /efi.

However, I was able to make it work by creating a symlink /EFI which points to /efi/EFI. This leads to the creation of a backup of the last bootable uki. Here is the output of ukibak -d with the symlink in place:

-> # ukibak -d
DEBUG [ukibak] Active loader image name is '/EFI/Linux/arch-linux.efi'. DEBUG [ukibak] Parsing image /EFI/Linux/arch-linux.efi... DEBUG [ukibak] Found section .text DEBUG [ukibak] Found section .rodata DEBUG [ukibak] Found section .data DEBUG [ukibak] Found section .sbat DEBUG [ukibak] Found section .sdmagic DEBUG [ukibak] Found section .reloc DEBUG [ukibak] Found section .osrel DEBUG [ukibak] Found section .uname DEBUG [ukibak] Found section .cmdline DEBUG [ukibak] Found section .splash DEBUG [ukibak] Found section .linux DEBUG [ukibak] Found section .initrd DEBUG [ukibak] Image is self contained. DEBUG [ukibak] Time since last reboot is 910s DEBUG [ukibak] File was modified 84011s ago. Minimum age is 910s. DEBUG [ukibak] Parsing image /EFI/Linux/linux-last.efi... INFO [ukibak] Copying '/EFI/Linux/arch-linux.efi' to '/EFI/Linux/linux-last.efi'... DEBUG [ukibak] Copy succeeded.

For the time being, the workaround is a good solution which works for me. I leave it up to you if you would like to further investigate the problem or close this issue.

Edit: Thank you @d-tux for the potential fix. Your post overlapped with mine, but I am hopeful your insight into the behavior of Path::join might fix this issue.

FlashSystems commented 9 months ago

Hello @d-tux, you are absolutley right, this EFI implementations return an absolute path. On both of my systems, the returned path is relative. Maybe it depends on how the path was supplied to efibootmgr command. Thank you for your effort and for suggesting a fix. I'll update my code, add some tests and release a new version as soon as possible.

@mib1982: Thank you for supplying the log. This confirms @d-tux's assumption: DEBUG [ukibak] Active loader image name is '/EFI/Linux/arch-linux.efi'.. The load image name starts with a slash.

FlashSystems commented 9 months ago

I've fixed the bug and added a test to make sure there will be no regressions. Please test Version 1.1.0. It would be great if you could let me know whether it also works in practice.

mib1982 commented 9 months ago

It looks like it has been fixed for me. Here is the output of ukibak -d after updating to Version 1.1.0:

-> # ukibak -d DEBUG [ukibak] Active loader image name is 'EFI/Linux/arch-linux.efi'. DEBUG [ukibak] Parsing image /efi/EFI/Linux/arch-linux.efi... DEBUG [ukibak] Found section .text DEBUG [ukibak] Found section .rodata DEBUG [ukibak] Found section .data DEBUG [ukibak] Found section .sbat DEBUG [ukibak] Found section .sdmagic DEBUG [ukibak] Found section .reloc DEBUG [ukibak] Found section .osrel DEBUG [ukibak] Found section .uname DEBUG [ukibak] Found section .cmdline DEBUG [ukibak] Found section .splash DEBUG [ukibak] Found section .linux DEBUG [ukibak] Found section .initrd DEBUG [ukibak] Image is self contained. DEBUG [ukibak] Time since last reboot is 16284s DEBUG [ukibak] File was modified 12537s ago. Minimum age is 16284s. INFO [ukibak] File '/efi/EFI/Linux/arch-linux.efi' was modified since the last reboot. Skipping copy.

Thanks for taking care of this issue so quickly and for creating this project.