Selaa lähdekoodia

Added WIP FTP driver

Toby Chui 3 vuotta sitten
vanhempi
commit
a8b392e029

BIN
arozos.exe~


+ 3 - 0
go.mod

@@ -51,8 +51,11 @@ require (
 	github.com/golang/snappy v0.0.4 // indirect
 	github.com/gopherjs/gopherjs v1.17.2 // indirect
 	github.com/gorilla/securecookie v1.1.1 // indirect
+	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-multierror v1.1.1 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
 	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
+	github.com/jlaffaye/ftp v0.1.0 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/klauspost/compress v1.15.9 // indirect
 	github.com/klauspost/pgzip v1.2.5 // indirect

+ 6 - 0
go.sum

@@ -291,10 +291,14 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb
 github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
 github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
 github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
 github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
@@ -321,6 +325,8 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
 github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
 github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
+github.com/jlaffaye/ftp v0.1.0 h1:DLGExl5nBoSFoNshAUHwXAezXwXBvFdx7/qwhucWNSE=
+github.com/jlaffaye/ftp v0.1.0/go.mod h1:hhq4G4crv+nW2qXtNYcuzLeOudG92Ps37HEKeg2e3lE=
 github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=

+ 146 - 0
mod/filesystem/abstractions/ftpfs/ftpFileWrapper.go

@@ -0,0 +1,146 @@
+package ftpfs
+
+import (
+	"io/fs"
+	"time"
+
+	"github.com/jlaffaye/ftp"
+)
+
+type File struct {
+	entry ftp.Entry
+}
+
+/*
+func NewFTPFsFile(wrappingFile *smb2.File) *File {
+	return &smbfsFile{
+		file: wrappingFile,
+	}
+}
+
+func (f *File) Chdir() error {
+	return arozfs.ErrOperationNotSupported
+}
+func (f *File) Chmod(mode os.FileMode) error {
+	return arozfs.ErrOperationNotSupported
+}
+func (f *File) Chown(uid, gid int) error {
+	return arozfs.ErrOperationNotSupported
+}
+func (f *File) Close() error {
+	return f.file.Close()
+}
+func (f *File) Name() string {
+	return f.file.Name()
+}
+func (f *File) Read(b []byte) (n int, err error) {
+	return f.file.Read(b)
+}
+func (f *File) ReadAt(b []byte, off int64) (n int, err error) {
+	return f.file.ReadAt(b, off)
+}
+func (f *File) ReadDir(n int) ([]fs.DirEntry, error) {
+	return []fs.DirEntry{}, arozfs.ErrOperationNotSupported
+}
+func (f *File) Readdirnames(n int) (names []string, err error) {
+	fi, err := f.file.Readdir(n)
+	if err != nil {
+		return []string{}, err
+	}
+	names = []string{}
+	for _, i := range fi {
+		names = append(names, i.Name())
+	}
+	return names, nil
+}
+func (f *File) ReadFrom(r io.Reader) (n int64, err error) {
+	return f.file.ReadFrom(r)
+}
+func (f *File) Readdir(n int) ([]fs.FileInfo, error) {
+	return f.file.Readdir(n)
+}
+func (f *File) Seek(offset int64, whence int) (ret int64, err error) {
+	return f.file.Seek(offset, whence)
+}
+func (f *File) Stat() (fs.FileInfo, error) {
+	return f.file.Stat()
+}
+func (f *File) Sync() error {
+	return f.file.Sync()
+}
+func (f *File) Truncate(size int64) error {
+	return f.file.Truncate(size)
+}
+func (f *File) Write(b []byte) (n int, err error) {
+	return f.file.Write(b)
+}
+func (f *File) WriteAt(b []byte, off int64) (n int, err error) {
+	return f.file.WriteAt(b, off)
+}
+func (f *File) WriteString(s string) (n int, err error) {
+	return f.file.WriteString(s)
+}
+*/
+
+type DirEntry struct {
+	finfo *ftp.Entry
+	conn  *ftp.ServerConn
+	path  string
+}
+
+func newDirEntryFromFTPEntry(targetEntry *ftp.Entry, conn *ftp.ServerConn, originalPath string) *DirEntry {
+	return &DirEntry{
+		finfo: targetEntry,
+		conn:  conn,
+	}
+}
+
+func (de DirEntry) Name() string {
+	return de.finfo.Name
+}
+
+func (de DirEntry) IsDir() bool {
+	return de.finfo.Type == ftp.EntryTypeFolder
+}
+
+func (de DirEntry) Type() fs.FileMode {
+	return 0777
+}
+
+func (de DirEntry) Info() (fs.FileInfo, error) {
+	e := NewFileInfoFromEntry(de.finfo, de.conn, de.path)
+	return e, nil
+}
+
+type FileInfo struct {
+	entry *ftp.Entry
+	conn  *ftp.ServerConn
+	path  string
+}
+
+func NewFileInfoFromEntry(e *ftp.Entry, c *ftp.ServerConn, originalPath string) FileInfo {
+	return FileInfo{
+		entry: e,
+		conn:  c,
+		path:  originalPath,
+	}
+}
+
+func (fi FileInfo) Name() string {
+	return fi.entry.Name
+}
+func (fi FileInfo) Size() int64 {
+	return int64(fi.entry.Size)
+}
+func (fi FileInfo) Mode() fs.FileMode {
+	return 664
+}
+func (fi FileInfo) ModTime() time.Time {
+	return fi.entry.Time
+}
+func (fi FileInfo) IsDir() bool {
+	return fi.entry.Type == ftp.EntryTypeFolder
+}
+func (fi FileInfo) Sys() interface{} {
+	return nil
+}

+ 226 - 0
mod/filesystem/abstractions/ftpfs/ftpfs.go

@@ -0,0 +1,226 @@
+package ftpfs
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/fs"
+	"io/ioutil"
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+	"time"
+
+	"github.com/jlaffaye/ftp"
+	"imuslab.com/arozos/mod/filesystem/arozfs"
+)
+
+/*
+	FTPFS.go
+
+	FTP Server as File System Abstraction
+
+*/
+
+type FTPFSAbstraction struct {
+	uuid      string
+	hierarchy string
+	conn      *ftp.ServerConn
+	closer    chan bool
+}
+
+func NewFTPFSAbstraction(uuid string, hierarchy string, hostname string, username string, password string) (FTPFSAbstraction, error) {
+	c, err := ftp.Dial(hostname, ftp.DialWithTimeout(5*time.Second))
+	if err != nil {
+		log.Println("[FTPFS] Unable to dial TCP: " + err.Error())
+		return FTPFSAbstraction{}, err
+	}
+
+	if username == "" && password == "" {
+		username = "anonymouss"
+		password = "anonymous"
+	}
+
+	//Login to the FTP account
+	err = c.Login(username, password)
+	if err != nil {
+		return FTPFSAbstraction{}, err
+	}
+
+	//Create a ticker to prevent connection close
+	ticker := time.NewTicker(180 * time.Second)
+	done := make(chan bool)
+
+	go func() {
+		for {
+			select {
+			case <-done:
+				return
+			case <-ticker.C:
+				c.NoOp()
+			}
+		}
+	}()
+
+	return FTPFSAbstraction{
+		uuid:      uuid,
+		hierarchy: hierarchy,
+		conn:      c,
+		closer:    done,
+	}, nil
+}
+func (l FTPFSAbstraction) Chmod(filename string, mode os.FileMode) error {
+	return arozfs.ErrOperationNotSupported
+}
+func (l FTPFSAbstraction) Chown(filename string, uid int, gid int) error {
+	return arozfs.ErrOperationNotSupported
+}
+func (l FTPFSAbstraction) Chtimes(filename string, atime time.Time, mtime time.Time) error {
+	return arozfs.ErrOperationNotSupported
+}
+func (l FTPFSAbstraction) Create(filename string) (arozfs.File, error) {
+	return nil, arozfs.ErrOperationNotSupported
+}
+func (l FTPFSAbstraction) Mkdir(filename string, mode os.FileMode) error {
+	return l.conn.MakeDir(filename)
+}
+func (l FTPFSAbstraction) MkdirAll(filename string, mode os.FileMode) error {
+	return l.Mkdir(filename, mode)
+}
+func (l FTPFSAbstraction) Name() string {
+	return ""
+}
+func (l FTPFSAbstraction) Open(filename string) (arozfs.File, error) {
+	return nil, arozfs.ErrOperationNotSupported
+}
+func (l FTPFSAbstraction) OpenFile(filename string, flag int, perm os.FileMode) (arozfs.File, error) {
+	return nil, arozfs.ErrOperationNotSupported
+}
+func (l FTPFSAbstraction) Remove(filename string) error {
+	filename = filterFilepath(filename)
+	return l.conn.Delete(filename)
+}
+func (l FTPFSAbstraction) RemoveAll(path string) error {
+	path = filterFilepath(path)
+	return l.conn.Delete(path)
+}
+func (l FTPFSAbstraction) Rename(oldname, newname string) error {
+	oldname = filterFilepath(oldname)
+	newname = filterFilepath(newname)
+	return l.conn.Rename(oldname, newname)
+}
+func (l FTPFSAbstraction) Stat(filename string) (os.FileInfo, error) {
+	return nil, arozfs.ErrNullOperation
+}
+func (l FTPFSAbstraction) Close() error {
+	return l.conn.Quit()
+}
+
+/*
+	Abstraction Utilities
+*/
+
+func (l FTPFSAbstraction) VirtualPathToRealPath(subpath string, username string) (string, error) {
+	return arozfs.GenericVirtualPathToRealPathTranslator(l.uuid, l.hierarchy, subpath, username)
+}
+
+func (l FTPFSAbstraction) RealPathToVirtualPath(fullpath string, username string) (string, error) {
+	return arozfs.GenericRealPathToVirtualPathTranslator(l.uuid, l.hierarchy, fullpath, username)
+}
+
+func (l FTPFSAbstraction) FileExists(realpath string) bool {
+	realpath = filterFilepath(realpath)
+	_, err := l.conn.GetEntry(realpath)
+	return err == nil
+}
+
+func (l FTPFSAbstraction) IsDir(realpath string) bool {
+	realpath = filterFilepath(realpath)
+	entry, err := l.conn.GetEntry(realpath)
+	if err != nil {
+		return false
+	}
+
+	return entry.Type == ftp.EntryTypeFolder
+}
+
+func (l FTPFSAbstraction) Glob(realpathWildcard string) ([]string, error) {
+	return []string{}, arozfs.ErrNullOperation
+}
+
+func (l FTPFSAbstraction) GetFileSize(realpath string) int64 {
+	realpath = filterFilepath(realpath)
+	entry, err := l.conn.GetEntry(realpath)
+	if err != nil {
+		return 0
+	}
+	return int64(entry.Size)
+}
+
+func (l FTPFSAbstraction) GetModTime(realpath string) (int64, error) {
+	realpath = filterFilepath(realpath)
+	entry, err := l.conn.GetEntry(realpath)
+	if err != nil {
+		return 0, err
+	}
+
+	return entry.Time.Unix(), nil
+}
+
+func (l FTPFSAbstraction) WriteFile(filename string, content []byte, mode os.FileMode) error {
+	filename = filterFilepath(filename)
+	reader := bytes.NewReader(content)
+	return l.conn.Stor(filename, reader)
+}
+
+func (l FTPFSAbstraction) ReadFile(filename string) ([]byte, error) {
+	filename = filterFilepath(filename)
+	r, err := l.conn.Retr(filename)
+	if err != nil {
+		panic(err)
+	}
+	defer r.Close()
+
+	return ioutil.ReadAll(r)
+}
+func (l FTPFSAbstraction) ReadDir(filename string) ([]fs.DirEntry, error) {
+	results := []fs.DirEntry{}
+	filename = filterFilepath(filename)
+	entries, err := l.conn.List(filename)
+	if err != nil {
+
+		return results, err
+	}
+
+	for _, entry := range entries {
+		entryFilename := arozfs.ToSlash(filepath.Join(filename, entry.Name))
+		fmt.Println(entryFilename)
+		thisDirEntry := newDirEntryFromFTPEntry(entry, l.conn, entryFilename)
+		results = append(results, thisDirEntry)
+	}
+	return results, nil
+}
+func (l FTPFSAbstraction) WriteStream(filename string, stream io.Reader, mode os.FileMode) error {
+	filename = filterFilepath(filename)
+	return l.conn.Stor(filename, stream)
+}
+func (l FTPFSAbstraction) ReadStream(filename string) (io.ReadCloser, error) {
+	filename = filterFilepath(filename)
+	return l.conn.Retr(filename)
+}
+
+func (l FTPFSAbstraction) Walk(root string, walkFn filepath.WalkFunc) error {
+	return arozfs.ErrOperationNotSupported
+}
+
+//Utilities
+func filterFilepath(rawpath string) string {
+	rawpath = arozfs.ToSlash(filepath.Clean(strings.TrimSpace(rawpath)))
+	if strings.HasPrefix(rawpath, "./") {
+		return rawpath[1:]
+	} else if rawpath == "." || rawpath == "" {
+		return "/"
+	}
+	return rawpath
+}

