xref: /aosp_15_r20/build/blueprint/pathtools/fs.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2016 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Workerpackage pathtools
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"bytes"
19*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
20*1fa6dee9SAndroid Build Coastguard Worker	"io"
21*1fa6dee9SAndroid Build Coastguard Worker	"io/fs"
22*1fa6dee9SAndroid Build Coastguard Worker	"io/ioutil"
23*1fa6dee9SAndroid Build Coastguard Worker	"os"
24*1fa6dee9SAndroid Build Coastguard Worker	"path/filepath"
25*1fa6dee9SAndroid Build Coastguard Worker	"sort"
26*1fa6dee9SAndroid Build Coastguard Worker	"strings"
27*1fa6dee9SAndroid Build Coastguard Worker	"sync"
28*1fa6dee9SAndroid Build Coastguard Worker	"syscall"
29*1fa6dee9SAndroid Build Coastguard Worker	"time"
30*1fa6dee9SAndroid Build Coastguard Worker)
31*1fa6dee9SAndroid Build Coastguard Worker
32*1fa6dee9SAndroid Build Coastguard Worker// Based on Andrew Gerrand's "10 things you (probably) dont' know about Go"
33*1fa6dee9SAndroid Build Coastguard Worker
34*1fa6dee9SAndroid Build Coastguard Workertype ShouldFollowSymlinks bool
35*1fa6dee9SAndroid Build Coastguard Worker
36*1fa6dee9SAndroid Build Coastguard Workerconst (
37*1fa6dee9SAndroid Build Coastguard Worker	FollowSymlinks     = ShouldFollowSymlinks(true)
38*1fa6dee9SAndroid Build Coastguard Worker	DontFollowSymlinks = ShouldFollowSymlinks(false)
39*1fa6dee9SAndroid Build Coastguard Worker)
40*1fa6dee9SAndroid Build Coastguard Worker
41*1fa6dee9SAndroid Build Coastguard Workervar OsFs FileSystem = &osFs{}
42*1fa6dee9SAndroid Build Coastguard Worker
43*1fa6dee9SAndroid Build Coastguard Workerfunc MockFs(files map[string][]byte) FileSystem {
44*1fa6dee9SAndroid Build Coastguard Worker	fs := &mockFs{
45*1fa6dee9SAndroid Build Coastguard Worker		files:    make(map[string][]byte, len(files)),
46*1fa6dee9SAndroid Build Coastguard Worker		dirs:     make(map[string]bool),
47*1fa6dee9SAndroid Build Coastguard Worker		symlinks: make(map[string]string),
48*1fa6dee9SAndroid Build Coastguard Worker		all:      []string(nil),
49*1fa6dee9SAndroid Build Coastguard Worker	}
50*1fa6dee9SAndroid Build Coastguard Worker
51*1fa6dee9SAndroid Build Coastguard Worker	for f, b := range files {
52*1fa6dee9SAndroid Build Coastguard Worker		if tokens := strings.SplitN(f, "->", 2); len(tokens) == 2 {
53*1fa6dee9SAndroid Build Coastguard Worker			fs.symlinks[strings.TrimSpace(tokens[0])] = strings.TrimSpace(tokens[1])
54*1fa6dee9SAndroid Build Coastguard Worker			continue
55*1fa6dee9SAndroid Build Coastguard Worker		}
56*1fa6dee9SAndroid Build Coastguard Worker
57*1fa6dee9SAndroid Build Coastguard Worker		fs.files[filepath.Clean(f)] = b
58*1fa6dee9SAndroid Build Coastguard Worker		dir := filepath.Dir(f)
59*1fa6dee9SAndroid Build Coastguard Worker		for dir != "." && dir != "/" {
60*1fa6dee9SAndroid Build Coastguard Worker			fs.dirs[dir] = true
61*1fa6dee9SAndroid Build Coastguard Worker			dir = filepath.Dir(dir)
62*1fa6dee9SAndroid Build Coastguard Worker		}
63*1fa6dee9SAndroid Build Coastguard Worker		fs.dirs[dir] = true
64*1fa6dee9SAndroid Build Coastguard Worker	}
65*1fa6dee9SAndroid Build Coastguard Worker
66*1fa6dee9SAndroid Build Coastguard Worker	fs.dirs["."] = true
67*1fa6dee9SAndroid Build Coastguard Worker	fs.dirs["/"] = true
68*1fa6dee9SAndroid Build Coastguard Worker
69*1fa6dee9SAndroid Build Coastguard Worker	for f := range fs.files {
70*1fa6dee9SAndroid Build Coastguard Worker		fs.all = append(fs.all, f)
71*1fa6dee9SAndroid Build Coastguard Worker	}
72*1fa6dee9SAndroid Build Coastguard Worker
73*1fa6dee9SAndroid Build Coastguard Worker	for d := range fs.dirs {
74*1fa6dee9SAndroid Build Coastguard Worker		fs.all = append(fs.all, d)
75*1fa6dee9SAndroid Build Coastguard Worker	}
76*1fa6dee9SAndroid Build Coastguard Worker
77*1fa6dee9SAndroid Build Coastguard Worker	for s := range fs.symlinks {
78*1fa6dee9SAndroid Build Coastguard Worker		fs.all = append(fs.all, s)
79*1fa6dee9SAndroid Build Coastguard Worker	}
80*1fa6dee9SAndroid Build Coastguard Worker
81*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(fs.all)
82*1fa6dee9SAndroid Build Coastguard Worker
83*1fa6dee9SAndroid Build Coastguard Worker	return fs
84*1fa6dee9SAndroid Build Coastguard Worker}
85*1fa6dee9SAndroid Build Coastguard Worker
86*1fa6dee9SAndroid Build Coastguard Workertype ReaderAtSeekerCloser interface {
87*1fa6dee9SAndroid Build Coastguard Worker	io.Reader
88*1fa6dee9SAndroid Build Coastguard Worker	io.ReaderAt
89*1fa6dee9SAndroid Build Coastguard Worker	io.Seeker
90*1fa6dee9SAndroid Build Coastguard Worker	io.Closer
91*1fa6dee9SAndroid Build Coastguard Worker}
92*1fa6dee9SAndroid Build Coastguard Worker
93*1fa6dee9SAndroid Build Coastguard Workertype FileSystem interface {
94*1fa6dee9SAndroid Build Coastguard Worker	// Open opens a file for reading. Follows symlinks.
95*1fa6dee9SAndroid Build Coastguard Worker	Open(name string) (ReaderAtSeekerCloser, error)
96*1fa6dee9SAndroid Build Coastguard Worker
97*1fa6dee9SAndroid Build Coastguard Worker	// OpenFile opens a file for read/write, if the file does not exist, and the
98*1fa6dee9SAndroid Build Coastguard Worker	// O_CREATE flag is passed, it is created with mode perm (before umask).
99*1fa6dee9SAndroid Build Coastguard Worker	OpenFile(name string, flag int, perm fs.FileMode) (io.WriteCloser, error)
100*1fa6dee9SAndroid Build Coastguard Worker
101*1fa6dee9SAndroid Build Coastguard Worker	// Exists returns whether the file exists and whether it is a directory.  Follows symlinks.
102*1fa6dee9SAndroid Build Coastguard Worker	Exists(name string) (bool, bool, error)
103*1fa6dee9SAndroid Build Coastguard Worker
104*1fa6dee9SAndroid Build Coastguard Worker	Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error)
105*1fa6dee9SAndroid Build Coastguard Worker	glob(pattern string) (matches []string, err error)
106*1fa6dee9SAndroid Build Coastguard Worker
107*1fa6dee9SAndroid Build Coastguard Worker	// IsDir returns true if the path points to a directory, false it it points to a file.  Follows symlinks.
108*1fa6dee9SAndroid Build Coastguard Worker	// Returns os.ErrNotExist if the path does not exist or is a symlink to a path that does not exist.
109*1fa6dee9SAndroid Build Coastguard Worker	IsDir(name string) (bool, error)
110*1fa6dee9SAndroid Build Coastguard Worker
111*1fa6dee9SAndroid Build Coastguard Worker	// IsSymlink returns true if the path points to a symlink, even if that symlink points to a path that does
112*1fa6dee9SAndroid Build Coastguard Worker	// not exist.  Returns os.ErrNotExist if the path does not exist.
113*1fa6dee9SAndroid Build Coastguard Worker	IsSymlink(name string) (bool, error)
114*1fa6dee9SAndroid Build Coastguard Worker
115*1fa6dee9SAndroid Build Coastguard Worker	// Lstat returns info on a file without following symlinks.
116*1fa6dee9SAndroid Build Coastguard Worker	Lstat(name string) (os.FileInfo, error)
117*1fa6dee9SAndroid Build Coastguard Worker
118*1fa6dee9SAndroid Build Coastguard Worker	// Lstat returns info on a file.
119*1fa6dee9SAndroid Build Coastguard Worker	Stat(name string) (os.FileInfo, error)
120*1fa6dee9SAndroid Build Coastguard Worker
121*1fa6dee9SAndroid Build Coastguard Worker	// ListDirsRecursive returns a list of all the directories in a path, following symlinks if requested.
122*1fa6dee9SAndroid Build Coastguard Worker	ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error)
123*1fa6dee9SAndroid Build Coastguard Worker
124*1fa6dee9SAndroid Build Coastguard Worker	// ReadDirNames returns a list of everything in a directory.
125*1fa6dee9SAndroid Build Coastguard Worker	ReadDirNames(name string) ([]string, error)
126*1fa6dee9SAndroid Build Coastguard Worker
127*1fa6dee9SAndroid Build Coastguard Worker	// Readlink returns the destination of the named symbolic link.
128*1fa6dee9SAndroid Build Coastguard Worker	Readlink(name string) (string, error)
129*1fa6dee9SAndroid Build Coastguard Worker}
130*1fa6dee9SAndroid Build Coastguard Worker
131*1fa6dee9SAndroid Build Coastguard Worker// osFs implements FileSystem using the local disk.
132*1fa6dee9SAndroid Build Coastguard Workertype osFs struct {
133*1fa6dee9SAndroid Build Coastguard Worker	srcDir        string
134*1fa6dee9SAndroid Build Coastguard Worker	openFilesChan chan bool
135*1fa6dee9SAndroid Build Coastguard Worker}
136*1fa6dee9SAndroid Build Coastguard Worker
137*1fa6dee9SAndroid Build Coastguard Workerfunc NewOsFs(path string) FileSystem {
138*1fa6dee9SAndroid Build Coastguard Worker	// Darwin has a default limit of 256 open files, rate limit open files to 200
139*1fa6dee9SAndroid Build Coastguard Worker	limit := 200
140*1fa6dee9SAndroid Build Coastguard Worker	return &osFs{
141*1fa6dee9SAndroid Build Coastguard Worker		srcDir:        path,
142*1fa6dee9SAndroid Build Coastguard Worker		openFilesChan: make(chan bool, limit),
143*1fa6dee9SAndroid Build Coastguard Worker	}
144*1fa6dee9SAndroid Build Coastguard Worker}
145*1fa6dee9SAndroid Build Coastguard Worker
146*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) acquire() {
147*1fa6dee9SAndroid Build Coastguard Worker	if fs.openFilesChan != nil {
148*1fa6dee9SAndroid Build Coastguard Worker		fs.openFilesChan <- true
149*1fa6dee9SAndroid Build Coastguard Worker	}
150*1fa6dee9SAndroid Build Coastguard Worker}
151*1fa6dee9SAndroid Build Coastguard Worker
152*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) release() {
153*1fa6dee9SAndroid Build Coastguard Worker	if fs.openFilesChan != nil {
154*1fa6dee9SAndroid Build Coastguard Worker		<-fs.openFilesChan
155*1fa6dee9SAndroid Build Coastguard Worker	}
156*1fa6dee9SAndroid Build Coastguard Worker}
157*1fa6dee9SAndroid Build Coastguard Worker
158*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) toAbs(path string) string {
159*1fa6dee9SAndroid Build Coastguard Worker	if filepath.IsAbs(path) {
160*1fa6dee9SAndroid Build Coastguard Worker		return path
161*1fa6dee9SAndroid Build Coastguard Worker	}
162*1fa6dee9SAndroid Build Coastguard Worker	return filepath.Join(fs.srcDir, path)
163*1fa6dee9SAndroid Build Coastguard Worker}
164*1fa6dee9SAndroid Build Coastguard Worker
165*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) removeSrcDirPrefix(path string) string {
166*1fa6dee9SAndroid Build Coastguard Worker	if fs.srcDir == "" {
167*1fa6dee9SAndroid Build Coastguard Worker		return path
168*1fa6dee9SAndroid Build Coastguard Worker	}
169*1fa6dee9SAndroid Build Coastguard Worker	rel, err := filepath.Rel(fs.srcDir, path)
170*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
171*1fa6dee9SAndroid Build Coastguard Worker		panic(fmt.Errorf("unexpected failure in removeSrcDirPrefix filepath.Rel(%s, %s): %s",
172*1fa6dee9SAndroid Build Coastguard Worker			fs.srcDir, path, err))
173*1fa6dee9SAndroid Build Coastguard Worker	}
174*1fa6dee9SAndroid Build Coastguard Worker	if strings.HasPrefix(rel, "../") {
175*1fa6dee9SAndroid Build Coastguard Worker		panic(fmt.Errorf("unexpected relative path outside directory in removeSrcDirPrefix filepath.Rel(%s, %s): %s",
176*1fa6dee9SAndroid Build Coastguard Worker			fs.srcDir, path, rel))
177*1fa6dee9SAndroid Build Coastguard Worker	}
178*1fa6dee9SAndroid Build Coastguard Worker	return rel
179*1fa6dee9SAndroid Build Coastguard Worker}
180*1fa6dee9SAndroid Build Coastguard Worker
181*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) removeSrcDirPrefixes(paths []string) []string {
182*1fa6dee9SAndroid Build Coastguard Worker	if fs.srcDir != "" {
183*1fa6dee9SAndroid Build Coastguard Worker		for i, path := range paths {
184*1fa6dee9SAndroid Build Coastguard Worker			paths[i] = fs.removeSrcDirPrefix(path)
185*1fa6dee9SAndroid Build Coastguard Worker		}
186*1fa6dee9SAndroid Build Coastguard Worker	}
187*1fa6dee9SAndroid Build Coastguard Worker	return paths
188*1fa6dee9SAndroid Build Coastguard Worker}
189*1fa6dee9SAndroid Build Coastguard Worker
190*1fa6dee9SAndroid Build Coastguard Worker// OsFile wraps an os.File to also release open file descriptors semaphore on close
191*1fa6dee9SAndroid Build Coastguard Workertype OsFile struct {
192*1fa6dee9SAndroid Build Coastguard Worker	*os.File
193*1fa6dee9SAndroid Build Coastguard Worker	fs *osFs
194*1fa6dee9SAndroid Build Coastguard Worker}
195*1fa6dee9SAndroid Build Coastguard Worker
196*1fa6dee9SAndroid Build Coastguard Worker// Close closes file and releases the open file descriptor semaphore
197*1fa6dee9SAndroid Build Coastguard Workerfunc (f *OsFile) Close() error {
198*1fa6dee9SAndroid Build Coastguard Worker	err := f.File.Close()
199*1fa6dee9SAndroid Build Coastguard Worker	f.fs.release()
200*1fa6dee9SAndroid Build Coastguard Worker	return err
201*1fa6dee9SAndroid Build Coastguard Worker}
202*1fa6dee9SAndroid Build Coastguard Worker
203*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) Open(name string) (ReaderAtSeekerCloser, error) {
204*1fa6dee9SAndroid Build Coastguard Worker	fs.acquire()
205*1fa6dee9SAndroid Build Coastguard Worker	f, err := os.Open(fs.toAbs(name))
206*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
207*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
208*1fa6dee9SAndroid Build Coastguard Worker	}
209*1fa6dee9SAndroid Build Coastguard Worker	return &OsFile{f, fs}, nil
210*1fa6dee9SAndroid Build Coastguard Worker}
211*1fa6dee9SAndroid Build Coastguard Worker
212*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) OpenFile(name string, flag int, perm fs.FileMode) (io.WriteCloser, error) {
213*1fa6dee9SAndroid Build Coastguard Worker	fs.acquire()
214*1fa6dee9SAndroid Build Coastguard Worker	f, err := os.OpenFile(fs.toAbs(name), flag, perm)
215*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
216*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
217*1fa6dee9SAndroid Build Coastguard Worker	}
218*1fa6dee9SAndroid Build Coastguard Worker	return &OsFile{f, fs}, nil
219*1fa6dee9SAndroid Build Coastguard Worker}
220*1fa6dee9SAndroid Build Coastguard Worker
221*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) Exists(name string) (bool, bool, error) {
222*1fa6dee9SAndroid Build Coastguard Worker	stat, err := os.Stat(fs.toAbs(name))
223*1fa6dee9SAndroid Build Coastguard Worker	if err == nil {
224*1fa6dee9SAndroid Build Coastguard Worker		return true, stat.IsDir(), nil
225*1fa6dee9SAndroid Build Coastguard Worker	} else if os.IsNotExist(err) {
226*1fa6dee9SAndroid Build Coastguard Worker		return false, false, nil
227*1fa6dee9SAndroid Build Coastguard Worker	} else {
228*1fa6dee9SAndroid Build Coastguard Worker		return false, false, err
229*1fa6dee9SAndroid Build Coastguard Worker	}
230*1fa6dee9SAndroid Build Coastguard Worker}
231*1fa6dee9SAndroid Build Coastguard Worker
232*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) IsDir(name string) (bool, error) {
233*1fa6dee9SAndroid Build Coastguard Worker	info, err := os.Stat(fs.toAbs(name))
234*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
235*1fa6dee9SAndroid Build Coastguard Worker		return false, err
236*1fa6dee9SAndroid Build Coastguard Worker	}
237*1fa6dee9SAndroid Build Coastguard Worker	return info.IsDir(), nil
238*1fa6dee9SAndroid Build Coastguard Worker}
239*1fa6dee9SAndroid Build Coastguard Worker
240*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) IsSymlink(name string) (bool, error) {
241*1fa6dee9SAndroid Build Coastguard Worker	if info, err := os.Lstat(fs.toAbs(name)); err != nil {
242*1fa6dee9SAndroid Build Coastguard Worker		return false, err
243*1fa6dee9SAndroid Build Coastguard Worker	} else {
244*1fa6dee9SAndroid Build Coastguard Worker		return info.Mode()&os.ModeSymlink != 0, nil
245*1fa6dee9SAndroid Build Coastguard Worker	}
246*1fa6dee9SAndroid Build Coastguard Worker}
247*1fa6dee9SAndroid Build Coastguard Worker
248*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) {
249*1fa6dee9SAndroid Build Coastguard Worker	return startGlob(fs, pattern, excludes, follow)
250*1fa6dee9SAndroid Build Coastguard Worker}
251*1fa6dee9SAndroid Build Coastguard Worker
252*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) glob(pattern string) ([]string, error) {
253*1fa6dee9SAndroid Build Coastguard Worker	fs.acquire()
254*1fa6dee9SAndroid Build Coastguard Worker	defer fs.release()
255*1fa6dee9SAndroid Build Coastguard Worker	paths, err := filepath.Glob(fs.toAbs(pattern))
256*1fa6dee9SAndroid Build Coastguard Worker	fs.removeSrcDirPrefixes(paths)
257*1fa6dee9SAndroid Build Coastguard Worker	return paths, err
258*1fa6dee9SAndroid Build Coastguard Worker}
259*1fa6dee9SAndroid Build Coastguard Worker
260*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) Lstat(path string) (stats os.FileInfo, err error) {
261*1fa6dee9SAndroid Build Coastguard Worker	return os.Lstat(fs.toAbs(path))
262*1fa6dee9SAndroid Build Coastguard Worker}
263*1fa6dee9SAndroid Build Coastguard Worker
264*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) Stat(path string) (stats os.FileInfo, err error) {
265*1fa6dee9SAndroid Build Coastguard Worker	return os.Stat(fs.toAbs(path))
266*1fa6dee9SAndroid Build Coastguard Worker}
267*1fa6dee9SAndroid Build Coastguard Worker
268*1fa6dee9SAndroid Build Coastguard Worker// Returns a list of all directories under dir
269*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) (dirs []string, err error) {
270*1fa6dee9SAndroid Build Coastguard Worker	return listDirsRecursive(fs, name, follow)
271*1fa6dee9SAndroid Build Coastguard Worker}
272*1fa6dee9SAndroid Build Coastguard Worker
273*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) ReadDirNames(name string) ([]string, error) {
274*1fa6dee9SAndroid Build Coastguard Worker	fs.acquire()
275*1fa6dee9SAndroid Build Coastguard Worker	defer fs.release()
276*1fa6dee9SAndroid Build Coastguard Worker	dir, err := os.Open(fs.toAbs(name))
277*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
278*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
279*1fa6dee9SAndroid Build Coastguard Worker	}
280*1fa6dee9SAndroid Build Coastguard Worker	defer dir.Close()
281*1fa6dee9SAndroid Build Coastguard Worker
282*1fa6dee9SAndroid Build Coastguard Worker	contents, err := dir.Readdirnames(-1)
283*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
284*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
285*1fa6dee9SAndroid Build Coastguard Worker	}
286*1fa6dee9SAndroid Build Coastguard Worker
287*1fa6dee9SAndroid Build Coastguard Worker	sort.Strings(contents)
288*1fa6dee9SAndroid Build Coastguard Worker	return contents, nil
289*1fa6dee9SAndroid Build Coastguard Worker}
290*1fa6dee9SAndroid Build Coastguard Worker
291*1fa6dee9SAndroid Build Coastguard Workerfunc (fs *osFs) Readlink(name string) (string, error) {
292*1fa6dee9SAndroid Build Coastguard Worker	return os.Readlink(fs.toAbs(name))
293*1fa6dee9SAndroid Build Coastguard Worker}
294*1fa6dee9SAndroid Build Coastguard Worker
295*1fa6dee9SAndroid Build Coastguard Workertype mockFs struct {
296*1fa6dee9SAndroid Build Coastguard Worker	files    map[string][]byte
297*1fa6dee9SAndroid Build Coastguard Worker	dirs     map[string]bool
298*1fa6dee9SAndroid Build Coastguard Worker	symlinks map[string]string
299*1fa6dee9SAndroid Build Coastguard Worker	all      []string
300*1fa6dee9SAndroid Build Coastguard Worker	lock     sync.RWMutex
301*1fa6dee9SAndroid Build Coastguard Worker}
302*1fa6dee9SAndroid Build Coastguard Worker
303*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) followSymlinks(name string) string {
304*1fa6dee9SAndroid Build Coastguard Worker	dir, file := quickSplit(name)
305*1fa6dee9SAndroid Build Coastguard Worker	if dir != "." && dir != "/" {
306*1fa6dee9SAndroid Build Coastguard Worker		dir = m.followSymlinks(dir)
307*1fa6dee9SAndroid Build Coastguard Worker	}
308*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Join(dir, file)
309*1fa6dee9SAndroid Build Coastguard Worker
310*1fa6dee9SAndroid Build Coastguard Worker	for i := 0; i < 255; i++ {
311*1fa6dee9SAndroid Build Coastguard Worker		i++
312*1fa6dee9SAndroid Build Coastguard Worker		if i > 255 {
313*1fa6dee9SAndroid Build Coastguard Worker			panic("symlink loop")
314*1fa6dee9SAndroid Build Coastguard Worker		}
315*1fa6dee9SAndroid Build Coastguard Worker		to, exists := m.symlinks[name]
316*1fa6dee9SAndroid Build Coastguard Worker		if !exists {
317*1fa6dee9SAndroid Build Coastguard Worker			break
318*1fa6dee9SAndroid Build Coastguard Worker		}
319*1fa6dee9SAndroid Build Coastguard Worker		if filepath.IsAbs(to) {
320*1fa6dee9SAndroid Build Coastguard Worker			name = to
321*1fa6dee9SAndroid Build Coastguard Worker		} else {
322*1fa6dee9SAndroid Build Coastguard Worker			name = filepath.Join(dir, to)
323*1fa6dee9SAndroid Build Coastguard Worker		}
324*1fa6dee9SAndroid Build Coastguard Worker	}
325*1fa6dee9SAndroid Build Coastguard Worker	return name
326*1fa6dee9SAndroid Build Coastguard Worker}
327*1fa6dee9SAndroid Build Coastguard Worker
328*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) Open(name string) (ReaderAtSeekerCloser, error) {
329*1fa6dee9SAndroid Build Coastguard Worker	m.lock.RLock()
330*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.RUnlock()
331*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
332*1fa6dee9SAndroid Build Coastguard Worker	name = m.followSymlinks(name)
333*1fa6dee9SAndroid Build Coastguard Worker	if f, ok := m.files[name]; ok {
334*1fa6dee9SAndroid Build Coastguard Worker		return struct {
335*1fa6dee9SAndroid Build Coastguard Worker			io.Closer
336*1fa6dee9SAndroid Build Coastguard Worker			*bytes.Reader
337*1fa6dee9SAndroid Build Coastguard Worker		}{
338*1fa6dee9SAndroid Build Coastguard Worker			ioutil.NopCloser(nil),
339*1fa6dee9SAndroid Build Coastguard Worker			bytes.NewReader(f),
340*1fa6dee9SAndroid Build Coastguard Worker		}, nil
341*1fa6dee9SAndroid Build Coastguard Worker	}
342*1fa6dee9SAndroid Build Coastguard Worker
343*1fa6dee9SAndroid Build Coastguard Worker	return nil, &os.PathError{
344*1fa6dee9SAndroid Build Coastguard Worker		Op:   "open",
345*1fa6dee9SAndroid Build Coastguard Worker		Path: name,
346*1fa6dee9SAndroid Build Coastguard Worker		Err:  os.ErrNotExist,
347*1fa6dee9SAndroid Build Coastguard Worker	}
348*1fa6dee9SAndroid Build Coastguard Worker}
349*1fa6dee9SAndroid Build Coastguard Worker
350*1fa6dee9SAndroid Build Coastguard Workertype MockFileWriter struct {
351*1fa6dee9SAndroid Build Coastguard Worker	name string
352*1fa6dee9SAndroid Build Coastguard Worker	fs   *mockFs
353*1fa6dee9SAndroid Build Coastguard Worker}
354*1fa6dee9SAndroid Build Coastguard Worker
355*1fa6dee9SAndroid Build Coastguard Workerfunc (b *MockFileWriter) Write(p []byte) (n int, err error) {
356*1fa6dee9SAndroid Build Coastguard Worker	b.fs.lock.Lock()
357*1fa6dee9SAndroid Build Coastguard Worker	defer b.fs.lock.Unlock()
358*1fa6dee9SAndroid Build Coastguard Worker	b.fs.files[b.name] = append(b.fs.files[b.name], p...)
359*1fa6dee9SAndroid Build Coastguard Worker	return n, nil
360*1fa6dee9SAndroid Build Coastguard Worker}
361*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) OpenFile(name string, flag int, perm fs.FileMode) (io.WriteCloser, error) {
362*1fa6dee9SAndroid Build Coastguard Worker	// For mockFs we simplify the logic here by just either creating a new file or
363*1fa6dee9SAndroid Build Coastguard Worker	// truncating an existing one.
364*1fa6dee9SAndroid Build Coastguard Worker	m.lock.Lock()
365*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.Unlock()
366*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
367*1fa6dee9SAndroid Build Coastguard Worker	name = m.followSymlinks(name)
368*1fa6dee9SAndroid Build Coastguard Worker	m.files[name] = []byte{}
369*1fa6dee9SAndroid Build Coastguard Worker	return struct {
370*1fa6dee9SAndroid Build Coastguard Worker		io.Closer
371*1fa6dee9SAndroid Build Coastguard Worker		io.Writer
372*1fa6dee9SAndroid Build Coastguard Worker	}{
373*1fa6dee9SAndroid Build Coastguard Worker		ioutil.NopCloser(nil),
374*1fa6dee9SAndroid Build Coastguard Worker		&MockFileWriter{
375*1fa6dee9SAndroid Build Coastguard Worker			name: name,
376*1fa6dee9SAndroid Build Coastguard Worker			fs:   m,
377*1fa6dee9SAndroid Build Coastguard Worker		},
378*1fa6dee9SAndroid Build Coastguard Worker	}, nil
379*1fa6dee9SAndroid Build Coastguard Worker}
380*1fa6dee9SAndroid Build Coastguard Worker
381*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) Exists(name string) (bool, bool, error) {
382*1fa6dee9SAndroid Build Coastguard Worker	m.lock.RLock()
383*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.RUnlock()
384*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
385*1fa6dee9SAndroid Build Coastguard Worker	name = m.followSymlinks(name)
386*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := m.files[name]; ok {
387*1fa6dee9SAndroid Build Coastguard Worker		return ok, false, nil
388*1fa6dee9SAndroid Build Coastguard Worker	}
389*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := m.dirs[name]; ok {
390*1fa6dee9SAndroid Build Coastguard Worker		return ok, true, nil
391*1fa6dee9SAndroid Build Coastguard Worker	}
392*1fa6dee9SAndroid Build Coastguard Worker	return false, false, nil
393*1fa6dee9SAndroid Build Coastguard Worker}
394*1fa6dee9SAndroid Build Coastguard Worker
395*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) IsDir(name string) (bool, error) {
396*1fa6dee9SAndroid Build Coastguard Worker	m.lock.RLock()
397*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.RUnlock()
398*1fa6dee9SAndroid Build Coastguard Worker	dir := filepath.Dir(name)
399*1fa6dee9SAndroid Build Coastguard Worker	if dir != "." && dir != "/" {
400*1fa6dee9SAndroid Build Coastguard Worker		isDir, err := m.IsDir(dir)
401*1fa6dee9SAndroid Build Coastguard Worker
402*1fa6dee9SAndroid Build Coastguard Worker		if serr, ok := err.(*os.SyscallError); ok && serr.Err == syscall.ENOTDIR {
403*1fa6dee9SAndroid Build Coastguard Worker			isDir = false
404*1fa6dee9SAndroid Build Coastguard Worker		} else if err != nil {
405*1fa6dee9SAndroid Build Coastguard Worker			return false, err
406*1fa6dee9SAndroid Build Coastguard Worker		}
407*1fa6dee9SAndroid Build Coastguard Worker
408*1fa6dee9SAndroid Build Coastguard Worker		if !isDir {
409*1fa6dee9SAndroid Build Coastguard Worker			return false, os.NewSyscallError("stat "+name, syscall.ENOTDIR)
410*1fa6dee9SAndroid Build Coastguard Worker		}
411*1fa6dee9SAndroid Build Coastguard Worker	}
412*1fa6dee9SAndroid Build Coastguard Worker
413*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
414*1fa6dee9SAndroid Build Coastguard Worker	name = m.followSymlinks(name)
415*1fa6dee9SAndroid Build Coastguard Worker
416*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := m.dirs[name]; ok {
417*1fa6dee9SAndroid Build Coastguard Worker		return true, nil
418*1fa6dee9SAndroid Build Coastguard Worker	}
419*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := m.files[name]; ok {
420*1fa6dee9SAndroid Build Coastguard Worker		return false, nil
421*1fa6dee9SAndroid Build Coastguard Worker	}
422*1fa6dee9SAndroid Build Coastguard Worker	return false, os.ErrNotExist
423*1fa6dee9SAndroid Build Coastguard Worker}
424*1fa6dee9SAndroid Build Coastguard Worker
425*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) IsSymlink(name string) (bool, error) {
426*1fa6dee9SAndroid Build Coastguard Worker	m.lock.RLock()
427*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.RUnlock()
428*1fa6dee9SAndroid Build Coastguard Worker	dir, file := quickSplit(name)
429*1fa6dee9SAndroid Build Coastguard Worker	dir = m.followSymlinks(dir)
430*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Join(dir, file)
431*1fa6dee9SAndroid Build Coastguard Worker
432*1fa6dee9SAndroid Build Coastguard Worker	if _, isSymlink := m.symlinks[name]; isSymlink {
433*1fa6dee9SAndroid Build Coastguard Worker		return true, nil
434*1fa6dee9SAndroid Build Coastguard Worker	}
435*1fa6dee9SAndroid Build Coastguard Worker	if _, isDir := m.dirs[name]; isDir {
436*1fa6dee9SAndroid Build Coastguard Worker		return false, nil
437*1fa6dee9SAndroid Build Coastguard Worker	}
438*1fa6dee9SAndroid Build Coastguard Worker	if _, isFile := m.files[name]; isFile {
439*1fa6dee9SAndroid Build Coastguard Worker		return false, nil
440*1fa6dee9SAndroid Build Coastguard Worker	}
441*1fa6dee9SAndroid Build Coastguard Worker	return false, os.ErrNotExist
442*1fa6dee9SAndroid Build Coastguard Worker}
443*1fa6dee9SAndroid Build Coastguard Worker
444*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) Glob(pattern string, excludes []string, follow ShouldFollowSymlinks) (GlobResult, error) {
445*1fa6dee9SAndroid Build Coastguard Worker	return startGlob(m, pattern, excludes, follow)
446*1fa6dee9SAndroid Build Coastguard Worker}
447*1fa6dee9SAndroid Build Coastguard Worker
448*1fa6dee9SAndroid Build Coastguard Workerfunc unescapeGlob(s string) string {
449*1fa6dee9SAndroid Build Coastguard Worker	i := 0
450*1fa6dee9SAndroid Build Coastguard Worker	for i < len(s) {
451*1fa6dee9SAndroid Build Coastguard Worker		if s[i] == '\\' {
452*1fa6dee9SAndroid Build Coastguard Worker			s = s[:i] + s[i+1:]
453*1fa6dee9SAndroid Build Coastguard Worker		} else {
454*1fa6dee9SAndroid Build Coastguard Worker			i++
455*1fa6dee9SAndroid Build Coastguard Worker		}
456*1fa6dee9SAndroid Build Coastguard Worker	}
457*1fa6dee9SAndroid Build Coastguard Worker	return s
458*1fa6dee9SAndroid Build Coastguard Worker}
459*1fa6dee9SAndroid Build Coastguard Worker
460*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) glob(pattern string) ([]string, error) {
461*1fa6dee9SAndroid Build Coastguard Worker	dir, file := quickSplit(pattern)
462*1fa6dee9SAndroid Build Coastguard Worker
463*1fa6dee9SAndroid Build Coastguard Worker	dir = unescapeGlob(dir)
464*1fa6dee9SAndroid Build Coastguard Worker	toDir := m.followSymlinks(dir)
465*1fa6dee9SAndroid Build Coastguard Worker
466*1fa6dee9SAndroid Build Coastguard Worker	var matches []string
467*1fa6dee9SAndroid Build Coastguard Worker	for _, f := range m.all {
468*1fa6dee9SAndroid Build Coastguard Worker		fDir, fFile := quickSplit(f)
469*1fa6dee9SAndroid Build Coastguard Worker		if toDir == fDir {
470*1fa6dee9SAndroid Build Coastguard Worker			match, err := filepath.Match(file, fFile)
471*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
472*1fa6dee9SAndroid Build Coastguard Worker				return nil, err
473*1fa6dee9SAndroid Build Coastguard Worker			}
474*1fa6dee9SAndroid Build Coastguard Worker			if (f == "." || f == "/") && f != pattern {
475*1fa6dee9SAndroid Build Coastguard Worker				// filepath.Glob won't return "." or "/" unless the pattern was "." or "/"
476*1fa6dee9SAndroid Build Coastguard Worker				match = false
477*1fa6dee9SAndroid Build Coastguard Worker			}
478*1fa6dee9SAndroid Build Coastguard Worker			if match {
479*1fa6dee9SAndroid Build Coastguard Worker				matches = append(matches, filepath.Join(dir, fFile))
480*1fa6dee9SAndroid Build Coastguard Worker			}
481*1fa6dee9SAndroid Build Coastguard Worker		}
482*1fa6dee9SAndroid Build Coastguard Worker	}
483*1fa6dee9SAndroid Build Coastguard Worker	return matches, nil
484*1fa6dee9SAndroid Build Coastguard Worker}
485*1fa6dee9SAndroid Build Coastguard Worker
486*1fa6dee9SAndroid Build Coastguard Workertype mockStat struct {
487*1fa6dee9SAndroid Build Coastguard Worker	name string
488*1fa6dee9SAndroid Build Coastguard Worker	size int64
489*1fa6dee9SAndroid Build Coastguard Worker	mode os.FileMode
490*1fa6dee9SAndroid Build Coastguard Worker}
491*1fa6dee9SAndroid Build Coastguard Worker
492*1fa6dee9SAndroid Build Coastguard Workerfunc (ms *mockStat) Name() string       { return ms.name }
493*1fa6dee9SAndroid Build Coastguard Workerfunc (ms *mockStat) IsDir() bool        { return ms.Mode().IsDir() }
494*1fa6dee9SAndroid Build Coastguard Workerfunc (ms *mockStat) Size() int64        { return ms.size }
495*1fa6dee9SAndroid Build Coastguard Workerfunc (ms *mockStat) Mode() os.FileMode  { return ms.mode }
496*1fa6dee9SAndroid Build Coastguard Workerfunc (ms *mockStat) ModTime() time.Time { return time.Time{} }
497*1fa6dee9SAndroid Build Coastguard Workerfunc (ms *mockStat) Sys() interface{}   { return nil }
498*1fa6dee9SAndroid Build Coastguard Worker
499*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) Lstat(name string) (os.FileInfo, error) {
500*1fa6dee9SAndroid Build Coastguard Worker	m.lock.RLock()
501*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.RUnlock()
502*1fa6dee9SAndroid Build Coastguard Worker	dir, file := quickSplit(name)
503*1fa6dee9SAndroid Build Coastguard Worker	dir = m.followSymlinks(dir)
504*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Join(dir, file)
505*1fa6dee9SAndroid Build Coastguard Worker
506*1fa6dee9SAndroid Build Coastguard Worker	ms := mockStat{
507*1fa6dee9SAndroid Build Coastguard Worker		name: file,
508*1fa6dee9SAndroid Build Coastguard Worker	}
509*1fa6dee9SAndroid Build Coastguard Worker
510*1fa6dee9SAndroid Build Coastguard Worker	if symlink, isSymlink := m.symlinks[name]; isSymlink {
511*1fa6dee9SAndroid Build Coastguard Worker		ms.mode = os.ModeSymlink
512*1fa6dee9SAndroid Build Coastguard Worker		ms.size = int64(len(symlink))
513*1fa6dee9SAndroid Build Coastguard Worker	} else if _, isDir := m.dirs[name]; isDir {
514*1fa6dee9SAndroid Build Coastguard Worker		ms.mode = os.ModeDir
515*1fa6dee9SAndroid Build Coastguard Worker	} else if _, isFile := m.files[name]; isFile {
516*1fa6dee9SAndroid Build Coastguard Worker		ms.mode = 0
517*1fa6dee9SAndroid Build Coastguard Worker		ms.size = int64(len(m.files[name]))
518*1fa6dee9SAndroid Build Coastguard Worker	} else {
519*1fa6dee9SAndroid Build Coastguard Worker		return nil, os.ErrNotExist
520*1fa6dee9SAndroid Build Coastguard Worker	}
521*1fa6dee9SAndroid Build Coastguard Worker
522*1fa6dee9SAndroid Build Coastguard Worker	return &ms, nil
523*1fa6dee9SAndroid Build Coastguard Worker}
524*1fa6dee9SAndroid Build Coastguard Worker
525*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) Stat(name string) (os.FileInfo, error) {
526*1fa6dee9SAndroid Build Coastguard Worker	m.lock.RLock()
527*1fa6dee9SAndroid Build Coastguard Worker	defer m.lock.RUnlock()
528*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
529*1fa6dee9SAndroid Build Coastguard Worker	origName := name
530*1fa6dee9SAndroid Build Coastguard Worker	name = m.followSymlinks(name)
531*1fa6dee9SAndroid Build Coastguard Worker
532*1fa6dee9SAndroid Build Coastguard Worker	ms := mockStat{
533*1fa6dee9SAndroid Build Coastguard Worker		name: filepath.Base(origName),
534*1fa6dee9SAndroid Build Coastguard Worker		size: int64(len(m.files[name])),
535*1fa6dee9SAndroid Build Coastguard Worker	}
536*1fa6dee9SAndroid Build Coastguard Worker
537*1fa6dee9SAndroid Build Coastguard Worker	if _, isDir := m.dirs[name]; isDir {
538*1fa6dee9SAndroid Build Coastguard Worker		ms.mode = os.ModeDir
539*1fa6dee9SAndroid Build Coastguard Worker	} else if _, isFile := m.files[name]; isFile {
540*1fa6dee9SAndroid Build Coastguard Worker		ms.mode = 0
541*1fa6dee9SAndroid Build Coastguard Worker		ms.size = int64(len(m.files[name]))
542*1fa6dee9SAndroid Build Coastguard Worker	} else {
543*1fa6dee9SAndroid Build Coastguard Worker		return nil, os.ErrNotExist
544*1fa6dee9SAndroid Build Coastguard Worker	}
545*1fa6dee9SAndroid Build Coastguard Worker
546*1fa6dee9SAndroid Build Coastguard Worker	return &ms, nil
547*1fa6dee9SAndroid Build Coastguard Worker}
548*1fa6dee9SAndroid Build Coastguard Worker
549*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) ReadDirNames(name string) ([]string, error) {
550*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
551*1fa6dee9SAndroid Build Coastguard Worker	name = m.followSymlinks(name)
552*1fa6dee9SAndroid Build Coastguard Worker
553*1fa6dee9SAndroid Build Coastguard Worker	exists, isDir, err := m.Exists(name)
554*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
555*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
556*1fa6dee9SAndroid Build Coastguard Worker	}
557*1fa6dee9SAndroid Build Coastguard Worker	if !exists {
558*1fa6dee9SAndroid Build Coastguard Worker		return nil, os.ErrNotExist
559*1fa6dee9SAndroid Build Coastguard Worker	}
560*1fa6dee9SAndroid Build Coastguard Worker	if !isDir {
561*1fa6dee9SAndroid Build Coastguard Worker		return nil, os.NewSyscallError("readdir", syscall.ENOTDIR)
562*1fa6dee9SAndroid Build Coastguard Worker	}
563*1fa6dee9SAndroid Build Coastguard Worker
564*1fa6dee9SAndroid Build Coastguard Worker	var ret []string
565*1fa6dee9SAndroid Build Coastguard Worker	for _, f := range m.all {
566*1fa6dee9SAndroid Build Coastguard Worker		dir, file := quickSplit(f)
567*1fa6dee9SAndroid Build Coastguard Worker		if dir == name && len(file) > 0 && file[0] != '.' {
568*1fa6dee9SAndroid Build Coastguard Worker			ret = append(ret, file)
569*1fa6dee9SAndroid Build Coastguard Worker		}
570*1fa6dee9SAndroid Build Coastguard Worker	}
571*1fa6dee9SAndroid Build Coastguard Worker	return ret, nil
572*1fa6dee9SAndroid Build Coastguard Worker}
573*1fa6dee9SAndroid Build Coastguard Worker
574*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) ListDirsRecursive(name string, follow ShouldFollowSymlinks) ([]string, error) {
575*1fa6dee9SAndroid Build Coastguard Worker	return listDirsRecursive(m, name, follow)
576*1fa6dee9SAndroid Build Coastguard Worker}
577*1fa6dee9SAndroid Build Coastguard Worker
578*1fa6dee9SAndroid Build Coastguard Workerfunc (m *mockFs) Readlink(name string) (string, error) {
579*1fa6dee9SAndroid Build Coastguard Worker	dir, file := quickSplit(name)
580*1fa6dee9SAndroid Build Coastguard Worker	dir = m.followSymlinks(dir)
581*1fa6dee9SAndroid Build Coastguard Worker
582*1fa6dee9SAndroid Build Coastguard Worker	origName := name
583*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Join(dir, file)
584*1fa6dee9SAndroid Build Coastguard Worker
585*1fa6dee9SAndroid Build Coastguard Worker	if dest, isSymlink := m.symlinks[name]; isSymlink {
586*1fa6dee9SAndroid Build Coastguard Worker		return dest, nil
587*1fa6dee9SAndroid Build Coastguard Worker	}
588*1fa6dee9SAndroid Build Coastguard Worker
589*1fa6dee9SAndroid Build Coastguard Worker	if exists, _, err := m.Exists(name); err != nil {
590*1fa6dee9SAndroid Build Coastguard Worker		return "", err
591*1fa6dee9SAndroid Build Coastguard Worker	} else if !exists {
592*1fa6dee9SAndroid Build Coastguard Worker		return "", os.ErrNotExist
593*1fa6dee9SAndroid Build Coastguard Worker	} else {
594*1fa6dee9SAndroid Build Coastguard Worker		return "", os.NewSyscallError("readlink: "+origName, syscall.EINVAL)
595*1fa6dee9SAndroid Build Coastguard Worker	}
596*1fa6dee9SAndroid Build Coastguard Worker}
597*1fa6dee9SAndroid Build Coastguard Worker
598*1fa6dee9SAndroid Build Coastguard Workerfunc listDirsRecursive(fs FileSystem, name string, follow ShouldFollowSymlinks) ([]string, error) {
599*1fa6dee9SAndroid Build Coastguard Worker	name = filepath.Clean(name)
600*1fa6dee9SAndroid Build Coastguard Worker
601*1fa6dee9SAndroid Build Coastguard Worker	isDir, err := fs.IsDir(name)
602*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
603*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
604*1fa6dee9SAndroid Build Coastguard Worker	}
605*1fa6dee9SAndroid Build Coastguard Worker
606*1fa6dee9SAndroid Build Coastguard Worker	if !isDir {
607*1fa6dee9SAndroid Build Coastguard Worker		return nil, nil
608*1fa6dee9SAndroid Build Coastguard Worker	}
609*1fa6dee9SAndroid Build Coastguard Worker
610*1fa6dee9SAndroid Build Coastguard Worker	dirs := []string{name}
611*1fa6dee9SAndroid Build Coastguard Worker
612*1fa6dee9SAndroid Build Coastguard Worker	subDirs, err := listDirsRecursiveRelative(fs, name, follow, 0)
613*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
614*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
615*1fa6dee9SAndroid Build Coastguard Worker	}
616*1fa6dee9SAndroid Build Coastguard Worker
617*1fa6dee9SAndroid Build Coastguard Worker	for _, d := range subDirs {
618*1fa6dee9SAndroid Build Coastguard Worker		dirs = append(dirs, filepath.Join(name, d))
619*1fa6dee9SAndroid Build Coastguard Worker	}
620*1fa6dee9SAndroid Build Coastguard Worker
621*1fa6dee9SAndroid Build Coastguard Worker	return dirs, nil
622*1fa6dee9SAndroid Build Coastguard Worker}
623*1fa6dee9SAndroid Build Coastguard Worker
624*1fa6dee9SAndroid Build Coastguard Workerfunc listDirsRecursiveRelative(fs FileSystem, name string, follow ShouldFollowSymlinks, depth int) ([]string, error) {
625*1fa6dee9SAndroid Build Coastguard Worker	depth++
626*1fa6dee9SAndroid Build Coastguard Worker	if depth > 255 {
627*1fa6dee9SAndroid Build Coastguard Worker		return nil, fmt.Errorf("too many symlinks")
628*1fa6dee9SAndroid Build Coastguard Worker	}
629*1fa6dee9SAndroid Build Coastguard Worker	contents, err := fs.ReadDirNames(name)
630*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
631*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
632*1fa6dee9SAndroid Build Coastguard Worker	}
633*1fa6dee9SAndroid Build Coastguard Worker
634*1fa6dee9SAndroid Build Coastguard Worker	var dirs []string
635*1fa6dee9SAndroid Build Coastguard Worker	for _, f := range contents {
636*1fa6dee9SAndroid Build Coastguard Worker		if f[0] == '.' {
637*1fa6dee9SAndroid Build Coastguard Worker			continue
638*1fa6dee9SAndroid Build Coastguard Worker		}
639*1fa6dee9SAndroid Build Coastguard Worker		f = filepath.Join(name, f)
640*1fa6dee9SAndroid Build Coastguard Worker		var info os.FileInfo
641*1fa6dee9SAndroid Build Coastguard Worker		if follow == DontFollowSymlinks {
642*1fa6dee9SAndroid Build Coastguard Worker			info, err = fs.Lstat(f)
643*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
644*1fa6dee9SAndroid Build Coastguard Worker				continue
645*1fa6dee9SAndroid Build Coastguard Worker			}
646*1fa6dee9SAndroid Build Coastguard Worker			if info.Mode()&os.ModeSymlink != 0 {
647*1fa6dee9SAndroid Build Coastguard Worker				continue
648*1fa6dee9SAndroid Build Coastguard Worker			}
649*1fa6dee9SAndroid Build Coastguard Worker		} else {
650*1fa6dee9SAndroid Build Coastguard Worker			info, err = fs.Stat(f)
651*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
652*1fa6dee9SAndroid Build Coastguard Worker				continue
653*1fa6dee9SAndroid Build Coastguard Worker			}
654*1fa6dee9SAndroid Build Coastguard Worker		}
655*1fa6dee9SAndroid Build Coastguard Worker		if info.IsDir() {
656*1fa6dee9SAndroid Build Coastguard Worker			dirs = append(dirs, f)
657*1fa6dee9SAndroid Build Coastguard Worker			subDirs, err := listDirsRecursiveRelative(fs, f, follow, depth)
658*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
659*1fa6dee9SAndroid Build Coastguard Worker				return nil, err
660*1fa6dee9SAndroid Build Coastguard Worker			}
661*1fa6dee9SAndroid Build Coastguard Worker			for _, s := range subDirs {
662*1fa6dee9SAndroid Build Coastguard Worker				dirs = append(dirs, filepath.Join(f, s))
663*1fa6dee9SAndroid Build Coastguard Worker			}
664*1fa6dee9SAndroid Build Coastguard Worker		}
665*1fa6dee9SAndroid Build Coastguard Worker	}
666*1fa6dee9SAndroid Build Coastguard Worker
667*1fa6dee9SAndroid Build Coastguard Worker	for i, d := range dirs {
668*1fa6dee9SAndroid Build Coastguard Worker		rel, err := filepath.Rel(name, d)
669*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
670*1fa6dee9SAndroid Build Coastguard Worker			return nil, err
671*1fa6dee9SAndroid Build Coastguard Worker		}
672*1fa6dee9SAndroid Build Coastguard Worker		dirs[i] = rel
673*1fa6dee9SAndroid Build Coastguard Worker	}
674*1fa6dee9SAndroid Build Coastguard Worker
675*1fa6dee9SAndroid Build Coastguard Worker	return dirs, nil
676*1fa6dee9SAndroid Build Coastguard Worker}
677