| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | package sftp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // sftp server counterpart
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"encoding" | 
					
						
							|  |  |  | 	"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" | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-05-19 13:16:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-07 14:55:15 +08:00
										 |  |  | const ( | 
					
						
							| 
									
										
										
										
											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,
 | 
					
						
							|  |  |  | // as specified at http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
 | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2015-08-05 12:11:01 +08:00
										 |  |  | 	maxTxPacket   uint32 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | 	return syscall.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), | 
					
						
							|  |  |  | 		maxTxPacket: 1 << 15, | 
					
						
							| 
									
										
										
										
											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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 			svr.sendPacket(orderedResponse{ | 
					
						
							|  |  |  | 				responsePacket: statusFromError(pkt, syscall.EPERM), | 
					
						
							|  |  |  | 				orderid:        pkt.orderId()}) | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 	switch p := p.requestPacket.(type) { | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxInitPacket: | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = sshFxVersionPacket{Version: sftpProtocolVersion} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpStatPacket: | 
					
						
							|  |  |  | 		// stat the requested file
 | 
					
						
							|  |  |  | 		info, err := os.Stat(p.Path) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +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 { | 
					
						
							|  |  |  | 			rpkt = statusFromError(p, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpLstatPacket: | 
					
						
							|  |  |  | 		// stat the requested file
 | 
					
						
							|  |  |  | 		info, err := os.Lstat(p.Path) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +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 { | 
					
						
							|  |  |  | 			rpkt = statusFromError(p, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpFstatPacket: | 
					
						
							|  |  |  | 		f, ok := s.getHandle(p.Handle) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		var err error = syscall.EBADF | 
					
						
							|  |  |  | 		var info os.FileInfo | 
					
						
							|  |  |  | 		if ok { | 
					
						
							|  |  |  | 			info, err = f.Stat() | 
					
						
							|  |  |  | 			rpkt = sshFxpStatResponse{ | 
					
						
							|  |  |  | 				ID:   p.ID, | 
					
						
							|  |  |  | 				info: info, | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 			rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case *sshFxpMkdirPacket: | 
					
						
							|  |  |  | 		// TODO FIXME: ignore flags field
 | 
					
						
							|  |  |  | 		err := os.Mkdir(p.Path, 0755) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpRmdirPacket: | 
					
						
							|  |  |  | 		err := os.Remove(p.Path) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpRemovePacket: | 
					
						
							|  |  |  | 		err := os.Remove(p.Filename) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpRenamePacket: | 
					
						
							|  |  |  | 		err := os.Rename(p.Oldpath, p.Newpath) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpSymlinkPacket: | 
					
						
							|  |  |  | 		err := os.Symlink(p.Targetpath, p.Linkpath) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpClosePacket: | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, s.closeHandle(p.Handle)) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpReadlinkPacket: | 
					
						
							|  |  |  | 		f, err := os.Readlink(p.Path) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = sshFxpNamePacket{ | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 			ID: p.ID, | 
					
						
							|  |  |  | 			NameAttrs: []sshFxpNameAttr{{ | 
					
						
							|  |  |  | 				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 { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 			rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	case *sshFxpRealpathPacket: | 
					
						
							|  |  |  | 		f, err := filepath.Abs(p.Path) | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | 		f = cleanPath(f) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = sshFxpNamePacket{ | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 			ID: p.ID, | 
					
						
							|  |  |  | 			NameAttrs: []sshFxpNameAttr{{ | 
					
						
							|  |  |  | 				Name:     f, | 
					
						
							|  |  |  | 				LongName: f, | 
					
						
							|  |  |  | 				Attrs:    emptyFileStat, | 
					
						
							|  |  |  | 			}}, | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			rpkt = statusFromError(p, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 18:47:20 +08:00
										 |  |  | 	case *sshFxpOpendirPacket: | 
					
						
							| 
									
										
										
										
											2018-05-26 12:47:30 +08:00
										 |  |  | 		if stat, err := os.Stat(p.Path); err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 			rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2018-05-26 12:47:30 +08:00
										 |  |  | 		} else if !stat.IsDir() { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 			rpkt = statusFromError(p, &os.PathError{ | 
					
						
							| 
									
										
										
										
											2018-05-26 12:47:30 +08:00
										 |  |  | 				Path: p.Path, Err: syscall.ENOTDIR}) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			rpkt = sshFxpOpenPacket{ | 
					
						
							|  |  |  | 				ID:     p.ID, | 
					
						
							|  |  |  | 				Path:   p.Path, | 
					
						
							|  |  |  | 				Pflags: ssh_FXF_READ, | 
					
						
							|  |  |  | 			}.respond(s) | 
					
						
							| 
									
										
										
										
											2018-05-26 12:47:30 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 	case *sshFxpReadPacket: | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		var err error = syscall.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 | 
					
						
							|  |  |  | 			data := make([]byte, clamp(p.Len, s.maxTxPacket)) | 
					
						
							|  |  |  | 			n, _err := f.ReadAt(data, int64(p.Offset)) | 
					
						
							|  |  |  | 			if _err != nil && (_err != io.EOF || n == 0) { | 
					
						
							|  |  |  | 				err = _err | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			rpkt = sshFxpDataPacket{ | 
					
						
							|  |  |  | 				ID:     p.ID, | 
					
						
							|  |  |  | 				Length: uint32(n), | 
					
						
							|  |  |  | 				Data:   data[:n], | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			rpkt = statusFromError(p, 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) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		var err error = syscall.EBADF | 
					
						
							|  |  |  | 		if ok { | 
					
						
							|  |  |  | 			_, err = f.WriteAt(p.Data, int64(p.Offset)) | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		rpkt = statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							|  |  |  | 		return errors.Errorf("unexpected packet type %T", p) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 	s.pktMgr.readyPacket(s.pktMgr.newOrderedResponse(rpkt, p.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
 | 
					
						
							|  |  |  | // is stopped.
 | 
					
						
							| 
									
										
										
										
											2015-09-07 14:55:15 +08:00
										 |  |  | func (svr *Server) Serve() error { | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2016-06-15 09:50:02 +08:00
										 |  |  | 		pktType, pktBytes, err = svr.recvPacket() | 
					
						
							| 
									
										
										
										
											2016-05-29 15:59:23 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											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 { | 
					
						
							| 
									
										
										
										
											2018-03-19 22:32:22 +08:00
										 |  |  | 			switch errors.Cause(err) { | 
					
						
							|  |  |  | 			case errUnknownExtendedPacket: | 
					
						
							|  |  |  | 				if err := svr.serverConn.sendError(pkt, ErrSshFxOpUnsupported); err != nil { | 
					
						
							|  |  |  | 					debug("failed to send err packet: %v", err) | 
					
						
							|  |  |  | 					svr.conn.Close() // shuts down recvPacket
 | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			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
 | 
					
						
							| 
									
										
										
										
											2016-05-19 12:49:23 +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 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | func (p sshFxpStatResponse) MarshalBinary() ([]byte, error) { | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	b := []byte{ssh_FXP_ATTRS} | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | 	b = marshalUint32(b, p.ID) | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	b = marshalFileInfo(b, p.info) | 
					
						
							|  |  |  | 	return b, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-08-06 03:57:28 +08:00
										 |  |  | var emptyFileStat = []interface{}{uint32(0)} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | func (p sshFxpOpenPacket) readonly() bool { | 
					
						
							|  |  |  | 	return !p.hasPflags(ssh_FXF_WRITE) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p sshFxpOpenPacket) hasPflags(flags ...uint32) bool { | 
					
						
							|  |  |  | 	for _, f := range flags { | 
					
						
							|  |  |  | 		if p.Pflags&f == 0 { | 
					
						
							|  |  |  | 			return false | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return true | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | func (p sshFxpOpenPacket) respond(svr *Server) responsePacket { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	var osFlags int | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	if p.hasPflags(ssh_FXF_READ, ssh_FXF_WRITE) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_RDWR | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	} else if p.hasPflags(ssh_FXF_WRITE) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_WRONLY | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	} else if p.hasPflags(ssh_FXF_READ) { | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 		osFlags |= os.O_RDONLY | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// how are they opening?
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		return statusFromError(p, syscall.EINVAL) | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	if p.hasPflags(ssh_FXF_APPEND) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_APPEND | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	if p.hasPflags(ssh_FXF_CREAT) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_CREATE | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	if p.hasPflags(ssh_FXF_TRUNC) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_TRUNC | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-08 06:27:37 +08:00
										 |  |  | 	if p.hasPflags(ssh_FXF_EXCL) { | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 		osFlags |= os.O_EXCL | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	f, err := os.OpenFile(p.Path, osFlags, 0644) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		return statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2015-07-26 16:32:19 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	handle := svr.nextHandle(f) | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	return sshFxpHandlePacket{ID: p.id(), Handle: handle} | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +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 { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		return statusFromError(p, syscall.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
										 |  |  | 	dirname := f.Name() | 
					
						
							|  |  |  | 	dirents, err := f.Readdir(128) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		return statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-08-01 06:46:13 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | 	ret := sshFxpNamePacket{ID: p.ID} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	for _, dirent := range dirents { | 
					
						
							|  |  |  | 		ret.NameAttrs = append(ret.NameAttrs, sshFxpNameAttr{ | 
					
						
							|  |  |  | 			Name:     dirent.Name(), | 
					
						
							|  |  |  | 			LongName: runLs(dirname, dirent), | 
					
						
							|  |  |  | 			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
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | func (p sshFxpSetstatPacket) respond(svr *Server) responsePacket { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	// additional unmarshalling is required for each possibility here
 | 
					
						
							|  |  |  | 	b := p.Attrs.([]byte) | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug("setstat name \"%s\"", p.Path) | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_SIZE) != 0 { | 
					
						
							|  |  |  | 		var size uint64 | 
					
						
							|  |  |  | 		if size, b, err = unmarshalUint64Safe(b); err == nil { | 
					
						
							|  |  |  | 			err = os.Truncate(p.Path, int64(size)) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0 { | 
					
						
							|  |  |  | 		var mode uint32 | 
					
						
							|  |  |  | 		if mode, b, err = unmarshalUint32Safe(b); err == nil { | 
					
						
							|  |  |  | 			err = os.Chmod(p.Path, os.FileMode(mode)) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0 { | 
					
						
							|  |  |  | 		var atime uint32 | 
					
						
							|  |  |  | 		var mtime uint32 | 
					
						
							|  |  |  | 		if atime, b, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							|  |  |  | 		} else if mtime, b, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			atimeT := time.Unix(int64(atime), 0) | 
					
						
							|  |  |  | 			mtimeT := time.Unix(int64(mtime), 0) | 
					
						
							|  |  |  | 			err = os.Chtimes(p.Path, atimeT, mtimeT) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_UIDGID) != 0 { | 
					
						
							|  |  |  | 		var uid uint32 | 
					
						
							|  |  |  | 		var gid uint32 | 
					
						
							|  |  |  | 		if uid, b, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							| 
									
										
										
										
											2018-02-16 03:00:22 +08:00
										 |  |  | 		} else if gid, _, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			err = os.Chown(p.Path, int(uid), int(gid)) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	return statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +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 { | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 		return statusFromError(p, syscall.EBADF) | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// additional unmarshalling is required for each possibility here
 | 
					
						
							|  |  |  | 	b := p.Attrs.([]byte) | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debug("fsetstat name \"%s\"", f.Name()) | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_SIZE) != 0 { | 
					
						
							|  |  |  | 		var size uint64 | 
					
						
							|  |  |  | 		if size, b, err = unmarshalUint64Safe(b); err == nil { | 
					
						
							|  |  |  | 			err = f.Truncate(int64(size)) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_PERMISSIONS) != 0 { | 
					
						
							|  |  |  | 		var mode uint32 | 
					
						
							|  |  |  | 		if mode, b, err = unmarshalUint32Safe(b); err == nil { | 
					
						
							|  |  |  | 			err = f.Chmod(os.FileMode(mode)) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_ACMODTIME) != 0 { | 
					
						
							|  |  |  | 		var atime uint32 | 
					
						
							|  |  |  | 		var mtime uint32 | 
					
						
							|  |  |  | 		if atime, b, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							|  |  |  | 		} else if mtime, b, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			atimeT := time.Unix(int64(atime), 0) | 
					
						
							|  |  |  | 			mtimeT := time.Unix(int64(mtime), 0) | 
					
						
							|  |  |  | 			err = os.Chtimes(f.Name(), atimeT, mtimeT) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (p.Flags & ssh_FILEXFER_ATTR_UIDGID) != 0 { | 
					
						
							|  |  |  | 		var uid uint32 | 
					
						
							|  |  |  | 		var gid uint32 | 
					
						
							|  |  |  | 		if uid, b, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							| 
									
										
										
										
											2018-02-16 03:00:22 +08:00
										 |  |  | 		} else if gid, _, err = unmarshalUint32Safe(b); err != nil { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			err = f.Chown(int(uid), int(gid)) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 05:54:02 +08:00
										 |  |  | 	return statusFromError(p, err) | 
					
						
							| 
									
										
										
										
											2015-09-07 17:13:07 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-05 05:15:21 +08:00
										 |  |  | // translateErrno translates a syscall error number to a SFTP error code.
 | 
					
						
							|  |  |  | func translateErrno(errno syscall.Errno) uint32 { | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	switch errno { | 
					
						
							|  |  |  | 	case 0: | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 		return ssh_FX_OK | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	case syscall.ENOENT: | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 		return ssh_FX_NO_SUCH_FILE | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	case syscall.EPERM: | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | 		return ssh_FX_PERMISSION_DENIED | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-31 01:56:52 +08:00
										 |  |  | 	return ssh_FX_FAILURE | 
					
						
							| 
									
										
										
										
											2015-07-31 14:43:00 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-14 08:52:53 +08:00
										 |  |  | func statusFromError(p ider, err error) sshFxpStatusPacket { | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 	ret := sshFxpStatusPacket{ | 
					
						
							| 
									
										
										
										
											2016-06-14 19:11:15 +08:00
										 |  |  | 		ID: p.id(), | 
					
						
							| 
									
										
										
										
											2015-07-25 16:19:29 +08:00
										 |  |  | 		StatusError: StatusError{ | 
					
						
							|  |  |  | 			// ssh_FX_OK                = 0
 | 
					
						
							|  |  |  | 			// ssh_FX_EOF               = 1
 | 
					
						
							|  |  |  | 			// ssh_FX_NO_SUCH_FILE      = 2 ENOENT
 | 
					
						
							|  |  |  | 			// ssh_FX_PERMISSION_DENIED = 3
 | 
					
						
							|  |  |  | 			// ssh_FX_FAILURE           = 4
 | 
					
						
							|  |  |  | 			// ssh_FX_BAD_MESSAGE       = 5
 | 
					
						
							|  |  |  | 			// ssh_FX_NO_CONNECTION     = 6
 | 
					
						
							|  |  |  | 			// ssh_FX_CONNECTION_LOST   = 7
 | 
					
						
							|  |  |  | 			// ssh_FX_OP_UNSUPPORTED    = 8
 | 
					
						
							| 
									
										
										
										
											2015-07-26 10:07:33 +08:00
										 |  |  | 			Code: ssh_FX_OK, | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							|  |  |  | 	ret.StatusError.Code = ssh_FX_FAILURE | 
					
						
							|  |  |  | 	ret.StatusError.msg = err.Error() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch e := err.(type) { | 
					
						
							|  |  |  | 	case syscall.Errno: | 
					
						
							|  |  |  | 		ret.StatusError.Code = translateErrno(e) | 
					
						
							|  |  |  | 	case *os.PathError: | 
					
						
							|  |  |  | 		debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err) | 
					
						
							|  |  |  | 		if errno, ok := e.Err.(syscall.Errno); ok { | 
					
						
							|  |  |  | 			ret.StatusError.Code = translateErrno(errno) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-27 09:26:44 +08:00
										 |  |  | 	case fxerr: | 
					
						
							|  |  |  | 		ret.StatusError.Code = uint32(e) | 
					
						
							| 
									
										
										
										
											2018-01-26 09:29:36 +08:00
										 |  |  | 	default: | 
					
						
							|  |  |  | 		switch e { | 
					
						
							|  |  |  | 		case io.EOF: | 
					
						
							| 
									
										
										
										
											2015-07-26 10:07:33 +08:00
										 |  |  | 			ret.StatusError.Code = ssh_FX_EOF | 
					
						
							| 
									
										
										
										
											2018-01-26 09:29:36 +08:00
										 |  |  | 		case os.ErrNotExist: | 
					
						
							| 
									
										
										
										
											2017-08-21 05:39:32 +08:00
										 |  |  | 			ret.StatusError.Code = ssh_FX_NO_SUCH_FILE | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-06-15 19:17:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func clamp(v, max uint32) uint32 { | 
					
						
							|  |  |  | 	if v > max { | 
					
						
							|  |  |  | 		return max | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return v | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-08-10 13:34:48 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func runLsTypeWord(dirent os.FileInfo) string { | 
					
						
							|  |  |  | 	// find first character, the type char
 | 
					
						
							|  |  |  | 	// b     Block special file.
 | 
					
						
							|  |  |  | 	// c     Character special file.
 | 
					
						
							|  |  |  | 	// d     Directory.
 | 
					
						
							|  |  |  | 	// l     Symbolic link.
 | 
					
						
							|  |  |  | 	// s     Socket link.
 | 
					
						
							|  |  |  | 	// p     FIFO.
 | 
					
						
							|  |  |  | 	// -     Regular file.
 | 
					
						
							|  |  |  | 	tc := '-' | 
					
						
							|  |  |  | 	mode := dirent.Mode() | 
					
						
							|  |  |  | 	if (mode & os.ModeDir) != 0 { | 
					
						
							|  |  |  | 		tc = 'd' | 
					
						
							|  |  |  | 	} else if (mode & os.ModeDevice) != 0 { | 
					
						
							|  |  |  | 		tc = 'b' | 
					
						
							|  |  |  | 		if (mode & os.ModeCharDevice) != 0 { | 
					
						
							|  |  |  | 			tc = 'c' | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} else if (mode & os.ModeSymlink) != 0 { | 
					
						
							|  |  |  | 		tc = 'l' | 
					
						
							|  |  |  | 	} else if (mode & os.ModeSocket) != 0 { | 
					
						
							|  |  |  | 		tc = 's' | 
					
						
							|  |  |  | 	} else if (mode & os.ModeNamedPipe) != 0 { | 
					
						
							|  |  |  | 		tc = 'p' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// owner
 | 
					
						
							|  |  |  | 	orc := '-' | 
					
						
							|  |  |  | 	if (mode & 0400) != 0 { | 
					
						
							|  |  |  | 		orc = 'r' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	owc := '-' | 
					
						
							|  |  |  | 	if (mode & 0200) != 0 { | 
					
						
							|  |  |  | 		owc = 'w' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oxc := '-' | 
					
						
							|  |  |  | 	ox := (mode & 0100) != 0 | 
					
						
							|  |  |  | 	setuid := (mode & os.ModeSetuid) != 0 | 
					
						
							|  |  |  | 	if ox && setuid { | 
					
						
							|  |  |  | 		oxc = 's' | 
					
						
							|  |  |  | 	} else if setuid { | 
					
						
							|  |  |  | 		oxc = 'S' | 
					
						
							|  |  |  | 	} else if ox { | 
					
						
							|  |  |  | 		oxc = 'x' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// group
 | 
					
						
							|  |  |  | 	grc := '-' | 
					
						
							|  |  |  | 	if (mode & 040) != 0 { | 
					
						
							|  |  |  | 		grc = 'r' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gwc := '-' | 
					
						
							|  |  |  | 	if (mode & 020) != 0 { | 
					
						
							|  |  |  | 		gwc = 'w' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	gxc := '-' | 
					
						
							|  |  |  | 	gx := (mode & 010) != 0 | 
					
						
							|  |  |  | 	setgid := (mode & os.ModeSetgid) != 0 | 
					
						
							|  |  |  | 	if gx && setgid { | 
					
						
							|  |  |  | 		gxc = 's' | 
					
						
							|  |  |  | 	} else if setgid { | 
					
						
							|  |  |  | 		gxc = 'S' | 
					
						
							|  |  |  | 	} else if gx { | 
					
						
							|  |  |  | 		gxc = 'x' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// all / others
 | 
					
						
							|  |  |  | 	arc := '-' | 
					
						
							|  |  |  | 	if (mode & 04) != 0 { | 
					
						
							|  |  |  | 		arc = 'r' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	awc := '-' | 
					
						
							|  |  |  | 	if (mode & 02) != 0 { | 
					
						
							|  |  |  | 		awc = 'w' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	axc := '-' | 
					
						
							|  |  |  | 	ax := (mode & 01) != 0 | 
					
						
							|  |  |  | 	sticky := (mode & os.ModeSticky) != 0 | 
					
						
							|  |  |  | 	if ax && sticky { | 
					
						
							|  |  |  | 		axc = 't' | 
					
						
							|  |  |  | 	} else if sticky { | 
					
						
							|  |  |  | 		axc = 'T' | 
					
						
							|  |  |  | 	} else if ax { | 
					
						
							|  |  |  | 		axc = 'x' | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fmt.Sprintf("%c%c%c%c%c%c%c%c%c%c", tc, orc, owc, oxc, grc, gwc, gxc, arc, awc, axc) | 
					
						
							|  |  |  | } |