From b7c70a981734803f5dbf45acd6bd2a643c1d75cf Mon Sep 17 00:00:00 2001 From: Audrius Butkevicius Date: Tue, 11 Jun 2019 07:27:13 +0100 Subject: [PATCH] lib/fs: Enhance mtimefs, use everywhere (fixes #5777) (#5776) --- go.sum | 2 + lib/fs/mtimefs.go | 78 ++++++++++++++++++++++++++++++ lib/fs/mtimefs_test.go | 106 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 186 insertions(+) diff --git a/go.sum b/go.sum index c97071db3..808ec48f3 100644 --- a/go.sum +++ b/go.sum @@ -125,10 +125,12 @@ github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1: github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1 h1:K0MGApIoQvMw27RTdJkPbr3JZ7DNbtxQNyi5STVM6Kw= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2 h1:6LJUbpNm42llc4HRCuvApCSWB/WfhuNo9K98Q9sNGfs= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20171128170426-e181e095bae9 h1:jmLW6izPBVlIbk4d+XgK9+sChGbVKxxOPmd9eqRHCjw= diff --git a/lib/fs/mtimefs.go b/lib/fs/mtimefs.go index 3f375640e..199fb8acb 100644 --- a/lib/fs/mtimefs.go +++ b/lib/fs/mtimefs.go @@ -66,6 +66,23 @@ func (f *MtimeFS) Chtimes(name string, atime, mtime time.Time) error { return nil } +func (f *MtimeFS) Stat(name string) (FileInfo, error) { + info, err := f.Filesystem.Stat(name) + if err != nil { + return nil, err + } + + real, virtual := f.load(name) + if real == info.ModTime() { + info = mtimeFileInfo{ + FileInfo: info, + mtime: virtual, + } + } + + return info, nil +} + func (f *MtimeFS) Lstat(name string) (FileInfo, error) { info, err := f.Filesystem.Lstat(name) if err != nil { @@ -83,6 +100,45 @@ func (f *MtimeFS) Lstat(name string) (FileInfo, error) { return info, nil } +func (f *MtimeFS) Walk(root string, walkFn WalkFunc) error { + return f.Filesystem.Walk(root, func(path string, info FileInfo, err error) error { + if info != nil { + real, virtual := f.load(path) + if real == info.ModTime() { + info = mtimeFileInfo{ + FileInfo: info, + mtime: virtual, + } + } + } + return walkFn(path, info, err) + }) +} + +func (f *MtimeFS) Create(name string) (File, error) { + fd, err := f.Filesystem.Create(name) + if err != nil { + return nil, err + } + return &mtimeFile{fd, f}, nil +} + +func (f *MtimeFS) Open(name string) (File, error) { + fd, err := f.Filesystem.Open(name) + if err != nil { + return nil, err + } + return &mtimeFile{fd, f}, nil +} + +func (f *MtimeFS) OpenFile(name string, flags int, mode FileMode) (File, error) { + fd, err := f.Filesystem.OpenFile(name, flags, mode) + if err != nil { + return nil, err + } + return &mtimeFile{fd, f}, nil +} + // "real" is the on disk timestamp // "virtual" is what want the timestamp to be @@ -135,6 +191,28 @@ func (m mtimeFileInfo) ModTime() time.Time { return m.mtime } +type mtimeFile struct { + File + fs *MtimeFS +} + +func (f *mtimeFile) Stat() (FileInfo, error) { + info, err := f.File.Stat() + if err != nil { + return nil, err + } + + real, virtual := f.fs.load(f.Name()) + if real == info.ModTime() { + info = mtimeFileInfo{ + FileInfo: info, + mtime: virtual, + } + } + + return info, nil +} + // The dbMtime is our database representation type dbMtime struct { diff --git a/lib/fs/mtimefs_test.go b/lib/fs/mtimefs_test.go index e8befb591..1c002d25e 100644 --- a/lib/fs/mtimefs_test.go +++ b/lib/fs/mtimefs_test.go @@ -10,6 +10,7 @@ import ( "errors" "io/ioutil" "os" + "path/filepath" "runtime" "testing" "time" @@ -81,6 +82,111 @@ func TestMtimeFS(t *testing.T) { } } +func TestMtimeFSWalk(t *testing.T) { + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer func() { _ = os.RemoveAll(dir) }() + + underlying := NewFilesystem(FilesystemTypeBasic, dir) + mtimefs := NewMtimeFS(underlying, make(mapStore)) + mtimefs.chtimes = failChtimes + + if err := ioutil.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0644); err != nil { + t.Fatal(err) + } + + oldStat, err := mtimefs.Lstat("file") + if err != nil { + t.Fatal(err) + } + + newTime := time.Now().Add(-2 * time.Hour) + + if err := mtimefs.Chtimes("file", newTime, newTime); err != nil { + t.Fatal(err) + } + + if newStat, err := mtimefs.Lstat("file"); err != nil { + t.Fatal(err) + } else if !newStat.ModTime().Equal(newTime) { + t.Errorf("expected time %v, lstat time %v", newTime, newStat.ModTime()) + } + + if underlyingStat, err := underlying.Lstat("file"); err != nil { + t.Fatal(err) + } else if !underlyingStat.ModTime().Equal(oldStat.ModTime()) { + t.Errorf("expected time %v, lstat time %v", oldStat.ModTime(), underlyingStat.ModTime()) + } + + found := false + _ = mtimefs.Walk("", func(path string, info FileInfo, err error) error { + if path == "file" { + found = true + if !info.ModTime().Equal(newTime) { + t.Errorf("expected time %v, lstat time %v", newTime, info.ModTime()) + } + } + return nil + }) + + if !found { + t.Error("did not find") + } +} + +func TestMtimeFSOpen(t *testing.T) { + dir, err := ioutil.TempDir("", "") + if err != nil { + t.Fatal(err) + } + defer func() { _ = os.RemoveAll(dir) }() + + underlying := NewFilesystem(FilesystemTypeBasic, dir) + mtimefs := NewMtimeFS(underlying, make(mapStore)) + mtimefs.chtimes = failChtimes + + if err := ioutil.WriteFile(filepath.Join(dir, "file"), []byte("hello"), 0644); err != nil { + t.Fatal(err) + } + + oldStat, err := mtimefs.Lstat("file") + if err != nil { + t.Fatal(err) + } + + newTime := time.Now().Add(-2 * time.Hour) + + if err := mtimefs.Chtimes("file", newTime, newTime); err != nil { + t.Fatal(err) + } + + if newStat, err := mtimefs.Lstat("file"); err != nil { + t.Fatal(err) + } else if !newStat.ModTime().Equal(newTime) { + t.Errorf("expected time %v, lstat time %v", newTime, newStat.ModTime()) + } + + if underlyingStat, err := underlying.Lstat("file"); err != nil { + t.Fatal(err) + } else if !underlyingStat.ModTime().Equal(oldStat.ModTime()) { + t.Errorf("expected time %v, lstat time %v", oldStat.ModTime(), underlyingStat.ModTime()) + } + + fd, err := mtimefs.Open("file") + if err != nil { + t.Fatal(err) + } + info, err := fd.Stat() + if err != nil { + t.Fatal(err) + } + if !info.ModTime().Equal(newTime) { + t.Errorf("expected time %v, lstat time %v", newTime, info.ModTime()) + } +} + func TestMtimeFSInsensitive(t *testing.T) { switch runtime.GOOS { case "darwin", "windows":