github.com/artpar/rclone@v1.67.3/cmd/serve/sftp/handler.go (about)

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