| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | package sftp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sftp server counterpart
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding" | 
					
						
							| 
									
										
										
										
											2021-06-05 05:18:41 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2016-01-11 04:10:18 +08:00
										 |  |  | 	"io/ioutil" | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2015-08-06 03:57:28 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2024-01-19 09:23:22 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-07 14:55:15 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	// SftpServerWorkerCount defines the number of workers for the SFTP server
 | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | 	SftpServerWorkerCount = 8 | 
					
						
							| 
									
										
										
										
											2015-09-07 14:55:15 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Server is an SSH File Transfer Protocol (sftp) server.
 | 
					
						
							|  |  |  | // This is intended to provide the sftp subsystem to an ssh server daemon.
 | 
					
						
							|  |  |  | // This implementation currently supports most of sftp server protocol version 3,
 | 
					
						
							| 
									
										
										
										
											2023-03-28 01:05:24 +08:00
										 |  |  | // as specified at https://filezilla-project.org/specs/draft-ietf-secsh-filexfer-02.txt
 | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | type Server struct { | 
					
						
							| 
									
										
										
										
											2017-07-04 08:38:09 +08:00
										 |  |  | 	*serverConn | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 	debugStream   io.Writer | 
					
						
							|  |  |  | 	readOnly      bool | 
					
						
							| 
									
										
										
										
											2017-08-21 06:23:55 +08:00
										 |  |  | 	pktMgr        *packetManager | 
					
						
							| 
									
										
										
										
											2015-09-08 13:50:46 +08:00
										 |  |  | 	openFiles     map[string]*os.File | 
					
						
							| 
									
										
										
										
											2016-05-29 14:32:05 +08:00
										 |  |  | 	openFilesLock sync.RWMutex | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	handleCount   int | 
					
						
							| 
									
										
										
										
											2022-10-14 18:33:40 +08:00
										 |  |  | 	workDir       string | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-08 13:50:46 +08:00
										 |  |  | func (svr *Server) nextHandle(f *os.File) string { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	svr.openFilesLock.Lock() | 
					
						
							|  |  |  | 	defer svr.openFilesLock.Unlock() | 
					
						
							|  |  |  | 	svr.handleCount++ | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	handle := strconv.Itoa(svr.handleCount) | 
					
						
							| 
									
										
										
										
											2015-09-08 13:50:46 +08:00
										 |  |  | 	svr.openFiles[handle] = f | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	return handle | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (svr *Server) closeHandle(handle string) error { | 
					
						
							|  |  |  | 	svr.openFilesLock.Lock() | 
					
						
							|  |  |  | 	defer svr.openFilesLock.Unlock() | 
					
						
							|  |  |  | 	if f, ok := svr.openFiles[handle]; ok { | 
					
						
							|  |  |  | 		delete(svr.openFiles, handle) | 
					
						
							|  |  |  | 		return f.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-11 00:11:47 +08:00
										 |  |  | 	return EBADF | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-08 13:50:46 +08:00
										 |  |  | func (svr *Server) getHandle(handle string) (*os.File, bool) { | 
					
						
							| 
									
										
										
										
											2015-07-30 08:24:24 +08:00
										 |  |  | 	svr.openFilesLock.RLock() | 
					
						
							|  |  |  | 	defer svr.openFilesLock.RUnlock() | 
					
						
							|  |  |  | 	f, ok := svr.openFiles[handle] | 
					
						
							|  |  |  | 	return f, ok | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | type serverRespondablePacket interface { | 
					
						
							|  |  |  | 	encoding.BinaryUnmarshaler | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	id() uint32 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	respond(svr *Server) responsePacket | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-11 04:10:18 +08:00
										 |  |  | // NewServer creates a new Server instance around the provided streams, serving
 | 
					
						
							| 
									
										
										
										
											2016-01-17 01:41:01 +08:00
										 |  |  | // content from the root of the filesystem.  Optionally, ServerOption
 | 
					
						
							| 
									
										
										
										
											2016-01-11 04:10:18 +08:00
										 |  |  | // functions may be specified to further configure the Server.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // A subsequent call to Serve() is required to begin serving files over SFTP.
 | 
					
						
							| 
									
										
										
										
											2016-05-29 14:32:05 +08:00
										 |  |  | func NewServer(rwc io.ReadWriteCloser, options ...ServerOption) (*Server, error) { | 
					
						
							| 
									
										
										
										
											2017-07-04 08:38:09 +08:00
										 |  |  | 	svrConn := &serverConn{ | 
					
						
							| 
									
										
										
										
											2017-03-15 09:02:17 +08:00
										 |  |  | 		conn: conn{ | 
					
						
							|  |  |  | 			Reader:      rwc, | 
					
						
							|  |  |  | 			WriteCloser: rwc, | 
					
						
							| 
									
										
										
										
											2016-06-15 09:50:02 +08:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-03-15 09:02:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	s := &Server{ | 
					
						
							|  |  |  | 		serverConn:  svrConn, | 
					
						
							| 
									
										
										
										
											2016-05-29 14:32:05 +08:00
										 |  |  | 		debugStream: ioutil.Discard, | 
					
						
							| 
									
										
										
										
											2017-07-04 08:38:09 +08:00
										 |  |  | 		pktMgr:      newPktMgr(svrConn), | 
					
						
							| 
									
										
										
										
											2016-05-29 14:32:05 +08:00
										 |  |  | 		openFiles:   make(map[string]*os.File), | 
					
						
							| 
									
										
										
										
											2016-01-11 04:10:18 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, o := range options { | 
					
						
							|  |  |  | 		if err := o(s); err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return s, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // A ServerOption is a function which applies configuration to a Server.
 | 
					
						
							|  |  |  | type ServerOption func(*Server) error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // WithDebug enables Server debugging output to the supplied io.Writer.
 | 
					
						
							| 
									
										
										
										
											2016-01-12 01:21:15 +08:00
										 |  |  | func WithDebug(w io.Writer) ServerOption { | 
					
						
							| 
									
										
										
										
											2016-01-11 04:10:18 +08:00
										 |  |  | 	return func(s *Server) error { | 
					
						
							|  |  |  | 		s.debugStream = w | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ReadOnly configures a Server to serve files in read-only mode.
 | 
					
						
							| 
									
										
										
										
											2016-01-12 01:21:15 +08:00
										 |  |  | func ReadOnly() ServerOption { | 
					
						
							| 
									
										
										
										
											2016-01-11 04:10:18 +08:00
										 |  |  | 	return func(s *Server) error { | 
					
						
							|  |  |  | 		s.readOnly = true | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-18 16:36:07 +08:00
										 |  |  | // WithAllocator enable the allocator.
 | 
					
						
							|  |  |  | // After processing a packet we keep in memory the allocated slices
 | 
					
						
							|  |  |  | // and we reuse them for new packets.
 | 
					
						
							|  |  |  | // The allocator is experimental
 | 
					
						
							|  |  |  | func WithAllocator() ServerOption { | 
					
						
							|  |  |  | 	return func(s *Server) error { | 
					
						
							|  |  |  | 		alloc := newAllocator() | 
					
						
							|  |  |  | 		s.pktMgr.alloc = alloc | 
					
						
							|  |  |  | 		s.conn.alloc = alloc | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-14 18:33:40 +08:00
										 |  |  | // WithServerWorkingDirectory sets a working directory to use as base
 | 
					
						
							|  |  |  | // for relative paths.
 | 
					
						
							|  |  |  | // If unset the default is current working directory (os.Getwd).
 | 
					
						
							|  |  |  | func WithServerWorkingDirectory(workDir string) ServerOption { | 
					
						
							|  |  |  | 	return func(s *Server) error { | 
					
						
							|  |  |  | 		s.workDir = cleanPath(workDir) | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-07 10:36:47 +08:00
										 |  |  | type rxPacket struct { | 
					
						
							|  |  |  | 	pktType  fxp | 
					
						
							|  |  |  | 	pktBytes []byte | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Up to N parallel servers
 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | func (svr *Server) sftpServerWorker(pktChan chan orderedRequest) error { | 
					
						
							| 
									
										
										
										
											2017-03-26 08:07:47 +08:00
										 |  |  | 	for pkt := range pktChan { | 
					
						
							| 
									
										
										
										
											2017-03-14 06:54:55 +08:00
										 |  |  | 		// readonly checks
 | 
					
						
							| 
									
										
										
										
											2017-03-14 07:02:25 +08:00
										 |  |  | 		readonly := true | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 		switch pkt := pkt.requestPacket.(type) { | 
					
						
							| 
									
										
										
										
											2017-03-14 06:54:55 +08:00
										 |  |  | 		case notReadOnly: | 
					
						
							|  |  |  | 			readonly = false | 
					
						
							| 
									
										
										
										
											2016-05-19 12:49:23 +08:00
										 |  |  | 		case *sshFxpOpenPacket: | 
					
						
							|  |  |  | 			readonly = pkt.readonly() | 
					
						
							| 
									
										
										
										
											2016-06-13 12:45:13 +08:00
										 |  |  | 		case *sshFxpExtendedPacket: | 
					
						
							| 
									
										
										
										
											2018-03-19 22:32:22 +08:00
										 |  |  | 			readonly = pkt.readonly() | 
					
						
							| 
									
										
										
										
											2016-05-19 12:49:23 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 		// If server is operating read-only and a write operation is requested,
 | 
					
						
							|  |  |  | 		// return permission denied
 | 
					
						
							| 
									
										
										
										
											2016-05-19 12:49:23 +08:00
										 |  |  | 		if !readonly && svr.readOnly { | 
					
						
							| 
									
										
										
										
											2020-03-15 02:42:19 +08:00
										 |  |  | 			svr.pktMgr.readyPacket( | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 				svr.pktMgr.newOrderedResponse(statusFromError(pkt.id(), syscall.EPERM), pkt.orderID()), | 
					
						
							| 
									
										
										
										
											2020-03-18 16:36:07 +08:00
										 |  |  | 			) | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		if err := handlePacket(svr, pkt); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2016-05-19 13:04:32 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | func handlePacket(s *Server, p orderedRequest) error { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	var rpkt responsePacket | 
					
						
							| 
									
										
										
										
											2020-03-15 02:42:19 +08:00
										 |  |  | 	orderID := p.orderID() | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 	switch p := p.requestPacket.(type) { | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxInitPacket: | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = &sshFxVersionPacket{ | 
					
						
							| 
									
										
										
										
											2019-08-27 15:18:15 +08:00
										 |  |  | 			Version:    sftpProtocolVersion, | 
					
						
							|  |  |  | 			Extensions: sftpExtensions, | 
					
						
							| 
									
										
										
										
											2019-05-25 03:23:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpStatPacket: | 
					
						
							|  |  |  | 		// stat the requested file
 | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		info, err := os.Stat(s.toLocalPath(p.Path)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = &sshFxpStatResponse{ | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 			ID:   p.ID, | 
					
						
							|  |  |  | 			info: info, | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpLstatPacket: | 
					
						
							|  |  |  | 		// stat the requested file
 | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		info, err := os.Lstat(s.toLocalPath(p.Path)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = &sshFxpStatResponse{ | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 			ID:   p.ID, | 
					
						
							|  |  |  | 			info: info, | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpFstatPacket: | 
					
						
							|  |  |  | 		f, ok := s.getHandle(p.Handle) | 
					
						
							| 
									
										
										
										
											2020-09-11 00:11:47 +08:00
										 |  |  | 		var err error = EBADF | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		var info os.FileInfo | 
					
						
							|  |  |  | 		if ok { | 
					
						
							|  |  |  | 			info, err = f.Stat() | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = &sshFxpStatResponse{ | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 				ID:   p.ID, | 
					
						
							|  |  |  | 				info: info, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case *sshFxpMkdirPacket: | 
					
						
							|  |  |  | 		// TODO FIXME: ignore flags field
 | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		err := os.Mkdir(s.toLocalPath(p.Path), 0o755) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpRmdirPacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		err := os.Remove(s.toLocalPath(p.Path)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpRemovePacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		err := os.Remove(s.toLocalPath(p.Filename)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpRenamePacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		err := os.Rename(s.toLocalPath(p.Oldpath), s.toLocalPath(p.Newpath)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpSymlinkPacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		err := os.Symlink(s.toLocalPath(p.Targetpath), s.toLocalPath(p.Linkpath)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpClosePacket: | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, s.closeHandle(p.Handle)) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpReadlinkPacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		f, err := os.Readlink(s.toLocalPath(p.Path)) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = &sshFxpNamePacket{ | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 			ID: p.ID, | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			NameAttrs: []*sshFxpNameAttr{ | 
					
						
							| 
									
										
										
										
											2021-02-23 05:29:35 +08:00
										 |  |  | 				{ | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 					Name:     f, | 
					
						
							|  |  |  | 					LongName: f, | 
					
						
							|  |  |  | 					Attrs:    emptyFileStat, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	case *sshFxpRealpathPacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		f, err := filepath.Abs(s.toLocalPath(p.Path)) | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | 		f = cleanPath(f) | 
					
						
							| 
									
										
										
										
											2022-10-17 22:09:17 +08:00
										 |  |  | 		rpkt = &sshFxpNamePacket{ | 
					
						
							|  |  |  | 			ID: p.ID, | 
					
						
							|  |  |  | 			NameAttrs: []*sshFxpNameAttr{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					Name:     f, | 
					
						
							|  |  |  | 					LongName: f, | 
					
						
							|  |  |  | 					Attrs:    emptyFileStat, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpOpendirPacket: | 
					
						
							| 
									
										
										
										
											2022-10-18 23:15:21 +08:00
										 |  |  | 		lp := s.toLocalPath(p.Path) | 
					
						
							| 
									
										
										
										
											2021-06-29 23:16:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-17 22:11:18 +08:00
										 |  |  | 		if stat, err := os.Stat(lp); err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2018-05-26 12:47:30 +08:00
										 |  |  | 		} else if !stat.IsDir() { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, &os.PathError{ | 
					
						
							| 
									
										
										
										
											2022-10-17 22:11:18 +08:00
										 |  |  | 				Path: lp, Err: syscall.ENOTDIR, | 
					
						
							| 
									
										
										
										
											2022-10-14 18:33:40 +08:00
										 |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = (&sshFxpOpenPacket{ | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 				ID:     p.ID, | 
					
						
							|  |  |  | 				Path:   p.Path, | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 				Pflags: sshFxfRead, | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			}).respond(s) | 
					
						
							| 
									
										
										
										
											2018-05-26 12:47:30 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 	case *sshFxpReadPacket: | 
					
						
							| 
									
										
										
										
											2020-09-11 00:11:47 +08:00
										 |  |  | 		var err error = EBADF | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 		f, ok := s.getHandle(p.Handle) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		if ok { | 
					
						
							|  |  |  | 			err = nil | 
					
						
							| 
									
										
										
										
											2020-03-15 02:42:19 +08:00
										 |  |  | 			data := p.getDataSlice(s.pktMgr.alloc, orderID) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 			n, _err := f.ReadAt(data, int64(p.Offset)) | 
					
						
							|  |  |  | 			if _err != nil && (_err != io.EOF || n == 0) { | 
					
						
							|  |  |  | 				err = _err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = &sshFxpDataPacket{ | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 				ID:     p.ID, | 
					
						
							|  |  |  | 				Length: uint32(n), | 
					
						
							|  |  |  | 				Data:   data[:n], | 
					
						
							| 
									
										
										
										
											2020-03-10 22:35:56 +08:00
										 |  |  | 				// do not use data[:n:n] here to clamp the capacity, we allocated extra capacity above to avoid reallocations
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 	case *sshFxpWritePacket: | 
					
						
							|  |  |  | 		f, ok := s.getHandle(p.Handle) | 
					
						
							| 
									
										
										
										
											2020-09-11 00:11:47 +08:00
										 |  |  | 		var err error = EBADF | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		if ok { | 
					
						
							|  |  |  | 			_, err = f.WriteAt(p.Data, int64(p.Offset)) | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		rpkt = statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2019-08-09 21:48:13 +08:00
										 |  |  | 	case *sshFxpExtendedPacket: | 
					
						
							|  |  |  | 		if p.SpecificPacket == nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 			rpkt = statusFromError(p.ID, ErrSSHFxOpUnsupported) | 
					
						
							| 
									
										
										
										
											2019-08-09 21:48:13 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			rpkt = p.respond(s) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case serverRespondablePacket: | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = p.respond(s) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2021-06-05 05:18:41 +08:00
										 |  |  | 		return fmt.Errorf("unexpected packet type %T", p) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-15 02:42:19 +08:00
										 |  |  | 	s.pktMgr.readyPacket(s.pktMgr.newOrderedResponse(rpkt, orderID)) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 04:11:16 +08:00
										 |  |  | // Serve serves SFTP connections until the streams stop or the SFTP subsystem
 | 
					
						
							| 
									
										
										
										
											2023-07-21 16:17:22 +08:00
										 |  |  | // is stopped. It returns nil if the server exits cleanly.
 | 
					
						
							| 
									
										
										
										
											2015-09-07 14:55:15 +08:00
										 |  |  | func (svr *Server) Serve() error { | 
					
						
							| 
									
										
										
										
											2020-03-18 16:36:07 +08:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if svr.pktMgr.alloc != nil { | 
					
						
							|  |  |  | 			svr.pktMgr.alloc.Free() | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 	runWorker := func(ch chan orderedRequest) { | 
					
						
							| 
									
										
										
										
											2017-04-06 05:19:14 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2017-07-04 08:53:55 +08:00
										 |  |  | 		go func() { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							|  |  |  | 			if err := svr.sftpServerWorker(ch); err != nil { | 
					
						
							|  |  |  | 				svr.conn.Close() // shuts down recvPacket
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2015-08-06 14:24:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-07-04 08:53:55 +08:00
										 |  |  | 	pktChan := svr.pktMgr.workerChan(runWorker) | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2017-03-14 09:24:32 +08:00
										 |  |  | 	var pkt requestPacket | 
					
						
							| 
									
										
										
										
											2016-06-15 18:04:25 +08:00
										 |  |  | 	var pktType uint8 | 
					
						
							|  |  |  | 	var pktBytes []byte | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2020-03-18 16:36:07 +08:00
										 |  |  | 		pktType, pktBytes, err = svr.serverConn.recvPacket(svr.pktMgr.getNextOrderID()) | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-21 16:17:22 +08:00
										 |  |  | 			// Check whether the connection terminated cleanly in-between packets.
 | 
					
						
							|  |  |  | 			if err == io.EOF { | 
					
						
							|  |  |  | 				err = nil | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-03-15 02:42:19 +08:00
										 |  |  | 			// we don't care about releasing allocated pages here, the server will quit and the allocator freed
 | 
					
						
							| 
									
										
										
										
											2015-09-07 10:36:47 +08:00
										 |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-03-14 07:49:47 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes}) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-05 05:18:41 +08:00
										 |  |  | 			switch { | 
					
						
							|  |  |  | 			case errors.Is(err, errUnknownExtendedPacket): | 
					
						
							| 
									
										
										
										
											2019-08-09 21:48:13 +08:00
										 |  |  | 				//if err := svr.serverConn.sendError(pkt, ErrSshFxOpUnsupported); err != nil {
 | 
					
						
							|  |  |  | 				//	debug("failed to send err packet: %v", err)
 | 
					
						
							|  |  |  | 				//	svr.conn.Close() // shuts down recvPacket
 | 
					
						
							|  |  |  | 				//	break
 | 
					
						
							|  |  |  | 				//}
 | 
					
						
							| 
									
										
										
										
											2018-03-19 22:32:22 +08:00
										 |  |  | 			default: | 
					
						
							|  |  |  | 				debug("makePacket err: %v", err) | 
					
						
							|  |  |  | 				svr.conn.Close() // shuts down recvPacket
 | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-03-14 07:49:47 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 		pktChan <- svr.pktMgr.newOrderedRequest(pkt) | 
					
						
							| 
									
										
										
										
											2015-09-07 10:36:47 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-24 04:47:05 +08:00
										 |  |  | 	close(pktChan) // shuts down sftpServerWorkers
 | 
					
						
							|  |  |  | 	wg.Wait()      // wait for all workers to exit
 | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-09 08:03:18 +08:00
										 |  |  | 	// close any still-open files
 | 
					
						
							|  |  |  | 	for handle, file := range svr.openFiles { | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 		fmt.Fprintf(svr.debugStream, "sftp server file with handle %q left open: %v\n", handle, file.Name()) | 
					
						
							| 
									
										
										
										
											2015-09-09 08:03:18 +08:00
										 |  |  | 		file.Close() | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 	return err // error from recvPacket
 | 
					
						
							| 
									
										
										
										
											2015-08-06 14:24:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 08:52:53 +08:00
										 |  |  | type ider interface { | 
					
						
							| 
									
										
										
										
											2016-06-14 19:11:15 +08:00
										 |  |  | 	id() uint32 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | // The init packet has no ID, so we just return a zero-value ID
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxInitPacket) id() uint32 { return 0 } | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | type sshFxpStatResponse struct { | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | 	ID   uint32 | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	info os.FileInfo | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpStatResponse) marshalPacket() ([]byte, []byte, error) { | 
					
						
							| 
									
										
										
										
											2020-10-29 04:54:31 +08:00
										 |  |  | 	l := 4 + 1 + 4 // uint32(length) + byte(type) + uint32(id)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b := make([]byte, 4, l) | 
					
						
							|  |  |  | 	b = append(b, sshFxpAttrs) | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | 	b = marshalUint32(b, p.ID) | 
					
						
							| 
									
										
										
										
											2020-10-29 04:54:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var payload []byte | 
					
						
							|  |  |  | 	payload = marshalFileInfo(payload, p.info) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return b, payload, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpStatResponse) MarshalBinary() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2020-10-29 04:54:31 +08:00
										 |  |  | 	header, payload, err := p.marshalPacket() | 
					
						
							|  |  |  | 	return append(header, payload...), err | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-06 03:57:28 +08:00
										 |  |  | var emptyFileStat = []interface{}{uint32(0)} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpOpenPacket) readonly() bool { | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	return !p.hasPflags(sshFxfWrite) | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpOpenPacket) hasPflags(flags ...uint32) bool { | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	for _, f := range flags { | 
					
						
							|  |  |  | 		if p.Pflags&f == 0 { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	var osFlags int | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	if p.hasPflags(sshFxfRead, sshFxfWrite) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_RDWR | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	} else if p.hasPflags(sshFxfWrite) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_WRONLY | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	} else if p.hasPflags(sshFxfRead) { | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 		osFlags |= os.O_RDONLY | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// how are they opening?
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		return statusFromError(p.ID, syscall.EINVAL) | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-03 06:33:58 +08:00
										 |  |  | 	// Don't use O_APPEND flag as it conflicts with WriteAt.
 | 
					
						
							|  |  |  | 	// The sshFxfAppend flag is a no-op here as the client sends the offsets.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	if p.hasPflags(sshFxfCreat) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_CREATE | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	if p.hasPflags(sshFxfTrunc) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_TRUNC | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	if p.hasPflags(sshFxfExcl) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_EXCL | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 08:20:23 +08:00
										 |  |  | 	mode := os.FileMode(0o644) | 
					
						
							| 
									
										
										
										
											2024-01-19 09:23:22 +08:00
										 |  |  | 	// Like OpenSSH, we only handle permissions here, and only when the file is being created.
 | 
					
						
							| 
									
										
										
										
											2024-01-19 08:20:23 +08:00
										 |  |  | 	// Otherwise, the permissions are ignored.
 | 
					
						
							| 
									
										
										
										
											2024-01-19 09:23:22 +08:00
										 |  |  | 	if p.Flags&sshFileXferAttrPermissions != 0 { | 
					
						
							|  |  |  | 		fs, err := p.unmarshalFileStat(p.Flags) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return statusFromError(p.ID, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-01-19 08:20:23 +08:00
										 |  |  | 		mode = fs.FileMode() & os.ModePerm | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	f, err := os.OpenFile(svr.toLocalPath(p.Path), osFlags, mode) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		return statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handle := svr.nextHandle(f) | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 	return &sshFxpHandlePacket{ID: p.ID, Handle: handle} | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpReaddirPacket) respond(svr *Server) responsePacket { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	f, ok := svr.getHandle(p.Handle) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		return statusFromError(p.ID, EBADF) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	dirents, err := f.Readdir(128) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		return statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-17 19:40:30 +08:00
										 |  |  | 	idLookup := osIDLookup{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 	ret := &sshFxpNamePacket{ID: p.ID} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	for _, dirent := range dirents { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		ret.NameAttrs = append(ret.NameAttrs, &sshFxpNameAttr{ | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 			Name:     dirent.Name(), | 
					
						
							| 
									
										
										
										
											2021-08-17 19:40:30 +08:00
										 |  |  | 			LongName: runLs(idLookup, dirent), | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 			Attrs:    []interface{}{dirent}, | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	return ret | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket { | 
					
						
							| 
									
										
										
										
											2024-01-19 08:20:23 +08:00
										 |  |  | 	path := svr.toLocalPath(p.Path) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 08:20:23 +08:00
										 |  |  | 	debug("setstat name %q", path) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 09:23:22 +08:00
										 |  |  | 	fs, err := p.unmarshalFileStat(p.Flags) | 
					
						
							| 
									
										
										
										
											2021-06-29 23:16:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrSize) != 0 { | 
					
						
							|  |  |  | 		err = os.Truncate(path, int64(fs.Size)) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrPermissions) != 0 { | 
					
						
							|  |  |  | 		err = os.Chmod(path, fs.FileMode()) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrUIDGID) != 0 { | 
					
						
							|  |  |  | 		err = os.Chown(path, int(fs.UID), int(fs.GID)) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrACmodTime) != 0 { | 
					
						
							|  |  |  | 		err = os.Chtimes(path, fs.AccessTime(), fs.ModTime()) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 	return statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	f, ok := svr.getHandle(p.Handle) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 		return statusFromError(p.ID, EBADF) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 08:20:23 +08:00
										 |  |  | 	path := f.Name() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug("fsetstat name %q", path) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-19 09:23:22 +08:00
										 |  |  | 	fs, err := p.unmarshalFileStat(p.Flags) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrSize) != 0 { | 
					
						
							|  |  |  | 		err = f.Truncate(int64(fs.Size)) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrPermissions) != 0 { | 
					
						
							|  |  |  | 		err = f.Chmod(fs.FileMode()) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrUIDGID) != 0 { | 
					
						
							|  |  |  | 		err = f.Chown(int(fs.UID), int(fs.GID)) | 
					
						
							| 
									
										
										
										
											2024-01-19 09:27:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-02-06 23:02:43 +08:00
										 |  |  | 	if err == nil && (p.Flags & sshFileXferAttrACmodTime) != 0 { | 
					
						
							|  |  |  | 		type chtimer interface { | 
					
						
							|  |  |  | 			Chtimes(atime, mtime time.Time) error | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch f := interface{}(f).(type) { | 
					
						
							|  |  |  | 		case chtimer: | 
					
						
							|  |  |  | 			// future-compatible, for when/if *os.File supports Chtimes.
 | 
					
						
							|  |  |  | 			err = f.Chtimes(fs.AccessTime(), fs.ModTime()) | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			err = os.Chtimes(path, fs.AccessTime(), fs.ModTime()) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | 	return statusFromError(p.ID, err) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-22 20:00:27 +08:00
										 |  |  | func statusFromError(id uint32, err error) *sshFxpStatusPacket { | 
					
						
							|  |  |  | 	ret := &sshFxpStatusPacket{ | 
					
						
							|  |  |  | 		ID: id, | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 		StatusError: StatusError{ | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 			// sshFXOk               = 0
 | 
					
						
							|  |  |  | 			// sshFXEOF              = 1
 | 
					
						
							|  |  |  | 			// sshFXNoSuchFile       = 2 ENOENT
 | 
					
						
							|  |  |  | 			// sshFXPermissionDenied = 3
 | 
					
						
							|  |  |  | 			// sshFXFailure          = 4
 | 
					
						
							|  |  |  | 			// sshFXBadMessage       = 5
 | 
					
						
							|  |  |  | 			// sshFXNoConnection     = 6
 | 
					
						
							|  |  |  | 			// sshFXConnectionLost   = 7
 | 
					
						
							|  |  |  | 			// sshFXOPUnsupported    = 8
 | 
					
						
							|  |  |  | 			Code: sshFxOk, | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-26 09:29:36 +08:00
										 |  |  | 	if err == nil { | 
					
						
							|  |  |  | 		return ret | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug("statusFromError: error is %T %#v", err, err) | 
					
						
							| 
									
										
										
										
											2019-08-30 23:04:37 +08:00
										 |  |  | 	ret.StatusError.Code = sshFxFailure | 
					
						
							| 
									
										
										
										
											2018-01-26 09:29:36 +08:00
										 |  |  | 	ret.StatusError.msg = err.Error() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-14 22:58:12 +08:00
										 |  |  | 	if os.IsNotExist(err) { | 
					
						
							|  |  |  | 		ret.StatusError.Code = sshFxNoSuchFile | 
					
						
							|  |  |  | 		return ret | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-11 00:11:47 +08:00
										 |  |  | 	if code, ok := translateSyscallError(err); ok { | 
					
						
							|  |  |  | 		ret.StatusError.Code = code | 
					
						
							|  |  |  | 		return ret | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-26 00:21:42 +08:00
										 |  |  | 	if errors.Is(err, io.EOF) { | 
					
						
							|  |  |  | 		ret.StatusError.Code = sshFxEOF | 
					
						
							|  |  |  | 		return ret | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var e fxerr | 
					
						
							|  |  |  | 	if errors.As(err, &e) { | 
					
						
							| 
									
										
										
										
											2018-01-27 09:26:44 +08:00
										 |  |  | 		ret.StatusError.Code = uint32(e) | 
					
						
							| 
									
										
										
										
											2022-12-26 00:21:42 +08:00
										 |  |  | 		return ret | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-01-26 09:29:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	return ret | 
					
						
							|  |  |  | } |