lib/fs, lib/api, lib/model: Expose mtime remappings as part of /db/file (#7624)

* lib/fs, lib/api, lib/model: Expose mtime remappings as part of /db/file

* Fix wrong error returned by CLI

* Gofmt

* Better names

* Review comments

* Review comments
This commit is contained in:
Audrius Butkevicius
2021-05-03 11:28:25 +01:00
committed by GitHub
parent f09dcb98eb
commit 87a0eecc31
16 changed files with 305 additions and 111 deletions
+8
View File
@@ -330,6 +330,14 @@ func (f *BasicFilesystem) SameFile(fi1, fi2 FileInfo) bool {
return os.SameFile(f1.osFileInfo(), f2.osFileInfo())
}
func (f *BasicFilesystem) underlying() (Filesystem, bool) {
return nil, false
}
func (f *BasicFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeNone
}
// basicFile implements the fs.File interface on top of an os.File
type basicFile struct {
*os.File
+8
View File
@@ -339,6 +339,14 @@ func (f *caseFilesystem) Unhide(name string) error {
return f.Filesystem.Unhide(name)
}
func (f *caseFilesystem) underlying() (Filesystem, bool) {
return f.Filesystem, true
}
func (f *caseFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeCase
}
func (f *caseFilesystem) checkCase(name string) error {
var err error
if name, err = Canonicalize(name); err != nil {
+12 -3
View File
@@ -161,7 +161,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
b.Fatal(err)
}
b.Run("rawfs", func(b *testing.B) {
fakefs := unwrapFilesystem(fsys).(*fakefs)
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok {
fakefs = ffs.(*fakeFS)
}
fakefs.resetCounters()
benchmarkWalkFakeFS(b, fsys, paths, 0, "")
fakefs.reportMetricsPerOp(b)
@@ -174,7 +177,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
Filesystem: fsys,
realCaser: newDefaultRealCaser(fsys),
}
fakefs := unwrapFilesystem(fsys).(*fakefs)
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok {
fakefs = ffs.(*fakeFS)
}
fakefs.resetCounters()
benchmarkWalkFakeFS(b, casefs, paths, 0, "")
fakefs.reportMetricsPerOp(b)
@@ -197,7 +203,10 @@ func BenchmarkWalkCaseFakeFS100k(b *testing.B) {
Filesystem: fsys,
realCaser: newDefaultRealCaser(fsys),
}
fakefs := unwrapFilesystem(fsys).(*fakefs)
var fakefs *fakeFS
if ffs, ok := unwrapFilesystem(fsys, filesystemWrapperTypeNone); ok {
fakefs = ffs.(*fakeFS)
}
fakefs.resetCounters()
benchmarkWalkFakeFS(b, casefs, paths, otherOpEvery, otherOpPath)
fakefs.reportMetricsPerOp(b)
+2 -1
View File
@@ -18,7 +18,8 @@ import (
// reason for existence is the Windows version, which allows creating
// symlinks when non-elevated.
func DebugSymlinkForTestsOnly(oldFs, newFs Filesystem, oldname, newname string) error {
if caseFs, ok := unwrapFilesystem(newFs).(*caseFilesystem); ok {
if fs, ok := unwrapFilesystem(newFs, filesystemWrapperTypeCase); ok {
caseFs := fs.(*caseFilesystem)
if err := caseFs.checkCase(newname); err != nil {
return err
}
+8
View File
@@ -52,3 +52,11 @@ func (fs *errorFilesystem) SameFile(fi1, fi2 FileInfo) bool { return false }
func (fs *errorFilesystem) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
return nil, nil, fs.err
}
func (fs *errorFilesystem) underlying() (Filesystem, bool) {
return nil, false
}
func (fs *errorFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeError
}
+57 -49
View File
@@ -27,7 +27,7 @@ import (
// see readShortAt()
const randomBlockShift = 14 // 128k
// fakefs is a fake filesystem for testing and benchmarking. It has the
// fakeFS is a fake filesystem for testing and benchmarking. It has the
// following properties:
//
// - File metadata is kept in RAM. Specifically, we remember which files and
@@ -37,7 +37,7 @@ const randomBlockShift = 14 // 128k
// - File contents are generated pseudorandomly with just the file name as
// seed. Writes are discarded, other than having the effect of increasing
// the file size. If you only write data that you've read from a file with
// the same name on a different fakefs, you'll never know the difference...
// the same name on a different fakeFS, you'll never know the difference...
//
// - We totally ignore permissions - pretend you are root.
//
@@ -51,10 +51,10 @@ const randomBlockShift = 14 // 128k
// insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
// latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
//
// - Two fakefs:s pointing at the same root path see the same files.
// - Two fakeFS:s pointing at the same root path see the same files.
//
type fakefs struct {
counters fakefsCounters
type fakeFS struct {
counters fakeFSCounters
uri string
mut sync.Mutex
root *fakeEntry
@@ -63,7 +63,7 @@ type fakefs struct {
latency time.Duration
}
type fakefsCounters struct {
type fakeFSCounters struct {
Chmod int64
Lchown int64
Chtimes int64
@@ -81,13 +81,13 @@ type fakefsCounters struct {
}
var (
fakefsMut sync.Mutex
fakefsFs = make(map[string]*fakefs)
fakeFSMut sync.Mutex
fakeFSCache = make(map[string]*fakeFS)
)
func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
fakefsMut.Lock()
defer fakefsMut.Unlock()
func newFakeFilesystem(rootURI string, _ ...Option) *fakeFS {
fakeFSMut.Lock()
defer fakeFSMut.Unlock()
root := rootURI
var params url.Values
@@ -97,12 +97,12 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
params = uri.Query()
}
if fs, ok := fakefsFs[rootURI]; ok {
if fs, ok := fakeFSCache[rootURI]; ok {
// Already have an fs at this path
return fs
}
fs := &fakefs{
fs := &fakeFS{
uri: "fake://" + rootURI,
root: &fakeEntry{
name: "/",
@@ -157,7 +157,7 @@ func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
// the filesystem initially.
fs.latency, _ = time.ParseDuration(params.Get("latency"))
fakefsFs[root] = fs
fakeFSCache[root] = fs
return fs
}
@@ -183,7 +183,7 @@ type fakeEntry struct {
content []byte
}
func (fs *fakefs) entryForName(name string) *fakeEntry {
func (fs *fakeFS) entryForName(name string) *fakeEntry {
// bug: lookup doesn't work through symlinks.
if fs.insens {
name = UnicodeLowercase(name)
@@ -210,7 +210,7 @@ func (fs *fakefs) entryForName(name string) *fakeEntry {
return entry
}
func (fs *fakefs) Chmod(name string, mode FileMode) error {
func (fs *fakeFS) Chmod(name string, mode FileMode) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Chmod++
@@ -223,7 +223,7 @@ func (fs *fakefs) Chmod(name string, mode FileMode) error {
return nil
}
func (fs *fakefs) Lchown(name string, uid, gid int) error {
func (fs *fakeFS) Lchown(name string, uid, gid int) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Lchown++
@@ -237,7 +237,7 @@ func (fs *fakefs) Lchown(name string, uid, gid int) error {
return nil
}
func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
func (fs *fakeFS) Chtimes(name string, atime time.Time, mtime time.Time) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Chtimes++
@@ -250,7 +250,7 @@ func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
return nil
}
func (fs *fakefs) create(name string) (*fakeEntry, error) {
func (fs *fakeFS) create(name string) (*fakeEntry, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Create++
@@ -296,7 +296,7 @@ func (fs *fakefs) create(name string) (*fakeEntry, error) {
return new, nil
}
func (fs *fakefs) Create(name string) (File, error) {
func (fs *fakeFS) Create(name string) (File, error) {
entry, err := fs.create(name)
if err != nil {
return nil, err
@@ -307,7 +307,7 @@ func (fs *fakefs) Create(name string) (File, error) {
return &fakeFile{fakeEntry: entry}, nil
}
func (fs *fakefs) CreateSymlink(target, name string) error {
func (fs *fakeFS) CreateSymlink(target, name string) error {
entry, err := fs.create(name)
if err != nil {
return err
@@ -317,7 +317,7 @@ func (fs *fakefs) CreateSymlink(target, name string) error {
return nil
}
func (fs *fakefs) DirNames(name string) ([]string, error) {
func (fs *fakeFS) DirNames(name string) ([]string, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.DirNames++
@@ -336,7 +336,7 @@ func (fs *fakefs) DirNames(name string) ([]string, error) {
return names, nil
}
func (fs *fakefs) Lstat(name string) (FileInfo, error) {
func (fs *fakeFS) Lstat(name string) (FileInfo, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Lstat++
@@ -355,7 +355,7 @@ func (fs *fakefs) Lstat(name string) (FileInfo, error) {
return info, nil
}
func (fs *fakefs) Mkdir(name string, perm FileMode) error {
func (fs *fakeFS) Mkdir(name string, perm FileMode) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Mkdir++
@@ -389,7 +389,7 @@ func (fs *fakefs) Mkdir(name string, perm FileMode) error {
return nil
}
func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
func (fs *fakeFS) MkdirAll(name string, perm FileMode) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.MkdirAll++
@@ -426,7 +426,7 @@ func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
return nil
}
func (fs *fakefs) Open(name string) (File, error) {
func (fs *fakeFS) Open(name string) (File, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Open++
@@ -443,7 +443,7 @@ func (fs *fakefs) Open(name string) (File, error) {
return &fakeFile{fakeEntry: entry}, nil
}
func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
func (fs *fakeFS) OpenFile(name string, flags int, mode FileMode) (File, error) {
if flags&os.O_CREATE == 0 {
return fs.Open(name)
}
@@ -486,7 +486,7 @@ func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error)
return &fakeFile{fakeEntry: newEntry}, nil
}
func (fs *fakefs) ReadSymlink(name string) (string, error) {
func (fs *fakeFS) ReadSymlink(name string) (string, error) {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.ReadSymlink++
@@ -501,7 +501,7 @@ func (fs *fakefs) ReadSymlink(name string) (string, error) {
return entry.dest, nil
}
func (fs *fakefs) Remove(name string) error {
func (fs *fakeFS) Remove(name string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Remove++
@@ -524,7 +524,7 @@ func (fs *fakefs) Remove(name string) error {
return nil
}
func (fs *fakefs) RemoveAll(name string) error {
func (fs *fakeFS) RemoveAll(name string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.RemoveAll++
@@ -536,7 +536,7 @@ func (fs *fakefs) RemoveAll(name string) error {
entry := fs.entryForName(filepath.Dir(name))
if entry == nil {
return nil // all tested real systems exibit this behaviour
return nil // all tested real systems exhibit this behaviour
}
// RemoveAll is easy when the file system uses garbage collection under
@@ -545,7 +545,7 @@ func (fs *fakefs) RemoveAll(name string) error {
return nil
}
func (fs *fakefs) Rename(oldname, newname string) error {
func (fs *fakeFS) Rename(oldname, newname string) error {
fs.mut.Lock()
defer fs.mut.Unlock()
fs.counters.Rename++
@@ -595,56 +595,56 @@ func (fs *fakefs) Rename(oldname, newname string) error {
return nil
}
func (fs *fakefs) Stat(name string) (FileInfo, error) {
func (fs *fakeFS) Stat(name string) (FileInfo, error) {
return fs.Lstat(name)
}
func (fs *fakefs) SymlinksSupported() bool {
func (fs *fakeFS) SymlinksSupported() bool {
return false
}
func (fs *fakefs) Walk(name string, walkFn WalkFunc) error {
func (fs *fakeFS) Walk(name string, walkFn WalkFunc) error {
return errors.New("not implemented")
}
func (fs *fakefs) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
func (fs *fakeFS) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
return nil, nil, ErrWatchNotSupported
}
func (fs *fakefs) Hide(name string) error {
func (fs *fakeFS) Hide(name string) error {
return nil
}
func (fs *fakefs) Unhide(name string) error {
func (fs *fakeFS) Unhide(name string) error {
return nil
}
func (fs *fakefs) Glob(pattern string) ([]string, error) {
func (fs *fakeFS) Glob(pattern string) ([]string, error) {
// gnnh we don't seem to actually require this in practice
return nil, errors.New("not implemented")
}
func (fs *fakefs) Roots() ([]string, error) {
func (fs *fakeFS) Roots() ([]string, error) {
return []string{"/"}, nil
}
func (fs *fakefs) Usage(name string) (Usage, error) {
func (fs *fakeFS) Usage(name string) (Usage, error) {
return Usage{}, errors.New("not implemented")
}
func (fs *fakefs) Type() FilesystemType {
func (fs *fakeFS) Type() FilesystemType {
return FilesystemTypeFake
}
func (fs *fakefs) URI() string {
func (fs *fakeFS) URI() string {
return fs.uri
}
func (fs *fakefs) Options() []Option {
func (fs *fakeFS) Options() []Option {
return nil
}
func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
func (fs *fakeFS) SameFile(fi1, fi2 FileInfo) bool {
// BUG: real systems base file sameness on path, inodes, etc
// we try our best, but FileInfo just doesn't have enough data
// so there be false positives, especially on Windows
@@ -659,17 +659,25 @@ func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
}
func (fs *fakefs) resetCounters() {
func (fs *fakeFS) underlying() (Filesystem, bool) {
return nil, false
}
func (fs *fakeFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeNone
}
func (fs *fakeFS) resetCounters() {
fs.mut.Lock()
fs.counters = fakefsCounters{}
fs.counters = fakeFSCounters{}
fs.mut.Unlock()
}
func (fs *fakefs) reportMetricsPerOp(b *testing.B) {
func (fs *fakeFS) reportMetricsPerOp(b *testing.B) {
fs.reportMetricsPer(b, 1, "op")
}
func (fs *fakefs) reportMetricsPer(b *testing.B, divisor float64, unit string) {
func (fs *fakeFS) reportMetricsPer(b *testing.B, divisor float64, unit string) {
fs.mut.Lock()
defer fs.mut.Unlock()
b.ReportMetric(float64(fs.counters.Lstat)/divisor/float64(b.N), "Lstat/"+unit)
+5 -5
View File
@@ -21,7 +21,7 @@ import (
)
func TestFakeFS(t *testing.T) {
// Test some basic aspects of the fakefs
// Test some basic aspects of the fakeFS
fs := newFakeFilesystem("/foo/bar/baz")
@@ -131,7 +131,7 @@ func TestFakeFS(t *testing.T) {
}
func testFakeFSRead(t *testing.T, fs Filesystem) {
// Test some basic aspects of the fakefs
// Test some basic aspects of the fakeFS
// Create
fd, _ := fs.Create("test")
defer fd.Close()
@@ -201,7 +201,7 @@ func TestFakeFSCaseSensitive(t *testing.T) {
{"FileName", testFakeFSFileName},
}
var filesystems = []testFS{
{"fakefs", newFakeFilesystem("/foo")},
{"fakeFS", newFakeFilesystem("/foo")},
}
testDir, sensitive := createTestDir(t)
@@ -237,7 +237,7 @@ func TestFakeFSCaseInsensitive(t *testing.T) {
}
var filesystems = []testFS{
{"fakefs", newFakeFilesystem("/foobar?insens=true")},
{"fakeFS", newFakeFilesystem("/foobar?insens=true")},
}
testDir, sensitive := createTestDir(t)
@@ -891,7 +891,7 @@ func testFakeFSCreateInsens(t *testing.T, fs Filesystem) {
t.Errorf("name of created file \"fOo\" is %s", fd2.Name())
}
// one would expect DirNames to show the last variant, but in fact it shows
// one would expect DirNames to show the last wrapperType, but in fact it shows
// the original one
assertDir(t, fs, "/", []string{"FOO"})
}
+24 -11
View File
@@ -16,6 +16,17 @@ import (
"time"
)
type filesystemWrapperType int32
const (
filesystemWrapperTypeNone filesystemWrapperType = iota
filesystemWrapperTypeMtime
filesystemWrapperTypeCase
filesystemWrapperTypeError
filesystemWrapperTypeWalk
filesystemWrapperTypeLog
)
// The Filesystem interface abstracts access to the file system.
type Filesystem interface {
Chmod(name string, mode FileMode) error
@@ -49,6 +60,10 @@ type Filesystem interface {
URI() string
Options() []Option
SameFile(fi1, fi2 FileInfo) bool
// Used for unwrapping things
underlying() (Filesystem, bool)
wrapperType() filesystemWrapperType
}
// The File interface abstracts access to a regular file, being a somewhat
@@ -284,18 +299,16 @@ func wrapFilesystem(fs Filesystem, wrapFn func(Filesystem) Filesystem) Filesyste
return fs
}
// unwrapFilesystem removes "wrapping" filesystems to expose the underlying filesystem.
func unwrapFilesystem(fs Filesystem) Filesystem {
// unwrapFilesystem removes "wrapping" filesystems to expose the filesystem of the requested wrapperType, if it exists.
func unwrapFilesystem(fs Filesystem, wrapperType filesystemWrapperType) (Filesystem, bool) {
var ok bool
for {
switch sfs := fs.(type) {
case *logFilesystem:
fs = sfs.Filesystem
case *walkFilesystem:
fs = sfs.Filesystem
case *mtimeFS:
fs = sfs.Filesystem
default:
return sfs
if fs.wrapperType() == wrapperType {
return fs, true
}
fs, ok = fs.underlying()
if !ok {
return nil, false
}
}
}
+8
View File
@@ -163,3 +163,11 @@ func (fs *logFilesystem) Usage(name string) (Usage, error) {
l.Debugln(getCaller(), fs.Type(), fs.URI(), "Usage", name, usage, err)
return usage, err
}
func (fs *logFilesystem) underlying() (Filesystem, bool) {
return fs.Filesystem, true
}
func (fs *logFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeLog
}
+54 -35
View File
@@ -7,6 +7,7 @@
package fs
import (
"errors"
"time"
)
@@ -69,14 +70,14 @@ func (f *mtimeFS) Stat(name string) (FileInfo, error) {
return nil, err
}
real, virtual, err := f.load(name)
mtimeMapping, err := f.load(name)
if err != nil {
return nil, err
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
@@ -89,14 +90,14 @@ func (f *mtimeFS) Lstat(name string) (FileInfo, error) {
return nil, err
}
real, virtual, err := f.load(name)
mtimeMapping, err := f.load(name)
if err != nil {
return nil, err
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
@@ -106,15 +107,15 @@ func (f *mtimeFS) Lstat(name string) (FileInfo, error) {
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, loadErr := f.load(path)
mtimeMapping, loadErr := f.load(path)
if loadErr != nil && err == nil {
// The iterator gets to deal with the error
err = loadErr
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
}
@@ -146,8 +147,13 @@ func (f *mtimeFS) OpenFile(name string, flags int, mode FileMode) (File, error)
return mtimeFile{fd, f}, nil
}
// "real" is the on disk timestamp
// "virtual" is what want the timestamp to be
func (f *mtimeFS) underlying() (Filesystem, bool) {
return f.Filesystem, true
}
func (f *mtimeFS) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeMtime
}
func (f *mtimeFS) save(name string, real, virtual time.Time) {
if f.caseInsensitive {
@@ -161,32 +167,31 @@ func (f *mtimeFS) save(name string, real, virtual time.Time) {
return
}
mtime := dbMtime{
real: real,
virtual: virtual,
mtime := MtimeMapping{
Real: real,
Virtual: virtual,
}
bs, _ := mtime.Marshal() // Can't fail
f.db.PutBytes(name, bs)
}
func (f *mtimeFS) load(name string) (real, virtual time.Time, err error) {
func (f *mtimeFS) load(name string) (MtimeMapping, error) {
if f.caseInsensitive {
name = UnicodeLowercase(name)
}
data, exists, err := f.db.Bytes(name)
if err != nil {
return time.Time{}, time.Time{}, err
return MtimeMapping{}, err
} else if !exists {
return time.Time{}, time.Time{}, nil
return MtimeMapping{}, nil
}
var mtime dbMtime
var mtime MtimeMapping
if err := mtime.Unmarshal(data); err != nil {
return time.Time{}, time.Time{}, err
return MtimeMapping{}, err
}
return mtime.real, mtime.virtual, nil
return mtime, nil
}
// The mtimeFileInfo is an os.FileInfo that lies about the ModTime().
@@ -211,43 +216,57 @@ func (f mtimeFile) Stat() (FileInfo, error) {
return nil, err
}
real, virtual, err := f.fs.load(f.Name())
mtimeMapping, err := f.fs.load(f.Name())
if err != nil {
return nil, err
}
if real == info.ModTime() {
if mtimeMapping.Real == info.ModTime() {
info = mtimeFileInfo{
FileInfo: info,
mtime: virtual,
mtime: mtimeMapping.Virtual,
}
}
return info, nil
}
// Used by copyRange to unwrap to the real file and access SyscallConn
func (f mtimeFile) unwrap() File {
return f.File
}
// The dbMtime is our database representation
type dbMtime struct {
real time.Time
virtual time.Time
// MtimeMapping represents the mapping as stored in the database
type MtimeMapping struct {
// "Real" is the on disk timestamp
Real time.Time `json:"real"`
// "Virtual" is what want the timestamp to be
Virtual time.Time `json:"virtual"`
}
func (t *dbMtime) Marshal() ([]byte, error) {
bs0, _ := t.real.MarshalBinary()
bs1, _ := t.virtual.MarshalBinary()
func (t *MtimeMapping) Marshal() ([]byte, error) {
bs0, _ := t.Real.MarshalBinary()
bs1, _ := t.Virtual.MarshalBinary()
return append(bs0, bs1...), nil
}
func (t *dbMtime) Unmarshal(bs []byte) error {
if err := t.real.UnmarshalBinary(bs[:len(bs)/2]); err != nil {
func (t *MtimeMapping) Unmarshal(bs []byte) error {
if err := t.Real.UnmarshalBinary(bs[:len(bs)/2]); err != nil {
return err
}
if err := t.virtual.UnmarshalBinary(bs[len(bs)/2:]); err != nil {
if err := t.Virtual.UnmarshalBinary(bs[len(bs)/2:]); err != nil {
return err
}
return nil
}
func GetMtimeMapping(fs Filesystem, file string) (MtimeMapping, error) {
fs, ok := unwrapFilesystem(fs, filesystemWrapperTypeMtime)
if !ok {
return MtimeMapping{}, errors.New("failed to unwrap")
}
mtimeFs, ok := fs.(*mtimeFS)
if !ok {
return MtimeMapping{}, errors.New("unwrapping failed")
}
return mtimeFs.load(file)
}
+8
View File
@@ -149,3 +149,11 @@ func (f *walkFilesystem) Walk(root string, walkFn WalkFunc) error {
}
return f.walk(root, info, walkFn, ancestors)
}
func (f *walkFilesystem) underlying() (Filesystem, bool) {
return f.Filesystem, true
}
func (f *walkFilesystem) wrapperType() filesystemWrapperType {
return filesystemWrapperTypeWalk
}