From 5f0f0dcaac13a5370a28a42ada9fd6be246ee807 Mon Sep 17 00:00:00 2001 From: ningmingxiao Date: Sun, 26 Oct 2025 22:39:28 +0800 Subject: [PATCH] content: ensure root directory exists before checking fs-verity support Currently, fs-verity support detection fails on fresh containerd installations because the content store root directory (io.containerd.content.v1.content) doesn't exist yet. This directory is only created when pulling images, causing checker to always be false on new hosts. The IsSupported() function attempts to create a temporary directory within rootPath to test fs-verity support, but fails when rootPath doesn't exist, returning an error that is silently ignored. Fix this by ensuring the root directory exists before performing the fs-verity support check in NewLabeledStore(). Signed-off-by: ningmingxiao --- plugins/content/local/store.go | 16 +++++++++-- plugins/content/local/store_test.go | 42 ++++++++++++++++++++++++++--- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/plugins/content/local/store.go b/plugins/content/local/store.go index 794c82c25e..1b9abdd3b0 100644 --- a/plugins/content/local/store.go +++ b/plugins/content/local/store.go @@ -18,8 +18,10 @@ package local import ( "context" + "errors" "fmt" "io" + "io/fs" "os" "path/filepath" "strconv" @@ -84,8 +86,18 @@ func NewStore(root string) (content.Store, error) { // require labels and should use `NewStore`. `NewLabeledStore` is primarily // useful for tests or standalone implementations. func NewLabeledStore(root string, ls LabelStore) (content.Store, error) { - supported, _ := fsverity.IsSupported(root) - + if _, err := os.Stat(root); err != nil { + if !errors.Is(err, fs.ErrNotExist) { + return nil, fmt.Errorf("failed to stat %q: %w", root, err) + } + if err := os.MkdirAll(root, 0755); err != nil { + return nil, fmt.Errorf("failed to mkdir %q: %w", root, err) + } + } + supported, err := fsverity.IsSupported(root) + if err != nil { + log.L.WithError(err).WithField("path", root).Warnf("failed check for fsverity support") + } s := &store{ root: root, ls: ls, diff --git a/plugins/content/local/store_test.go b/plugins/content/local/store_test.go index 54b082dcd6..0ce3a9c9c9 100644 --- a/plugins/content/local/store_test.go +++ b/plugins/content/local/store_test.go @@ -25,6 +25,7 @@ import ( "fmt" "io" "os" + "os/exec" "path/filepath" "reflect" "runtime" @@ -94,15 +95,50 @@ func (mls *memoryLabelStore) Update(d digest.Digest, update map[string]string) ( func TestContent(t *testing.T) { testsuite.ContentSuite(t, "fs", func(ctx context.Context, root string) (context.Context, content.Store, func() error, error) { cs, err := NewLabeledStore(root, newMemoryLabelStore()) - if err != nil { - return nil, nil, nil, err - } + assert.NoError(t, err) return ctx, cs, func() error { return nil }, nil }) } +func TestContentRootDir(t *testing.T) { + // test dir exist + dirExist := t.TempDir() + _, err := NewLabeledStore(dirExist, newMemoryLabelStore()) + assert.NoError(t, err) + // test dir doesn't exist + dir := filepath.Join(t.TempDir(), "test_dir001") + _, err = NewLabeledStore(dir, newMemoryLabelStore()) + assert.NoError(t, err) + _, err = os.Stat(dir) + assert.NoError(t, err) +} + +func TestInvalidPermissionRootDir(t *testing.T) { + // test dir permissions are invalid + if os.Getuid() != 0 { + t.Skip("skipping test that requires root") + } + _, err := exec.LookPath("chattr") + if err != nil { + t.Skip("skipping test that requires chattr command") + } + dirBadPermission := t.TempDir() + cmd := exec.Command("chattr", "+i", dirBadPermission) + _, err = cmd.CombinedOutput() + assert.NoError(t, err) + defer func() { + cmd := exec.Command("chattr", "-i", dirBadPermission) + _, err = cmd.CombinedOutput() + assert.NoError(t, err) + }() + _, err = fsverity.IsSupported(dirBadPermission) + if err == nil { + t.Fatal(fmt.Errorf("err can't be nil")) + } +} + func TestContentWriter(t *testing.T) { ctx, tmpdir, cs, cleanup := contentStoreEnv(t) defer cleanup()