@@ -791,6 +791,8 @@ pub(crate) async fn run_impl(opts: RunEphemeralOpts) -> Result<()> {
791791
792792 // Process host mounts and prepare virtiofsd instances for each using async manager
793793 let mut additional_mounts = Vec :: new ( ) ;
794+ // Collect mount unit credentials to inject via SMBIOS instead of writing to filesystem
795+ let mut mount_unit_smbios_creds = Vec :: new ( ) ;
794796
795797 debug ! (
796798 "Checking for host mounts directory: /run/host-mounts exists = {}" ,
@@ -801,8 +803,7 @@ pub(crate) async fn run_impl(opts: RunEphemeralOpts) -> Result<()> {
801803 Utf8Path :: new( "/run/systemd-units" ) . exists( )
802804 ) ;
803805
804- let target_unitdir = "/run/source-image/etc/systemd/system" ;
805-
806+ let mut mount_unit_names = Vec :: new ( ) ;
806807 if Utf8Path :: new ( "/run/host-mounts" ) . exists ( ) {
807808 for entry in fs:: read_dir ( "/run/host-mounts" ) ? {
808809 let entry = entry?;
@@ -835,73 +836,48 @@ pub(crate) async fn run_impl(opts: RunEphemeralOpts) -> Result<()> {
835836 } ;
836837 additional_mounts. push ( ( virtiofsd_config, tag. clone ( ) ) ) ;
837838
838- // Create individual . mount unit for this virtiofs mount
839+ // Generate mount unit via SMBIOS credentials instead of writing to filesystem
839840 let mount_point = format ! ( "/run/virtiofs-mnt-{}" , mount_name_str) ;
840-
841- // Use systemd-escape to properly escape the mount path
842- let escaped_path = Command :: new ( "systemd-escape" )
843- . args ( [ "-p" , & mount_point] )
844- . output ( )
845- . map ( |output| String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) )
846- . unwrap_or_else ( |_| {
847- // Fallback if systemd-escape is not available
848- mount_point
849- . replace ( "/" , "-" )
850- . trim_start_matches ( '-' )
851- . to_string ( )
852- } ) ;
853-
854- let mount_unit_name = format ! ( "{}.mount" , escaped_path) ;
855- let mount_options = if is_readonly { "ro" } else { "defaults" } ;
856-
857- let mount_unit_content = format ! (
858- r#"[Unit]
859- Description=Mount virtiofs {}
860- DefaultDependencies=no
861- After=systemd-remount-fs.service
862- Before=local-fs.target shutdown.target
863-
864- [Mount]
865- What={}
866- Where={}
867- Type=virtiofs
868- Options={}
869-
870- [Install]
871- WantedBy=local-fs.target
872- "# ,
873- mount_name_str, tag, mount_point, mount_options
841+ let unit_name = crate :: credentials:: guest_path_to_unit_name ( & mount_point) ;
842+ let mount_unit_content =
843+ crate :: credentials:: generate_mount_unit ( & tag, & mount_point, is_readonly) ;
844+ let encoded_mount = data_encoding:: BASE64 . encode ( mount_unit_content. as_bytes ( ) ) ;
845+
846+ // Create SMBIOS credential for the mount unit
847+ let mount_cred = format ! (
848+ "io.systemd.credential.binary:systemd.extra-unit.{unit_name}={encoded_mount}"
874849 ) ;
850+ mount_unit_smbios_creds. push ( mount_cred) ;
875851
876- let mount_unit_path = format ! ( "{target_unitdir}/{mount_unit_name}" ) ;
877- fs:: write ( & mount_unit_path, mount_unit_content)
878- . with_context ( || format ! ( "Failed to write mount unit to {}" , mount_unit_path) ) ?;
879-
880- // Enable the mount unit by creating symlink in local-fs.target.wants/
881- let wants_dir = format ! ( "{target_unitdir}/local-fs.target.wants" ) ;
882- fs:: create_dir_all ( & wants_dir) ?;
883- let wants_link = format ! ( "{}/{}" , wants_dir, mount_unit_name) ;
884- let relative_target = format ! ( "../{}" , mount_unit_name) ;
885- std:: os:: unix:: fs:: symlink ( & relative_target, & wants_link) ?;
886-
887- // Create mount point directory in the image
888- let image_mount_point = format ! ( "/run/source-image{}" , mount_point) ;
889- fs:: create_dir_all ( & image_mount_point) . ok ( ) ;
852+ // Collect unit name for the local-fs.target dropin
853+ mount_unit_names. push ( unit_name. clone ( ) ) ;
890854
891855 debug ! (
892- "Generated mount unit: {} (enabled in local-fs.target )" ,
893- mount_unit_name
856+ "Generated SMBIOS credential for mount unit: {} ({} )" ,
857+ unit_name , mode
894858 ) ;
895859 }
896860 }
897861
862+ // If we have mount units, create a single dropin for local-fs.target
863+ if !mount_unit_names. is_empty ( ) {
864+ let wants_list = mount_unit_names. join ( " " ) ;
865+ let dropin_content = format ! ( "[Unit]\n Wants={}\n " , wants_list) ;
866+ let encoded_dropin = data_encoding:: BASE64 . encode ( dropin_content. as_bytes ( ) ) ;
867+ let dropin_cred = format ! (
868+ "io.systemd.credential.binary:systemd.unit-dropin.local-fs.target~bcvk-mounts={encoded_dropin}"
869+ ) ;
870+ mount_unit_smbios_creds. push ( dropin_cred) ;
871+ debug ! (
872+ "Created local-fs.target dropin for {} mount units" ,
873+ mount_unit_names. len( )
874+ ) ;
875+ }
876+
898877 // Handle --execute: pipes will be created when adding to qemu_config later
899878 // No need to create files anymore as we're using pipes
900879
901- let default_wantsdir = format ! ( "{target_unitdir}/default.target.wants" ) ;
902- fs:: create_dir_all ( & default_wantsdir) ?;
903-
904- // Create systemd unit to stream journal to virtio-serial device
880+ // Create systemd unit to stream journal to virtio-serial device via SMBIOS credential
905881 let journal_stream_unit = r#"[Unit]
906882Description=Stream systemd journal to host via virtio-serial
907883DefaultDependencies=no
@@ -919,17 +895,23 @@ RestartSec=1s
919895[Install]
920896WantedBy=sysinit.target
921897"# ;
922- let journal_unit_path = format ! ( "{target_unitdir}/bcvk-journal-stream.service" ) ;
923- tokio:: fs:: write ( & journal_unit_path, journal_stream_unit) . await ?;
924- debug ! ( "Created journal streaming unit at {journal_unit_path}" ) ;
925-
926- // Enable the journal streaming unit
927- let sysinit_wantsdir = format ! ( "{target_unitdir}/sysinit.target.wants" ) ;
928- tokio:: fs:: create_dir_all ( & sysinit_wantsdir) . await ?;
929- let journal_wants_link = format ! ( "{sysinit_wantsdir}/bcvk-journal-stream.service" ) ;
930- tokio:: fs:: symlink ( "../bcvk-journal-stream.service" , & journal_wants_link) . await ?;
931- debug ! ( "Enabled journal streaming unit in sysinit.target.wants" ) ;
898+ let encoded_journal = data_encoding:: BASE64 . encode ( journal_stream_unit. as_bytes ( ) ) ;
899+ let journal_cred = format ! (
900+ "io.systemd.credential.binary:systemd.extra-unit.bcvk-journal-stream.service={encoded_journal}"
901+ ) ;
902+ mount_unit_smbios_creds. push ( journal_cred) ;
903+ debug ! ( "Generated SMBIOS credential for journal streaming unit" ) ;
904+
905+ // Create dropin for sysinit.target to enable journal streaming
906+ let journal_dropin = "[Unit]\n Wants=bcvk-journal-stream.service\n " ;
907+ let encoded_dropin = data_encoding:: BASE64 . encode ( journal_dropin. as_bytes ( ) ) ;
908+ let dropin_cred = format ! (
909+ "io.systemd.credential.binary:systemd.unit-dropin.sysinit.target~bcvk-journal={encoded_dropin}"
910+ ) ;
911+ mount_unit_smbios_creds. push ( dropin_cred) ;
912+ debug ! ( "Created sysinit.target dropin to enable journal streaming unit" ) ;
932913
914+ // Create execute units via SMBIOS credentials if needed
933915 match opts. common . execute . as_slice ( ) {
934916 [ ] => { }
935917 elts => {
@@ -949,8 +931,7 @@ StandardError=inherit
949931 service_content. push_str ( & format ! ( "ExecStart={elt}\n " ) ) ;
950932 }
951933
952- let service_finish = format ! (
953- r#"[Unit]
934+ let service_finish = r#"[Unit]
954935Description=Execute Script Service Completion
955936After=bootc-execute.service
956937Requires=dev-virtio\\x2dports-executestatus.device
@@ -960,24 +941,34 @@ Type=oneshot
960941ExecStart=systemctl show bootc-execute
961942ExecStart=systemctl poweroff
962943StandardOutput=file:/dev/virtio-ports/executestatus
963- "#
964- ) ;
944+ "# ;
965945
966- let service_path = format ! ( "{target_unitdir}/bootc-execute.service" ) ;
967- fs:: write ( service_path, service_content) ?;
968- let service_path = format ! ( "{target_unitdir}/bootc-execute-finish.service" ) ;
969- fs:: write ( service_path, service_finish) ?;
946+ // Inject execute units via SMBIOS credentials
947+ let encoded_execute = data_encoding:: BASE64 . encode ( service_content. as_bytes ( ) ) ;
948+ let execute_cred = format ! (
949+ "io.systemd.credential.binary:systemd.extra-unit.bootc-execute.service={encoded_execute}"
950+ ) ;
951+ mount_unit_smbios_creds. push ( execute_cred) ;
970952
971- for svc in [ "bootc-execute.service" , "bootc-execute-finish.service" ] {
972- let wants_link = format ! ( "{default_wantsdir}/{svc}" ) ;
973- debug ! ( "Creating execute service symlink: {}" , & wants_link) ;
974- std:: os:: unix:: fs:: symlink ( format ! ( "../{svc}" ) , wants_link) ?;
975- }
953+ let encoded_finish = data_encoding:: BASE64 . encode ( service_finish. as_bytes ( ) ) ;
954+ let finish_cred = format ! (
955+ "io.systemd.credential.binary:systemd.extra-unit.bootc-execute-finish.service={encoded_finish}"
956+ ) ;
957+ mount_unit_smbios_creds. push ( finish_cred) ;
958+
959+ // Create dropin for default.target to enable execute services
960+ let execute_dropin =
961+ "[Unit]\n Wants=bootc-execute.service bootc-execute-finish.service\n " ;
962+ let encoded_dropin = data_encoding:: BASE64 . encode ( execute_dropin. as_bytes ( ) ) ;
963+ let dropin_cred = format ! (
964+ "io.systemd.credential.binary:systemd.unit-dropin.default.target~bcvk-execute={encoded_dropin}"
965+ ) ;
966+ mount_unit_smbios_creds. push ( dropin_cred) ;
967+ debug ! ( "Generated SMBIOS credentials for execute units" ) ;
976968 }
977969 }
978970
979- // Copy systemd units if provided (after mount units have been generated)
980- // Also inject if we created mount units that need to be copied
971+ // Copy systemd units if provided (for --systemd-units-dir option)
981972 inject_systemd_units ( ) ?;
982973
983974 // Prepare main virtiofsd config for the source image (will be spawned by QEMU)
@@ -1062,22 +1053,30 @@ StandardOutput=file:/dev/virtio-ports/executestatus
10621053 "swap" . into ( ) ,
10631054 crate :: to_disk:: Format :: Raw ,
10641055 ) ;
1065- let svc = format ! (
1066- r#"[Unit]
1056+
1057+ // Create swap unit via SMBIOS credential
1058+ let svc = r#"[Unit]
10671059Description=bcvk ephemeral swap
10681060
10691061[Swap]
10701062What=/dev/disk/by-id/virtio-swap
10711063Options=
1072- "#
1073- ) ;
1074-
1064+ "# ;
10751065 let service_name = r#"dev-disk-by\x2did-virtio\x2dswap.swap"# ;
1076- let service_path = format ! ( "{target_unitdir}/{service_name}" ) ;
1077- fs:: write ( & service_path, svc) ?;
1066+ let encoded_swap = data_encoding:: BASE64 . encode ( svc. as_bytes ( ) ) ;
1067+ let swap_cred = format ! (
1068+ "io.systemd.credential.binary:systemd.extra-unit.{service_name}={encoded_swap}"
1069+ ) ;
1070+ mount_unit_smbios_creds. push ( swap_cred) ;
10781071
1079- let wants_link = format ! ( "{default_wantsdir}/{service_name}" ) ;
1080- std:: os:: unix:: fs:: symlink ( format ! ( "../{service_name}" ) , wants_link) ?;
1072+ // Create dropin for default.target to enable swap
1073+ let swap_dropin = format ! ( "[Unit]\n Wants={service_name}\n " ) ;
1074+ let encoded_dropin = data_encoding:: BASE64 . encode ( swap_dropin. as_bytes ( ) ) ;
1075+ let dropin_cred = format ! (
1076+ "io.systemd.credential.binary:systemd.unit-dropin.default.target~bcvk-swap={encoded_dropin}"
1077+ ) ;
1078+ mount_unit_smbios_creds. push ( dropin_cred) ;
1079+ debug ! ( "Generated SMBIOS credential for swap unit" ) ;
10811080
10821081 tmp_swapfile = Some ( tmpf) ;
10831082 }
@@ -1207,6 +1206,13 @@ Options=
12071206 } ) ?;
12081207 } ;
12091208
1209+ // Add all SMBIOS credentials for mount units, journal, and execute services
1210+ let cred_count = mount_unit_smbios_creds. len ( ) ;
1211+ for cred in mount_unit_smbios_creds {
1212+ qemu_config. add_smbios_credential ( cred) ;
1213+ }
1214+ debug ! ( "Added {} SMBIOS credentials to QEMU config" , cred_count) ;
1215+
12101216 debug ! ( "Starting QEMU with systemd debugging enabled" ) ;
12111217
12121218 // Spawn QEMU with all virtiofsd processes handled internally
0 commit comments