|
@@ -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")
|