//go:build !windows && !darwin /* Copyright The containerd Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package blockfile import ( "fmt" "os" "os/exec" "path/filepath" "testing" "github.com/containerd/containerd/v2/core/mount" ) func setupSnapshotter(t *testing.T) ([]Opt, error) { mkfs, err := exec.LookPath("mkfs.ext4") if err != nil { t.Skipf("Could not find mkfs.ext4: %v", err) } loopbackSize := int64(8 << 20) // 8 MB if os.Getpagesize() > 4096 { loopbackSize = int64(16 << 20) // 16 MB } scratch := filepath.Join(t.TempDir(), "scratch") scratchDevFile, err := os.OpenFile(scratch, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) if err != nil { return nil, fmt.Errorf("failed to create %s: %w", scratch, err) } if err := scratchDevFile.Truncate(loopbackSize); err != nil { scratchDevFile.Close() return nil, fmt.Errorf("failed to resize %s file: %w", scratch, err) } if err := scratchDevFile.Sync(); err != nil { scratchDevFile.Close() return nil, fmt.Errorf("failed to sync %s file: %w", scratch, err) } scratchDevFile.Close() if out, err := exec.Command(mkfs, scratch).CombinedOutput(); err != nil { return nil, fmt.Errorf("failed to make ext4 filesystem (out: %q): %w", out, err) } defaultOpts := []string{"loop", "direct-io", "sync"} if err := testMount(t, scratch, defaultOpts); err != nil { return nil, err } return []Opt{ WithScratchFile(scratch), WithFSType("ext4"), WithMountOptions(defaultOpts), WithRecreateScratch(false), // reduce IO presure in CI withViewHookHelper(testViewHook), }, nil } func testMount(t *testing.T, scratchFile string, opts []string) error { root, err := os.MkdirTemp(t.TempDir(), "") if err != nil { return err } defer os.RemoveAll(root) m := []mount.Mount{ { Type: "ext4", Source: scratchFile, Options: opts, }, } if err := mount.All(m, root); err != nil { return fmt.Errorf("failed to mount device %s: %w", scratchFile, err) } if err := os.Remove(filepath.Join(root, "lost+found")); err != nil { return err } return mount.UnmountAll(root, 0) } func testViewHook(backingFile string, fsType string, defaultOpts []string) error { root, err := os.MkdirTemp("", "blockfile-testViewHook-XXXX") if err != nil { return err } defer os.RemoveAll(root) // FIXME(fuweid): Mount with rw to force fs to handle recover mountOpts := []mount.Mount{ { Type: fsType, Source: backingFile, Options: defaultOpts, }, } if err := mount.All(mountOpts, root); err != nil { return fmt.Errorf("failed to mount device %s: %w", backingFile, err) } if err := mount.UnmountAll(root, 0); err != nil { return fmt.Errorf("failed to unmount device %s: %w", backingFile, err) } return nil }