@@ -29,7 +29,7 @@ fn write_file(writer: &mut impl Write, path: &str, content: &[u8]) -> io::Result
2929 Ok ( ( ) )
3030}
3131
32- /// CPIO entry: either a directory or a file with content .
32+ /// CPIO entry: either a directory or a regular file (0644) .
3333enum Entry {
3434 Dir ( & ' static str ) ,
3535 File ( & ' static str , & ' static [ u8 ] ) ,
@@ -41,6 +41,7 @@ pub fn create_initramfs_units_cpio() -> io::Result<Vec<u8>> {
4141
4242 const UNIT_DIR : & str = "usr/lib/systemd/system" ;
4343 const DROPIN_DIR : & str = "usr/lib/systemd/system/initrd-fs.target.d" ;
44+ const ROOT_FS_DROPIN_DIR : & str = "usr/lib/systemd/system/initrd-root-fs.target.d" ;
4445
4546 let entries: & [ Entry ] = & [
4647 // Directory hierarchy
@@ -49,6 +50,17 @@ pub fn create_initramfs_units_cpio() -> io::Result<Vec<u8>> {
4950 Dir ( "usr/lib/systemd" ) ,
5051 Dir ( UNIT_DIR ) ,
5152 Dir ( DROPIN_DIR ) ,
53+ Dir ( ROOT_FS_DROPIN_DIR ) ,
54+ // sysroot.mount — mounts the virtiofs "rootfs" tag read-only at
55+ // /sysroot. bcvk does not set root= on the kernel cmdline, so
56+ // systemd-fstab-generator never generates a competing sysroot.mount,
57+ // and dracut sets rootok=1 via its UNSET branch (no root= arg → trust
58+ // systemd generators). The bcvk-sysroot.conf drop-in below wires
59+ // this unit into initrd-root-fs.target.
60+ File (
61+ "usr/lib/systemd/system/sysroot.mount" ,
62+ include_bytes ! ( "units/sysroot.mount" ) ,
63+ ) ,
5264 // Service units
5365 File (
5466 "usr/lib/systemd/system/bcvk-etc-overlay.service" ,
@@ -66,6 +78,15 @@ pub fn create_initramfs_units_cpio() -> io::Result<Vec<u8>> {
6678 "usr/lib/systemd/system/bcvk-journal-stream.service" ,
6779 include_bytes ! ( "units/bcvk-journal-stream.service" ) ,
6880 ) ,
81+ // Drop-in to pull sysroot.mount into initrd-root-fs.target. Without
82+ // this, nothing in the dependency graph actually requests the mount;
83+ // dracut-rootfs-generator normally creates an
84+ // initrd-root-fs.target.requires/sysroot.mount symlink for block-device
85+ // roots, but for virtiofs (not a block device) it skips that step.
86+ File (
87+ "usr/lib/systemd/system/initrd-root-fs.target.d/bcvk-sysroot.conf" ,
88+ b"[Unit]\n Requires=sysroot.mount\n After=sysroot.mount\n " ,
89+ ) ,
6990 // Drop-in configs to pull units into initrd-fs.target
7091 File (
7192 "usr/lib/systemd/system/initrd-fs.target.d/bcvk-etc-overlay.conf" ,
@@ -104,6 +125,7 @@ mod tests {
104125
105126 let mut entries = Vec :: new ( ) ;
106127 let mut etc_overlay_content = None ;
128+ let mut sysroot_mount_content = None ;
107129
108130 loop {
109131 let mut reader = cpio:: NewcReader :: new ( cursor) . expect ( "failed to read CPIO entry" ) ;
@@ -115,13 +137,20 @@ mod tests {
115137 let size = reader. entry ( ) . file_size ( ) as usize ;
116138 let mode = reader. entry ( ) . mode ( ) ;
117139
118- // Read file content for verification
119- if name == "usr/lib/systemd/system/bcvk-etc-overlay.service" {
120- let mut content = vec ! [ 0u8 ; size] ;
121- reader
122- . read_exact ( & mut content)
123- . expect ( "failed to read file content" ) ;
124- etc_overlay_content = Some ( String :: from_utf8 ( content) . expect ( "invalid UTF-8" ) ) ;
140+ let mut content_buf = vec ! [ 0u8 ; size] ;
141+ reader
142+ . read_exact ( & mut content_buf)
143+ . expect ( "failed to read file content" ) ;
144+ let content_str = String :: from_utf8 ( content_buf) . ok ( ) ;
145+
146+ match name. as_str ( ) {
147+ "usr/lib/systemd/system/bcvk-etc-overlay.service" => {
148+ etc_overlay_content = content_str. clone ( )
149+ }
150+ "usr/lib/systemd/system/sysroot.mount" => {
151+ sysroot_mount_content = content_str. clone ( )
152+ }
153+ _ => { }
125154 }
126155
127156 entries. push ( ( name, size, mode) ) ;
@@ -130,37 +159,64 @@ mod tests {
130159
131160 let names: Vec < _ > = entries. iter ( ) . map ( |( n, _, _) | n. as_str ( ) ) . collect ( ) ;
132161
133- // Verify directories
162+ // Verify directory hierarchy
134163 assert ! ( names. contains( & "usr" ) ) ;
135164 assert ! ( names. contains( & "usr/lib" ) ) ;
136165 assert ! ( names. contains( & "usr/lib/systemd" ) ) ;
137166 assert ! ( names. contains( & "usr/lib/systemd/system" ) ) ;
138167 assert ! ( names. contains( & "usr/lib/systemd/system/initrd-fs.target.d" ) ) ;
168+ assert ! ( names. contains( & "usr/lib/systemd/system/initrd-root-fs.target.d" ) ) ;
139169
140- // Verify service files
170+ // sysroot.mount must be present and correct
171+ assert ! (
172+ names. contains( & "usr/lib/systemd/system/sysroot.mount" ) ,
173+ "sysroot.mount must be injected"
174+ ) ;
175+ let sysroot = sysroot_mount_content. expect ( "sysroot.mount content missing" ) ;
176+ assert ! (
177+ sysroot. contains( "Type=virtiofs" ) ,
178+ "sysroot.mount must use virtiofs"
179+ ) ;
180+ assert ! (
181+ sysroot. contains( "What=rootfs" ) ,
182+ "sysroot.mount must mount the 'rootfs' tag"
183+ ) ;
184+ assert ! (
185+ sysroot. contains( "Where=/sysroot" ) ,
186+ "sysroot.mount must target /sysroot"
187+ ) ;
188+ assert ! (
189+ sysroot. contains( "Options=ro" ) ,
190+ "sysroot.mount must be read-only"
191+ ) ;
192+
193+ // Service units
141194 assert ! ( names. contains( & "usr/lib/systemd/system/bcvk-etc-overlay.service" ) ) ;
142195 assert ! ( names. contains( & "usr/lib/systemd/system/bcvk-var-ephemeral.service" ) ) ;
143196 assert ! ( names. contains( & "usr/lib/systemd/system/bcvk-copy-units.service" ) ) ;
144197 assert ! ( names. contains( & "usr/lib/systemd/system/bcvk-journal-stream.service" ) ) ;
145198
146- // Verify drop-in configs
199+ // initrd-root-fs.target drop-in
200+ assert ! ( names. contains( & "usr/lib/systemd/system/initrd-root-fs.target.d/bcvk-sysroot.conf" ) ) ;
201+
202+ // Drop-in configs
147203 assert ! ( names. contains( & "usr/lib/systemd/system/initrd-fs.target.d/bcvk-etc-overlay.conf" ) ) ;
148204 assert ! (
149205 names. contains( & "usr/lib/systemd/system/initrd-fs.target.d/bcvk-var-ephemeral.conf" )
150206 ) ;
151207 assert ! ( names. contains( & "usr/lib/systemd/system/initrd-fs.target.d/bcvk-copy-units.conf" ) ) ;
152208
153- // Verify file modes
209+ // Verify file modes: all entries are either regular files (0644) or directories
154210 for ( name, _size, mode) in & entries {
155211 let file_type = * mode & 0o170000 ;
156- if name. ends_with ( ".service" ) || name. ends_with ( ".conf" ) {
157- assert_eq ! ( file_type, 0o100000 , "{} should be regular file" , name) ;
212+ if name. ends_with ( ".service" ) || name. ends_with ( ".conf" ) || name . ends_with ( ".mount" ) {
213+ assert_eq ! ( file_type, 0o100000 , "{} should be a regular file" , name) ;
158214 } else {
159- assert_eq ! ( file_type, 0o040000 , "{} should be directory" , name) ;
215+ assert_eq ! ( file_type, 0o040000 , "{} should be a directory" , name) ;
160216 }
161217 }
162218
163- // Verify file content is valid systemd unit
219+ // bcvk-etc-overlay.service must be a valid systemd unit
164220 let content = etc_overlay_content. expect ( "bcvk-etc-overlay.service not found" ) ;
165221 assert ! ( content. contains( "[Unit]" ) ) ;
166222 assert ! ( content. contains( "[Service]" ) ) ;
0 commit comments