@@ -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,6 +412,7 @@ 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) ? {
411418 SELinuxLabelState :: Unlabeled => {
@@ -414,35 +421,52 @@ pub(crate) fn ensure_dir_labeled_recurse(
414421 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+ . skip_mountpoints ( )
427+ . path_base ( path_for_read. as_std_path ( ) ) ;
428+
429+ root. open_dir ( path_for_read) ?
430+ . walk :: < _ , anyhow:: Error > ( & config, |component| {
431+ let metadata = component. entry . metadata ( ) ?;
432+
433+ // Check if this entry should be skipped
434+ if let Some ( ( skip_dev, skip_ino) ) = skip {
435+ if ( metadata. dev ( ) , metadata. ino ( ) ) == ( skip_dev, skip_ino) {
436+ tracing:: debug!( "Skipping dev={skip_dev} inode={skip_ino}" ) ;
437+ // For directories, Break skips traversal into the directory
438+ // but continues with the next sibling. For non-directories,
439+ // Break would skip all remaining siblings, so use Continue
440+ // to skip only this entry.
441+ if component. file_type . is_dir ( ) {
442+ return Ok ( ControlFlow :: Break ( ( ) ) ) ;
443+ } else {
444+ return Ok ( ControlFlow :: Continue ( ( ) ) ) ;
445+ }
446+ }
425447 }
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) ;
432448
433- if metadata . is_dir ( ) {
434- ensure_dir_labeled_recurse ( root , path, policy , skip ) ?;
435- } else {
449+ let path = Utf8Path :: from_path ( component . path )
450+ . ok_or_else ( || anyhow :: anyhow! ( "Invalid non-UTF-8 path: {:?}" , component . path ) ) ?;
451+
436452 match ensure_labeled ( root, path, & metadata, policy) ? {
437453 SELinuxLabelState :: Unlabeled => {
438454 n += 1 ;
439455 }
440- SELinuxLabelState :: Unsupported => break ,
456+ // We check for Unsupported on the starting directory above,
457+ // and the walk uses noxdev + skip_mountpoints to stay on
458+ // the same filesystem, so hitting Unsupported here is
459+ // unexpected.
460+ SELinuxLabelState :: Unsupported => {
461+ anyhow:: bail!(
462+ "Unexpected SELinuxLabelState::Unsupported during walk at {path}"
463+ ) ;
464+ }
441465 SELinuxLabelState :: Labeled => { }
442466 }
443- }
444- path . pop ( ) ;
445- }
467+
468+ Ok ( ControlFlow :: Continue ( ( ) ) )
469+ } ) ? ;
446470
447471 if n > 0 {
448472 tracing:: debug!( "Relabeled {n} objects in {path}" ) ;
0 commit comments