@@ -16,6 +16,7 @@ use linux_syscall::{
16
16
SYS_fsopen ,
17
17
SYS_mount_setattr ,
18
18
SYS_move_mount ,
19
+ SYS_open_tree ,
19
20
} ;
20
21
21
22
use super :: runtime;
@@ -36,6 +37,8 @@ const FSMOUNT_CLOEXEC: u32 = 0x00000001;
36
37
const MOUNT_ATTR_RDONLY : u32 = 0x00000001 ;
37
38
const MOUNT_ATTR_SIZE_VER0 : u32 = 32 ;
38
39
const MOVE_MOUNT_F_EMPTY_PATH : u32 = 0x00000004 ;
40
+ const OPEN_TREE_CLONE : u32 = 1 ;
41
+ const OPEN_TREE_CLOEXEC : u32 = 0o2000000 ;
39
42
40
43
// Linux fcntl constants from /usr/include/fcntl.h.
41
44
const AT_EMPTY_PATH : u32 = 0x1000 ;
@@ -467,87 +470,6 @@ where
467
470
rt. ensure_required_directories ( ) . await
468
471
}
469
472
470
- async fn mount_live_layers ( & self , rt : & runtime:: Runtime ) -> Result < ( ) > {
471
- // Mounts the bind mounts from the any live layers in the runtime the top of paths
472
- // inside /spfs
473
- //
474
- // It requires the mount destinations to exist under
475
- // /spfs/. If they do not, the mount commands will error. The
476
- // mount destinations are either provided by one of the layers
477
- // in the runtime, or by an earlier call to
478
- // ensure_extra_bind_mount_locations_exist() made in
479
- // initialize_runtime()
480
- let live_layers = rt. live_layers ( ) ;
481
- if !live_layers. is_empty ( ) {
482
- tracing:: debug!( "mounting the extra bind mounts over the {SPFS_DIR} filesystem ..." ) ;
483
- let mount = super :: resolve:: which ( "mount" ) . unwrap_or_else ( || "/usr/bin/mount" . into ( ) ) ;
484
-
485
- for layer in live_layers {
486
- let injection_mounts = layer. bind_mounts ( ) ;
487
-
488
- for extra_mount in injection_mounts {
489
- let dest = if extra_mount. dest . starts_with ( SPFS_DIR_PREFIX ) {
490
- PathBuf :: from ( extra_mount. dest . clone ( ) )
491
- } else {
492
- PathBuf :: from ( SPFS_DIR ) . join ( extra_mount. dest . clone ( ) )
493
- } ;
494
-
495
- let mut cmd = tokio:: process:: Command :: new ( mount. clone ( ) ) ;
496
- cmd. arg ( "--bind" ) ;
497
- cmd. arg ( extra_mount. src . to_string_lossy ( ) . into_owned ( ) ) ;
498
- cmd. arg ( dest) ;
499
- tracing:: debug!( "About to run: {cmd:?}" ) ;
500
-
501
- match cmd. status ( ) . await {
502
- Err ( err) => {
503
- return Err ( Error :: process_spawn_error ( "mount" . to_owned ( ) , err, None ) )
504
- }
505
- Ok ( status) => match status. code ( ) {
506
- Some ( 0 ) => ( ) ,
507
- _ => {
508
- return Err ( format ! (
509
- "Failed to inject bind mount into the {SPFS_DIR} filesystem using: {cmd:?}"
510
- ) . into ( ) )
511
- }
512
- } ,
513
- }
514
- }
515
- }
516
- }
517
- Ok ( ( ) )
518
- }
519
-
520
- async fn unmount_live_layers ( & self , rt : & runtime:: Runtime ) -> Result < ( ) > {
521
- // Unmount the bind mounted items from the live layers
522
- let live_layers = rt. live_layers ( ) ;
523
- if !live_layers. is_empty ( ) {
524
- tracing:: debug!( "unmounting the extra bind mounts from the {SPFS_DIR} filesystem ..." ) ;
525
- let umount =
526
- super :: resolve:: which ( "umount" ) . unwrap_or_else ( || "/usr/bin/umount" . into ( ) ) ;
527
-
528
- for layer in live_layers {
529
- let injection_mounts = layer. bind_mounts ( ) ;
530
-
531
- for extra_mount in injection_mounts {
532
- let mut cmd = tokio:: process:: Command :: new ( umount. clone ( ) ) ;
533
- cmd. arg ( PathBuf :: from ( SPFS_DIR ) . join ( extra_mount. dest . clone ( ) ) ) ;
534
- tracing:: debug!( "About to run: {cmd:?}" ) ;
535
-
536
- match cmd. status ( ) . await {
537
- Err ( err) => {
538
- return Err ( Error :: process_spawn_error ( "umount" . to_owned ( ) , err, None ) )
539
- }
540
- Ok ( status) => match status. code ( ) {
541
- Some ( 0 ) => ( ) ,
542
- _ => return Err ( format ! ( "Failed to unmount a bind mount injected into the {SPFS_DIR} filesystem using: {cmd:?}" ) . into ( ) ) ,
543
- } ,
544
- }
545
- }
546
- }
547
- }
548
- Ok ( ( ) )
549
- }
550
-
551
473
/// Mounts an overlayfs built up from the given list of rendered
552
474
/// layered directories (layer_dirs).
553
475
///
@@ -569,7 +491,7 @@ where
569
491
} else {
570
492
mount_overlayfs_command ( rt, layer_dirs) . await ?;
571
493
}
572
- self . mount_live_layers ( rt) . await
494
+ mount_live_layers ( rt) . await
573
495
}
574
496
575
497
#[ cfg( feature = "fuse-backend" ) ]
@@ -580,7 +502,7 @@ where
580
502
#[ cfg( feature = "fuse-backend" ) ]
581
503
pub ( crate ) async fn mount_env_fuse ( & self , rt : & runtime:: Runtime ) -> Result < ( ) > {
582
504
self . mount_fuse_onto ( rt, SPFS_DIR ) . await ?;
583
- self . mount_live_layers ( rt) . await
505
+ mount_live_layers ( rt) . await
584
506
}
585
507
586
508
#[ cfg( feature = "fuse-backend" ) ]
@@ -900,7 +822,7 @@ where
900
822
// Unmount any extra paths mounted in the depths of
901
823
// the fuse-only backend before fuse itself is
902
824
// unmounted to avoid issue with lazy unmounting.
903
- self . unmount_live_layers ( rt) . await ?;
825
+ unmount_live_layers ( rt) . await ?;
904
826
std:: path:: Path :: new ( SPFS_DIR )
905
827
}
906
828
runtime:: MountBackend :: OverlayFsWithRenders | runtime:: MountBackend :: WinFsp => {
@@ -1229,7 +1151,6 @@ pub fn option_to_string(option: &fuser::MountOption) -> String {
1229
1151
}
1230
1152
}
1231
1153
1232
-
1233
1154
/// Mount overlayfs layers using the mount command.
1234
1155
async fn mount_overlayfs_command < P : AsRef < Path > > (
1235
1156
rt : & runtime:: Runtime ,
@@ -1537,6 +1458,198 @@ fn mount_overlayfs_syscalls<P: AsRef<Path>>(rt: &runtime::Runtime, layer_dirs: &
1537
1458
Ok ( ( ) )
1538
1459
}
1539
1460
1461
+ /// Mounts the bind mounts from the any live layers in the runtime the top of paths inside /spfs.
1462
+ async fn mount_live_layers ( rt : & runtime:: Runtime ) -> Result < ( ) > {
1463
+ // This requires the mount destinations to exist under
1464
+ // /spfs/. If they do not, the mount commands will error. The
1465
+ // mount destinations are either provided by one of the layers
1466
+ // in the runtime, or by an earlier call to
1467
+ // ensure_extra_bind_mount_locations_exist() made in
1468
+ // initialize_runtime()
1469
+ let live_layers = rt. live_layers ( ) ;
1470
+ if !live_layers. is_empty ( ) {
1471
+ let spfs_config = crate :: Config :: current ( ) ?;
1472
+ if spfs_config. filesystem . use_mount_syscalls {
1473
+ mount_live_layers_syscalls ( live_layers) ?;
1474
+ } else {
1475
+ mount_live_layers_command ( live_layers) . await ?;
1476
+ }
1477
+ }
1478
+
1479
+ Ok ( ( ) )
1480
+ }
1481
+
1482
+ /// Bind-mount live layers using the "mount" command.
1483
+ async fn mount_live_layers_command ( live_layers : & Vec < runtime:: LiveLayer > ) -> Result < ( ) > {
1484
+ tracing:: debug!(
1485
+ "mounting extra bind mounts over the {SPFS_DIR} filesystem using the mount command"
1486
+ ) ;
1487
+ let mount = super :: resolve:: which ( "mount" ) . unwrap_or_else ( || "/usr/bin/mount" . into ( ) ) ;
1488
+
1489
+ for layer in live_layers {
1490
+ let injection_mounts = layer. bind_mounts ( ) ;
1491
+
1492
+ for extra_mount in injection_mounts {
1493
+ let dest = if extra_mount. dest . starts_with ( SPFS_DIR_PREFIX ) {
1494
+ PathBuf :: from ( extra_mount. dest . clone ( ) )
1495
+ } else {
1496
+ PathBuf :: from ( SPFS_DIR ) . join ( extra_mount. dest . clone ( ) )
1497
+ } ;
1498
+
1499
+ let mut cmd = tokio:: process:: Command :: new ( mount. clone ( ) ) ;
1500
+ cmd. arg ( "--bind" ) ;
1501
+ cmd. arg ( extra_mount. src . to_string_lossy ( ) . into_owned ( ) ) ;
1502
+ cmd. arg ( dest) ;
1503
+ tracing:: debug!( "About to run: {cmd:?}" ) ;
1504
+
1505
+ match cmd. status ( ) . await {
1506
+ Err ( err) => return Err ( Error :: process_spawn_error ( "mount" . to_owned ( ) , err, None ) ) ,
1507
+ Ok ( status) => match status. code ( ) {
1508
+ Some ( 0 ) => ( ) ,
1509
+ _ => {
1510
+ return Err ( format ! (
1511
+ "Failed to inject bind mount into the {SPFS_DIR} filesystem using: {cmd:?}"
1512
+ )
1513
+ . into ( ) )
1514
+ }
1515
+ } ,
1516
+ }
1517
+ }
1518
+ }
1519
+
1520
+ Ok ( ( ) )
1521
+ }
1522
+
1523
+ /// Bind-mount live layers using mount syscalls.
1524
+ fn mount_live_layers_syscalls ( live_layers : & Vec < runtime:: LiveLayer > ) -> Result < ( ) > {
1525
+ tracing:: debug!(
1526
+ "mounting extra bind mounts over the {SPFS_DIR} filesystem using mount syscalls"
1527
+ ) ;
1528
+
1529
+ for layer in live_layers {
1530
+ let injection_mounts = layer. bind_mounts ( ) ;
1531
+
1532
+ for extra_mount in injection_mounts {
1533
+ let Ok ( src) = extra_mount. src . canonicalize ( ) else {
1534
+ return Err ( format ! ( "unable to canonicalize {:?}" , extra_mount. src) . into ( ) ) ;
1535
+ } ;
1536
+ let dest = if extra_mount. dest . starts_with ( SPFS_DIR_PREFIX ) {
1537
+ PathBuf :: from ( extra_mount. dest . clone ( ) )
1538
+ } else {
1539
+ PathBuf :: from ( SPFS_DIR ) . join ( extra_mount. dest . clone ( ) )
1540
+ } ;
1541
+ let src_path = CString :: new ( src. to_string_lossy ( ) . as_ref ( ) )
1542
+ . expect ( "allocate CString for live layer bind source" ) ;
1543
+ let dest_path = CString :: new ( dest. to_string_lossy ( ) . as_ref ( ) )
1544
+ . expect ( "allocate CString for live layer destination" ) ;
1545
+
1546
+ // mount_fd = open_tree(AT_FDCWD, "<src>", OPEN_TREE_CLONE|OPEN_TREE_CLOEXEC)
1547
+ let mount_fd = unsafe {
1548
+ syscall ! (
1549
+ SYS_open_tree ,
1550
+ AT_FDCWD ,
1551
+ src_path. as_ptr( ) ,
1552
+ OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC
1553
+ )
1554
+ }
1555
+ . as_u64_unchecked ( ) as i32 ;
1556
+ if mount_fd < 0 {
1557
+ return Err ( format ! ( "mount_live_layers_syscalls::SYS_open_tree(AT_FDCWD, {:?}, OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC) error: {}" ,
1558
+ src_path, mount_fd) . into ( ) ) ;
1559
+ }
1560
+
1561
+ // move_mount(mount_fd, "", AT_FDCWD, "<dest>", MOVE_MOUNT_F_EMPTY_PATH)
1562
+ let rc = unsafe {
1563
+ syscall ! (
1564
+ SYS_move_mount ,
1565
+ mount_fd,
1566
+ c"" . as_ptr( ) ,
1567
+ AT_FDCWD ,
1568
+ dest_path. as_ptr( ) ,
1569
+ MOVE_MOUNT_F_EMPTY_PATH
1570
+ )
1571
+ }
1572
+ . as_u64_unchecked ( ) as i32 ;
1573
+ if rc != 0 {
1574
+ return Err ( format ! (
1575
+ "mount_overlayfs_syscalls::SYS_move_mount({}, \" \" , AT_FDCWD, {:?}, MOVE_MOUNT_F_EMPTY_PATH) error: {}" ,
1576
+ mount_fd, dest_path, rc
1577
+ )
1578
+ . into ( ) ) ;
1579
+ }
1580
+ nix:: unistd:: close ( mount_fd as std:: ffi:: c_int ) ?;
1581
+ }
1582
+ }
1583
+
1584
+ Ok ( ( ) )
1585
+ }
1586
+
1587
+ /// Unmount the bind mounted items from the live layers
1588
+ async fn unmount_live_layers ( rt : & runtime:: Runtime ) -> Result < ( ) > {
1589
+ let live_layers = rt. live_layers ( ) ;
1590
+ if !live_layers. is_empty ( ) {
1591
+ let spfs_config = crate :: Config :: current ( ) ?;
1592
+ if spfs_config. filesystem . use_mount_syscalls {
1593
+ unmount_live_layers_syscalls ( live_layers) ?;
1594
+ } else {
1595
+ unmount_live_layers_command ( live_layers) . await ?;
1596
+ }
1597
+ }
1598
+
1599
+ Ok ( ( ) )
1600
+ }
1601
+
1602
+ /// Unmount live layers using the "umount" command.
1603
+ async fn unmount_live_layers_command ( live_layers : & Vec < runtime:: LiveLayer > ) -> Result < ( ) > {
1604
+ tracing:: debug!(
1605
+ "unmounting the extra bind mounts from the {SPFS_DIR} filesystem using the umount command ..."
1606
+ ) ;
1607
+ let umount = super :: resolve:: which ( "umount" ) . unwrap_or_else ( || "/usr/bin/umount" . into ( ) ) ;
1608
+ for layer in live_layers {
1609
+ let injection_mounts = layer. bind_mounts ( ) ;
1610
+ for extra_mount in injection_mounts {
1611
+ let mut cmd = tokio:: process:: Command :: new ( umount. clone ( ) ) ;
1612
+ cmd. arg ( PathBuf :: from ( SPFS_DIR ) . join ( extra_mount. dest . clone ( ) ) ) ;
1613
+ tracing:: debug!( "About to run: {cmd:?}" ) ;
1614
+ match cmd. status ( ) . await {
1615
+ Err ( err) => {
1616
+ return Err ( Error :: process_spawn_error ( "umount" . to_owned ( ) , err, None ) )
1617
+ }
1618
+ Ok ( status) => match status. code ( ) {
1619
+ Some ( 0 ) => ( ) ,
1620
+ _ => return Err ( format ! ( "Failed to unmount a bind mount injected into the {SPFS_DIR} filesystem using: {cmd:?}" ) . into ( ) ) ,
1621
+ } ,
1622
+ }
1623
+ }
1624
+ }
1625
+
1626
+ Ok ( ( ) )
1627
+ }
1628
+
1629
+ /// Unmount live layers using umount syscalls.
1630
+ fn unmount_live_layers_syscalls ( live_layers : & Vec < runtime:: LiveLayer > ) -> Result < ( ) > {
1631
+ tracing:: debug!(
1632
+ "unmounting the extra bind mounts from the {SPFS_DIR} filesystem using syscalls ..."
1633
+ ) ;
1634
+ for layer in live_layers {
1635
+ let injection_mounts = layer. bind_mounts ( ) ;
1636
+ for extra_mount in injection_mounts {
1637
+ let dest = if extra_mount. dest . starts_with ( SPFS_DIR_PREFIX ) {
1638
+ PathBuf :: from ( extra_mount. dest . clone ( ) )
1639
+ } else {
1640
+ PathBuf :: from ( SPFS_DIR ) . join ( extra_mount. dest . clone ( ) )
1641
+ } ;
1642
+ let flags = nix:: mount:: MntFlags :: empty ( ) ;
1643
+ let result = nix:: mount:: umount2 ( & dest, flags) ;
1644
+ if let Err ( err) = result {
1645
+ return Err ( Error :: wrap_nix ( err, format ! ( "Failed to unmount {dest:?}" ) ) ) ;
1646
+ }
1647
+ }
1648
+ }
1649
+
1650
+ Ok ( ( ) )
1651
+ }
1652
+
1540
1653
/// Prevent a structure from being [`Send`].
1541
1654
struct NotSendMarker ( std:: marker:: PhantomData < * mut u8 > ) ;
1542
1655
0 commit comments