@@ -16,11 +16,38 @@ type FileSystem interface {
16
16
Mkdir (name string , perm os.FileMode ) error
17
17
}
18
18
19
+ type FileSystemOpen interface {
20
+ FileSystem
21
+ OpenFile (name string , flag int , perm os.FileMode ) (file * os.File , err error )
22
+ }
23
+
24
+ type FileSystemSFTPOpen interface {
25
+ FileSystem
26
+ OpenFile (path string , f int ) (* File , error ) // sftp package has a strange OpenFile method with no perm
27
+ }
28
+
29
+ // common subset of os.File and sftp.File
30
+ type svrFile interface {
31
+ Chmod (mode os.FileMode ) error
32
+ Chown (uid , gid int ) error
33
+ Close () error
34
+ Read (b []byte ) (int , error )
35
+ Seek (offset int64 , whence int ) (int64 , error )
36
+ Stat () (os.FileInfo , error )
37
+ Truncate (size int64 ) error
38
+ Write (b []byte ) (int , error )
39
+ // func (f *File) WriteTo(w io.Writer) (int64, error) // not in os
40
+ // func (f *File) ReadFrom(r io.Reader) (int64, error) // not in os
41
+ }
42
+
19
43
type nativeFs struct {
20
44
}
21
45
22
46
func (nfs * nativeFs ) Lstat (p string ) (os.FileInfo , error ) { return os .Lstat (p ) }
23
47
func (nfs * nativeFs ) Mkdir (name string , perm os.FileMode ) error { return os .Mkdir (name , perm ) }
48
+ func (nfs * nativeFs ) OpenFile (name string , flag int , perm os.FileMode ) (file * os.File , err error ) {
49
+ return os .OpenFile (name , flag , perm )
50
+ }
24
51
25
52
type Server struct {
26
53
in io.Reader
@@ -29,18 +56,36 @@ type Server struct {
29
56
lastId uint32
30
57
fs FileSystem
31
58
pktChan chan serverRespondablePacket
32
- openFiles map [string ]* svrFile
33
- openFilesLock * sync.Mutex
59
+ openFiles map [string ]svrFile
60
+ openFilesLock * sync.RWMutex
61
+ handleCount int
62
+ }
63
+
64
+ func (svr * Server ) nextHandle (f svrFile ) string {
65
+ svr .openFilesLock .Lock ()
66
+ defer svr .openFilesLock .Unlock ()
67
+ svr .handleCount ++
68
+ handle := fmt .Sprintf ("%d" , svr .handleCount )
69
+ svr .openFiles [handle ] = f
70
+ return handle
71
+ }
72
+
73
+ func (svr * Server ) closeHandle (handle string ) error {
74
+ svr .openFilesLock .Lock ()
75
+ defer svr .openFilesLock .Unlock ()
76
+ if f , ok := svr .openFiles [handle ]; ok {
77
+ delete (svr .openFiles , handle )
78
+ return f .Close ()
79
+ } else {
80
+ return syscall .EBADF
81
+ }
34
82
}
35
83
36
84
type serverRespondablePacket interface {
37
85
encoding.BinaryUnmarshaler
38
86
respond (svr * Server ) error
39
87
}
40
88
41
- type svrFile struct {
42
- }
43
-
44
89
// Creates a new server instance around the provided streams.
45
90
// A subsequent call to Run() is required.
46
91
func NewServer (in io.Reader , out io.Writer , rootDir string ) (* Server , error ) {
@@ -57,8 +102,8 @@ func NewServer(in io.Reader, out io.Writer, rootDir string) (*Server, error) {
57
102
rootDir : rootDir ,
58
103
fs : & nativeFs {},
59
104
pktChan : make (chan serverRespondablePacket , 4 ),
60
- openFiles : map [string ]* svrFile {},
61
- openFilesLock : & sync.Mutex {},
105
+ openFiles : map [string ]svrFile {},
106
+ openFilesLock : & sync.RWMutex {},
62
107
}, nil
63
108
}
64
109
@@ -94,7 +139,9 @@ func (svr *Server) decodePacket(pktType fxp, pktBytes []byte) (serverRespondable
94
139
pkt = & sshFxpLstatPacket {}
95
140
case ssh_FXP_VERSION :
96
141
case ssh_FXP_OPEN :
142
+ pkt = & sshFxpOpenPacket {}
97
143
case ssh_FXP_CLOSE :
144
+ pkt = & sshFxpClosePacket {}
98
145
case ssh_FXP_READ :
99
146
case ssh_FXP_WRITE :
100
147
case ssh_FXP_FSTAT :
@@ -170,16 +217,49 @@ func (p sshFxpMkdirPacket) respond(svr *Server) error {
170
217
return svr .sendPacket (statusFromError (p .Id , err ))
171
218
}
172
219
173
- type sshFxpStatusPacket struct {
174
- Id uint32
175
- StatusError
220
+ func (p sshFxpOpenPacket ) respond (svr * Server ) error {
221
+ osFlags := 0
222
+ if p .Pflags & ssh_FXF_READ != 0 && p .Pflags & ssh_FXF_WRITE != 0 {
223
+ osFlags |= os .O_RDWR
224
+ } else if p .Pflags & ssh_FXF_READ != 0 {
225
+ osFlags |= os .O_RDONLY
226
+ } else if p .Pflags & ssh_FXF_WRITE != 0 {
227
+ osFlags |= os .O_WRONLY
228
+ }
229
+ if p .Pflags & ssh_FXF_APPEND != 0 {
230
+ osFlags |= os .O_APPEND
231
+ }
232
+ if p .Pflags & ssh_FXF_CREAT != 0 {
233
+ osFlags |= os .O_CREATE
234
+ }
235
+ if p .Pflags & ssh_FXF_TRUNC != 0 {
236
+ osFlags |= os .O_TRUNC
237
+ }
238
+ if p .Pflags & ssh_FXF_EXCL != 0 {
239
+ osFlags |= os .O_EXCL
240
+ }
241
+
242
+ if fso , ok := svr .fs .(FileSystemOpen ); ok {
243
+ if f , err := fso .OpenFile (p .Path , osFlags , 0644 ); err != nil {
244
+ return svr .sendPacket (statusFromError (p .Id , err ))
245
+ } else {
246
+ handle := svr .nextHandle (f )
247
+ return svr .sendPacket (sshFxpHandlePacket {p .Id , handle })
248
+ }
249
+ } else if sftpo , ok := svr .fs .(FileSystemSFTPOpen ); ok {
250
+ if f , err := sftpo .OpenFile (p .Path , osFlags ); err != nil {
251
+ return svr .sendPacket (statusFromError (p .Id , err ))
252
+ } else {
253
+ handle := svr .nextHandle (f )
254
+ return svr .sendPacket (sshFxpHandlePacket {p .Id , handle })
255
+ }
256
+ } else {
257
+ return svr .sendPacket (statusFromError (p .Id , fmt .Errorf ("unknown filesystem backend" )))
258
+ }
176
259
}
177
260
178
- func (p sshFxpStatusPacket ) MarshalBinary () ([]byte , error ) {
179
- b := []byte {ssh_FXP_STATUS }
180
- b = marshalUint32 (b , p .Id )
181
- b = marshalStatus (b , p .StatusError )
182
- return b , nil
261
+ func (p sshFxpClosePacket ) respond (svr * Server ) error {
262
+ return svr .sendPacket (statusFromError (p .Id , svr .closeHandle (p .Handle )))
183
263
}
184
264
185
265
func statusFromError (id uint32 , err error ) sshFxpStatusPacket {
0 commit comments