diff --git a/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/app-containers/containerd/0001-ctr-no-such-device-fix.patch b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/app-containers/containerd/0001-ctr-no-such-device-fix.patch new file mode 100644 index 0000000000..d0675ed8ca --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/app-containers/containerd/0001-ctr-no-such-device-fix.patch @@ -0,0 +1,228 @@ +From 3d509bcd335b15cece69ebfa117681d2715df930 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Poulin?= +Date: Wed, 26 Nov 2025 18:28:26 -0500 +Subject: [PATCH 1/2] core/mount/manager: add tests for WithTemporary option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add tests for the WithTemporary mount activation used by `ctr images mount`. +Covers bind mount and overlay scenarios to catch regressions like #12549. + +Signed-off-by: Jérôme Poulin +--- + core/mount/manager/manager_linux_test.go | 157 ++++++++++++++++++++++- + 1 file changed, 156 insertions(+), 1 deletion(-) + +diff --git a/core/mount/manager/manager_linux_test.go b/core/mount/manager/manager_linux_test.go +index 06ab38c2b7b0..b62f3fdc8cf4 100644 +--- a/core/mount/manager/manager_linux_test.go ++++ b/core/mount/manager/manager_linux_test.go +@@ -314,6 +314,161 @@ func TestLoopbackOverlay(t *testing.T) { + } + } + ++// TestTemporaryMountActivation tests the WithTemporary option used by ++// `ctr images mount`. This verifies the bind mount returned in info.System ++// points to a valid, mounted directory. ++func TestTemporaryMountActivation(t *testing.T) { ++ testutil.RequiresRoot(t) ++ ctx := logtest.WithT(context.Background(), t) ++ ctx = namespaces.WithNamespace(ctx, "test") ++ td := t.TempDir() ++ metadb := filepath.Join(td, "mounts.db") ++ targetdir := filepath.Join(td, "m") ++ db, err := bolt.Open(metadb, 0600, nil) ++ require.NoError(t, err) ++ defer db.Close() ++ ++ sourcedir := filepath.Join(td, "source") ++ require.NoError(t, os.MkdirAll(sourcedir, 0755)) ++ ++ a := fstest.Apply( ++ fstest.CreateFile("/testfile.txt", []byte("test content\n"), 0644), ++ fstest.CreateDir("/testdir", 0755), ++ ) ++ require.NoError(t, a.Apply(sourcedir)) ++ ++ mounts := []mount.Mount{ ++ { ++ Type: "bind", ++ Source: sourcedir, ++ Options: []string{"rbind", "ro"}, ++ }, ++ } ++ ++ m, err := NewManager(db, targetdir) ++ require.NoError(t, err) ++ ++ ainfo, err := m.Activate(ctx, "temp-mount-test", mounts, mount.WithTemporary) ++ require.NoError(t, err) ++ defer func() { ++ assert.NoError(t, m.Deactivate(ctx, "temp-mount-test")) ++ }() ++ ++ require.NotEmpty(t, ainfo.System, "Expected system mounts to be returned") ++ ++ require.Len(t, ainfo.System, 1, "Expected exactly one system mount") ++ systemMount := ainfo.System[0] ++ assert.Equal(t, "bind", systemMount.Type, "Expected bind mount type") ++ ++ sourceInfo, err := os.Stat(systemMount.Source) ++ require.NoError(t, err, "Bind mount source %q should exist", systemMount.Source) ++ assert.True(t, sourceInfo.IsDir(), "Bind mount source should be a directory") ++ ++ testFile := filepath.Join(systemMount.Source, "testfile.txt") ++ content, err := os.ReadFile(testFile) ++ require.NoError(t, err, "Should be able to read test file from bind mount source") ++ assert.Equal(t, "test content\n", string(content), "Test file content should match") ++ ++ targetMount := filepath.Join(td, "target") ++ require.NoError(t, os.MkdirAll(targetMount, 0755)) ++ ++ err = mount.All(ainfo.System, targetMount) ++ require.NoError(t, err, "Should be able to mount system mounts to target") ++ defer testutil.Unmount(t, targetMount) ++ ++ targetTestFile := filepath.Join(targetMount, "testfile.txt") ++ targetContent, err := os.ReadFile(targetTestFile) ++ require.NoError(t, err, "Should be able to read test file from target mount") ++ assert.Equal(t, "test content\n", string(targetContent), "Target test file content should match") ++} ++ ++// TestTemporaryOverlayMountActivation tests WithTemporary with overlay mounts, ++// which is the more common case for `ctr images mount` with overlay snapshotter. ++func TestTemporaryOverlayMountActivation(t *testing.T) { ++ testutil.RequiresRoot(t) ++ ctx := logtest.WithT(context.Background(), t) ++ ctx = namespaces.WithNamespace(ctx, "test") ++ td := t.TempDir() ++ metadb := filepath.Join(td, "mounts.db") ++ targetdir := filepath.Join(td, "m") ++ db, err := bolt.Open(metadb, 0600, nil) ++ require.NoError(t, err) ++ defer db.Close() ++ ++ lower1 := filepath.Join(td, "lower1") ++ lower2 := filepath.Join(td, "lower2") ++ upper := filepath.Join(td, "upper") ++ work := filepath.Join(td, "work") ++ ++ require.NoError(t, os.MkdirAll(lower1, 0755)) ++ require.NoError(t, os.MkdirAll(lower2, 0755)) ++ require.NoError(t, os.MkdirAll(upper, 0755)) ++ require.NoError(t, os.MkdirAll(work, 0755)) ++ ++ require.NoError(t, os.WriteFile(filepath.Join(lower1, "file1.txt"), []byte("layer1\n"), 0644)) ++ require.NoError(t, os.WriteFile(filepath.Join(lower2, "file2.txt"), []byte("layer2\n"), 0644)) ++ ++ mounts := []mount.Mount{ ++ { ++ Type: "overlay", ++ Source: "overlay", ++ Options: []string{ ++ fmt.Sprintf("lowerdir=%s:%s", lower2, lower1), ++ fmt.Sprintf("upperdir=%s", upper), ++ fmt.Sprintf("workdir=%s", work), ++ }, ++ }, ++ } ++ ++ m, err := NewManager(db, targetdir) ++ require.NoError(t, err) ++ ++ ainfo, err := m.Activate(ctx, "temp-overlay-test", mounts, mount.WithTemporary) ++ require.NoError(t, err) ++ defer func() { ++ assert.NoError(t, m.Deactivate(ctx, "temp-overlay-test")) ++ }() ++ ++ require.NotEmpty(t, ainfo.System, "Expected system mounts to be returned") ++ ++ require.Len(t, ainfo.System, 1, "Expected exactly one system mount") ++ systemMount := ainfo.System[0] ++ assert.Equal(t, "bind", systemMount.Type, "Expected bind mount type") ++ ++ sourceInfo, err := os.Stat(systemMount.Source) ++ require.NoError(t, err, "Bind mount source %q should exist", systemMount.Source) ++ assert.True(t, sourceInfo.IsDir(), "Bind mount source should be a directory") ++ ++ file1 := filepath.Join(systemMount.Source, "file1.txt") ++ file2 := filepath.Join(systemMount.Source, "file2.txt") ++ ++ content1, err := os.ReadFile(file1) ++ require.NoError(t, err, "Should be able to read file1 from overlay via bind source") ++ assert.Equal(t, "layer1\n", string(content1)) ++ ++ content2, err := os.ReadFile(file2) ++ require.NoError(t, err, "Should be able to read file2 from overlay via bind source") ++ assert.Equal(t, "layer2\n", string(content2)) ++ ++ targetMount := filepath.Join(td, "target") ++ require.NoError(t, os.MkdirAll(targetMount, 0755)) ++ ++ err = mount.All(ainfo.System, targetMount) ++ require.NoError(t, err, "Should be able to mount system mounts to target") ++ defer testutil.Unmount(t, targetMount) ++ ++ targetFile1 := filepath.Join(targetMount, "file1.txt") ++ targetFile2 := filepath.Join(targetMount, "file2.txt") ++ ++ targetContent1, err := os.ReadFile(targetFile1) ++ require.NoError(t, err) ++ assert.Equal(t, "layer1\n", string(targetContent1)) ++ ++ targetContent2, err := os.ReadFile(targetFile2) ++ require.NoError(t, err) ++ assert.Equal(t, "layer2\n", string(targetContent2)) ++} ++ + func initalizeBlockDevice(td string, a fstest.Applier) (string, error) { + file, err := os.CreateTemp(td, "fs-") + if err != nil { +@@ -342,7 +497,7 @@ func initalizeBlockDevice(td string, a fstest.Applier) (string, error) { + + m := mount.Mount{ + Type: "ext4", +- Source: dpath, // previous mount ++ Source: dpath, + Options: []string{"loop"}, + } + target, err := os.MkdirTemp(td, "mount-") + +From 1d79082735d46fe24ded00a55ea6e3a33954593e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Poulin?= +Date: Wed, 26 Nov 2025 18:29:31 -0500 +Subject: [PATCH 2/2] core/mount/manager: fix bind mount missing rbind option +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The bind mount created for temporary activations was missing the +Options field, causing mount to fail with "no such device" because +the MS_BIND flag wasn't being set. + +Fixes #12549 + +Signed-off-by: Jérôme Poulin +--- + core/mount/manager/manager.go | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/core/mount/manager/manager.go b/core/mount/manager/manager.go +index d43c1beb8e7c..5163c17dbcac 100644 +--- a/core/mount/manager/manager.go ++++ b/core/mount/manager/manager.go +@@ -430,9 +430,9 @@ func (mm *mountManager) Activate(ctx context.Context, name string, mounts []moun + // TODO: Add config for whether to add the bind mount? + if config.Temporary && firstSystemMount > 0 { + mounts = append(mounts, mount.Mount{ +- Type: "bind", +- Source: mounted[firstSystemMount-1].MountPoint, +- // TODO : Configurable bind mount options? ++ Type: "bind", ++ Source: mounted[firstSystemMount-1].MountPoint, ++ Options: []string{"rbind"}, + }) + } + diff --git a/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/app-containers/containerd/README.md b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/app-containers/containerd/README.md new file mode 100644 index 0000000000..9e27991eac --- /dev/null +++ b/sdk_container/src/third_party/coreos-overlay/coreos/user-patches/app-containers/containerd/README.md @@ -0,0 +1,3 @@ +The `0001-ctr-no-such-device-fix.patch` fixes a regression detected by +our `cl.toolbox.dnf-install` test. It is merged into the 2.2 release +branch in upstream, but no release with the fix happened yet.