Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions drivers/overlay/overlay.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/containers/storage/pkg/directory"
"github.com/containers/storage/pkg/fileutils"
"github.com/containers/storage/pkg/fsutils"
"github.com/containers/storage/pkg/ioutils"
"github.com/containers/storage/pkg/idmap"
"github.com/containers/storage/pkg/idtools"
"github.com/containers/storage/pkg/mount"
Expand Down Expand Up @@ -2361,6 +2362,10 @@ func (d *Driver) ApplyDiffFromStagingDirectory(id, parent string, diffOutput *gr
return err
}

if err := ioutils.SyncDirectoryContents(stagingDirectory); err != nil {
return fmt.Errorf("sync staging directory before rename: %w", err)
}

return os.Rename(stagingDirectory, diffPath)
}

Expand Down
63 changes: 63 additions & 0 deletions pkg/ioutils/sync_directory_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//go:build linux

package ioutils

import (
"fmt"
"io/fs"
"os"
"path/filepath"

"golang.org/x/sys/unix"
)

// SyncDirectoryContents flushes file data and directory metadata under dir to
// physical storage. Call this before atomically renaming a fully populated
// staging directory to its final location.
func SyncDirectoryContents(dir string) error {
var dirs []string

err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
return walkErr
}
if d.IsDir() {
dirs = append(dirs, path)
return nil
}

f, err := os.Open(path)
if err != nil {
return err
}

syncErr := unix.Fdatasync(int(f.Fd()))
closeErr := f.Close()
if syncErr != nil {
return syncErr
}

return closeErr
})
if err != nil {
return fmt.Errorf("sync directory contents in %q: %w", dir, err)
}

for i := len(dirs) - 1; i >= 0; i-- {
dfd, err := os.Open(dirs[i])
if err != nil {
return fmt.Errorf("open directory %q for sync: %w", dirs[i], err)
}

syncErr := unix.Fsync(int(dfd.Fd()))
closeErr := dfd.Close()
if syncErr != nil {
return fmt.Errorf("sync directory %q: %w", dirs[i], syncErr)
}
if closeErr != nil {
return fmt.Errorf("close directory %q after sync: %w", dirs[i], closeErr)
}
}

return nil
}
32 changes: 32 additions & 0 deletions pkg/ioutils/sync_directory_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//go:build linux

package ioutils

import (
"os"
"path/filepath"
"testing"
)

func TestSyncDirectoryContents(t *testing.T) {
dir := t.TempDir()

nested := filepath.Join(dir, "nested")
if err := os.MkdirAll(nested, 0o755); err != nil {
t.Fatalf("mkdir nested: %v", err)
}

files := []string{
filepath.Join(dir, "file1"),
filepath.Join(nested, "file2"),
}
for _, file := range files {
if err := os.WriteFile(file, []byte("storage-resilience"), 0o644); err != nil {
t.Fatalf("write file %q: %v", file, err)
}
}

if err := SyncDirectoryContents(dir); err != nil {
t.Fatalf("SyncDirectoryContents: %v", err)
}
}
Loading