in internal/fs/inode/file.go [686:770]
func (f *FileInode) SetMtime(
ctx context.Context,
mtime time.Time) (err error) {
if f.IsUnlinked() {
// No need to update mtime on GCS for unlinked file.
return
}
// When bufferedWritesHandler instance is not nil, set time on bwh.
// It will not be nil in 2 cases when bufferedWrites are enabled:
// 1. local files
// 2. After first write on empty GCS files.
if f.bwh != nil {
f.bwh.SetMtime(mtime)
return
}
// If we have a local temp file, stat it.
var sr gcsx.StatResult
if f.content != nil {
sr, err = f.content.Stat()
if err != nil {
err = fmt.Errorf("stat: %w", err)
return
}
}
// 1. If the local content is dirty, simply update its mtime and return. This
// will cause the object in the bucket to be updated once we sync. If we lose
// power or something the mtime update will be lost, but so will the file
// data modifications so this doesn't seem so bad. It's worth saving the
// round trip to GCS for the common case of Linux writeback caching, where we
// always receive a setattr request just before a flush of a dirty file.
//
// 2. If the file is local, that means its not yet synced to GCS. Just update
// the mtime locally, it will be synced when the object is created on GCS.
if sr.Mtime != nil || f.IsLocal() {
f.content.SetMtime(mtime)
return
}
// Otherwise, update the backing object's metadata.
formatted := mtime.UTC().Format(time.RFC3339Nano)
srcGen := f.SourceGeneration()
req := &gcs.UpdateObjectRequest{
Name: f.src.Name,
Generation: srcGen.Object,
MetaGenerationPrecondition: &srcGen.Metadata,
Metadata: map[string]*string{
FileMtimeMetadataKey: &formatted,
},
}
o, err := f.bucket.UpdateObject(ctx, req)
if err == nil {
var minObj gcs.MinObject
minObjPtr := storageutil.ConvertObjToMinObject(o)
if minObjPtr != nil {
minObj = *minObjPtr
}
f.src = minObj
f.updateMRDWrapper()
return
}
var notFoundErr *gcs.NotFoundError
if errors.As(err, ¬FoundErr) {
// Special case: silently ignore not found errors, which mean the file has
// been unlinked.
err = nil
return
}
var preconditionErr *gcs.PreconditionError
if errors.As(err, &preconditionErr) {
// Special case: silently ignore precondition errors, which we also take to
// mean the file has been unlinked.
err = nil
return
}
err = fmt.Errorf("UpdateObject: %w", err)
return
}