mirror of https://github.com/pkg/sftp.git
Refactor tests and make `toLocalPath` a method on `Server`
This commit is contained in:
parent
b3f237d7ad
commit
c93b0a0af2
|
@ -1247,7 +1247,7 @@ func (p *sshFxpExtendedPacketPosixRename) UnmarshalBinary(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *sshFxpExtendedPacketPosixRename) respond(s *Server) responsePacket {
|
func (p *sshFxpExtendedPacketPosixRename) respond(s *Server) responsePacket {
|
||||||
err := os.Rename(toLocalPath(s.workDir, p.Oldpath), toLocalPath(s.workDir, p.Newpath))
|
err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
|
||||||
return statusFromError(p.ID, err)
|
return statusFromError(p.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1276,6 +1276,6 @@ func (p *sshFxpExtendedPacketHardlink) UnmarshalBinary(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *sshFxpExtendedPacketHardlink) respond(s *Server) responsePacket {
|
func (p *sshFxpExtendedPacketHardlink) respond(s *Server) responsePacket {
|
||||||
err := os.Link(toLocalPath(s.workDir, p.Oldpath), toLocalPath(s.workDir, p.Newpath))
|
err := os.Link(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
|
||||||
return statusFromError(p.ID, err)
|
return statusFromError(p.ID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
package sftp
|
package sftp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,24 +13,3 @@ func fakeFileInfoSys() interface{} {
|
||||||
func testOsSys(sys interface{}) error {
|
func testOsSys(sys interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLocalPath(workDir, p string) string {
|
|
||||||
if workDir != "" && !path.IsAbs(p) {
|
|
||||||
p = path.Join(workDir, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
lp := filepath.FromSlash(p)
|
|
||||||
|
|
||||||
if path.IsAbs(p) {
|
|
||||||
tmp := lp[1:]
|
|
||||||
|
|
||||||
if filepath.IsAbs(tmp) {
|
|
||||||
// If the FromSlash without any starting slashes is absolute,
|
|
||||||
// then we have a filepath encoded with a prefix '/'.
|
|
||||||
// e.g. "/#s/boot" to "#s/boot"
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lp
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ package sftp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"path"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -22,11 +21,3 @@ func testOsSys(sys interface{}) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLocalPath(workDir, p string) string {
|
|
||||||
if workDir != "" && !path.IsAbs(p) {
|
|
||||||
p = path.Join(workDir, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
111
request_test.go
111
request_test.go
|
@ -5,7 +5,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"runtime"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -248,113 +247,3 @@ func TestOpendirHandleReuse(t *testing.T) {
|
||||||
rpkt = request.call(handlers, pkt, nil, 0)
|
rpkt = request.call(handlers, pkt, nil, 0)
|
||||||
assert.IsType(t, &sshFxpNamePacket{}, rpkt)
|
assert.IsType(t, &sshFxpNamePacket{}, rpkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_toLocalPath(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
workDir string
|
|
||||||
p string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
goos string
|
|
||||||
args args
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "empty path with no workdir",
|
|
||||||
args: args{p: ""},
|
|
||||||
want: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "relative path with no workdir",
|
|
||||||
args: args{p: "file"},
|
|
||||||
want: "file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "absolute path with no workdir",
|
|
||||||
args: args{p: "/file"},
|
|
||||||
want: "/file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and empty path on Unix",
|
|
||||||
goos: "linux",
|
|
||||||
args: args{workDir: cleanPath("/home/user"), p: ""},
|
|
||||||
want: "/home/user",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and relative path on Unix",
|
|
||||||
goos: "linux",
|
|
||||||
args: args{workDir: cleanPath("/home/user"), p: "file"},
|
|
||||||
want: "/home/user/file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and relative path with . on Unix",
|
|
||||||
goos: "linux",
|
|
||||||
args: args{workDir: cleanPath("/home/user"), p: "."},
|
|
||||||
want: "/home/user",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and relative path with . and file on Unix",
|
|
||||||
goos: "linux",
|
|
||||||
args: args{workDir: cleanPath("/home/user"), p: "./file"},
|
|
||||||
want: "/home/user/file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and absolute path on Unix",
|
|
||||||
goos: "linux",
|
|
||||||
args: args{workDir: cleanPath("/home/user"), p: "/file"},
|
|
||||||
want: "/file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and non-unixy path on Unix prefixes workdir",
|
|
||||||
goos: "linux",
|
|
||||||
args: args{workDir: cleanPath("/home/user"), p: "C:\\file"},
|
|
||||||
want: "/home/user/C:\\file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and empty path on Windows",
|
|
||||||
goos: "windows",
|
|
||||||
args: args{workDir: cleanPath("C:\\Users\\User"), p: ""},
|
|
||||||
want: "C:\\Users\\User",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and relative path on Windows",
|
|
||||||
goos: "windows",
|
|
||||||
args: args{workDir: cleanPath("C:\\Users\\User"), p: "file"},
|
|
||||||
want: "C:\\Users\\User\\file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and relative path with . on Windows",
|
|
||||||
goos: "windows",
|
|
||||||
args: args{workDir: cleanPath("C:\\Users\\User"), p: "."},
|
|
||||||
want: "C:\\Users\\User",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and relative path with . and file on Windows",
|
|
||||||
goos: "windows",
|
|
||||||
args: args{workDir: cleanPath("C:\\Users\\User"), p: "./file"},
|
|
||||||
want: "C:\\Users\\User\\file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and absolute path on Windows",
|
|
||||||
goos: "windows",
|
|
||||||
args: args{workDir: cleanPath("C:\\Users\\User"), p: "/C:/file"},
|
|
||||||
want: "C:\\file",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "workdir and non-unixy path on Windows prefixes workdir",
|
|
||||||
goos: "windows",
|
|
||||||
args: args{workDir: cleanPath("C:\\Users\\User"), p: "C:\\file"},
|
|
||||||
want: "C:\\Users\\User\\C:\\file",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
if tt.goos != "" && runtime.GOOS != tt.goos {
|
|
||||||
t.Skipf("Skipping test for %s on %s", tt.goos, runtime.GOOS)
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(t, tt.want, toLocalPath(tt.args.workDir, tt.args.p), "wrong local path")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
package sftp
|
package sftp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -13,36 +11,3 @@ func fakeFileInfoSys() interface{} {
|
||||||
func testOsSys(sys interface{}) error {
|
func testOsSys(sys interface{}) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func toLocalPath(workDir, p string) string {
|
|
||||||
if workDir != "" && !path.IsAbs(p) {
|
|
||||||
p = path.Join(workDir, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
lp := filepath.FromSlash(p)
|
|
||||||
|
|
||||||
if path.IsAbs(p) {
|
|
||||||
tmp := lp
|
|
||||||
for len(tmp) > 0 && tmp[0] == '\\' {
|
|
||||||
tmp = tmp[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if filepath.IsAbs(tmp) {
|
|
||||||
// If the FromSlash without any starting slashes is absolute,
|
|
||||||
// then we have a filepath encoded with a prefix '/'.
|
|
||||||
// e.g. "/C:/Windows" to "C:\\Windows"
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp += "\\"
|
|
||||||
|
|
||||||
if filepath.IsAbs(tmp) {
|
|
||||||
// If the FromSlash without any starting slashes but with extra end slash is absolute,
|
|
||||||
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
|
|
||||||
// e.g. "/C:" to "C:\\"
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lp
|
|
||||||
}
|
|
||||||
|
|
24
server.go
24
server.go
|
@ -185,7 +185,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
||||||
}
|
}
|
||||||
case *sshFxpStatPacket:
|
case *sshFxpStatPacket:
|
||||||
// stat the requested file
|
// stat the requested file
|
||||||
info, err := os.Stat(toLocalPath(s.workDir, p.Path))
|
info, err := os.Stat(s.toLocalPath(p.Path))
|
||||||
rpkt = &sshFxpStatResponse{
|
rpkt = &sshFxpStatResponse{
|
||||||
ID: p.ID,
|
ID: p.ID,
|
||||||
info: info,
|
info: info,
|
||||||
|
@ -195,7 +195,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
||||||
}
|
}
|
||||||
case *sshFxpLstatPacket:
|
case *sshFxpLstatPacket:
|
||||||
// stat the requested file
|
// stat the requested file
|
||||||
info, err := os.Lstat(toLocalPath(s.workDir, p.Path))
|
info, err := os.Lstat(s.toLocalPath(p.Path))
|
||||||
rpkt = &sshFxpStatResponse{
|
rpkt = &sshFxpStatResponse{
|
||||||
ID: p.ID,
|
ID: p.ID,
|
||||||
info: info,
|
info: info,
|
||||||
|
@ -219,24 +219,24 @@ func handlePacket(s *Server, p orderedRequest) error {
|
||||||
}
|
}
|
||||||
case *sshFxpMkdirPacket:
|
case *sshFxpMkdirPacket:
|
||||||
// TODO FIXME: ignore flags field
|
// TODO FIXME: ignore flags field
|
||||||
err := os.Mkdir(toLocalPath(s.workDir, p.Path), 0o755)
|
err := os.Mkdir(s.toLocalPath(p.Path), 0o755)
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
case *sshFxpRmdirPacket:
|
case *sshFxpRmdirPacket:
|
||||||
err := os.Remove(toLocalPath(s.workDir, p.Path))
|
err := os.Remove(s.toLocalPath(p.Path))
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
case *sshFxpRemovePacket:
|
case *sshFxpRemovePacket:
|
||||||
err := os.Remove(toLocalPath(s.workDir, p.Filename))
|
err := os.Remove(s.toLocalPath(p.Filename))
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
case *sshFxpRenamePacket:
|
case *sshFxpRenamePacket:
|
||||||
err := os.Rename(toLocalPath(s.workDir, p.Oldpath), toLocalPath(s.workDir, p.Newpath))
|
err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath))
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
case *sshFxpSymlinkPacket:
|
case *sshFxpSymlinkPacket:
|
||||||
err := os.Symlink(toLocalPath(s.workDir, p.Targetpath), toLocalPath(s.workDir, p.Linkpath))
|
err := os.Symlink(s.toLocalPath(p.Targetpath), s.toLocalPath(p.Linkpath))
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
case *sshFxpClosePacket:
|
case *sshFxpClosePacket:
|
||||||
rpkt = statusFromError(p.ID, s.closeHandle(p.Handle))
|
rpkt = statusFromError(p.ID, s.closeHandle(p.Handle))
|
||||||
case *sshFxpReadlinkPacket:
|
case *sshFxpReadlinkPacket:
|
||||||
f, err := os.Readlink(toLocalPath(s.workDir, p.Path))
|
f, err := os.Readlink(s.toLocalPath(p.Path))
|
||||||
rpkt = &sshFxpNamePacket{
|
rpkt = &sshFxpNamePacket{
|
||||||
ID: p.ID,
|
ID: p.ID,
|
||||||
NameAttrs: []*sshFxpNameAttr{
|
NameAttrs: []*sshFxpNameAttr{
|
||||||
|
@ -251,7 +251,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
}
|
}
|
||||||
case *sshFxpRealpathPacket:
|
case *sshFxpRealpathPacket:
|
||||||
f, err := filepath.Abs(toLocalPath(s.workDir, p.Path))
|
f, err := filepath.Abs(s.toLocalPath(p.Path))
|
||||||
f = cleanPath(f)
|
f = cleanPath(f)
|
||||||
rpkt = &sshFxpNamePacket{
|
rpkt = &sshFxpNamePacket{
|
||||||
ID: p.ID,
|
ID: p.ID,
|
||||||
|
@ -267,7 +267,7 @@ func handlePacket(s *Server, p orderedRequest) error {
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
}
|
}
|
||||||
case *sshFxpOpendirPacket:
|
case *sshFxpOpendirPacket:
|
||||||
lp := toLocalPath(s.workDir, p.Path)
|
lp := s.toLocalPath(p.Path)
|
||||||
|
|
||||||
if stat, err := os.Stat(lp); err != nil {
|
if stat, err := os.Stat(lp); err != nil {
|
||||||
rpkt = statusFromError(p.ID, err)
|
rpkt = statusFromError(p.ID, err)
|
||||||
|
@ -458,7 +458,7 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket {
|
||||||
osFlags |= os.O_EXCL
|
osFlags |= os.O_EXCL
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile(toLocalPath(svr.workDir, p.Path), osFlags, 0o644)
|
f, err := os.OpenFile(svr.toLocalPath(p.Path), osFlags, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return statusFromError(p.ID, err)
|
return statusFromError(p.ID, err)
|
||||||
}
|
}
|
||||||
|
@ -496,7 +496,7 @@ func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket {
|
||||||
b := p.Attrs.([]byte)
|
b := p.Attrs.([]byte)
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
p.Path = toLocalPath(svr.workDir, p.Path)
|
p.Path = svr.toLocalPath(p.Path)
|
||||||
|
|
||||||
debug("setstat name \"%s\"", p.Path)
|
debug("setstat name \"%s\"", p.Path)
|
||||||
if (p.Flags & sshFileXferAttrSize) != 0 {
|
if (p.Flags & sshFileXferAttrSize) != 0 {
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
//go:build plan9
|
||||||
|
// +build plan9
|
||||||
|
|
||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) toLocalPath(p string) string {
|
||||||
|
if s.workDir != "" && !path.IsAbs(p) {
|
||||||
|
p = path.Join(s.workDir, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
lp := filepath.FromSlash(p)
|
||||||
|
|
||||||
|
if path.IsAbs(p) {
|
||||||
|
tmp := lp[1:]
|
||||||
|
|
||||||
|
if filepath.IsAbs(tmp) {
|
||||||
|
// If the FromSlash without any starting slashes is absolute,
|
||||||
|
// then we have a filepath encoded with a prefix '/'.
|
||||||
|
// e.g. "/#s/boot" to "#s/boot"
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lp
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
//go:build !windows && !plan9
|
||||||
|
// +build !windows,!plan9
|
||||||
|
|
||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) toLocalPath(p string) string {
|
||||||
|
if s.workDir != "" && !path.IsAbs(p) {
|
||||||
|
p = path.Join(s.workDir, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
//go:build !windows && !plan9
|
||||||
|
// +build !windows,!plan9
|
||||||
|
|
||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServer_toLocalPath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
withWorkDir string
|
||||||
|
p string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty path with no workdir",
|
||||||
|
p: "",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path with no workdir",
|
||||||
|
p: "file",
|
||||||
|
want: "file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "absolute path with no workdir",
|
||||||
|
p: "/file",
|
||||||
|
want: "/file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and empty path",
|
||||||
|
withWorkDir: "/home/user",
|
||||||
|
p: "",
|
||||||
|
want: "/home/user",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and relative path",
|
||||||
|
withWorkDir: "/home/user",
|
||||||
|
p: "file",
|
||||||
|
want: "/home/user/file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and relative path with .",
|
||||||
|
withWorkDir: "/home/user",
|
||||||
|
p: ".",
|
||||||
|
want: "/home/user",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and relative path with . and file",
|
||||||
|
withWorkDir: "/home/user",
|
||||||
|
p: "./file",
|
||||||
|
want: "/home/user/file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and absolute path",
|
||||||
|
withWorkDir: "/home/user",
|
||||||
|
p: "/file",
|
||||||
|
want: "/file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and non-unixy path prefixes workdir",
|
||||||
|
withWorkDir: "/home/user",
|
||||||
|
p: "C:\\file",
|
||||||
|
// This may look like a bug but it is the result of passing
|
||||||
|
// invalid input (a non-unixy path) to the server.
|
||||||
|
want: "/home/user/C:\\file",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// We don't need to initialize the Server further to test
|
||||||
|
// toLocalPath behavior.
|
||||||
|
s := &Server{}
|
||||||
|
if tt.withWorkDir != "" {
|
||||||
|
if err := WithServerWorkingDirectory(tt.withWorkDir)(s); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := s.toLocalPath(tt.p); got != tt.want {
|
||||||
|
t.Errorf("Server.toLocalPath() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) toLocalPath(p string) string {
|
||||||
|
if s.workDir != "" && !path.IsAbs(p) {
|
||||||
|
p = path.Join(s.workDir, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
lp := filepath.FromSlash(p)
|
||||||
|
|
||||||
|
if path.IsAbs(p) {
|
||||||
|
tmp := lp
|
||||||
|
for len(tmp) > 0 && tmp[0] == '\\' {
|
||||||
|
tmp = tmp[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.IsAbs(tmp) {
|
||||||
|
// If the FromSlash without any starting slashes is absolute,
|
||||||
|
// then we have a filepath encoded with a prefix '/'.
|
||||||
|
// e.g. "/C:/Windows" to "C:\\Windows"
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp += "\\"
|
||||||
|
|
||||||
|
if filepath.IsAbs(tmp) {
|
||||||
|
// If the FromSlash without any starting slashes but with extra end slash is absolute,
|
||||||
|
// then we have a filepath encoded with a prefix '/' and a dropped '/' at the end.
|
||||||
|
// e.g. "/C:" to "C:\\"
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lp
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package sftp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServer_toLocalPath(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
withWorkDir string
|
||||||
|
p string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty path with no workdir",
|
||||||
|
p: "",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path with no workdir",
|
||||||
|
p: "file",
|
||||||
|
want: "file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "absolute path with no workdir",
|
||||||
|
p: "/file",
|
||||||
|
want: "\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and empty path",
|
||||||
|
withWorkDir: "C:\\Users\\User",
|
||||||
|
p: "",
|
||||||
|
want: "C:\\Users\\User",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and relative path",
|
||||||
|
withWorkDir: "C:\\Users\\User",
|
||||||
|
p: "file",
|
||||||
|
want: "C:\\Users\\User\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and relative path with .",
|
||||||
|
withWorkDir: "C:\\Users\\User",
|
||||||
|
p: ".",
|
||||||
|
want: "C:\\Users\\User",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and relative path with . and file",
|
||||||
|
withWorkDir: "C:\\Users\\User",
|
||||||
|
p: "./file",
|
||||||
|
want: "C:\\Users\\User\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and absolute path",
|
||||||
|
withWorkDir: "C:\\Users\\User",
|
||||||
|
p: "/C:/file",
|
||||||
|
want: "C:\\file",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "workdir and non-unixy path prefixes workdir",
|
||||||
|
withWorkDir: "C:\\Users\\User",
|
||||||
|
p: "C:\\file",
|
||||||
|
// This may look like a bug but it is the result of passing
|
||||||
|
// invalid input (a non-unixy path) to the server.
|
||||||
|
want: "C:\\Users\\User\\C:\\file",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
// We don't need to initialize the Server further to test
|
||||||
|
// toLocalPath behavior.
|
||||||
|
s := &Server{}
|
||||||
|
if tt.withWorkDir != "" {
|
||||||
|
if err := WithServerWorkingDirectory(tt.withWorkDir)(s); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if got := s.toLocalPath(tt.p); got != tt.want {
|
||||||
|
t.Errorf("Server.toLocalPath() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue