mirror of
https://github.com/rclone/rclone.git
synced 2024-11-25 09:41:44 +08:00
vfs, cmount: make truncate work properly in the presence or otherwise of open files
This commit is contained in:
parent
94adf4f43b
commit
dec21ccf63
|
@ -319,28 +319,14 @@ func (fsys *FS) Truncate(path string, size int64, fh uint64) (errc int) {
|
||||||
if errc != 0 {
|
if errc != 0 {
|
||||||
return errc
|
return errc
|
||||||
}
|
}
|
||||||
// Read the size so far
|
var err error
|
||||||
var currentSize int64
|
|
||||||
if handle != nil {
|
if handle != nil {
|
||||||
fi, err := handle.Stat()
|
err = handle.Truncate(size)
|
||||||
if err != nil {
|
|
||||||
return translateError(err)
|
|
||||||
}
|
|
||||||
currentSize = fi.Size()
|
|
||||||
} else {
|
} else {
|
||||||
currentSize = node.Size()
|
err = node.Truncate(size)
|
||||||
}
|
}
|
||||||
fs.Debugf(path, "truncate to %d, currentSize %d", size, currentSize)
|
if err != nil {
|
||||||
if currentSize != size {
|
return translateError(err)
|
||||||
var err error
|
|
||||||
if handle != nil {
|
|
||||||
err = handle.Truncate(size)
|
|
||||||
} else {
|
|
||||||
err = node.Truncate(size)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return translateError(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
80
vfs/file.go
80
vfs/file.go
|
@ -19,7 +19,7 @@ type File struct {
|
||||||
mu sync.RWMutex // protects the following
|
mu sync.RWMutex // protects the following
|
||||||
o fs.Object // NB o may be nil if file is being written
|
o fs.Object // NB o may be nil if file is being written
|
||||||
leaf string // leaf name of the object
|
leaf string // leaf name of the object
|
||||||
writers int // number of writers for this file
|
writers []Handle // writers for this file
|
||||||
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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,10 +90,28 @@ func (f *File) rename(d *Dir, o fs.Object) {
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// addWriters increments or decrements the writers
|
// addWriter adds a write handle to the file
|
||||||
func (f *File) addWriters(n int) {
|
func (f *File) addWriter(h Handle) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
f.writers += n
|
f.writers = append(f.writers, h)
|
||||||
|
f.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// delWriter removes a write handle from the file
|
||||||
|
func (f *File) delWriter(h Handle) {
|
||||||
|
f.mu.Lock()
|
||||||
|
var found = -1
|
||||||
|
for i := range f.writers {
|
||||||
|
if f.writers[i] == h {
|
||||||
|
found = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if found >= 0 {
|
||||||
|
f.writers = append(f.writers[:found], f.writers[found+1:]...)
|
||||||
|
} else {
|
||||||
|
fs.Debugf(f.o, "File.delWriter couldn't find handle")
|
||||||
|
}
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +124,7 @@ func (f *File) ModTime() (modTime time.Time) {
|
||||||
|
|
||||||
if !f.d.vfs.Opt.NoModTime {
|
if !f.d.vfs.Opt.NoModTime {
|
||||||
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
||||||
if f.o == nil || f.writers != 0 {
|
if f.o == nil || len(f.writers) != 0 {
|
||||||
if !f.pendingModTime.IsZero() {
|
if !f.pendingModTime.IsZero() {
|
||||||
return f.pendingModTime
|
return f.pendingModTime
|
||||||
}
|
}
|
||||||
|
@ -124,7 +142,7 @@ func (f *File) Size() int64 {
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
|
|
||||||
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
||||||
if f.o == nil || f.writers != 0 {
|
if f.o == nil || len(f.writers) != 0 {
|
||||||
return atomic.LoadInt64(&f.size)
|
return atomic.LoadInt64(&f.size)
|
||||||
}
|
}
|
||||||
return f.o.Size()
|
return f.o.Size()
|
||||||
|
@ -188,6 +206,13 @@ func (f *File) setObject(o fs.Object) {
|
||||||
f.d.addObject(f)
|
f.d.addObject(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// exists returns whether the file exists already
|
||||||
|
func (f *File) exists() bool {
|
||||||
|
f.mu.Lock()
|
||||||
|
defer f.mu.Unlock()
|
||||||
|
return f.o != nil
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for f.o to become non nil for a short time returning it or an
|
// Wait for f.o to become non nil for a short time returning it or an
|
||||||
// error. Use when opening a read handle.
|
// error. Use when opening a read handle.
|
||||||
//
|
//
|
||||||
|
@ -196,12 +221,12 @@ func (f *File) waitForValidObject() (o fs.Object, err error) {
|
||||||
for i := 0; i < 50; i++ {
|
for i := 0; i < 50; i++ {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
o = f.o
|
o = f.o
|
||||||
writers := f.writers
|
nwriters := len(f.writers)
|
||||||
f.mu.Unlock()
|
f.mu.Unlock()
|
||||||
if o != nil {
|
if o != nil {
|
||||||
return o, nil
|
return o, nil
|
||||||
}
|
}
|
||||||
if writers == 0 {
|
if nwriters == 0 {
|
||||||
return nil, errors.New("can't open file - writer failed")
|
return nil, errors.New("can't open file - writer failed")
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
@ -383,9 +408,40 @@ func (f *File) Open(flags int) (fd Handle, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate changes the size of the named file.
|
// Truncate changes the size of the named file.
|
||||||
func (f *File) Truncate(size int64) error {
|
func (f *File) Truncate(size int64) (err error) {
|
||||||
if f.d.vfs.Opt.CacheMode >= CacheModeWrites {
|
// make a copy of fh.writers with the lock held then unlock so
|
||||||
|
// we can call other file methods.
|
||||||
|
f.mu.Lock()
|
||||||
|
writers := make([]Handle, len(f.writers))
|
||||||
|
copy(writers, f.writers)
|
||||||
|
f.mu.Unlock()
|
||||||
|
|
||||||
|
// If have writers then call truncate for each writer
|
||||||
|
if len(writers) != 0 {
|
||||||
|
fs.Debugf(f.o, "Truncating %d file handles", len(writers))
|
||||||
|
for _, h := range writers {
|
||||||
|
truncateErr := h.Truncate(size)
|
||||||
|
if truncateErr != nil {
|
||||||
|
err = truncateErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
// FIXME
|
fs.Debugf(f.o, "Truncating file")
|
||||||
return ENOSYS
|
|
||||||
|
// Otherwise if no writers then truncate the file by opening
|
||||||
|
// the file and truncating it.
|
||||||
|
flags := os.O_WRONLY
|
||||||
|
if size == 0 {
|
||||||
|
flags |= os.O_TRUNC
|
||||||
|
}
|
||||||
|
fh, err := f.Open(flags)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fs.CheckClose(fh, &err)
|
||||||
|
if size != 0 {
|
||||||
|
return fh.Truncate(size)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,12 @@ func newRWFileHandle(d *Dir, f *File, remote string, flags int) (fh *RWFileHandl
|
||||||
flags: flags,
|
flags: flags,
|
||||||
osPath: osPath,
|
osPath: osPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rdwrMode := fh.flags & accessModeMask
|
||||||
|
if rdwrMode != os.O_RDONLY {
|
||||||
|
fh.file.addWriter(fh)
|
||||||
|
}
|
||||||
|
|
||||||
return fh, nil
|
return fh, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,8 +69,6 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rdwrMode := fh.flags & accessModeMask
|
|
||||||
|
|
||||||
// if not truncating the file, need to read it first
|
// if not truncating the file, need to read it first
|
||||||
if fh.flags&os.O_TRUNC == 0 && !truncate {
|
if fh.flags&os.O_TRUNC == 0 && !truncate {
|
||||||
// Fetch the file if it hasn't changed
|
// Fetch the file if it hasn't changed
|
||||||
|
@ -92,9 +96,6 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
|
||||||
fh.file.setSize(0)
|
fh.file.setSize(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if rdwrMode != os.O_RDONLY {
|
|
||||||
fh.file.addWriters(1)
|
|
||||||
}
|
|
||||||
fs.Debugf(fh.remote, "Opening cached copy with flags=%s", decodeOpenFlags(fh.flags))
|
fs.Debugf(fh.remote, "Opening cached copy with flags=%s", decodeOpenFlags(fh.flags))
|
||||||
fd, err := os.OpenFile(fh.osPath, fh.flags|os.O_CREATE, 0600)
|
fd, err := os.OpenFile(fh.osPath, fh.flags|os.O_CREATE, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -140,6 +141,9 @@ func (fh *RWFileHandle) close() (err error) {
|
||||||
}
|
}
|
||||||
fh.closed = true
|
fh.closed = true
|
||||||
rdwrMode := fh.flags & accessModeMask
|
rdwrMode := fh.flags & accessModeMask
|
||||||
|
if rdwrMode != os.O_RDONLY {
|
||||||
|
fh.file.delWriter(fh)
|
||||||
|
}
|
||||||
if !fh.opened {
|
if !fh.opened {
|
||||||
// If read only then return
|
// If read only then return
|
||||||
if rdwrMode == os.O_RDONLY {
|
if rdwrMode == os.O_RDONLY {
|
||||||
|
@ -157,7 +161,6 @@ func (fh *RWFileHandle) close() (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rdwrMode != os.O_RDONLY {
|
if rdwrMode != os.O_RDONLY {
|
||||||
fh.file.addWriters(-1)
|
|
||||||
fi, err := fh.File.Stat()
|
fi, err := fh.File.Stat()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fs.Errorf(fh.remote, "Failed to stat cache file: %v", err)
|
fs.Errorf(fh.remote, "Failed to stat cache file: %v", err)
|
||||||
|
|
24
vfs/write.go
24
vfs/write.go
|
@ -49,7 +49,7 @@ func newWriteFileHandle(d *Dir, f *File, remote string) (*WriteFileHandle, error
|
||||||
fh.o = o
|
fh.o = o
|
||||||
fh.result <- err
|
fh.result <- err
|
||||||
}()
|
}()
|
||||||
fh.file.addWriters(1)
|
fh.file.addWriter(fh)
|
||||||
fh.file.setSize(0)
|
fh.file.setSize(0)
|
||||||
return fh, nil
|
return fh, nil
|
||||||
}
|
}
|
||||||
|
@ -129,6 +129,11 @@ func (fh *WriteFileHandle) Write(p []byte) (n int, err error) {
|
||||||
return fh.writeAt(p, fh.offset)
|
return fh.writeAt(p, fh.offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteString a string to the file
|
||||||
|
func (fh *WriteFileHandle) WriteString(s string) (n int, err error) {
|
||||||
|
return fh.Write([]byte(s))
|
||||||
|
}
|
||||||
|
|
||||||
// Offset returns the offset of the file pointer
|
// Offset returns the offset of the file pointer
|
||||||
func (fh *WriteFileHandle) Offset() (offset int64) {
|
func (fh *WriteFileHandle) Offset() (offset int64) {
|
||||||
fh.mu.Lock()
|
fh.mu.Lock()
|
||||||
|
@ -145,7 +150,7 @@ func (fh *WriteFileHandle) close() error {
|
||||||
return ECLOSED
|
return ECLOSED
|
||||||
}
|
}
|
||||||
fh.closed = true
|
fh.closed = true
|
||||||
fh.file.addWriters(-1)
|
fh.file.delWriter(fh)
|
||||||
writeCloseErr := fh.pipeWriter.Close()
|
writeCloseErr := fh.pipeWriter.Close()
|
||||||
err := <-fh.result
|
err := <-fh.result
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -228,3 +233,18 @@ func (fh *WriteFileHandle) Stat() (os.FileInfo, error) {
|
||||||
defer fh.mu.Unlock()
|
defer fh.mu.Unlock()
|
||||||
return fh.file, nil
|
return fh.file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Truncate file to given size
|
||||||
|
func (fh *WriteFileHandle) Truncate(size int64) (err error) {
|
||||||
|
fh.mu.Lock()
|
||||||
|
defer fh.mu.Unlock()
|
||||||
|
if fh.closed {
|
||||||
|
return ECLOSED
|
||||||
|
}
|
||||||
|
if size != fh.offset {
|
||||||
|
fs.Errorf(fh.remote, "Truncate: Can't change size without cache")
|
||||||
|
return EPERM
|
||||||
|
}
|
||||||
|
// File is correct size
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user