| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | package sftp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-12-22 03:58:29 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2016-07-15 12:11:34 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-07-27 02:32:37 +08:00
										 |  |  | 	"strconv" | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2016-07-30 02:22:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/pkg/errors" | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | var maxTxPacket uint32 = 1 << 15 | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-26 02:42:18 +08:00
										 |  |  | // Handlers contains the 4 SFTP server request handlers.
 | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | type Handlers struct { | 
					
						
							| 
									
										
										
										
											2016-07-09 05:13:03 +08:00
										 |  |  | 	FileGet  FileReader | 
					
						
							|  |  |  | 	FilePut  FileWriter | 
					
						
							|  |  |  | 	FileCmd  FileCmder | 
					
						
							| 
									
										
										
										
											2017-07-28 09:22:11 +08:00
										 |  |  | 	FileList FileLister | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-26 02:42:18 +08:00
										 |  |  | // RequestServer abstracts the sftp protocol with an http request-like protocol
 | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | type RequestServer struct { | 
					
						
							| 
									
										
										
										
											2017-07-04 08:38:09 +08:00
										 |  |  | 	*serverConn | 
					
						
							| 
									
										
										
										
											2016-07-12 11:19:49 +08:00
										 |  |  | 	Handlers        Handlers | 
					
						
							| 
									
										
										
										
											2017-08-21 06:23:55 +08:00
										 |  |  | 	pktMgr          *packetManager | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | 	openRequests    map[string]*Request | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 	openRequestLock sync.RWMutex | 
					
						
							| 
									
										
										
										
											2016-07-27 02:32:37 +08:00
										 |  |  | 	handleCount     int | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-26 02:42:18 +08:00
										 |  |  | // NewRequestServer creates/allocates/returns new RequestServer.
 | 
					
						
							|  |  |  | // Normally there there will be one server per user-session.
 | 
					
						
							|  |  |  | func NewRequestServer(rwc io.ReadWriteCloser, h Handlers) *RequestServer { | 
					
						
							| 
									
										
										
										
											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-07-07 02:59:55 +08:00
										 |  |  | 		}, | 
					
						
							| 
									
										
										
										
											2017-03-15 09:02:17 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return &RequestServer{ | 
					
						
							|  |  |  | 		serverConn:   svrConn, | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:23 +08:00
										 |  |  | 		Handlers:     h, | 
					
						
							| 
									
										
										
										
											2017-07-04 08:38:09 +08:00
										 |  |  | 		pktMgr:       newPktMgr(svrConn), | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | 		openRequests: make(map[string]*Request), | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | // New Open packet/Request
 | 
					
						
							| 
									
										
										
										
											2017-08-21 08:03:15 +08:00
										 |  |  | func (rs *RequestServer) nextRequest(r *Request) string { | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 	rs.openRequestLock.Lock() | 
					
						
							|  |  |  | 	defer rs.openRequestLock.Unlock() | 
					
						
							| 
									
										
										
										
											2016-07-27 02:32:37 +08:00
										 |  |  | 	rs.handleCount++ | 
					
						
							|  |  |  | 	handle := strconv.Itoa(rs.handleCount) | 
					
						
							| 
									
										
										
										
											2018-11-21 08:34:41 +08:00
										 |  |  | 	r.handle = handle | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | 	rs.openRequests[handle] = r | 
					
						
							| 
									
										
										
										
											2016-07-27 02:32:37 +08:00
										 |  |  | 	return handle | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-29 10:23:54 +08:00
										 |  |  | // Returns Request from openRequests, bool is false if it is missing.
 | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | //
 | 
					
						
							|  |  |  | // The Requests in openRequests work essentially as open file descriptors that
 | 
					
						
							|  |  |  | // you can do different things with. What you are doing with it are denoted by
 | 
					
						
							| 
									
										
										
										
											2019-01-29 10:23:54 +08:00
										 |  |  | // the first packet of that type (read/write/etc).
 | 
					
						
							|  |  |  | func (rs *RequestServer) getRequest(handle string) (*Request, bool) { | 
					
						
							| 
									
										
										
										
											2016-08-02 11:22:06 +08:00
										 |  |  | 	rs.openRequestLock.RLock() | 
					
						
							| 
									
										
										
										
											2019-01-29 10:23:54 +08:00
										 |  |  | 	defer rs.openRequestLock.RUnlock() | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 	r, ok := rs.openRequests[handle] | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | 	return r, ok | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-29 10:23:54 +08:00
										 |  |  | // Close the Request and clear from openRequests map
 | 
					
						
							| 
									
										
										
										
											2017-12-13 02:05:18 +08:00
										 |  |  | func (rs *RequestServer) closeRequest(handle string) error { | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 	rs.openRequestLock.Lock() | 
					
						
							|  |  |  | 	defer rs.openRequestLock.Unlock() | 
					
						
							| 
									
										
										
										
											2016-08-20 02:52:43 +08:00
										 |  |  | 	if r, ok := rs.openRequests[handle]; ok { | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 		delete(rs.openRequests, handle) | 
					
						
							| 
									
										
										
										
											2017-12-13 02:05:18 +08:00
										 |  |  | 		return r.close() | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-13 02:05:18 +08:00
										 |  |  | 	return syscall.EBADF | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-26 02:42:18 +08:00
										 |  |  | // Close the read/write/closer to trigger exiting the main server loop
 | 
					
						
							| 
									
										
										
										
											2016-07-23 07:20:00 +08:00
										 |  |  | func (rs *RequestServer) Close() error { return rs.conn.Close() } | 
					
						
							| 
									
										
										
										
											2016-07-21 07:48:31 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-26 02:42:18 +08:00
										 |  |  | // Serve requests for user session
 | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | func (rs *RequestServer) Serve() error { | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | 	ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	var wg sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 	runWorker := func(ch chan orderedRequest) { | 
					
						
							| 
									
										
										
										
											2017-04-24 05:27:25 +08:00
										 |  |  | 		wg.Add(1) | 
					
						
							| 
									
										
										
										
											2017-07-04 08:53:55 +08:00
										 |  |  | 		go func() { | 
					
						
							|  |  |  | 			defer wg.Done() | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | 			if err := rs.packetWorker(ctx, ch); err != nil { | 
					
						
							| 
									
										
										
										
											2017-07-04 08:53:55 +08:00
										 |  |  | 				rs.conn.Close() // shuts down recvPacket
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}() | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-07-04 08:53:55 +08:00
										 |  |  | 	pktChan := rs.pktMgr.workerChan(runWorker) | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2017-03-15 09:02:17 +08:00
										 |  |  | 	var pkt requestPacket | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	var pktType uint8 | 
					
						
							|  |  |  | 	var pktBytes []byte | 
					
						
							|  |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2016-07-09 03:38:35 +08:00
										 |  |  | 		pktType, pktBytes, err = rs.recvPacket() | 
					
						
							| 
									
										
										
										
											2016-07-19 02:50:45 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-24 05:27:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-15 09:02:17 +08:00
										 |  |  | 		pkt, err = makePacket(rxPacket{fxp(pktType), pktBytes}) | 
					
						
							| 
									
										
										
										
											2016-07-19 02:50:45 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-03-19 22:32:22 +08:00
										 |  |  | 			switch errors.Cause(err) { | 
					
						
							|  |  |  | 			case errUnknownExtendedPacket: | 
					
						
							| 
									
										
										
										
											2019-08-09 21:48:13 +08:00
										 |  |  | 				// do nothing
 | 
					
						
							| 
									
										
										
										
											2018-03-19 22:32:22 +08:00
										 |  |  | 			default: | 
					
						
							|  |  |  | 				debug("makePacket err: %v", err) | 
					
						
							|  |  |  | 				rs.conn.Close() // shuts down recvPacket
 | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-19 02:50:45 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-04-24 05:27:25 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 		pktChan <- rs.pktMgr.newOrderedRequest(pkt) | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-24 05:27:25 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	close(pktChan) // shuts down sftpServerWorkers
 | 
					
						
							|  |  |  | 	wg.Wait()      // wait for all workers to exit
 | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-31 07:55:34 +08:00
										 |  |  | 	// make sure all open requests are properly closed
 | 
					
						
							|  |  |  | 	// (eg. possible on dropped connections, client crashes, etc.)
 | 
					
						
							|  |  |  | 	for handle, req := range rs.openRequests { | 
					
						
							|  |  |  | 		delete(rs.openRequests, handle) | 
					
						
							|  |  |  | 		req.close() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | func (rs *RequestServer) packetWorker( | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 	ctx context.Context, pktChan chan orderedRequest, | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | ) error { | 
					
						
							| 
									
										
										
										
											2017-04-24 05:27:25 +08:00
										 |  |  | 	for pkt := range pktChan { | 
					
						
							| 
									
										
										
										
											2019-05-25 03:23:18 +08:00
										 |  |  | 		if epkt, ok := pkt.requestPacket.(*sshFxpExtendedPacket); ok { | 
					
						
							|  |  |  | 			pkt.requestPacket = epkt.SpecificPacket | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-26 02:52:07 +08:00
										 |  |  | 		var rpkt responsePacket | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 		switch pkt := pkt.requestPacket.(type) { | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 		case *sshFxInitPacket: | 
					
						
							| 
									
										
										
										
											2019-08-27 15:18:15 +08:00
										 |  |  | 			rpkt = sshFxVersionPacket{Version: sftpProtocolVersion, Extensions: sftpExtensions} | 
					
						
							| 
									
										
										
										
											2016-07-12 11:19:49 +08:00
										 |  |  | 		case *sshFxpClosePacket: | 
					
						
							| 
									
										
										
										
											2016-07-30 02:22:07 +08:00
										 |  |  | 			handle := pkt.getHandle() | 
					
						
							| 
									
										
										
										
											2017-12-13 02:05:18 +08:00
										 |  |  | 			rpkt = statusFromError(pkt, rs.closeRequest(handle)) | 
					
						
							| 
									
										
										
										
											2016-07-15 12:11:34 +08:00
										 |  |  | 		case *sshFxpRealpathPacket: | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | 			rpkt = cleanPacketPath(pkt) | 
					
						
							| 
									
										
										
										
											2018-01-08 10:30:26 +08:00
										 |  |  | 		case *sshFxpOpendirPacket: | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | 			request := requestFromPacket(ctx, pkt) | 
					
						
							| 
									
										
										
										
											2019-01-29 10:20:55 +08:00
										 |  |  | 			rs.nextRequest(request) | 
					
						
							|  |  |  | 			rpkt = request.opendir(rs.Handlers, pkt) | 
					
						
							| 
									
										
										
										
											2018-01-08 10:30:26 +08:00
										 |  |  | 		case *sshFxpOpenPacket: | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | 			request := requestFromPacket(ctx, pkt) | 
					
						
							| 
									
										
										
										
											2018-11-21 08:34:41 +08:00
										 |  |  | 			rs.nextRequest(request) | 
					
						
							| 
									
										
										
										
											2019-01-29 10:20:55 +08:00
										 |  |  | 			rpkt = request.open(rs.Handlers, pkt) | 
					
						
							| 
									
										
										
										
											2019-01-29 10:23:54 +08:00
										 |  |  | 		case *sshFxpFstatPacket: | 
					
						
							|  |  |  | 			handle := pkt.getHandle() | 
					
						
							|  |  |  | 			request, ok := rs.getRequest(handle) | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				rpkt = statusFromError(pkt, syscall.EBADF) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				request = NewRequest("Stat", request.Filepath) | 
					
						
							|  |  |  | 				rpkt = request.call(rs.Handlers, pkt) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:53 +08:00
										 |  |  | 		case *sshFxpExtendedPacket: | 
					
						
							|  |  |  | 			switch expkt := pkt.SpecificPacket.(type) { | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				rpkt = statusFromError(pkt, ErrSshFxOpUnsupported) | 
					
						
							|  |  |  | 			case *sshFxpExtendedPacketPosixRename: | 
					
						
							|  |  |  | 				request := NewRequest("Rename", expkt.Oldpath) | 
					
						
							|  |  |  | 				request.Target = expkt.Newpath | 
					
						
							|  |  |  | 				rpkt = request.call(rs.Handlers, pkt) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-13 08:36:12 +08:00
										 |  |  | 		case hasHandle: | 
					
						
							| 
									
										
										
										
											2016-07-30 02:22:07 +08:00
										 |  |  | 			handle := pkt.getHandle() | 
					
						
							| 
									
										
										
										
											2019-01-29 10:23:54 +08:00
										 |  |  | 			request, ok := rs.getRequest(handle) | 
					
						
							| 
									
										
										
										
											2017-12-24 08:34:22 +08:00
										 |  |  | 			if !ok { | 
					
						
							| 
									
										
										
										
											2016-07-30 02:22:07 +08:00
										 |  |  | 				rpkt = statusFromError(pkt, syscall.EBADF) | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2017-08-23 10:24:14 +08:00
										 |  |  | 				rpkt = request.call(rs.Handlers, pkt) | 
					
						
							| 
									
										
										
										
											2016-07-30 02:22:07 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		case hasPath: | 
					
						
							| 
									
										
										
										
											2018-02-02 03:32:59 +08:00
										 |  |  | 			request := requestFromPacket(ctx, pkt) | 
					
						
							| 
									
										
										
										
											2017-08-23 10:24:14 +08:00
										 |  |  | 			rpkt = request.call(rs.Handlers, pkt) | 
					
						
							| 
									
										
										
										
											2018-01-31 07:55:34 +08:00
										 |  |  | 			request.close() | 
					
						
							| 
									
										
										
										
											2016-07-30 02:22:07 +08:00
										 |  |  | 		default: | 
					
						
							| 
									
										
										
										
											2019-08-20 03:57:53 +08:00
										 |  |  | 			rpkt = statusFromError(pkt, ErrSshFxOpUnsupported) | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-12 11:19:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-26 07:03:30 +08:00
										 |  |  | 		rs.pktMgr.readyPacket( | 
					
						
							|  |  |  | 			rs.pktMgr.newOrderedResponse(rpkt, pkt.orderId())) | 
					
						
							| 
									
										
										
										
											2016-07-07 02:59:55 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-13 08:36:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-08 10:30:26 +08:00
										 |  |  | // clean and return name packet for file
 | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | func cleanPacketPath(pkt *sshFxpRealpathPacket) responsePacket { | 
					
						
							|  |  |  | 	path := cleanPath(pkt.getPath()) | 
					
						
							| 
									
										
										
										
											2016-07-15 12:11:34 +08:00
										 |  |  | 	return &sshFxpNamePacket{ | 
					
						
							|  |  |  | 		ID: pkt.id(), | 
					
						
							|  |  |  | 		NameAttrs: []sshFxpNameAttr{{ | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | 			Name:     path, | 
					
						
							|  |  |  | 			LongName: path, | 
					
						
							| 
									
										
										
										
											2016-07-15 12:11:34 +08:00
										 |  |  | 			Attrs:    emptyFileStat, | 
					
						
							|  |  |  | 		}}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-22 03:58:29 +08:00
										 |  |  | // Makes sure we have a clean POSIX (/) absolute path to work with
 | 
					
						
							|  |  |  | func cleanPath(p string) string { | 
					
						
							|  |  |  | 	p = filepath.ToSlash(p) | 
					
						
							| 
									
										
										
										
											2018-01-08 07:23:55 +08:00
										 |  |  | 	if !filepath.IsAbs(p) { | 
					
						
							| 
									
										
										
										
											2017-12-22 03:58:29 +08:00
										 |  |  | 		p = "/" + p | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-22 03:58:29 +08:00
										 |  |  | 	return path.Clean(p) | 
					
						
							| 
									
										
										
										
											2017-08-13 20:00:08 +08:00
										 |  |  | } |