Skip to content

Commit 5498087

Browse files
add ability to export initfs as block (#268)
This refactors the rootfs command to produce an image and have the ability to create an ext4 formatted block of the init filesystem directly from the command. closes #220 Signed-off-by: crosbymichael <[email protected]>
1 parent e08b0d4 commit 5498087

File tree

3 files changed

+100
-14
lines changed

3 files changed

+100
-14
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,12 @@ containerization:
5757
init: containerization vminitd
5858
@echo Creating init.ext4...
5959
@rm -f bin/init.rootfs.tar.gz bin/init.block
60-
@./bin/cctl rootfs create --vminitd vminitd/bin/vminitd --labels org.opencontainers.image.source=https://github.com/apple/containerization --vmexec vminitd/bin/vmexec bin/init.rootfs.tar.gz vminit:latest
60+
@./bin/cctl rootfs create \
61+
--vminitd vminitd/bin/vminitd \
62+
--labels org.opencontainers.image.source=https://github.com/apple/containerization \
63+
--vmexec vminitd/bin/vmexec \
64+
--image vminit:latest \
65+
bin/init.rootfs.tar.gz
6166

6267
.PHONY: cross-prep
6368
cross-prep:

Sources/Containerization/Image/Unpacker/EXT4Unpacker.swift

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,56 @@ public struct EXT4Unpacker: Unpacker {
3232
self.blockSizeInBytes = blockSizeInBytes
3333
}
3434

35-
public func unpack(_ image: Image, for platform: Platform, at path: URL, progress: ProgressHandler? = nil) async throws -> Mount {
35+
#if os(macOS)
36+
/// Performs the unpacking of a tar archive into a filesystem.
37+
/// - Parameters:
38+
/// - archive: The archive to unpack.
39+
/// - compression: The compression to use when unpacking the image.
40+
/// - path: The path to the filesystem that will be created.
41+
public func unpack(
42+
archive: URL,
43+
compression: ContainerizationArchive.Filter,
44+
at path: URL
45+
) throws {
46+
let cleanedPath = try prepareUnpackPath(path: path)
47+
let filesystem = try EXT4.Formatter(
48+
FilePath(cleanedPath),
49+
minDiskSize: blockSizeInBytes
50+
)
51+
defer { try? filesystem.close() }
52+
53+
try filesystem.unpack(
54+
source: archive,
55+
format: .paxRestricted,
56+
compression: compression,
57+
progress: nil
58+
)
59+
}
60+
#endif
61+
62+
/// Returns a `Mount` point after unpacking the image into a filesystem.
63+
/// - Parameters:
64+
/// - image: The image to unpack.
65+
/// - platform: The platform content to unpack.
66+
/// - path: The path to the directory where the filesystem will be created.
67+
/// - progress: The progress handler to invoke as the unpacking progresses.
68+
public func unpack(
69+
_ image: Image,
70+
for platform: Platform,
71+
at path: URL,
72+
progress: ProgressHandler? = nil
73+
) async throws -> Mount {
3674
#if !os(macOS)
3775
throw ContainerizationError(.unsupported, message: "Cannot unpack an image on current platform")
3876
#else
39-
let blockPath = try prepareUnpackPath(path: path)
77+
let cleanedPath = try prepareUnpackPath(path: path)
4078
let manifest = try await image.manifest(for: platform)
41-
let filesystem = try EXT4.Formatter(FilePath(path), minDiskSize: blockSizeInBytes)
79+
let filesystem = try EXT4.Formatter(
80+
FilePath(
81+
cleanedPath
82+
),
83+
minDiskSize: blockSizeInBytes
84+
)
4285
defer { try? filesystem.close() }
4386

4487
for layer in manifest.layers {
@@ -64,7 +107,7 @@ public struct EXT4Unpacker: Unpacker {
64107

65108
return .block(
66109
format: "ext4",
67-
source: blockPath,
110+
source: cleanedPath,
68111
destination: "/",
69112
options: []
70113
)

Sources/cctl/RootfsCommand.swift

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ extension Application {
4646
@Option(name: .long, help: "Labels to add to the built image of the form <key1>=<value1>, [<key2>=<value2>,...]")
4747
var labels: [String] = []
4848

49-
@Argument var rootfsPath: String
49+
@Option(name: .customLong("image"), help: "The name of the image to produce.")
50+
var imageName: String?
5051

51-
@Argument var tag: String
52+
@Option(name: .customLong("ext4"), help: "The path to an ext4 image to create.")
53+
var ext4File: String?
54+
55+
// The path where the intermediate tar archive is created.
56+
@Argument var tarPath: String
5257

5358
private static let directories = [
5459
"bin",
@@ -63,19 +68,51 @@ extension Application {
6368
]
6469

6570
func run() async throws {
66-
try await writeArchive()
71+
let path = URL(filePath: self.tarPath)
72+
try await writeArchive(path: path)
73+
74+
if let image = self.imageName {
75+
print("creating initfs image \(image)...")
76+
try await outputImage(
77+
path: path,
78+
reference: image
79+
)
80+
}
81+
82+
if let ext4Path = self.ext4File {
83+
print("creating initfs ext4 image at \(ext4Path)...")
84+
try await outputExt4(
85+
archive: path,
86+
to: URL(filePath: ext4Path)
87+
)
88+
}
89+
}
90+
91+
private func outputExt4(archive: URL, to path: URL) async throws {
92+
let unpacker = EXT4Unpacker(blockSizeInBytes: 256.mib())
93+
94+
try unpacker.unpack(archive: archive, compression: .gzip, at: path)
95+
}
96+
97+
private func outputImage(path: URL, reference: String) async throws {
6798
let p = try Platform(from: platformString)
68-
let rootfs = URL(filePath: rootfsPath)
6999
let labels = Application.parseKeyValuePairs(from: labels)
70100
_ = try await InitImage.create(
71-
reference: tag, rootfs: rootfs,
72-
platform: p, labels: labels,
101+
reference: reference,
102+
rootfs: path,
103+
platform: p,
104+
labels: labels,
73105
imageStore: Application.imageStore,
74-
contentStore: Application.contentStore)
106+
contentStore: Application.contentStore
107+
)
75108
}
76109

77-
private func writeArchive() async throws {
78-
let writer = try ArchiveWriter(format: .pax, filter: .gzip, file: URL(filePath: rootfsPath))
110+
private func writeArchive(path: URL) async throws {
111+
let writer = try ArchiveWriter(
112+
format: .pax,
113+
filter: .gzip,
114+
file: path,
115+
)
79116
let ts = Date()
80117
let entry = WriteEntry()
81118
entry.permissions = 0o755
@@ -84,6 +121,7 @@ extension Application {
84121
entry.group = 0
85122
entry.owner = 0
86123
entry.fileType = .directory
124+
87125
// create the initial directory structure.
88126
for dir in Self.directories {
89127
entry.path = dir

0 commit comments

Comments
 (0)