diff --git a/finder/finder.go b/finder/finder.go index ce1747575..89be0f5f4 100644 --- a/finder/finder.go +++ b/finder/finder.go @@ -1384,7 +1384,7 @@ func (f *Finder) listDirSync(dir *pathMap) { f.onFsError(path, err) // if listing the contents of the directory fails (presumably due to // permission denied), then treat the directory as empty - children = []os.FileInfo{} + children = nil } var subdirs []string diff --git a/finder/fs/fs.go b/finder/fs/fs.go index eff8ad075..3de548659 100644 --- a/finder/fs/fs.go +++ b/finder/fs/fs.go @@ -51,7 +51,7 @@ type FileSystem interface { // getting information about files Open(name string) (file io.ReadCloser, err error) Lstat(path string) (stats os.FileInfo, err error) - ReadDir(path string) (contents []os.FileInfo, err error) + ReadDir(path string) (contents []DirEntryInfo, err error) InodeNumber(info os.FileInfo) (number uint64, err error) DeviceNumber(info os.FileInfo) (number uint64, err error) @@ -67,18 +67,39 @@ type FileSystem interface { ViewId() (id string) // Some unique id of the user accessing the filesystem } +// DentryInfo is a subset of the functionality available through os.FileInfo that might be able +// to be gleaned through only a syscall.Getdents without requiring a syscall.Lstat of every file. +type DirEntryInfo interface { + Name() string + Mode() os.FileMode // the file type encoded as an os.FileMode + IsDir() bool +} + +var _ DirEntryInfo = os.FileInfo(nil) + // osFs implements FileSystem using the local disk. type osFs struct{} +var _ FileSystem = (*osFs)(nil) + func (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) } func (osFs) Lstat(path string) (stats os.FileInfo, err error) { return os.Lstat(path) } -func (osFs) ReadDir(path string) (contents []os.FileInfo, err error) { - return ioutil.ReadDir(path) +func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) { + entries, err := ioutil.ReadDir(path) + if err != nil { + return nil, err + } + for _, entry := range entries { + contents = append(contents, entry) + } + + return contents, nil } + func (osFs) Rename(oldPath string, newPath string) error { return os.Rename(oldPath, newPath) } @@ -154,6 +175,8 @@ type MockFs struct { aggregatesLock sync.Mutex } +var _ FileSystem = (*MockFs)(nil) + type mockInode struct { modTime time.Time permTime time.Time @@ -475,7 +498,7 @@ func (m *MockFs) PermTime(info os.FileInfo) (when time.Time, err error) { fmt.Errorf("%v is not a mockFileInfo", info) } -func (m *MockFs) ReadDir(path string) (contents []os.FileInfo, err error) { +func (m *MockFs) ReadDir(path string) (contents []DirEntryInfo, err error) { // update aggregates m.aggregatesLock.Lock() m.ReadDirCalls = append(m.ReadDirCalls, path) @@ -486,7 +509,7 @@ func (m *MockFs) ReadDir(path string) (contents []os.FileInfo, err error) { if err != nil { return nil, err } - results := []os.FileInfo{} + results := []DirEntryInfo{} dir, err := m.getDir(path, false) if err != nil { return nil, err