// Copyright © 2014 Steve Francia . // Copyright 2013 tsuru authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package afero provides types and methods for interacting with the filesystem, // as an abstraction layer. // Afero also provides a few implementations that are mostly interoperable. One that // uses the operating system filesystem, one that uses memory to store files // (cross platform) and an interface that should be implemented if you want to // provide your own filesystem. package afero import ( "errors" "io" "os" "path/filepath" "sort" "time" ) // File represents a file in the filesystem. type File interface { io.Closer io.Reader io.ReaderAt io.Seeker io.Writer io.WriterAt //Fd() uintptr Stat() (os.FileInfo, error) Readdir(count int) ([]os.FileInfo, error) Readdirnames(n int) ([]string, error) WriteString(s string) (ret int, err error) Truncate(size int64) error Name() string } // Fs is the filesystem interface. // // Any simulated or real filesystem should implement this interface. type Fs interface { // Create creates a file in the filesystem, returning the file and an // error, if any happens. Create(name string) (File, error) // Mkdir creates a directory in the filesystem, return an error if any // happens. Mkdir(name string, perm os.FileMode) error // MkdirAll creates a directory path and all parents that does not exist // yet. MkdirAll(path string, perm os.FileMode) error // Open opens a file, returning it or an error, if any happens. Open(name string) (File, error) // OpenFile opens a file using the given flags and the given mode. OpenFile(name string, flag int, perm os.FileMode) (File, error) // Remove removes a file identified by name, returning an error, if any // happens. Remove(name string) error // RemoveAll removes a directory path and all any children it contains. It // does not fail if the path does not exist (return nil). RemoveAll(path string) error // Rename renames a file. Rename(oldname, newname string) error // Stat returns a FileInfo describing the named file, or an error, if any // happens. Stat(name string) (os.FileInfo, error) // The name of this FileSystem Name() string //Chmod changes the mode of the named file to mode. Chmod(name string, mode os.FileMode) error //Chtimes changes the access and modification times of the named file Chtimes(name string, atime time.Time, mtime time.Time) error } var ( ErrFileClosed = errors.New("File is closed") ErrOutOfRange = errors.New("Out of range") ErrTooLarge = errors.New("Too large") ErrFileNotFound = os.ErrNotExist ErrFileExists = os.ErrExist ErrDestinationExists = os.ErrExist ) // ReadDir reads the directory named by dirname and returns // a list of sorted directory entries. func ReadDir(dirname string, fs Fs) ([]os.FileInfo, error) { f, err := fs.Open(dirname) if err != nil { return nil, err } list, err := f.Readdir(-1) f.Close() if err != nil { return nil, err } sort.Sort(byName(list)) return list, nil } // readDirNames reads the directory named by dirname and returns // a sorted list of directory entries. // adapted from https://golang.org/src/path/filepath/path.go func readDirNames(dirname string, fs Fs) ([]string, error) { f, err := fs.Open(dirname) if err != nil { return nil, err } names, err := f.Readdirnames(-1) f.Close() if err != nil { return nil, err } sort.Strings(names) return names, nil } // walk recursively descends path, calling walkFn // adapted from https://golang.org/src/path/filepath/path.go func walk(path string, info os.FileInfo, walkFn filepath.WalkFunc, fs Fs) error { err := walkFn(path, info, nil) if err != nil { if info.IsDir() && err == filepath.SkipDir { return nil } return err } if !info.IsDir() { return nil } names, err := readDirNames(path, fs) if err != nil { return walkFn(path, info, err) } for _, name := range names { filename := filepath.Join(path, name) fileInfo, err := lstatIfOs(filename, fs) if err != nil { if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir { return err } } else { err = walk(filename, fileInfo, walkFn, fs) if err != nil { if !fileInfo.IsDir() || err != filepath.SkipDir { return err } } } } return nil } // if the filesystem is OsFs use Lstat, else use fs.Stat func lstatIfOs(path string, fs Fs) (info os.FileInfo, err error) { _, ok := fs.(*OsFs) if ok { info, err = os.Lstat(path) } else { info, err = fs.Stat(path) } return } // Walk walks the file tree rooted at root, calling walkFn for each file or // directory in the tree, including root. All errors that arise visiting files // and directories are filtered by walkFn. The files are walked in lexical // order, which makes the output deterministic but means that for very // large directories Walk can be inefficient. // Walk does not follow symbolic links. func Walk(root string, walkFn filepath.WalkFunc, fs Fs) error { info, err := lstatIfOs(root, fs) if err != nil { return walkFn(root, nil, err) } return walk(root, info, walkFn, fs) } // byName implements sort.Interface. type byName []os.FileInfo func (f byName) Len() int { return len(f) } func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }