package vfscopy import ( "os" "io" "path/filepath" "path" "strings" "errors" ) // FileSystem is copied from http.FileSystem type FileSystem interface { Open(name string) (File, error) } // File is copied from http.File type File interface { io.Closer io.Reader io.Seeker Readdir(count int) ([]os.FileInfo, error) Stat() (os.FileInfo, error) } // Dir is an implementation of a Virtual FileSystem type Dir string // mapDirOpenError maps the provided non-nil error from opening name // to a possibly better non-nil error. In particular, it turns OS-specific errors // about opening files in non-directories into os.ErrNotExist. See Issue 18984. func mapDirOpenError(originalErr error, name string) error { if os.IsNotExist(originalErr) || os.IsPermission(originalErr) { return originalErr } parts := strings.Split(name, string(filepath.Separator)) for i := range parts { if parts[i] == "" { continue } fi, err := os.Stat(strings.Join(parts[:i+1], string(filepath.Separator))) if err != nil { return originalErr } if !fi.IsDir() { return os.ErrNotExist } } return originalErr } // Open opens a file relative to a virtual filesystem func (d Dir) Open(name string) (File, error) { if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) { return nil, errors.New("http: invalid character in file path") } dir := string(d) if dir == "" { dir = "." } fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name))) f, err := os.Open(fullName) if err != nil { return nil, mapDirOpenError(err, fullName) } return f, nil }