Skip to content

feat(erofs): initial commit for erofs support #626

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:
echo "running kernel is: $(uname -a)"
- name: docker-clone
run: |
make docker-clone "STACKER_DOCKER_BASE=docker://" CLONE_D="$PWD/.build/oci-clone"
make docker-clone "STACKER_DOCKER_BASE=docker://ghcr.io/project-stacker/" CLONE_D="$PWD/.build/oci-clone"
- name: Go-download
run: |
make go-download
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ on:

jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
services:
registry:
image: ghcr.io/project-stacker/registry:2
Expand Down Expand Up @@ -71,7 +71,7 @@ jobs:
echo "running kernel is: $(uname -a)"
- name: docker-clone
run: |
make docker-clone "STACKER_DOCKER_BASE=docker://" CLONE_D="$PWD/.build/oci-clone"
make docker-clone "STACKER_DOCKER_BASE=docker://ghcr.io/project-stacker/" CLONE_D="$PWD/.build/oci-clone"
- name: Go-download
run: |
make go-download
Expand Down
11 changes: 6 additions & 5 deletions cmd/stacker/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"

cli "github.com/urfave/cli/v2"
"machinerun.io/atomfs/squashfs"
"machinerun.io/atomfs/pkg/verity"
"stackerbuild.io/stacker/pkg/stacker"
"stackerbuild.io/stacker/pkg/types"
)
Expand Down Expand Up @@ -52,12 +52,13 @@ func initCommonBuildFlags() []cli.Flag {
},
&cli.StringSliceFlag{
Name: "layer-type",
Usage: "set the output layer type (supported values: tar, squashfs); can be supplied multiple times",
Usage: "set the output layer type (supported values: tar, squashfs, erofs); can be supplied multiple times",
Value: cli.NewStringSlice("tar"),
},
&cli.BoolFlag{
Name: "no-squashfs-verity",
Usage: "do not append dm-verity data to squashfs archives",
Name: "no-verity",
Usage: "do not append dm-verity data to fs archives",
Aliases: []string{"no-squashfs-verity"},
},
&cli.BoolFlag{
Name: "require-hash",
Expand Down Expand Up @@ -103,7 +104,7 @@ func newBuildArgs(ctx *cli.Context) (stacker.BuildArgs, error) {
AnnotationsNamespace: ctx.String("annotations-namespace"),
}
var err error
verity := squashfs.VerityMetadata(!ctx.Bool("no-squashfs-verity"))
verity := verity.VerityMetadata(!ctx.Bool("no-verity"))
args.LayerTypes, err = types.NewLayerTypes(ctx.StringSlice("layer-type"), verity)
return args, err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/stacker/inspect.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/opencontainers/umoci/oci/casext"
"github.com/pkg/errors"
cli "github.com/urfave/cli/v2"
stackeroci "machinerun.io/atomfs/oci"
stackeroci "machinerun.io/atomfs/pkg/oci"
)

