| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | package sftp | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | // This serves as an example of how to implement the request server handler as
 | 
					
						
							|  |  |  |  | // well as a dummy backend for testing. It implements an in-memory backend that
 | 
					
						
							|  |  |  |  | // works as a very simple filesystem with simple flat key-value lookup system.
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	"io" | 
					
						
							|  |  |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2020-09-03 01:21:12 +08:00
										 |  |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2017-07-11 07:43:58 +08:00
										 |  |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2016-07-30 06:57:06 +08:00
										 |  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2019-01-29 10:20:55 +08:00
										 |  |  |  | 	"syscall" | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	"time" | 
					
						
							|  |  |  |  | ) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:35:41 +08:00
										 |  |  |  | const maxSymlinkFollows = 5 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | var errTooManySymlinks = errors.New("too many symbolic links") | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-12 05:26:11 +08:00
										 |  |  |  | // InMemHandler returns a Hanlders object with the test handlers.
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | func InMemHandler() Handlers { | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | 	root := &root{ | 
					
						
							| 
									
										
										
										
											2020-09-21 20:42:29 +08:00
										 |  |  |  | 		rootFile: &memFile{name: "/", modtime: time.Now(), isdir: true}, | 
					
						
							| 
									
										
										
										
											2020-09-21 20:18:41 +08:00
										 |  |  |  | 		files:    make(map[string]*memFile), | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | 	return Handlers{root, root, root, root} | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-12 05:26:11 +08:00
										 |  |  |  | // Example Handlers
 | 
					
						
							| 
									
										
										
										
											2017-08-13 15:22:00 +08:00
										 |  |  |  | func (fs *root) Fileread(r *Request) (io.ReaderAt, error) { | 
					
						
							| 
									
										
										
										
											2020-09-21 21:46:42 +08:00
										 |  |  |  | 	flags := r.Pflags() | 
					
						
							|  |  |  |  | 	if !flags.Read { | 
					
						
							|  |  |  |  | 		// sanity check
 | 
					
						
							|  |  |  |  | 		return nil, os.ErrInvalid | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-21 21:45:59 +08:00
										 |  |  |  | 	return fs.OpenFile(r) | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:56:04 +08:00
										 |  |  |  | func (fs *root) Filewrite(r *Request) (io.WriterAt, error) { | 
					
						
							| 
									
										
										
										
											2020-09-21 21:46:42 +08:00
										 |  |  |  | 	flags := r.Pflags() | 
					
						
							|  |  |  |  | 	if !flags.Write { | 
					
						
							|  |  |  |  | 		// sanity check
 | 
					
						
							|  |  |  |  | 		return nil, os.ErrInvalid | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-21 21:45:59 +08:00
										 |  |  |  | 	return fs.OpenFile(r) | 
					
						
							| 
									
										
										
										
											2020-09-17 20:56:04 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func (fs *root) OpenFile(r *Request) (WriterAtReaderAt, error) { | 
					
						
							|  |  |  |  | 	if fs.mockErr != nil { | 
					
						
							|  |  |  |  | 		return nil, fs.mockErr | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	_ = r.WithContext(r.Context()) // initialize context for deadlock testing
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	fs.mu.Lock() | 
					
						
							|  |  |  |  | 	defer fs.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return fs.openfile(r.Filepath, r.Flags) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | func (fs *root) putfile(pathname string, file *memFile) error { | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 	pathname, err := fs.canonName(pathname) | 
					
						
							| 
									
										
										
										
											2020-09-25 21:34:03 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 		return err | 
					
						
							| 
									
										
										
										
											2020-09-25 21:34:03 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	if !strings.HasPrefix(pathname, "/") { | 
					
						
							|  |  |  |  | 		return os.ErrInvalid | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	if _, err := fs.lfetch(pathname); err != os.ErrNotExist { | 
					
						
							|  |  |  |  | 		return os.ErrExist | 
					
						
							| 
									
										
										
										
											2020-09-25 21:34:03 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	file.name = pathname | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 	fs.files[pathname] = file | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:56:04 +08:00
										 |  |  |  | func (fs *root) openfile(pathname string, flags uint32) (*memFile, error) { | 
					
						
							|  |  |  |  | 	pflags := newFileOpenFlags(flags) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	file, err := fs.fetch(pathname) | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	if err == os.ErrNotExist { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:56:04 +08:00
										 |  |  |  | 		if !pflags.Creat { | 
					
						
							|  |  |  |  | 			return nil, os.ErrNotExist | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:35:41 +08:00
										 |  |  |  | 		var count int | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 		// You can create files through dangling symlinks.
 | 
					
						
							| 
									
										
										
										
											2020-09-25 21:34:03 +08:00
										 |  |  |  | 		link, err := fs.lfetch(pathname) | 
					
						
							|  |  |  |  | 		for err == nil && link.symlink != "" { | 
					
						
							|  |  |  |  | 			if pflags.Excl { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 				// unless you also passed in O_EXCL
 | 
					
						
							| 
									
										
										
										
											2020-09-25 21:34:03 +08:00
										 |  |  |  | 				return nil, os.ErrInvalid | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:35:41 +08:00
										 |  |  |  | 			if count++; count > maxSymlinkFollows { | 
					
						
							|  |  |  |  | 				return nil, errTooManySymlinks | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 21:34:03 +08:00
										 |  |  |  | 			pathname = link.symlink | 
					
						
							|  |  |  |  | 			link, err = fs.lfetch(pathname) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 		file := &memFile{ | 
					
						
							|  |  |  |  | 			modtime: time.Now(), | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		if err := fs.putfile(pathname, file); err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		return file, nil | 
					
						
							| 
									
										
										
										
											2020-09-17 21:46:13 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2020-09-03 01:21:12 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:58:38 +08:00
										 |  |  |  | 	if pflags.Creat && pflags.Excl { | 
					
						
							|  |  |  |  | 		return nil, os.ErrExist | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-21 21:45:59 +08:00
										 |  |  |  | 	if file.IsDir() { | 
					
						
							|  |  |  |  | 		return nil, os.ErrInvalid | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 	if pflags.Trunc { | 
					
						
							|  |  |  |  | 		if err := file.Truncate(0); err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:56:04 +08:00
										 |  |  |  | 	return file, nil | 
					
						
							| 
									
										
										
										
											2020-08-30 16:40:22 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-13 15:22:00 +08:00
										 |  |  |  | func (fs *root) Filecmd(r *Request) error { | 
					
						
							| 
									
										
										
										
											2018-01-17 06:18:45 +08:00
										 |  |  |  | 	if fs.mockErr != nil { | 
					
						
							|  |  |  |  | 		return fs.mockErr | 
					
						
							| 
									
										
										
										
											2018-01-08 10:29:18 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-02-05 05:22:46 +08:00
										 |  |  |  | 	_ = r.WithContext(r.Context()) // initialize context for deadlock testing
 | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	fs.mu.Lock() | 
					
						
							|  |  |  |  | 	defer fs.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	switch r.Method { | 
					
						
							| 
									
										
										
										
											2017-04-25 12:36:09 +08:00
										 |  |  |  | 	case "Setstat": | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 		file, err := fs.openfile(r.Filepath, sshFxfWrite) | 
					
						
							| 
									
										
										
										
											2020-08-22 15:51:12 +08:00
										 |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			return err | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 15:51:12 +08:00
										 |  |  |  | 		if r.AttrFlags().Size { | 
					
						
							|  |  |  |  | 			return file.Truncate(int64(r.Attributes().Size)) | 
					
						
							|  |  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	case "Rename": | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 		// SFTP-v2: "It is an error if there already exists a file with the name specified by newpath."
 | 
					
						
							|  |  |  |  | 		// This varies from the POSIX specification, which allows limited replacement of target files.
 | 
					
						
							| 
									
										
										
										
											2020-09-25 20:35:15 +08:00
										 |  |  |  | 		if fs.exists(r.Target) { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 			return os.ErrExist | 
					
						
							| 
									
										
										
										
											2020-09-25 20:35:15 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 		return fs.rename(r.Filepath, r.Target) | 
					
						
							| 
									
										
										
										
											2019-10-09 23:40:31 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 	case "Rmdir": | 
					
						
							|  |  |  |  | 		return fs.rmdir(r.Filepath) | 
					
						
							| 
									
										
										
										
											2019-10-09 23:40:31 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 	case "Remove": | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 		// IEEE 1003.1 remove explicitly can unlink files and remove empty directories.
 | 
					
						
							|  |  |  |  | 		// We use instead here the semantics of unlink, which is allowed to be restricted against directories.
 | 
					
						
							|  |  |  |  | 		return fs.unlink(r.Filepath) | 
					
						
							| 
									
										
										
										
											2019-10-09 23:40:31 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	case "Mkdir": | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | 		return fs.mkdir(r.Filepath) | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-25 03:23:18 +08:00
										 |  |  |  | 	case "Link": | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | 		return fs.link(r.Filepath, r.Target) | 
					
						
							| 
									
										
										
										
											2020-09-17 23:31:22 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	case "Symlink": | 
					
						
							| 
									
										
										
										
											2020-09-24 23:20:10 +08:00
										 |  |  |  | 		// NOTE: r.Filepath is the target, and r.Target is the linkpath.
 | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 		return fs.symlink(r.Filepath, r.Target) | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 21:43:18 +08:00
										 |  |  |  | 	return errors.New("unsupported") | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func (fs *root) rename(oldpath, newpath string) error { | 
					
						
							|  |  |  |  | 	file, err := fs.lfetch(oldpath) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 	newpath, err = fs.canonName(newpath) | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	if !strings.HasPrefix(newpath, "/") { | 
					
						
							|  |  |  |  | 		return os.ErrInvalid | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	target, err := fs.lfetch(newpath) | 
					
						
							|  |  |  |  | 	if err != os.ErrNotExist { | 
					
						
							|  |  |  |  | 		if target == file { | 
					
						
							|  |  |  |  | 			// IEEE 1003.1: if oldpath and newpath are the same directory entry,
 | 
					
						
							|  |  |  |  | 			// then return no error, and perform no further action.
 | 
					
						
							|  |  |  |  | 			return nil | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		switch { | 
					
						
							|  |  |  |  | 		case file.IsDir(): | 
					
						
							|  |  |  |  | 			// IEEE 1003.1: if oldpath is a directory, and newpath exists,
 | 
					
						
							|  |  |  |  | 			// then newpath must be a directory, and empty.
 | 
					
						
							|  |  |  |  | 			// It is to be removed prior to rename.
 | 
					
						
							|  |  |  |  | 			if err := fs.rmdir(newpath); err != nil { | 
					
						
							|  |  |  |  | 				return err | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		case target.IsDir(): | 
					
						
							|  |  |  |  | 			// IEEE 1003.1: if oldpath is not a directory, and newpath exists,
 | 
					
						
							|  |  |  |  | 			// then newpath may not be a directory.
 | 
					
						
							|  |  |  |  | 			return syscall.EISDIR | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 	fs.files[newpath] = file | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	if file.IsDir() { | 
					
						
							|  |  |  |  | 		dirprefix := file.name + "/" | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 		for name, file := range fs.files { | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 			if strings.HasPrefix(name, dirprefix) { | 
					
						
							|  |  |  |  | 				newname := path.Join(newpath, strings.TrimPrefix(name, dirprefix)) | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 				fs.files[newname] = file | 
					
						
							|  |  |  |  | 				file.name = newname | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 				delete(fs.files, name) | 
					
						
							|  |  |  |  | 			} | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	file.name = newpath | 
					
						
							| 
									
										
										
										
											2020-09-24 21:37:46 +08:00
										 |  |  |  | 	delete(fs.files, oldpath) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 03:58:57 +08:00
										 |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 20:35:15 +08:00
										 |  |  |  | func (fs *root) PosixRename(r *Request) error { | 
					
						
							|  |  |  |  | 	if fs.mockErr != nil { | 
					
						
							|  |  |  |  | 		return fs.mockErr | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	_ = r.WithContext(r.Context()) // initialize context for deadlock testing
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	fs.mu.Lock() | 
					
						
							|  |  |  |  | 	defer fs.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return fs.rename(r.Filepath, r.Target) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 02:13:19 +08:00
										 |  |  |  | func (fs *root) StatVFS(r *Request) (*StatVFS, error) { | 
					
						
							|  |  |  |  | 	if fs.mockErr != nil { | 
					
						
							|  |  |  |  | 		return nil, fs.mockErr | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return getStatVFSForPath(r.Filepath) | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | func (fs *root) mkdir(pathname string) error { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	dir := &memFile{ | 
					
						
							|  |  |  |  | 		modtime: time.Now(), | 
					
						
							|  |  |  |  | 		isdir:   true, | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	return fs.putfile(pathname, dir) | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | func (fs *root) rmdir(pathname string) error { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	// IEEE 1003.1: If pathname is a symlink, then rmdir should fail with ENOTDIR.
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 	dir, err := fs.lfetch(pathname) | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 	if !dir.IsDir() { | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 		return syscall.ENOTDIR | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 	// use the dir‘s internal name not the pathname we passed in.
 | 
					
						
							|  |  |  |  | 	// the dir.name is always the canonical name of a directory.
 | 
					
						
							|  |  |  |  | 	pathname = dir.name | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	for name := range fs.files { | 
					
						
							|  |  |  |  | 		if path.Dir(name) == pathname { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 			return errors.New("directory not empty") | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	delete(fs.files, pathname) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | func (fs *root) link(oldpath, newpath string) error { | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	file, err := fs.lfetch(oldpath) | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if file.IsDir() { | 
					
						
							|  |  |  |  | 		return errors.New("hard link not allowed for directory") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	return fs.putfile(newpath, file) | 
					
						
							| 
									
										
										
										
											2020-09-21 20:40:35 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | // symlink() creates a symbolic link named `linkpath` which contains the string `target`.
 | 
					
						
							|  |  |  |  | // NOTE! This would be called with `symlink(req.Filepath, req.Target)` due to different semantics.
 | 
					
						
							|  |  |  |  | func (fs *root) symlink(target, linkpath string) error { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	link := &memFile{ | 
					
						
							|  |  |  |  | 		modtime: time.Now(), | 
					
						
							|  |  |  |  | 		symlink: target, | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 	return fs.putfile(linkpath, link) | 
					
						
							| 
									
										
										
										
											2020-09-22 00:55:15 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | func (fs *root) unlink(pathname string) error { | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 	// does not follow symlinks!
 | 
					
						
							|  |  |  |  | 	file, err := fs.lfetch(pathname) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if file.IsDir() { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 		// IEEE 1003.1: implementations may opt out of allowing the unlinking of directories.
 | 
					
						
							|  |  |  |  | 		// SFTP-v2: SSH_FXP_REMOVE may not remove directories.
 | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 		return os.ErrInvalid | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 	// DO NOT use the file’s internal name.
 | 
					
						
							|  |  |  |  | 	// because of hard-links files cannot have a single canonical name.
 | 
					
						
							|  |  |  |  | 	delete(fs.files, pathname) | 
					
						
							| 
									
										
										
										
											2020-09-17 23:53:23 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-28 09:22:11 +08:00
										 |  |  |  | type listerat []os.FileInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Modeled after strings.Reader's ReadAt() implementation
 | 
					
						
							|  |  |  |  | func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) { | 
					
						
							|  |  |  |  | 	var n int | 
					
						
							|  |  |  |  | 	if offset >= int64(len(f)) { | 
					
						
							|  |  |  |  | 		return 0, io.EOF | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	n = copy(ls, f[offset:]) | 
					
						
							|  |  |  |  | 	if n < len(ls) { | 
					
						
							|  |  |  |  | 		return n, io.EOF | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	return n, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-13 15:22:00 +08:00
										 |  |  |  | func (fs *root) Filelist(r *Request) (ListerAt, error) { | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 	if fs.mockErr != nil { | 
					
						
							|  |  |  |  | 		return nil, fs.mockErr | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	_ = r.WithContext(r.Context()) // initialize context for deadlock testing
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	fs.mu.Lock() | 
					
						
							|  |  |  |  | 	defer fs.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 	switch r.Method { | 
					
						
							|  |  |  |  | 	case "List": | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 		files, err := fs.readdir(r.Filepath) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return listerat(files), nil | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 	case "Stat": | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 		file, err := fs.fetch(r.Filepath) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 		return listerat{file}, nil | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 	case "Readlink": | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 		symlink, err := fs.readlink(r.Filepath) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:13:10 +08:00
										 |  |  |  | 		// SFTP-v2: The server will respond with a SSH_FXP_NAME packet containing only
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 		// one name and a dummy attributes value.
 | 
					
						
							|  |  |  |  | 		return listerat{ | 
					
						
							|  |  |  |  | 			&memFile{ | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 				name: symlink, | 
					
						
							|  |  |  |  | 				err:  os.ErrNotExist, // prevent accidental use as a reader/writer.
 | 
					
						
							| 
									
										
										
										
											2020-09-24 23:03:10 +08:00
										 |  |  |  | 			}, | 
					
						
							|  |  |  |  | 		}, nil | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-24 20:42:23 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 21:43:18 +08:00
										 |  |  |  | 	return nil, errors.New("unsupported") | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func (fs *root) readdir(pathname string) ([]os.FileInfo, error) { | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	dir, err := fs.fetch(pathname) | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	if !dir.IsDir() { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 		return nil, syscall.ENOTDIR | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	var files []os.FileInfo | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	for name, file := range fs.files { | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 		if path.Dir(name) == dir.name { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 			files = append(files, file) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	sort.Slice(files, func(i, j int) bool { return files[i].Name() < files[j].Name() }) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return files, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | func (fs *root) readlink(pathname string) (string, error) { | 
					
						
							|  |  |  |  | 	file, err := fs.lfetch(pathname) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return "", err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if file.symlink == "" { | 
					
						
							|  |  |  |  | 		return "", os.ErrInvalid | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return file.symlink, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-06 14:26:58 +08:00
										 |  |  |  | // implements LstatFileLister interface
 | 
					
						
							|  |  |  |  | func (fs *root) Lstat(r *Request) (ListerAt, error) { | 
					
						
							|  |  |  |  | 	if fs.mockErr != nil { | 
					
						
							|  |  |  |  | 		return nil, fs.mockErr | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 	_ = r.WithContext(r.Context()) // initialize context for deadlock testing
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:03:39 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	fs.mu.Lock() | 
					
						
							|  |  |  |  | 	defer fs.mu.Unlock() | 
					
						
							| 
									
										
										
										
											2020-09-06 14:26:58 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | 	file, err := fs.lfetch(r.Filepath) | 
					
						
							| 
									
										
										
										
											2020-09-06 14:26:58 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							|  |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	return listerat{file}, nil | 
					
						
							| 
									
										
										
										
											2020-09-06 14:26:58 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | // In memory file-system-y thing that the Hanlders live on
 | 
					
						
							|  |  |  |  | type root struct { | 
					
						
							| 
									
										
										
										
											2022-07-16 15:55:51 +08:00
										 |  |  |  | 	rootFile *memFile | 
					
						
							|  |  |  |  | 	mockErr  error | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	mu    sync.Mutex | 
					
						
							|  |  |  |  | 	files map[string]*memFile | 
					
						
							| 
									
										
										
										
											2018-01-17 06:18:45 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | // Set a mocked error that the next handler call will return.
 | 
					
						
							|  |  |  |  | // Set to nil to reset for no error.
 | 
					
						
							|  |  |  |  | func (fs *root) returnErr(err error) { | 
					
						
							|  |  |  |  | 	fs.mockErr = err | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | func (fs *root) lfetch(path string) (*memFile, error) { | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | 	if path == "/" { | 
					
						
							| 
									
										
										
										
											2020-09-21 20:18:41 +08:00
										 |  |  |  | 		return fs.rootFile, nil | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	file, ok := fs.files[path] | 
					
						
							|  |  |  |  | 	if file == nil { | 
					
						
							|  |  |  |  | 		if ok { | 
					
						
							|  |  |  |  | 			delete(fs.files, path) | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 		return nil, os.ErrNotExist | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return file, nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | // canonName returns the “canonical” name of a file, that is:
 | 
					
						
							|  |  |  |  | // if the directory of the pathname is a symlink, it follows that symlink to the valid directory name.
 | 
					
						
							|  |  |  |  | // this is relatively easy, since `dir.name` will be the only valid canonical path for a directory.
 | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | func (fs *root) canonName(pathname string) (string, error) { | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	dirname, filename := path.Dir(pathname), path.Base(pathname) | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:35:41 +08:00
										 |  |  |  | 	dir, err := fs.fetch(dirname) | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 		return "", err | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if !dir.IsDir() { | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 		return "", syscall.ENOTDIR | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 	return path.Join(dir.name, filename), nil | 
					
						
							| 
									
										
										
										
											2020-09-17 21:46:13 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-25 20:35:15 +08:00
										 |  |  |  | func (fs *root) exists(path string) bool { | 
					
						
							| 
									
										
										
										
											2020-09-25 22:01:02 +08:00
										 |  |  |  | 	path, err := fs.canonName(path) | 
					
						
							| 
									
										
										
										
											2020-09-25 20:35:15 +08:00
										 |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return false | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	_, err = fs.lfetch(path) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return err != os.ErrNotExist | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-24 22:50:14 +08:00
										 |  |  |  | func (fs *root) fetch(path string) (*memFile, error) { | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | 	file, err := fs.lfetch(path) | 
					
						
							|  |  |  |  | 	if err != nil { | 
					
						
							|  |  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-26 00:35:41 +08:00
										 |  |  |  | 	var count int | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | 	for file.symlink != "" { | 
					
						
							| 
									
										
										
										
											2020-09-26 00:35:41 +08:00
										 |  |  |  | 		if count++; count > maxSymlinkFollows { | 
					
						
							|  |  |  |  | 			return nil, errTooManySymlinks | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 19:47:19 +08:00
										 |  |  |  | 		file, err = fs.lfetch(file.symlink) | 
					
						
							|  |  |  |  | 		if err != nil { | 
					
						
							|  |  |  |  | 			return nil, err | 
					
						
							|  |  |  |  | 		} | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return file, nil | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-21 21:45:59 +08:00
										 |  |  |  | // Implements os.FileInfo, io.ReaderAt and io.WriterAt interfaces.
 | 
					
						
							| 
									
										
										
										
											2016-07-21 07:47:28 +08:00
										 |  |  |  | // These are the 3 interfaces necessary for the Handlers.
 | 
					
						
							| 
									
										
										
										
											2019-09-12 14:17:32 +08:00
										 |  |  |  | // Implements the optional interface TransferError.
 | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | type memFile struct { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	name    string | 
					
						
							|  |  |  |  | 	modtime time.Time | 
					
						
							|  |  |  |  | 	symlink string | 
					
						
							|  |  |  |  | 	isdir   bool | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 	mu      sync.RWMutex | 
					
						
							|  |  |  |  | 	content []byte | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	err     error | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | // These are helper functions, they must be called while holding the memFile.mu mutex
 | 
					
						
							|  |  |  |  | func (f *memFile) size() int64  { return int64(len(f.content)) } | 
					
						
							|  |  |  |  | func (f *memFile) grow(n int64) { f.content = append(f.content, make([]byte, n)...) } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | // Have memFile fulfill os.FileInfo interface
 | 
					
						
							| 
									
										
										
										
											2020-09-03 01:26:46 +08:00
										 |  |  |  | func (f *memFile) Name() string { return path.Base(f.name) } | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | func (f *memFile) Size() int64 { | 
					
						
							|  |  |  |  | 	f.mu.Lock() | 
					
						
							|  |  |  |  | 	defer f.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return f.size() | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | func (f *memFile) Mode() os.FileMode { | 
					
						
							|  |  |  |  | 	if f.isdir { | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 		return os.FileMode(0755) | os.ModeDir | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-20 02:25:33 +08:00
										 |  |  |  | 	if f.symlink != "" { | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 		return os.FileMode(0777) | os.ModeSymlink | 
					
						
							| 
									
										
										
										
											2016-07-20 02:25:33 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	return os.FileMode(0644) | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-09-14 05:05:32 +08:00
										 |  |  |  | func (f *memFile) ModTime() time.Time { return f.modtime } | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | func (f *memFile) IsDir() bool        { return f.isdir } | 
					
						
							|  |  |  |  | func (f *memFile) Sys() interface{} { | 
					
						
							| 
									
										
										
										
											2017-02-25 12:06:51 +08:00
										 |  |  |  | 	return fakeFileInfoSys() | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | func (f *memFile) ReadAt(b []byte, off int64) (int, error) { | 
					
						
							|  |  |  |  | 	f.mu.Lock() | 
					
						
							|  |  |  |  | 	defer f.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	if f.err != nil { | 
					
						
							|  |  |  |  | 		return 0, f.err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	if off < 0 { | 
					
						
							|  |  |  |  | 		return 0, errors.New("memFile.ReadAt: negative offset") | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	if off >= f.size() { | 
					
						
							|  |  |  |  | 		return 0, io.EOF | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	n := copy(b, f.content[off:]) | 
					
						
							|  |  |  |  | 	if n < len(b) { | 
					
						
							|  |  |  |  | 		return n, io.EOF | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	return n, nil | 
					
						
							| 
									
										
										
										
											2016-07-19 06:53:13 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | func (f *memFile) WriteAt(b []byte, off int64) (int, error) { | 
					
						
							| 
									
										
										
										
											2016-08-02 07:13:11 +08:00
										 |  |  |  | 	// fmt.Println(string(p), off)
 | 
					
						
							| 
									
										
										
										
											2016-08-02 05:09:42 +08:00
										 |  |  |  | 	// mimic write delays, should be optional
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	time.Sleep(time.Microsecond * time.Duration(len(b))) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	f.mu.Lock() | 
					
						
							|  |  |  |  | 	defer f.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	if f.err != nil { | 
					
						
							|  |  |  |  | 		return 0, f.err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	grow := int64(len(b)) + off - f.size() | 
					
						
							|  |  |  |  | 	if grow > 0 { | 
					
						
							|  |  |  |  | 		f.grow(grow) | 
					
						
							| 
									
										
										
										
											2016-07-30 06:57:06 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-09-12 14:17:32 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	return copy(f.content[off:], b), nil | 
					
						
							| 
									
										
										
										
											2020-08-30 16:40:22 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 15:51:12 +08:00
										 |  |  |  | func (f *memFile) Truncate(size int64) error { | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	f.mu.Lock() | 
					
						
							|  |  |  |  | 	defer f.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	if f.err != nil { | 
					
						
							|  |  |  |  | 		return f.err | 
					
						
							|  |  |  |  | 	} | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 	grow := size - f.size() | 
					
						
							| 
									
										
										
										
											2020-08-22 15:51:12 +08:00
										 |  |  |  | 	if grow <= 0 { | 
					
						
							|  |  |  |  | 		f.content = f.content[:size] | 
					
						
							|  |  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 		f.grow(grow) | 
					
						
							| 
									
										
										
										
											2020-08-22 15:51:12 +08:00
										 |  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-09-17 17:53:13 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-22 15:51:12 +08:00
										 |  |  |  | 	return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-12 14:17:32 +08:00
										 |  |  |  | func (f *memFile) TransferError(err error) { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:14:59 +08:00
										 |  |  |  | 	f.mu.Lock() | 
					
						
							|  |  |  |  | 	defer f.mu.Unlock() | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 	f.err = err | 
					
						
							| 
									
										
										
										
											2019-09-12 14:17:32 +08:00
										 |  |  |  | } |