diff --git a/src/runtime/pkg/containerd-shim-v2/create.go b/src/runtime/pkg/containerd-shim-v2/create.go index 6dcb459637..0f2d5f7e65 100644 --- a/src/runtime/pkg/containerd-shim-v2/create.go +++ b/src/runtime/pkg/containerd-shim-v2/create.go @@ -315,7 +315,7 @@ func checkAndMount(s *service, r *taskAPI.CreateTaskRequest) (bool, error) { return false, nil } - if virtcontainers.HasErofsOptions(m.Options) { + if virtcontainers.IsErofsRootFS(virtcontainers.RootFs{Options: m.Options, Type: m.Type}) { return false, nil } diff --git a/src/runtime/pkg/katautils/create.go b/src/runtime/pkg/katautils/create.go index 1b83adf027..350a32fc54 100644 --- a/src/runtime/pkg/katautils/create.go +++ b/src/runtime/pkg/katautils/create.go @@ -130,7 +130,7 @@ func CreateSandbox(ctx context.Context, vci vc.VC, ociSpec specs.Spec, runtimeCo } if !rootFs.Mounted && len(sandboxConfig.Containers) == 1 { - if rootFs.Source != "" && !vc.HasOptionPrefix(rootFs.Options, vc.VirtualVolumePrefix) && !vc.HasErofsOptions(rootFs.Options) { + if rootFs.Source != "" && !vc.HasOptionPrefix(rootFs.Options, vc.VirtualVolumePrefix) && !vc.IsErofsRootFS(rootFs) { realPath, err := ResolvePath(rootFs.Source) if err != nil { return nil, vc.Process{}, err @@ -244,7 +244,7 @@ func CreateContainer(ctx context.Context, sandbox vc.VCSandbox, ociSpec specs.Sp } if !rootFs.Mounted { - if rootFs.Source != "" && !vc.IsNydusRootFSType(rootFs.Type) && !vc.HasErofsOptions(rootFs.Options) { + if rootFs.Source != "" && !vc.IsNydusRootFSType(rootFs.Type) && !vc.IsErofsRootFS(rootFs) { realPath, err := ResolvePath(rootFs.Source) if err != nil { return vc.Process{}, err diff --git a/src/runtime/virtcontainers/container.go b/src/runtime/virtcontainers/container.go index 484d9e2452..b554df4cfa 100644 --- a/src/runtime/virtcontainers/container.go +++ b/src/runtime/virtcontainers/container.go @@ -821,12 +821,48 @@ func (c *Container) createMounts(ctx context.Context) error { return c.createBlockDevices(ctx) } +func findMountSource(mnt string) (string, error) { + output, err := os.ReadFile("/proc/mounts") + if err != nil { + return "", err + } + + // /proc/mounts has 6 fields per line, one mount per line, e.g. + // /dev/loop0 /var/lib/containerd/io.containerd.snapshotter.v1.erofs/snapshots/1/fs erofs ro,relatime,user_xattr,acl,cache_strategy=readaround 0 0 + for _, line := range strings.Split(string(output), "\n") { + parts := strings.Split(line, " ") + if len(parts) == 6 { + switch parts[2] { + case "erofs": + if parts[1] == mnt { + return parts[0], nil + } + } + } + } + return "", fmt.Errorf("erofs mount not found for %s", mnt) +} + func (c *Container) createErofsDevices() ([]config.DeviceInfo, error) { var deviceInfos []config.DeviceInfo - if HasErofsOptions(c.rootFs.Options) { - parsedOptions := parseRootFsOptions(c.rootFs.Options) - for _, path := range parsedOptions { - di, err := c.createDeviceInfo(path+"/layer.erofs", path+"/layer.erofs", true, true) + if IsErofsRootFS(c.rootFs) { + lowerdirs := parseErofsRootFsOptions(c.rootFs.Options) + for _, path := range lowerdirs { + s, err := findMountSource(path) + if err != nil { + return nil, err + } + if strings.HasPrefix(s, "/dev/loop") { + b, err := os.ReadFile(fmt.Sprintf("/sys/block/loop%s/loop/backing_file", strings.TrimPrefix(s, "/dev/loop"))) + if err != nil { + return nil, err + } + s = strings.TrimSuffix(string(b), "\n") + } + if filepath.Base(s) != "layer.erofs" { + return nil, fmt.Errorf("unsupported mount source %s for %s", s, path) + } + di, err := c.createDeviceInfo(s, s, true, true) if err != nil { return nil, err } @@ -1076,7 +1112,7 @@ func (c *Container) create(ctx context.Context) (err error) { } }() - if c.checkBlockDeviceSupport(ctx) && !IsNydusRootFSType(c.rootFs.Type) && !HasErofsOptions(c.rootFs.Options) { + if c.checkBlockDeviceSupport(ctx) && !IsNydusRootFSType(c.rootFs.Type) && !IsErofsRootFS(c.rootFs) { // If the rootfs is backed by a block device, go ahead and hotplug it to the guest if err = c.hotplugDrive(ctx); err != nil { return diff --git a/src/runtime/virtcontainers/fs_share_linux.go b/src/runtime/virtcontainers/fs_share_linux.go index 3599e618c2..7d5455fe4b 100644 --- a/src/runtime/virtcontainers/fs_share_linux.go +++ b/src/runtime/virtcontainers/fs_share_linux.go @@ -626,7 +626,7 @@ func (f *FilesystemShare) ShareRootFilesystem(ctx context.Context, c *Container) return f.shareRootFilesystemWithNydus(ctx, c) } - if HasErofsOptions(c.rootFs.Options) { + if IsErofsRootFS(c.rootFs) { return f.shareRootFilesystemWithErofs(ctx, c) } diff --git a/src/runtime/virtcontainers/kata_agent.go b/src/runtime/virtcontainers/kata_agent.go index f287d4d1d2..7c22373e4d 100644 --- a/src/runtime/virtcontainers/kata_agent.go +++ b/src/runtime/virtcontainers/kata_agent.go @@ -2703,9 +2703,13 @@ func IsNydusRootFSType(s string) bool { return strings.HasPrefix(path.Base(s), "nydus-overlayfs") } -// HasErofsOptions checks if any of the options contain io.containerd.snapshotter.v1.erofs path -func HasErofsOptions(options []string) bool { - for _, opt := range options { +// IsErofsRootFS checks if any of the options contain io.containerd.snapshotter.v1.erofs path +func IsErofsRootFS(root RootFs) bool { + // TODO: support containerd mount manager: https://github.com/containerd/containerd/issues/11303 + if root.Type != "overlay" { + return false + } + for _, opt := range root.Options { if strings.Contains(opt, "io.containerd.snapshotter.v1.erofs") { return true } @@ -2713,21 +2717,15 @@ func HasErofsOptions(options []string) bool { return false } -func parseRootFsOptions(options []string) []string { +func parseErofsRootFsOptions(options []string) []string { lowerdirs := []string{} for _, opt := range options { if strings.HasPrefix(opt, "lowerdir=") { lowerdirValue := strings.TrimPrefix(opt, "lowerdir=") - paths := strings.Split(lowerdirValue, ":") - - for _, path := range paths { - path = strings.TrimSuffix(path, "/fs") - lowerdirs = append(lowerdirs, path) - } + lowerdirs = append(lowerdirs, strings.Split(lowerdirValue, ":")...) } } - return lowerdirs }