Skip to content

Commit 951846c

Browse files
saehejkangjglogan
andcommitted
update filesystem operation to run in new namespace
Co-authored-by: John Logan <john_logan@apple.com>
1 parent 5796abe commit 951846c

7 files changed

Lines changed: 108 additions & 8 deletions

File tree

Sources/Containerization/LinuxContainer.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,8 +1134,7 @@ extension LinuxContainer {
11341134
guard let vminitd = agent as? Vminitd else {
11351135
throw ContainerizationError(.unsupported, message: "filesystemOperation requires Vminitd agent")
11361136
}
1137-
let guestPath = URL(filePath: Self.guestRootfsPath(self.id)).appending(path: path).path
1138-
try await vminitd.filesystemOperation(operation: operation, path: guestPath)
1137+
try await vminitd.filesystemOperation(operation: operation, path: path, containerID: self.id)
11391138
}
11401139
}
11411140
}

Sources/Containerization/LinuxPod.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,8 +1285,7 @@ extension LinuxPod {
12851285
guard let vminitd = agent as? Vminitd else {
12861286
throw ContainerizationError(.unsupported, message: "filesystemOperation requires Vminitd agent")
12871287
}
1288-
let guestPath = URL(filePath: Self.guestRootfsPath(containerID)).appending(path: path).path
1289-
try await vminitd.filesystemOperation(operation: operation, path: guestPath)
1288+
try await vminitd.filesystemOperation(operation: operation, path: path, containerID: containerID)
12901289
}
12911290
}
12921291
}

Sources/Containerization/SandboxContext/SandboxContext.pb.swift

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1154,6 +1154,15 @@ public nonisolated struct Com_Apple_Containerization_Sandbox_V3_FilesystemOperat
11541154
set {operation = .thaw(newValue)}
11551155
}
11561156

1157+
public var containerID: String {
1158+
get {_containerID ?? String()}
1159+
set {_containerID = newValue}
1160+
}
1161+
/// Returns true if `containerID` has been explicitly set.
1162+
public var hasContainerID: Bool {self._containerID != nil}
1163+
/// Clears the value of `containerID`. Subsequent reads from it will return its default value.
1164+
public mutating func clearContainerID() {self._containerID = nil}
1165+
11571166
public var unknownFields = SwiftProtobuf.UnknownStorage()
11581167

11591168
public nonisolated enum OneOf_Operation: Equatable, Sendable {
@@ -1164,6 +1173,8 @@ public nonisolated struct Com_Apple_Containerization_Sandbox_V3_FilesystemOperat
11641173
}
11651174

11661175
public init() {}
1176+
1177+
fileprivate var _containerID: String? = nil
11671178
}
11681179

11691180
public nonisolated struct Com_Apple_Containerization_Sandbox_V3_FilesystemOperationResponse: Sendable {
@@ -3453,7 +3464,7 @@ nonisolated extension Com_Apple_Containerization_Sandbox_V3_FiTrimResult: SwiftP
34533464

34543465
nonisolated extension Com_Apple_Containerization_Sandbox_V3_FilesystemOperationRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
34553466
public static let protoMessageName: String = _protobuf_package + ".FilesystemOperationRequest"
3456-
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}path\0\u{1}trim\0\u{1}freeze\0\u{1}thaw\0")
3467+
public static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}path\0\u{1}trim\0\u{1}freeze\0\u{1}thaw\0\u{1}containerID\0")
34573468

34583469
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
34593470
while let fieldNumber = try decoder.nextFieldNumber() {
@@ -3501,6 +3512,7 @@ nonisolated extension Com_Apple_Containerization_Sandbox_V3_FilesystemOperationR
35013512
self.operation = .thaw(v)
35023513
}
35033514
}()
3515+
case 5: try { try decoder.decodeSingularStringField(value: &self._containerID) }()
35043516
default: break
35053517
}
35063518
}
@@ -3529,12 +3541,16 @@ nonisolated extension Com_Apple_Containerization_Sandbox_V3_FilesystemOperationR
35293541
}()
35303542
case nil: break
35313543
}
3544+
try { if let v = self._containerID {
3545+
try visitor.visitSingularStringField(value: v, fieldNumber: 5)
3546+
} }()
35323547
try unknownFields.traverse(visitor: &visitor)
35333548
}
35343549

35353550
public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_FilesystemOperationRequest, rhs: Com_Apple_Containerization_Sandbox_V3_FilesystemOperationRequest) -> Bool {
35363551
if lhs.path != rhs.path {return false}
35373552
if lhs.operation != rhs.operation {return false}
3553+
if lhs._containerID != rhs._containerID {return false}
35383554
if lhs.unknownFields != rhs.unknownFields {return false}
35393555
return true
35403556
}

Sources/Containerization/SandboxContext/SandboxContext.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ message FilesystemOperationRequest {
315315
FiFreezeParams freeze = 3;
316316
FiThawParams thaw = 4;
317317
}
318+
optional string containerID = 5;
318319
}
319320

