Эх сурвалжийг харах

Finalized experimental sftp server module

Toby Chui 2 жил өмнө
parent
commit
d509f4627a

+ 128 - 43
mod/storage/sftpserver/vroot.go

@@ -4,6 +4,7 @@ import (
 	"errors"
 	"fmt"
 	"io"
+	"io/fs"
 	"os"
 	"path"
 	"path/filepath"
@@ -32,6 +33,36 @@ type rootFolder struct {
 	content []byte
 }
 
+//Fake folders in root for vroot redirections
+type rootEntry struct {
+	thisFsh *filesystem.FileSystemHandler
+}
+
+func NewVrootEmulatedDirEntry(fsh *filesystem.FileSystemHandler) *rootEntry {
+	return &rootEntry{
+		thisFsh: fsh,
+	}
+}
+
+func (r *rootEntry) Name() string {
+	return r.thisFsh.UUID
+}
+func (r *rootEntry) Size() int64 {
+	return 0
+}
+func (r *rootEntry) Mode() os.FileMode {
+	return fs.ModeDir
+}
+func (r *rootEntry) ModTime() time.Time {
+	return time.Now()
+}
+func (r *rootEntry) IsDir() bool {
+	return true
+}
+func (r *rootEntry) Sys() interface{} {
+	return nil
+}
+
 type sftpFileInterface interface {
 	Name() string
 	Size() int64
@@ -120,7 +151,6 @@ func (fs *root) getFshFromID(fshID string) *filesystem.FileSystemHandler {
 	return nil
 }
 
-// Example Handlers
 func (fs *root) Fileread(r *sftp.Request) (io.ReaderAt, error) {
 	flags := r.Pflags()
 	if !flags.Read {
@@ -132,22 +162,37 @@ func (fs *root) Fileread(r *sftp.Request) (io.ReaderAt, error) {
 }
 
 func (fs *root) Filewrite(r *sftp.Request) (io.WriterAt, error) {
+	if arozfs.ToSlash(filepath.Dir(r.Filepath)) == "/" {
+		//Uploading to virtual root folder. Return error
+		return nil, errors.New("ArozOS SFTP root is read only")
+	}
 
-	return nil, errors.New("wip")
-}
+	fsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(r.Filepath)
+	if err != nil {
+		return nil, err
+	}
 
-func (fs *root) OpenFile(r *sftp.Request) (sftp.WriterAtReaderAt, error) {
+	f, err := fsh.FileSystemAbstraction.OpenFile(rpath, os.O_CREATE|os.O_WRONLY, 0775)
+	if err != nil {
+		return nil, err
+	}
 
-	return nil, errors.New("wip")
+	return f, nil
 }
 
-func (fs *root) putfile(pathname string, file *arozfs.File) error {
+func (fs *root) OpenFile(r *sftp.Request) (sftp.WriterAtReaderAt, error) {
+	fmt.Println("Open File", r.Filepath)
+	fsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(r.Filepath)
+	if err != nil {
+		return nil, err
+	}
 
-	return nil
-}
+	f, err := fsh.FileSystemAbstraction.OpenFile(rpath, os.O_RDWR, 0775)
+	if err != nil {
+		return nil, err
+	}
 
-func (fs *root) openfile(pathname string, flags uint32) (*arozfs.File, error) {
-	return nil, errors.New("wip")
+	return f, nil
 }
 
 func (fs *root) Filecmd(r *sftp.Request) error {
@@ -158,9 +203,9 @@ func (fs *root) Filecmd(r *sftp.Request) error {
 	case "Rename":
 		// 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.
-		if fs.exists(r.Target) {
-			return os.ErrExist
-		}
+		//if fs.exists(r.Target) {
+		//	return os.ErrExist
+		//}
 
 		return fs.rename(r.Filepath, r.Target)
 
@@ -187,8 +232,39 @@ func (fs *root) Filecmd(r *sftp.Request) error {
 }
 
 func (fs *root) rename(oldpath, newpath string) error {
+	oldFsh, _, realOldPath, err := fs.getFshAndSubpathFromSFTPPathname(oldpath)
+	if err != nil {
+		return err
+	}
+	newFsh, _, realNewPath, err := fs.getFshAndSubpathFromSFTPPathname(newpath)
+	if err != nil {
+		return err
+	}
 
-	return errors.New("wip")
+	if oldFsh.UUID == newFsh.UUID {
+		//Use rename function
+		err = oldFsh.FileSystemAbstraction.Rename(realOldPath, realNewPath)
+		if err != nil {
+			return err
+		}
+	} else {
+		//Cross root rename (aka move)
+		src, err := oldFsh.FileSystemAbstraction.ReadStream(realOldPath)
+		if err != nil {
+			return err
+		}
+		defer src.Close()
+
+		err = newFsh.FileSystemAbstraction.WriteStream(realNewPath, src, 0775)
+		if err != nil {
+			return err
+		}
+
+		//Remove the src
+		//oldFsh.FileSystemAbstraction.RemoveAll(realOldPath)
+	}
+
+	return nil
 }
 
 func (fs *root) PosixRename(r *sftp.Request) error {
@@ -196,18 +272,24 @@ func (fs *root) PosixRename(r *sftp.Request) error {
 }
 
 func (fs *root) StatVFS(r *sftp.Request) (*sftp.StatVFS, error) {
-
-	return nil, errors.New("wip")
+	return nil, errors.New("unsupported")
 }
 
 func (fs *root) mkdir(pathname string) error {
+	fsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(pathname)
+	if err != nil {
+		return err
+	}
 
-	return errors.New("wip")
+	return fsh.FileSystemAbstraction.MkdirAll(rpath, 0775)
 }
 
 func (fs *root) rmdir(pathname string) error {
-
-	return errors.New("wip")
+	fsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(pathname)
+	if err != nil {
+		return err
+	}
+	return fsh.FileSystemAbstraction.RemoveAll(rpath)
 }
 
 func (fs *root) link(oldpath, newpath string) error {
@@ -221,16 +303,18 @@ func (fs *root) symlink(target, linkpath string) error {
 }
 
 func (fs *root) unlink(pathname string) error {
+	fsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(pathname)
+	if err != nil {
+		return err
+	}
 
-	/*
-		if file.IsDir() {
-			// IEEE 1003.1: implementations may opt out of allowing the unlinking of directories.
-			// SFTP-v2: SSH_FXP_REMOVE may not remove directories.
-			return os.ErrInvalid
-		}
-	*/
+	if fsh.FileSystemAbstraction.IsDir(rpath) {
+		// IEEE 1003.1: implementations may opt out of allowing the unlinking of directories.
+		// SFTP-v2: SSH_FXP_REMOVE may not remove directories.
+		return os.ErrInvalid
+	}
 
-	return errors.New("wip")
+	return fsh.FileSystemAbstraction.Remove(rpath)
 }
 
 type listerat []os.FileInfo
@@ -272,21 +356,26 @@ func (fs *root) Filelist(r *sftp.Request) (sftp.ListerAt, error) {
 }
 
 func (fs *root) readdir(pathname string) ([]os.FileInfo, error) {
-	dir, err := fs.fetch(pathname)
-	if err != nil {
-		return nil, err
-	}
-
-	if !dir.IsDir() {
-		return nil, syscall.ENOTDIR
+	if cleanPath(pathname) == "/" {
+		//Handle special root listing
+		results := []os.FileInfo{}
+		for _, fsh := range fs.fshs {
+			results = append(results, NewVrootEmulatedDirEntry(fsh))
+		}
+		return results, nil
 	}
 
 	//Get the content of the dir using fsh infrastructure
-	fmt.Println("READDIR", pathname, dir.Name())
 	targetFsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(pathname)
 	if err != nil {
 		return nil, err
 	}
+
+	if !targetFsh.FileSystemAbstraction.IsDir(rpath) {
+		return nil, syscall.ENOTDIR
+	}
+
+	//Read Dir, and convert the results into os.FileInfo
 	entries, err := targetFsh.FileSystemAbstraction.ReadDir(rpath)
 	if err != nil {
 		return nil, err
@@ -363,14 +452,16 @@ func (fs *root) getFshAndSubpathFromSFTPPathname(pathname string) (*filesystem.F
 
 func (fs *root) lfetch(path string) (sftpFileInterface, error) {
 	path = strings.TrimSpace(path)
-	fmt.Println("lfetch", path)
 	if path == "/" {
-		fmt.Println("Requesting Root")
+		fmt.Println("Requesting SFTP Root")
 		return fs.rootFile, nil
 	}
 
 	//Fetching path other than root. Extract the vroot id from the path
 	fsh, _, rpath, err := fs.getFshAndSubpathFromSFTPPathname(path)
+	if err != nil {
+		return nil, err
+	}
 	fshAbs := fsh.FileSystemAbstraction
 
 	if !fshAbs.FileExists(rpath) {
@@ -388,11 +479,6 @@ func (fs *root) lfetch(path string) (sftpFileInterface, error) {
 	return f2, nil
 }
 
-func (fs *root) exists(path string) bool {
-	_, err := fs.lfetch(path)
-	return err != nil
-}
-
 func (fs *root) fetch(path string) (sftpFileInterface, error) {
 	file, err := fs.lfetch(path)
 	if err != nil {
@@ -420,7 +506,6 @@ func (f *rootFolder) ReadAt(b []byte, off int64) (int, error) {
 }
 
 func (f *rootFolder) WriteAt(b []byte, off int64) (int, error) {
-	// fmt.Println(string(p), off)
 	// mimic write delays, should be optional
 	time.Sleep(time.Microsecond * time.Duration(len(b)))
 	return 0, errors.New("root folder not support writeAt")