Composefs: an opportunistically sharing verified image filesystem
Get series with b4
b4 am [email protected]Versions newest first · linked by author and title
- v3 Latest6 patches 80 repliesIn review
- v26 patches 26 repliesSuperseded
- v1 RFC6 patches 7 repliesSuperseded
Changes in v3 from the cover letter
- Simplified filesystem format to use fixed size inodes. This resulted in simpler (now < 2k lines) code as well as higher performance at the cost of slightly (~40%) larger images.
- We now use multi-page mappings from the page cache, which removes limits on sizes of xattrs and makes the dirent handling code simpler.
- Added more documentation about the on-disk file format.
- General cleanups based on review comments.
Patches in v3 No applies recorded yet
| # | Subject and files | Size | Replies | Applied |
|---|---|---|---|---|
| 1/6 | fsverity: Export fsverity_get_digestfs/ | +1 | 0 replies | |
| 2/6 | composefs: Add on-disk layout headerfs/ | +172 | 0 replies | |
| 3/6 | composefs: Add descriptor parsing codefs/ fs/ | +775 | 0 replies | |
| 4/6 | composefs: Add filesystem implementationfs/ | +750 | 0 replies | |
| 5/6 | composefs: Add documentationDocumentation/ Documentation/ | +160 | 1 reply | |
| 6/6 | composefs: Add kconfig and build supportfs/ fs/ fs/ +1 more | +25 | 0 replies |
Reviews trailers per patch
No Reviewed-by, Acked-by or Tested-by trailers for v3 yet.
Cover letter [PATCH v3 0/6] Composefs: an opportunistically sharing verified image filesystem
Giuseppe Scrivano and I have recently been working on a new project we call composefs. This is the first time we propose this publically and we would like some feedback on it.
At its core, composefs is a way to construct and use read only images that are used similar to how you would use e.g. loop-back mounted squashfs images. On top of this composefs has two fundamental features. First it allows sharing of file data (both on disk and in page cache) between images, and secondly it has dm-verity like validation on read.
Let me first start with a minimal example of how this can be used, before going into the details:
Suppose we have this source for an image:
rootfs/
├── dir │ └── another_a ├── file_a └── file_b
We can then use this to generate an image file and a set of content-addressed backing files:
# mkcomposefs --digest-store=objects rootfs/ rootfs.img # ls -l rootfs.img objects/*/* -rw-------. 1 root root 10 Nov 18 13:20 objects/02/927862b4ab9fb69919187bb78d394e235ce444eeb0a890d37e955827fe4bf4 -rw-------. 1 root root 10 Nov 18 13:20 objects/cc/3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f -rw-r--r--. 1 root root 4228 Nov 18 13:20 rootfs.img
The rootfs.img file contains all information about directory and file metadata plus references to the backing files by name. We can now mount this and look at the result:
# mount -t composefs rootfs.img -o basedir=objects /mnt # ls /mnt/ dir file_a file_b # cat /mnt/file_a content_a
When reading this file the kernel is actually reading the backing file, in a fashion similar to overlayfs. Since the backing file is content-addressed, the objects directory can be shared for multiple images, and any files that happen to have the same content are shared. I refer to this as opportunistic sharing, as it is different than the more course-grained explicit sharing used by e.g. container base images.
The next step is the validation. Note how the object files have fs-verity enabled. In fact, they are named by their fs-verity digest:
# fsverity digest objects/*/* sha256:02927862b4ab9fb69919187bb78d394e235ce444eeb0a890d37e955827fe4bf4 objects/02/927862b4ab9fb69919187bb78d394e235ce444eeb0a890d37e955827fe4bf4 sha256:cc3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f objects/cc/3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f
The generated filesystm image may contain the expected digest for the backing files. When the backing file digest is incorrect, the open will fail, and if the open succeeds, any other on-disk file-changes will be detected by fs-verity:
# cat objects/cc/3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f content_a # rm -f objects/cc/3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f # echo modified > objects/cc/3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f # cat /mnt/file_a WARNING: composefs backing file '3da5b14909626fc99443f580e4d8c9b990e85e0a1d18883dc89b23d43e173f' unexpectedly had no fs-verity digest cat: /mnt/file_a: Input/output error
This re-uses the existing fs-verity functionallity to protect against changes in file contents, while adding on top of it protection against changes in filesystem metadata and structure. I.e. protecting against replacing a fs-verity enabled file or modifying file permissions or xattrs.
To be fully verified we need another step: we use fs-verity on the image itself. Then we pass the expected digest on the mount command line (which will be verified at mount time):
# fsverity enable rootfs.img # fsverity digest rootfs.img sha256:da42003782992856240a3e25264b19601016114775debd80c01620260af86a76 rootfs.img # mount -t composefs rootfs.img -o basedir=objects,digest=da42003782992856240a3e25264b19601016114775debd80c01620260af86a76 /mnt
So, given a trusted set of mount options (say unlocked from TPM), we have a fully verified filesystem tree mounted, with opportunistic finegrained sharing of identical files.
So, why do we want this? There are two initial users. First of all we want to use the opportunistic sharing for the podman container image baselayer. The idea is to use a composefs mount as the lower directory in an overlay mount, with the upper directory being the container work dir. This will allow automatical file-level disk and page-cache sharning between any two images, independent of details like the permissions and timestamps of the files.
Secondly we are interested in using the verification aspects of composefs in the ostree project. Ostree already supports a content-addressed object store, but it is currently referenced by hardlink farms. The object store and the trees that reference it are signed and verified at download time, but there is no runtime verification. If we replace the hardlink farm with a composefs image that points into the existing object store we can use the verification to implement runtime verification.
In fact, the tooling to create composefs images is 100% reproducible, so all we need is to add the composefs image fs-verity digest into the ostree commit. Then the image can be reconstructed from the ostree commit info, generating a file with the same fs-verity digest.
These are the usecases we're currently interested in, but there seems to be a breadth of other possible uses. For example, many systems use loopback mounts for images (like lxc or snap), and these could take advantage of the opportunistic sharing. We've also talked about using fuse to implement a local cache for the backing files. I.e. you would have the second basedir be a fuse filesystem. On lookup failure in the first basedir it downloads the file and saves it in the first basedir for later lookups. There are many interesting possibilities here.
The patch series contains some documentation on the file format and how to use the filesystem.
The userspace tools (and a standalone kernel module) is available here:
https://github.com/containers/composefs
Initial work on ostree integration is here:
https://github.com/ostreedev/ostree/pull/2640
Changes since v2:
- Simplified filesystem format to use fixed size inodes. This resulted in simpler (now < 2k lines) code as well as higher performance at the cost of slightly (~40%) larger images.
- We now use multi-page mappings from the page cache, which removes limits on sizes of xattrs and makes the dirent handling code simpler.
- Added more documentation about the on-disk file format.
- General cleanups based on review comments.
Changes since v1:
- Fixed some minor compiler warnings
- Fixed build with !CONFIG_MMU
- Documentation fixes from review by Bagas Sanjaya
- Code style and cleanup from review by Brian Masney
- Use existing kernel helpers for hex digit conversion
- Use kmap_local_page() instead of deprecated kmap()
Alexander Larsson (6):
fsverity: Export fsverity_get_digest
composefs: Add on-disk layout header
composefs: Add descriptor parsing code
composefs: Add filesystem implementation
composefs: Add documentation
composefs: Add kconfig and build support
Documentation/filesystems/composefs.rst | 159 +++++ Documentation/filesystems/index.rst | 1 + fs/Kconfig | 1 + fs/Makefile | 1 + fs/composefs/Kconfig | 18 + fs/composefs/Makefile | 5 + fs/composefs/cfs-internals.h | 55 ++ fs/composefs/cfs-reader.c | 720 +++++++++++++++++++++++ fs/composefs/cfs.c | 750 ++++++++++++++++++++++++ fs/composefs/cfs.h | 172 ++++++ fs/verity/measure.c | 1 + 11 files changed, 1883 insertions(+) create mode 100644 Documentation/filesystems/composefs.rst create mode 100644 fs/composefs/Kconfig create mode 100644 fs/composefs/Makefile create mode 100644 fs/composefs/cfs-internals.h create mode 100644 fs/composefs/cfs-reader.c create mode 100644 fs/composefs/cfs.c create mode 100644 fs/composefs/cfs.h
Signature · 1 line
2.39.0