mirror of
https://github.com/containerd/containerd.git
synced 2026-11-06 05:17:02 +00:00
A layer served from the layer content cache was symlinked into the snapshot as its layer.erofs. That put a path inside the snapshot directory aliasing a blob owned by the operator and shared with every other snapshot of that layer. Everything written to that path went straight into the cache entry. Each write site then needed its own guard: the differ had to refuse to apply, Commit had to refuse to convert through a dangling link, and Remove had to avoid clearing IMMUTABLE_FL on a file it did not own. A missed guard corrupted content shared across images. A pruned entry left a dangling link that read as an absent blob on one path and as populated content on another. Record where the blob is on the snapshot and mount it from there. Nothing in the snapshot directory refers to content it does not own. A stray write creates a local file, conversion cannot reach the cache, and removal has nothing to follow. The guards Commit and Remove needed for a symlink are gone. The differ still refuses, on the read-only mounts the snapshotter hands out. resolveBlob is the one place a blob is located. It reports whether the blob is already populated, meaning there is nothing to apply or convert, and whether it belongs to this snapshot, meaning it may be written. Mounts are then assembled without consulting the filesystem to find out what a layer is. A source that does not resolve is an error on every path, including the parentless path in mounts(). A flattened parent's device= is resolved the same way. fsverity is only measured for a blob the snapshot owns. Measuring a cache entry would require the operator to enable fsverity on content this snapshotter never wrote. NewSnapshotter already rejects fsverity together with the layer content cache. The record is kept in labels outside the containerd.io/snapshot/ namespace. Labels in that namespace are inherited from image annotations, which would let an image name any path on the host. The same filter runs on everything the metadata snapshotter passes down. A client cannot set these labels, and cannot send them back after reading them through Stat. Commit and Update carry them across. The dm-verity sidecar is read from beside the cache entry. Nothing is copied into the snapshot. The sidecar cannot fall out of step with the blob it pins. An entry required to have one is rejected at Prepare. Signed-off-by: Derek McGowan <derek@mcg.dev>