mirror of
https://github.com/golang/go
synced 2024-11-18 10:54:40 -07:00
godoc: follow symbolic links to folders in GOROOT
Directory walking in godoc relies on ReadDir which returns the result of os.Lstat. Instead make the the OS VFS's ReadDir use os.Stat on symlinks before returning. Fixes golang/go#15049 Change-Id: I34d17ca0027b0245f5ef434a000e5a3fe2af11cf Reviewed-on: https://go-review.googlesource.com/45096 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
This commit is contained in:
parent
6f1996fdfe
commit
4ce273956a
@ -7,9 +7,11 @@ package vfs
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
pathpkg "path"
|
pathpkg "path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OS returns an implementation of FileSystem reading from the
|
// OS returns an implementation of FileSystem reading from the
|
||||||
@ -57,9 +59,35 @@ func (root osFS) Lstat(path string) (os.FileInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (root osFS) Stat(path string) (os.FileInfo, error) {
|
func (root osFS) Stat(path string) (os.FileInfo, error) {
|
||||||
return os.Stat(root.resolve(path))
|
return stat(root.resolve(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var readdir = ioutil.ReadDir // for testing
|
||||||
|
var stat = os.Stat // for testing
|
||||||
|
|
||||||
func (root osFS) ReadDir(path string) ([]os.FileInfo, error) {
|
func (root osFS) ReadDir(path string) ([]os.FileInfo, error) {
|
||||||
return ioutil.ReadDir(root.resolve(path)) // is sorted
|
fis, err := readdir(root.resolve(path))
|
||||||
|
if err != nil {
|
||||||
|
return fis, err
|
||||||
|
}
|
||||||
|
ret := fis[:0]
|
||||||
|
|
||||||
|
// reread the files with os.Stat since they might be symbolic links
|
||||||
|
for _, fi := range fis {
|
||||||
|
if fi.Mode()&os.ModeSymlink != 0 {
|
||||||
|
baseName := fi.Name()
|
||||||
|
fi, err = root.Stat(pathpkg.Join(path, baseName))
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) && strings.HasPrefix(baseName, ".") {
|
||||||
|
// Ignore editor spam files without log spam.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Printf("ignoring symlink: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ret = append(ret, fi)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil // is sorted
|
||||||
}
|
}
|
||||||
|
109
godoc/vfs/os_test.go
Normal file
109
godoc/vfs/os_test.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
// Copyright 2017 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 (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fakeFileInfo struct {
|
||||||
|
dir bool
|
||||||
|
link bool
|
||||||
|
basename string
|
||||||
|
modtime time.Time
|
||||||
|
ents []*fakeFileInfo
|
||||||
|
contents string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeFileInfo) Name() string { return f.basename }
|
||||||
|
func (f *fakeFileInfo) Sys() interface{} { return nil }
|
||||||
|
func (f *fakeFileInfo) ModTime() time.Time { return f.modtime }
|
||||||
|
func (f *fakeFileInfo) IsDir() bool { return f.dir }
|
||||||
|
func (f *fakeFileInfo) Size() int64 { return int64(len(f.contents)) }
|
||||||
|
func (f *fakeFileInfo) Mode() os.FileMode {
|
||||||
|
if f.dir {
|
||||||
|
return 0755 | os.ModeDir
|
||||||
|
}
|
||||||
|
if f.link {
|
||||||
|
return 0644 | os.ModeSymlink
|
||||||
|
}
|
||||||
|
return 0644
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOSReadDirFollowsSymLinks(t *testing.T) {
|
||||||
|
// Stat is called on ReadDir output by osFS
|
||||||
|
oldstat := stat
|
||||||
|
stat = func(path string) (os.FileInfo, error) {
|
||||||
|
if filepath.ToSlash(path) != "/tmp/subdir/is_link" {
|
||||||
|
t.Fatalf("stat called on unexpected path %q", path)
|
||||||
|
}
|
||||||
|
return &fakeFileInfo{
|
||||||
|
link: false,
|
||||||
|
basename: "foo",
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
defer func() { stat = oldstat }()
|
||||||
|
|
||||||
|
oldreaddir := readdir
|
||||||
|
readdir = func(path string) ([]os.FileInfo, error) {
|
||||||
|
return []os.FileInfo{
|
||||||
|
&fakeFileInfo{
|
||||||
|
link: true,
|
||||||
|
basename: "is_link",
|
||||||
|
},
|
||||||
|
&fakeFileInfo{
|
||||||
|
link: false,
|
||||||
|
basename: "not_link",
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
defer func() { readdir = oldreaddir }()
|
||||||
|
|
||||||
|
fs := OS("/tmp")
|
||||||
|
result, err := fs.ReadDir("subdir")
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var gotBuf bytes.Buffer
|
||||||
|
for i, fi := range result {
|
||||||
|
fmt.Fprintf(&gotBuf, "result[%d] = %v, %v\n", i, fi.Name(), fi.Mode())
|
||||||
|
}
|
||||||
|
got := gotBuf.String()
|
||||||
|
want := `result[0] = foo, -rw-r--r--
|
||||||
|
result[1] = not_link, -rw-r--r--
|
||||||
|
`
|
||||||
|
if got != want {
|
||||||
|
t.Errorf("ReadDir got:\n%s\n\nwant:\n%s\n", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOSReadDirHandlesReadDirErrors(t *testing.T) {
|
||||||
|
oldreaddir := readdir
|
||||||
|
readdir = func(path string) ([]os.FileInfo, error) {
|
||||||
|
return []os.FileInfo{
|
||||||
|
&fakeFileInfo{
|
||||||
|
dir: false,
|
||||||
|
basename: "foo",
|
||||||
|
},
|
||||||
|
}, errors.New("some arbitrary filesystem failure")
|
||||||
|
}
|
||||||
|
defer func() { readdir = oldreaddir }()
|
||||||
|
|
||||||
|
fs := OS("/tmp")
|
||||||
|
_, err := fs.ReadDir("subdir")
|
||||||
|
|
||||||
|
if got, want := fmt.Sprint(err), "some arbitrary filesystem failure"; got != want {
|
||||||
|
t.Errorf("ReadDir = %v; want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user