@@ -387,14 +387,21 @@ 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` and `skip_mountpoints` to avoid crossing
392+ /// mount point boundaries
393+ /// (e.g. into sysfs, procfs, etc.).
394+ /// The provided `skip` parameter is a device/inode pair that we will ignore
395+ /// (and not traverse into).
392396pub ( crate ) fn ensure_dir_labeled_recurse (
393397 root : & Dir ,
394398 path : & mut Utf8PathBuf ,
395399 policy : & ostree:: SePolicy ,
396400 skip : Option < ( libc:: dev_t , libc:: ino64_t ) > ,
397401) -> Result < ( ) > {
402+ use cap_std_ext:: dirext:: WalkConfiguration ;
403+ use std:: ops:: ControlFlow ;
404+
398405 // Juggle the cap-std requirement for relative paths vs the libselinux
399406 // requirement for absolute paths by special casing the empty string "" as "."
400407 // just for the initial directory enumeration.
@@ -406,6 +413,7 @@ pub(crate) fn ensure_dir_labeled_recurse(
406413
407414 let mut n = 0u64 ;
408415
416+ // Label the starting directory itself; the walk API only visits children.
409417 let metadata = root. symlink_metadata ( path_for_read) ?;
410418 match ensure_labeled ( root, path, & metadata, policy) ? {
411419 SELinuxLabelState :: Unlabeled => {
@@ -414,35 +422,52 @@ pub(crate) fn ensure_dir_labeled_recurse(
414422 SELinuxLabelState :: Unsupported => return Ok ( ( ) ) ,
415423 SELinuxLabelState :: Labeled => { }
416424 }
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 ;
425+ let config = WalkConfiguration :: default ( )
426+ . noxdev ( )
427+ . skip_mountpoints ( )
428+ . path_base ( path_for_read. as_std_path ( ) ) ;
429+
430+ root. open_dir ( path_for_read) ?
431+ . walk :: < _ , anyhow:: Error > ( & config, |component| {
432+ let metadata = component. entry . metadata ( ) ?;
433+
434+ // Check if this entry should be skipped
435+ if let Some ( ( skip_dev, skip_ino) ) = skip {
436+ if ( metadata. dev ( ) , metadata. ino ( ) ) == ( skip_dev, skip_ino) {
437+ tracing:: debug!( "Skipping dev={skip_dev} inode={skip_ino}" ) ;
438+ // For directories, Break skips traversal into the directory
439+ // but continues with the next sibling. For non-directories,
440+ // Break would skip all remaining siblings, so use Continue
441+ // to skip only this entry.
442+ if component. file_type . is_dir ( ) {
443+ return Ok ( ControlFlow :: Break ( ( ) ) ) ;
444+ } else {
445+ return Ok ( ControlFlow :: Continue ( ( ) ) ) ;
446+ }
447+ }
425448 }
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) ;
432449
433- if metadata . is_dir ( ) {
434- ensure_dir_labeled_recurse ( root , path, policy , skip ) ?;
435- } else {
450+ let path = Utf8Path :: from_path ( component . path )
451+ . ok_or_else ( || anyhow :: anyhow! ( "Invalid non-UTF-8 path: {:?}" , component . path ) ) ?;
452+
436453 match ensure_labeled ( root, path, & metadata, policy) ? {
437454 SELinuxLabelState :: Unlabeled => {
438455 n += 1 ;
439456 }
440- SELinuxLabelState :: Unsupported => break ,
457+ // We check for Unsupported on the starting directory above,
458+ // and the walk uses noxdev + skip_mountpoints to stay on
459+ // the same filesystem, so hitting Unsupported here is
460+ // unexpected.
461+ SELinuxLabelState :: Unsupported => {
462+ anyhow:: bail!(
463+ "Unexpected SELinuxLabelState::Unsupported during walk at {path}"
464+ ) ;
465+ }
441466 SELinuxLabelState :: Labeled => { }
442467 }
443- }
444- path . pop ( ) ;
445- }
468+
469+ Ok ( ControlFlow :: Continue ( ( ) ) )
470+ } ) ? ;
446471
447472 if n > 0 {
448473 tracing:: debug!( "Relabeled {n} objects in {path}" ) ;
0 commit comments