Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion Sources/Containerization/AttachedFilesystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import ContainerizationExtras
import ContainerizationOCI
import Foundation

/// A filesystem that was attached and able to be mounted inside the runtime environment.
public struct AttachedFilesystem: Sendable {
Expand All @@ -32,7 +33,16 @@ public struct AttachedFilesystem: Sendable {
public init(mount: Mount, allocator: any AddressAllocator<Character>) throws {
switch mount.type {
case "virtiofs":
let name = try hashMountSource(source: mount.source)
let name: String = try {
guard mount.isFile else {
return try hashMountSource(source: mount.source)
}

let fileTag = try hashMountSource(source: mount.source)
let directory = try hashMountSource(source: mount.hardlinkDirectory!)

return URL(string: directory)!.appendingPathComponent(fileTag).path
}()
self.source = name
case "ext4":
let char = try allocator.allocate()
Expand Down
19 changes: 19 additions & 0 deletions Sources/Containerization/ContainerManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,19 @@ public struct ContainerManager: Sendable {
}
config.bootLog = BootLog.file(path: self.containerRoot.appendingPathComponent(id).appendingPathComponent("bootlog.log"))
try configuration(&config)

let sharedDir = sharedFileDirectory(id)
for i in config.mounts.indices {
if config.mounts[i].isFile {
let file = URL(fileURLWithPath: config.mounts[i].source)
let fileTag = try hashMountSource(source: config.mounts[i].source)
let hardlink = sharedDir.appendingPathComponent(fileTag)

try FileManager.default.linkItem(at: file, to: hardlink)
config.mounts[i].hardlinkDirectory = sharedDir.path
config.mounts[i].options.append("bind")
}
}
}
}

Expand All @@ -433,9 +446,15 @@ public struct ContainerManager: Sendable {
private func createContainerRoot(_ id: String) throws -> URL {
let path = containerRoot.appendingPathComponent(id)
try FileManager.default.createDirectory(at: path, withIntermediateDirectories: false)
try FileManager.default.createDirectory(at: sharedFileDirectory(id), withIntermediateDirectories: false)

return path
}

private func sharedFileDirectory(_ id: String) -> URL {
containerRoot.appendingPathComponent(id).appendingPathComponent("virtiofs")
}

private func unpack(image: Image, destination: URL, size: UInt64) async throws -> Mount {
do {
let unpacker = EXT4Unpacker(blockSizeInBytes: size)
Expand Down
12 changes: 11 additions & 1 deletion Sources/Containerization/Mount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public struct Mount: Sendable {
/// should create for this specific mount (virtioblock
/// virtiofs etc.).
public let runtimeOptions: RuntimeOptions
/// hardlink directory path if the source is file.
public var hardlinkDirectory: String?

/// A type representing a "hint" of what type
/// of mount this really is (block, directory, purely
Expand Down Expand Up @@ -144,7 +146,15 @@ extension Mount {
throw ContainerizationError(.notFound, message: "directory \(source) does not exist")
}

let name = try hashMountSource(source: self.source)
let source = isFile ? self.hardlinkDirectory! : self.source
let name = try hashMountSource(source: source)
guard
!config.directorySharingDevices.contains(
where: { ($0 as? VZVirtioFileSystemDeviceConfiguration)?.tag == name })
else {
break
}

let urlSource = URL(fileURLWithPath: source)

let device = VZVirtioFileSystemDeviceConfiguration(tag: name)
Expand Down
10 changes: 10 additions & 0 deletions Sources/Containerization/VZVirtualMachineInstance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,16 @@ extension Mount {
var isBlock: Bool {
type == "ext4"
}

var isFile: Bool {
guard self.type == "virtiofs" else {
return false
}

var isDirectory: ObjCBool = false
let exists = FileManager.default.fileExists(atPath: self.source, isDirectory: &isDirectory)
return exists && !isDirectory.boolValue
}
}

extension Kernel {
Expand Down
29 changes: 27 additions & 2 deletions Sources/ContainerizationOS/Mount/Mount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ extension Mount {
return false
}

public var isSharedFile: Bool {
type == "" && self.options.contains("bind")
}

/// Mount the mount relative to `root` with the current set of data in the object.
/// Optionally provide `createWithPerms` to set the permissions for the directory that
/// it will be mounted at.
Expand Down Expand Up @@ -146,12 +150,25 @@ extension Mount {
// Ensure propagation type change flags aren't included in other calls.
let originalFlags = opts.flags & ~(propagationTypes)

let targetURL = URL(fileURLWithPath: self.target)
// TODO: What are lines 153, 154 for?
// let targetURL = URL(fileURLWithPath: self.target)
// let targetParent = targetURL.deletingLastPathComponent().path
// if let perms = createWithPerms {
// try mkdirAll(targetParent, perms)
// }

let targetURL = URL(fileURLWithPath: target)
let targetParent = targetURL.deletingLastPathComponent().path
if let perms = createWithPerms {
try mkdirAll(targetParent, perms)
}
try mkdirAll(target, 0o755)

if self.isSharedFile {
try mkdirAll(targetParent, 0o755)
createFile(target)
} else {
try mkdirAll(target, 0o755)
}

if opts.flags & Int32(MS_REMOUNT) == 0 || !dataString.isEmpty {
guard _mount(self.source, target, self.type, UInt(originalFlags), dataString) == 0 else {
Expand Down Expand Up @@ -186,6 +203,14 @@ extension Mount {
)
}

private func createFile(_ name: String) {
_ = FileManager.default.createFile(
atPath: name,
contents: nil,
attributes: nil
)
}

private func parseMountOptions() -> MountOptions {
var mountOpts = MountOptions()
for option in self.options {
Expand Down
27 changes: 21 additions & 6 deletions vminitd/Sources/vmexec/Mount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ import Musl
struct ContainerMount {
private let mounts: [ContainerizationOCI.Mount]
private let rootfs: String
private let sharedFileDirectory: URL

init(rootfs: String, mounts: [ContainerizationOCI.Mount]) {
init(rootfs: String, sharedFileDirectory: URL, mounts: [ContainerizationOCI.Mount]) {
self.rootfs = rootfs
self.sharedFileDirectory = sharedFileDirectory
self.mounts = mounts
}

func mountToRootfs() throws {
for m in self.mounts {
let osMount = m.toOSMount()
let osMount = m.toOSMount(sharedFileDirectory)
try osMount.mount(root: self.rootfs)
}
}
Expand All @@ -55,10 +57,23 @@ struct ContainerMount {
}

extension ContainerizationOCI.Mount {
func toOSMount() -> ContainerizationOS.Mount {
ContainerizationOS.Mount(
type: self.type,
source: self.source,
var isSharedFile: Bool {
type == "virtiofs" && options.contains("bind")
}

func toOSMount(_ sharedFileDirectory: URL) -> ContainerizationOS.Mount {
let type = isSharedFile ? "" : self.type
let source = {
guard isSharedFile else {
return self.source
}
let name = URL(string: self.source)!.lastPathComponent
return sharedFileDirectory.appendingPathComponent(name).path
}()

return ContainerizationOS.Mount(
type: type,
source: source,
target: self.destination,
options: self.options
)
Expand Down
20 changes: 17 additions & 3 deletions vminitd/Sources/vmexec/RunCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,12 @@ struct RunCommand: ParsableCommand {
}

private func childRootSetup(rootfs: ContainerizationOCI.Root, mounts: [ContainerizationOCI.Mount], log: Logger) throws {
let sharedFileDirectory = URL(fileURLWithPath: bundlePath).appendingPathComponent("virtiofs")

// setup rootfs
try prepareRoot(rootfs: rootfs.path)
try mountRootfs(rootfs: rootfs.path, mounts: mounts)
try prepareSharedFiles(sharedFileDirectory: sharedFileDirectory, mounts: mounts)
try mountRootfs(rootfs: rootfs.path, sharedFileDirectory: sharedFileDirectory, mounts: mounts)
try setDevSymlinks(rootfs: rootfs.path)

try pivotRoot(rootfs: rootfs.path)
Expand Down Expand Up @@ -185,8 +188,8 @@ struct RunCommand: ParsableCommand {
}
}

private func mountRootfs(rootfs: String, mounts: [ContainerizationOCI.Mount]) throws {
let containerMount = ContainerMount(rootfs: rootfs, mounts: mounts)
private func mountRootfs(rootfs: String, sharedFileDirectory: URL, mounts: [ContainerizationOCI.Mount]) throws {
let containerMount = ContainerMount(rootfs: rootfs, sharedFileDirectory: sharedFileDirectory, mounts: mounts)
try containerMount.mountToRootfs()
try containerMount.configureConsole()
}
Expand All @@ -201,6 +204,17 @@ struct RunCommand: ParsableCommand {
}
}

private func prepareSharedFiles(sharedFileDirectory: URL, mounts: [ContainerizationOCI.Mount]) throws {
if let source = mounts.first(where: { $0.isSharedFile })?.source {
let tag = URL(string: source)!.deletingLastPathComponent().path

try FileManager.default.createDirectory(at: sharedFileDirectory, withIntermediateDirectories: false)
guard mount(tag, sharedFileDirectory.path, "virtiofs", UInt(0), nil) == 0 else {
throw App.Errno(stage: "mount(shared)")
}
}
}

private func setDevSymlinks(rootfs: String) throws {
let links: [(src: String, dst: String)] = [
("/proc/self/fd", "/dev/fd"),
Expand Down