@@ -67,6 +67,48 @@ pub(crate) fn supports_bootupd(root: &Dir) -> Result<bool> {
6767 Ok ( r)
6868}
6969
70+ /// Check whether the target bootupd supports `--filesystem`.
71+ ///
72+ /// Runs `bootupctl backend install --help` and looks for `--filesystem` in the
73+ /// output. When `deployment_path` is set the command runs inside a bwrap
74+ /// container so we probe the binary from the target image.
75+ fn bootupd_supports_filesystem ( rootfs : & Utf8Path , deployment_path : Option < & str > ) -> Result < bool > {
76+ let help_args = [ "bootupctl" , "backend" , "install" , "--help" ] ;
77+ let output = if let Some ( deploy) = deployment_path {
78+ let target_root = rootfs. join ( deploy) ;
79+ BwrapCmd :: new ( & target_root)
80+ . setenv (
81+ "PATH" ,
82+ "/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin" ,
83+ )
84+ . run_get_string ( help_args) ?
85+ } else {
86+ Command :: new ( "bootupctl" )
87+ . args ( & help_args[ 1 ..] )
88+ . log_debug ( )
89+ . run_get_string ( ) ?
90+ } ;
91+
92+ let use_filesystem = output. contains ( "--filesystem" ) ;
93+
94+ if use_filesystem {
95+ tracing:: debug!( "bootupd supports --filesystem" ) ;
96+ } else {
97+ tracing:: debug!( "bootupd does not support --filesystem, falling back to --device" ) ;
98+ }
99+
100+ Ok ( use_filesystem)
101+ }
102+
103+ /// Install the bootloader via bootupd.
104+ ///
105+ /// When the target bootupd supports `--filesystem` we pass it pointing at a
106+ /// block-backed mount so that bootupd can resolve the backing device(s) itself
107+ /// via `lsblk`. In the bwrap path we bind-mount the physical root at
108+ /// `/sysroot` to give `lsblk` a real block-backed path.
109+ ///
110+ /// For older bootupd versions that lack `--filesystem` we fall back to the
111+ /// legacy `--device <device_path> <rootfs>` invocation.
70112#[ context( "Installing bootloader" ) ]
71113pub ( crate ) fn install_via_bootupd (
72114 device : & bootc_blockdev:: Device ,
@@ -91,8 +133,6 @@ pub(crate) fn install_via_bootupd(
91133
92134 println ! ( "Installing bootloader via bootupd" ) ;
93135
94- let device_path = device. path ( ) ;
95-
96136 // Build the bootupctl arguments
97137 let mut bootupd_args: Vec < & str > = vec ! [ "backend" , "install" ] ;
98138 if configopts. bootupd_skip_boot_uuid {
@@ -107,26 +147,55 @@ pub(crate) fn install_via_bootupd(
107147 if let Some ( ref opts) = bootupd_opts {
108148 bootupd_args. extend ( opts. iter ( ) . copied ( ) ) ;
109149 }
110- bootupd_args. extend ( [ "--device" , & device_path, rootfs_mount] ) ;
150+
151+ // When the target bootupd lacks --filesystem support, fall back to the
152+ // legacy --device flag. For --device we need the whole-disk device path
153+ // (e.g. /dev/vda), not a partition (e.g. /dev/vda3), so resolve the
154+ // parent via require_single_root(). (Older bootupd doesn't support
155+ // multiple backing devices anyway.)
156+ // Computed before building bootupd_args so the String lives long enough.
157+ let root_device_path = if bootupd_supports_filesystem ( rootfs, deployment_path)
158+ . context ( "Probing bootupd --filesystem support" ) ?
159+ {
160+ None
161+ } else {
162+ Some ( device. require_single_root ( ) ?. path ( ) )
163+ } ;
164+ if let Some ( ref dev) = root_device_path {
165+ tracing:: debug!( "bootupd does not support --filesystem, falling back to --device {dev}" ) ;
166+ bootupd_args. extend ( [ "--device" , dev] ) ;
167+ bootupd_args. push ( rootfs_mount) ;
168+ } else {
169+ tracing:: debug!( "bootupd supports --filesystem" ) ;
170+ bootupd_args. extend ( [ "--filesystem" , rootfs_mount] ) ;
171+ bootupd_args. push ( rootfs_mount) ;
172+ }
111173
112174 // Run inside a bwrap container. It takes care of mounting and creating
113175 // the necessary API filesystems in the target deployment and acts as
114176 // a nicer `chroot`.
115177 if let Some ( deploy) = deployment_path {
116178 let target_root = rootfs. join ( deploy) ;
117179 let boot_path = rootfs. join ( "boot" ) ;
180+ let rootfs_path = rootfs. to_path_buf ( ) ;
118181
119182 tracing:: debug!( "Running bootupctl via bwrap in {}" , target_root) ;
120183
121184 // Prepend "bootupctl" to the args for bwrap
122185 let mut bwrap_args = vec ! [ "bootupctl" ] ;
123186 bwrap_args. extend ( bootupd_args) ;
124187
125- let cmd = BwrapCmd :: new ( & target_root)
188+ let mut cmd = BwrapCmd :: new ( & target_root)
126189 // Bind mount /boot from the physical target root so bootupctl can find
127190 // the boot partition and install the bootloader there
128191 . bind ( & boot_path, & "/boot" ) ;
129192
193+ // Only bind mount the physical root at /sysroot when using --filesystem;
194+ // bootupd needs it to resolve backing block devices via lsblk.
195+ if root_device_path. is_none ( ) {
196+ cmd = cmd. bind ( & rootfs_path, & "/sysroot" ) ;
197+ }
198+
130199 // The $PATH in the bwrap env is not complete enough for some images
131200 // so we inject a reasonnable default.
132201 // This is causing bootupctl and/or sfdisk binaries
0 commit comments