@@ -27,6 +27,7 @@ import ContainerizationOS
2727import Foundation
2828import GRPCCore
2929import GRPCProtobuf
30+ import LCShim
3031import Logging
3132import NIOCore
3233import NIOPosix
@@ -722,18 +723,95 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
722723 async throws -> Com_Apple_Containerization_Sandbox_V3_FilesystemOperationResponse
723724 {
724725 let path = FilePath ( request. path)
726+ if !request. hasContainerID {
727+ throw ContainerizationError (
728+ . invalidArgument,
729+ message: " containerID is required "
730+ )
731+ }
732+
733+ guard let operation = request. operation else {
734+ throw ContainerizationError (
735+ . invalidArgument,
736+ message: " operation is required "
737+ )
738+ }
739+
740+ let container = try await state. get ( container: request. containerID)
741+ guard let containerPid = await container. pid else {
742+ throw ContainerizationError (
743+ . invalidArgument,
744+ message: " container PID is not present "
745+ )
746+ }
725747
726748 log. debug (
727749 " filesystemOperation " ,
728750 metadata: [
729- " operation " : " \( String ( describing: request. operation) ) " ,
751+ " containerID " : " \( request. containerID) " ,
752+ " containerPid " : " \( containerPid) " ,
753+ " operation " : " \( operation) " ,
730754 " path " : " \( path) " ,
731755 ] )
732756
733757 if !path. isAbsolute {
734758 throw RPCError ( code: . invalidArgument, message: " path must be absolute " )
735759 }
736760
761+ let selfMountFd = open ( " /proc/self/ns/mnt " , O_RDONLY | O_CLOEXEC)
762+ if selfMountFd < 0 {
763+ let error = swiftErrno ( " open " )
764+ throw RPCError ( code: . internalError, message: " failed to open self mount namespace " , cause: error)
765+ }
766+
767+ defer { close ( selfMountFd) }
768+
769+ let containerMountFd = open ( " /proc/ \( containerPid) /ns/mnt " , O_RDONLY | O_CLOEXEC)
770+ if containerMountFd < 0 {
771+ let error = swiftErrno ( " open " )
772+ throw RPCError ( code: . internalError, message: " failed to open container mount namespace " , cause: error)
773+ }
774+
775+ defer { close ( containerMountFd) }
776+
777+ var finfo = _stat_struct ( )
778+ let selfMountStat = fstat ( selfMountFd, & finfo)
779+ if selfMountStat != 0 {
780+ let error = swiftErrno ( " fstat " )
781+ throw RPCError ( code: . internalError, message: " failed to stat self mount namespace " , cause: error)
782+ }
783+ let selfInode = finfo. st_ino
784+
785+ let containerMountStat = fstat ( containerMountFd, & finfo)
786+ if containerMountStat != 0 {
787+ let error = swiftErrno ( " fstat " )
788+ throw RPCError ( code: . internalError, message: " failed to stat container mount namespace " , cause: error)
789+ }
790+ let containerInode = finfo. st_ino
791+
792+ if selfInode == containerInode {
793+ try doFilesystemOperation ( path: path, operation: operation)
794+ } else {
795+ try await self . runOnDedicatedThread {
796+ if unshare ( CLONE_FS) != 0 {
797+ let error = self . swiftErrno ( " unshare(CLONE_FS) " )
798+ throw RPCError ( code: . internalError, message: " failed to unshare filesystem namespace " , cause: error)
799+ }
800+ if setns ( containerMountFd, CLONE_NEWNS) != 0 {
801+ let error = self . swiftErrno ( " setns(CLONE_NEWNS) " )
802+ throw RPCError ( code: . internalError, message: " failed to enter container mount namespace " , cause: error)
803+ }
804+ try self . doFilesystemOperation ( path: path, operation: operation)
805+ }
806+ }
807+
808+ return . init( )
809+ }
810+
811+ private func doFilesystemOperation(
812+ path: FilePath ,
813+ operation: Com_Apple_Containerization_Sandbox_V3_FilesystemOperationRequest . OneOf_Operation
814+ ) throws {
737815 var finfo = _stat_struct ( )
738816 let rc = _stat ( path. string, & finfo)
739817 if rc != 0 {
@@ -753,20 +831,18 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
753831 defer { close ( fd) }
754832
755833 do {
756- switch request . operation {
757- case . freeze:
834+ switch operation {
835+ case . freeze( _ ) :
758836 try freezeFilesystem ( fd: fd)
759- case . thaw:
837+ case . thaw( _ ) :
760838 try thawFilesystem ( fd: fd)
761839 case . trim( let params) :
762840 switch params. schedule {
763- case . oneShot:
841+ case . oneShot( _ ) :
764842 try trimFilesystem ( fd: fd)
765843 case . none:
766844 throw RPCError ( code: . invalidArgument, message: " trim schedule must be specified " )
767845 }
768- case . none:
769- throw RPCError ( code: . invalidArgument, message: " invalid operation " )
770846 }
771847 } catch {
772848 log. error (
@@ -776,8 +852,6 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
776852 ] )
777853 throw RPCError ( code: . internalError, message: " filesystemOperation " , cause: error)
778854 }
779-
780- return . init( )
781855 }
782856
783857 private func freezeFilesystem( fd: Int32 ) throws {
@@ -1650,6 +1724,22 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
16501724 return error
16511725 }
16521726
1727+ private func runOnDedicatedThread< T: Sendable > (
1728+ _ work: @escaping @Sendable ( ) throws -> T
1729+ ) async throws -> T {
1730+ try await withCheckedThrowingContinuation { continuation in
1731+ let thread = Thread {
1732+ do {
1733+ let result = try work ( )
1734+ continuation. resume ( returning: result)
1735+ } catch {
1736+ continuation. resume ( throwing: error)
1737+ }
1738+ }
1739+ thread. start ( )
1740+ }
1741+ }
1742+
16531743 // NOTE: This is just crummy. It works because today the assumption is
16541744 // every NIC in the root net namespace is for the container(s), but if we
16551745 // ever supported individual containers having their own NICs/IPs then this
0 commit comments