internal/godoc: remove vfs
Change-Id: I508d92508553be76e42b4066e540c146e1c9ba53
Reviewed-on: https://go-review.googlesource.com/c/website/+/293490
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
diff --git a/internal/godoc/vfs/emptyvfs.go b/internal/godoc/vfs/emptyvfs.go
deleted file mode 100644
index 24954b1..0000000
--- a/internal/godoc/vfs/emptyvfs.go
+++ /dev/null
@@ -1,85 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vfs
-
-import (
- "fmt"
- "os"
- "time"
-)
-
-// NewNameSpace returns a NameSpace pre-initialized with an empty
-// emulated directory mounted on the root mount point "/". This
-// allows directory traversal routines to work properly even if
-// a folder is not explicitly mounted at root by the user.
-func NewNameSpace() NameSpace {
- ns := NameSpace{}
- ns.Bind("/", &emptyVFS{}, "/", BindReplace)
- return ns
-}
-
-// type emptyVFS emulates a FileSystem consisting of an empty directory
-type emptyVFS struct{}
-
-// Open implements Opener. Since emptyVFS is an empty directory, all
-// attempts to open a file should returns errors.
-func (e *emptyVFS) Open(path string) (ReadSeekCloser, error) {
- if path == "/" {
- return nil, fmt.Errorf("open: / is a directory")
- }
- return nil, os.ErrNotExist
-}
-
-// Stat returns os.FileInfo for an empty directory if the path is
-// is root "/" or error. os.FileInfo is implemented by emptyVFS
-func (e *emptyVFS) Stat(path string) (os.FileInfo, error) {
- if path == "/" {
- return e, nil
- }
- return nil, os.ErrNotExist
-}
-
-func (e *emptyVFS) Lstat(path string) (os.FileInfo, error) {
- return e.Stat(path)
-}
-
-// ReadDir returns an empty os.FileInfo slice for "/", else error.
-func (e *emptyVFS) ReadDir(path string) ([]os.FileInfo, error) {
- if path == "/" {
- return []os.FileInfo{}, nil
- }
- return nil, os.ErrNotExist
-}
-
-func (e *emptyVFS) String() string {
- return "emptyVFS(/)"
-}
-
-// These functions below implement os.FileInfo for the single
-// empty emulated directory.
-
-func (e *emptyVFS) Name() string {
- return "/"
-}
-
-func (e *emptyVFS) Size() int64 {
- return 0
-}
-
-func (e *emptyVFS) Mode() os.FileMode {
- return os.ModeDir | os.ModePerm
-}
-
-func (e *emptyVFS) ModTime() time.Time {
- return time.Time{}
-}
-
-func (e *emptyVFS) IsDir() bool {
- return true
-}
-
-func (e *emptyVFS) Sys() interface{} {
- return nil
-}
diff --git a/internal/godoc/vfs/fs.go b/internal/godoc/vfs/fs.go
deleted file mode 100644
index 2f122b9..0000000
--- a/internal/godoc/vfs/fs.go
+++ /dev/null
@@ -1,78 +0,0 @@
-// Copyright 2021 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-//go:build go1.16
-// +build go1.16
-
-package vfs
-
-import (
- "io/fs"
- "os"
- "path"
- "strings"
-)
-
-// FromFS converts an fs.FS to the FileSystem interface.
-func FromFS(fsys fs.FS) FileSystem {
- return &fsysToFileSystem{fsys}
-}
-
-type fsysToFileSystem struct {
- fsys fs.FS
-}
-
-func (f *fsysToFileSystem) fsPath(name string) string {
- name = path.Clean(name)
- if name == "/" {
- return "."
- }
- return strings.TrimPrefix(name, "/")
-}
-
-func (f *fsysToFileSystem) Open(name string) (ReadSeekCloser, error) {
- file, err := f.fsys.Open(f.fsPath(name))
- if err != nil {
- return nil, err
- }
- if rsc, ok := file.(ReadSeekCloser); ok {
- return rsc, nil
- }
- return &noSeekFile{f.fsPath(name), file}, nil
-}
-
-func (f *fsysToFileSystem) Lstat(name string) (os.FileInfo, error) {
- return fs.Stat(f.fsys, f.fsPath(name))
-}
-
-func (f *fsysToFileSystem) Stat(name string) (os.FileInfo, error) {
- return fs.Stat(f.fsys, f.fsPath(name))
-}
-
-func (f *fsysToFileSystem) ReadDir(name string) ([]os.FileInfo, error) {
- dirs, err := fs.ReadDir(f.fsys, f.fsPath(name))
- var infos []os.FileInfo
- for _, d := range dirs {
- info, err1 := d.Info()
- if err1 != nil {
- if err == nil {
- err = err1
- }
- continue
- }
- infos = append(infos, info)
- }
- return infos, err
-}
-
-func (f *fsysToFileSystem) String() string { return "io/fs" }
-
-type noSeekFile struct {
- path string
- fs.File
-}
-
-func (f *noSeekFile) Seek(offset int64, whence int) (int64, error) {
- return 0, &fs.PathError{Op: "seek", Path: f.path, Err: fs.ErrInvalid}
-}
diff --git a/internal/godoc/vfs/gatefs/gatefs.go b/internal/godoc/vfs/gatefs/gatefs.go
deleted file mode 100644
index 1c2d8fa..0000000
--- a/internal/godoc/vfs/gatefs/gatefs.go
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package gatefs provides an implementation of the FileSystem
-// interface that wraps another FileSystem and limits its concurrency.
-package gatefs // import "golang.org/x/website/internal/godoc/vfs/gatefs"
-
-import (
- "fmt"
- "os"
-
- "golang.org/x/website/internal/godoc/vfs"
-)
-
-// New returns a new FileSystem that delegates to fs.
-// If gateCh is non-nil and buffered, it's used as a gate
-// to limit concurrency on calls to fs.
-func New(fs vfs.FileSystem, gateCh chan bool) vfs.FileSystem {
- if cap(gateCh) == 0 {
- return fs
- }
- return gatefs{fs, gate(gateCh)}
-}
-
-type gate chan bool
-
-func (g gate) enter() { g <- true }
-func (g gate) leave() { <-g }
-
-type gatefs struct {
- fs vfs.FileSystem
- gate
-}
-
-func (fs gatefs) String() string {
- return fmt.Sprintf("gated(%s, %d)", fs.fs.String(), cap(fs.gate))
-}
-
-func (fs gatefs) Open(p string) (vfs.ReadSeekCloser, error) {
- fs.enter()
- defer fs.leave()
- rsc, err := fs.fs.Open(p)
- if err != nil {
- return nil, err
- }
- return gatef{rsc, fs.gate}, nil
-}
-
-func (fs gatefs) Lstat(p string) (os.FileInfo, error) {
- fs.enter()
- defer fs.leave()
- return fs.fs.Lstat(p)
-}
-
-func (fs gatefs) Stat(p string) (os.FileInfo, error) {
- fs.enter()
- defer fs.leave()
- return fs.fs.Stat(p)
-}
-
-func (fs gatefs) ReadDir(p string) ([]os.FileInfo, error) {
- fs.enter()
- defer fs.leave()
- return fs.fs.ReadDir(p)
-}
-
-type gatef struct {
- rsc vfs.ReadSeekCloser
- gate
-}
-
-func (f gatef) Read(p []byte) (n int, err error) {
- f.enter()
- defer f.leave()
- return f.rsc.Read(p)
-}
-
-func (f gatef) Seek(offset int64, whence int) (ret int64, err error) {
- f.enter()
- defer f.leave()
- return f.rsc.Seek(offset, whence)
-}
-
-func (f gatef) Close() error {
- f.enter()
- defer f.leave()
- return f.rsc.Close()
-}
diff --git a/internal/godoc/vfs/httpfs/httpfs.go b/internal/godoc/vfs/httpfs/httpfs.go
deleted file mode 100644
index 292366a..0000000
--- a/internal/godoc/vfs/httpfs/httpfs.go
+++ /dev/null
@@ -1,94 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package httpfs implements http.FileSystem using a godoc vfs.FileSystem.
-package httpfs // import "golang.org/x/website/internal/godoc/vfs/httpfs"
-
-import (
- "fmt"
- "io"
- "net/http"
- "os"
-
- "golang.org/x/website/internal/godoc/vfs"
-)
-
-func New(fs vfs.FileSystem) http.FileSystem {
- return &httpFS{fs}
-}
-
-type httpFS struct {
- fs vfs.FileSystem
-}
-
-func (h *httpFS) Open(name string) (http.File, error) {
- fi, err := h.fs.Stat(name)
- if err != nil {
- return nil, err
- }
- if fi.IsDir() {
- return &httpDir{h.fs, name, nil}, nil
- }
- f, err := h.fs.Open(name)
- if err != nil {
- return nil, err
- }
- return &httpFile{h.fs, f, name}, nil
-}
-
-// httpDir implements http.File for a directory in a FileSystem.
-type httpDir struct {
- fs vfs.FileSystem
- name string
- pending []os.FileInfo
-}
-
-func (h *httpDir) Close() error { return nil }
-func (h *httpDir) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) }
-func (h *httpDir) Read([]byte) (int, error) {
- return 0, fmt.Errorf("cannot Read from directory %s", h.name)
-}
-
-func (h *httpDir) Seek(offset int64, whence int) (int64, error) {
- if offset == 0 && whence == 0 {
- h.pending = nil
- return 0, nil
- }
- return 0, fmt.Errorf("unsupported Seek in directory %s", h.name)
-}
-
-func (h *httpDir) Readdir(count int) ([]os.FileInfo, error) {
- if h.pending == nil {
- d, err := h.fs.ReadDir(h.name)
- if err != nil {
- return nil, err
- }
- if d == nil {
- d = []os.FileInfo{} // not nil
- }
- h.pending = d
- }
-
- if len(h.pending) == 0 && count > 0 {
- return nil, io.EOF
- }
- if count <= 0 || count > len(h.pending) {
- count = len(h.pending)
- }
- d := h.pending[:count]
- h.pending = h.pending[count:]
- return d, nil
-}
-
-// httpFile implements http.File for a file (not directory) in a FileSystem.
-type httpFile struct {
- fs vfs.FileSystem
- vfs.ReadSeekCloser
- name string
-}
-
-func (h *httpFile) Stat() (os.FileInfo, error) { return h.fs.Stat(h.name) }
-func (h *httpFile) Readdir(int) ([]os.FileInfo, error) {
- return nil, fmt.Errorf("cannot Readdir from file %s", h.name)
-}
diff --git a/internal/godoc/vfs/mapfs/mapfs.go b/internal/godoc/vfs/mapfs/mapfs.go
deleted file mode 100644
index e0aebe3..0000000
--- a/internal/godoc/vfs/mapfs/mapfs.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package mapfs file provides an implementation of the FileSystem
-// interface based on the contents of a map[string]string.
-package mapfs // import "golang.org/x/website/internal/godoc/vfs/mapfs"
-
-import (
- "fmt"
- "io"
- "os"
- pathpkg "path"
- "sort"
- "strings"
- "time"
-
- "golang.org/x/website/internal/godoc/vfs"
-)
-
-// New returns a new FileSystem from the provided map.
-// Map keys must be forward slash-separated paths with
-// no leading slash, such as "file1.txt" or "dir/file2.txt".
-// New panics if any of the paths contain a leading slash.
-func New(m map[string]string) vfs.FileSystem {
- // Verify all provided paths are relative before proceeding.
- var pathsWithLeadingSlash []string
- for p := range m {
- if strings.HasPrefix(p, "/") {
- pathsWithLeadingSlash = append(pathsWithLeadingSlash, p)
- }
- }
- if len(pathsWithLeadingSlash) > 0 {
- panic(fmt.Errorf("mapfs.New: invalid paths with a leading slash: %q", pathsWithLeadingSlash))
- }
-
- return mapFS(m)
-}
-
-// mapFS is the map based implementation of FileSystem
-type mapFS map[string]string
-
-func (fs mapFS) String() string { return "mapfs" }
-
-func (fs mapFS) Close() error { return nil }
-
-func filename(p string) string {
- return strings.TrimPrefix(p, "/")
-}
-
-func (fs mapFS) Open(p string) (vfs.ReadSeekCloser, error) {
- b, ok := fs[filename(p)]
- if !ok {
- return nil, os.ErrNotExist
- }
- return nopCloser{strings.NewReader(b)}, nil
-}
-
-func fileInfo(name, contents string) os.FileInfo {
- return mapFI{name: pathpkg.Base(name), size: len(contents)}
-}
-
-func dirInfo(name string) os.FileInfo {
- return mapFI{name: pathpkg.Base(name), dir: true}
-}
-
-func (fs mapFS) Lstat(p string) (os.FileInfo, error) {
- b, ok := fs[filename(p)]
- if ok {
- return fileInfo(p, b), nil
- }
- ents, _ := fs.ReadDir(p)
- if len(ents) > 0 {
- return dirInfo(p), nil
- }
- return nil, os.ErrNotExist
-}
-
-func (fs mapFS) Stat(p string) (os.FileInfo, error) {
- return fs.Lstat(p)
-}
-
-// slashdir returns path.Dir(p), but special-cases paths not beginning
-// with a slash to be in the root.
-func slashdir(p string) string {
- d := pathpkg.Dir(p)
- if d == "." {
- return "/"
- }
- if strings.HasPrefix(p, "/") {
- return d
- }
- return "/" + d
-}
-
-func (fs mapFS) ReadDir(p string) ([]os.FileInfo, error) {
- p = pathpkg.Clean(p)
- var ents []string
- fim := make(map[string]os.FileInfo) // base -> fi
- for fn, b := range fs {
- dir := slashdir(fn)
- isFile := true
- var lastBase string
- for {
- if dir == p {
- base := lastBase
- if isFile {
- base = pathpkg.Base(fn)
- }
- if fim[base] == nil {
- var fi os.FileInfo
- if isFile {
- fi = fileInfo(fn, b)
- } else {
- fi = dirInfo(base)
- }
- ents = append(ents, base)
- fim[base] = fi
- }
- }
- if dir == "/" {
- break
- } else {
- isFile = false
- lastBase = pathpkg.Base(dir)
- dir = pathpkg.Dir(dir)
- }
- }
- }
- if len(ents) == 0 {
- return nil, os.ErrNotExist
- }
-
- sort.Strings(ents)
- var list []os.FileInfo
- for _, dir := range ents {
- list = append(list, fim[dir])
- }
- return list, nil
-}
-
-// mapFI is the map-based implementation of FileInfo.
-type mapFI struct {
- name string
- size int
- dir bool
-}
-
-func (fi mapFI) IsDir() bool { return fi.dir }
-func (fi mapFI) ModTime() time.Time { return time.Time{} }
-func (fi mapFI) Mode() os.FileMode {
- if fi.IsDir() {
- return 0755 | os.ModeDir
- }
- return 0444
-}
-func (fi mapFI) Name() string { return pathpkg.Base(fi.name) }
-func (fi mapFI) Size() int64 { return int64(fi.size) }
-func (fi mapFI) Sys() interface{} { return nil }
-
-type nopCloser struct {
- io.ReadSeeker
-}
-
-func (nc nopCloser) Close() error { return nil }
diff --git a/internal/godoc/vfs/mapfs/mapfs_test.go b/internal/godoc/vfs/mapfs/mapfs_test.go
deleted file mode 100644
index 6b7db29..0000000
--- a/internal/godoc/vfs/mapfs/mapfs_test.go
+++ /dev/null
@@ -1,111 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package mapfs
-
-import (
- "io/ioutil"
- "os"
- "reflect"
- "testing"
-)
-
-func TestOpenRoot(t *testing.T) {
- fs := New(map[string]string{
- "foo/bar/three.txt": "a",
- "foo/bar.txt": "b",
- "top.txt": "c",
- "other-top.txt": "d",
- })
- tests := []struct {
- path string
- want string
- }{
- {"/foo/bar/three.txt", "a"},
- {"foo/bar/three.txt", "a"},
- {"foo/bar.txt", "b"},
- {"top.txt", "c"},
- {"/top.txt", "c"},
- {"other-top.txt", "d"},
- {"/other-top.txt", "d"},
- }
- for _, tt := range tests {
- rsc, err := fs.Open(tt.path)
- if err != nil {
- t.Errorf("Open(%q) = %v", tt.path, err)
- continue
- }
- slurp, err := ioutil.ReadAll(rsc)
- if err != nil {
- t.Error(err)
- }
- if string(slurp) != tt.want {
- t.Errorf("Read(%q) = %q; want %q", tt.path, tt.want, slurp)
- }
- rsc.Close()
- }
-
- _, err := fs.Open("/xxxx")
- if !os.IsNotExist(err) {
- t.Errorf("ReadDir /xxxx = %v; want os.IsNotExist error", err)
- }
-}
-
-func TestReaddir(t *testing.T) {
- fs := New(map[string]string{
- "foo/bar/three.txt": "333",
- "foo/bar.txt": "22",
- "top.txt": "top.txt file",
- "other-top.txt": "other-top.txt file",
- })
- tests := []struct {
- dir string
- want []os.FileInfo
- }{
- {
- dir: "/",
- want: []os.FileInfo{
- mapFI{name: "foo", dir: true},
- mapFI{name: "other-top.txt", size: len("other-top.txt file")},
- mapFI{name: "top.txt", size: len("top.txt file")},
- },
- },
- {
- dir: "/foo",
- want: []os.FileInfo{
- mapFI{name: "bar", dir: true},
- mapFI{name: "bar.txt", size: 2},
- },
- },
- {
- dir: "/foo/",
- want: []os.FileInfo{
- mapFI{name: "bar", dir: true},
- mapFI{name: "bar.txt", size: 2},
- },
- },
- {
- dir: "/foo/bar",
- want: []os.FileInfo{
- mapFI{name: "three.txt", size: 3},
- },
- },
- }
- for _, tt := range tests {
- fis, err := fs.ReadDir(tt.dir)
- if err != nil {
- t.Errorf("ReadDir(%q) = %v", tt.dir, err)
- continue
- }
- if !reflect.DeepEqual(fis, tt.want) {
- t.Errorf("ReadDir(%q) = %#v; want %#v", tt.dir, fis, tt.want)
- continue
- }
- }
-
- _, err := fs.ReadDir("/xxxx")
- if !os.IsNotExist(err) {
- t.Errorf("ReadDir /xxxx = %v; want os.IsNotExist error", err)
- }
-}
diff --git a/internal/godoc/vfs/namespace.go b/internal/godoc/vfs/namespace.go
deleted file mode 100644
index 38a6358..0000000
--- a/internal/godoc/vfs/namespace.go
+++ /dev/null
@@ -1,375 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vfs
-
-import (
- "fmt"
- "io"
- "os"
- pathpkg "path"
- "sort"
- "strings"
- "time"
-)
-
-// Setting debugNS = true will enable debugging prints about
-// name space translations.
-const debugNS = false
-
-// A NameSpace is a file system made up of other file systems
-// mounted at specific locations in the name space.
-//
-// The representation is a map from mount point locations
-// to the list of file systems mounted at that location. A traditional
-// Unix mount table would use a single file system per mount point,
-// but we want to be able to mount multiple file systems on a single
-// mount point and have the system behave as if the union of those
-// file systems were present at the mount point.
-// For example, if the OS file system has a Go installation in
-// c:\Go and additional Go path trees in d:\Work1 and d:\Work2, then
-// this name space creates the view we want for the godoc server:
-//
-// NameSpace{
-// "/": {
-// {old: "/", fs: OS(`c:\Go`), new: "/"},
-// },
-// "/src/pkg": {
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-// },
-// }
-//
-// This is created by executing:
-//
-// ns := NameSpace{}
-// ns.Bind("/", OS(`c:\Go`), "/", BindReplace)
-// ns.Bind("/src/pkg", OS(`d:\Work1`), "/src", BindAfter)
-// ns.Bind("/src/pkg", OS(`d:\Work2`), "/src", BindAfter)
-//
-// A particular mount point entry is a triple (old, fs, new), meaning that to
-// operate on a path beginning with old, replace that prefix (old) with new
-// and then pass that path to the FileSystem implementation fs.
-//
-// If you do not explicitly mount a FileSystem at the root mountpoint "/" of the
-// NameSpace like above, Stat("/") will return a "not found" error which could
-// break typical directory traversal routines. In such cases, use NewNameSpace()
-// to get a NameSpace pre-initialized with an emulated empty directory at root.
-//
-// Given this name space, a ReadDir of /src/pkg/code will check each prefix
-// of the path for a mount point (first /src/pkg/code, then /src/pkg, then /src,
-// then /), stopping when it finds one. For the above example, /src/pkg/code
-// will find the mount point at /src/pkg:
-//
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-//
-// ReadDir will when execute these three calls and merge the results:
-//
-// OS(`c:\Go`).ReadDir("/src/pkg/code")
-// OS(`d:\Work1').ReadDir("/src/code")
-// OS(`d:\Work2').ReadDir("/src/code")
-//
-// Note that the "/src/pkg" in "/src/pkg/code" has been replaced by
-// just "/src" in the final two calls.
-//
-// OS is itself an implementation of a file system: it implements
-// OS(`c:\Go`).ReadDir("/src/pkg/code") as ioutil.ReadDir(`c:\Go\src\pkg\code`).
-//
-// Because the new path is evaluated by fs (here OS(root)), another way
-// to read the mount table is to mentally combine fs+new, so that this table:
-//
-// {old: "/src/pkg", fs: OS(`c:\Go`), new: "/src/pkg"},
-// {old: "/src/pkg", fs: OS(`d:\Work1`), new: "/src"},
-// {old: "/src/pkg", fs: OS(`d:\Work2`), new: "/src"},
-//
-// reads as:
-//
-// "/src/pkg" -> c:\Go\src\pkg
-// "/src/pkg" -> d:\Work1\src
-// "/src/pkg" -> d:\Work2\src
-//
-// An invariant (a redundancy) of the name space representation is that
-// ns[mtpt][i].old is always equal to mtpt (in the example, ns["/src/pkg"]'s
-// mount table entries always have old == "/src/pkg"). The 'old' field is
-// useful to callers, because they receive just a []mountedFS and not any
-// other indication of which mount point was found.
-//
-type NameSpace map[string][]mountedFS
-
-// A mountedFS handles requests for path by replacing
-// a prefix 'old' with 'new' and then calling the fs methods.
-type mountedFS struct {
- old string
- fs FileSystem
- new string
-}
-
-// hasPathPrefix reports whether x == y or x == y + "/" + more.
-func hasPathPrefix(x, y string) bool {
- return x == y || strings.HasPrefix(x, y) && (strings.HasSuffix(y, "/") || strings.HasPrefix(x[len(y):], "/"))
-}
-
-// translate translates path for use in m, replacing old with new.
-//
-// mountedFS{"/src/pkg", fs, "/src"}.translate("/src/pkg/code") == "/src/code".
-func (m mountedFS) translate(path string) string {
- path = pathpkg.Clean("/" + path)
- if !hasPathPrefix(path, m.old) {
- panic("translate " + path + " but old=" + m.old)
- }
- return pathpkg.Join(m.new, path[len(m.old):])
-}
-
-func (NameSpace) String() string {
- return "ns"
-}
-
-// Fprint writes a text representation of the name space to w.
-func (ns NameSpace) Fprint(w io.Writer) {
- fmt.Fprint(w, "name space {\n")
- var all []string
- for mtpt := range ns {
- all = append(all, mtpt)
- }
- sort.Strings(all)
- for _, mtpt := range all {
- fmt.Fprintf(w, "\t%s:\n", mtpt)
- for _, m := range ns[mtpt] {
- fmt.Fprintf(w, "\t\t%s %s\n", m.fs, m.new)
- }
- }
- fmt.Fprint(w, "}\n")
-}
-
-// clean returns a cleaned, rooted path for evaluation.
-// It canonicalizes the path so that we can use string operations
-// to analyze it.
-func (NameSpace) clean(path string) string {
- return pathpkg.Clean("/" + path)
-}
-
-type BindMode int
-
-const (
- BindReplace BindMode = iota
- BindBefore
- BindAfter
-)
-
-// Bind causes references to old to redirect to the path new in newfs.
-// If mode is BindReplace, old redirections are discarded.
-// If mode is BindBefore, this redirection takes priority over existing ones,
-// but earlier ones are still consulted for paths that do not exist in newfs.
-// If mode is BindAfter, this redirection happens only after existing ones
-// have been tried and failed.
-func (ns NameSpace) Bind(old string, newfs FileSystem, new string, mode BindMode) {
- old = ns.clean(old)
- new = ns.clean(new)
- m := mountedFS{old, newfs, new}
- var mtpt []mountedFS
- switch mode {
- case BindReplace:
- mtpt = append(mtpt, m)
- case BindAfter:
- mtpt = append(mtpt, ns.resolve(old)...)
- mtpt = append(mtpt, m)
- case BindBefore:
- mtpt = append(mtpt, m)
- mtpt = append(mtpt, ns.resolve(old)...)
- }
-
- // Extend m.old, m.new in inherited mount point entries.
- for i := range mtpt {
- m := &mtpt[i]
- if m.old != old {
- if !hasPathPrefix(old, m.old) {
- // This should not happen. If it does, panic so
- // that we can see the call trace that led to it.
- panic(fmt.Sprintf("invalid Bind: old=%q m={%q, %s, %q}", old, m.old, m.fs.String(), m.new))
- }
- suffix := old[len(m.old):]
- m.old = pathpkg.Join(m.old, suffix)
- m.new = pathpkg.Join(m.new, suffix)
- }
- }
-
- ns[old] = mtpt
-}
-
-// resolve resolves a path to the list of mountedFS to use for path.
-func (ns NameSpace) resolve(path string) []mountedFS {
- path = ns.clean(path)
- for {
- if m := ns[path]; m != nil {
- if debugNS {
- fmt.Printf("resolve %s: %v\n", path, m)
- }
- return m
- }
- if path == "/" {
- break
- }
- path = pathpkg.Dir(path)
- }
- return nil
-}
-
-// Open implements the FileSystem Open method.
-func (ns NameSpace) Open(path string) (ReadSeekCloser, error) {
- var err error
- for _, m := range ns.resolve(path) {
- if debugNS {
- fmt.Printf("tx %s: %v\n", path, m.translate(path))
- }
- tp := m.translate(path)
- r, err1 := m.fs.Open(tp)
- if err1 == nil {
- return r, nil
- }
- // IsNotExist errors in overlay FSes can mask real errors in
- // the underlying FS, so ignore them if there is another error.
- if err == nil || os.IsNotExist(err) {
- err = err1
- }
- }
- if err == nil {
- err = &os.PathError{Op: "open", Path: path, Err: os.ErrNotExist}
- }
- return nil, err
-}
-
-// stat implements the FileSystem Stat and Lstat methods.
-func (ns NameSpace) stat(path string, f func(FileSystem, string) (os.FileInfo, error)) (os.FileInfo, error) {
- var err error
- for _, m := range ns.resolve(path) {
- fi, err1 := f(m.fs, m.translate(path))
- if err1 == nil {
- return fi, nil
- }
- if err == nil {
- err = err1
- }
- }
- if err == nil {
- err = &os.PathError{Op: "stat", Path: path, Err: os.ErrNotExist}
- }
- return nil, err
-}
-
-func (ns NameSpace) Stat(path string) (os.FileInfo, error) {
- return ns.stat(path, FileSystem.Stat)
-}
-
-func (ns NameSpace) Lstat(path string) (os.FileInfo, error) {
- return ns.stat(path, FileSystem.Lstat)
-}
-
-// dirInfo is a trivial implementation of os.FileInfo for a directory.
-type dirInfo string
-
-func (d dirInfo) Name() string { return string(d) }
-func (d dirInfo) Size() int64 { return 0 }
-func (d dirInfo) Mode() os.FileMode { return os.ModeDir | 0555 }
-func (d dirInfo) ModTime() time.Time { return startTime }
-func (d dirInfo) IsDir() bool { return true }
-func (d dirInfo) Sys() interface{} { return nil }
-
-var startTime = time.Now()
-
-// ReadDir implements the FileSystem ReadDir method. It's where most of the magic is.
-// (The rest is in resolve.)
-//
-// Logically, ReadDir must return the union of all the directories that are named
-// by path. In order to avoid misinterpreting Go packages, of all the directories
-// that contain Go source code, we only include the files from the first,
-// but we include subdirectories from all.
-//
-// ReadDir must also return directory entries needed to reach mount points.
-// If the name space looks like the example in the type NameSpace comment,
-// but c:\Go does not have a src/pkg subdirectory, we still want to be able
-// to find that subdirectory, because we've mounted d:\Work1 and d:\Work2
-// there. So if we don't see "src" in the directory listing for c:\Go, we add an
-// entry for it before returning.
-//
-func (ns NameSpace) ReadDir(path string) ([]os.FileInfo, error) {
- path = ns.clean(path)
-
- // List matching directories and determine whether any of them contain
- // Go files.
- var (
- dirs [][]os.FileInfo
- goDirIndex = -1
- readDirErr error
- )
-
- for _, m := range ns.resolve(path) {
- dir, err := m.fs.ReadDir(m.translate(path))
- if err != nil {
- if readDirErr == nil {
- readDirErr = err
- }
- continue
- }
-
- dirs = append(dirs, dir)
-
- if goDirIndex < 0 {
- for _, f := range dir {
- if !f.IsDir() && strings.HasSuffix(f.Name(), ".go") {
- goDirIndex = len(dirs) - 1
- break
- }
- }
- }
- }
-
- // Build a list of files and subdirectories. If a directory contains Go files,
- // only include files from that directory. Otherwise, include files from
- // all directories. Include subdirectories from all directories regardless
- // of whether Go files are present.
- haveName := make(map[string]bool)
- var all []os.FileInfo
- for i, dir := range dirs {
- for _, f := range dir {
- name := f.Name()
- if !haveName[name] && (f.IsDir() || goDirIndex < 0 || goDirIndex == i) {
- all = append(all, f)
- haveName[name] = true
- }
- }
- }
-
- // Add any missing directories needed to reach mount points.
- for old := range ns {
- if hasPathPrefix(old, path) && old != path {
- // Find next element after path in old.
- elem := old[len(path):]
- elem = strings.TrimPrefix(elem, "/")
- if i := strings.Index(elem, "/"); i >= 0 {
- elem = elem[:i]
- }
- if !haveName[elem] {
- haveName[elem] = true
- all = append(all, dirInfo(elem))
- }
- }
- }
-
- if len(all) == 0 {
- return nil, readDirErr
- }
-
- sort.Sort(byName(all))
- return all, nil
-}
-
-// 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] }
diff --git a/internal/godoc/vfs/namespace_test.go b/internal/godoc/vfs/namespace_test.go
deleted file mode 100644
index 32b5e96..0000000
--- a/internal/godoc/vfs/namespace_test.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// Copyright 2016 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vfs_test
-
-import (
- "fmt"
- "strings"
- "testing"
- "time"
-
- "golang.org/x/website/internal/godoc/vfs"
- "golang.org/x/website/internal/godoc/vfs/mapfs"
-)
-
-func TestNewNameSpace(t *testing.T) {
-
- // We will mount this filesystem under /fs1
- mount := mapfs.New(map[string]string{"fs1file": "abcdefgh"})
-
- // Existing process. This should give error on Stat("/")
- t1 := vfs.NameSpace{}
- t1.Bind("/fs1", mount, "/", vfs.BindReplace)
-
- // using NewNameSpace. This should work fine.
- t2 := vfs.NewNameSpace()
- t2.Bind("/fs1", mount, "/", vfs.BindReplace)
-
- testcases := map[string][]bool{
- "/": {false, true},
- "/fs1": {true, true},
- "/fs1/fs1file": {true, true},
- }
-
- fss := []vfs.FileSystem{t1, t2}
-
- for j, fs := range fss {
- for k, v := range testcases {
- _, err := fs.Stat(k)
- result := err == nil
- if result != v[j] {
- t.Errorf("fs: %d, testcase: %s, want: %v, got: %v, err: %s", j, k, v[j], result, err)
- }
- }
- }
-
- fi, err := t2.Stat("/")
- if err != nil {
- t.Fatal(err)
- }
-
- if fi.Name() != "/" {
- t.Errorf("t2.Name() : want:%s got:%s", "/", fi.Name())
- }
-
- if !fi.ModTime().IsZero() {
- t.Errorf("t2.ModTime() : want:%v got:%v", time.Time{}, fi.ModTime())
- }
-}
-
-func TestReadDirUnion(t *testing.T) {
- for _, tc := range []struct {
- desc string
- ns vfs.NameSpace
- path, want string
- }{
- {
- desc: "no_go_files",
- ns: func() vfs.NameSpace {
- rootFs := mapfs.New(map[string]string{
- "doc/a.txt": "1",
- "doc/b.txt": "1",
- "doc/dir1/d1.txt": "",
- })
- docFs := mapfs.New(map[string]string{
- "doc/a.txt": "22",
- "doc/dir2/d2.txt": "",
- })
- ns := vfs.NameSpace{}
- ns.Bind("/", rootFs, "/", vfs.BindReplace)
- ns.Bind("/doc", docFs, "/doc", vfs.BindBefore)
- return ns
- }(),
- path: "/doc",
- want: "a.txt:2,b.txt:1,dir1:0,dir2:0",
- }, {
- desc: "have_go_files",
- ns: func() vfs.NameSpace {
- a := mapfs.New(map[string]string{
- "src/x/a.txt": "",
- "src/x/suba/sub.txt": "",
- })
- b := mapfs.New(map[string]string{
- "src/x/b.go": "package b",
- "src/x/subb/sub.txt": "",
- })
- c := mapfs.New(map[string]string{
- "src/x/c.txt": "",
- "src/x/subc/sub.txt": "",
- })
- ns := vfs.NameSpace{}
- ns.Bind("/", a, "/", vfs.BindReplace)
- ns.Bind("/", b, "/", vfs.BindAfter)
- ns.Bind("/", c, "/", vfs.BindAfter)
- return ns
- }(),
- path: "/src/x",
- want: "b.go:9,suba:0,subb:0,subc:0",
- }, {
- desc: "empty_mount",
- ns: func() vfs.NameSpace {
- ns := vfs.NameSpace{}
- ns.Bind("/empty", mapfs.New(nil), "/empty", vfs.BindReplace)
- return ns
- }(),
- path: "/",
- want: "empty:0",
- },
- } {
- t.Run(tc.desc, func(t *testing.T) {
- fis, err := tc.ns.ReadDir(tc.path)
- if err != nil {
- t.Fatal(err)
- }
- buf := &strings.Builder{}
- sep := ""
- for _, fi := range fis {
- fmt.Fprintf(buf, "%s%s:%d", sep, fi.Name(), fi.Size())
- sep = ","
- }
- if got := buf.String(); got != tc.want {
- t.Errorf("got %q; want %q", got, tc.want)
- }
- })
- }
-}
diff --git a/internal/godoc/vfs/os.go b/internal/godoc/vfs/os.go
deleted file mode 100644
index 7b6025b..0000000
--- a/internal/godoc/vfs/os.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package vfs
-
-import (
- "fmt"
- "io/ioutil"
- "os"
- pathpkg "path"
- "path/filepath"
- "runtime"
-)
-
-// We expose a new variable because otherwise we need to copy the findGOROOT logic again
-// from cmd/godoc which is already copied twice from the standard library.
-
-// GOROOT is the GOROOT path under which the godoc binary is running.
-// It is needed to check whether a filesystem root is under GOROOT or not.
-// This is set from cmd/godoc/main.go.
-var GOROOT = runtime.GOROOT()
-
-// OS returns an implementation of FileSystem reading from the
-// tree rooted at root. Recording a root is convenient everywhere
-// but necessary on Windows, because the slash-separated path
-// passed to Open has no way to specify a drive letter. Using a root
-// lets code refer to OS(`c:\`), OS(`d:\`) and so on.
-func OS(root string) FileSystem {
- return osFS{rootPath: root}
-}
-
-type osFS struct {
- rootPath string
-}
-
-func (root osFS) String() string { return "os(" + root.rootPath + ")" }
-
-func (root osFS) resolve(path string) string {
- // Clean the path so that it cannot possibly begin with ../.
- // If it did, the result of filepath.Join would be outside the
- // tree rooted at root. We probably won't ever see a path
- // with .. in it, but be safe anyway.
- path = pathpkg.Clean("/" + path)
-
- return filepath.Join(root.rootPath, path)
-}
-
-func (root osFS) Open(path string) (ReadSeekCloser, error) {
- f, err := os.Open(root.resolve(path))
- if err != nil {
- return nil, err
- }
- fi, err := f.Stat()
- if err != nil {
- f.Close()
- return nil, err
- }
- if fi.IsDir() {
- f.Close()
- return nil, fmt.Errorf("Open: %s is a directory", path)
- }
- return f, nil
-}
-
-func (root osFS) Lstat(path string) (os.FileInfo, error) {
- return os.Lstat(root.resolve(path))
-}
-
-func (root osFS) Stat(path string) (os.FileInfo, error) {
- return os.Stat(root.resolve(path))
-}
-
-func (root osFS) ReadDir(path string) ([]os.FileInfo, error) {
- return ioutil.ReadDir(root.resolve(path)) // is sorted
-}
diff --git a/internal/godoc/vfs/vfs.go b/internal/godoc/vfs/vfs.go
deleted file mode 100644
index ecc54d0..0000000
--- a/internal/godoc/vfs/vfs.go
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2013 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Package vfs defines types for abstract file system access and provides an
-// implementation accessing the file system of the underlying OS.
-package vfs // import "golang.org/x/website/internal/godoc/vfs"
-
-import (
- "io"
- "io/ioutil"
- "os"
-)
-
-// The FileSystem interface specifies the methods godoc is using
-// to access the file system for which it serves documentation.
-type FileSystem interface {
- Opener
- Lstat(path string) (os.FileInfo, error)
- Stat(path string) (os.FileInfo, error)
- ReadDir(path string) ([]os.FileInfo, error)
- String() string
-}
-
-// Opener is a minimal virtual filesystem that can only open regular files.
-type Opener interface {
- Open(name string) (ReadSeekCloser, error)
-}
-
-// A ReadSeekCloser can Read, Seek, and Close.
-type ReadSeekCloser interface {
- io.Reader
- io.Seeker
- io.Closer
-}
-
-// ReadFile reads the file named by path from fs and returns the contents.
-func ReadFile(fs Opener, path string) ([]byte, error) {
- rc, err := fs.Open(path)
- if err != nil {
- return nil, err
- }
- defer rc.Close()
- return ioutil.ReadAll(rc)
-}