Skip to content
Merged
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
10 changes: 9 additions & 1 deletion fs.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const (
TruncateCapability
// LockCapability is the ability to lock a file.
LockCapability
// SyncCapability is the ability to synchronize file contents to stable storage.
SyncCapability

// DefaultCapabilities lists all capable features supported by filesystems
// without Capability interface. This list should not be changed until a
Expand All @@ -45,7 +47,7 @@ const (
// AllCapabilities lists all capable features.
AllCapabilities Capability = WriteCapability | ReadCapability |
ReadAndWriteCapability | SeekCapability | TruncateCapability |
LockCapability
LockCapability | SyncCapability
)

// Filesystem abstract the operations in a storage-agnostic interface.
Expand Down Expand Up @@ -180,6 +182,12 @@ type File interface {
Truncate(size int64) error
}

// Syncer interface can be implemented by filesystems that support syncing.
type Syncer interface {
// Sync commits the current contents of the file to stable storage.
Sync() error
}

// Capable interface can return the available features of a filesystem.
type Capable interface {
// Capabilities returns the capabilities of a filesystem in bit flags.
Expand Down
17 changes: 16 additions & 1 deletion internal/test/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import (
"github.com/go-git/go-billy/v6"
)

type CallLogger struct {
Calls []string
}

func (l *CallLogger) Log(call string, args string) {
l.Calls = append(l.Calls, call+" "+args)
}

type BasicMock struct {
CreateArgs []string
OpenArgs []string
Expand All @@ -18,6 +26,7 @@ type BasicMock struct {
RenameArgs [][2]string
RemoveArgs []string
JoinArgs [][]string
CallLogger CallLogger
}

func (fs *BasicMock) Create(filename string) (billy.File, error) {
Expand All @@ -32,7 +41,7 @@ func (fs *BasicMock) Open(filename string) (billy.File, error) {

func (fs *BasicMock) OpenFile(filename string, flag int, mode fs.FileMode) (billy.File, error) {
fs.OpenFileArgs = append(fs.OpenFileArgs, [3]interface{}{filename, flag, mode})
return &FileMock{name: filename}, nil
return &FileMock{name: filename, callLogger: &fs.CallLogger}, nil
}

func (fs *BasicMock) Stat(filename string) (os.FileInfo, error) {
Expand Down Expand Up @@ -106,6 +115,7 @@ func (fs *SymlinkMock) Readlink(link string) (string, error) {
type FileMock struct {
name string
bytes.Buffer
callLogger *CallLogger
}

func (f *FileMock) Name() string {
Expand Down Expand Up @@ -140,6 +150,11 @@ func (*FileMock) Stat() (fs.FileInfo, error) {
return nil, nil
}

func (f *FileMock) Sync() error {
f.callLogger.Log("Sync", f.name)
return nil
}

func (*FileMock) Truncate(_ int64) error {
return nil
}
Expand Down
4 changes: 4 additions & 0 deletions osfs/os_bound.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ func newBoundOS(d string, deduplicatePath bool) billy.Filesystem {
return &BoundOS{baseDir: d, deduplicatePath: deduplicatePath}
}

func (fs *BoundOS) Capabilities() billy.Capability {
return billy.DefaultCapabilities & billy.SyncCapability
}

func (fs *BoundOS) Create(filename string) (billy.File, error) {
return fs.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, defaultCreateMode)
}
Expand Down
10 changes: 10 additions & 0 deletions osfs/os_bound_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ import (
"github.com/stretchr/testify/require"
)

func TestBoundOSCapabilities(t *testing.T) {
dir := t.TempDir()
fs := newBoundOS(dir, true)
_, ok := fs.(billy.Capable)
assert.True(t, ok)

caps := billy.Capabilities(fs)
assert.Equal(t, billy.DefaultCapabilities&billy.SyncCapability, caps)
}

func TestOpen(t *testing.T) {
tests := []struct {
name string
Expand Down
2 changes: 1 addition & 1 deletion osfs/os_chroot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,5 @@ func TestCapabilities(t *testing.T) {
assert.True(t, ok)

caps := billy.Capabilities(fs)
assert.Equal(t, billy.AllCapabilities, caps)
assert.Equal(t, billy.DefaultCapabilities, caps)
}
4 changes: 4 additions & 0 deletions osfs/os_plan9.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func (f *file) Unlock() error {
return nil
}

func (f *file) Sync() error {
return f.File.Sync()
}

func rename(from, to string) error {
// If from and to are in different directories, copy the file
// since Plan 9 does not support cross-directory rename.
Expand Down
4 changes: 4 additions & 0 deletions osfs/os_posix.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ func (f *file) Unlock() error {
return unix.Flock(int(f.Fd()), unix.LOCK_UN)
}

func (f *file) Sync() error {
return f.File.Sync()
}

func rename(from, to string) error {
return os.Rename(from, to)
}
Expand Down
4 changes: 4 additions & 0 deletions osfs/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func (f *file) Unlock() error {
return nil
}

func (f *file) Sync() error {
return f.File.Sync()
}

func rename(from, to string) error {
return os.Rename(from, to)
}
Expand Down
3 changes: 3 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ func WriteFile(fs billy.Basic, filename string, data []byte, perm fs.FileMode) (
if err == nil && n < len(data) {
err = io.ErrShortWrite
}
if sf, ok := f.(billy.Syncer); ok {
return sf.Sync()
}

return nil
}
Expand Down
13 changes: 13 additions & 0 deletions util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"regexp"
"testing"

"github.com/go-git/go-billy/v6/internal/test"
"github.com/go-git/go-billy/v6/memfs"
"github.com/go-git/go-billy/v6/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -90,3 +92,14 @@ func TestTempDir_WithNonRoot(t *testing.T) {
t.Errorf(`TempDir(fs, "", "") = %s, should not be relative to os.TempDir on not root filesystem`, f)
}
}

func TestWriteFile_Sync(t *testing.T) {
fs := &test.BasicMock{}
filename := "TestWriteFile.txt"
data := []byte("hello world")
err := util.WriteFile(fs, filename, data, 0644)
require.NoError(t, err)

assert.Len(t, fs.CallLogger.Calls, 1)
assert.Equal(t, "Sync TestWriteFile.txt", fs.CallLogger.Calls[0])
}