mirror of
https://github.com/rclone/rclone.git
synced 2024-11-22 14:22:04 +08:00
mount: replace use of WriteAt with Write for cache mode >= writes and O_APPEND
os.File.WriteAt returns an error if a file was opened with O_APPEND. This replaces it with os.File.Write if the file was opened with O_APPEND.
This commit is contained in:
parent
daff5a824e
commit
378a3f4133
|
@ -371,7 +371,12 @@ func (fsys *FS) Write(path string, buff []byte, ofst int64, fh uint64) (n int) {
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return errc
|
return errc
|
||||||
}
|
}
|
||||||
n, err := handle.WriteAt(buff, ofst)
|
var err error
|
||||||
|
if fsys.VFS.Opt.CacheMode < vfs.CacheModeWrites || handle.Node().Mode()&os.ModeAppend == 0 {
|
||||||
|
n, err = handle.WriteAt(buff, ofst)
|
||||||
|
} else {
|
||||||
|
n, err = handle.Write(buff)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ package mount
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
"bazil.org/fuse"
|
"bazil.org/fuse"
|
||||||
fusefs "bazil.org/fuse/fs"
|
fusefs "bazil.org/fuse/fs"
|
||||||
|
@ -41,7 +42,12 @@ var _ fusefs.HandleWriter = (*FileHandle)(nil)
|
||||||
// Write data to the file handle
|
// Write data to the file handle
|
||||||
func (fh *FileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) {
|
func (fh *FileHandle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) {
|
||||||
defer log.Trace(fh, "len=%d, offset=%d", len(req.Data), req.Offset)("written=%d, err=%v", &resp.Size, &err)
|
defer log.Trace(fh, "len=%d, offset=%d", len(req.Data), req.Offset)("written=%d, err=%v", &resp.Size, &err)
|
||||||
n, err := fh.Handle.WriteAt(req.Data, req.Offset)
|
var n int
|
||||||
|
if fh.Handle.Node().VFS().Opt.CacheMode < vfs.CacheModeWrites || fh.Handle.Node().Mode()&os.ModeAppend == 0 {
|
||||||
|
n, err = fh.Handle.WriteAt(req.Data, req.Offset)
|
||||||
|
} else {
|
||||||
|
n, err = fh.Handle.Write(req.Data)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return translateError(err)
|
return translateError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,11 @@ func osCreate(name string) (*os.File, error) {
|
||||||
return os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
return os.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// os.Create with append
|
||||||
|
func osAppend(name string) (*os.File, error) {
|
||||||
|
return os.OpenFile(name, os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
}
|
||||||
|
|
||||||
// TestFileModTimeWithOpenWriters tests mod time on open files
|
// TestFileModTimeWithOpenWriters tests mod time on open files
|
||||||
func TestFileModTimeWithOpenWriters(t *testing.T) {
|
func TestFileModTimeWithOpenWriters(t *testing.T) {
|
||||||
run.skipIfNoFUSE(t)
|
run.skipIfNoFUSE(t)
|
||||||
|
|
|
@ -78,6 +78,7 @@ func RunTests(t *testing.T, fn MountFn) {
|
||||||
t.Run("TestWriteFileDoubleClose", TestWriteFileDoubleClose)
|
t.Run("TestWriteFileDoubleClose", TestWriteFileDoubleClose)
|
||||||
t.Run("TestWriteFileFsync", TestWriteFileFsync)
|
t.Run("TestWriteFileFsync", TestWriteFileFsync)
|
||||||
t.Run("TestWriteFileDup", TestWriteFileDup)
|
t.Run("TestWriteFileDup", TestWriteFileDup)
|
||||||
|
t.Run("TestWriteFileAppend", TestWriteFileAppend)
|
||||||
})
|
})
|
||||||
log.Printf("Finished test run with cache mode %v (ok=%v)", cacheMode, ok)
|
log.Printf("Finished test run with cache mode %v (ok=%v)", cacheMode, ok)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package mounttest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -130,3 +131,48 @@ func TestWriteFileDup(t *testing.T) {
|
||||||
run.waitForWriters()
|
run.waitForWriters()
|
||||||
run.rm(t, "to be synced")
|
run.rm(t, "to be synced")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestWriteFileAppend tests that O_APPEND works on cache backends >= writes
|
||||||
|
func TestWriteFileAppend(t *testing.T) {
|
||||||
|
run.skipIfNoFUSE(t)
|
||||||
|
|
||||||
|
if run.vfs.Opt.CacheMode < vfs.CacheModeWrites {
|
||||||
|
t.Skip("not supported on vfs-cache-mode < writes")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Windows needs the v1.5 release of WinFsp to handle O_APPEND properly.
|
||||||
|
// Until it gets released, skip this test on Windows.
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("currently unsupported on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
|
filepath := run.path("to be synced")
|
||||||
|
fh, err := osCreate(filepath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testData := []byte("0123456789")
|
||||||
|
appendData := []byte("10")
|
||||||
|
|
||||||
|
_, err = fh.Write(testData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = fh.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
fh, err = osAppend(filepath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
_, err = fh.Write(appendData)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
err = fh.Close()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
info, err := os.Stat(filepath)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.EqualValues(t, len(testData)+len(appendData), info.Size())
|
||||||
|
|
||||||
|
run.waitForWriters()
|
||||||
|
run.rm(t, "to be synced")
|
||||||
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ type File struct {
|
||||||
modified bool // has the cache file be modified by a RWFileHandle?
|
modified bool // has the cache file be modified by a RWFileHandle?
|
||||||
pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written
|
pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written
|
||||||
pendingRenameFun func(ctx context.Context) error // will be run/renamed after all writers close
|
pendingRenameFun func(ctx context.Context) error // will be run/renamed after all writers close
|
||||||
|
appendMode bool // file was opened with O_APPEND
|
||||||
|
|
||||||
muRW sync.Mutex // synchonize RWFileHandle.openPending(), RWFileHandle.close() and File.Remove
|
muRW sync.Mutex // synchonize RWFileHandle.openPending(), RWFileHandle.close() and File.Remove
|
||||||
}
|
}
|
||||||
|
@ -65,7 +66,11 @@ func (f *File) IsDir() bool {
|
||||||
|
|
||||||
// Mode bits of the file or directory - satisfies Node interface
|
// Mode bits of the file or directory - satisfies Node interface
|
||||||
func (f *File) Mode() (mode os.FileMode) {
|
func (f *File) Mode() (mode os.FileMode) {
|
||||||
return f.d.vfs.Opt.FilePerms
|
mode = f.d.vfs.Opt.FilePerms
|
||||||
|
if f.appendMode {
|
||||||
|
mode |= os.ModeAppend
|
||||||
|
}
|
||||||
|
return mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Name (base) of the directory - satisfies Node interface
|
// Name (base) of the directory - satisfies Node interface
|
||||||
|
@ -539,6 +544,7 @@ func (f *File) Open(flags int) (fd Handle, err error) {
|
||||||
// If append is set then set read to force openRW
|
// If append is set then set read to force openRW
|
||||||
if flags&os.O_APPEND != 0 {
|
if flags&os.O_APPEND != 0 {
|
||||||
read = true
|
read = true
|
||||||
|
f.appendMode = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// If truncate is set then set write to force openRW
|
// If truncate is set then set write to force openRW
|
||||||
|
|
Loading…
Reference in New Issue
Block a user