320321
message FilesystemOperationResponse {

Sources/Containerization/VirtualMachineAgent.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public protocol VirtualMachineAgent: Sendable {
4141
/// Close any resources held by the agent.
4242
func close() async throws
4343
// Perform a filesystem operation on the given path.
44-
func filesystemOperation(operation: FilesystemOperation, path: String) async throws
44+
func filesystemOperation(operation: FilesystemOperation, path: String, containerID: String?) async throws
4545

4646
// POSIX-y
4747
func getenv(key: String) async throws -> String

Sources/Containerization/Vminitd.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,14 @@ extension Vminitd: VirtualMachineAgent {
213213
}
214214

215215
/// Perform a filesystem operation on a path inside the sandbox's environment.
216-
public func filesystemOperation(operation: FilesystemOperation, path: String) async throws {
216+
public func filesystemOperation(operation: FilesystemOperation, path: String, containerID: String? = nil) async throws {
217217
_ = try await client.filesystemOperation(
218218
.with {
219219
$0.operation = operation.toProtoOperation()
220220
$0.path = path
221+
if let containerID {
222+
$0.containerID = containerID
223+
}
221224
})
222225
}
223226

vminitd/Sources/VminitdCore/Server+GRPC.swift

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -722,10 +722,21 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
722722
async throws -> Com_Apple_Containerization_Sandbox_V3_FilesystemOperationResponse
723723
{
724724
let path = FilePath(request.path)
725+
if !request.hasContainerID {
726+
throw ContainerizationError(
727+
.invalidArgument,
728+
message: "containerID is required"
729+
)
730+
}
731+
732+
let container = try await state.get(container: request.containerID)
733+
let containerPid = container.pid
725734

726735
log.debug(
727736
"filesystemOperation",
728737
metadata: [
738+
"containerID": "\(request.containerID)",
739+
"containerPid": "\(containerPid)",
729740
"operation": "\(String(describing: request.operation))",
730741
"path": "\(path)",
731742
])
@@ -734,6 +745,61 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
734745
throw RPCError(code: .invalidArgument, message: "path must be absolute")
735746
}
736747

748+
let selfMountFd = open("/proc/self/ns/mount", O_RDONLY | O_DIRECTORY | O_CLOEXEC)
749+
if selfMountFd < 0 {
750+
let error = swiftErrno("open")
751+
throw RPCError(code: .internalError, message: "failed to open self mount namespace", cause: error)
752+
}
753+
754+
defer {
755+
close(selfMountFd)
756+
}
757+
758+
let containerMountFd = open("/proc/\(containerPid)/ns/mount", O_RDONLY | O_DIRECTORY | O_CLOEXEC)
759+
if containerMountFd < 0 {
760+
let error = swiftErrno("open")
761+
throw RPCError(code: .internalError, message: "failed to open container mount namespace", cause: error)
762+
}
763+
764+
defer {
765+
close(containerMountFd)
766+
}
767+
768+
var finfo = _stat_struct()
769+
let selfMountStat = fstat(selfMountFd, &finfo)
770+
if selfMountStat != 0 {
771+
let error = swiftErrno("fstat")
772+
throw RPCError(code: .internalError, message: "failed to stat self mount namespace", cause: error)
773+
}
774+
let selfInode = finfo.st_ino
775+
776+
let containerMountStat = fstat(containerMountFd, &finfo)
777+
if containerMountStat != 0 {
778+
let error = swiftErrno("fstat")
779+
throw RPCError(code: .internalError, message: "failed to stat container mount namespace", cause: error)
780+
}
781+
let containerInode = finfo.st_ino
782+
783+
if selfInode == containerInode {
784+
try doFilesystemOperation(path: path, operation: request.operation)
785+
} else {
786+
try await self.runOnDedicatedThread {
787+
if unshare(CLONE_FS) != 0 {
788+
let error = swiftErrno("unshare(CLONE_FS)")
789+
throw RPCError(code: .internalError, message: "failed to unshare filesystem namespace", cause: error)
790+
}
791+
if setns(containerMountFd, CLONE_NEWNS) != 0 {
792+
let error = swiftErrno("setns(CLONE_NEWNS)")
793+
throw RPCError(code: .internalError, message: "failed to enter container mount namespace", cause: error)
794+
}
795+
try doFilesystemOperation(path: path, operation: request.operation)
796+
}
797+
}
798+
799+
return .init()
800+
}
801+
802+
private func doFilesystemOperation(path: FilePath, operation: Com_Apple_Containerization_Sandbox_V3_FilesystemOperationRequest.Operation) throws {
737803
var finfo = _stat_struct()
738804
let rc = _stat(path.string, &finfo)
739805
if rc != 0 {
@@ -753,7 +819,7 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
753819
defer { close(fd) }
754820

755821
do {
756-
switch request.operation {
822+
switch operation {
757823
case .freeze:
758824
try freezeFilesystem(fd: fd)
759825
case .thaw:
@@ -1650,6 +1716,22 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContext.SimpleServ
16501716
return error
16511717
}
16521718

1719+
private func runOnDedicatedThread<T: Sendable>(
1720+
_ work: @escaping () throws -> T
1721+
) async throws -> T {
1722+
try await withCheckedThrowingContinuation { continuation in
1723+
let thread = Thread {
1724+
do {
1725+
let result = try work()
1726+
continuation.resume(returning: result)
1727+
} catch {
1728+
continuation.resume(throwing: error)
1729+
}
1730+
}
1731+
thread.start()
1732+
}
1733+
}
1734+
16531735
// NOTE: This is just crummy. It works because today the assumption is
16541736
// every NIC in the root net namespace is for the container(s), but if we
16551737
// ever supported individual containers having their own NICs/IPs then this

0 commit comments

Comments
 (0)