1010use indicatif:: ProgressBar ;
1111use std:: ffi:: OsStr ;
1212use std:: fs;
13+ #[ cfg( unix) ]
14+ use std:: os:: unix:: fs:: MetadataExt ;
15+
1316use std:: io:: { IsTerminal , stdin} ;
1417use std:: os:: unix:: fs:: PermissionsExt ;
1518use std:: path:: Path ;
@@ -22,8 +25,8 @@ use uucore::translate;
2225
2326use super :: super :: {
2427 InteractiveMode , Options , is_dir_empty, is_readable_metadata, prompt_descend, remove_file,
25- show_permission_denied_error , show_removal_error , verbose_removed_directory ,
26- verbose_removed_file,
28+ should_skip_different_device_with_dev , show_permission_denied_error , show_removal_error ,
29+ verbose_removed_directory , verbose_removed_file,
2730} ;
2831
2932#[ inline]
@@ -276,6 +279,7 @@ pub fn safe_remove_dir_recursive(
276279 path : & Path ,
277280 options : & Options ,
278281 progress_bar : Option < & ProgressBar > ,
282+ current_dev_id : Option < u64 > ,
279283) -> bool {
280284 // Base case 1: this is a file or a symbolic link.
281285 // Use lstat to avoid race condition between check and use
@@ -289,6 +293,16 @@ pub fn safe_remove_dir_recursive(
289293 }
290294 } ;
291295
296+ let check_device = options. one_fs || options. preserve_root_all ;
297+ #[ cfg( unix) ]
298+ let current_dev_id = if check_device {
299+ current_dev_id. or_else ( || path. symlink_metadata ( ) . ok ( ) . map ( |m| m. dev ( ) ) )
300+ } else {
301+ None
302+ } ;
303+ #[ cfg( not( unix) ) ]
304+ let current_dev_id: Option < u64 > = None ;
305+
292306 // Try to open the directory using DirFd for secure traversal
293307 let dir_fd = match DirFd :: open ( path) {
294308 Ok ( fd) => fd,
@@ -309,7 +323,12 @@ pub fn safe_remove_dir_recursive(
309323 }
310324 } ;
311325
312- let error = safe_remove_dir_recursive_impl ( path, & dir_fd, options) ;
326+ let error = safe_remove_dir_recursive_impl (
327+ path,
328+ & dir_fd,
329+ options,
330+ if check_device { current_dev_id } else { None } ,
331+ ) ;
313332
314333 // After processing all children, remove the directory itself
315334 if error {
@@ -345,7 +364,12 @@ pub fn safe_remove_dir_recursive(
345364 }
346365}
347366
348- pub fn safe_remove_dir_recursive_impl ( path : & Path , dir_fd : & DirFd , options : & Options ) -> bool {
367+ pub fn safe_remove_dir_recursive_impl (
368+ path : & Path ,
369+ dir_fd : & DirFd ,
370+ options : & Options ,
371+ parent_dev_id : Option < u64 > ,
372+ ) -> bool {
349373 // Read directory entries using safe traversal
350374 let entries = match dir_fd. read_dir ( ) {
351375 Ok ( entries) => entries,
@@ -361,6 +385,7 @@ pub fn safe_remove_dir_recursive_impl(path: &Path, dir_fd: &DirFd, options: &Opt
361385 } ;
362386
363387 let mut error = false ;
388+ let check_device = options. one_fs || options. preserve_root_all ;
364389
365390 // Process each entry
366391 for entry_name in entries {
@@ -379,6 +404,17 @@ pub fn safe_remove_dir_recursive_impl(path: &Path, dir_fd: &DirFd, options: &Opt
379404 let is_dir = ( entry_stat. st_mode & libc:: S_IFMT ) == libc:: S_IFDIR ;
380405
381406 if is_dir {
407+ if check_device
408+ && should_skip_different_device_with_dev (
409+ parent_dev_id,
410+ entry_stat. st_dev ,
411+ options,
412+ & entry_path,
413+ )
414+ {
415+ error = true ;
416+ continue ;
417+ }
382418 // Ask user if they want to descend into this directory
383419 if options. interactive == InteractiveMode :: Always
384420 && !is_dir_empty ( & entry_path)
@@ -407,7 +443,16 @@ pub fn safe_remove_dir_recursive_impl(path: &Path, dir_fd: &DirFd, options: &Opt
407443 }
408444 } ;
409445
410- let child_error = safe_remove_dir_recursive_impl ( & entry_path, & child_dir_fd, options) ;
446+ let child_error = safe_remove_dir_recursive_impl (
447+ & entry_path,
448+ & child_dir_fd,
449+ options,
450+ if check_device {
451+ Some ( entry_stat. st_dev )
452+ } else {
453+ None
454+ } ,
455+ ) ;
411456 error = error || child_error;
412457
413458 // Ask user permission if needed for this subdirectory
0 commit comments