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  }