Agent: Add and implement writeFile (#259)

This change adds in a new rpc that we probably should swap to for the
underlying implementation of a couple rpcs we currently have. This new
rpc simply writes to an existing or new file some data. This is useful
for guest (and container) setup for things like /etc/hosts,
/etc/resolv.conf, writing to one-off cgroup files and so on.

Currently I've just implemented subtree_control toggling for the root cg
in standardSetup with this new change, but in the future I'd like to
swap the implementations of our /etc/hosts and /etc/resolv.conf logic
with this.
This commit is contained in:
Danny Canter authored and GitHub committed 2025-08-11 12:33:30 -04:00
1 parent 7b00f39140
commit 87541cbe65
6 files changed
+380 -1

No files matched your search

@@ -64,6 +64,29 @@ extension Vminitd: VirtualMachineAgent {
for mount in mounts {
try await self.mount(mount)
}
// Setup root cg subtree_control.
let data = "+memory +pids +io +cpu +cpuset".data(using: .utf8)!
try await writeFile(
path: "/sys/fs/cgroup/cgroup.subtree_control",
data: data,
flags: .init(),
mode: 0
)
}
public func writeFile(path: String, data: Data, flags: WriteFileFlags, mode: UInt32) async throws {
_ = try await client.writeFile(
.with {
$0.path = path
$0.mode = mode
$0.data = data
$0.flags = .with {
$0.append = flags.append
$0.createIfMissing = flags.create
$0.createParentDirs = flags.createParentDirectories
}
})
}
/// Mount a filesystem in the sandbox's environment.
@@ -74,6 +74,11 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtoc
callOptions: CallOptions?
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>
func writeFile(
_ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,
callOptions: CallOptions?
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>
func createProcess(
_ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,
callOptions: CallOptions?
@@ -309,6 +314,24 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextClientProtocol {
)
}
/// Write data to an existing or new file.
///
/// - Parameters:
/// - request: Request to send to WriteFile.
/// - callOptions: Call options.
/// - Returns: A `UnaryCall` with futures for the metadata, status and response.
public func writeFile(
_ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,
callOptions: CallOptions? = nil
) -> UnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse> {
return self.makeUnaryCall(
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeWriteFileInterceptors() ?? []
)
}
/// Create a new process inside the container.
///
/// - Parameters:
@@ -720,6 +743,11 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientP
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>
func makeWriteFileCall(
_ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,
callOptions: CallOptions?
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>
func makeCreateProcessCall(
_ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,
callOptions: CallOptions?
@@ -912,6 +940,18 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtoco
)
}
public func makeWriteFileCall(
_ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,
callOptions: CallOptions? = nil
) -> GRPCAsyncUnaryCall<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse> {
return self.makeAsyncUnaryCall(
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeWriteFileInterceptors() ?? []
)
}
public func makeCreateProcessCall(
_ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,
callOptions: CallOptions? = nil
@@ -1215,6 +1255,18 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncClientProtoco
)
}
public func writeFile(
_ request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,
callOptions: CallOptions? = nil
) async throws -> Com_Apple_Containerization_Sandbox_V3_WriteFileResponse {
return try await self.performAsyncUnaryCall(
path: Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile.path,
request: request,
callOptions: callOptions ?? self.defaultCallOptions,
interceptors: self.interceptors?.makeWriteFileInterceptors() ?? []
)
}
public func createProcess(
_ request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,
callOptions: CallOptions? = nil
@@ -1463,6 +1515,9 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextClientInterc
/// - Returns: Interceptors to use when invoking 'setupEmulator'.
func makeSetupEmulatorInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>]
/// - Returns: Interceptors to use when invoking 'writeFile'.
func makeWriteFileInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>]
/// - Returns: Interceptors to use when invoking 'createProcess'.
func makeCreateProcessInterceptors() -> [ClientInterceptor<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>]
@@ -1528,6 +1583,7 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.sysctl,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setTime,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.setupEmulator,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.writeFile,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.createProcess,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.deleteProcess,
Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata.Methods.startProcess,
@@ -1597,6 +1653,12 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextClientMetadata {
type: GRPCCallType.unary
)
public static let writeFile = GRPCMethodDescriptor(
name: "WriteFile",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/WriteFile",
type: GRPCCallType.unary
)
public static let createProcess = GRPCMethodDescriptor(
name: "CreateProcess",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/CreateProcess",
@@ -1731,6 +1793,9 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider: Ca
/// Set up an emulator in the guest for a specific binary format.
func setupEmulator(request: Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>
/// Write data to an existing or new file.
func writeFile(request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>
/// Create a new process inside the container.
func createProcess(request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>
@@ -1868,6 +1933,15 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextProvider {
userFunction: self.setupEmulator(request:context:)
)
case "WriteFile":
return UnaryServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>(),
interceptors: self.interceptors?.makeWriteFileInterceptors() ?? [],
userFunction: self.writeFile(request:context:)
)
case "CreateProcess":
return UnaryServerHandler(
context: context,
@@ -2083,6 +2157,12 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvide
context: GRPCAsyncServerCallContext
) async throws -> Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse
/// Write data to an existing or new file.
func writeFile(
request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest,
context: GRPCAsyncServerCallContext
) async throws -> Com_Apple_Containerization_Sandbox_V3_WriteFileResponse
/// Create a new process inside the container.
func createProcess(
request: Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest,
@@ -2278,6 +2358,15 @@ extension Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvider {
wrapping: { try await self.setupEmulator(request: $0, context: $1) }
)
case "WriteFile":
return GRPCAsyncServerHandler(
context: context,
requestDeserializer: ProtobufDeserializer<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest>(),
responseSerializer: ProtobufSerializer<Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>(),
interceptors: self.interceptors?.makeWriteFileInterceptors() ?? [],
wrapping: { try await self.writeFile(request: $0, context: $1) }
)
case "CreateProcess":
return GRPCAsyncServerHandler(
context: context,
@@ -2471,6 +2560,10 @@ public protocol Com_Apple_Containerization_Sandbox_V3_SandboxContextServerInterc
/// Defaults to calling `self.makeInterceptors()`.
func makeSetupEmulatorInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_SetupEmulatorRequest, Com_Apple_Containerization_Sandbox_V3_SetupEmulatorResponse>]
/// - Returns: Interceptors to use when handling 'writeFile'.
/// Defaults to calling `self.makeInterceptors()`.
func makeWriteFileInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, Com_Apple_Containerization_Sandbox_V3_WriteFileResponse>]
/// - Returns: Interceptors to use when handling 'createProcess'.
/// Defaults to calling `self.makeInterceptors()`.
func makeCreateProcessInterceptors() -> [ServerInterceptor<Com_Apple_Containerization_Sandbox_V3_CreateProcessRequest, Com_Apple_Containerization_Sandbox_V3_CreateProcessResponse>]
@@ -2553,6 +2646,7 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.sysctl,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.setTime,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.setupEmulator,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.writeFile,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.createProcess,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.deleteProcess,
Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata.Methods.startProcess,
@@ -2622,6 +2716,12 @@ public enum Com_Apple_Containerization_Sandbox_V3_SandboxContextServerMetadata {
type: GRPCCallType.unary
)
public static let writeFile = GRPCMethodDescriptor(
name: "WriteFile",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/WriteFile",
type: GRPCCallType.unary
)
public static let createProcess = GRPCMethodDescriptor(
name: "CreateProcess",
path: "/com.apple.containerization.sandbox.v3.SandboxContext/CreateProcess",
@@ -682,6 +682,59 @@ public struct Com_Apple_Containerization_Sandbox_V3_MkdirResponse: Sendable {
public init() {}
}
public struct Com_Apple_Containerization_Sandbox_V3_WriteFileRequest: @unchecked Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var path: String = String()
public var data: Data = Data()
public var mode: UInt32 = 0
public var flags: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags {
get {return _flags ?? Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags()}
set {_flags = newValue}
}
/// Returns true if `flags` has been explicitly set.
public var hasFlags: Bool {return self._flags != nil}
/// Clears the value of `flags`. Subsequent reads from it will return its default value.
public mutating func clearFlags() {self._flags = nil}
public var unknownFields = SwiftProtobuf.UnknownStorage()
public struct WriteFileFlags: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var createParentDirs: Bool = false
public var append: Bool = false
public var createIfMissing: Bool = false
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public init() {}
fileprivate var _flags: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags? = nil
}
public struct Com_Apple_Containerization_Sandbox_V3_WriteFileResponse: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
public var unknownFields = SwiftProtobuf.UnknownStorage()
public init() {}
}
public struct Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: Sendable {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
@@ -2152,6 +2205,123 @@ extension Com_Apple_Containerization_Sandbox_V3_MkdirResponse: SwiftProtobuf.Mes
}
}
extension Com_Apple_Containerization_Sandbox_V3_WriteFileRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".WriteFileRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "path"),
2: .same(proto: "data"),
3: .same(proto: "mode"),
4: .same(proto: "flags"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularStringField(value: &self.path) }()
case 2: try { try decoder.decodeSingularBytesField(value: &self.data) }()
case 3: try { try decoder.decodeSingularUInt32Field(value: &self.mode) }()
case 4: try { try decoder.decodeSingularMessageField(value: &self._flags) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if !self.path.isEmpty {
try visitor.visitSingularStringField(value: self.path, fieldNumber: 1)
}
if !self.data.isEmpty {
try visitor.visitSingularBytesField(value: self.data, fieldNumber: 2)
}
if self.mode != 0 {
try visitor.visitSingularUInt32Field(value: self.mode, fieldNumber: 3)
}
try { if let v = self._flags {
try visitor.visitSingularMessageField(value: v, fieldNumber: 4)
} }()
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, rhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest) -> Bool {
if lhs.path != rhs.path {return false}
if lhs.data != rhs.data {return false}
if lhs.mode != rhs.mode {return false}
if lhs._flags != rhs._flags {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.protoMessageName + ".WriteFileFlags"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .standard(proto: "create_parent_dirs"),
2: .same(proto: "append"),
3: .standard(proto: "create_if_missing"),
]
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularBoolField(value: &self.createParentDirs) }()
case 2: try { try decoder.decodeSingularBoolField(value: &self.append) }()
case 3: try { try decoder.decodeSingularBoolField(value: &self.createIfMissing) }()
default: break
}
}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.createParentDirs != false {
try visitor.visitSingularBoolField(value: self.createParentDirs, fieldNumber: 1)
}
if self.append != false {
try visitor.visitSingularBoolField(value: self.append, fieldNumber: 2)
}
if self.createIfMissing != false {
try visitor.visitSingularBoolField(value: self.createIfMissing, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags, rhs: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest.WriteFileFlags) -> Bool {
if lhs.createParentDirs != rhs.createParentDirs {return false}
if lhs.append != rhs.append {return false}
if lhs.createIfMissing != rhs.createIfMissing {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Containerization_Sandbox_V3_WriteFileResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".WriteFileResponse"
public static let _protobuf_nameMap = SwiftProtobuf._NameMap()
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
// Load everything into unknown fields
while try decoder.nextFieldNumber() != nil {}
}
public func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
try unknownFields.traverse(visitor: &visitor)
}
public static func ==(lhs: Com_Apple_Containerization_Sandbox_V3_WriteFileResponse, rhs: Com_Apple_Containerization_Sandbox_V3_WriteFileResponse) -> Bool {
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension Com_Apple_Containerization_Sandbox_V3_IpLinkSetRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
public static let protoMessageName: String = _protobuf_package + ".IpLinkSetRequest"
public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
@@ -20,6 +20,8 @@ service SandboxContext {
rpc SetTime(SetTimeRequest) returns (SetTimeResponse);
// Set up an emulator in the guest for a specific binary format.
rpc SetupEmulator(SetupEmulatorRequest) returns (SetupEmulatorResponse);
// Write data to an existing or new file.
rpc WriteFile(WriteFileRequest) returns (WriteFileResponse);
// Create a new process inside the container.
rpc CreateProcess(CreateProcessRequest) returns (CreateProcessResponse);
@@ -201,6 +203,20 @@ message MkdirRequest {
message MkdirResponse {}
message WriteFileRequest {
message WriteFileFlags {
bool create_parent_dirs = 1;
bool append = 2;
bool create_if_missing = 3;
}
string path = 1;
bytes data = 2;
uint32 mode = 3;
WriteFileFlags flags = 4;
}
message WriteFileResponse {}
message IpLinkSetRequest {
string interface = 1;
bool up = 2;
@@ -18,6 +18,12 @@ import ContainerizationError
import ContainerizationOCI
import Foundation
public struct WriteFileFlags {
public var createParentDirectories = false
public var append = false
public var create = false
}
/// A protocol for the agent running inside a virtual machine. If an operation isn't
/// supported the implementation MUST return a ContainerizationError with a code of
/// `.unsupported`.
@@ -28,7 +34,7 @@ public protocol VirtualMachineAgent: Sendable {
/// Close any resources held by the agent.
func close() async throws
// POSIX
// POSIX-y
func getenv(key: String) async throws -> String
func setenv(key: String, value: String) async throws
func mount(_ mount: ContainerizationOCI.Mount) async throws
@@ -36,6 +42,7 @@ public protocol VirtualMachineAgent: Sendable {
func mkdir(path: String, all: Bool, perms: UInt32) async throws
@discardableResult
func kill(pid: Int32, signal: Int32) async throws -> Int32
func writeFile(path: String, data: Data, flags: WriteFileFlags, mode: UInt32) async throws
// Process lifecycle
func createProcess(
@@ -71,4 +78,8 @@ extension VirtualMachineAgent {
public func configureHosts(config: Hosts, location: String) async throws {
throw ContainerizationError(.unsupported, message: "configureHosts")
}
public func writeFile(path: String, data: Data, flags: WriteFileFlags, mode: UInt32) async throws {
throw ContainerizationError(.unsupported, message: "writeFile")
}
}
+59
View File
@@ -245,6 +245,65 @@ extension Initd: Com_Apple_Containerization_Sandbox_V3_SandboxContextAsyncProvid
return .init()
}
func writeFile(request: Com_Apple_Containerization_Sandbox_V3_WriteFileRequest, context: GRPC.GRPCAsyncServerCallContext)
async throws -> Com_Apple_Containerization_Sandbox_V3_WriteFileResponse
{
log.debug(
"writeFile",
metadata: [
"path": "\(request.path)",
"mode": "\(request.mode)",
"dataSize": "\(request.data.count)",
])
do {
if request.flags.createParentDirs {
let fileURL = URL(fileURLWithPath: request.path)
let parentDir = fileURL.deletingLastPathComponent()
try FileManager.default.createDirectory(
at: parentDir,
withIntermediateDirectories: true
)
}
var flags = O_WRONLY
if request.flags.createIfMissing {
flags |= O_CREAT
}
if request.flags.append {
flags |= O_APPEND
}
let mode = request.mode > 0 ? mode_t(request.mode) : mode_t(0644)
let fd = open(request.path, flags, mode)
guard fd != -1 else {
let error = swiftErrno("open")
throw GRPCStatus(
code: .internalError,
message: "writeFile: failed to open file: \(error)"
)
}
let fh = FileHandle(fileDescriptor: fd, closeOnDealloc: true)
try fh.write(contentsOf: request.data)
} catch {
log.error(
"writeFile",
metadata: [
"error": "\(error)"
])
if error is GRPCStatus {
throw error
}
throw GRPCStatus(
code: .internalError,
message: "writeFile: \(error)"
)
}
return .init()
}
func mount(request: Com_Apple_Containerization_Sandbox_V3_MountRequest, context: GRPC.GRPCAsyncServerCallContext)
async throws -> Com_Apple_Containerization_Sandbox_V3_MountResponse
{