From 98b35dcfc3d7db2830c9b69a4ab0bed0049e01c9 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Wed, 2 Mar 2022 18:33:41 +0100 Subject: [PATCH 1/2] request server: add WithStartDirectory option --- request-server.go | 42 +++++++++++++++++++++++++++++++++--------- request-server_test.go | 35 +++++++++++++++++++++++++++++++++-- request.go | 14 ++++++++------ 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/request-server.go b/request-server.go index 5fa828bb..b7dadd6c 100644 --- a/request-server.go +++ b/request-server.go @@ -27,6 +27,8 @@ type RequestServer struct { *serverConn pktMgr *packetManager + startDirectory string + mu sync.RWMutex handleCount int openRequests map[string]*Request @@ -47,6 +49,14 @@ func WithRSAllocator() RequestServerOption { } } +// WithStartDirectory sets a start directory to use as base for relative paths. +// If unset the default is "/" +func WithStartDirectory(startDirectory string) RequestServerOption { + return func(rs *RequestServer) { + rs.startDirectory = cleanPath(startDirectory) + } +} + // NewRequestServer creates/allocates/returns new RequestServer. // Normally there will be one server per user-session. func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServerOption) *RequestServer { @@ -62,6 +72,8 @@ func NewRequestServer(rwc io.ReadWriteCloser, h Handlers, options ...RequestServ serverConn: svrConn, pktMgr: newPktMgr(svrConn), + startDirectory: "/", + openRequests: make(map[string]*Request), } @@ -210,11 +222,11 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR if realPather, ok := rs.Handlers.FileList.(RealPathFileLister); ok { realPath = realPather.RealPath(pkt.getPath()) } else { - realPath = cleanPath(pkt.getPath()) + realPath = cleanPathWithBase(rs.startDirectory, pkt.getPath()) } rpkt = cleanPacketPath(pkt, realPath) case *sshFxpOpendirPacket: - request := requestFromPacket(ctx, pkt) + request := requestFromPacket(ctx, pkt, rs.startDirectory) handle := rs.nextRequest(request) rpkt = request.opendir(rs.Handlers, pkt) if _, ok := rpkt.(*sshFxpHandlePacket); !ok { @@ -222,7 +234,7 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR rs.closeRequest(handle) } case *sshFxpOpenPacket: - request := requestFromPacket(ctx, pkt) + request := requestFromPacket(ctx, pkt, rs.startDirectory) handle := rs.nextRequest(request) rpkt = request.open(rs.Handlers, pkt) if _, ok := rpkt.(*sshFxpHandlePacket); !ok { @@ -235,7 +247,10 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR if !ok { rpkt = statusFromError(pkt.ID, EBADF) } else { - request = NewRequest("Stat", request.Filepath) + request = &Request{ + Method: "Stat", + Filepath: cleanPathWithBase(rs.startDirectory, request.Filepath), + } rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID) } case *sshFxpFsetstatPacket: @@ -244,15 +259,24 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR if !ok { rpkt = statusFromError(pkt.ID, EBADF) } else { - request = NewRequest("Setstat", request.Filepath) + request = &Request{ + Method: "Setstat", + Filepath: cleanPathWithBase(rs.startDirectory, request.Filepath), + } rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID) } case *sshFxpExtendedPacketPosixRename: - request := NewRequest("PosixRename", pkt.Oldpath) - request.Target = pkt.Newpath + request := &Request{ + Method: "PosixRename", + Filepath: cleanPathWithBase(rs.startDirectory, pkt.Oldpath), + Target: cleanPathWithBase(rs.startDirectory, pkt.Newpath), + } rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID) case *sshFxpExtendedPacketStatVFS: - request := NewRequest("StatVFS", pkt.Path) + request := &Request{ + Method: "StatVFS", + Filepath: cleanPathWithBase(rs.startDirectory, pkt.Path), + } rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID) case hasHandle: handle := pkt.getHandle() @@ -263,7 +287,7 @@ func (rs *RequestServer) packetWorker(ctx context.Context, pktChan chan orderedR rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID) } case hasPath: - request := requestFromPacket(ctx, pkt) + request := requestFromPacket(ctx, pkt, rs.startDirectory) rpkt = request.call(rs.Handlers, pkt, rs.pktMgr.alloc, orderID) request.close() default: diff --git a/request-server_test.go b/request-server_test.go index 88862fb0..c8e64d63 100644 --- a/request-server_test.go +++ b/request-server_test.go @@ -37,7 +37,7 @@ func (cs csPair) testHandler() *root { const sock = "/tmp/rstest.sock" -func clientRequestServerPair(t *testing.T) *csPair { +func clientRequestServerPair(t *testing.T, options ...RequestServerOption) *csPair { skipIfWindows(t) skipIfPlan9(t) @@ -62,7 +62,6 @@ func clientRequestServerPair(t *testing.T) *csPair { require.NoError(t, err) handlers := InMemHandler() - var options []RequestServerOption if *testAllocator { options = append(options, WithRSAllocator()) } @@ -781,6 +780,37 @@ func TestRequestStatVFSError(t *testing.T) { checkRequestServerAllocator(t, p) } +func TestRequestStartDirOption(t *testing.T) { + startDir := "/start/dir" + p := clientRequestServerPair(t, WithStartDirectory(startDir)) + defer p.Close() + + // create the start directory + err := p.cli.MkdirAll(startDir) + require.NoError(t, err) + // the working directory must be the defined start directory + wd, err := p.cli.Getwd() + require.NoError(t, err) + require.Equal(t, startDir, wd) + // upload a file using a relative path, it must be uploaded to the start directory + fileName := "file.txt" + _, err = putTestFile(p.cli, fileName, "") + require.NoError(t, err) + // we must be able to stat the file using both a relative and an absolute path + for _, filePath := range []string{fileName, path.Join(startDir, fileName)} { + fi, err := p.cli.Stat(filePath) + require.NoError(t, err) + assert.Equal(t, fileName, fi.Name()) + } + // list dir contents using a relative path + entries, err := p.cli.ReadDir(".") + assert.NoError(t, err) + assert.Len(t, entries, 1) + // delete the file using a relative path + err = p.cli.Remove(fileName) + assert.NoError(t, err) +} + func TestCleanDisconnect(t *testing.T) { p := clientRequestServerPair(t) defer p.Close() @@ -831,6 +861,7 @@ func TestRealPath(t *testing.T) { func TestCleanPath(t *testing.T) { assert.Equal(t, "/", cleanPath("/")) assert.Equal(t, "/", cleanPath(".")) + assert.Equal(t, "/", cleanPath("")) assert.Equal(t, "/", cleanPath("/.")) assert.Equal(t, "/", cleanPath("/a/..")) assert.Equal(t, "/a/c", cleanPath("/a/b/../c")) diff --git a/request.go b/request.go index c6da4b60..116c27aa 100644 --- a/request.go +++ b/request.go @@ -168,9 +168,11 @@ func (r *Request) copy() *Request { } // New Request initialized based on packet data -func requestFromPacket(ctx context.Context, pkt hasPath) *Request { - method := requestMethod(pkt) - request := NewRequest(method, pkt.getPath()) +func requestFromPacket(ctx context.Context, pkt hasPath, baseDir string) *Request { + request := &Request{ + Method: requestMethod(pkt), + Filepath: cleanPathWithBase(baseDir, pkt.getPath()), + } request.ctx, request.cancelCtx = context.WithCancel(ctx) switch p := pkt.(type) { @@ -180,13 +182,13 @@ func requestFromPacket(ctx context.Context, pkt hasPath) *Request { request.Flags = p.Flags request.Attrs = p.Attrs.([]byte) case *sshFxpRenamePacket: - request.Target = cleanPath(p.Newpath) + request.Target = cleanPathWithBase(baseDir, p.Newpath) case *sshFxpSymlinkPacket: // NOTE: given a POSIX compliant signature: symlink(target, linkpath string) // this makes Request.Target the linkpath, and Request.Filepath the target. - request.Target = cleanPath(p.Linkpath) + request.Target = cleanPathWithBase(baseDir, p.Linkpath) case *sshFxpExtendedPacketHardlink: - request.Target = cleanPath(p.Newpath) + request.Target = cleanPathWithBase(baseDir, p.Newpath) } return request } From 03407a734b9efff4722d28980fea4c87745fe64b Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Wed, 2 Mar 2022 18:44:54 +0100 Subject: [PATCH 2/2] mark RealPathFileLister as deprecated --- request-interfaces.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/request-interfaces.go b/request-interfaces.go index c8c424c9..e5dc49bb 100644 --- a/request-interfaces.go +++ b/request-interfaces.go @@ -90,6 +90,8 @@ type LstatFileLister interface { // We use "/" as start directory for relative paths, implementing this // interface you can customize the start directory. // You have to return an absolute POSIX path. +// +// Deprecated: if you want to set a start directory use WithStartDirectory RequestServerOption instead. type RealPathFileLister interface { FileLister RealPath(string) string