github.com/divyam234/rclone@v1.64.1/cmd/serve/sftp/handler.go (about) 1 //go:build !plan9 2 // +build !plan9 3 4 package sftp 5 6 import ( 7 "io" 8 "os" 9 "syscall" 10 "time" 11 12 "github.com/pkg/sftp" 13 "github.com/divyam234/rclone/fs" 14 "github.com/divyam234/rclone/vfs" 15 ) 16 17 // vfsHandler converts the VFS to be served by SFTP 18 type vfsHandler struct { 19 *vfs.VFS 20 } 21 22 // vfsHandler returns a Handlers object with the test handlers. 23 func newVFSHandler(vfs *vfs.VFS) sftp.Handlers { 24 v := vfsHandler{VFS: vfs} 25 return sftp.Handlers{ 26 FileGet: v, 27 FilePut: v, 28 FileCmd: v, 29 FileList: v, 30 } 31 } 32 33 func (v vfsHandler) Fileread(r *sftp.Request) (io.ReaderAt, error) { 34 file, err := v.OpenFile(r.Filepath, os.O_RDONLY, 0777) 35 if err != nil { 36 return nil, err 37 } 38 return file, nil 39 } 40 41 func (v vfsHandler) Filewrite(r *sftp.Request) (io.WriterAt, error) { 42 file, err := v.OpenFile(r.Filepath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0777) 43 if err != nil { 44 return nil, err 45 } 46 return file, nil 47 } 48 49 func (v vfsHandler) Filecmd(r *sftp.Request) error { 50 switch r.Method { 51 case "Setstat": 52 attr := r.Attributes() 53 if attr.Mtime != 0 { 54 modTime := time.Unix(int64(attr.Mtime), 0) 55 err := v.Chtimes(r.Filepath, modTime, modTime) 56 if err != nil { 57 return err 58 } 59 } 60 return nil 61 case "Rename": 62 err := v.Rename(r.Filepath, r.Target) 63 if err != nil { 64 return err 65 } 66 case "Rmdir", "Remove": 67 err := v.Remove(r.Filepath) 68 if err != nil { 69 return err 70 } 71 case "Mkdir": 72 err := v.Mkdir(r.Filepath, 0777) 73 if err != nil { 74 return err 75 } 76 case "Symlink": 77 // FIXME 78 // _, err := v.fetch(r.Filepath) 79 // if err != nil { 80 // return err 81 // } 82 // link := newMemFile(r.Target, false) 83 // link.symlink = r.Filepath 84 // v.files[r.Target] = link 85 return sftp.ErrSshFxOpUnsupported 86 } 87 return nil 88 } 89 90 type listerat []os.FileInfo 91 92 // Modeled after strings.Reader's ReadAt() implementation 93 func (f listerat) ListAt(ls []os.FileInfo, offset int64) (int, error) { 94 var n int 95 if offset >= int64(len(f)) { 96 return 0, io.EOF 97 } 98 n = copy(ls, f[offset:]) 99 if n < len(ls) { 100 return n, io.EOF 101 } 102 return n, nil 103 } 104 105 func (v vfsHandler) Filelist(r *sftp.Request) (l sftp.ListerAt, err error) { 106 var node vfs.Node 107 var handle vfs.Handle 108 switch r.Method { 109 case "List": 110 node, err = v.Stat(r.Filepath) 111 if err != nil { 112 return nil, err 113 } 114 if !node.IsDir() { 115 return nil, syscall.ENOTDIR 116 } 117 handle, err = node.Open(os.O_RDONLY) 118 if err != nil { 119 return nil, err 120 } 121 defer fs.CheckClose(handle, &err) 122 fis, err := handle.Readdir(-1) 123 if err != nil { 124 return nil, err 125 } 126 return listerat(fis), nil 127 case "Stat": 128 node, err = v.Stat(r.Filepath) 129 if err != nil { 130 return nil, err 131 } 132 return listerat([]os.FileInfo{node}), nil 133 case "Readlink": 134 // FIXME 135 // if file.symlink != "" { 136 // file, err = v.fetch(file.symlink) 137 // if err != nil { 138 // return nil, err 139 // } 140 // } 141 // return listerat([]os.FileInfo{file}), nil 142 } 143 return nil, sftp.ErrSshFxOpUnsupported 144 }