From 01d76b1709eb76de8bbe5ab5e732fab22472880a Mon Sep 17 00:00:00 2001 From: Ramkumar Chinchani Date: Thu, 3 Oct 2024 20:46:15 +0000 Subject: [PATCH] refactor: decouple verity from filesystem interfaces We are going to support multiple underlying filesystems (squash, erofs and maybe more). As long as they are filesystem image blobs, verity data can be appended. Signed-off-by: Ramkumar Chinchani --- cmd/atomfs/mount.go | 2 +- cmd/atomfs/umount.go | 2 +- molecule.go | 4 +- pkg/common/common.go | 11 ++++++ {erofs => pkg/erofs}/erofs.go | 4 +- pkg/erofs/fs.go | 38 +++++++++++++++++++ {erofs => pkg/erofs}/mediatype.go | 0 {erofs => pkg/erofs}/superblock.go | 0 {erofs => pkg/erofs}/verity.go | 2 +- {erofs => pkg/erofs}/verity_static.go | 0 {erofs => pkg/erofs}/verity_test.go | 0 pkg/fs/fs.go | 30 +++++++++++++++ {log => pkg/log}/log.go | 0 {mount => pkg/mount}/mountinfo.go | 0 pkg/squashfs/fs.go | 41 +++++++++++++++++++++ {squashfs => pkg/squashfs}/mediatype.go | 12 ++---- {squashfs => pkg/squashfs}/squashfs.go | 21 +++++++++-- {squashfs => pkg/squashfs}/superblock.go | 0 {squashfs => pkg/squashfs}/verity_static.go | 0 {squashfs => pkg/squashfs}/verity_test.go | 0 pkg/types/types.go | 32 ++++++++++++++++ verity/mediatype.go | 10 +++++ {squashfs => verity}/verity.go | 29 +++------------ 23 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 pkg/common/common.go rename {erofs => pkg/erofs}/erofs.go (99%) create mode 100644 pkg/erofs/fs.go rename {erofs => pkg/erofs}/mediatype.go (100%) rename {erofs => pkg/erofs}/superblock.go (100%) rename {erofs => pkg/erofs}/verity.go (99%) rename {erofs => pkg/erofs}/verity_static.go (100%) rename {erofs => pkg/erofs}/verity_test.go (100%) create mode 100644 pkg/fs/fs.go rename {log => pkg/log}/log.go (100%) rename {mount => pkg/mount}/mountinfo.go (100%) create mode 100644 pkg/squashfs/fs.go rename {squashfs => pkg/squashfs}/mediatype.go (71%) rename {squashfs => pkg/squashfs}/squashfs.go (97%) rename {squashfs => pkg/squashfs}/superblock.go (100%) rename {squashfs => pkg/squashfs}/verity_static.go (100%) rename {squashfs => pkg/squashfs}/verity_test.go (100%) create mode 100644 pkg/types/types.go create mode 100644 verity/mediatype.go rename {squashfs => verity}/verity.go (95%) diff --git a/cmd/atomfs/mount.go b/cmd/atomfs/mount.go index d0ea6b6..cf80a13 100644 --- a/cmd/atomfs/mount.go +++ b/cmd/atomfs/mount.go @@ -12,7 +12,7 @@ import ( "github.com/urfave/cli" "golang.org/x/sys/unix" "machinerun.io/atomfs" - "machinerun.io/atomfs/squashfs" + "machinerun.io/atomfs/pkg/squashfs" ) var mountCmd = cli.Command{ diff --git a/cmd/atomfs/umount.go b/cmd/atomfs/umount.go index a60a4c1..d650962 100644 --- a/cmd/atomfs/umount.go +++ b/cmd/atomfs/umount.go @@ -7,7 +7,7 @@ import ( "syscall" "github.com/urfave/cli" - "machinerun.io/atomfs/mount" + "machinerun.io/atomfs/pkg/mount" ) var umountCmd = cli.Command{ diff --git a/molecule.go b/molecule.go index 5ba3497..6823faf 100644 --- a/molecule.go +++ b/molecule.go @@ -9,8 +9,8 @@ import ( ispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "golang.org/x/sys/unix" - "machinerun.io/atomfs/mount" - "machinerun.io/atomfs/squashfs" + "machinerun.io/atomfs/pkg/mount" + "machinerun.io/atomfs/pkg/squashfs" ) type Molecule struct { diff --git a/pkg/common/common.go b/pkg/common/common.go new file mode 100644 index 0000000..c83afc6 --- /dev/null +++ b/pkg/common/common.go @@ -0,0 +1,11 @@ +package common + +import "os" + +func FileChanged(a os.FileInfo, path string) bool { + b, err := os.Lstat(path) + if err != nil { + return true + } + return !os.SameFile(a, b) +} diff --git a/erofs/erofs.go b/pkg/erofs/erofs.go similarity index 99% rename from erofs/erofs.go rename to pkg/erofs/erofs.go index e088b71..86d37bc 100644 --- a/erofs/erofs.go +++ b/pkg/erofs/erofs.go @@ -18,8 +18,8 @@ import ( "github.com/Masterminds/semver/v3" "github.com/pkg/errors" "golang.org/x/sys/unix" - "machinerun.io/atomfs/log" - "machinerun.io/atomfs/mount" + "machinerun.io/atomfs/pkg/log" + "machinerun.io/atomfs/pkg/mount" ) var checkZstdSupported sync.Once diff --git a/pkg/erofs/fs.go b/pkg/erofs/fs.go new file mode 100644 index 0000000..13ff3d5 --- /dev/null +++ b/pkg/erofs/fs.go @@ -0,0 +1,38 @@ +package erofs + +import ( + "io" + + "machinerun.io/atomfs/verity" +) + +type erofs struct { +} + +func New() *erofs { + return &erofs{} +} + +func (fs *erofs) Make(tempdir string, rootfs string, eps *ExcludePaths, verity verity.VerityMetadata) (io.ReadCloser, string, string, error) { +} + +// Mount a filesystem as container root, without host root privileges. +func (fs *erofs) GuestMount(fsFile string, mountpoint string) error { + return nil +} + +func (fs *erofs) Mount(fsFile, mountpoint, rootHash string) error { + return nil +} + +func (fs *erofs) HostMount(fsFile string, mountpoint string, rootHash string) error { + return nil +} + +func (fs *erofs) Umount(mountpoint string) error { + return nil +} + +func (fs *erofs) VerityDataLocation() uint64 { + return 0 +} diff --git a/erofs/mediatype.go b/pkg/erofs/mediatype.go similarity index 100% rename from erofs/mediatype.go rename to pkg/erofs/mediatype.go diff --git a/erofs/superblock.go b/pkg/erofs/superblock.go similarity index 100% rename from erofs/superblock.go rename to pkg/erofs/superblock.go diff --git a/erofs/verity.go b/pkg/erofs/verity.go similarity index 99% rename from erofs/verity.go rename to pkg/erofs/verity.go index d0ca4d4..d678a90 100644 --- a/erofs/verity.go +++ b/pkg/erofs/verity.go @@ -77,7 +77,7 @@ import ( "github.com/martinjungblut/go-cryptsetup" "github.com/pkg/errors" "golang.org/x/sys/unix" - "machinerun.io/atomfs/mount" + "machinerun.io/atomfs/pkg/mount" ) const VerityRootHashAnnotation = "io.stackeroci.stacker.erofs_verity_root_hash" diff --git a/erofs/verity_static.go b/pkg/erofs/verity_static.go similarity index 100% rename from erofs/verity_static.go rename to pkg/erofs/verity_static.go diff --git a/erofs/verity_test.go b/pkg/erofs/verity_test.go similarity index 100% rename from erofs/verity_test.go rename to pkg/erofs/verity_test.go diff --git a/pkg/fs/fs.go b/pkg/fs/fs.go new file mode 100644 index 0000000..36941c0 --- /dev/null +++ b/pkg/fs/fs.go @@ -0,0 +1,30 @@ +package fs + +import ( + "machinerun.io/atomfs/pkg/erofs" + "machinerun.io/atomfs/pkg/squashfs" + "machinerun.io/atomfs/pkg/types" +) + +// New creates a filesystem instance. +func New(fsType types.FilesystemType) types.Filesystem { + switch fsType { + case types.Squashfs: + return squashfs.New() + case types.Erofs: + return erofs.New() + default: + return nil + } +} + +// NewFromMediaType creates a filesystem instance based on media-type. +func NewFromMediaType(mediaType string) types.Filesystem { + if squashfs.IsSquashfsMediaType(mediaType) { + return New(types.Squashfs) + } else if erofs.IsErofsMediaType(mediaType) { + return New(types.Erofs) + } + + return nil +} diff --git a/log/log.go b/pkg/log/log.go similarity index 100% rename from log/log.go rename to pkg/log/log.go diff --git a/mount/mountinfo.go b/pkg/mount/mountinfo.go similarity index 100% rename from mount/mountinfo.go rename to pkg/mount/mountinfo.go diff --git a/pkg/squashfs/fs.go b/pkg/squashfs/fs.go new file mode 100644 index 0000000..49c8451 --- /dev/null +++ b/pkg/squashfs/fs.go @@ -0,0 +1,41 @@ +package squashfs + +import ( + "io" + + "machinerun.io/atomfs/verity" +) + +type squashfs struct { +} + +func New() *squashfs { + return &squashfs{} +} + +func (fs *squashfs) Make(tempdir string, rootfs string, eps *ExcludePaths, verity verity.VerityMetadata) (io.ReadCloser, string, string, error) { +} + +// Mount a filesystem as container root, without host root privileges. +func (fs *squashfs) GuestMount(fsFile string, mountpoint string) error { + return nil +} + +func (fs *squashfs) Mount(fsFile, mountpoint, rootHash string) error { + return nil +} + +func (fs *squashfs) HostMount(fsFile string, mountpoint string, rootHash string) error { + return nil +} + +func (fs *squashfs) Umount(mountpoint string) error { + return nil +} + +func (fs *squashfs) VerityDataLocation() uint64 { + return 0 +} + +func (fs *squashfs) ExtractSingle(fsFile string, extractDir string) error { +} diff --git a/squashfs/mediatype.go b/pkg/squashfs/mediatype.go similarity index 71% rename from squashfs/mediatype.go rename to pkg/squashfs/mediatype.go index 051fe9b..5c33c95 100644 --- a/squashfs/mediatype.go +++ b/pkg/squashfs/mediatype.go @@ -3,21 +3,17 @@ package squashfs import ( "fmt" "strings" + + "machinerun.io/atomfs/verity" ) type SquashfsCompression string -type VerityMetadata bool const ( BaseMediaTypeLayerSquashfs = "application/vnd.stacker.image.layer.squashfs" GzipCompression SquashfsCompression = "gzip" ZstdCompression SquashfsCompression = "zstd" - - veritySuffix = "verity" - - VerityMetadataPresent VerityMetadata = true - VerityMetadataMissing VerityMetadata = false ) func IsSquashfsMediaType(mediaType string) bool { @@ -27,11 +23,11 @@ func IsSquashfsMediaType(mediaType string) bool { func GenerateSquashfsMediaType(comp SquashfsCompression, verity VerityMetadata) string { verityString := "" if verity { - verityString = fmt.Sprintf("+%s", veritySuffix) + verityString = fmt.Sprintf("+%s", verity.VeritySuffix) } return fmt.Sprintf("%s+%s%s", BaseMediaTypeLayerSquashfs, comp, verityString) } func HasVerityMetadata(mediaType string) VerityMetadata { - return VerityMetadata(strings.HasSuffix(mediaType, veritySuffix)) + return VerityMetadata(strings.HasSuffix(mediaType, verity.VeritySuffix)) } diff --git a/squashfs/squashfs.go b/pkg/squashfs/squashfs.go similarity index 97% rename from squashfs/squashfs.go rename to pkg/squashfs/squashfs.go index 328b061..f7cdebd 100644 --- a/squashfs/squashfs.go +++ b/pkg/squashfs/squashfs.go @@ -18,8 +18,10 @@ import ( "github.com/Masterminds/semver/v3" "github.com/pkg/errors" "golang.org/x/sys/unix" - "machinerun.io/atomfs/log" - "machinerun.io/atomfs/mount" + "machinerun.io/atomfs/pkg/common" + "machinerun.io/atomfs/pkg/log" + "machinerun.io/atomfs/pkg/mount" + _ "machinerun.io/atomfs/verity" ) var checkZstdSupported sync.Once @@ -168,7 +170,7 @@ func MakeSquashfs(tempdir string, rootfs string, eps *ExcludePaths, verity Verit } if verity { - rootHash, err = appendVerityData(tmpSquashfs.Name()) + rootHash, err = verity.AppendVerityData(tmpSquashfs.Name()) if err != nil { return nil, "", rootHash, err } @@ -366,7 +368,7 @@ func squashFuse(squashFile, extractDir string) (*exec.Cmd, error) { } } } - for count := 0; !fileChanged(fiPre, extractDir); count++ { + for count := 0; !common.FileChanged(fiPre, extractDir); count++ { if cmd.ProcessState != nil { // process exited, the Wait() call in the goroutine above // caused ProcessState to be populated. @@ -742,3 +744,14 @@ func whichSearch(name string, paths []string) string { return "" } + +func verityDataLocation(sblock *superblock) (uint64, error) { + squashLen := sblock.size + + // squashfs is padded out to the nearest 4k + if squashLen%4096 != 0 { + squashLen = squashLen + (4096 - squashLen%4096) + } + + return squashLen, nil +} diff --git a/squashfs/superblock.go b/pkg/squashfs/superblock.go similarity index 100% rename from squashfs/superblock.go rename to pkg/squashfs/superblock.go diff --git a/squashfs/verity_static.go b/pkg/squashfs/verity_static.go similarity index 100% rename from squashfs/verity_static.go rename to pkg/squashfs/verity_static.go diff --git a/squashfs/verity_test.go b/pkg/squashfs/verity_test.go similarity index 100% rename from squashfs/verity_test.go rename to pkg/squashfs/verity_test.go diff --git a/pkg/types/types.go b/pkg/types/types.go new file mode 100644 index 0000000..ccdf95c --- /dev/null +++ b/pkg/types/types.go @@ -0,0 +1,32 @@ +package types + +import ( + "io" + + "machinerun.io/atomfs/verity" +) + +type Filesystem interface { + // Make a filesystem image. + Make(tempdir string, rootfs string, eps *ExcludePaths, verity verity.VerityMetadata) (io.ReadCloser, string, string, error) + + // Mount a filesystem as container root, without host root privileges. + GuestMount(fsFile string, mountpoint string) error + + Mount(fs, mountpoint, rootHash string) error + + HostMount(fs string, mountpoint string, rootHash string) error + + Umount(mountpoint string) error + + VerityDataLocation() uint64 + + ExtractSingle(fsFile string, extractDir string) error +} + +type FilesystemType string + +const ( + Squashfs FilesystemType = "squashfs" + Erofs FilesystemType = "erofs" +) diff --git a/verity/mediatype.go b/verity/mediatype.go new file mode 100644 index 0000000..6333e79 --- /dev/null +++ b/verity/mediatype.go @@ -0,0 +1,10 @@ +package verity + +type VerityMetadata bool + +const ( + VeritySuffix = "verity" + + VerityMetadataPresent VerityMetadata = true + VerityMetadataMissing VerityMetadata = false +) diff --git a/squashfs/verity.go b/verity/verity.go similarity index 95% rename from squashfs/verity.go rename to verity/verity.go index 6642f86..b81275b 100644 --- a/squashfs/verity.go +++ b/verity/verity.go @@ -1,4 +1,4 @@ -package squashfs +package verity // #cgo pkg-config: libcryptsetup devmapper --static // #include @@ -77,7 +77,7 @@ import ( "github.com/martinjungblut/go-cryptsetup" "github.com/pkg/errors" "golang.org/x/sys/unix" - "machinerun.io/atomfs/mount" + "machinerun.io/atomfs/pkg/mount" ) const VerityRootHashAnnotation = "io.stackeroci.stacker.squashfs_verity_root_hash" @@ -130,7 +130,7 @@ func isCryptsetupEINVAL(err error) bool { var cryptsetupTooOld = errors.Errorf("libcryptsetup not new enough, need >= 2.3.0") -func appendVerityData(file string) (string, error) { +func AppendVerityData(file string) (string, error) { fi, err := os.Lstat(file) if err != nil { return "", errors.WithStack(err) @@ -179,27 +179,8 @@ func appendVerityData(file string) (string, error) { return fmt.Sprintf("%x", rootHash), errors.WithStack(err) } -func verityDataLocation(sblock *superblock) (uint64, error) { - squashLen := sblock.size - - // squashfs is padded out to the nearest 4k - if squashLen%4096 != 0 { - squashLen = squashLen + (4096 - squashLen%4096) - } - - return squashLen, nil -} - func verityName(p string) string { - return fmt.Sprintf("%s-%s", p, veritySuffix) -} - -func fileChanged(a os.FileInfo, path string) bool { - b, err := os.Lstat(path) - if err != nil { - return true - } - return !os.SameFile(a, b) + return fmt.Sprintf("%s-%s", p, VeritySuffix) } // Mount a filesystem as container root, without host root @@ -454,7 +435,7 @@ func Umount(mountpoint string) error { // was this a verity mount or a regular loopback mount? (if it's a // regular loopback mount, we detached it above, so need to do anything // special here; verity doesn't play as nicely) - if strings.HasSuffix(theMount.Source, veritySuffix) { + if strings.HasSuffix(theMount.Source, VeritySuffix) { // find the loop device that backs the verity device deviceNo, err := findLoopBackingVerity(theMount.Source) if err != nil {