forked from mirror/afero
Improve absolute file paths handling in BasePathFs
A common mistake in using the BasePathFs is to give it a real OS absolute file path instead of a virtual one relative to the virtual base. This commit adds some tests and returns an error on Windows in this case. On Unix we have to train the users to do a better job. See https://github.com/spf13/hugo/issues/1800
This commit is contained in:
parent
3c51231761
commit
ddb4d0857d
23
basepath.go
23
basepath.go
|
@ -1,8 +1,10 @@
|
|||
package afero
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
@ -27,6 +29,11 @@ func NewBasePathFs(source Fs, path string) Fs {
|
|||
// on a file outside the base path it returns the given file name and an error,
|
||||
// else the given file with the base path prepended
|
||||
func (b *BasePathFs) RealPath(name string) (path string, err error) {
|
||||
|
||||
if err := validateBasePathName(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bpath := filepath.Clean(b.path)
|
||||
path = filepath.Clean(filepath.Join(bpath, name))
|
||||
if !strings.HasPrefix(path, bpath) {
|
||||
|
@ -35,6 +42,22 @@ func (b *BasePathFs) RealPath(name string) (path string, err error) {
|
|||
return path, nil
|
||||
}
|
||||
|
||||
func validateBasePathName(name string) error {
|
||||
if runtime.GOOS != "windows" {
|
||||
// Not much to do here;
|
||||
// the virtual file paths all look absolute on *nix.
|
||||
return nil
|
||||
}
|
||||
|
||||
// On Windows a common mistake would be to provide an absolute OS path
|
||||
// We could strip out the base part, but that would not be very portable.
|
||||
if filepath.IsAbs(name) {
|
||||
return &os.PathError{"realPath", name, errors.New("got a real OS path instead of a virtual")}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *BasePathFs) Chtimes(name string, atime, mtime time.Time) (err error) {
|
||||
if name, err = b.RealPath(name); err != nil {
|
||||
return &os.PathError{"chtimes", name, err}
|
||||
|
|
|
@ -2,6 +2,8 @@ package afero
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -35,3 +37,56 @@ func TestBasePathRoot(t *testing.T) {
|
|||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealPath(t *testing.T) {
|
||||
fs := NewOsFs()
|
||||
baseDir, err := TempDir(fs, "", "base")
|
||||
if err != nil {
|
||||
t.Fatal("error creating tempDir", err)
|
||||
}
|
||||
defer fs.RemoveAll(baseDir)
|
||||
anotherDir, err := TempDir(fs, "", "another")
|
||||
if err != nil {
|
||||
t.Fatal("error creating tempDir", err)
|
||||
}
|
||||
defer fs.RemoveAll(anotherDir)
|
||||
|
||||
bp := NewBasePathFs(fs, baseDir).(*BasePathFs)
|
||||
|
||||
subDir := filepath.Join(baseDir, "s1")
|
||||
|
||||
realPath, err := bp.RealPath("/s1")
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Got error %s", err)
|
||||
}
|
||||
|
||||
if realPath != subDir {
|
||||
t.Errorf("Expected \n%s got \n%s", subDir, realPath)
|
||||
}
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
_, err = bp.RealPath(anotherDir)
|
||||
|
||||
if err == nil {
|
||||
t.Errorf("Expected error")
|
||||
}
|
||||
|
||||
} else {
|
||||
// on *nix we have no way of just looking at the path and tell that anotherDir
|
||||
// is not inside the base file system.
|
||||
// The user will receive an os.ErrNotExist later.
|
||||
surrealPath, err := bp.RealPath(anotherDir)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Got error %s", err)
|
||||
}
|
||||
|
||||
excpected := filepath.Join(baseDir, anotherDir)
|
||||
|
||||
if surrealPath != excpected {
|
||||
t.Errorf("Expected \n%s got \n%s", excpected, surrealPath)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue