## Summary
Exclude `Sources/CloudHypervisor/README.md` from the `CloudHypervisor`
SwiftPM target. The README remains tracked and available as source
documentation, while package evaluation and builds no longer emit an
unhandled-file warning.
## Motivation
[apple/containerization#782](https://github.com/apple/containerization/pull/782)
added a standalone `CloudHypervisor` target with a README inside its
source directory. SwiftPM requires non-source files in target
directories to be declared as resources or excluded. This README is
documentation rather than a runtime resource, so exclusion is the
smallest accurate manifest change.
## Changes
- Add `README.md` to the `CloudHypervisor` target's `exclude` list.
- Preserve the existing README content and location.
- Leave runtime code, products, resources, and package APIs unchanged.
## Validation
```sh
swift package describe >/dev/null
swift test --disable-automatic-resolution --filter CloudHypervisorTests
make check
git diff --check upstream/main...HEAD
```
- Package evaluation completes without the
`Sources/CloudHypervisor/README.md` warning.
- All 41 focused `CloudHypervisorTests` pass.
- Swift formatting and Hawkeye license checks pass.
- The committed diff has no whitespace errors.
## Compatibility And Risk
This manifest-only change does not alter compiled sources, runtime
resources, public APIs, or supported platforms. The README is already
non-executable documentation, so excluding it accurately records
SwiftPM's intended treatment of the file.
## Related
- Follow-up to
[apple/containerization#782](https://github.com/apple/containerization/pull/782).
## Release Note Highlight
- Stops SwiftPM builds from warning about the `CloudHypervisor` README
added by
[apple/containerization#782](https://github.com/apple/containerization/pull/782).
- Fixes#776
- Release 2.9.0 of `grpc-swift-nio-transport` fixes
the HTTP/2 initialization race that required a
`HTTP2ConnectBufferingHandler` for the vminitd
API. This upgrades the dependency and eliminates
the workaround.
Changes the default Linux capability set for container processes from
`.allCapabilities` to `.defaultOCICapabilities`, making the library
secure-by-default. Callers that genuinely need elevated capabilities
must now opt in explicitly.
Signed-off-by: michael_crosby <michael_crosby@apple.com>
apple/containerization currently runs containers in per-container VMs on
macOS hosts via Virtualization.framework. This adds a second VMM backend
so the same Swift orchestration layer (LinuxContainer / LinuxPod /
Vminitd gRPC contract) runs on Linux hosts via cloud-hypervisor + KVM.
**CloudHypervisor Swift package** (`Sources/CloudHypervisor/`) — a thin
client for cloud-hypervisor's REST-over-UDS API, layered on
AsyncHTTPClient. Endpoints cover VMM / VM lifecycle / hotplug (disk, fs,
net, vsock, remove-device). Cross-platform (compiles on macOS for unit
tests; consumed at runtime only by the Linux side of Containerization).
**CH backend in Containerization** — one cloud-hypervisor subprocess per
VM, gated behind `#if os(Linux)`. CHVirtualMachineManager /
CHVirtualMachineInstance mirror the VZ shape behind the existing
VirtualMachineManager / VirtualMachineInstance protocol. CHProcess and
VirtiofsdProcess manage the binaries; CHHotplugProvider handles
virtio-blk and virtio-fs runtime hotplug (with one virtiofsd per unique
source-hash tag, refcounted across containers).
**Linux host networking** — BridgeManager brings up a Linux bridge with
an IPv4 subnet and (opt-in via `--enable-nat`) iptables MASQUERADE +
scoped FORWARD rules. LinuxBridgedNetwork enslaves a fresh TAP per
container to the bridge. State is recorded under `/run/containerization`
so `cctl bridge delete` reverses exactly what create did. Bridge
teardown verifies the link kind via sysfs to refuse deleting non-bridge
interfaces.
**cctl run / bridge** — end-to-end Linux container run path (image pull,
ext4 rootfs assembly, VM boot, container exec) plus `cctl bridge
create|delete` for the host network plumbing.
**Build & dist** — `make linux-build` / `make linux-integration` build
and exercise the host side inside an apple/container `--virtualization`
dev container. `make dist-x86_64` produces a deployment tarball (cctl +
cloud-hypervisor + virtiofsd + initfs + kernel) cross-compiled from the
aarch64 dev container; pipeline documented in `docs/x86_64-build.md`.
Static-musl C deps and the Zig cross compiler are pinned by SHA256.
The host orchestrator runs as root. Per-VM runtime state lives under
`/run/containerization/ch/<UUID>` with mode 0700; UDS sockets inside are
bound with mode 0600. Vminitd's gRPC channel inherits that trust
boundary — socket-file perms are the auth.
Sandbox flags are upstream-secure by default. Two per-component opt-outs
exist for the apple/container dev-container case (where the host seccomp
profile SIGSYS-kills CH and virtiofsd):
- `CONTAINERIZATION_NO_CH_SECCOMP=1` — `cloud-hypervisor --seccomp
false`.
- `CONTAINERIZATION_NO_VIRTIOFSD_SANDBOX=1` — `virtiofsd --sandbox
none`. Each emits a one-shot `logger.warning` at process start. Legacy
alias `CONTAINERIZATION_RELAXED_SANDBOX=1` flips both. cctl spawns both
binaries with `setsid` and a minimal env allowlist (PATH / HOME /
RUST_LOG / RUST_BACKTRACE) so the parent's secrets don't leak to
children.
`make linux-integration` runs the cross-platform integration suite
against a real cloud-hypervisor VM inside the dev container. Linux runs
the cross-platform subset (`process true`/`false`/`echo hi`, virtiofs
round-trip, hotplug); the macOS suite is unchanged.
Signed-off-by: michael_crosby <michael_crosby@apple.com>
The `PodVolume` type in `LinuxPod` only defined the `nbd` enum value -
however, disk based images are also supported and the pattern is
essentially the same
Signed-off-by: Aditya Ramani <a_ramani@apple.com>
When unpacking an OCI/tar layer, create() already creates missing parent
directories recursively, so regular files and symlinks with absent
parent entries unpack correctly. link() did not, so a hardlink whose
parent directory had no explicit archive entry failed with "<path> not
found" (e.g. images produced by Bazel rules_img). Mirror create()'s
implicit parent creation in link() so such layers unpack, matching
Docker/containerd.
Adds a direct link() unit test and an end-to-end unpack regression test
covering a hardlink, regular file, and symlink with no explicit parent.
Fixes https://github.com/apple/container/issues/1797
- Closes#774.
- Adds a default journal configuration parameter to the EXT4Unpacker
init function, defaulting to nil (no journal) to preserve source
compatibility.
The `--log-level` option when running the agent sub-command for vminitd
was being silently ignored cause of the way the agent is being run. As a
workaround we need to read `/proc/self/cmdline` to get the right args
Seems like a typo.
The change only affected a comment and does not disturb the code at all
(backward compatible).
Co-authored-by: J Logan <john_logan@apple.com>
- CI runners moved to the Xcode developer beta as the default, but macOS
builds are failing with a conflicting options error for
`-warnings-as-errors` and `-suppress-warnings`.
## Summary
`Platform.==` treats `arm64` with `nil` variant as equal to `arm64/v8`,
but `hash(into:)` used `description` which serializes them differently
(`linux/arm64` vs `linux/arm64/v8`). This violates the `Hashable`
contract — equal values must produce the same hash.
### Root cause
```swift
// == returns true for these two
let a = Platform(arch: "arm64", os: "linux", variant: nil)
let b = Platform(arch: "arm64", os: "linux", variant: "v8")
a == b // true ✓
// but hash was different — broken
a.hashValue == b.hashValue // false ✗ (before this fix)
```
This mismatch caused `Set<Platform>` and `Dictionary<Platform, ...>`
lookups to silently miss entries when one platform was decoded from JSON
(no `variant` field in the manifest) and another was created via
`Platform(from:)` or `Platform.current` (which both set `variant =
"v8"`).
### Practical consequence
In `apple/container`, this manifests as inconsistent platform-string
normalization across stages of a single `container build` — some stages
log `linux/arm64`, others `linux/arm64/v8` — which can cause `COPY
--from=<stage>` to fail to resolve the source stage under concurrent
builds. See apple/container#1542.
### Fix
`hash(into:)` now normalizes `arm64` with `nil` variant to `"v8"` before
hashing, matching the existing `==` behavior.
Extends the network plumbing to support per-interface IPv6 address
configuration.
The `Interface` protocol supports `ipv6Address` and `ipv6Gateway`.
The agent's networking RPCs carry per-family fields via new
`InterfaceAddress`, `LinkRoute`, and `DefaultRoute` types in
`ContainerizationExtras`.
`NetlinkSession` adds IPv6 methods for address and route operations.
---------
Co-authored-by: michael_crosby <michael_crosby@apple.com>
Co-authored-by: Michael Crosby <crosbymichael@gmail.com>
`.totalFileAllocatedSizeKey` returns nil for directories on Darwin but
on Linux it returns `st_blocks * st_blksize` (4 KB each) in Foundation.
The empty-store test summed three directory inodes on Linux and failed
with `#expect(size == 0)`. This change adds filter on the enumerator to
regular files only so the totals are content-only and will work for both
Darwin and Linux.
This PR adds `totalAllocatedSize()` to the `ContentStore` protocol so it
can be used to get the on-disk footprint without reaching past the
abstraction. `LocalContentStore` implements it by walking its base path,
covering both committed blobs and active ingest sessions.
- Closes#712.
- Replace synchronous `write()` calls in
`BidirectionalRelay` with non-blocking I/O
and `DispatchSourceWrite` backpressure
handling. Under concurrent vsock proxy load,
a single blocked write on the shared serial
dispatch queue would freeze all relay
connections permanently, including unrelated
new connections.
- Set relay file descriptors to `O_NONBLOCK`
and handle `EAGAIN` by suspending reads and
installing a write source to drain pending data.
- Give each `BidirectionalRelay` its own serial queue
instead of sharing one from `UnixSocketRelayManager`,
eliminating cross-connection blocking.
- Resume suspended read sources before cancelling
in `stop()` — GCD does not deliver cancel handlers
on suspended dispatch sources, which caused file
descriptor and memory leaks on teardown under
backpressure.
- Guard-unwrap `buf.baseAddress` in
`drainPendingWrite`.
- Closes#744
- Adds the initial `FilePathOps` utility type
- Adds the `absolutePath` implementation
- Adds the `FilePathOpsTests` file and initial test cases
The `IPv4Address(_ bytes: [UInt8])` initializer in
ContainerizationExtras shifts the third octet by 16 bits instead of 8:
```swift
self.value =
(UInt32(bytes[0]) << 24)
| (UInt32(bytes[1]) << 16)
| (UInt32(bytes[2]) << 16) // should be << 8
| UInt32(bytes[3])
```
Because `bytes[2]` lands in the same bit range as `bytes[1]`, the second
octet gets corrupted by the OR, the third octet is dropped, and bits 8
through 15 are always left zero. Concretely, decoding `[192, 168, 1, 1]`
yields `192.169.0.1` instead of `192.168.1.1`, and `[18, 52, 86, 120]`
yields `18.118.0.120` instead of `18.52.86.120`.
This went unnoticed because the `bytes` computed property getter uses
the correct `>> 8` for the third octet, but there was no test exercising
the byte-array initializer, so the encode and decode paths were never
checked against each other. The sibling `IPv6Address(_ bytes:)`
initializer uses the correct descending shifts (`<< 120, << 112, ... <<
8, << 0`), which is what the IPv4 version should mirror.
The fix changes the third octet shift to 8 bits so the initializer is
the exact inverse of the `bytes` property. I also added two tests to the
initializer suite: a valid-input test that asserts both the resulting
`value` and that `init(bytes).bytes == bytes` round-trips, and an
invalid-length test. The round-trip test fails on the current code and
passes with the fix.
Verification: `swift test --filter ContainerizationExtrasTests` passes
221 tests in 26 suites (the IPv4Address suite goes from 23 to 25 tests).
The new round-trip test fails before the one-line change and passes
after.
Signed-off-by: Aditya Singh <adisin650@gmail.com>
- Closes#749.
- ExportOperation hardcoded the pushed index descriptor's
mediaType to the OCI image index type. RegistryClient.push
uses that descriptor's mediaType as the HTTP Content-Type
header. When the source index was in
Docker manifest.list.v2+json format (the common case for
images pulled from Docker Hub and other public registries),
the body's embedded mediaType field disagreed with the
header, and OCI registries rejected the index PUT with
HTTP 400 MANIFEST_INVALID.
- Use the source index's mediaType for the pushed descriptor
so the header always matches the body. Per-architecture
child manifests are unaffected because they were already
pushed with their actual mediaType.
- Add a parameterized unit test for ExportOperation.export
covering both Docker manifest.list
- Closes#745.
- Facilitates TOCTOU-safe recursion over directory contents.
- Replace FileDescriptor extensions with a static utility type to
prevent potential namespacing issues as this project and Swift evolve.
External Swift Package Manager consumers can only see products declared
in the top-level `Package.swift`. The `VminitdCore` library was declared
inside the `vminitd` subpackage, so it was impossible to build a custom
`vminitd` against it via a URL dependency on `containerization`. This
change moves the `VminitdCore` to the top-level `Package.swift`.
This pull request enhances the handling of file and directory copy
operations in Linux containers, particularly by improving destination
path resolution and error handling for the `copyIn` operation with the
`Stat RPC`
This PR is needed for
[container#1190](https://github.com/apple/container/pull/1190)
Today we craft the proxied sockets we create at a path that contains the
container ID. This is normally completely fine, except if the ID is
enormous, as it can cause the UDS path to be longer than the allowed
length.
I think realistically we should have a limit on this. Technically today
the ID length restriction is whatever our grpc buffer length is..
I think 64 is long enough so that folks could use an entire hex based
sha256, any uuid, and also sethostname has a length restriction of 64
anyways.
- We were using Swift 6.3, which can pick up a newer version that is out
of sync of both the `.swift-version` file for the project and the Swift
SDK downloaded in the Dockerfile.
Move the MiB rounding from LinuxContainer into
`VZVirtualMachineInstance`, so all callers are covered in one place and
the alignment constraint stays with the VMM layer that owns it.
This will also ensure LinuxPod can get memory alignment for free.
The stdout and stdin IOPairs in TerminalIO share the same Terminal fd.
Closing stdin first would close the Terminal, causing stdout to read -1
from fileDescriptor and fail to unregister from epoll. This left stale
handler closures (capturing the IOPair) in ProcessSupervisor.handlers
indefinitely.
Additionally, vsock sockets created in TerminalIO.start() with
closeOnDeinit disabled were never closed if the process exited before
attach() created IOPairs.
## Summary
Enables CIFS (SMB) filesystem support in the arm64 kernel config,
required for the SMB volume driver added in apple/container#1414.
Without this change, SMB mounts fail at runtime with `errno 19
(ENODEV)`.
## Changes
- `kernel/config-arm64`: enable `CONFIG_CIFS=y` and related options
(`CIFS_ALLOW_INSECURE_LEGACY`, `CIFS_UPCALL`, `CIFS_XATTR`,
`CIFS_POSIX`, `CIFS_DFS_UPCALL`)
This PR refactors `ArchiveWriter` to add an API `archive(_ paths:
base:)`. This API is used to archive the contents at each path
independently, similar to doing `tar -cvf archive.tar foo.bin
/bar/baz.txt`.
This issue doesn't affect any of our existing products. This is a
preemptive fix for downstream consumers of EXT4.format where, in some
platforms, leading `//` in the path could get resolved into a FileTree
that looks like this
```sh
/
└── /
└── usr
```