This commit is contained in:
Shreyas Goenka 2023-06-19 14:37:29 +02:00
parent cf04ef8550
commit 7529c8a4ca
No known key found for this signature in database
GPG Key ID: 92A07DF49CCB0622
5 changed files with 109 additions and 9 deletions

View File

@ -18,11 +18,8 @@ type LocalClient struct {
}
func NewLocalClient(root string) (Filer, error) {
if runtime.GOOS == "windows" && root == "/" {
// Windows file systems do not have a "root" directory. Instead paths require
// a Volume/Drive letter specified. This allows us to refer to files across
// different drives from a single client
return &LocalClient{root: NopRootPath{}}, nil
if runtime.GOOS == "windows" {
return &LocalClient{root: WindowsRootPath{root}}, nil
}
return &LocalClient{
root: NewUnixRootPath(root),

View File

@ -1,7 +1,12 @@
package filer
// RootPath can be joined with a relative path and ensures that
// the returned path is always a strict child of the root path.
type RootPath interface {
// Join returns the specified path name joined to the root.
// It returns an error if the resulting path is not a strict child of the root path.
Join(string) (string, error)
Root() string
}

View File

@ -6,8 +6,6 @@ import (
"strings"
)
// UnixRootPath can be joined with a relative path and ensures that
// the returned path is always a strict child of the root path.
type UnixRootPath struct {
rootPath string
}
@ -20,8 +18,6 @@ func NewUnixRootPath(name string) UnixRootPath {
}
}
// Join returns the specified path name joined to the root.
// It returns an error if the resulting path is not a strict child of the root path.
func (p UnixRootPath) Join(name string) (string, error) {
absPath := path.Join(p.rootPath, name)

View File

@ -0,0 +1,39 @@
package filer
import (
"fmt"
"path/filepath"
"strings"
)
type WindowsRootPath struct {
rootPath string
}
func NewWindowsRootPath(name string) WindowsRootPath {
// Windows file systems do not have a "root" directory. Instead paths require
// a Volume/Drive letter specified. If a user of this struct specifies "/" then
// we treat it as the "root" and skip any validation
if name == "/" {
return WindowsRootPath{""}
}
return WindowsRootPath{filepath.Clean(name)}
}
// Join returns the specified path name joined to the root.
// It returns an error if the resulting path is not a strict child of the root path.
func (p WindowsRootPath) Join(name string) (string, error) {
absPath := filepath.Join(p.rootPath, name)
// Don't allow escaping the specified root using relative paths.
if !strings.HasPrefix(absPath, p.rootPath) {
return "", fmt.Errorf("relative path escapes root: %s", name)
}
return absPath, nil
}
func (p WindowsRootPath) Root() string {
return p.rootPath
}

View File

@ -0,0 +1,63 @@
package filer
import (
"runtime"
"testing"
"github.com/stretchr/testify/assert"
)
func TestWindowsRootPathForRoot(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("this test is meant for windows")
}
rp := NewWindowsRootPath("/")
// Assert root value returned
assert.Equal(t, "", rp.Root())
// case: absolute windows path
path, err := rp.Join(`c:\a\b`)
assert.NoError(t, err)
assert.Equal(t, `c:\a\b`, path)
// case: absolute windows path following file URI scheme
path, err = rp.Join(`D:/a/b`)
assert.NoError(t, err)
assert.Equal(t, `D:/a/b`, path)
// case: relative windows paths
path, err = rp.Join(`c:a\b`)
assert.NoError(t, err)
assert.Equal(t, `c:a\b`, path)
path, err = rp.Join(`c:a`)
assert.NoError(t, err)
assert.Equal(t, `c:a`, path)
// case: relative windows paths following file URI scheme
path, err = rp.Join(`c:a/b`)
assert.NoError(t, err)
assert.Equal(t, `C:a/b`, path)
}
func TestWindowsRootPath(t *testing.T) {
if runtime.GOOS != "windows" {
t.Skip("this test is meant for windows")
}
tmpDir := t.TempDir()
rp := NewWindowsRootPath(t.TempDir())
// Assert root value returned
assert.Equal(t, tmpDir, rp.Root())
path, err := rp.Join(`a\b\c`)
assert.NoError(t, err)
assert.Equal(t, tmpDir+`\a\b`, path)
path, err = rp.Join("a/b")
assert.NoError(t, err)
assert.Equal(t, tmpDir + `\a/b`, path)
}