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