Resolved conflict with SftpServerWorkerCount
Splitted cleanPath into cleanPacketPath and cleanPath for better handling of slashes in file paths
Added test for cleanPath func
Removed code duplication => filepath.ToSlash(filepath.Clean(...)) => cleanPath(...)
Fixed tests for runLs to match year or time
Renamed constants to fit hound rules
diff --git a/packet-manager.go b/packet-manager.go
index 6d1a8e5..906b3ee 100644
--- a/packet-manager.go
+++ b/packet-manager.go
@@ -25,11 +25,11 @@
func newPktMgr(sender packetSender) packetManager {
s := packetManager{
- requests: make(chan requestPacket, sftpServerWorkerCount),
- responses: make(chan responsePacket, sftpServerWorkerCount),
+ requests: make(chan requestPacket, SftpServerWorkerCount),
+ responses: make(chan responsePacket, SftpServerWorkerCount),
fini: make(chan struct{}),
- incoming: make([]uint32, 0, sftpServerWorkerCount),
- outgoing: make([]responsePacket, 0, sftpServerWorkerCount),
+ incoming: make([]uint32, 0, SftpServerWorkerCount),
+ outgoing: make([]responsePacket, 0, SftpServerWorkerCount),
sender: sender,
working: &sync.WaitGroup{},
}
@@ -41,7 +41,7 @@
// send id of 0 for packets without id
func (s packetManager) incomingPacket(pkt requestPacket) {
s.working.Add(1)
- s.requests <- pkt // buffer == sftpServerWorkerCount
+ s.requests <- pkt // buffer == SftpServerWorkerCount
}
// register outgoing packets as being ready
@@ -63,15 +63,15 @@
// transfers.
func (s *packetManager) workerChan(runWorker func(requestChan)) requestChan {
- rwChan := make(chan requestPacket, sftpServerWorkerCount)
- for i := 0; i < sftpServerWorkerCount; i++ {
+ rwChan := make(chan requestPacket, SftpServerWorkerCount)
+ for i := 0; i < SftpServerWorkerCount; i++ {
runWorker(rwChan)
}
cmdChan := make(chan requestPacket)
runWorker(cmdChan)
- pktChan := make(chan requestPacket, sftpServerWorkerCount)
+ pktChan := make(chan requestPacket, SftpServerWorkerCount)
go func() {
// start with cmdChan
curChan := cmdChan
@@ -147,10 +147,10 @@
}
}
-func outfilter(o []responsePacket) []uint32 {
- res := make([]uint32, 0, len(o))
- for _, v := range o {
- res = append(res, v.id())
- }
- return res
-}
+//func outfilter(o []responsePacket) []uint32 {
+// res := make([]uint32, 0, len(o))
+// for _, v := range o {
+// res = append(res, v.id())
+// }
+// return res
+//}
diff --git a/request-server.go b/request-server.go
index d0b1ba6..9e87559 100644
--- a/request-server.go
+++ b/request-server.go
@@ -131,7 +131,7 @@
rs.closeRequest(handle)
rpkt = statusFromError(pkt, nil)
case *sshFxpRealpathPacket:
- rpkt = cleanPath(pkt)
+ rpkt = cleanPacketPath(pkt)
case isOpener:
handle := rs.nextRequest(requestFromPacket(pkt))
rpkt = sshFxpHandlePacket{pkt.id(), handle}
@@ -181,25 +181,26 @@
return nil
}
-func cleanPath(pkt *sshFxpRealpathPacket) responsePacket {
- path := pkt.getPath()
- if !filepath.IsAbs(path) {
- // prevent double slash (e.g. on windows, / paths are not absolute)
- path = "/" + strings.TrimPrefix(path, "/")
- } // all paths are absolute
-
- cleaned_path := filepath.ToSlash(filepath.Clean(path))
-
+func cleanPacketPath(pkt *sshFxpRealpathPacket) responsePacket {
+ path := cleanPath(pkt.getPath())
return &sshFxpNamePacket{
ID: pkt.id(),
NameAttrs: []sshFxpNameAttr{{
- Name: cleaned_path,
- LongName: cleaned_path,
+ Name: path,
+ LongName: path,
Attrs: emptyFileStat,
}},
}
}
+func cleanPath(path string) string {
+ cleanSlashPath := filepath.ToSlash(filepath.Clean(path))
+ if !strings.HasPrefix(cleanSlashPath, "/") {
+ return "/" + cleanSlashPath
+ }
+ return cleanSlashPath
+}
+
func (rs *RequestServer) handle(request Request, pkt requestPacket) responsePacket {
// fmt.Println("Request Method: ", request.Method)
rpkt, err := request.handle(rs.Handlers)
diff --git a/request-server_test.go b/request-server_test.go
index ee9621b..b12561d 100644
--- a/request-server_test.go
+++ b/request-server_test.go
@@ -327,3 +327,20 @@
names := []string{di[18].Name(), di[81].Name()}
assert.Equal(t, []string{"foo_18", "foo_81"}, names)
}
+
+func TestCleanPath(t *testing.T) {
+ assert.Equal(t, "/", cleanPath("/"))
+ assert.Equal(t, "/", cleanPath("//"))
+ assert.Equal(t, "/a", cleanPath("/a/"))
+ assert.Equal(t, "/a", cleanPath("a/"))
+ assert.Equal(t, "/a/b/c", cleanPath("/a//b//c/"))
+
+ // filepath.ToSlash does not touch \ as char on unix systems, so os.PathSeparator is used for windows compatible tests
+ bslash := string(os.PathSeparator)
+ assert.Equal(t, "/", cleanPath(bslash))
+ assert.Equal(t, "/", cleanPath(bslash+bslash))
+ assert.Equal(t, "/a", cleanPath(bslash+"a"+bslash))
+ assert.Equal(t, "/a", cleanPath("a"+bslash))
+ assert.Equal(t, "/a/b/c", cleanPath(bslash+"a"+bslash+bslash+"b"+bslash+bslash+"c"+bslash))
+
+}
diff --git a/request.go b/request.go
index 5b7502f..ac07475 100644
--- a/request.go
+++ b/request.go
@@ -52,17 +52,17 @@
request.Flags = p.Flags
request.Attrs = p.Attrs.([]byte)
case *sshFxpRenamePacket:
- request.Target = filepath.ToSlash(filepath.Clean(p.Newpath))
+ request.Target = cleanPath(p.Newpath)
case *sshFxpSymlinkPacket:
- request.Target = filepath.ToSlash(filepath.Clean(p.Linkpath))
+ request.Target = cleanPath(p.Linkpath)
}
return request
}
// NewRequest creates a new Request object.
func NewRequest(method, path string) Request {
- request := Request{Method: method, Filepath: filepath.ToSlash(filepath.Clean(path))}
- request.packets = make(chan packet_data, sftpServerWorkerCount)
+ request := Request{Method: method, Filepath: cleanPath(path)}
+ request.packets = make(chan packet_data, SftpServerWorkerCount)
request.state = &state{}
request.stateLock = &sync.RWMutex{}
return request
diff --git a/request_test.go b/request_test.go
index e537fc1..6eda6ff 100644
--- a/request_test.go
+++ b/request_test.go
@@ -65,13 +65,13 @@
Method: method,
Attrs: []byte("foo"),
Target: "foo",
- packets: make(chan packet_data, sftpServerWorkerCount),
+ packets: make(chan packet_data, SftpServerWorkerCount),
state: &state{},
stateLock: &sync.RWMutex{},
}
for _, p := range []packet_data{
- packet_data{id: 1, data: filecontents[:5], length: 5},
- packet_data{id: 2, data: filecontents[5:], length: 5, offset: 5}} {
+ {id: 1, data: filecontents[:5], length: 5},
+ {id: 2, data: filecontents[5:], length: 5, offset: 5}} {
request.packets <- p
}
return request
diff --git a/server.go b/server.go
index 9168bc6..80a1288 100644
--- a/server.go
+++ b/server.go
@@ -18,7 +18,7 @@
)
const (
- sftpServerWorkerCount = 8
+ SftpServerWorkerCount = 8
)
// Server is an SSH File Transfer Protocol (sftp) server.
@@ -230,8 +230,7 @@
if err != nil {
return s.sendError(p, err)
}
- f = filepath.Clean(f)
- f = filepath.ToSlash(f) // make path more Unix like on windows servers
+ f = cleanPath(f)
return s.sendPacket(sshFxpNamePacket{
ID: p.ID,
NameAttrs: []sshFxpNameAttr{{
diff --git a/server_test.go b/server_test.go
index 87e4022..f2f034a 100644
--- a/server_test.go
+++ b/server_test.go
@@ -10,23 +10,22 @@
)
const (
- TYPE_DIRECTORY = "d"
- TYPE_FILE = "[^d]"
+ typeDirectory = "d"
+ typeFile = "[^d]"
)
func TestRunLsWithExamplesDirectory(t *testing.T) {
path := "examples"
- item,_ := os.Stat(path)
+ item, _ := os.Stat(path)
result := runLs(path, item)
- runLsTestHelper(t, result, TYPE_DIRECTORY, path)
+ runLsTestHelper(t, result, typeDirectory, path)
}
-
func TestRunLsWithLicensesFile(t *testing.T) {
path := "LICENSE"
- item,_ := os.Stat(path)
+ item, _ := os.Stat(path)
result := runLs(path, item)
- runLsTestHelper(t, result, TYPE_FILE, path)
+ runLsTestHelper(t, result, typeFile, path)
}
/*
@@ -71,83 +70,87 @@
// permissions (len 10, "drwxr-xr-x")
got := result[0:10]
- if ok, err := regexp.MatchString("^" + expectedType + "[rwx-]{9}$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): permission field mismatch, expected dir, got: %#v, err: %#v", path, got, err)
+ if ok, err := regexp.MatchString("^"+expectedType+"[rwx-]{9}$", got); !ok {
+ t.Errorf("runLs(%#v, *FileInfo): permission field mismatch, expected dir, got: %#v, err: %#v", path, got, err)
}
// space
got = result[10:11]
if ok, err := regexp.MatchString("^\\s$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): spacer 1 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): spacer 1 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
}
// link count (len 3, number)
got = result[12:15]
if ok, err := regexp.MatchString("^\\s*[0-9]+$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): link count field mismatch, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): link count field mismatch, got: %#v, err: %#v", path, got, err)
}
// spacer
got = result[15:16]
if ok, err := regexp.MatchString("^\\s$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): spacer 2 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): spacer 2 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
}
// username / uid (len 8, number or string)
got = result[16:24]
if ok, err := regexp.MatchString("^[^\\s]{1,8}\\s*$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): username / uid mismatch, expected user, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): username / uid mismatch, expected user, got: %#v, err: %#v", path, got, err)
}
// spacer
got = result[24:25]
if ok, err := regexp.MatchString("^\\s$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): spacer 3 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): spacer 3 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
}
// groupname / gid (len 8, number or string)
got = result[25:33]
if ok, err := regexp.MatchString("^[^\\s]{1,8}\\s*$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): groupname / gid mismatch, expected group, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): groupname / gid mismatch, expected group, got: %#v, err: %#v", path, got, err)
}
-
// spacer
got = result[33:34]
if ok, err := regexp.MatchString("^\\s$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): spacer 4 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): spacer 4 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
}
// filesize (len 8)
got = result[34:42]
if ok, err := regexp.MatchString("^\\s*[0-9]+$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): filesize field mismatch, expected size in bytes, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): filesize field mismatch, expected size in bytes, got: %#v, err: %#v", path, got, err)
}
// spacer
got = result[42:43]
if ok, err := regexp.MatchString("^\\s$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): spacer 5 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): spacer 5 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
}
// mod time (len 12, e.g. Aug 9 19:46)
got = result[43:55]
layout := "Jan 2 15:04"
_, err := time.Parse(layout, got)
+
if err != nil {
- t.Errorf("runLs(%#v, *FileInfo): mod time field mismatch, expected date layout %s, got: %#v, err: %#v", path, layout, got, err)
+ layout = "Jan 2 2006"
+ _, err = time.Parse(layout, got)
+ }
+ if err != nil {
+ t.Errorf("runLs(%#v, *FileInfo): mod time field mismatch, expected date layout %s, got: %#v, err: %#v", path, layout, got, err)
}
// spacer
got = result[55:56]
if ok, err := regexp.MatchString("^\\s$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): spacer 6 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): spacer 6 mismatch, expected whitespace, got: %#v, err: %#v", path, got, err)
}
// filename
got = result[56:]
if ok, err := regexp.MatchString("^"+path+"$", got); !ok {
- t.Errorf("runLs(%#v, *FileInfo): name field mismatch, expected examples, got: %#v, err: %#v", path, got, err)
+ t.Errorf("runLs(%#v, *FileInfo): name field mismatch, expected examples, got: %#v, err: %#v", path, got, err)
}
}