+ 21 - 21
mod/filesystem/filesystem.go

@@ -23,6 +23,7 @@ import (
 	"time"
 
 	db "imuslab.com/arozos/mod/database"
+	"imuslab.com/arozos/mod/filesystem/abstractions/ftpfs"
 	"imuslab.com/arozos/mod/filesystem/abstractions/localfs"
 	"imuslab.com/arozos/mod/filesystem/abstractions/smbfs"
 	"imuslab.com/arozos/mod/filesystem/abstractions/webdavfs"
@@ -226,27 +227,26 @@ func NewFileSystemHandler(option FileSystemOption) (*FileSystemHandler, error) {
 			Filesystem:            fstype,
 			Closed:                false,
 		}, nil
-	} else if fstype == "nfs" {
-		/*
-			ftpfs, err := nfsfs.NewNFSFileSystemAbstraction(option.Uuid, option.Hierarchy, option.Path, option.Username, option.Password)
-			if err != nil {
-				return nil, err
-			}
-			return &FileSystemHandler{
-				Name:                  option.Name,
-				UUID:                  option.Uuid,
-				Path:                  option.Path,
-				ReadOnly:              option.Access == arozfs.FsReadOnly,
-				RequireBuffer:         true,
-				Hierarchy:             option.Hierarchy,
-				HierarchyConfig:       nil,
-				InitiationTime:        time.Now().Unix(),
-				FilesystemDatabase:    nil,
-				FileSystemAbstraction: ftpfs,
-				Filesystem:            fstype,
-				Closed:                false,
-			}, nil
-		*/
+	} else if fstype == "ftp" {
+
+		ftpfs, err := ftpfs.NewFTPFSAbstraction(option.Uuid, option.Hierarchy, option.Path, option.Username, option.Password)
+		if err != nil {
+			return nil, err
+		}
+		return &FileSystemHandler{
+			Name:                  option.Name,
+			UUID:                  option.Uuid,
+			Path:                  option.Path,
+			ReadOnly:              option.Access == arozfs.FsReadOnly,
+			RequireBuffer:         true,
+			Hierarchy:             option.Hierarchy,
+			HierarchyConfig:       nil,
+			InitiationTime:        time.Now().Unix(),
+			FilesystemDatabase:    nil,
+			FileSystemAbstraction: ftpfs,
+			Filesystem:            fstype,
+			Closed:                false,
+		}, nil
 
 	} else if option.Filesystem == "virtual" {
 		//Virtual filesystem, deprecated

+ 1 - 0
web/SystemAO/storage/fshedit.html

@@ -143,6 +143,7 @@
                     <div class="item" data-value="fat">FAT</div>
                     <div class="item" data-value="webdav">WebDAV</div>
                     <div class="item" data-value="smb">SMB</div>
+                    <div class="item" data-value="ftp">FTP</div>
                 </div>
                 </div>
             </div>