123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983 |
- // Copyright 2017 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 (
- "bytes"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "os/user"
- "path/filepath"
- "sync"
- "time"
- )
- var OsFs FileSystem = osFs{}
- func NewMockFs(files map[string][]byte) *MockFs {
- workDir := "/cwd"
- fs := &MockFs{
- Clock: NewClock(time.Unix(2, 2)),
- workDir: workDir,
- }
- fs.root = *fs.newDir()
- fs.MkDirs(workDir)
- for path, bytes := range files {
- dir := filepath.Dir(path)
- fs.MkDirs(dir)
- fs.WriteFile(path, bytes, 0777)
- }
- return fs
- }
- type FileSystem interface {
- // getting information about files
- Open(name string) (file io.ReadCloser, 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)
- InodeNumber(info os.FileInfo) (number uint64, err error)
- DeviceNumber(info os.FileInfo) (number uint64, err error)
- PermTime(info os.FileInfo) (time time.Time, err error)
- // changing contents of the filesystem
- Rename(oldPath string, newPath string) (err error)
- WriteFile(path string, data []byte, perm os.FileMode) (err error)
- Remove(path string) (err error)
- RemoveAll(path string) (err error)
- // metadata about the filesystem
- 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
- }
- type dirEntryInfo struct {
- name string
- mode os.FileMode
- modeExists bool
- }
- var _ DirEntryInfo = os.FileInfo(nil)
- func (d *dirEntryInfo) Name() string { return d.name }
- func (d *dirEntryInfo) Mode() os.FileMode { return d.mode }
- func (d *dirEntryInfo) IsDir() bool { return d.mode.IsDir() }
- func (d *dirEntryInfo) String() string { return d.name + ": " + d.mode.String() }
- // 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) Stat(path string) (stats os.FileInfo, err error) {
- return os.Stat(path)
- }
- func (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
- entries, err := 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)
- }
- func (osFs) WriteFile(path string, data []byte, perm os.FileMode) error {
- return ioutil.WriteFile(path, data, perm)
- }
- func (osFs) Remove(path string) error {
- return os.Remove(path)
- }
- func (osFs) RemoveAll(path string) error {
- return os.RemoveAll(path)
- }
- func (osFs) ViewId() (id string) {
- user, err := user.Current()
- if err != nil {
- return ""
- }
- username := user.Username
- hostname, err := os.Hostname()
- if err != nil {
- return ""
- }
- return username + "@" + hostname
- }
- type Clock struct {
- time time.Time
- }
- func NewClock(startTime time.Time) *Clock {
- return &Clock{time: startTime}
- }
- func (c *Clock) Tick() {
- c.time = c.time.Add(time.Microsecond)
- }
- func (c *Clock) Time() time.Time {
- return c.time
- }
- // given "/a/b/c/d", pathSplit returns ("/a/b/c", "d")
- func pathSplit(path string) (dir string, leaf string) {
- dir, leaf = filepath.Split(path)
- if dir != "/" && len(dir) > 0 {
- dir = dir[:len(dir)-1]
- }
- return dir, leaf
- }
- // MockFs supports singlethreaded writes and multithreaded reads
- type MockFs struct {
- // configuration
- viewId string //
- deviceNumber uint64
- // implementation
- root mockDir
- Clock *Clock
- workDir string
- nextInodeNumber uint64
- // history of requests, for tests to check
- StatCalls []string
- ReadDirCalls []string
- aggregatesLock sync.Mutex
- }
- var _ FileSystem = (*MockFs)(nil)
- type mockInode struct {
- modTime time.Time
- permTime time.Time
- sys interface{}
- inodeNumber uint64
- readErr error
- }
- func (m mockInode) ModTime() time.Time {
- return m.modTime
- }
- func (m mockInode) Sys() interface{} {
- return m.sys
- }
- type mockFile struct {
- bytes []byte
- mockInode
- }
- type mockLink struct {
- target string
- mockInode
- }
- type mockDir struct {
- mockInode
- subdirs map[string]*mockDir
- files map[string]*mockFile
- symlinks map[string]*mockLink
- }
- func (m *MockFs) resolve(path string, followLastLink bool) (result string, err error) {
- if !filepath.IsAbs(path) {
- path = filepath.Join(m.workDir, path)
- }
- path = filepath.Clean(path)
- return m.followLinks(path, followLastLink, 10)
- }
- // note that followLinks can return a file path that doesn't exist
- func (m *MockFs) followLinks(path string, followLastLink bool, count int) (canonicalPath string, err error) {
- if path == "/" {
- return path, nil
- }
- parentPath, leaf := pathSplit(path)
- if parentPath == path {
- err = fmt.Errorf("Internal error: %v yields itself as a parent", path)
- panic(err.Error())
- }
- parentPath, err = m.followLinks(parentPath, true, count)
- if err != nil {
- return "", err
- }
- parentNode, err := m.getDir(parentPath, false)
- if err != nil {
- return "", err
- }
- if parentNode.readErr != nil {
- return "", &os.PathError{
- Op: "read",
- Path: path,
- Err: parentNode.readErr,
- }
- }
- link, isLink := parentNode.symlinks[leaf]
- if isLink && followLastLink {
- if count <= 0 {
- // probably a loop
- return "", &os.PathError{
- Op: "read",
- Path: path,
- Err: fmt.Errorf("too many levels of symbolic links"),
- }
- }
- if link.readErr != nil {
- return "", &os.PathError{
- Op: "read",
- Path: path,
- Err: link.readErr,
- }
- }
- target := m.followLink(link, parentPath)
- return m.followLinks(target, followLastLink, count-1)
- }
- return path, nil
- }
- func (m *MockFs) followLink(link *mockLink, parentPath string) (result string) {
- return filepath.Clean(filepath.Join(parentPath, link.target))
- }
- func (m *MockFs) getFile(parentDir *mockDir, fileName string) (file *mockFile, err error) {
- file, isFile := parentDir.files[fileName]
- if !isFile {
- _, isDir := parentDir.subdirs[fileName]
- _, isLink := parentDir.symlinks[fileName]
- if isDir || isLink {
- return nil, &os.PathError{
- Op: "open",
- Path: fileName,
- Err: os.ErrInvalid,
- }
- }
- return nil, &os.PathError{
- Op: "open",
- Path: fileName,
- Err: os.ErrNotExist,
- }
- }
- if file.readErr != nil {
- return nil, &os.PathError{
- Op: "open",
- Path: fileName,
- Err: file.readErr,
- }
- }
- return file, nil
- }
- func (m *MockFs) getInode(parentDir *mockDir, name string) (inode *mockInode, err error) {
- file, isFile := parentDir.files[name]
- if isFile {
- return &file.mockInode, nil
- }
- link, isLink := parentDir.symlinks[name]
- if isLink {
- return &link.mockInode, nil
- }
- dir, isDir := parentDir.subdirs[name]
- if isDir {
- return &dir.mockInode, nil
- }
- return nil, &os.PathError{
- Op: "stat",
- Path: name,
- Err: os.ErrNotExist,
- }
- }
- func (m *MockFs) Open(path string) (io.ReadCloser, error) {
- path, err := m.resolve(path, true)
- if err != nil {
- return nil, err
- }
- if err != nil {
- return nil, err
- }
- parentPath, base := pathSplit(path)
- parentDir, err := m.getDir(parentPath, false)
- if err != nil {
- return nil, err
- }
- file, err := m.getFile(parentDir, base)
- if err != nil {
- return nil, err
- }
- return struct {
- io.Closer
- *bytes.Reader
- }{
- ioutil.NopCloser(nil),
- bytes.NewReader(file.bytes),
- }, nil
- }
- // a mockFileInfo is for exporting file stats in a way that satisfies the FileInfo interface
- type mockFileInfo struct {
- path string
- size int64
- modTime time.Time // time at which the inode's contents were modified
- permTime time.Time // time at which the inode's permissions were modified
- mode os.FileMode
- inodeNumber uint64
- deviceNumber uint64
- }
- func (m *mockFileInfo) Name() string {
- return m.path
- }
- func (m *mockFileInfo) Size() int64 {
- return m.size
- }
- func (m *mockFileInfo) Mode() os.FileMode {
- return m.mode
- }
- func (m *mockFileInfo) ModTime() time.Time {
- return m.modTime
- }
- func (m *mockFileInfo) IsDir() bool {
- return m.mode&os.ModeDir != 0
- }
- func (m *mockFileInfo) Sys() interface{} {
- return nil
- }
- func (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
- return &mockFileInfo{
- path: filepath.Base(path),
- size: 1,
- modTime: d.modTime,
- permTime: d.permTime,
- mode: os.ModeDir,
- inodeNumber: d.inodeNumber,
- deviceNumber: m.deviceNumber,
- }
- }
- func (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
- return &mockFileInfo{
- path: filepath.Base(path),
- size: 1,
- modTime: f.modTime,
- permTime: f.permTime,
- mode: 0,
- inodeNumber: f.inodeNumber,
- deviceNumber: m.deviceNumber,
- }
- }
- func (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) {
- return &mockFileInfo{
- path: filepath.Base(path),
- size: 1,
- modTime: l.modTime,
- permTime: l.permTime,
- mode: os.ModeSymlink,
- inodeNumber: l.inodeNumber,
- deviceNumber: m.deviceNumber,
- }
- }
- func (m *MockFs) Lstat(path string) (stats os.FileInfo, err error) {
- // update aggregates
- m.aggregatesLock.Lock()
- m.StatCalls = append(m.StatCalls, path)
- m.aggregatesLock.Unlock()
- // resolve symlinks
- path, err = m.resolve(path, false)
- if err != nil {
- return nil, err
- }
- // special case for root dir
- if path == "/" {
- return m.dirToFileInfo(&m.root, "/"), nil
- }
- // determine type and handle appropriately
- parentPath, baseName := pathSplit(path)
- dir, err := m.getDir(parentPath, false)
- if err != nil {
- return nil, err
- }
- subdir, subdirExists := dir.subdirs[baseName]
- if subdirExists {
- return m.dirToFileInfo(subdir, path), nil
- }
- file, fileExists := dir.files[baseName]
- if fileExists {
- return m.fileToFileInfo(file, path), nil
- }
- link, linkExists := dir.symlinks[baseName]
- if linkExists {
- return m.linkToFileInfo(link, path), nil
- }
- // not found
- return nil, &os.PathError{
- Op: "stat",
- Path: path,
- Err: os.ErrNotExist,
- }
- }
- 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) {
- mockInfo, ok := info.(*mockFileInfo)
- if ok {
- return mockInfo.inodeNumber, nil
- }
- return 0, fmt.Errorf("%v is not a mockFileInfo", info)
- }
- func (m *MockFs) DeviceNumber(info os.FileInfo) (number uint64, err error) {
- mockInfo, ok := info.(*mockFileInfo)
- if ok {
- return mockInfo.deviceNumber, nil
- }
- return 0, fmt.Errorf("%v is not a mockFileInfo", info)
- }
- func (m *MockFs) PermTime(info os.FileInfo) (when time.Time, err error) {
- mockInfo, ok := info.(*mockFileInfo)
- if ok {
- return mockInfo.permTime, nil
- }
- return time.Date(0, 0, 0, 0, 0, 0, 0, nil),
- fmt.Errorf("%v is not a mockFileInfo", info)
- }
- func (m *MockFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
- // update aggregates
- m.aggregatesLock.Lock()
- m.ReadDirCalls = append(m.ReadDirCalls, path)
- m.aggregatesLock.Unlock()
- // locate directory
- path, err = m.resolve(path, true)
- if err != nil {
- return nil, err
- }
- results := []DirEntryInfo{}
- dir, err := m.getDir(path, false)
- if err != nil {
- return nil, err
- }
- if dir.readErr != nil {
- return nil, &os.PathError{
- Op: "read",
- Path: path,
- Err: dir.readErr,
- }
- }
- // describe its contents
- for name, subdir := range dir.subdirs {
- dirInfo := m.dirToFileInfo(subdir, name)
- results = append(results, dirInfo)
- }
- for name, file := range dir.files {
- info := m.fileToFileInfo(file, name)
- results = append(results, info)
- }
- for name, link := range dir.symlinks {
- info := m.linkToFileInfo(link, name)
- results = append(results, info)
- }
- return results, nil
- }
- func (m *MockFs) Rename(sourcePath string, destPath string) error {
- // validate source parent exists
- sourcePath, err := m.resolve(sourcePath, false)
- if err != nil {
- return err
- }
- sourceParentPath := filepath.Dir(sourcePath)
- sourceParentDir, err := m.getDir(sourceParentPath, false)
- if err != nil {
- return err
- }
- if sourceParentDir == nil {
- return &os.PathError{
- Op: "move",
- Path: sourcePath,
- Err: os.ErrNotExist,
- }
- }
- if sourceParentDir.readErr != nil {
- return &os.PathError{
- Op: "move",
- Path: sourcePath,
- Err: sourceParentDir.readErr,
- }
- }
- // validate dest parent exists
- destPath, err = m.resolve(destPath, false)
- destParentPath := filepath.Dir(destPath)
- destParentDir, err := m.getDir(destParentPath, false)
- if err != nil {
- return err
- }
- if destParentDir == nil {
- return &os.PathError{
- Op: "move",
- Path: destParentPath,
- Err: os.ErrNotExist,
- }
- }
- if destParentDir.readErr != nil {
- return &os.PathError{
- Op: "move",
- Path: destParentPath,
- Err: destParentDir.readErr,
- }
- }
- // check the source and dest themselves
- sourceBase := filepath.Base(sourcePath)
- destBase := filepath.Base(destPath)
- file, sourceIsFile := sourceParentDir.files[sourceBase]
- dir, sourceIsDir := sourceParentDir.subdirs[sourceBase]
- link, sourceIsLink := sourceParentDir.symlinks[sourceBase]
- // validate that the source exists
- if !sourceIsFile && !sourceIsDir && !sourceIsLink {
- return &os.PathError{
- Op: "move",
- Path: sourcePath,
- Err: os.ErrNotExist,
- }
- }
- // validate the destination doesn't already exist as an incompatible type
- _, destWasFile := destParentDir.files[destBase]
- _, destWasDir := destParentDir.subdirs[destBase]
- _, destWasLink := destParentDir.symlinks[destBase]
- if destWasDir {
- return &os.PathError{
- Op: "move",
- Path: destPath,
- Err: errors.New("destination exists as a directory"),
- }
- }
- if sourceIsDir && (destWasFile || destWasLink) {
- return &os.PathError{
- Op: "move",
- Path: destPath,
- Err: errors.New("destination exists as a file"),
- }
- }
- if destWasFile {
- delete(destParentDir.files, destBase)
- }
- if destWasDir {
- delete(destParentDir.subdirs, destBase)
- }
- if destWasLink {
- delete(destParentDir.symlinks, destBase)
- }
- if sourceIsFile {
- destParentDir.files[destBase] = file
- delete(sourceParentDir.files, sourceBase)
- }
- if sourceIsDir {
- destParentDir.subdirs[destBase] = dir
- delete(sourceParentDir.subdirs, sourceBase)
- }
- if sourceIsLink {
- destParentDir.symlinks[destBase] = link
- delete(destParentDir.symlinks, sourceBase)
- }
- destParentDir.modTime = m.Clock.Time()
- sourceParentDir.modTime = m.Clock.Time()
- return nil
- }
- func (m *MockFs) newInodeNumber() uint64 {
- result := m.nextInodeNumber
- m.nextInodeNumber++
- return result
- }
- func (m *MockFs) WriteFile(filePath string, data []byte, perm os.FileMode) error {
- filePath, err := m.resolve(filePath, true)
- if err != nil {
- return err
- }
- parentPath := filepath.Dir(filePath)
- parentDir, err := m.getDir(parentPath, false)
- if err != nil || parentDir == nil {
- return &os.PathError{
- Op: "write",
- Path: parentPath,
- Err: os.ErrNotExist,
- }
- }
- if parentDir.readErr != nil {
- return &os.PathError{
- Op: "write",
- Path: parentPath,
- Err: parentDir.readErr,
- }
- }
- baseName := filepath.Base(filePath)
- _, exists := parentDir.files[baseName]
- if !exists {
- parentDir.modTime = m.Clock.Time()
- parentDir.files[baseName] = m.newFile()
- } else {
- readErr := parentDir.files[baseName].readErr
- if readErr != nil {
- return &os.PathError{
- Op: "write",
- Path: filePath,
- Err: readErr,
- }
- }
- }
- file := parentDir.files[baseName]
- file.bytes = data
- file.modTime = m.Clock.Time()
- return nil
- }
- func (m *MockFs) newFile() *mockFile {
- newFile := &mockFile{}
- newFile.inodeNumber = m.newInodeNumber()
- newFile.modTime = m.Clock.Time()
- newFile.permTime = newFile.modTime
- return newFile
- }
- func (m *MockFs) newDir() *mockDir {
- newDir := &mockDir{
- subdirs: make(map[string]*mockDir, 0),
- files: make(map[string]*mockFile, 0),
- symlinks: make(map[string]*mockLink, 0),
- }
- newDir.inodeNumber = m.newInodeNumber()
- newDir.modTime = m.Clock.Time()
- newDir.permTime = newDir.modTime
- return newDir
- }
- func (m *MockFs) newLink(target string) *mockLink {
- newLink := &mockLink{
- target: target,
- }
- newLink.inodeNumber = m.newInodeNumber()
- newLink.modTime = m.Clock.Time()
- newLink.permTime = newLink.modTime
- return newLink
- }
- func (m *MockFs) MkDirs(path string) error {
- _, err := m.getDir(path, true)
- return err
- }
- // getDir doesn't support symlinks
- func (m *MockFs) getDir(path string, createIfMissing bool) (dir *mockDir, err error) {
- cleanedPath := filepath.Clean(path)
- if cleanedPath == "/" {
- return &m.root, nil
- }
- parentPath, leaf := pathSplit(cleanedPath)
- if len(parentPath) >= len(path) {
- return &m.root, nil
- }
- parent, err := m.getDir(parentPath, createIfMissing)
- if err != nil {
- return nil, err
- }
- if parent.readErr != nil {
- return nil, &os.PathError{
- Op: "stat",
- Path: path,
- Err: parent.readErr,
- }
- }
- childDir, dirExists := parent.subdirs[leaf]
- if !dirExists {
- if createIfMissing {
- // confirm that a file with the same name doesn't already exist
- _, fileExists := parent.files[leaf]
- if fileExists {
- return nil, &os.PathError{
- Op: "mkdir",
- Path: path,
- Err: os.ErrExist,
- }
- }
- // create this directory
- childDir = m.newDir()
- parent.subdirs[leaf] = childDir
- parent.modTime = m.Clock.Time()
- } else {
- return nil, &os.PathError{
- Op: "stat",
- Path: path,
- Err: os.ErrNotExist,
- }
- }
- }
- return childDir, nil
- }
- func (m *MockFs) Remove(path string) (err error) {
- path, err = m.resolve(path, false)
- parentPath, leaf := pathSplit(path)
- if len(leaf) == 0 {
- return fmt.Errorf("Cannot remove %v\n", path)
- }
- parentDir, err := m.getDir(parentPath, false)
- if err != nil {
- return err
- }
- if parentDir == nil {
- return &os.PathError{
- Op: "remove",
- Path: path,
- Err: os.ErrNotExist,
- }
- }
- if parentDir.readErr != nil {
- return &os.PathError{
- Op: "remove",
- Path: path,
- Err: parentDir.readErr,
- }
- }
- _, isDir := parentDir.subdirs[leaf]
- if isDir {
- return &os.PathError{
- Op: "remove",
- Path: path,
- Err: os.ErrInvalid,
- }
- }
- _, isLink := parentDir.symlinks[leaf]
- if isLink {
- delete(parentDir.symlinks, leaf)
- } else {
- _, isFile := parentDir.files[leaf]
- if !isFile {
- return &os.PathError{
- Op: "remove",
- Path: path,
- Err: os.ErrNotExist,
- }
- }
- delete(parentDir.files, leaf)
- }
- parentDir.modTime = m.Clock.Time()
- return nil
- }
- func (m *MockFs) Symlink(oldPath string, newPath string) (err error) {
- newPath, err = m.resolve(newPath, false)
- if err != nil {
- return err
- }
- newParentPath, leaf := pathSplit(newPath)
- newParentDir, err := m.getDir(newParentPath, false)
- if newParentDir.readErr != nil {
- return &os.PathError{
- Op: "link",
- Path: newPath,
- Err: newParentDir.readErr,
- }
- }
- if err != nil {
- return err
- }
- newParentDir.symlinks[leaf] = m.newLink(oldPath)
- return nil
- }
- func (m *MockFs) RemoveAll(path string) (err error) {
- path, err = m.resolve(path, false)
- if err != nil {
- return err
- }
- parentPath, leaf := pathSplit(path)
- if len(leaf) == 0 {
- return fmt.Errorf("Cannot remove %v\n", path)
- }
- parentDir, err := m.getDir(parentPath, false)
- if err != nil {
- return err
- }
- if parentDir == nil {
- return &os.PathError{
- Op: "removeAll",
- Path: path,
- Err: os.ErrNotExist,
- }
- }
- if parentDir.readErr != nil {
- return &os.PathError{
- Op: "removeAll",
- Path: path,
- Err: parentDir.readErr,
- }
- }
- _, isFile := parentDir.files[leaf]
- _, isLink := parentDir.symlinks[leaf]
- if isFile || isLink {
- return m.Remove(path)
- }
- _, isDir := parentDir.subdirs[leaf]
- if !isDir {
- if !isDir {
- return &os.PathError{
- Op: "removeAll",
- Path: path,
- Err: os.ErrNotExist,
- }
- }
- }
- delete(parentDir.subdirs, leaf)
- parentDir.modTime = m.Clock.Time()
- return nil
- }
- func (m *MockFs) SetReadable(path string, readable bool) error {
- var readErr error
- if !readable {
- readErr = os.ErrPermission
- }
- return m.SetReadErr(path, readErr)
- }
- func (m *MockFs) SetReadErr(path string, readErr error) error {
- path, err := m.resolve(path, false)
- if err != nil {
- return err
- }
- parentPath, leaf := filepath.Split(path)
- parentDir, err := m.getDir(parentPath, false)
- if err != nil {
- return err
- }
- if parentDir.readErr != nil {
- return &os.PathError{
- Op: "chmod",
- Path: parentPath,
- Err: parentDir.readErr,
- }
- }
- inode, err := m.getInode(parentDir, leaf)
- if err != nil {
- return err
- }
- inode.readErr = readErr
- inode.permTime = m.Clock.Time()
- return nil
- }
- func (m *MockFs) ClearMetrics() {
- m.ReadDirCalls = []string{}
- m.StatCalls = []string{}
- }
- func (m *MockFs) ViewId() (id string) {
- return m.viewId
- }
- func (m *MockFs) SetViewId(id string) {
- m.viewId = id
- }
- func (m *MockFs) SetDeviceNumber(deviceNumber uint64) {
- m.deviceNumber = deviceNumber
- }
|