var inspectCmd = cli.Command{
Expand Down
8 changes: 4 additions & 4 deletions cmd/stacker/internal_go.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/pkg/errors"
cli "github.com/urfave/cli/v2"
"golang.org/x/sys/unix"
"machinerun.io/atomfs"
"machinerun.io/atomfs/pkg/molecule"
"stackerbuild.io/stacker/pkg/lib"
"stackerbuild.io/stacker/pkg/log"
"stackerbuild.io/stacker/pkg/overlay"
Expand Down Expand Up @@ -176,14 +176,14 @@ func doAtomfsMount(ctx *cli.Context) error {
tag := ctx.Args().Get(0)
mountpoint := ctx.Args().Get(1)

opts := atomfs.MountOCIOpts{
opts := molecule.MountOCIOpts{
OCIDir: config.OCIDir,
Tag: tag,
Target: mountpoint,
AllowMissingVerityData: true,
}

mol, err := atomfs.BuildMoleculeFromOCI(opts)
mol, err := molecule.BuildMoleculeFromOCI(opts)
if err != nil {
return err
}
Expand All @@ -199,5 +199,5 @@ func doAtomfsUmount(ctx *cli.Context) error {
}

mountpoint := ctx.Args().Get(0)
return atomfs.Umount(mountpoint)
return molecule.Umount(mountpoint)
}
6 changes: 3 additions & 3 deletions cmd/stacker/publish.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"github.com/pkg/errors"
cli "github.com/urfave/cli/v2"
"machinerun.io/atomfs/squashfs"
"machinerun.io/atomfs/pkg/verity"
"stackerbuild.io/stacker/pkg/lib"
"stackerbuild.io/stacker/pkg/stacker"
"stackerbuild.io/stacker/pkg/types"
Expand Down Expand Up @@ -69,7 +69,7 @@ var publishCmd = cli.Command{
},
&cli.StringSliceFlag{
Name: "layer-type",
Usage: "set the output layer type (supported values: tar, squashfs); can be supplied multiple times",
Usage: "set the output layer type (supported values: tar, squashfs, erofs); can be supplied multiple times",
Value: cli.NewStringSlice("tar"),
},
&cli.StringSliceFlag{
Expand Down Expand Up @@ -108,7 +108,7 @@ func beforePublish(ctx *cli.Context) error {
}

func doPublish(ctx *cli.Context) error {
verity := squashfs.VerityMetadata(!ctx.Bool("no-squashfs-verity"))
verity := verity.VerityMetadata(!ctx.Bool("no-verity"))
layerTypes, err := types.NewLayerTypes(ctx.StringSlice("layer-type"), verity)
if err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions cmd/stacker/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ func validateLayerTypeFlags(ctx *cli.Context) error {
break
case "squashfs":
break
case "erofs":
break
default:
return errors.Errorf("unknown layer type: %s", layerType)
}
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ require (
github.com/mitchellh/hashstructure v1.1.0
github.com/moby/buildkit v0.11.4
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc4
github.com/opencontainers/image-spec v1.1.0
github.com/opencontainers/umoci v0.4.8-0.20220412065115-12453f247749
github.com/pkg/errors v0.9.1
github.com/pkg/xattr v0.4.9
Expand Down Expand Up @@ -278,7 +278,7 @@ require (
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
machinerun.io/atomfs v1.1.3
machinerun.io/atomfs v1.2.0
modernc.org/libc v1.37.6 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.7.2 // indirect
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -754,8 +754,8 @@ github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3ev
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0=
github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
Expand Down Expand Up @@ -1598,8 +1598,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
machinerun.io/atomfs v1.1.3 h1:oV1SH7VI2MqAks7FlirhLLKvyVcJkMB0NFevXF8EJaU=
machinerun.io/atomfs v1.1.3/go.mod h1:qXz4epm3/7vEpEyf9YaTCafp3CwbUeDa1XrYyx7qbPc=
machinerun.io/atomfs v1.2.0 h1:w2YtDncppFjOKWGeK0yfgCYcB5dJIZSngVHb6UWljv0=
machinerun.io/atomfs v1.2.0/go.mod h1:jrGbuGXiCPi4LFoMvcPRnqPGlxe3pW8UtLEnhUcGRmI=
modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
Expand Down
7 changes: 7 additions & 0 deletions install-build-deps.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ installdeps_ubuntu() {
squashfuse
libarchive-tools
shellcheck
erofs-utils
erofsfuse
)

case "$VERSION_ID" in
Expand Down Expand Up @@ -86,6 +88,11 @@ installdeps_ubuntu() {
sudo apt -yy install golang-go
go version
fi

# cloud kernels, like linux-azure, don't include erofs in the linux-modules package and instead put it linux-modules-extra
if ! modinfo erofs &>/dev/null; then
sudo apt -yy install linux-modules-extra-$(uname -r)
fi
}

enable_userns() {
Expand Down
5 changes: 3 additions & 2 deletions pkg/lib/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (
"github.com/opencontainers/umoci/mutate"
"github.com/opencontainers/umoci/oci/casext"
"github.com/stretchr/testify/assert"
"machinerun.io/atomfs/squashfs"
"machinerun.io/atomfs/pkg/squashfs"
"machinerun.io/atomfs/pkg/verity"
)

func createImage(dir string, tag string) error {
Expand Down Expand Up @@ -48,7 +49,7 @@ func createImage(dir string, tag string) error {

// need *something* in the layer, why not just recursively include the
// OCI image for maximum confusion :)
layer, mediaType, _, err := squashfs.MakeSquashfs(dir, path.Join(dir, "oci"), nil, squashfs.VerityMetadataMissing)
layer, mediaType, _, err := squashfs.MakeSquashfs(dir, path.Join(dir, "oci"), nil, verity.VerityMetadataMissing)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/overlay/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/umoci/oci/casext"
"github.com/pkg/errors"
stackeroci "machinerun.io/atomfs/oci"
stackeroci "machinerun.io/atomfs/pkg/oci"
"stackerbuild.io/stacker/pkg/log"
"stackerbuild.io/stacker/pkg/types"
)
Expand Down
18 changes: 11 additions & 7 deletions pkg/overlay/pack.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
"github.com/opencontainers/umoci/oci/layer"
"github.com/pkg/errors"
"github.com/pkg/xattr"
stackeroci "machinerun.io/atomfs/oci"
"machinerun.io/atomfs/squashfs"
stackerfs "machinerun.io/atomfs/pkg/fs"
stackeroci "machinerun.io/atomfs/pkg/oci"
fstypes "machinerun.io/atomfs/pkg/types"
"machinerun.io/atomfs/pkg/verity"
"stackerbuild.io/stacker/pkg/lib"
"stackerbuild.io/stacker/pkg/log"
"stackerbuild.io/stacker/pkg/storage"
Expand Down Expand Up @@ -280,7 +282,8 @@
blob = layer.GenerateInsertLayer(contents, "/", false, &packOptions)
mediaType = ispec.MediaTypeImageLayer
} else {
blob, mediaType, rootHash, err = squashfs.MakeSquashfs(ociDir, contents, nil, layerType.Verity)
fsi := stackerfs.New(fstypes.FilesystemType(layerType.Type))
blob, mediaType, rootHash, err = fsi.Make(ociDir, contents, nil, layerType.Verity)
if err != nil {
return nil, "", "", err
}
Expand All @@ -303,7 +306,7 @@

annotations := map[string]string{}
if rootHash != "" {
annotations[squashfs.VerityRootHashAnnotation] = rootHash
annotations[verity.VerityRootHashAnnotation] = rootHash
}

desc := ispec.Descriptor{
Expand Down Expand Up @@ -443,7 +446,7 @@
} else {
annotations := map[string]string{}
if rootHash != "" {
annotations[squashfs.VerityRootHashAnnotation] = rootHash
annotations[verity.VerityRootHashAnnotation] = rootHash
}
desc, err = mutator.Add(context.Background(), mediaType, blob, history, mutate.NoopCompressor, annotations)
if err != nil {
Expand Down Expand Up @@ -693,10 +696,11 @@
return nil
}

if squashfs.IsSquashfsMediaType(l.MediaType) {
return squashfs.ExtractSingleSquash(
if fsi := stackerfs.NewFromMediaType(l.MediaType); fsi != nil {
return fsi.ExtractSingle(

Check warning on line 700 in pkg/overlay/pack.go

View check run for this annotation

Codecov / codecov/patch

pkg/overlay/pack.go#L700

Added line #L700 was not covered by tests
path.Join(ociDir, "blobs", "sha256", l.Digest.Encoded()), extractDir)
}

switch l.MediaType {
case ispec.MediaTypeImageLayer, ispec.MediaTypeImageLayerGzip:
tarEx.Lock()
Expand Down
37 changes: 23 additions & 14 deletions pkg/types/layer_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@

ispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"machinerun.io/atomfs/squashfs"
"machinerun.io/atomfs/pkg/erofs"
"machinerun.io/atomfs/pkg/squashfs"
"machinerun.io/atomfs/pkg/verity"
)

var ErrEmptyLayers = errors.New("empty layers")

type LayerType struct {
Type string
Verity squashfs.VerityMetadata
Verity verity.VerityMetadata
}

func (lt LayerType) String() string {
Expand Down Expand Up @@ -44,14 +46,14 @@
return errors.Wrapf(err, "bad verity bool: %s", fields[1])
}

lt.Verity = squashfs.VerityMetadata(result)
lt.Verity = verity.VerityMetadata(result)

return nil
}

func NewLayerType(lt string, verity squashfs.VerityMetadata) (LayerType, error) {
func NewLayerType(lt string, verity verity.VerityMetadata) (LayerType, error) {
switch lt {
case "squashfs":
case "squashfs", "erofs":
return LayerType{Type: lt, Verity: verity}, nil
case "tar":
return LayerType{Type: lt}, nil
Expand All @@ -62,31 +64,38 @@

func NewLayerTypeManifest(manifest ispec.Manifest) (LayerType, error) {
if len(manifest.Layers) == 0 {
return NewLayerType("tar", squashfs.VerityMetadataMissing)
return NewLayerType("tar", verity.VerityMetadataMissing)
}

_, verityMetadataPresent := manifest.Layers[0].Annotations[verity.VerityRootHashAnnotation]

switch manifest.Layers[0].MediaType {
case squashfs.BaseMediaTypeLayerSquashfs:
// older stackers generated media types without compression information
fallthrough
case squashfs.GenerateSquashfsMediaType(squashfs.GzipCompression, squashfs.VerityMetadataMissing):
case squashfs.GenerateSquashfsMediaType(squashfs.GzipCompression):
fallthrough

Check warning on line 77 in pkg/types/layer_type.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/layer_type.go#L76-L77

Added lines #L76 - L77 were not covered by tests
case squashfs.GenerateSquashfsMediaType(squashfs.ZstdCompression):
return NewLayerType("squashfs", verity.VerityMetadata(verityMetadataPresent))
case erofs.BaseMediaTypeLayerErofs:
// older stackers generated media types without compression information
fallthrough
case erofs.GenerateErofsMediaType(erofs.LZ4HCCompression):

Check warning on line 83 in pkg/types/layer_type.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/layer_type.go#L80-L83

Added lines #L80 - L83 were not covered by tests
fallthrough
case squashfs.GenerateSquashfsMediaType(squashfs.ZstdCompression, squashfs.VerityMetadataMissing):
return NewLayerType("squashfs", squashfs.VerityMetadataMissing)
case squashfs.GenerateSquashfsMediaType(squashfs.GzipCompression, squashfs.VerityMetadataPresent):
case erofs.GenerateErofsMediaType(erofs.LZ4Compression):

Check warning on line 85 in pkg/types/layer_type.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/layer_type.go#L85

Added line #L85 was not covered by tests
fallthrough
case squashfs.GenerateSquashfsMediaType(squashfs.ZstdCompression, squashfs.VerityMetadataPresent):
return NewLayerType("squashfs", squashfs.VerityMetadataPresent)
case erofs.GenerateErofsMediaType(erofs.ZstdCompression):
return NewLayerType("erofs", verity.VerityMetadata(verityMetadataPresent))

Check warning on line 88 in pkg/types/layer_type.go

View check run for this annotation

Codecov / codecov/patch

pkg/types/layer_type.go#L87-L88

Added lines #L87 - L88 were not covered by tests
case ispec.MediaTypeImageLayerGzip:
fallthrough
case ispec.MediaTypeImageLayer:
return NewLayerType("tar", squashfs.VerityMetadataMissing)
return NewLayerType("tar", verity.VerityMetadataMissing)
default:
return LayerType{}, errors.Errorf("invalid layer type %s", manifest.Layers[0].MediaType)
}
}

func NewLayerTypes(lts []string, verity squashfs.VerityMetadata) ([]LayerType, error) {
func NewLayerTypes(lts []string, verity verity.VerityMetadata) ([]LayerType, error) {
ret := []LayerType{}
for _, lt := range lts {
hoisted, err := NewLayerType(lt, verity)
Expand Down
Loading