10
10
11
11
use cap_std:: fs:: { Dir , File , Metadata } ;
12
12
use cap_tempfile:: cap_std;
13
+ use rustix:: path:: Arg ;
13
14
use std:: ffi:: OsStr ;
14
15
use std:: io:: Result ;
15
16
use std:: io:: { self , Write } ;
@@ -36,6 +37,10 @@ pub trait CapStdExtDirExt {
36
37
#[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
37
38
fn open_dir_rooted_ext ( & self , path : impl AsRef < Path > ) -> Result < crate :: RootDir > ;
38
39
40
+ /// Open the target directory, but return Ok(None) if this would cross a mount point.
41
+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
42
+ fn open_dir_noxdev ( & self , path : impl AsRef < Path > ) -> Result < Option < Dir > > ;
43
+
39
44
/// Create the target directory, but do nothing if a directory already exists at that path.
40
45
/// The return value will be `true` if the directory was created. An error will be
41
46
/// returned if the path is a non-directory. Symbolic links will be followed.
@@ -308,6 +313,33 @@ fn subdir_of<'d, 'p>(d: &'d Dir, p: &'p Path) -> io::Result<(DirOwnedOrBorrowed<
308
313
Ok ( ( r, name) )
309
314
}
310
315
316
+ /// A thin wrapper for [`openat2`] but that retries on interruption.
317
+ fn openat2_with_retry (
318
+ dirfd : impl std:: os:: fd:: AsFd ,
319
+ path : impl AsRef < Path > ,
320
+ oflags : rustix:: fs:: OFlags ,
321
+ mode : rustix:: fs:: Mode ,
322
+ resolve : rustix:: fs:: ResolveFlags ,
323
+ ) -> rustix:: io:: Result < std:: os:: fd:: OwnedFd > {
324
+ let dirfd = dirfd. as_fd ( ) ;
325
+ let path = path. as_ref ( ) ;
326
+ // We loop forever on EAGAIN right now. The cap-std version loops just 4 times,
327
+ // which seems really arbitrary.
328
+ path. into_with_c_str ( |path_c_str| ' start: loop {
329
+ match rustix:: fs:: openat2 ( dirfd, path_c_str, oflags, mode, resolve) {
330
+ Ok ( file) => {
331
+ return Ok ( file) ;
332
+ }
333
+ Err ( rustix:: io:: Errno :: AGAIN | rustix:: io:: Errno :: INTR ) => {
334
+ continue ' start;
335
+ }
336
+ Err ( e) => {
337
+ return Err ( e) ;
338
+ }
339
+ }
340
+ } )
341
+ }
342
+
311
343
fn is_mountpoint_impl_statx ( root : & Dir , path : & Path ) -> Result < Option < bool > > {
312
344
// https://github.com/systemd/systemd/blob/8fbf0a214e2fe474655b17a4b663122943b55db0/src/basic/mountpoint-util.c#L176
313
345
use rustix:: fs:: { AtFlags , StatxFlags } ;
@@ -344,6 +376,23 @@ impl CapStdExtDirExt for Dir {
344
376
crate :: RootDir :: new ( self , path)
345
377
}
346
378
379
+ /// Open the target directory, but return Ok(None) if this would cross a mount point.
380
+ #[ cfg( any( target_os = "android" , target_os = "linux" ) ) ]
381
+ fn open_dir_noxdev ( & self , path : impl AsRef < std:: path:: Path > ) -> std:: io:: Result < Option < Dir > > {
382
+ use rustix:: fs:: { Mode , OFlags , ResolveFlags } ;
383
+ match openat2_with_retry (
384
+ self ,
385
+ path,
386
+ OFlags :: CLOEXEC | OFlags :: DIRECTORY | OFlags :: NOFOLLOW ,
387
+ Mode :: empty ( ) ,
388
+ ResolveFlags :: NO_XDEV | ResolveFlags :: BENEATH ,
389
+ ) {
390
+ Ok ( r) => Ok ( Some ( Dir :: reopen_dir ( & r) ?) ) ,
391
+ Err ( e) if e == rustix:: io:: Errno :: XDEV => Ok ( None ) ,
392
+ Err ( e) => Err ( e. into ( ) ) ,
393
+ }
394
+ }
395
+
347
396
fn ensure_dir_with (
348
397
& self ,
349
398
p : impl AsRef < Path > ,
0 commit comments