copy/move: detect file size change during copy/move - fixes #1250
This commit is contained in:
parent
1018e9bb27
commit
4c1ffc7f54
2 changed files with 50 additions and 0 deletions
|
@ -651,10 +651,17 @@ type localOpenFile struct {
|
||||||
o *Object // object that is open
|
o *Object // object that is open
|
||||||
in io.ReadCloser // handle we are wrapping
|
in io.ReadCloser // handle we are wrapping
|
||||||
hash *hash.MultiHasher // currently accumulating hashes
|
hash *hash.MultiHasher // currently accumulating hashes
|
||||||
|
fd *os.File // file object reference
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read bytes from the object - see io.Reader
|
// Read bytes from the object - see io.Reader
|
||||||
func (file *localOpenFile) Read(p []byte) (n int, err error) {
|
func (file *localOpenFile) Read(p []byte) (n int, err error) {
|
||||||
|
// Check if file has the same size and modTime
|
||||||
|
fi, err := file.fd.Stat()
|
||||||
|
if file.o.size != fi.Size() || file.o.modTime != fi.ModTime() {
|
||||||
|
return 0, errors.New("can't copy - source file is being updated")
|
||||||
|
}
|
||||||
|
|
||||||
n, err = file.in.Read(p)
|
n, err = file.in.Read(p)
|
||||||
if n > 0 {
|
if n > 0 {
|
||||||
// Hash routines never return an error
|
// Hash routines never return an error
|
||||||
|
@ -713,6 +720,7 @@ func (o *Object) Open(options ...fs.OpenOption) (in io.ReadCloser, err error) {
|
||||||
o: o,
|
o: o,
|
||||||
in: wrappedFd,
|
in: wrappedFd,
|
||||||
hash: hash,
|
hash: hash,
|
||||||
|
fd: fd,
|
||||||
}
|
}
|
||||||
return in, nil
|
return in, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,23 @@
|
||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ncw/rclone/fs/hash"
|
||||||
|
"github.com/ncw/rclone/fstest"
|
||||||
|
"github.com/ncw/rclone/lib/readers"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestMain drives the tests
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
fstest.TestMain(m)
|
||||||
|
}
|
||||||
|
|
||||||
func TestMapper(t *testing.T) {
|
func TestMapper(t *testing.T) {
|
||||||
m := newMapper()
|
m := newMapper()
|
||||||
assert.Equal(t, m.m, map[string]string{})
|
assert.Equal(t, m.m, map[string]string{})
|
||||||
|
@ -17,4 +29,34 @@ func TestMapper(t *testing.T) {
|
||||||
})
|
})
|
||||||
assert.Equal(t, "potato", m.Load("potato"))
|
assert.Equal(t, "potato", m.Load("potato"))
|
||||||
assert.Equal(t, "-r?'a´o¨", m.Load("-r'áö"))
|
assert.Equal(t, "-r?'a´o¨", m.Load("-r'áö"))
|
||||||
|
|
||||||
|
// Test copy with source file that's updating
|
||||||
|
r := fstest.NewRun(t)
|
||||||
|
defer r.Finalise()
|
||||||
|
filePath := "sub dir/local test"
|
||||||
|
r.WriteFile(filePath, "content", time.Now())
|
||||||
|
|
||||||
|
fd, err := os.Open(path.Join(r.LocalName, filePath))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed opening file %q: %v", filePath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err := fd.Stat()
|
||||||
|
o := &Object{size: fi.Size(), modTime: fi.ModTime()}
|
||||||
|
wrappedFd := readers.NewLimitedReadCloser(fd, -1)
|
||||||
|
hash, err := hash.NewMultiHasherTypes(hash.Supported)
|
||||||
|
in := localOpenFile{
|
||||||
|
o: o,
|
||||||
|
in: wrappedFd,
|
||||||
|
hash: hash,
|
||||||
|
fd: fd,
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 1)
|
||||||
|
_, err = in.Read(buf)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
r.WriteFile(filePath, "content updated", time.Now())
|
||||||
|
_, err = in.Read(buf)
|
||||||
|
require.Errorf(t, err, "can't copy - source file is being updated")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue