@@ -387,14 +387,20 @@ pub(crate) fn relabel_recurse(
387387 relabel_recurse_inner ( root, & mut path, as_path. as_mut ( ) , policy)
388388}
389389
390- /// A wrapper for creating a directory, also optionally setting a SELinux label.
391- /// The provided `skip` parameter is a device/inode that we will ignore (and not traverse).
390+ /// Recursively ensure all files under a directory have SELinux labels.
391+ /// Uses the `walk` API with `noxdev` to avoid crossing mount point boundaries
392+ /// (e.g. into sysfs, procfs, etc.).
393+ /// The provided `skip` parameter is a device/inode pair that we will ignore
394+ /// (and not traverse into).
392395pub ( crate ) fn ensure_dir_labeled_recurse (
393396 root : & Dir ,
394397 path : & mut Utf8PathBuf ,
395398 policy : & ostree:: SePolicy ,
396399 skip : Option < ( libc:: dev_t , libc:: ino64_t ) > ,
397400) -> Result < ( ) > {
401+ use cap_std_ext:: dirext:: WalkConfiguration ;
402+ use std:: ops:: ControlFlow ;
403+
398404 // Juggle the cap-std requirement for relative paths vs the libselinux
399405 // requirement for absolute paths by special casing the empty string "" as "."
400406 // just for the initial directory enumeration.
@@ -406,43 +412,68 @@ pub(crate) fn ensure_dir_labeled_recurse(
406412
407413 let mut n = 0u64 ;
408414
415+ // Label the starting directory itself; the walk API only visits children.
409416 let metadata = root. symlink_metadata ( path_for_read) ?;
410417 match ensure_labeled ( root, path, & metadata, policy) ? {
418+ SELinuxLabelState :: Unsupported => return Ok ( ( ) ) ,
411419 SELinuxLabelState :: Unlabeled => {
412420 n += 1 ;
413421 }
414- SELinuxLabelState :: Unsupported => return Ok ( ( ) ) ,
415422 SELinuxLabelState :: Labeled => { }
416423 }
417-
418- for ent in root. read_dir ( path_for_read) ? {
419- let ent = ent?;
420- let metadata = ent. metadata ( ) ?;
421- if let Some ( ( skip_dev, skip_ino) ) = skip. as_ref ( ) . copied ( ) {
422- if ( metadata. dev ( ) , metadata. ino ( ) ) == ( skip_dev, skip_ino) {
423- tracing:: debug!( "Skipping dev={skip_dev} inode={skip_ino}" ) ;
424- continue ;
424+ let config = WalkConfiguration :: default ( )
425+ . noxdev ( )
426+ . path_base ( path_for_read. as_std_path ( ) ) ;
427+
428+ root. open_dir ( path_for_read) ?
429+ . walk :: < _ , anyhow:: Error > ( & config, |component| {
430+ let metadata = component. entry . metadata ( ) ?;
431+
432+ // Check if this entry should be skipped
433+ if let Some ( ( skip_dev, skip_ino) ) = skip {
434+ if ( metadata. dev ( ) , metadata. ino ( ) ) == ( skip_dev, skip_ino) {
435+ tracing:: debug!( "Skipping dev={skip_dev} inode={skip_ino}" ) ;
436+ // For directories, Break skips traversal into the directory
437+ // but continues with the next sibling. For non-directories,
438+ // Break would skip all remaining siblings, so use Continue
439+ // to skip only this entry.
440+ if component. file_type . is_dir ( ) {
441+ return Ok ( ControlFlow :: Break ( ( ) ) ) ;
442+ } else {
443+ return Ok ( ControlFlow :: Continue ( ( ) ) ) ;
444+ }
445+ }
425446 }
426- }
427- let name = ent. file_name ( ) ;
428- let name = name
429- . to_str ( )
430- . ok_or_else ( || anyhow:: anyhow!( "Invalid non-UTF-8 filename: {name:?}" ) ) ?;
431- path. push ( name) ;
432447
433- if metadata . is_dir ( ) {
434- ensure_dir_labeled_recurse ( root , path, policy , skip ) ?;
435- } else {
448+ let path = Utf8Path :: from_path ( component . path )
449+ . ok_or_else ( || anyhow :: anyhow! ( "Invalid non-UTF-8 path: {:?}" , component . path ) ) ?;
450+
436451 match ensure_labeled ( root, path, & metadata, policy) ? {
437452 SELinuxLabelState :: Unlabeled => {
438453 n += 1 ;
439454 }
440- SELinuxLabelState :: Unsupported => break ,
455+ // We check for Unsupported on the starting directory above,
456+ // and the walk uses noxdev to stay on the same filesystem,
457+ // so hitting Unsupported here would be unexpected unless
458+ // this entry is a mountpoint on a filesystem that doesn't
459+ // support xattrs (e.g. the vfat EFI system partition).
460+ SELinuxLabelState :: Unsupported => {
461+ match component. dir . is_mountpoint ( component. filename ) ? {
462+ // Definitely a mountpoint, or can't determine; skip it
463+ Some ( true ) | None => return Ok ( ControlFlow :: Break ( ( ) ) ) ,
464+ // Definitely not a mountpoint; this is unexpected
465+ Some ( false ) => {
466+ anyhow:: bail!(
467+ "Unexpected SELinuxLabelState::Unsupported during walk at {path}"
468+ ) ;
469+ }
470+ }
471+ }
441472 SELinuxLabelState :: Labeled => { }
442473 }
443- }
444- path . pop ( ) ;
445- }
474+
475+ Ok ( ControlFlow :: Continue ( ( ) ) )
476+ } ) ? ;
446477
447478 if n > 0 {
448479 tracing:: debug!( "Relabeled {n} objects in {path}" ) ;
0 commit comments