vfs: don't cache the path in RW file objects to fix renaming

This commit is contained in:
Nick Craig-Wood 2019-12-09 14:25:54 +00:00
parent a186284b23
commit 241921c786
2 changed files with 28 additions and 26 deletions

View file

@ -95,6 +95,11 @@ func (f *File) Path() string {
return path.Join(f.d.path, f.leaf) return path.Join(f.d.path, f.leaf)
} }
// osPath returns the full path of the file in the cache in OS format
func (f *File) osPath() string {
return f.d.vfs.cache.toOSPath(f.Path())
}
// Sys returns underlying data source (can be nil) - satisfies Node interface // Sys returns underlying data source (can be nil) - satisfies Node interface
func (f *File) Sys() interface{} { func (f *File) Sys() interface{} {
return nil return nil
@ -473,7 +478,7 @@ func (f *File) openRW(flags int) (fh *RWFileHandle, err error) {
} }
// fs.Debugf(o, "File.openRW") // fs.Debugf(o, "File.openRW")
fh, err = newRWFileHandle(d, f, f.Path(), flags) fh, err = newRWFileHandle(d, f, flags)
if err != nil { if err != nil {
fs.Errorf(f, "File.openRW failed: %v", err) fs.Errorf(f, "File.openRW failed: %v", err)
return nil, err return nil, err

View file

@ -24,14 +24,12 @@ type RWFileHandle struct {
*os.File *os.File
mu sync.Mutex mu sync.Mutex
closed bool // set if handle has been closed closed bool // set if handle has been closed
remote string
file *File file *File
d *Dir d *Dir
opened bool opened bool
flags int // open flags flags int // open flags
osPath string // path to the file in the cache writeCalled bool // if any Write() methods have been called
writeCalled bool // if any Write() methods have been called changed bool // file contents was changed in any other way
changed bool // file contents was changed in any other way
} }
// Check interfaces // Check interfaces
@ -44,26 +42,25 @@ var (
_ io.Closer = (*RWFileHandle)(nil) _ io.Closer = (*RWFileHandle)(nil)
) )
func newRWFileHandle(d *Dir, f *File, remote string, flags int) (fh *RWFileHandle, err error) { func newRWFileHandle(d *Dir, f *File, flags int) (fh *RWFileHandle, err error) {
// if O_CREATE and O_EXCL are set and if path already exists, then return EEXIST // if O_CREATE and O_EXCL are set and if path already exists, then return EEXIST
if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL && f.exists() { if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL && f.exists() {
return nil, EEXIST return nil, EEXIST
} }
fh = &RWFileHandle{ fh = &RWFileHandle{
file: f, file: f,
d: d, d: d,
remote: remote, flags: flags,
flags: flags,
} }
// mark the file as open in the cache - must be done before the mkdir // mark the file as open in the cache - must be done before the mkdir
fh.d.vfs.cache.open(fh.remote) fh.d.vfs.cache.open(fh.file.Path())
// Make a place for the file // Make a place for the file
fh.osPath, err = d.vfs.cache.mkdir(remote) _, err = d.vfs.cache.mkdir(fh.file.Path())
if err != nil { if err != nil {
fh.d.vfs.cache.close(fh.remote) fh.d.vfs.cache.close(fh.file.Path())
return nil, errors.Wrap(err, "open RW handle failed to make cache directory") return nil, errors.Wrap(err, "open RW handle failed to make cache directory")
} }
@ -113,9 +110,9 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
// If the remote object exists AND its cached file exists locally AND there are no // If the remote object exists AND its cached file exists locally AND there are no
// other RW handles with it open, then attempt to update it. // other RW handles with it open, then attempt to update it.
if o != nil && fh.file.rwOpens() == 0 { if o != nil && fh.file.rwOpens() == 0 {
cacheObj, err := fh.d.vfs.cache.f.NewObject(context.TODO(), fh.remote) cacheObj, err := fh.d.vfs.cache.f.NewObject(context.TODO(), fh.file.Path())
if err == nil && cacheObj != nil { if err == nil && cacheObj != nil {
_, err = copyObj(fh.d.vfs.cache.f, cacheObj, fh.remote, o) _, err = copyObj(fh.d.vfs.cache.f, cacheObj, fh.file.Path(), o)
if err != nil { if err != nil {
return errors.Wrap(err, "open RW handle failed to update cached file") return errors.Wrap(err, "open RW handle failed to update cached file")
} }
@ -123,12 +120,12 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
} }
// try to open a exising cache file // try to open a exising cache file
fd, err = file.OpenFile(fh.osPath, cacheFileOpenFlags&^os.O_CREATE, 0600) fd, err = file.OpenFile(fh.file.osPath(), cacheFileOpenFlags&^os.O_CREATE, 0600)
if os.IsNotExist(err) { if os.IsNotExist(err) {
// cache file does not exist, so need to fetch it if we have an object to fetch // cache file does not exist, so need to fetch it if we have an object to fetch
// it from // it from
if o != nil { if o != nil {
_, err = copyObj(fh.d.vfs.cache.f, nil, fh.remote, o) _, err = copyObj(fh.d.vfs.cache.f, nil, fh.file.Path(), o)
if err != nil { if err != nil {
cause := errors.Cause(err) cause := errors.Cause(err)
if cause != fs.ErrorObjectNotFound && cause != fs.ErrorDirNotFound { if cause != fs.ErrorObjectNotFound && cause != fs.ErrorDirNotFound {
@ -162,7 +159,7 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
fh.changed = true fh.changed = true
if fh.flags&os.O_CREATE == 0 && fh.file.exists() { if fh.flags&os.O_CREATE == 0 && fh.file.exists() {
// create an empty file if it exists on the source // create an empty file if it exists on the source
err = ioutil.WriteFile(fh.osPath, []byte{}, 0600) err = ioutil.WriteFile(fh.file.osPath(), []byte{}, 0600)
if err != nil { if err != nil {
return errors.Wrap(err, "cache open failed to create zero length file") return errors.Wrap(err, "cache open failed to create zero length file")
} }
@ -172,9 +169,9 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
// exists in these cases. // exists in these cases.
if runtime.GOOS == "windows" && fh.flags&os.O_APPEND != 0 { if runtime.GOOS == "windows" && fh.flags&os.O_APPEND != 0 {
cacheFileOpenFlags &^= os.O_TRUNC cacheFileOpenFlags &^= os.O_TRUNC
_, err = os.Stat(fh.osPath) _, err = os.Stat(fh.file.osPath())
if err == nil { if err == nil {
err = os.Truncate(fh.osPath, 0) err = os.Truncate(fh.file.osPath(), 0)
if err != nil { if err != nil {
return errors.Wrap(err, "cache open failed to truncate") return errors.Wrap(err, "cache open failed to truncate")
} }
@ -184,7 +181,7 @@ func (fh *RWFileHandle) openPending(truncate bool) (err error) {
if fd == nil { if fd == nil {
fs.Debugf(fh.logPrefix(), "Opening cached copy with flags=%s", decodeOpenFlags(fh.flags)) fs.Debugf(fh.logPrefix(), "Opening cached copy with flags=%s", decodeOpenFlags(fh.flags))
fd, err = file.OpenFile(fh.osPath, cacheFileOpenFlags, 0600) fd, err = file.OpenFile(fh.file.osPath(), cacheFileOpenFlags, 0600)
if err != nil { if err != nil {
return errors.Wrap(err, "cache open file failed") return errors.Wrap(err, "cache open file failed")
} }
@ -280,14 +277,14 @@ func (fh *RWFileHandle) flushWrites(closeFile bool) error {
if isCopied { if isCopied {
// Transfer the temp file to the remote // Transfer the temp file to the remote
cacheObj, err := fh.d.vfs.cache.f.NewObject(context.TODO(), fh.remote) cacheObj, err := fh.d.vfs.cache.f.NewObject(context.TODO(), fh.file.Path())
if err != nil { if err != nil {
err = errors.Wrap(err, "failed to find cache file") err = errors.Wrap(err, "failed to find cache file")
fs.Errorf(fh.logPrefix(), "%v", err) fs.Errorf(fh.logPrefix(), "%v", err)
return err return err
} }
o, err := copyObj(fh.d.vfs.f, fh.file.getObject(), fh.remote, cacheObj) o, err := copyObj(fh.d.vfs.f, fh.file.getObject(), fh.file.Path(), cacheObj)
if err != nil { if err != nil {
err = errors.Wrap(err, "failed to transfer file from cache to remote") err = errors.Wrap(err, "failed to transfer file from cache to remote")
fs.Errorf(fh.logPrefix(), "%v", err) fs.Errorf(fh.logPrefix(), "%v", err)
@ -320,7 +317,7 @@ func (fh *RWFileHandle) close() (err error) {
if fh.opened { if fh.opened {
fh.file.delRWOpen() fh.file.delRWOpen()
} }
fh.d.vfs.cache.close(fh.remote) fh.d.vfs.cache.close(fh.file.Path())
}() }()
return fh.flushWrites(true) return fh.flushWrites(true)
@ -549,5 +546,5 @@ func (fh *RWFileHandle) Sync() error {
} }
func (fh *RWFileHandle) logPrefix() string { func (fh *RWFileHandle) logPrefix() string {
return fmt.Sprintf("%s(%p)", fh.remote, fh) return fmt.Sprintf("%s(%p)", fh.file.Path(), fh)
} }