mirror of
https://github.com/rclone/rclone.git
synced 2024-12-23 07:29:35 +01:00
allow modTime to be changed even before all writers are closed (fixes #1197 -- again)
This commit is contained in:
parent
2b8c461e04
commit
2fd86c93fc
@ -16,11 +16,12 @@ import (
|
||||
|
||||
// File represents a file
|
||||
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.RWMutex // protects the following
|
||||
o fs.Object // NB o may be nil if file is being written
|
||||
writers int // number of writers for this file
|
||||
size int64 // size of file - read and written with atomic int64 - must be 64 bit aligned
|
||||
d *Dir // parent directory - read only
|
||||
mu sync.RWMutex // protects the following
|
||||
o fs.Object // NB o may be nil if file is being written
|
||||
writers int // number of writers for this file
|
||||
pendingModTime time.Time // will be applied once o becomes available, i.e. after file was written
|
||||
}
|
||||
|
||||
// newFile creates a new File
|
||||
@ -59,6 +60,12 @@ func (f *File) Attr(ctx context.Context, a *fuse.Attr) error {
|
||||
// if o is nil it isn't valid yet, so return the size so far
|
||||
if f.o == nil {
|
||||
a.Size = uint64(atomic.LoadInt64(&f.size))
|
||||
if !noModTime && !f.pendingModTime.IsZero() {
|
||||
a.Atime = f.pendingModTime
|
||||
a.Mtime = f.pendingModTime
|
||||
a.Ctime = f.pendingModTime
|
||||
a.Crtime = f.pendingModTime
|
||||
}
|
||||
} else {
|
||||
a.Size = uint64(f.o.Size())
|
||||
if !noModTime {
|
||||
@ -83,33 +90,44 @@ func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse
|
||||
return nil
|
||||
}
|
||||
|
||||
// if o is nil it isn't valid yet
|
||||
o, err := f.waitForValidObject()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
|
||||
var newTime time.Time
|
||||
if req.Valid.MtimeNow() {
|
||||
newTime = time.Now()
|
||||
f.pendingModTime = time.Now()
|
||||
} else if req.Valid.Mtime() {
|
||||
newTime = req.Mtime
|
||||
f.pendingModTime = req.Mtime
|
||||
}
|
||||
|
||||
if !newTime.IsZero() {
|
||||
err := o.SetModTime(newTime)
|
||||
switch err {
|
||||
case nil:
|
||||
fs.Debugf(o, "File.Setattr ModTime OK")
|
||||
case fs.ErrorCantSetModTime:
|
||||
// do nothing, in order to not break "touch somefile" if it exists already
|
||||
default:
|
||||
fs.Errorf(o, "File.Setattr ModTime error: %v", err)
|
||||
return err
|
||||
}
|
||||
if f.o != nil {
|
||||
return f.applyPendingModTime()
|
||||
}
|
||||
|
||||
// queue up for later, hoping f.o becomes available
|
||||
return nil
|
||||
}
|
||||
|
||||
// call with the mutex held
|
||||
func (f *File) applyPendingModTime() error {
|
||||
defer func() { f.pendingModTime = time.Time{} }()
|
||||
|
||||
if f.pendingModTime.IsZero() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if f.o == nil {
|
||||
return errors.New("Cannot apply ModTime, file object is not available")
|
||||
}
|
||||
|
||||
err := f.o.SetModTime(f.pendingModTime)
|
||||
switch err {
|
||||
case nil:
|
||||
fs.Debugf(f.o, "File.applyPendingModTime OK")
|
||||
case fs.ErrorCantSetModTime:
|
||||
// do nothing, in order to not break "touch somefile" if it exists already
|
||||
default:
|
||||
fs.Errorf(f.o, "File.applyPendingModTime error: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -125,6 +143,7 @@ func (f *File) setObject(o fs.Object) {
|
||||
f.mu.Lock()
|
||||
defer f.mu.Unlock()
|
||||
f.o = o
|
||||
_ = f.applyPendingModTime()
|
||||
f.d.addObject(o, f)
|
||||
}
|
||||
|
||||
|
@ -28,3 +28,30 @@ func TestFileModTime(t *testing.T) {
|
||||
|
||||
run.rm(t, "file")
|
||||
}
|
||||
|
||||
func TestFileModTimeWithOpenWriters(t *testing.T) {
|
||||
run.skipIfNoFUSE(t)
|
||||
|
||||
mtime := time.Date(2012, 11, 18, 17, 32, 31, 0, time.UTC)
|
||||
filepath := run.path("cp-archive-test")
|
||||
|
||||
f, err := os.Create(filepath)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = f.Write([]byte{104, 105})
|
||||
require.NoError(t, err)
|
||||
|
||||
err = os.Chtimes(filepath, mtime, mtime)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = f.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := os.Stat(filepath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// avoid errors because of timezone differences
|
||||
assert.Equal(t, info.ModTime().Unix(), mtime.Unix())
|
||||
|
||||
run.rm(t, "cp-archive-test")
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user