mirror of
https://github.com/rclone/rclone.git
synced 2025-12-11 22:14:05 +01:00
This commit is contained in:
94
vfs/file.go
94
vfs/file.go
@@ -18,16 +18,17 @@ type File struct {
|
||||
size int64 // size of file - read and written with atomic int64 - must be 64 bit aligned
|
||||
d *Dir // parent directory - read only
|
||||
|
||||
mu sync.Mutex // protects the following
|
||||
o fs.Object // NB o may be nil if file is being written
|
||||
leaf string // leaf name of the object
|
||||
rwOpenCount int // number of open files on this handle
|
||||
writers []Handle // writers for this file
|
||||
nwriters int32 // len(writers) which is read/updated with atomic
|
||||
readWriters int // how many RWFileHandle are open for writing
|
||||
readWriterClosing bool // is a RWFileHandle currently cosing?
|
||||
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
|
||||
mu sync.Mutex // protects the following
|
||||
o fs.Object // NB o may be nil if file is being written
|
||||
leaf string // leaf name of the object
|
||||
rwOpenCount int // number of open files on this handle
|
||||
writers []Handle // writers for this file
|
||||
nwriters int32 // len(writers) which is read/updated with atomic
|
||||
readWriters int // how many RWFileHandle are open for writing
|
||||
readWriterClosing bool // is a RWFileHandle currently cosing?
|
||||
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
|
||||
pendingRenameFun func() error // will be run/renamed after all writers close
|
||||
|
||||
muRW sync.Mutex // synchonize RWFileHandle.openPending(), RWFileHandle.close() and File.Remove
|
||||
}
|
||||
@@ -90,13 +91,61 @@ func (f *File) Node() Node {
|
||||
return f
|
||||
}
|
||||
|
||||
// rename should be called to update the internals after a rename
|
||||
func (f *File) rename(d *Dir, o fs.Object) {
|
||||
f.mu.Lock()
|
||||
f.o = o
|
||||
f.d = d
|
||||
f.leaf = path.Base(o.Remote())
|
||||
f.mu.Unlock()
|
||||
// applyPendingRename runs a previously set rename operation if there are no
|
||||
// more remaining writers. Call without lock held.
|
||||
func (f *File) applyPendingRename() {
|
||||
fun := f.pendingRenameFun
|
||||
if fun == nil || f.writingInProgress() {
|
||||
return
|
||||
}
|
||||
fs.Debugf(f.o, "Running delayed rename now")
|
||||
err := fun()
|
||||
fs.Errorf(f.Path(), "File.Rename error: %v", err)
|
||||
}
|
||||
|
||||
// rename attempts to immediately rename a file if there are no open writers.
|
||||
// Otherwise it will queue the rename operation on the remote until no writers
|
||||
// remain.
|
||||
func (f *File) rename(destDir *Dir, newName string) error {
|
||||
// FIXME: could Copy then Delete if Move not available
|
||||
// - though care needed if case insensitive...
|
||||
doMove := f.d.f.Features().Move
|
||||
if doMove == nil {
|
||||
err := errors.Errorf("Fs %q can't rename files (no Move)", f.d.f)
|
||||
fs.Errorf(f.Path(), "Dir.Rename error: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
renameCall := func() error {
|
||||
newPath := path.Join(destDir.path, newName)
|
||||
newObject, err := doMove(f.o, newPath)
|
||||
if err != nil {
|
||||
fs.Errorf(f.Path(), "File.Rename error: %v", err)
|
||||
return err
|
||||
}
|
||||
// Update the node with the new details
|
||||
fs.Debugf(f.o, "Updating file with %v %p", newObject, f)
|
||||
// f.rename(destDir, newObject)
|
||||
f.mu.Lock()
|
||||
f.o = newObject
|
||||
f.d = destDir
|
||||
f.leaf = path.Base(newObject.Remote())
|
||||
f.pendingRenameFun = nil
|
||||
f.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.writingInProgress() {
|
||||
fs.Debugf(f.o, "File is currently open, delaying rename %p", f)
|
||||
f.mu.Lock()
|
||||
f.d = destDir
|
||||
f.leaf = newName
|
||||
f.pendingRenameFun = renameCall
|
||||
f.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
return renameCall()
|
||||
}
|
||||
|
||||
// addWriter adds a write handle to the file
|
||||
@@ -138,6 +187,7 @@ func (f *File) delWriter(h Handle, modifiedCacheFile bool) (lastWriterAndModifie
|
||||
if lastWriterAndModified {
|
||||
f.modified = false
|
||||
}
|
||||
defer f.applyPendingRename()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -171,6 +221,7 @@ func (f *File) finishWriterClose() {
|
||||
f.mu.Lock()
|
||||
f.readWriterClosing = false
|
||||
f.mu.Unlock()
|
||||
f.applyPendingRename()
|
||||
}
|
||||
|
||||
// activeWriters returns the number of writers on the file
|
||||
@@ -216,7 +267,7 @@ func (f *File) Size() int64 {
|
||||
defer f.mu.Unlock()
|
||||
|
||||
// if o is nil it isn't valid yet or there are writers, so return the size so far
|
||||
if f.o == nil || len(f.writers) != 0 || f.readWriterClosing {
|
||||
if f.writingInProgress() {
|
||||
return atomic.LoadInt64(&f.size)
|
||||
}
|
||||
return nonNegative(f.o.Size())
|
||||
@@ -233,7 +284,7 @@ func (f *File) SetModTime(modTime time.Time) error {
|
||||
f.pendingModTime = modTime
|
||||
|
||||
// Only update the ModTime when there are no writers, setObject will do it
|
||||
if f.o != nil && len(f.writers) == 0 && !f.readWriterClosing {
|
||||
if !f.writingInProgress() {
|
||||
return f.applyPendingModTime()
|
||||
}
|
||||
|
||||
@@ -267,6 +318,11 @@ func (f *File) applyPendingModTime() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// writingInProgress returns true of there are any open writers
|
||||
func (f *File) writingInProgress() bool {
|
||||
return f.o == nil || len(f.writers) != 0 || f.readWriterClosing
|
||||
}
|
||||
|
||||
// Update the size while writing
|
||||
func (f *File) setSize(n int64) {
|
||||
atomic.StoreInt64(&f.size, n)
|
||||
|
||||
Reference in New Issue
Block a user