xref: /aosp_15_r20/build/soong/finder/fs/fs.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Workerpackage fs
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"bytes"
19*333d2b36SAndroid Build Coastguard Worker	"errors"
20*333d2b36SAndroid Build Coastguard Worker	"fmt"
21*333d2b36SAndroid Build Coastguard Worker	"io"
22*333d2b36SAndroid Build Coastguard Worker	"io/ioutil"
23*333d2b36SAndroid Build Coastguard Worker	"os"
24*333d2b36SAndroid Build Coastguard Worker	"os/user"
25*333d2b36SAndroid Build Coastguard Worker	"path/filepath"
26*333d2b36SAndroid Build Coastguard Worker	"sync"
27*333d2b36SAndroid Build Coastguard Worker	"time"
28*333d2b36SAndroid Build Coastguard Worker)
29*333d2b36SAndroid Build Coastguard Worker
30*333d2b36SAndroid Build Coastguard Workervar OsFs FileSystem = osFs{}
31*333d2b36SAndroid Build Coastguard Worker
32*333d2b36SAndroid Build Coastguard Workerfunc NewMockFs(files map[string][]byte) *MockFs {
33*333d2b36SAndroid Build Coastguard Worker	workDir := "/cwd"
34*333d2b36SAndroid Build Coastguard Worker	fs := &MockFs{
35*333d2b36SAndroid Build Coastguard Worker		Clock:   NewClock(time.Unix(2, 2)),
36*333d2b36SAndroid Build Coastguard Worker		workDir: workDir,
37*333d2b36SAndroid Build Coastguard Worker	}
38*333d2b36SAndroid Build Coastguard Worker	fs.root = *fs.newDir()
39*333d2b36SAndroid Build Coastguard Worker	fs.MkDirs(workDir)
40*333d2b36SAndroid Build Coastguard Worker
41*333d2b36SAndroid Build Coastguard Worker	for path, bytes := range files {
42*333d2b36SAndroid Build Coastguard Worker		dir := filepath.Dir(path)
43*333d2b36SAndroid Build Coastguard Worker		fs.MkDirs(dir)
44*333d2b36SAndroid Build Coastguard Worker		fs.WriteFile(path, bytes, 0777)
45*333d2b36SAndroid Build Coastguard Worker	}
46*333d2b36SAndroid Build Coastguard Worker
47*333d2b36SAndroid Build Coastguard Worker	return fs
48*333d2b36SAndroid Build Coastguard Worker}
49*333d2b36SAndroid Build Coastguard Worker
50*333d2b36SAndroid Build Coastguard Workertype FileSystem interface {
51*333d2b36SAndroid Build Coastguard Worker	// getting information about files
52*333d2b36SAndroid Build Coastguard Worker	Open(name string) (file io.ReadCloser, err error)
53*333d2b36SAndroid Build Coastguard Worker	Lstat(path string) (stats os.FileInfo, err error)
54*333d2b36SAndroid Build Coastguard Worker	Stat(path string) (stats os.FileInfo, err error)
55*333d2b36SAndroid Build Coastguard Worker	ReadDir(path string) (contents []DirEntryInfo, err error)
56*333d2b36SAndroid Build Coastguard Worker
57*333d2b36SAndroid Build Coastguard Worker	InodeNumber(info os.FileInfo) (number uint64, err error)
58*333d2b36SAndroid Build Coastguard Worker	DeviceNumber(info os.FileInfo) (number uint64, err error)
59*333d2b36SAndroid Build Coastguard Worker	PermTime(info os.FileInfo) (time time.Time, err error)
60*333d2b36SAndroid Build Coastguard Worker
61*333d2b36SAndroid Build Coastguard Worker	// changing contents of the filesystem
62*333d2b36SAndroid Build Coastguard Worker	Rename(oldPath string, newPath string) (err error)
63*333d2b36SAndroid Build Coastguard Worker	WriteFile(path string, data []byte, perm os.FileMode) (err error)
64*333d2b36SAndroid Build Coastguard Worker	Remove(path string) (err error)
65*333d2b36SAndroid Build Coastguard Worker	RemoveAll(path string) (err error)
66*333d2b36SAndroid Build Coastguard Worker
67*333d2b36SAndroid Build Coastguard Worker	// metadata about the filesystem
68*333d2b36SAndroid Build Coastguard Worker	ViewId() (id string) // Some unique id of the user accessing the filesystem
69*333d2b36SAndroid Build Coastguard Worker}
70*333d2b36SAndroid Build Coastguard Worker
71*333d2b36SAndroid Build Coastguard Worker// DentryInfo is a subset of the functionality available through os.FileInfo that might be able
72*333d2b36SAndroid Build Coastguard Worker// to be gleaned through only a syscall.Getdents without requiring a syscall.Lstat of every file.
73*333d2b36SAndroid Build Coastguard Workertype DirEntryInfo interface {
74*333d2b36SAndroid Build Coastguard Worker	Name() string
75*333d2b36SAndroid Build Coastguard Worker	Mode() os.FileMode // the file type encoded as an os.FileMode
76*333d2b36SAndroid Build Coastguard Worker	IsDir() bool
77*333d2b36SAndroid Build Coastguard Worker}
78*333d2b36SAndroid Build Coastguard Worker
79*333d2b36SAndroid Build Coastguard Workertype dirEntryInfo struct {
80*333d2b36SAndroid Build Coastguard Worker	name       string
81*333d2b36SAndroid Build Coastguard Worker	mode       os.FileMode
82*333d2b36SAndroid Build Coastguard Worker	modeExists bool
83*333d2b36SAndroid Build Coastguard Worker}
84*333d2b36SAndroid Build Coastguard Worker
85*333d2b36SAndroid Build Coastguard Workervar _ DirEntryInfo = os.FileInfo(nil)
86*333d2b36SAndroid Build Coastguard Worker
87*333d2b36SAndroid Build Coastguard Workerfunc (d *dirEntryInfo) Name() string      { return d.name }
88*333d2b36SAndroid Build Coastguard Workerfunc (d *dirEntryInfo) Mode() os.FileMode { return d.mode }
89*333d2b36SAndroid Build Coastguard Workerfunc (d *dirEntryInfo) IsDir() bool       { return d.mode.IsDir() }
90*333d2b36SAndroid Build Coastguard Workerfunc (d *dirEntryInfo) String() string    { return d.name + ": " + d.mode.String() }
91*333d2b36SAndroid Build Coastguard Worker
92*333d2b36SAndroid Build Coastguard Worker// osFs implements FileSystem using the local disk.
93*333d2b36SAndroid Build Coastguard Workertype osFs struct{}
94*333d2b36SAndroid Build Coastguard Worker
95*333d2b36SAndroid Build Coastguard Workervar _ FileSystem = (*osFs)(nil)
96*333d2b36SAndroid Build Coastguard Worker
97*333d2b36SAndroid Build Coastguard Workerfunc (osFs) Open(name string) (io.ReadCloser, error) { return os.Open(name) }
98*333d2b36SAndroid Build Coastguard Worker
99*333d2b36SAndroid Build Coastguard Workerfunc (osFs) Lstat(path string) (stats os.FileInfo, err error) {
100*333d2b36SAndroid Build Coastguard Worker	return os.Lstat(path)
101*333d2b36SAndroid Build Coastguard Worker}
102*333d2b36SAndroid Build Coastguard Worker
103*333d2b36SAndroid Build Coastguard Workerfunc (osFs) Stat(path string) (stats os.FileInfo, err error) {
104*333d2b36SAndroid Build Coastguard Worker	return os.Stat(path)
105*333d2b36SAndroid Build Coastguard Worker}
106*333d2b36SAndroid Build Coastguard Worker
107*333d2b36SAndroid Build Coastguard Workerfunc (osFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
108*333d2b36SAndroid Build Coastguard Worker	entries, err := readdir(path)
109*333d2b36SAndroid Build Coastguard Worker	if err != nil {
110*333d2b36SAndroid Build Coastguard Worker		return nil, err
111*333d2b36SAndroid Build Coastguard Worker	}
112*333d2b36SAndroid Build Coastguard Worker	for _, entry := range entries {
113*333d2b36SAndroid Build Coastguard Worker		contents = append(contents, entry)
114*333d2b36SAndroid Build Coastguard Worker	}
115*333d2b36SAndroid Build Coastguard Worker
116*333d2b36SAndroid Build Coastguard Worker	return contents, nil
117*333d2b36SAndroid Build Coastguard Worker}
118*333d2b36SAndroid Build Coastguard Worker
119*333d2b36SAndroid Build Coastguard Workerfunc (osFs) Rename(oldPath string, newPath string) error {
120*333d2b36SAndroid Build Coastguard Worker	return os.Rename(oldPath, newPath)
121*333d2b36SAndroid Build Coastguard Worker}
122*333d2b36SAndroid Build Coastguard Worker
123*333d2b36SAndroid Build Coastguard Workerfunc (osFs) WriteFile(path string, data []byte, perm os.FileMode) error {
124*333d2b36SAndroid Build Coastguard Worker	return ioutil.WriteFile(path, data, perm)
125*333d2b36SAndroid Build Coastguard Worker}
126*333d2b36SAndroid Build Coastguard Worker
127*333d2b36SAndroid Build Coastguard Workerfunc (osFs) Remove(path string) error {
128*333d2b36SAndroid Build Coastguard Worker	return os.Remove(path)
129*333d2b36SAndroid Build Coastguard Worker}
130*333d2b36SAndroid Build Coastguard Worker
131*333d2b36SAndroid Build Coastguard Workerfunc (osFs) RemoveAll(path string) error {
132*333d2b36SAndroid Build Coastguard Worker	return os.RemoveAll(path)
133*333d2b36SAndroid Build Coastguard Worker}
134*333d2b36SAndroid Build Coastguard Worker
135*333d2b36SAndroid Build Coastguard Workerfunc (osFs) ViewId() (id string) {
136*333d2b36SAndroid Build Coastguard Worker	user, err := user.Current()
137*333d2b36SAndroid Build Coastguard Worker	if err != nil {
138*333d2b36SAndroid Build Coastguard Worker		return ""
139*333d2b36SAndroid Build Coastguard Worker	}
140*333d2b36SAndroid Build Coastguard Worker	username := user.Username
141*333d2b36SAndroid Build Coastguard Worker
142*333d2b36SAndroid Build Coastguard Worker	hostname, err := os.Hostname()
143*333d2b36SAndroid Build Coastguard Worker	if err != nil {
144*333d2b36SAndroid Build Coastguard Worker		return ""
145*333d2b36SAndroid Build Coastguard Worker	}
146*333d2b36SAndroid Build Coastguard Worker
147*333d2b36SAndroid Build Coastguard Worker	return username + "@" + hostname
148*333d2b36SAndroid Build Coastguard Worker}
149*333d2b36SAndroid Build Coastguard Worker
150*333d2b36SAndroid Build Coastguard Workertype Clock struct {
151*333d2b36SAndroid Build Coastguard Worker	time time.Time
152*333d2b36SAndroid Build Coastguard Worker}
153*333d2b36SAndroid Build Coastguard Worker
154*333d2b36SAndroid Build Coastguard Workerfunc NewClock(startTime time.Time) *Clock {
155*333d2b36SAndroid Build Coastguard Worker	return &Clock{time: startTime}
156*333d2b36SAndroid Build Coastguard Worker
157*333d2b36SAndroid Build Coastguard Worker}
158*333d2b36SAndroid Build Coastguard Worker
159*333d2b36SAndroid Build Coastguard Workerfunc (c *Clock) Tick() {
160*333d2b36SAndroid Build Coastguard Worker	c.time = c.time.Add(time.Microsecond)
161*333d2b36SAndroid Build Coastguard Worker}
162*333d2b36SAndroid Build Coastguard Worker
163*333d2b36SAndroid Build Coastguard Workerfunc (c *Clock) Time() time.Time {
164*333d2b36SAndroid Build Coastguard Worker	return c.time
165*333d2b36SAndroid Build Coastguard Worker}
166*333d2b36SAndroid Build Coastguard Worker
167*333d2b36SAndroid Build Coastguard Worker// given "/a/b/c/d", pathSplit returns ("/a/b/c", "d")
168*333d2b36SAndroid Build Coastguard Workerfunc pathSplit(path string) (dir string, leaf string) {
169*333d2b36SAndroid Build Coastguard Worker	dir, leaf = filepath.Split(path)
170*333d2b36SAndroid Build Coastguard Worker	if dir != "/" && len(dir) > 0 {
171*333d2b36SAndroid Build Coastguard Worker		dir = dir[:len(dir)-1]
172*333d2b36SAndroid Build Coastguard Worker	}
173*333d2b36SAndroid Build Coastguard Worker	return dir, leaf
174*333d2b36SAndroid Build Coastguard Worker}
175*333d2b36SAndroid Build Coastguard Worker
176*333d2b36SAndroid Build Coastguard Worker// MockFs supports singlethreaded writes and multithreaded reads
177*333d2b36SAndroid Build Coastguard Workertype MockFs struct {
178*333d2b36SAndroid Build Coastguard Worker	// configuration
179*333d2b36SAndroid Build Coastguard Worker	viewId       string //
180*333d2b36SAndroid Build Coastguard Worker	deviceNumber uint64
181*333d2b36SAndroid Build Coastguard Worker
182*333d2b36SAndroid Build Coastguard Worker	// implementation
183*333d2b36SAndroid Build Coastguard Worker	root            mockDir
184*333d2b36SAndroid Build Coastguard Worker	Clock           *Clock
185*333d2b36SAndroid Build Coastguard Worker	workDir         string
186*333d2b36SAndroid Build Coastguard Worker	nextInodeNumber uint64
187*333d2b36SAndroid Build Coastguard Worker
188*333d2b36SAndroid Build Coastguard Worker	// history of requests, for tests to check
189*333d2b36SAndroid Build Coastguard Worker	StatCalls      []string
190*333d2b36SAndroid Build Coastguard Worker	ReadDirCalls   []string
191*333d2b36SAndroid Build Coastguard Worker	aggregatesLock sync.Mutex
192*333d2b36SAndroid Build Coastguard Worker}
193*333d2b36SAndroid Build Coastguard Worker
194*333d2b36SAndroid Build Coastguard Workervar _ FileSystem = (*MockFs)(nil)
195*333d2b36SAndroid Build Coastguard Worker
196*333d2b36SAndroid Build Coastguard Workertype mockInode struct {
197*333d2b36SAndroid Build Coastguard Worker	modTime     time.Time
198*333d2b36SAndroid Build Coastguard Worker	permTime    time.Time
199*333d2b36SAndroid Build Coastguard Worker	sys         interface{}
200*333d2b36SAndroid Build Coastguard Worker	inodeNumber uint64
201*333d2b36SAndroid Build Coastguard Worker	readErr     error
202*333d2b36SAndroid Build Coastguard Worker}
203*333d2b36SAndroid Build Coastguard Worker
204*333d2b36SAndroid Build Coastguard Workerfunc (m mockInode) ModTime() time.Time {
205*333d2b36SAndroid Build Coastguard Worker	return m.modTime
206*333d2b36SAndroid Build Coastguard Worker}
207*333d2b36SAndroid Build Coastguard Worker
208*333d2b36SAndroid Build Coastguard Workerfunc (m mockInode) Sys() interface{} {
209*333d2b36SAndroid Build Coastguard Worker	return m.sys
210*333d2b36SAndroid Build Coastguard Worker}
211*333d2b36SAndroid Build Coastguard Worker
212*333d2b36SAndroid Build Coastguard Workertype mockFile struct {
213*333d2b36SAndroid Build Coastguard Worker	bytes []byte
214*333d2b36SAndroid Build Coastguard Worker
215*333d2b36SAndroid Build Coastguard Worker	mockInode
216*333d2b36SAndroid Build Coastguard Worker}
217*333d2b36SAndroid Build Coastguard Worker
218*333d2b36SAndroid Build Coastguard Workertype mockLink struct {
219*333d2b36SAndroid Build Coastguard Worker	target string
220*333d2b36SAndroid Build Coastguard Worker
221*333d2b36SAndroid Build Coastguard Worker	mockInode
222*333d2b36SAndroid Build Coastguard Worker}
223*333d2b36SAndroid Build Coastguard Worker
224*333d2b36SAndroid Build Coastguard Workertype mockDir struct {
225*333d2b36SAndroid Build Coastguard Worker	mockInode
226*333d2b36SAndroid Build Coastguard Worker
227*333d2b36SAndroid Build Coastguard Worker	subdirs  map[string]*mockDir
228*333d2b36SAndroid Build Coastguard Worker	files    map[string]*mockFile
229*333d2b36SAndroid Build Coastguard Worker	symlinks map[string]*mockLink
230*333d2b36SAndroid Build Coastguard Worker}
231*333d2b36SAndroid Build Coastguard Worker
232*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) resolve(path string, followLastLink bool) (result string, err error) {
233*333d2b36SAndroid Build Coastguard Worker	if !filepath.IsAbs(path) {
234*333d2b36SAndroid Build Coastguard Worker		path = filepath.Join(m.workDir, path)
235*333d2b36SAndroid Build Coastguard Worker	}
236*333d2b36SAndroid Build Coastguard Worker	path = filepath.Clean(path)
237*333d2b36SAndroid Build Coastguard Worker
238*333d2b36SAndroid Build Coastguard Worker	return m.followLinks(path, followLastLink, 10)
239*333d2b36SAndroid Build Coastguard Worker}
240*333d2b36SAndroid Build Coastguard Worker
241*333d2b36SAndroid Build Coastguard Worker// note that followLinks can return a file path that doesn't exist
242*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) followLinks(path string, followLastLink bool, count int) (canonicalPath string, err error) {
243*333d2b36SAndroid Build Coastguard Worker	if path == "/" {
244*333d2b36SAndroid Build Coastguard Worker		return path, nil
245*333d2b36SAndroid Build Coastguard Worker	}
246*333d2b36SAndroid Build Coastguard Worker
247*333d2b36SAndroid Build Coastguard Worker	parentPath, leaf := pathSplit(path)
248*333d2b36SAndroid Build Coastguard Worker	if parentPath == path {
249*333d2b36SAndroid Build Coastguard Worker		err = fmt.Errorf("Internal error: %v yields itself as a parent", path)
250*333d2b36SAndroid Build Coastguard Worker		panic(err.Error())
251*333d2b36SAndroid Build Coastguard Worker	}
252*333d2b36SAndroid Build Coastguard Worker
253*333d2b36SAndroid Build Coastguard Worker	parentPath, err = m.followLinks(parentPath, true, count)
254*333d2b36SAndroid Build Coastguard Worker	if err != nil {
255*333d2b36SAndroid Build Coastguard Worker		return "", err
256*333d2b36SAndroid Build Coastguard Worker	}
257*333d2b36SAndroid Build Coastguard Worker
258*333d2b36SAndroid Build Coastguard Worker	parentNode, err := m.getDir(parentPath, false)
259*333d2b36SAndroid Build Coastguard Worker	if err != nil {
260*333d2b36SAndroid Build Coastguard Worker		return "", err
261*333d2b36SAndroid Build Coastguard Worker	}
262*333d2b36SAndroid Build Coastguard Worker	if parentNode.readErr != nil {
263*333d2b36SAndroid Build Coastguard Worker		return "", &os.PathError{
264*333d2b36SAndroid Build Coastguard Worker			Op:   "read",
265*333d2b36SAndroid Build Coastguard Worker			Path: path,
266*333d2b36SAndroid Build Coastguard Worker			Err:  parentNode.readErr,
267*333d2b36SAndroid Build Coastguard Worker		}
268*333d2b36SAndroid Build Coastguard Worker	}
269*333d2b36SAndroid Build Coastguard Worker
270*333d2b36SAndroid Build Coastguard Worker	link, isLink := parentNode.symlinks[leaf]
271*333d2b36SAndroid Build Coastguard Worker	if isLink && followLastLink {
272*333d2b36SAndroid Build Coastguard Worker		if count <= 0 {
273*333d2b36SAndroid Build Coastguard Worker			// probably a loop
274*333d2b36SAndroid Build Coastguard Worker			return "", &os.PathError{
275*333d2b36SAndroid Build Coastguard Worker				Op:   "read",
276*333d2b36SAndroid Build Coastguard Worker				Path: path,
277*333d2b36SAndroid Build Coastguard Worker				Err:  fmt.Errorf("too many levels of symbolic links"),
278*333d2b36SAndroid Build Coastguard Worker			}
279*333d2b36SAndroid Build Coastguard Worker		}
280*333d2b36SAndroid Build Coastguard Worker
281*333d2b36SAndroid Build Coastguard Worker		if link.readErr != nil {
282*333d2b36SAndroid Build Coastguard Worker			return "", &os.PathError{
283*333d2b36SAndroid Build Coastguard Worker				Op:   "read",
284*333d2b36SAndroid Build Coastguard Worker				Path: path,
285*333d2b36SAndroid Build Coastguard Worker				Err:  link.readErr,
286*333d2b36SAndroid Build Coastguard Worker			}
287*333d2b36SAndroid Build Coastguard Worker		}
288*333d2b36SAndroid Build Coastguard Worker
289*333d2b36SAndroid Build Coastguard Worker		target := m.followLink(link, parentPath)
290*333d2b36SAndroid Build Coastguard Worker		return m.followLinks(target, followLastLink, count-1)
291*333d2b36SAndroid Build Coastguard Worker	}
292*333d2b36SAndroid Build Coastguard Worker	return path, nil
293*333d2b36SAndroid Build Coastguard Worker}
294*333d2b36SAndroid Build Coastguard Worker
295*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) followLink(link *mockLink, parentPath string) (result string) {
296*333d2b36SAndroid Build Coastguard Worker	return filepath.Clean(filepath.Join(parentPath, link.target))
297*333d2b36SAndroid Build Coastguard Worker}
298*333d2b36SAndroid Build Coastguard Worker
299*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) getFile(parentDir *mockDir, fileName string) (file *mockFile, err error) {
300*333d2b36SAndroid Build Coastguard Worker	file, isFile := parentDir.files[fileName]
301*333d2b36SAndroid Build Coastguard Worker	if !isFile {
302*333d2b36SAndroid Build Coastguard Worker		_, isDir := parentDir.subdirs[fileName]
303*333d2b36SAndroid Build Coastguard Worker		_, isLink := parentDir.symlinks[fileName]
304*333d2b36SAndroid Build Coastguard Worker		if isDir || isLink {
305*333d2b36SAndroid Build Coastguard Worker			return nil, &os.PathError{
306*333d2b36SAndroid Build Coastguard Worker				Op:   "open",
307*333d2b36SAndroid Build Coastguard Worker				Path: fileName,
308*333d2b36SAndroid Build Coastguard Worker				Err:  os.ErrInvalid,
309*333d2b36SAndroid Build Coastguard Worker			}
310*333d2b36SAndroid Build Coastguard Worker		}
311*333d2b36SAndroid Build Coastguard Worker
312*333d2b36SAndroid Build Coastguard Worker		return nil, &os.PathError{
313*333d2b36SAndroid Build Coastguard Worker			Op:   "open",
314*333d2b36SAndroid Build Coastguard Worker			Path: fileName,
315*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
316*333d2b36SAndroid Build Coastguard Worker		}
317*333d2b36SAndroid Build Coastguard Worker	}
318*333d2b36SAndroid Build Coastguard Worker	if file.readErr != nil {
319*333d2b36SAndroid Build Coastguard Worker		return nil, &os.PathError{
320*333d2b36SAndroid Build Coastguard Worker			Op:   "open",
321*333d2b36SAndroid Build Coastguard Worker			Path: fileName,
322*333d2b36SAndroid Build Coastguard Worker			Err:  file.readErr,
323*333d2b36SAndroid Build Coastguard Worker		}
324*333d2b36SAndroid Build Coastguard Worker	}
325*333d2b36SAndroid Build Coastguard Worker	return file, nil
326*333d2b36SAndroid Build Coastguard Worker}
327*333d2b36SAndroid Build Coastguard Worker
328*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) getInode(parentDir *mockDir, name string) (inode *mockInode, err error) {
329*333d2b36SAndroid Build Coastguard Worker	file, isFile := parentDir.files[name]
330*333d2b36SAndroid Build Coastguard Worker	if isFile {
331*333d2b36SAndroid Build Coastguard Worker		return &file.mockInode, nil
332*333d2b36SAndroid Build Coastguard Worker	}
333*333d2b36SAndroid Build Coastguard Worker	link, isLink := parentDir.symlinks[name]
334*333d2b36SAndroid Build Coastguard Worker	if isLink {
335*333d2b36SAndroid Build Coastguard Worker		return &link.mockInode, nil
336*333d2b36SAndroid Build Coastguard Worker	}
337*333d2b36SAndroid Build Coastguard Worker	dir, isDir := parentDir.subdirs[name]
338*333d2b36SAndroid Build Coastguard Worker	if isDir {
339*333d2b36SAndroid Build Coastguard Worker		return &dir.mockInode, nil
340*333d2b36SAndroid Build Coastguard Worker	}
341*333d2b36SAndroid Build Coastguard Worker	return nil, &os.PathError{
342*333d2b36SAndroid Build Coastguard Worker		Op:   "stat",
343*333d2b36SAndroid Build Coastguard Worker		Path: name,
344*333d2b36SAndroid Build Coastguard Worker		Err:  os.ErrNotExist,
345*333d2b36SAndroid Build Coastguard Worker	}
346*333d2b36SAndroid Build Coastguard Worker
347*333d2b36SAndroid Build Coastguard Worker}
348*333d2b36SAndroid Build Coastguard Worker
349*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) Open(path string) (io.ReadCloser, error) {
350*333d2b36SAndroid Build Coastguard Worker	path, err := m.resolve(path, true)
351*333d2b36SAndroid Build Coastguard Worker	if err != nil {
352*333d2b36SAndroid Build Coastguard Worker		return nil, err
353*333d2b36SAndroid Build Coastguard Worker	}
354*333d2b36SAndroid Build Coastguard Worker
355*333d2b36SAndroid Build Coastguard Worker	if err != nil {
356*333d2b36SAndroid Build Coastguard Worker		return nil, err
357*333d2b36SAndroid Build Coastguard Worker	}
358*333d2b36SAndroid Build Coastguard Worker
359*333d2b36SAndroid Build Coastguard Worker	parentPath, base := pathSplit(path)
360*333d2b36SAndroid Build Coastguard Worker	parentDir, err := m.getDir(parentPath, false)
361*333d2b36SAndroid Build Coastguard Worker	if err != nil {
362*333d2b36SAndroid Build Coastguard Worker		return nil, err
363*333d2b36SAndroid Build Coastguard Worker	}
364*333d2b36SAndroid Build Coastguard Worker	file, err := m.getFile(parentDir, base)
365*333d2b36SAndroid Build Coastguard Worker	if err != nil {
366*333d2b36SAndroid Build Coastguard Worker		return nil, err
367*333d2b36SAndroid Build Coastguard Worker	}
368*333d2b36SAndroid Build Coastguard Worker	return struct {
369*333d2b36SAndroid Build Coastguard Worker		io.Closer
370*333d2b36SAndroid Build Coastguard Worker		*bytes.Reader
371*333d2b36SAndroid Build Coastguard Worker	}{
372*333d2b36SAndroid Build Coastguard Worker		ioutil.NopCloser(nil),
373*333d2b36SAndroid Build Coastguard Worker		bytes.NewReader(file.bytes),
374*333d2b36SAndroid Build Coastguard Worker	}, nil
375*333d2b36SAndroid Build Coastguard Worker
376*333d2b36SAndroid Build Coastguard Worker}
377*333d2b36SAndroid Build Coastguard Worker
378*333d2b36SAndroid Build Coastguard Worker// a mockFileInfo is for exporting file stats in a way that satisfies the FileInfo interface
379*333d2b36SAndroid Build Coastguard Workertype mockFileInfo struct {
380*333d2b36SAndroid Build Coastguard Worker	path         string
381*333d2b36SAndroid Build Coastguard Worker	size         int64
382*333d2b36SAndroid Build Coastguard Worker	modTime      time.Time // time at which the inode's contents were modified
383*333d2b36SAndroid Build Coastguard Worker	permTime     time.Time // time at which the inode's permissions were modified
384*333d2b36SAndroid Build Coastguard Worker	mode         os.FileMode
385*333d2b36SAndroid Build Coastguard Worker	inodeNumber  uint64
386*333d2b36SAndroid Build Coastguard Worker	deviceNumber uint64
387*333d2b36SAndroid Build Coastguard Worker}
388*333d2b36SAndroid Build Coastguard Worker
389*333d2b36SAndroid Build Coastguard Workerfunc (m *mockFileInfo) Name() string {
390*333d2b36SAndroid Build Coastguard Worker	return m.path
391*333d2b36SAndroid Build Coastguard Worker}
392*333d2b36SAndroid Build Coastguard Worker
393*333d2b36SAndroid Build Coastguard Workerfunc (m *mockFileInfo) Size() int64 {
394*333d2b36SAndroid Build Coastguard Worker	return m.size
395*333d2b36SAndroid Build Coastguard Worker}
396*333d2b36SAndroid Build Coastguard Worker
397*333d2b36SAndroid Build Coastguard Workerfunc (m *mockFileInfo) Mode() os.FileMode {
398*333d2b36SAndroid Build Coastguard Worker	return m.mode
399*333d2b36SAndroid Build Coastguard Worker}
400*333d2b36SAndroid Build Coastguard Worker
401*333d2b36SAndroid Build Coastguard Workerfunc (m *mockFileInfo) ModTime() time.Time {
402*333d2b36SAndroid Build Coastguard Worker	return m.modTime
403*333d2b36SAndroid Build Coastguard Worker}
404*333d2b36SAndroid Build Coastguard Worker
405*333d2b36SAndroid Build Coastguard Workerfunc (m *mockFileInfo) IsDir() bool {
406*333d2b36SAndroid Build Coastguard Worker	return m.mode&os.ModeDir != 0
407*333d2b36SAndroid Build Coastguard Worker}
408*333d2b36SAndroid Build Coastguard Worker
409*333d2b36SAndroid Build Coastguard Workerfunc (m *mockFileInfo) Sys() interface{} {
410*333d2b36SAndroid Build Coastguard Worker	return nil
411*333d2b36SAndroid Build Coastguard Worker}
412*333d2b36SAndroid Build Coastguard Worker
413*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) dirToFileInfo(d *mockDir, path string) (info *mockFileInfo) {
414*333d2b36SAndroid Build Coastguard Worker	return &mockFileInfo{
415*333d2b36SAndroid Build Coastguard Worker		path:         filepath.Base(path),
416*333d2b36SAndroid Build Coastguard Worker		size:         1,
417*333d2b36SAndroid Build Coastguard Worker		modTime:      d.modTime,
418*333d2b36SAndroid Build Coastguard Worker		permTime:     d.permTime,
419*333d2b36SAndroid Build Coastguard Worker		mode:         os.ModeDir,
420*333d2b36SAndroid Build Coastguard Worker		inodeNumber:  d.inodeNumber,
421*333d2b36SAndroid Build Coastguard Worker		deviceNumber: m.deviceNumber,
422*333d2b36SAndroid Build Coastguard Worker	}
423*333d2b36SAndroid Build Coastguard Worker
424*333d2b36SAndroid Build Coastguard Worker}
425*333d2b36SAndroid Build Coastguard Worker
426*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) fileToFileInfo(f *mockFile, path string) (info *mockFileInfo) {
427*333d2b36SAndroid Build Coastguard Worker	return &mockFileInfo{
428*333d2b36SAndroid Build Coastguard Worker		path:         filepath.Base(path),
429*333d2b36SAndroid Build Coastguard Worker		size:         1,
430*333d2b36SAndroid Build Coastguard Worker		modTime:      f.modTime,
431*333d2b36SAndroid Build Coastguard Worker		permTime:     f.permTime,
432*333d2b36SAndroid Build Coastguard Worker		mode:         0,
433*333d2b36SAndroid Build Coastguard Worker		inodeNumber:  f.inodeNumber,
434*333d2b36SAndroid Build Coastguard Worker		deviceNumber: m.deviceNumber,
435*333d2b36SAndroid Build Coastguard Worker	}
436*333d2b36SAndroid Build Coastguard Worker}
437*333d2b36SAndroid Build Coastguard Worker
438*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) linkToFileInfo(l *mockLink, path string) (info *mockFileInfo) {
439*333d2b36SAndroid Build Coastguard Worker	return &mockFileInfo{
440*333d2b36SAndroid Build Coastguard Worker		path:         filepath.Base(path),
441*333d2b36SAndroid Build Coastguard Worker		size:         1,
442*333d2b36SAndroid Build Coastguard Worker		modTime:      l.modTime,
443*333d2b36SAndroid Build Coastguard Worker		permTime:     l.permTime,
444*333d2b36SAndroid Build Coastguard Worker		mode:         os.ModeSymlink,
445*333d2b36SAndroid Build Coastguard Worker		inodeNumber:  l.inodeNumber,
446*333d2b36SAndroid Build Coastguard Worker		deviceNumber: m.deviceNumber,
447*333d2b36SAndroid Build Coastguard Worker	}
448*333d2b36SAndroid Build Coastguard Worker}
449*333d2b36SAndroid Build Coastguard Worker
450*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) Lstat(path string) (stats os.FileInfo, err error) {
451*333d2b36SAndroid Build Coastguard Worker	// update aggregates
452*333d2b36SAndroid Build Coastguard Worker	m.aggregatesLock.Lock()
453*333d2b36SAndroid Build Coastguard Worker	m.StatCalls = append(m.StatCalls, path)
454*333d2b36SAndroid Build Coastguard Worker	m.aggregatesLock.Unlock()
455*333d2b36SAndroid Build Coastguard Worker
456*333d2b36SAndroid Build Coastguard Worker	// resolve symlinks
457*333d2b36SAndroid Build Coastguard Worker	path, err = m.resolve(path, false)
458*333d2b36SAndroid Build Coastguard Worker	if err != nil {
459*333d2b36SAndroid Build Coastguard Worker		return nil, err
460*333d2b36SAndroid Build Coastguard Worker	}
461*333d2b36SAndroid Build Coastguard Worker
462*333d2b36SAndroid Build Coastguard Worker	// special case for root dir
463*333d2b36SAndroid Build Coastguard Worker	if path == "/" {
464*333d2b36SAndroid Build Coastguard Worker		return m.dirToFileInfo(&m.root, "/"), nil
465*333d2b36SAndroid Build Coastguard Worker	}
466*333d2b36SAndroid Build Coastguard Worker
467*333d2b36SAndroid Build Coastguard Worker	// determine type and handle appropriately
468*333d2b36SAndroid Build Coastguard Worker	parentPath, baseName := pathSplit(path)
469*333d2b36SAndroid Build Coastguard Worker	dir, err := m.getDir(parentPath, false)
470*333d2b36SAndroid Build Coastguard Worker	if err != nil {
471*333d2b36SAndroid Build Coastguard Worker		return nil, err
472*333d2b36SAndroid Build Coastguard Worker	}
473*333d2b36SAndroid Build Coastguard Worker	subdir, subdirExists := dir.subdirs[baseName]
474*333d2b36SAndroid Build Coastguard Worker	if subdirExists {
475*333d2b36SAndroid Build Coastguard Worker		return m.dirToFileInfo(subdir, path), nil
476*333d2b36SAndroid Build Coastguard Worker	}
477*333d2b36SAndroid Build Coastguard Worker	file, fileExists := dir.files[baseName]
478*333d2b36SAndroid Build Coastguard Worker	if fileExists {
479*333d2b36SAndroid Build Coastguard Worker		return m.fileToFileInfo(file, path), nil
480*333d2b36SAndroid Build Coastguard Worker	}
481*333d2b36SAndroid Build Coastguard Worker	link, linkExists := dir.symlinks[baseName]
482*333d2b36SAndroid Build Coastguard Worker	if linkExists {
483*333d2b36SAndroid Build Coastguard Worker		return m.linkToFileInfo(link, path), nil
484*333d2b36SAndroid Build Coastguard Worker	}
485*333d2b36SAndroid Build Coastguard Worker	// not found
486*333d2b36SAndroid Build Coastguard Worker	return nil, &os.PathError{
487*333d2b36SAndroid Build Coastguard Worker		Op:   "stat",
488*333d2b36SAndroid Build Coastguard Worker		Path: path,
489*333d2b36SAndroid Build Coastguard Worker		Err:  os.ErrNotExist,
490*333d2b36SAndroid Build Coastguard Worker	}
491*333d2b36SAndroid Build Coastguard Worker}
492*333d2b36SAndroid Build Coastguard Worker
493*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) Stat(path string) (stats os.FileInfo, err error) {
494*333d2b36SAndroid Build Coastguard Worker	// resolve symlinks
495*333d2b36SAndroid Build Coastguard Worker	path, err = m.resolve(path, true)
496*333d2b36SAndroid Build Coastguard Worker	if err != nil {
497*333d2b36SAndroid Build Coastguard Worker		return nil, err
498*333d2b36SAndroid Build Coastguard Worker	}
499*333d2b36SAndroid Build Coastguard Worker
500*333d2b36SAndroid Build Coastguard Worker	return m.Lstat(path)
501*333d2b36SAndroid Build Coastguard Worker}
502*333d2b36SAndroid Build Coastguard Worker
503*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) InodeNumber(info os.FileInfo) (number uint64, err error) {
504*333d2b36SAndroid Build Coastguard Worker	mockInfo, ok := info.(*mockFileInfo)
505*333d2b36SAndroid Build Coastguard Worker	if ok {
506*333d2b36SAndroid Build Coastguard Worker		return mockInfo.inodeNumber, nil
507*333d2b36SAndroid Build Coastguard Worker	}
508*333d2b36SAndroid Build Coastguard Worker	return 0, fmt.Errorf("%v is not a mockFileInfo", info)
509*333d2b36SAndroid Build Coastguard Worker}
510*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) DeviceNumber(info os.FileInfo) (number uint64, err error) {
511*333d2b36SAndroid Build Coastguard Worker	mockInfo, ok := info.(*mockFileInfo)
512*333d2b36SAndroid Build Coastguard Worker	if ok {
513*333d2b36SAndroid Build Coastguard Worker		return mockInfo.deviceNumber, nil
514*333d2b36SAndroid Build Coastguard Worker	}
515*333d2b36SAndroid Build Coastguard Worker	return 0, fmt.Errorf("%v is not a mockFileInfo", info)
516*333d2b36SAndroid Build Coastguard Worker}
517*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) PermTime(info os.FileInfo) (when time.Time, err error) {
518*333d2b36SAndroid Build Coastguard Worker	mockInfo, ok := info.(*mockFileInfo)
519*333d2b36SAndroid Build Coastguard Worker	if ok {
520*333d2b36SAndroid Build Coastguard Worker		return mockInfo.permTime, nil
521*333d2b36SAndroid Build Coastguard Worker	}
522*333d2b36SAndroid Build Coastguard Worker	return time.Date(0, 0, 0, 0, 0, 0, 0, nil),
523*333d2b36SAndroid Build Coastguard Worker		fmt.Errorf("%v is not a mockFileInfo", info)
524*333d2b36SAndroid Build Coastguard Worker}
525*333d2b36SAndroid Build Coastguard Worker
526*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) ReadDir(path string) (contents []DirEntryInfo, err error) {
527*333d2b36SAndroid Build Coastguard Worker	// update aggregates
528*333d2b36SAndroid Build Coastguard Worker	m.aggregatesLock.Lock()
529*333d2b36SAndroid Build Coastguard Worker	m.ReadDirCalls = append(m.ReadDirCalls, path)
530*333d2b36SAndroid Build Coastguard Worker	m.aggregatesLock.Unlock()
531*333d2b36SAndroid Build Coastguard Worker
532*333d2b36SAndroid Build Coastguard Worker	// locate directory
533*333d2b36SAndroid Build Coastguard Worker	path, err = m.resolve(path, true)
534*333d2b36SAndroid Build Coastguard Worker	if err != nil {
535*333d2b36SAndroid Build Coastguard Worker		return nil, err
536*333d2b36SAndroid Build Coastguard Worker	}
537*333d2b36SAndroid Build Coastguard Worker	results := []DirEntryInfo{}
538*333d2b36SAndroid Build Coastguard Worker	dir, err := m.getDir(path, false)
539*333d2b36SAndroid Build Coastguard Worker	if err != nil {
540*333d2b36SAndroid Build Coastguard Worker		return nil, err
541*333d2b36SAndroid Build Coastguard Worker	}
542*333d2b36SAndroid Build Coastguard Worker	if dir.readErr != nil {
543*333d2b36SAndroid Build Coastguard Worker		return nil, &os.PathError{
544*333d2b36SAndroid Build Coastguard Worker			Op:   "read",
545*333d2b36SAndroid Build Coastguard Worker			Path: path,
546*333d2b36SAndroid Build Coastguard Worker			Err:  dir.readErr,
547*333d2b36SAndroid Build Coastguard Worker		}
548*333d2b36SAndroid Build Coastguard Worker	}
549*333d2b36SAndroid Build Coastguard Worker	// describe its contents
550*333d2b36SAndroid Build Coastguard Worker	for name, subdir := range dir.subdirs {
551*333d2b36SAndroid Build Coastguard Worker		dirInfo := m.dirToFileInfo(subdir, name)
552*333d2b36SAndroid Build Coastguard Worker		results = append(results, dirInfo)
553*333d2b36SAndroid Build Coastguard Worker	}
554*333d2b36SAndroid Build Coastguard Worker	for name, file := range dir.files {
555*333d2b36SAndroid Build Coastguard Worker		info := m.fileToFileInfo(file, name)
556*333d2b36SAndroid Build Coastguard Worker		results = append(results, info)
557*333d2b36SAndroid Build Coastguard Worker	}
558*333d2b36SAndroid Build Coastguard Worker	for name, link := range dir.symlinks {
559*333d2b36SAndroid Build Coastguard Worker		info := m.linkToFileInfo(link, name)
560*333d2b36SAndroid Build Coastguard Worker		results = append(results, info)
561*333d2b36SAndroid Build Coastguard Worker	}
562*333d2b36SAndroid Build Coastguard Worker	return results, nil
563*333d2b36SAndroid Build Coastguard Worker}
564*333d2b36SAndroid Build Coastguard Worker
565*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) Rename(sourcePath string, destPath string) error {
566*333d2b36SAndroid Build Coastguard Worker	// validate source parent exists
567*333d2b36SAndroid Build Coastguard Worker	sourcePath, err := m.resolve(sourcePath, false)
568*333d2b36SAndroid Build Coastguard Worker	if err != nil {
569*333d2b36SAndroid Build Coastguard Worker		return err
570*333d2b36SAndroid Build Coastguard Worker	}
571*333d2b36SAndroid Build Coastguard Worker	sourceParentPath := filepath.Dir(sourcePath)
572*333d2b36SAndroid Build Coastguard Worker	sourceParentDir, err := m.getDir(sourceParentPath, false)
573*333d2b36SAndroid Build Coastguard Worker	if err != nil {
574*333d2b36SAndroid Build Coastguard Worker		return err
575*333d2b36SAndroid Build Coastguard Worker	}
576*333d2b36SAndroid Build Coastguard Worker	if sourceParentDir == nil {
577*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
578*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
579*333d2b36SAndroid Build Coastguard Worker			Path: sourcePath,
580*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
581*333d2b36SAndroid Build Coastguard Worker		}
582*333d2b36SAndroid Build Coastguard Worker	}
583*333d2b36SAndroid Build Coastguard Worker	if sourceParentDir.readErr != nil {
584*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
585*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
586*333d2b36SAndroid Build Coastguard Worker			Path: sourcePath,
587*333d2b36SAndroid Build Coastguard Worker			Err:  sourceParentDir.readErr,
588*333d2b36SAndroid Build Coastguard Worker		}
589*333d2b36SAndroid Build Coastguard Worker	}
590*333d2b36SAndroid Build Coastguard Worker
591*333d2b36SAndroid Build Coastguard Worker	// validate dest parent exists
592*333d2b36SAndroid Build Coastguard Worker	destPath, err = m.resolve(destPath, false)
593*333d2b36SAndroid Build Coastguard Worker	destParentPath := filepath.Dir(destPath)
594*333d2b36SAndroid Build Coastguard Worker	destParentDir, err := m.getDir(destParentPath, false)
595*333d2b36SAndroid Build Coastguard Worker	if err != nil {
596*333d2b36SAndroid Build Coastguard Worker		return err
597*333d2b36SAndroid Build Coastguard Worker	}
598*333d2b36SAndroid Build Coastguard Worker	if destParentDir == nil {
599*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
600*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
601*333d2b36SAndroid Build Coastguard Worker			Path: destParentPath,
602*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
603*333d2b36SAndroid Build Coastguard Worker		}
604*333d2b36SAndroid Build Coastguard Worker	}
605*333d2b36SAndroid Build Coastguard Worker	if destParentDir.readErr != nil {
606*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
607*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
608*333d2b36SAndroid Build Coastguard Worker			Path: destParentPath,
609*333d2b36SAndroid Build Coastguard Worker			Err:  destParentDir.readErr,
610*333d2b36SAndroid Build Coastguard Worker		}
611*333d2b36SAndroid Build Coastguard Worker	}
612*333d2b36SAndroid Build Coastguard Worker	// check the source and dest themselves
613*333d2b36SAndroid Build Coastguard Worker	sourceBase := filepath.Base(sourcePath)
614*333d2b36SAndroid Build Coastguard Worker	destBase := filepath.Base(destPath)
615*333d2b36SAndroid Build Coastguard Worker
616*333d2b36SAndroid Build Coastguard Worker	file, sourceIsFile := sourceParentDir.files[sourceBase]
617*333d2b36SAndroid Build Coastguard Worker	dir, sourceIsDir := sourceParentDir.subdirs[sourceBase]
618*333d2b36SAndroid Build Coastguard Worker	link, sourceIsLink := sourceParentDir.symlinks[sourceBase]
619*333d2b36SAndroid Build Coastguard Worker
620*333d2b36SAndroid Build Coastguard Worker	// validate that the source exists
621*333d2b36SAndroid Build Coastguard Worker	if !sourceIsFile && !sourceIsDir && !sourceIsLink {
622*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
623*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
624*333d2b36SAndroid Build Coastguard Worker			Path: sourcePath,
625*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
626*333d2b36SAndroid Build Coastguard Worker		}
627*333d2b36SAndroid Build Coastguard Worker
628*333d2b36SAndroid Build Coastguard Worker	}
629*333d2b36SAndroid Build Coastguard Worker
630*333d2b36SAndroid Build Coastguard Worker	// validate the destination doesn't already exist as an incompatible type
631*333d2b36SAndroid Build Coastguard Worker	_, destWasFile := destParentDir.files[destBase]
632*333d2b36SAndroid Build Coastguard Worker	_, destWasDir := destParentDir.subdirs[destBase]
633*333d2b36SAndroid Build Coastguard Worker	_, destWasLink := destParentDir.symlinks[destBase]
634*333d2b36SAndroid Build Coastguard Worker
635*333d2b36SAndroid Build Coastguard Worker	if destWasDir {
636*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
637*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
638*333d2b36SAndroid Build Coastguard Worker			Path: destPath,
639*333d2b36SAndroid Build Coastguard Worker			Err:  errors.New("destination exists as a directory"),
640*333d2b36SAndroid Build Coastguard Worker		}
641*333d2b36SAndroid Build Coastguard Worker	}
642*333d2b36SAndroid Build Coastguard Worker
643*333d2b36SAndroid Build Coastguard Worker	if sourceIsDir && (destWasFile || destWasLink) {
644*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
645*333d2b36SAndroid Build Coastguard Worker			Op:   "move",
646*333d2b36SAndroid Build Coastguard Worker			Path: destPath,
647*333d2b36SAndroid Build Coastguard Worker			Err:  errors.New("destination exists as a file"),
648*333d2b36SAndroid Build Coastguard Worker		}
649*333d2b36SAndroid Build Coastguard Worker	}
650*333d2b36SAndroid Build Coastguard Worker
651*333d2b36SAndroid Build Coastguard Worker	if destWasFile {
652*333d2b36SAndroid Build Coastguard Worker		delete(destParentDir.files, destBase)
653*333d2b36SAndroid Build Coastguard Worker	}
654*333d2b36SAndroid Build Coastguard Worker	if destWasDir {
655*333d2b36SAndroid Build Coastguard Worker		delete(destParentDir.subdirs, destBase)
656*333d2b36SAndroid Build Coastguard Worker	}
657*333d2b36SAndroid Build Coastguard Worker	if destWasLink {
658*333d2b36SAndroid Build Coastguard Worker		delete(destParentDir.symlinks, destBase)
659*333d2b36SAndroid Build Coastguard Worker	}
660*333d2b36SAndroid Build Coastguard Worker
661*333d2b36SAndroid Build Coastguard Worker	if sourceIsFile {
662*333d2b36SAndroid Build Coastguard Worker		destParentDir.files[destBase] = file
663*333d2b36SAndroid Build Coastguard Worker		delete(sourceParentDir.files, sourceBase)
664*333d2b36SAndroid Build Coastguard Worker	}
665*333d2b36SAndroid Build Coastguard Worker	if sourceIsDir {
666*333d2b36SAndroid Build Coastguard Worker		destParentDir.subdirs[destBase] = dir
667*333d2b36SAndroid Build Coastguard Worker		delete(sourceParentDir.subdirs, sourceBase)
668*333d2b36SAndroid Build Coastguard Worker	}
669*333d2b36SAndroid Build Coastguard Worker	if sourceIsLink {
670*333d2b36SAndroid Build Coastguard Worker		destParentDir.symlinks[destBase] = link
671*333d2b36SAndroid Build Coastguard Worker		delete(destParentDir.symlinks, sourceBase)
672*333d2b36SAndroid Build Coastguard Worker	}
673*333d2b36SAndroid Build Coastguard Worker
674*333d2b36SAndroid Build Coastguard Worker	destParentDir.modTime = m.Clock.Time()
675*333d2b36SAndroid Build Coastguard Worker	sourceParentDir.modTime = m.Clock.Time()
676*333d2b36SAndroid Build Coastguard Worker	return nil
677*333d2b36SAndroid Build Coastguard Worker}
678*333d2b36SAndroid Build Coastguard Worker
679*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) newInodeNumber() uint64 {
680*333d2b36SAndroid Build Coastguard Worker	result := m.nextInodeNumber
681*333d2b36SAndroid Build Coastguard Worker	m.nextInodeNumber++
682*333d2b36SAndroid Build Coastguard Worker	return result
683*333d2b36SAndroid Build Coastguard Worker}
684*333d2b36SAndroid Build Coastguard Worker
685*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) WriteFile(filePath string, data []byte, perm os.FileMode) error {
686*333d2b36SAndroid Build Coastguard Worker	filePath, err := m.resolve(filePath, true)
687*333d2b36SAndroid Build Coastguard Worker	if err != nil {
688*333d2b36SAndroid Build Coastguard Worker		return err
689*333d2b36SAndroid Build Coastguard Worker	}
690*333d2b36SAndroid Build Coastguard Worker	parentPath := filepath.Dir(filePath)
691*333d2b36SAndroid Build Coastguard Worker	parentDir, err := m.getDir(parentPath, false)
692*333d2b36SAndroid Build Coastguard Worker	if err != nil || parentDir == nil {
693*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
694*333d2b36SAndroid Build Coastguard Worker			Op:   "write",
695*333d2b36SAndroid Build Coastguard Worker			Path: parentPath,
696*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
697*333d2b36SAndroid Build Coastguard Worker		}
698*333d2b36SAndroid Build Coastguard Worker	}
699*333d2b36SAndroid Build Coastguard Worker	if parentDir.readErr != nil {
700*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
701*333d2b36SAndroid Build Coastguard Worker			Op:   "write",
702*333d2b36SAndroid Build Coastguard Worker			Path: parentPath,
703*333d2b36SAndroid Build Coastguard Worker			Err:  parentDir.readErr,
704*333d2b36SAndroid Build Coastguard Worker		}
705*333d2b36SAndroid Build Coastguard Worker	}
706*333d2b36SAndroid Build Coastguard Worker
707*333d2b36SAndroid Build Coastguard Worker	baseName := filepath.Base(filePath)
708*333d2b36SAndroid Build Coastguard Worker	_, exists := parentDir.files[baseName]
709*333d2b36SAndroid Build Coastguard Worker	if !exists {
710*333d2b36SAndroid Build Coastguard Worker		parentDir.modTime = m.Clock.Time()
711*333d2b36SAndroid Build Coastguard Worker		parentDir.files[baseName] = m.newFile()
712*333d2b36SAndroid Build Coastguard Worker	} else {
713*333d2b36SAndroid Build Coastguard Worker		readErr := parentDir.files[baseName].readErr
714*333d2b36SAndroid Build Coastguard Worker		if readErr != nil {
715*333d2b36SAndroid Build Coastguard Worker			return &os.PathError{
716*333d2b36SAndroid Build Coastguard Worker				Op:   "write",
717*333d2b36SAndroid Build Coastguard Worker				Path: filePath,
718*333d2b36SAndroid Build Coastguard Worker				Err:  readErr,
719*333d2b36SAndroid Build Coastguard Worker			}
720*333d2b36SAndroid Build Coastguard Worker		}
721*333d2b36SAndroid Build Coastguard Worker	}
722*333d2b36SAndroid Build Coastguard Worker	file := parentDir.files[baseName]
723*333d2b36SAndroid Build Coastguard Worker	file.bytes = data
724*333d2b36SAndroid Build Coastguard Worker	file.modTime = m.Clock.Time()
725*333d2b36SAndroid Build Coastguard Worker	return nil
726*333d2b36SAndroid Build Coastguard Worker}
727*333d2b36SAndroid Build Coastguard Worker
728*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) newFile() *mockFile {
729*333d2b36SAndroid Build Coastguard Worker	newFile := &mockFile{}
730*333d2b36SAndroid Build Coastguard Worker	newFile.inodeNumber = m.newInodeNumber()
731*333d2b36SAndroid Build Coastguard Worker	newFile.modTime = m.Clock.Time()
732*333d2b36SAndroid Build Coastguard Worker	newFile.permTime = newFile.modTime
733*333d2b36SAndroid Build Coastguard Worker	return newFile
734*333d2b36SAndroid Build Coastguard Worker}
735*333d2b36SAndroid Build Coastguard Worker
736*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) newDir() *mockDir {
737*333d2b36SAndroid Build Coastguard Worker	newDir := &mockDir{
738*333d2b36SAndroid Build Coastguard Worker		subdirs:  make(map[string]*mockDir, 0),
739*333d2b36SAndroid Build Coastguard Worker		files:    make(map[string]*mockFile, 0),
740*333d2b36SAndroid Build Coastguard Worker		symlinks: make(map[string]*mockLink, 0),
741*333d2b36SAndroid Build Coastguard Worker	}
742*333d2b36SAndroid Build Coastguard Worker	newDir.inodeNumber = m.newInodeNumber()
743*333d2b36SAndroid Build Coastguard Worker	newDir.modTime = m.Clock.Time()
744*333d2b36SAndroid Build Coastguard Worker	newDir.permTime = newDir.modTime
745*333d2b36SAndroid Build Coastguard Worker	return newDir
746*333d2b36SAndroid Build Coastguard Worker}
747*333d2b36SAndroid Build Coastguard Worker
748*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) newLink(target string) *mockLink {
749*333d2b36SAndroid Build Coastguard Worker	newLink := &mockLink{
750*333d2b36SAndroid Build Coastguard Worker		target: target,
751*333d2b36SAndroid Build Coastguard Worker	}
752*333d2b36SAndroid Build Coastguard Worker	newLink.inodeNumber = m.newInodeNumber()
753*333d2b36SAndroid Build Coastguard Worker	newLink.modTime = m.Clock.Time()
754*333d2b36SAndroid Build Coastguard Worker	newLink.permTime = newLink.modTime
755*333d2b36SAndroid Build Coastguard Worker
756*333d2b36SAndroid Build Coastguard Worker	return newLink
757*333d2b36SAndroid Build Coastguard Worker}
758*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) MkDirs(path string) error {
759*333d2b36SAndroid Build Coastguard Worker	_, err := m.getDir(path, true)
760*333d2b36SAndroid Build Coastguard Worker	return err
761*333d2b36SAndroid Build Coastguard Worker}
762*333d2b36SAndroid Build Coastguard Worker
763*333d2b36SAndroid Build Coastguard Worker// getDir doesn't support symlinks
764*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) getDir(path string, createIfMissing bool) (dir *mockDir, err error) {
765*333d2b36SAndroid Build Coastguard Worker	cleanedPath := filepath.Clean(path)
766*333d2b36SAndroid Build Coastguard Worker	if cleanedPath == "/" {
767*333d2b36SAndroid Build Coastguard Worker		return &m.root, nil
768*333d2b36SAndroid Build Coastguard Worker	}
769*333d2b36SAndroid Build Coastguard Worker
770*333d2b36SAndroid Build Coastguard Worker	parentPath, leaf := pathSplit(cleanedPath)
771*333d2b36SAndroid Build Coastguard Worker	if len(parentPath) >= len(path) {
772*333d2b36SAndroid Build Coastguard Worker		return &m.root, nil
773*333d2b36SAndroid Build Coastguard Worker	}
774*333d2b36SAndroid Build Coastguard Worker	parent, err := m.getDir(parentPath, createIfMissing)
775*333d2b36SAndroid Build Coastguard Worker	if err != nil {
776*333d2b36SAndroid Build Coastguard Worker		return nil, err
777*333d2b36SAndroid Build Coastguard Worker	}
778*333d2b36SAndroid Build Coastguard Worker	if parent.readErr != nil {
779*333d2b36SAndroid Build Coastguard Worker		return nil, &os.PathError{
780*333d2b36SAndroid Build Coastguard Worker			Op:   "stat",
781*333d2b36SAndroid Build Coastguard Worker			Path: path,
782*333d2b36SAndroid Build Coastguard Worker			Err:  parent.readErr,
783*333d2b36SAndroid Build Coastguard Worker		}
784*333d2b36SAndroid Build Coastguard Worker	}
785*333d2b36SAndroid Build Coastguard Worker	childDir, dirExists := parent.subdirs[leaf]
786*333d2b36SAndroid Build Coastguard Worker	if !dirExists {
787*333d2b36SAndroid Build Coastguard Worker		if createIfMissing {
788*333d2b36SAndroid Build Coastguard Worker			// confirm that a file with the same name doesn't already exist
789*333d2b36SAndroid Build Coastguard Worker			_, fileExists := parent.files[leaf]
790*333d2b36SAndroid Build Coastguard Worker			if fileExists {
791*333d2b36SAndroid Build Coastguard Worker				return nil, &os.PathError{
792*333d2b36SAndroid Build Coastguard Worker					Op:   "mkdir",
793*333d2b36SAndroid Build Coastguard Worker					Path: path,
794*333d2b36SAndroid Build Coastguard Worker					Err:  os.ErrExist,
795*333d2b36SAndroid Build Coastguard Worker				}
796*333d2b36SAndroid Build Coastguard Worker			}
797*333d2b36SAndroid Build Coastguard Worker			// create this directory
798*333d2b36SAndroid Build Coastguard Worker			childDir = m.newDir()
799*333d2b36SAndroid Build Coastguard Worker			parent.subdirs[leaf] = childDir
800*333d2b36SAndroid Build Coastguard Worker			parent.modTime = m.Clock.Time()
801*333d2b36SAndroid Build Coastguard Worker		} else {
802*333d2b36SAndroid Build Coastguard Worker			return nil, &os.PathError{
803*333d2b36SAndroid Build Coastguard Worker				Op:   "stat",
804*333d2b36SAndroid Build Coastguard Worker				Path: path,
805*333d2b36SAndroid Build Coastguard Worker				Err:  os.ErrNotExist,
806*333d2b36SAndroid Build Coastguard Worker			}
807*333d2b36SAndroid Build Coastguard Worker		}
808*333d2b36SAndroid Build Coastguard Worker	}
809*333d2b36SAndroid Build Coastguard Worker	return childDir, nil
810*333d2b36SAndroid Build Coastguard Worker
811*333d2b36SAndroid Build Coastguard Worker}
812*333d2b36SAndroid Build Coastguard Worker
813*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) Remove(path string) (err error) {
814*333d2b36SAndroid Build Coastguard Worker	path, err = m.resolve(path, false)
815*333d2b36SAndroid Build Coastguard Worker	parentPath, leaf := pathSplit(path)
816*333d2b36SAndroid Build Coastguard Worker	if len(leaf) == 0 {
817*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("Cannot remove %v\n", path)
818*333d2b36SAndroid Build Coastguard Worker	}
819*333d2b36SAndroid Build Coastguard Worker	parentDir, err := m.getDir(parentPath, false)
820*333d2b36SAndroid Build Coastguard Worker	if err != nil {
821*333d2b36SAndroid Build Coastguard Worker		return err
822*333d2b36SAndroid Build Coastguard Worker	}
823*333d2b36SAndroid Build Coastguard Worker	if parentDir == nil {
824*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
825*333d2b36SAndroid Build Coastguard Worker			Op:   "remove",
826*333d2b36SAndroid Build Coastguard Worker			Path: path,
827*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
828*333d2b36SAndroid Build Coastguard Worker		}
829*333d2b36SAndroid Build Coastguard Worker	}
830*333d2b36SAndroid Build Coastguard Worker	if parentDir.readErr != nil {
831*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
832*333d2b36SAndroid Build Coastguard Worker			Op:   "remove",
833*333d2b36SAndroid Build Coastguard Worker			Path: path,
834*333d2b36SAndroid Build Coastguard Worker			Err:  parentDir.readErr,
835*333d2b36SAndroid Build Coastguard Worker		}
836*333d2b36SAndroid Build Coastguard Worker	}
837*333d2b36SAndroid Build Coastguard Worker	_, isDir := parentDir.subdirs[leaf]
838*333d2b36SAndroid Build Coastguard Worker	if isDir {
839*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
840*333d2b36SAndroid Build Coastguard Worker			Op:   "remove",
841*333d2b36SAndroid Build Coastguard Worker			Path: path,
842*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrInvalid,
843*333d2b36SAndroid Build Coastguard Worker		}
844*333d2b36SAndroid Build Coastguard Worker	}
845*333d2b36SAndroid Build Coastguard Worker	_, isLink := parentDir.symlinks[leaf]
846*333d2b36SAndroid Build Coastguard Worker	if isLink {
847*333d2b36SAndroid Build Coastguard Worker		delete(parentDir.symlinks, leaf)
848*333d2b36SAndroid Build Coastguard Worker	} else {
849*333d2b36SAndroid Build Coastguard Worker		_, isFile := parentDir.files[leaf]
850*333d2b36SAndroid Build Coastguard Worker		if !isFile {
851*333d2b36SAndroid Build Coastguard Worker			return &os.PathError{
852*333d2b36SAndroid Build Coastguard Worker				Op:   "remove",
853*333d2b36SAndroid Build Coastguard Worker				Path: path,
854*333d2b36SAndroid Build Coastguard Worker				Err:  os.ErrNotExist,
855*333d2b36SAndroid Build Coastguard Worker			}
856*333d2b36SAndroid Build Coastguard Worker		}
857*333d2b36SAndroid Build Coastguard Worker		delete(parentDir.files, leaf)
858*333d2b36SAndroid Build Coastguard Worker	}
859*333d2b36SAndroid Build Coastguard Worker	parentDir.modTime = m.Clock.Time()
860*333d2b36SAndroid Build Coastguard Worker	return nil
861*333d2b36SAndroid Build Coastguard Worker}
862*333d2b36SAndroid Build Coastguard Worker
863*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) Symlink(oldPath string, newPath string) (err error) {
864*333d2b36SAndroid Build Coastguard Worker	newPath, err = m.resolve(newPath, false)
865*333d2b36SAndroid Build Coastguard Worker	if err != nil {
866*333d2b36SAndroid Build Coastguard Worker		return err
867*333d2b36SAndroid Build Coastguard Worker	}
868*333d2b36SAndroid Build Coastguard Worker
869*333d2b36SAndroid Build Coastguard Worker	newParentPath, leaf := pathSplit(newPath)
870*333d2b36SAndroid Build Coastguard Worker	newParentDir, err := m.getDir(newParentPath, false)
871*333d2b36SAndroid Build Coastguard Worker	if newParentDir.readErr != nil {
872*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
873*333d2b36SAndroid Build Coastguard Worker			Op:   "link",
874*333d2b36SAndroid Build Coastguard Worker			Path: newPath,
875*333d2b36SAndroid Build Coastguard Worker			Err:  newParentDir.readErr,
876*333d2b36SAndroid Build Coastguard Worker		}
877*333d2b36SAndroid Build Coastguard Worker	}
878*333d2b36SAndroid Build Coastguard Worker	if err != nil {
879*333d2b36SAndroid Build Coastguard Worker		return err
880*333d2b36SAndroid Build Coastguard Worker	}
881*333d2b36SAndroid Build Coastguard Worker	newParentDir.symlinks[leaf] = m.newLink(oldPath)
882*333d2b36SAndroid Build Coastguard Worker	return nil
883*333d2b36SAndroid Build Coastguard Worker}
884*333d2b36SAndroid Build Coastguard Worker
885*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) RemoveAll(path string) (err error) {
886*333d2b36SAndroid Build Coastguard Worker	path, err = m.resolve(path, false)
887*333d2b36SAndroid Build Coastguard Worker	if err != nil {
888*333d2b36SAndroid Build Coastguard Worker		return err
889*333d2b36SAndroid Build Coastguard Worker	}
890*333d2b36SAndroid Build Coastguard Worker	parentPath, leaf := pathSplit(path)
891*333d2b36SAndroid Build Coastguard Worker	if len(leaf) == 0 {
892*333d2b36SAndroid Build Coastguard Worker		return fmt.Errorf("Cannot remove %v\n", path)
893*333d2b36SAndroid Build Coastguard Worker	}
894*333d2b36SAndroid Build Coastguard Worker	parentDir, err := m.getDir(parentPath, false)
895*333d2b36SAndroid Build Coastguard Worker	if err != nil {
896*333d2b36SAndroid Build Coastguard Worker		return err
897*333d2b36SAndroid Build Coastguard Worker	}
898*333d2b36SAndroid Build Coastguard Worker	if parentDir == nil {
899*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
900*333d2b36SAndroid Build Coastguard Worker			Op:   "removeAll",
901*333d2b36SAndroid Build Coastguard Worker			Path: path,
902*333d2b36SAndroid Build Coastguard Worker			Err:  os.ErrNotExist,
903*333d2b36SAndroid Build Coastguard Worker		}
904*333d2b36SAndroid Build Coastguard Worker	}
905*333d2b36SAndroid Build Coastguard Worker	if parentDir.readErr != nil {
906*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
907*333d2b36SAndroid Build Coastguard Worker			Op:   "removeAll",
908*333d2b36SAndroid Build Coastguard Worker			Path: path,
909*333d2b36SAndroid Build Coastguard Worker			Err:  parentDir.readErr,
910*333d2b36SAndroid Build Coastguard Worker		}
911*333d2b36SAndroid Build Coastguard Worker
912*333d2b36SAndroid Build Coastguard Worker	}
913*333d2b36SAndroid Build Coastguard Worker	_, isFile := parentDir.files[leaf]
914*333d2b36SAndroid Build Coastguard Worker	_, isLink := parentDir.symlinks[leaf]
915*333d2b36SAndroid Build Coastguard Worker	if isFile || isLink {
916*333d2b36SAndroid Build Coastguard Worker		return m.Remove(path)
917*333d2b36SAndroid Build Coastguard Worker	}
918*333d2b36SAndroid Build Coastguard Worker	_, isDir := parentDir.subdirs[leaf]
919*333d2b36SAndroid Build Coastguard Worker	if !isDir {
920*333d2b36SAndroid Build Coastguard Worker		if !isDir {
921*333d2b36SAndroid Build Coastguard Worker			return &os.PathError{
922*333d2b36SAndroid Build Coastguard Worker				Op:   "removeAll",
923*333d2b36SAndroid Build Coastguard Worker				Path: path,
924*333d2b36SAndroid Build Coastguard Worker				Err:  os.ErrNotExist,
925*333d2b36SAndroid Build Coastguard Worker			}
926*333d2b36SAndroid Build Coastguard Worker		}
927*333d2b36SAndroid Build Coastguard Worker	}
928*333d2b36SAndroid Build Coastguard Worker
929*333d2b36SAndroid Build Coastguard Worker	delete(parentDir.subdirs, leaf)
930*333d2b36SAndroid Build Coastguard Worker	parentDir.modTime = m.Clock.Time()
931*333d2b36SAndroid Build Coastguard Worker	return nil
932*333d2b36SAndroid Build Coastguard Worker}
933*333d2b36SAndroid Build Coastguard Worker
934*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) SetReadable(path string, readable bool) error {
935*333d2b36SAndroid Build Coastguard Worker	var readErr error
936*333d2b36SAndroid Build Coastguard Worker	if !readable {
937*333d2b36SAndroid Build Coastguard Worker		readErr = os.ErrPermission
938*333d2b36SAndroid Build Coastguard Worker	}
939*333d2b36SAndroid Build Coastguard Worker	return m.SetReadErr(path, readErr)
940*333d2b36SAndroid Build Coastguard Worker}
941*333d2b36SAndroid Build Coastguard Worker
942*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) SetReadErr(path string, readErr error) error {
943*333d2b36SAndroid Build Coastguard Worker	path, err := m.resolve(path, false)
944*333d2b36SAndroid Build Coastguard Worker	if err != nil {
945*333d2b36SAndroid Build Coastguard Worker		return err
946*333d2b36SAndroid Build Coastguard Worker	}
947*333d2b36SAndroid Build Coastguard Worker	parentPath, leaf := filepath.Split(path)
948*333d2b36SAndroid Build Coastguard Worker	parentDir, err := m.getDir(parentPath, false)
949*333d2b36SAndroid Build Coastguard Worker	if err != nil {
950*333d2b36SAndroid Build Coastguard Worker		return err
951*333d2b36SAndroid Build Coastguard Worker	}
952*333d2b36SAndroid Build Coastguard Worker	if parentDir.readErr != nil {
953*333d2b36SAndroid Build Coastguard Worker		return &os.PathError{
954*333d2b36SAndroid Build Coastguard Worker			Op:   "chmod",
955*333d2b36SAndroid Build Coastguard Worker			Path: parentPath,
956*333d2b36SAndroid Build Coastguard Worker			Err:  parentDir.readErr,
957*333d2b36SAndroid Build Coastguard Worker		}
958*333d2b36SAndroid Build Coastguard Worker	}
959*333d2b36SAndroid Build Coastguard Worker
960*333d2b36SAndroid Build Coastguard Worker	inode, err := m.getInode(parentDir, leaf)
961*333d2b36SAndroid Build Coastguard Worker	if err != nil {
962*333d2b36SAndroid Build Coastguard Worker		return err
963*333d2b36SAndroid Build Coastguard Worker	}
964*333d2b36SAndroid Build Coastguard Worker	inode.readErr = readErr
965*333d2b36SAndroid Build Coastguard Worker	inode.permTime = m.Clock.Time()
966*333d2b36SAndroid Build Coastguard Worker	return nil
967*333d2b36SAndroid Build Coastguard Worker}
968*333d2b36SAndroid Build Coastguard Worker
969*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) ClearMetrics() {
970*333d2b36SAndroid Build Coastguard Worker	m.ReadDirCalls = []string{}
971*333d2b36SAndroid Build Coastguard Worker	m.StatCalls = []string{}
972*333d2b36SAndroid Build Coastguard Worker}
973*333d2b36SAndroid Build Coastguard Worker
974*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) ViewId() (id string) {
975*333d2b36SAndroid Build Coastguard Worker	return m.viewId
976*333d2b36SAndroid Build Coastguard Worker}
977*333d2b36SAndroid Build Coastguard Worker
978*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) SetViewId(id string) {
979*333d2b36SAndroid Build Coastguard Worker	m.viewId = id
980*333d2b36SAndroid Build Coastguard Worker}
981*333d2b36SAndroid Build Coastguard Workerfunc (m *MockFs) SetDeviceNumber(deviceNumber uint64) {
982*333d2b36SAndroid Build Coastguard Worker	m.deviceNumber = deviceNumber
983*333d2b36SAndroid Build Coastguard Worker}
984