From 87177a7fe873a61b2005d73534c4205f9a8b604d Mon Sep 17 00:00:00 2001 From: Tianyu Zhou Date: Tue, 30 Jun 2026 21:09:28 +0800 Subject: [PATCH] fix: donate EROFS submount FDs in goferless mode When an EROFS rootfs runs without a gofer process, runsc still has to provide image FDs for EROFS config mounts. The goferless path only opened the rootfs image, so boot could run out of gofer FDs while preparing submounts. This patch donates EROFS submount image FDs in config mount order and adds a test to cover such case. Signed-off-by: Tianyu Zhou --- runsc/container/container.go | 24 +++++++++++++++++++++++- runsc/container/container_test.go | 13 ++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/runsc/container/container.go b/runsc/container/container.go index f0bfbf2c48..ff15f731ef 100644 --- a/runsc/container/container.go +++ b/runsc/container/container.go @@ -1389,7 +1389,29 @@ func (c *Container) createGoferProcess(conf *config.Config, mountHints *boot.Pod if err != nil { return nil, nil, nil, nil, fmt.Errorf("opening rootfs image %q: %v", rootfsHint.Mount.Source, err) } - return []*os.File{ioFile}, nil, nil, nil, nil + ioFiles := []*os.File{ioFile} + cu := cleanup.Make(func() { + for _, f := range ioFiles { + _ = f.Close() + } + }) + defer cu.Clean() + cfgIdx := 1 + for _, m := range c.Spec.Mounts { + if !specutils.HasMountConfig(m) { + continue + } + if c.GoferMountConfs[cfgIdx].ShouldUseErofs() { + f, err := os.Open(m.Source) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("opening EROFS image %q: %v", m.Source, err) + } + ioFiles = append(ioFiles, f) + } + cfgIdx++ + } + cu.Release() + return ioFiles, nil, nil, nil, nil } // Ensure we don't leak FDs to the gofer process. diff --git a/runsc/container/container_test.go b/runsc/container/container_test.go index 8b282c4fe0..c954a0fcc8 100644 --- a/runsc/container/container_test.go +++ b/runsc/container/container_test.go @@ -4460,7 +4460,7 @@ func createRootfsEROFS(dir string) (string, string, error) { // Handcraft the following mount points that the sentry mounts need, because EROFS // does not support creating synthetic directories yet and we may not want to use // overlay in some tests. - for _, dir := range []string{"dev", "proc", "sys", "tmp"} { + for _, dir := range []string{"data", "dev", "proc", "sys", "tmp"} { if err := os.Mkdir(filepath.Join(rootfsDir, dir), 0755); err != nil { return "", "", fmt.Errorf("os.Mkdir() failed: %v", err) } @@ -4523,6 +4523,17 @@ func TestRootfsEROFS(t *testing.T) { Source: mountDir, }, }, + + // Case 3: EROFS rootfs with an EROFS backed config mount. This stays + // in goferless mode and requires donating both EROFS image FDs. + { + { + Type: erofs.Name, + Destination: "/data", + Source: rootfsImage, + Options: []string{"ro"}, + }, + }, } { spec.Mounts = mounts