forked from TrueCloudLab/rclone
allow modTime to be changed even before all writers are closed (fixes #1197 -- again)
This commit is contained in:
parent
2b8c461e04
commit
2fd86c93fc
2 changed files with 71 additions and 25 deletions
|
@ -21,6 +21,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
|
||||||
writers int // number of writers for this file
|
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
|
// 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 o is nil it isn't valid yet, so return the size so far
|
||||||
if f.o == nil {
|
if f.o == nil {
|
||||||
a.Size = uint64(atomic.LoadInt64(&f.size))
|
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 {
|
} else {
|
||||||
a.Size = uint64(f.o.Size())
|
a.Size = uint64(f.o.Size())
|
||||||
if !noModTime {
|
if !noModTime {
|
||||||
|
@ -83,34 +90,45 @@ func (f *File) Setattr(ctx context.Context, req *fuse.SetattrRequest, resp *fuse
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// if o is nil it isn't valid yet
|
|
||||||
o, err := f.waitForValidObject()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
|
|
||||||
var newTime time.Time
|
|
||||||
if req.Valid.MtimeNow() {
|
if req.Valid.MtimeNow() {
|
||||||
newTime = time.Now()
|
f.pendingModTime = time.Now()
|
||||||
} else if req.Valid.Mtime() {
|
} else if req.Valid.Mtime() {
|
||||||
newTime = req.Mtime
|
f.pendingModTime = req.Mtime
|
||||||
}
|
}
|
||||||
|
|
||||||
if !newTime.IsZero() {
|
if f.o != nil {
|
||||||
err := o.SetModTime(newTime)
|
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 {
|
switch err {
|
||||||
case nil:
|
case nil:
|
||||||
fs.Debugf(o, "File.Setattr ModTime OK")
|
fs.Debugf(f.o, "File.applyPendingModTime OK")
|
||||||
case fs.ErrorCantSetModTime:
|
case fs.ErrorCantSetModTime:
|
||||||
// do nothing, in order to not break "touch somefile" if it exists already
|
// do nothing, in order to not break "touch somefile" if it exists already
|
||||||
default:
|
default:
|
||||||
fs.Errorf(o, "File.Setattr ModTime error: %v", err)
|
fs.Errorf(f.o, "File.applyPendingModTime error: %v", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -125,6 +143,7 @@ func (f *File) setObject(o fs.Object) {
|
||||||
f.mu.Lock()
|
f.mu.Lock()
|
||||||
defer f.mu.Unlock()
|
defer f.mu.Unlock()
|
||||||
f.o = o
|
f.o = o
|
||||||
|
_ = f.applyPendingModTime()
|
||||||
f.d.addObject(o, f)
|
f.d.addObject(o, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,3 +28,30 @@ func TestFileModTime(t *testing.T) {
|
||||||
|
|
||||||
run.rm(t, "file")
|
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 a new issue