This commit is contained in:
+21
-4
@@ -23,13 +23,24 @@ var (
|
||||
ErrNotRelative = errors.New("not a relative path")
|
||||
)
|
||||
|
||||
func WithJunctionsAsDirs() Option {
|
||||
return func(fs Filesystem) {
|
||||
if basic, ok := fs.(*BasicFilesystem); !ok {
|
||||
l.Warnln("WithJunctionsAsDirs must only be used with FilesystemTypeBasic")
|
||||
} else {
|
||||
basic.junctionsAsDirs = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The BasicFilesystem implements all aspects by delegating to package os.
|
||||
// All paths are relative to the root and cannot (should not) escape the root directory.
|
||||
type BasicFilesystem struct {
|
||||
root string
|
||||
root string
|
||||
junctionsAsDirs bool
|
||||
}
|
||||
|
||||
func newBasicFilesystem(root string) *BasicFilesystem {
|
||||
func newBasicFilesystem(root string, opts ...Option) *BasicFilesystem {
|
||||
if root == "" {
|
||||
root = "." // Otherwise "" becomes "/" below
|
||||
}
|
||||
@@ -64,7 +75,13 @@ func newBasicFilesystem(root string) *BasicFilesystem {
|
||||
root = longFilenameSupport(root)
|
||||
}
|
||||
|
||||
return &BasicFilesystem{root}
|
||||
fs := &BasicFilesystem{
|
||||
root: root,
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(fs)
|
||||
}
|
||||
return fs
|
||||
}
|
||||
|
||||
// rooted expands the relative path to the full path that is then used with os
|
||||
@@ -145,7 +162,7 @@ func (f *BasicFilesystem) Lstat(name string) (FileInfo, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fi, err := underlyingLstat(name)
|
||||
fi, err := f.underlyingLstat(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
|
||||
// Lstat is like os.Lstat, except lobotomized for Android. See
|
||||
// https://forum.syncthing.net/t/2395
|
||||
func underlyingLstat(name string) (fi os.FileInfo, err error) {
|
||||
func (*BasicFilesystem) underlyingLstat(name string) (fi os.FileInfo, err error) {
|
||||
for i := 0; i < 10; i++ { // We have to draw the line somewhere
|
||||
fi, err = os.Lstat(name)
|
||||
if err, ok := err.(*os.PathError); ok && err.Err == syscall.EINTR {
|
||||
@@ -10,6 +10,6 @@ package fs
|
||||
|
||||
import "os"
|
||||
|
||||
func underlyingLstat(name string) (fi os.FileInfo, err error) {
|
||||
func (*BasicFilesystem) underlyingLstat(name string) (fi os.FileInfo, err error) {
|
||||
return os.Lstat(name)
|
||||
}
|
||||
@@ -66,12 +66,12 @@ func (fi *dirJunctFileInfo) IsDir() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func underlyingLstat(name string) (os.FileInfo, error) {
|
||||
func (f *BasicFilesystem) underlyingLstat(name string) (os.FileInfo, error) {
|
||||
var fi, err = os.Lstat(name)
|
||||
|
||||
// NTFS directory junctions are treated as ordinary directories,
|
||||
// NTFS directory junctions can be treated as ordinary directories,
|
||||
// see https://forum.syncthing.net/t/option-to-follow-directory-junctions-symbolic-links/14750
|
||||
if err == nil && fi.Mode()&os.ModeSymlink != 0 {
|
||||
if err == nil && f.junctionsAsDirs && fi.Mode()&os.ModeSymlink != 0 {
|
||||
var isJunct bool
|
||||
isJunct, err = isDirectoryJunction(name)
|
||||
if err == nil && isJunct {
|
||||
+1
-1
@@ -66,7 +66,7 @@ var (
|
||||
fakefsFs = make(map[string]*fakefs)
|
||||
)
|
||||
|
||||
func newFakeFilesystem(rootURI string) *fakefs {
|
||||
func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
|
||||
fakefsMut.Lock()
|
||||
defer fakefsMut.Unlock()
|
||||
|
||||
|
||||
@@ -178,13 +178,15 @@ var IsPermission = os.IsPermission
|
||||
// IsPathSeparator is the equivalent of os.IsPathSeparator
|
||||
var IsPathSeparator = os.IsPathSeparator
|
||||
|
||||
func NewFilesystem(fsType FilesystemType, uri string) Filesystem {
|
||||
type Option func(Filesystem)
|
||||
|
||||
func NewFilesystem(fsType FilesystemType, uri string, opts ...Option) Filesystem {
|
||||
var fs Filesystem
|
||||
switch fsType {
|
||||
case FilesystemTypeBasic:
|
||||
fs = newBasicFilesystem(uri)
|
||||
fs = newBasicFilesystem(uri, opts...)
|
||||
case FilesystemTypeFake:
|
||||
fs = newFakeFilesystem(uri)
|
||||
fs = newFakeFilesystem(uri, opts...)
|
||||
default:
|
||||
l.Debugln("Unknown filesystem", fsType, uri)
|
||||
fs = &errorFilesystem{
|
||||
|
||||
@@ -57,7 +57,7 @@ func testWalkTraverseDirJunct(t *testing.T, fsType FilesystemType, uri string) {
|
||||
t.Skip("Directory junctions are available and tested on windows only")
|
||||
}
|
||||
|
||||
fs := NewFilesystem(fsType, uri)
|
||||
fs := NewFilesystem(fsType, uri, WithJunctionsAsDirs())
|
||||
|
||||
if err := fs.MkdirAll("target/foo", 0); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -90,7 +90,7 @@ func testWalkInfiniteRecursion(t *testing.T, fsType FilesystemType, uri string)
|
||||
t.Skip("Infinite recursion detection is tested on windows only")
|
||||
}
|
||||
|
||||
fs := NewFilesystem(fsType, uri)
|
||||
fs := NewFilesystem(fsType, uri, WithJunctionsAsDirs())
|
||||
|
||||
if err := fs.MkdirAll("target/foo", 0); err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
Reference in New Issue
Block a user