-
-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Back to Go1.13, see #388, REVERT BACK LATER
- Loading branch information
Showing
4 changed files
with
259 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,233 @@ | ||
package main | ||
|
||
import ( | ||
"errors" | ||
"os" | ||
"path" | ||
"sort" | ||
) | ||
|
||
// An FS provides access to a hierarchical file system. | ||
// | ||
// The FS interface is the minimum implementation required of the file system. | ||
// A file system may implement additional interfaces, | ||
// such as ReadFileFS, to provide additional or optimized functionality. | ||
type FS interface { | ||
// Open opens the named file. | ||
// | ||
// When Open returns an error, it should be of type *PathError | ||
// with the Op field set to "open", the Path field set to name, | ||
// and the Err field describing the problem. | ||
// | ||
// Open should reject attempts to open names that do not satisfy | ||
// ValidPath(name), returning a *PathError with Err set to | ||
// ErrInvalid or ErrNotExist. | ||
Open(name string) (*os.File, error) | ||
Stat(name string) (os.FileInfo, error) | ||
} | ||
|
||
// ReadDir reads the named directory, | ||
// returning all its directory entries sorted by filename. | ||
// If an error occurs reading the directory, | ||
// ReadDir returns the entries it was able to read before the error, | ||
// along with the error. | ||
func ReadDir(fsys FS, name string) ([]DirEntry, error) { | ||
f, err := fsys.Open(name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer f.Close() | ||
|
||
infos, err := f.Readdir(-1) | ||
dirs := make([]DirEntry, len(infos)) | ||
for i, info := range infos { | ||
dirs[i] = &statDirEntry{info} | ||
} | ||
sort.Slice(dirs, func(i, j int) bool { return dirs[i].Name() < dirs[j].Name() }) | ||
return dirs, err | ||
} | ||
|
||
// DirFS returns a file system (an fs.FS) for the tree of files rooted at the directory dir. | ||
// | ||
// Note that DirFS("/prefix") only guarantees that the Open calls it makes to the | ||
// operating system will begin with "/prefix": DirFS("/prefix").Open("file") is the | ||
// same as os.Open("/prefix/file"). So if /prefix/file is a symbolic link pointing outside | ||
// the /prefix tree, then using DirFS does not stop the access any more than using | ||
// os.Open does. DirFS is therefore not a general substitute for a chroot-style security | ||
// mechanism when the directory tree contains arbitrary content. | ||
func DirFS(dir string) FS { | ||
return dirFS(dir) | ||
} | ||
|
||
func containsAny(s, chars string) bool { | ||
for i := 0; i < len(s); i++ { | ||
for j := 0; j < len(chars); j++ { | ||
if s[i] == chars[j] { | ||
return true | ||
} | ||
} | ||
} | ||
return false | ||
} | ||
|
||
type dirFS string | ||
|
||
func (dir dirFS) Open(name string) (*os.File, error) { | ||
//if !fs.ValidPath(name) || runtime.GOOS == "windows" && containsAny(name, `\:`) { | ||
// return nil, &PathError{Op: "open", Path: name, Err: ErrInvalid} | ||
//} | ||
f, err := os.Open(string(dir) + "/" + name) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return f, nil | ||
} | ||
|
||
func (dir dirFS) Stat(name string) (os.FileInfo, error) { | ||
info, err := os.Stat(string(dir) + "/" + name) | ||
if err != nil { | ||
return info, err | ||
} | ||
return info, nil | ||
} | ||
|
||
// SkipDir is used as a return value from WalkDirFuncs to indicate that | ||
// the directory named in the call is to be skipped. It is not returned | ||
// as an error by any function. | ||
var SkipDir = errors.New("skip this directory") | ||
|
||
// WalkDirFunc is the type of the function called by WalkDir to visit | ||
// each file or directory. | ||
// | ||
// The path argument contains the argument to WalkDir as a prefix. | ||
// That is, if WalkDir is called with root argument "dir" and finds a file | ||
// named "a" in that directory, the walk function will be called with | ||
// argument "dir/a". | ||
// | ||
// The d argument is the fs.DirEntry for the named path. | ||
// | ||
// The error result returned by the function controls how WalkDir | ||
// continues. If the function returns the special value SkipDir, WalkDir | ||
// skips the current directory (path if d.IsDir() is true, otherwise | ||
// path's parent directory). Otherwise, if the function returns a non-nil | ||
// error, WalkDir stops entirely and returns that error. | ||
// | ||
// The err argument reports an error related to path, signaling that | ||
// WalkDir will not walk into that directory. The function can decide how | ||
// to handle that error; as described earlier, returning the error will | ||
// cause WalkDir to stop walking the entire tree. | ||
// | ||
// WalkDir calls the function with a non-nil err argument in two cases. | ||
// | ||
// First, if the initial fs.Stat on the root directory fails, WalkDir | ||
// calls the function with path set to root, d set to nil, and err set to | ||
// the error from fs.Stat. | ||
// | ||
// Second, if a directory's ReadDir method fails, WalkDir calls the | ||
// function with path set to the directory's path, d set to an | ||
// fs.DirEntry describing the directory, and err set to the error from | ||
// ReadDir. In this second case, the function is called twice with the | ||
// path of the directory: the first call is before the directory read is | ||
// attempted and has err set to nil, giving the function a chance to | ||
// return SkipDir and avoid the ReadDir entirely. The second call is | ||
// after a failed ReadDir and reports the error from ReadDir. | ||
// (If ReadDir succeeds, there is no second call.) | ||
// | ||
// The differences between WalkDirFunc compared to filepath.WalkFunc are: | ||
// | ||
// - The second argument has type fs.DirEntry instead of fs.FileInfo. | ||
// - The function is called before reading a directory, to allow SkipDir | ||
// to bypass the directory read entirely. | ||
// - If a directory read fails, the function is called a second time | ||
// for that directory to report the error. | ||
// | ||
type WalkDirFunc func(path string, d DirEntry, err error) error | ||
|
||
// walkDir recursively descends path, calling walkDirFn. | ||
func walkDir(fsys FS, name string, d DirEntry, walkDirFn WalkDirFunc) error { | ||
if err := walkDirFn(name, d, nil); err != nil || !d.IsDir() { | ||
if err == SkipDir && d.IsDir() { | ||
// Successfully skipped directory. | ||
err = nil | ||
} | ||
return err | ||
} | ||
|
||
dirs, err := ReadDir(fsys, name) | ||
if err != nil { | ||
// Second call, to report ReadDir error. | ||
err = walkDirFn(name, d, err) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
|
||
for _, d1 := range dirs { | ||
name1 := path.Join(name, d1.Name()) | ||
if err := walkDir(fsys, name1, d1, walkDirFn); err != nil { | ||
if err == SkipDir { | ||
break | ||
} | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// WalkDir walks the file tree rooted at root, calling fn for each file or | ||
// directory in the tree, including root. | ||
// | ||
// All errors that arise visiting files and directories are filtered by fn: | ||
// see the fs.WalkDirFunc documentation for details. | ||
// | ||
// The files are walked in lexical order, which makes the output deterministic | ||
// but requires WalkDir to read an entire directory into memory before proceeding | ||
// to walk that directory. | ||
// | ||
// WalkDir does not follow symbolic links found in directories, | ||
// but if root itself is a symbolic link, its target will be walked. | ||
func WalkDir(fsys FS, root string, fn WalkDirFunc) error { | ||
info, err := fsys.Stat(root) | ||
if err != nil { | ||
err = fn(root, nil, err) | ||
} else { | ||
err = walkDir(fsys, root, &statDirEntry{info}, fn) | ||
} | ||
if err == SkipDir { | ||
return nil | ||
} | ||
return err | ||
} | ||
|
||
// A DirEntry is an entry read from a directory | ||
// (using the ReadDir function or a ReadDirFile's ReadDir method). | ||
type DirEntry interface { | ||
// Name returns the name of the file (or subdirectory) described by the entry. | ||
// This name is only the final element of the path (the base name), not the entire path. | ||
// For example, Name would return "hello.go" not "/home/gopher/hello.go". | ||
Name() string | ||
|
||
// IsDir reports whether the entry describes a directory. | ||
IsDir() bool | ||
|
||
// Type returns the type bits for the entry. | ||
// The type bits are a subset of the usual FileMode bits, those returned by the FileMode.Type method. | ||
Type() os.FileMode | ||
|
||
// Info returns the FileInfo for the file or subdirectory described by the entry. | ||
// The returned FileInfo may be from the time of the original directory read | ||
// or from the time of the call to Info. If the file has been removed or renamed | ||
// since the directory read, Info may return an error satisfying errors.Is(err, ErrNotExist). | ||
// If the entry denotes a symbolic link, Info reports the information about the link itself, | ||
// not the link's target. | ||
Info() (os.FileInfo, error) | ||
} | ||
|
||
type statDirEntry struct { | ||
info os.FileInfo | ||
} | ||
|
||
func (d *statDirEntry) Name() string { return d.info.Name() } | ||
func (d *statDirEntry) IsDir() bool { return d.info.IsDir() } | ||
func (d *statDirEntry) Type() os.FileMode { return d.info.Mode() & os.ModeType } | ||
func (d *statDirEntry) Info() (os.FileInfo, error) { return d.info, nil } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters