Add Stat to finder/fs

Add a Stat method to finder/fs that will be used by finder to read
the mode of the target of a symlink.

Bug: 157656545
Test: fs_test.go
Change-Id: Ie2b4509b7d11857d9a1685de4477088b91d43c63
This commit is contained in:
Colin Cross
2020-06-29 23:01:52 -07:00
parent 7f8aa39abe
commit 7cdad45cf2
3 changed files with 101 additions and 9 deletions

View File

@@ -25,6 +25,7 @@ bootstrap_go_package {
"test.go", "test.go",
], ],
testSrcs: [ testSrcs: [
"fs_test.go",
"readdir_test.go", "readdir_test.go",
], ],
darwin: { darwin: {

View File

@@ -51,6 +51,7 @@ type FileSystem interface {
// getting information about files // getting information about files
Open(name string) (file io.ReadCloser, err error) Open(name string) (file io.ReadCloser, err error)
Lstat(path string) (stats os.FileInfo, err error) Lstat(path string) (stats os.FileInfo, err error)
Stat(path string) (stats os.FileInfo, err error)
ReadDir(path string) (contents []DirEntryInfo, err error) ReadDir(path string) (contents []DirEntryInfo, err error)
InodeNumber(info os.FileInfo) (number uint64, err error) InodeNumber(info os.FileInfo) (number uint64, err error)
@@ -99,6 +100,10 @@ func (osFs) Lstat(path string) (stats os.FileInfo, err error) {
return os.Lstat(path) return os.Lstat(path)
} }
func (osFs) Stat(path string) (stats os.FileInfo, err error) {
return os.Stat(path)
}
func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) { func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
entries, err := readdir(path) entries, err := readdir(path)
if err != nil { if err != nil {
@@ -376,7 +381,7 @@ type mockFileInfo struct {
size int64 size int64
modTime time.Time // time at which the inode's contents were modified modTime time.Time // time at which the inode's contents were modified
permTime time.Time // time at which the inode's permissions were modified permTime time.Time // time at which the inode's permissions were modified
isDir bool mode os.FileMode
inodeNumber uint64 inodeNumber uint64
deviceNumber uint64 deviceNumber uint64
} }
@@ -390,7 +395,7 @@ func (m *mockFileInfo) Size() int64 {
} }
func (m *mockFileInfo) Mode() os.FileMode { func (m *mockFileInfo) Mode() os.FileMode {
return 0 return m.mode
} }
func (m *mockFileInfo) ModTime() time.Time { func (m *mockFileInfo) ModTime() time.Time {
@@ -398,7 +403,7 @@ func (m *mockFileInfo) ModTime() time.Time {
} }
func (m *mockFileInfo) IsDir() bool { func (m *mockFileInfo) IsDir() bool {
return m.isDir return m.mode&os.ModeDir != 0
} }
func (m *mockFileInfo) Sys() interface{} { func (m *mockFileInfo) Sys() interface{} {
@@ -407,11 +412,11 @@ func (m *mockFileInfo) Sys() interface{} {
func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) { func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
return &mockFileInfo{ return &mockFileInfo{
path: path, path: filepath.Base(path),
size: 1, size: 1,
modTime: d.modTime, modTime: d.modTime,
permTime: d.permTime, permTime: d.permTime,
isDir: true, mode: os.ModeDir,
inodeNumber: d.inodeNumber, inodeNumber: d.inodeNumber,
deviceNumber: m.deviceNumber, deviceNumber: m.deviceNumber,
} }
@@ -420,11 +425,11 @@ func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) { func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
return &mockFileInfo{ return &mockFileInfo{
path: path, path: filepath.Base(path),
size: 1, size: 1,
modTime: f.modTime, modTime: f.modTime,
permTime: f.permTime, permTime: f.permTime,
isDir: false, mode: 0,
inodeNumber: f.inodeNumber, inodeNumber: f.inodeNumber,
deviceNumber: m.deviceNumber, deviceNumber: m.deviceNumber,
} }
@@ -432,11 +437,11 @@ func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) { func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) {
return &mockFileInfo{ return &mockFileInfo{
path: path, path: filepath.Base(path),
size: 1, size: 1,
modTime: l.modTime, modTime: l.modTime,
permTime: l.permTime, permTime: l.permTime,
isDir: false, mode: os.ModeSymlink,
inodeNumber: l.inodeNumber, inodeNumber: l.inodeNumber,
deviceNumber: m.deviceNumber, deviceNumber: m.deviceNumber,
} }
@@ -485,6 +490,16 @@ func (m *MockFs) Lstat(path string) (stats os.FileInfo, err error) {
} }
} }
func (m *MockFs) Stat(path string) (stats os.FileInfo, err error) {
// resolve symlinks
path, err = m.resolve(path, true)
if err != nil {
return nil, err
}
return m.Lstat(path)
}
func (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) { func (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) {
mockInfo, ok := info.(*mockFileInfo) mockInfo, ok := info.(*mockFileInfo)
if ok { if ok {

76
finder/fs/fs_test.go Normal file
View File

@@ -0,0 +1,76 @@
// Copyright 2020 Google Inc. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package fs
import (
"os"
"testing"
)
func TestMockFs_LstatStatSymlinks(t *testing.T) {
// setup filesystem
filesystem := NewMockFs(nil)
Create(t, "/tmp/realdir/hi.txt", filesystem)
Create(t, "/tmp/realdir/ignoreme.txt", filesystem)
Link(t, "/tmp/links/dir", "../realdir", filesystem)
Link(t, "/tmp/links/file", "../realdir/hi.txt", filesystem)
Link(t, "/tmp/links/broken", "nothingHere", filesystem)
Link(t, "/tmp/links/recursive", "recursive", filesystem)
assertStat := func(t *testing.T, stat os.FileInfo, err error, wantName string, wantMode os.FileMode) {
t.Helper()
if err != nil {
t.Error(err)
return
}
if g, w := stat.Name(), wantName; g != w {
t.Errorf("want name %q, got %q", w, g)
}
if g, w := stat.Mode(), wantMode; g != w {
t.Errorf("%s: want mode %q, got %q", wantName, w, g)
}
}
assertErr := func(t *testing.T, err error, wantErr string) {
if err == nil || err.Error() != wantErr {
t.Errorf("want error %q, got %q", wantErr, err)
}
}
stat, err := filesystem.Lstat("/tmp/links/dir")
assertStat(t, stat, err, "dir", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/dir")
assertStat(t, stat, err, "realdir", os.ModeDir)
stat, err = filesystem.Lstat("/tmp/links/file")
assertStat(t, stat, err, "file", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/file")
assertStat(t, stat, err, "hi.txt", 0)
stat, err = filesystem.Lstat("/tmp/links/broken")
assertStat(t, stat, err, "broken", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/broken")
assertErr(t, err, "stat /tmp/links/nothingHere: file does not exist")
stat, err = filesystem.Lstat("/tmp/links/recursive")
assertStat(t, stat, err, "recursive", os.ModeSymlink)
stat, err = filesystem.Stat("/tmp/links/recursive")
assertErr(t, err, "read /tmp/links/recursive: too many levels of symbolic links")
}