Commit e048210e authored by Mark Sheahan's avatar Mark Sheahan

add Realpath client public fn and fix server impl

parent e84cc8c7
......@@ -611,6 +611,11 @@ func (c *Client) Rename(oldname, newname string) error {
}
}
// Realpath requests the canonical name (absolute path, symlinks resolved) of a requested path
func (c *Client) Realpath(path string) (string, error) {
return c.realpath(path)
}
func (c *Client) realpath(path string) (string, error) {
id := c.nextID()
typ, data, err := c.sendRequest(sshFxpRealpathPacket{
......
......@@ -578,6 +578,47 @@ func TestClientReadLink(t *testing.T) {
}
}
func TestClientRealpath(t *testing.T) {
sftp, cmd := testClient(t, READONLY, NO_DELAY)
defer cmd.Wait()
defer sftp.Close()
f, err := ioutil.TempFile("", "sftptest")
if err != nil {
t.Fatal(err)
}
defer os.Remove(f.Name())
realName, err := filepath.EvalSymlinks(f.Name())
if err != nil {
t.Fatal(err)
}
realName, err = filepath.Abs(realName)
if err != nil {
t.Fatal(err)
}
realName = filepath.Clean(realName)
linkName := realName + ".softlink"
// create a symlink that points at sftptest
if err := os.Symlink(realName, linkName); err != nil {
t.Fatal(err)
}
defer os.Remove(linkName)
// compare names
want := realName
got, err := sftp.Realpath(linkName)
if err != nil {
t.Fatal(err)
}
// check that realpath is valid
if want != got {
t.Fatalf("Realpath(%q): got %v", want, got)
}
}
func TestClientSymlink(t *testing.T) {
sftp, cmd := testClient(t, READWRITE, NO_DELAY)
defer cmd.Wait()
......
......@@ -361,7 +361,12 @@ func (p sshFxpReadlinkPacket) respond(svr *Server) error {
func (p sshFxpRealpathPacket) readonly() bool { return true }
func (p sshFxpRealpathPacket) respond(svr *Server) error {
f, err := filepath.Abs(p.Path)
f, err := filepath.EvalSymlinks(p.Path)
if err != nil {
return svr.sendPacket(statusFromError(p.ID, err))
}
f, err = filepath.Abs(f)
if err != nil {
return svr.sendPacket(statusFromError(p.ID, err))
}
......
......@@ -503,6 +503,63 @@ func TestServerSymlink(t *testing.T) {
}
}
func TestServerRealpath(t *testing.T) {
// create servers of each variant
listenerGo, hostGo, portGo := testServer(t, GOLANG_SFTP, READONLY)
listenerOp, hostOp, portOp := testServer(t, OPENSSH_SFTP, READONLY)
defer listenerGo.Close()
defer listenerOp.Close()
// create clients for each server
clientConfig := ssh.ClientConfig{
Config: ssh.Config{ },
User: "test",
Auth: []ssh.AuthMethod{ssh.Password("")},
}
clientConfigGo := clientConfig
clientConfigOp := clientConfig
sshClientGo, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", hostGo, portGo), &clientConfigGo)
if err != nil {
t.Fatal(err)
}
sshClientOp, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", hostOp, portOp), &clientConfigOp)
if err != nil {
t.Fatal(err)
}
sftpClientGo, err := NewClient(sshClientGo)
if err != nil {
t.Fatal(err)
}
sftpClientOp, err := NewClient(sshClientOp)
if err != nil {
t.Fatal(err)
}
// create a file, and a convoluted path under it
dir, err := ioutil.TempDir("", "sftptestServerRealpath")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
f1 := path.Join(dir, "a", "b", "b.txt")
f1strange := strings.Join([]string{dir, "a", "b", "c", "d", "e", "..", "..", "..", "b.txt"}, string(os.PathSeparator))
if err := os.MkdirAll(path.Join(dir, "a", "b", "c", "d", "e"), 0755); err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile(f1, []byte("hello"), 0644); err != nil {
t.Fatal(err)
}
// compare output from go and openssh
if outputGo, err := sftpClientGo.Realpath(f1strange); err != nil {
t.Fatalf("failed: %v %v", err, string(outputGo))
} else if outputOp, err := sftpClientOp.Realpath(f1strange); err != nil {
t.Fatalf("failed: %v %v", err, string(outputOp))
} else if outputGo != outputOp {
t.Fatalf("output differs:\ngo: '%v'\nop: '%v'\n", outputGo, outputOp)
}
}
func TestServerPut(t *testing.T) {
listenerGo, hostGo, portGo := testServer(t, GOLANG_SFTP, READONLY)
defer listenerGo.Close()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment