7d0d7e66ca
If a file handle is duplicated with dup() and the duplicate handle is flushed, rclone will go ahead and close the file, making the original file handle stale. This change removes the close() call from Flush() and replaces it with FlushWrites() so that the file only gets closed when Release() is called. The new FlushWrites method takes care of actually writing the file back to the underlying storage. Fixes #3381
68 lines
1.4 KiB
Go
68 lines
1.4 KiB
Go
// +build linux darwin freebsd
|
|
|
|
package mounttest
|
|
|
|
import (
|
|
"runtime"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/sys/unix"
|
|
|
|
"github.com/rclone/rclone/vfs"
|
|
)
|
|
|
|
// TestWriteFileDoubleClose tests double close on write
|
|
func TestWriteFileDoubleClose(t *testing.T) {
|
|
run.skipIfNoFUSE(t)
|
|
if runtime.GOOS == "darwin" {
|
|
t.Skip("Skipping test on OSX")
|
|
}
|
|
|
|
out, err := osCreate(run.path("testdoubleclose"))
|
|
assert.NoError(t, err)
|
|
fd := out.Fd()
|
|
|
|
fd1, err := unix.Dup(int(fd))
|
|
assert.NoError(t, err)
|
|
|
|
fd2, err := unix.Dup(int(fd))
|
|
assert.NoError(t, err)
|
|
|
|
// close one of the dups - should produce no error
|
|
err = unix.Close(fd1)
|
|
assert.NoError(t, err)
|
|
|
|
// write to the file
|
|
buf := []byte("hello")
|
|
n, err := out.Write(buf)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, 5, n)
|
|
|
|
// close it
|
|
err = out.Close()
|
|
assert.NoError(t, err)
|
|
|
|
// write to the other dup
|
|
_, err = unix.Write(fd2, buf)
|
|
if run.vfs.Opt.CacheMode < vfs.CacheModeWrites {
|
|
// produces an error if cache mode < writes
|
|
assert.Error(t, err, "input/output error")
|
|
} else {
|
|
// otherwise does not produce an error
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
// close the dup - should not produce an error
|
|
err = unix.Close(fd2)
|
|
assert.NoError(t, err)
|
|
|
|
run.waitForWriters()
|
|
run.rm(t, "testdoubleclose")
|
|
}
|
|
|
|
// writeTestDup performs the platform-specific implementation of the dup() unix
|
|
func writeTestDup(oldfd uintptr) (uintptr, error) {
|
|
newfd, err := unix.Dup(int(oldfd))
|
|
return uintptr(newfd), err
|
|
}
|