Remove unnecessary interface methods and implement cur direcoty #95

Merged
lunny merged 3 commits from lunny/cur_dir into master 2020-02-08 10:21:03 +00:00
9 changed files with 123 additions and 82 deletions

15
cmd.go
View File

@ -225,9 +225,18 @@ func (cmd commandCwd) RequireAuth() bool {
func (cmd commandCwd) Execute(conn *Conn, param string) {
path := conn.buildPath(param)
err := conn.driver.ChangeDir(path)
info, err := conn.driver.Stat(path)
if err != nil {
conn.writeMessage(550, fmt.Sprint("Directory change to ", path, " failed: ", err))
return
}
if !info.IsDir() {
conn.writeMessage(550, fmt.Sprint("Directory change to ", path, " is a file"))
return
}
err = conn.changeCurDir(path)
if err == nil {
conn.namePrefix = path
conn.writeMessage(250, "Directory changed to "+path)
} else {
conn.writeMessage(550, fmt.Sprint("Directory change to ", path, " failed: ", err))
@ -713,7 +722,7 @@ func (cmd commandPwd) RequireAuth() bool {
}
func (cmd commandPwd) Execute(conn *Conn, param string) {
conn.writeMessage(257, "\""+conn.namePrefix+"\" is the current directory")
conn.writeMessage(257, "\""+conn.curDir+"\" is the current directory")
}
// CommandQuit responds to the QUIT FTP command. The client has requested the

11
conn.go
View File

@ -37,7 +37,7 @@ type Conn struct {
server *Server
tlsConfig *tls.Config
sessionID string
namePrefix string
curDir string
reqUser string
user string
renameFrom string
@ -242,9 +242,9 @@ func (conn *Conn) buildPath(filename string) (fullPath string) {
if len(filename) > 0 && filename[0:1] == "/" {
fullPath = filepath.Clean(filename)
} else if len(filename) > 0 && filename != "-a" {
fullPath = filepath.Clean(conn.namePrefix + "/" + filename)
fullPath = filepath.Clean(conn.curDir + "/" + filename)
} else {
fullPath = filepath.Clean(conn.namePrefix)
fullPath = filepath.Clean(conn.curDir)
}
fullPath = strings.Replace(fullPath, "//", "/", -1)
fullPath = strings.Replace(fullPath, string(filepath.Separator), "/", -1)
@ -279,3 +279,8 @@ func (conn *Conn) sendOutofBandDataWriter(data io.ReadCloser) error {
return nil
}
func (conn *Conn) changeCurDir(path string) error {
conn.curDir = path
return nil
}

View File

@ -12,7 +12,7 @@ import (
func TestConnBuildPath(t *testing.T) {
c := &Conn{
namePrefix: "",
curDir: "",
}
var pathtests = []struct {
in string

View File

@ -20,20 +20,12 @@ type DriverFactory interface {
// chosen persistence layer. graval will create a new instance of your
// driver for each client that connects and delegate to it as required.
type Driver interface {
// Init init
Init(*Conn)
// params - a file path
// returns - a time indicating when the requested path was last modified
// - an error if the file doesn't exist or the user lacks
// permissions
Stat(string) (FileInfo, error)
// params - path
// returns - true if the current user is permitted to change to the
// requested path
ChangeDir(string) error
// params - path, function on file or subdir found
// returns - error
// path
@ -71,19 +63,6 @@ type MultipleDriver struct {
drivers map[string]Driver
}
// Init init
func (driver *MultipleDriver) Init(conn *Conn) {
}
func (driver *MultipleDriver) ChangeDir(path string) error {
for prefix, driver := range driver.drivers {
if strings.HasPrefix(path, prefix) {
return driver.ChangeDir(strings.TrimPrefix(path, prefix))
}
}
return errors.New("Not a directory")
}
func (driver *MultipleDriver) Stat(path string) (FileInfo, error) {
for prefix, driver := range driver.drivers {
if strings.HasPrefix(path, prefix) {

View File

@ -23,22 +23,6 @@ func (driver *FileDriver) realPath(path string) string {
return filepath.Join(append([]string{driver.RootPath}, paths...)...)
}
func (driver *FileDriver) Init(conn *Conn) {
//driver.conn = conn
}
func (driver *FileDriver) ChangeDir(path string) error {
rPath := driver.realPath(path)
f, err := os.Lstat(rPath)
if err != nil {
return err
}
if f.IsDir() {
return nil
}
return errors.New("Not a directory")
}
func (driver *FileDriver) Stat(path string) (FileInfo, error) {
basepath := driver.realPath(path)
rPath, err := filepath.Abs(basepath)
@ -149,9 +133,9 @@ func (driver *FileDriver) GetFile(path string, offset int64) (int64, io.ReadClos
return 0, nil, err
}
f.Seek(offset, os.SEEK_SET)
f.Seek(offset, io.SeekStart)
return info.Size(), f, nil
return info.Size() - offset, f, nil
}
func (driver *FileDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {

View File

@ -7,6 +7,7 @@ package server
import (
"errors"
"io"
"log"
"os"
"strings"
"time"
@ -15,7 +16,7 @@ import (
)
var (
ErrNotImplemented = errors.New("not implemented")
_ Driver = &MinioDriver{}
)
type MinioDriver struct {
@ -24,17 +25,18 @@ type MinioDriver struct {
bucket string
}
func (driver *MinioDriver) Init(conn *Conn) {
}
func (driver *MinioDriver) ChangeDir(path string) error {
return ErrNotImplemented
}
func buildMinioPath(p string) string {
return strings.TrimPrefix(p, "/")
}
func buildMinioDir(p string) string {
v := buildMinioPath(p)
if !strings.HasSuffix(v, "/") {
return v + "/"
}
return v
}
type minioFileInfo struct {
p string
info minio.ObjectInfo
@ -80,15 +82,16 @@ func (m *minioFileInfo) Group() string {
func (driver *MinioDriver) isDir(path string) (bool, error) {
doneCh := make(chan struct{})
defer close(doneCh)
p := buildMinioPath(path)
objectCh := driver.client.ListObjects(driver.bucket, p, false, doneCh)
for object := range objectCh {
if object.Err != nil {
return false, object.Err
}
return true, nil
p := buildMinioDir(path)
info, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
if err != nil {
return false, err
}
return false, nil
if info.Err != nil {
return false, info.Err
}
return true, nil
}
func (driver *MinioDriver) Stat(path string) (FileInfo, error) {
@ -103,7 +106,7 @@ func (driver *MinioDriver) Stat(path string) (FileInfo, error) {
p := buildMinioPath(path)
objInfo, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
if err != nil {
if isDir, err := driver.isDir(path); err != nil {
if isDir, err := driver.isDir(p); err != nil {
return nil, err
} else if isDir {
return &minioFileInfo{
@ -132,6 +135,11 @@ func (driver *MinioDriver) ListDir(path string, callback func(FileInfo) error) e
return object.Err
}
// ignore itself
if object.Key == p {
continue
}
if err := callback(&minioFileInfo{
p: object.Key,
info: object,
@ -149,7 +157,7 @@ func (driver *MinioDriver) DeleteDir(path string) error {
defer close(doneCh)
p := buildMinioPath(path)
objectCh := driver.client.ListObjects(driver.bucket, p, false, doneCh)
objectCh := driver.client.ListObjects(driver.bucket, p, true, doneCh)
for object := range objectCh {
if object.Err != nil {
return object.Err
@ -181,32 +189,59 @@ func (driver *MinioDriver) Rename(fromPath string, toPath string) error {
}
func (driver *MinioDriver) MakeDir(path string) error {
return nil
dirPath := buildMinioDir(path)
_, err := driver.client.PutObject(driver.bucket, dirPath, nil, 0, minio.PutObjectOptions{ContentType: "application/octet-stream"})
return err
}
func (driver *MinioDriver) GetFile(path string, offset int64) (int64, io.ReadCloser, error) {
if offset > 0 {
return 0, nil, ErrNotImplemented
}
object, err := driver.client.GetObject(driver.bucket, buildMinioPath(path), minio.GetObjectOptions{})
var opts = minio.GetObjectOptions{}
object, err := driver.client.GetObject(driver.bucket, buildMinioPath(path), opts)
if err != nil {
return 0, nil, err
}
object.Seek(offset, io.SeekStart)
info, err := object.Stat()
if err != nil {
return 0, nil, err
}
return info.Size, object, nil
return info.Size - offset, object, nil
}
func (driver *MinioDriver) PutFile(destPath string, data io.Reader, appendData bool) (int64, error) {
p := buildMinioPath(destPath)
if !appendData {
return driver.client.PutObject(driver.bucket, buildMinioPath(destPath), data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
return driver.client.PutObject(driver.bucket, p, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
}
return 0, ErrNotImplemented
tempFile := p + ".tmp"
//tempDstFile := p + ".dst"
defer func() {
if err := driver.DeleteFile(tempFile); err != nil {
log.Println(err)
}
/*if err := driver.DeleteFile(tempDstFile); err != nil {
log.Println(err)
}*/
}()
size, err := driver.client.PutObject(driver.bucket, tempFile, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
return size, err
}
var srcs = []minio.SourceInfo{
minio.NewSourceInfo(driver.bucket, tempFile, nil),
minio.NewSourceInfo(driver.bucket, p, nil),
}
dst, err := minio.NewDestinationInfo(driver.bucket, p, nil, nil)
if err != nil {
return 0, err
}
return size, driver.client.ComposeObject(dst, srcs)
}
type MinioDriverFactory struct {

View File

@ -54,6 +54,10 @@ func TestMinioDriver(t *testing.T) {
assert.NoError(t, f.Login("admin", "admin"))
assert.Error(t, f.Login("admin", ""))
curDir, err := f.CurrentDir()
assert.NoError(t, err)
assert.EqualValues(t, "/", curDir)
err = f.RemoveDir("/")
assert.NoError(t, err)
@ -73,8 +77,8 @@ func TestMinioDriver(t *testing.T) {
assert.NoError(t, err)
assert.EqualValues(t, 1, len(entries))
assert.EqualValues(t, "server_test.go", entries[0].Name)
assert.EqualValues(t, 4, entries[0].Size)
assert.EqualValues(t, ftp.EntryTypeFile, entries[0].Type)
assert.EqualValues(t, len(buf), entries[0].Size)
size, err := f.FileSize("/server_test.go")
assert.NoError(t, err)
@ -107,6 +111,27 @@ func TestMinioDriver(t *testing.T) {
assert.EqualValues(t, 4, entries[0].Size)
assert.EqualValues(t, ftp.EntryTypeFile, entries[0].Type)
err = f.MakeDir("/src")
assert.NoError(t, err)
err = f.ChangeDir("/src")
assert.NoError(t, err)
curDir, err = f.CurrentDir()
assert.NoError(t, err)
assert.EqualValues(t, "/src", curDir)
err = f.MakeDir("/new/1/2")
assert.NoError(t, err)
assert.NoError(t, f.Stor("/test/1/2/server_test3.go", strings.NewReader(content)))
r, err = f.RetrFrom("/test/1/2/server_test3.go", 2)
assert.NoError(t, err)
buf, err = ioutil.ReadAll(r)
assert.NoError(t, err)
assert.EqualValues(t, "st", string(buf))
break
}
})

View File

@ -163,7 +163,7 @@ func NewServer(opts *ServerOpts) *Server {
// will handle all auth and persistence details.
func (server *Server) newConn(tcpConn net.Conn, driver Driver) *Conn {
c := new(Conn)
c.namePrefix = "/"
c.curDir = "/"
c.conn = tcpConn
c.controlReader = bufio.NewReader(tcpConn)
c.controlWriter = bufio.NewWriter(tcpConn)
@ -173,8 +173,6 @@ func (server *Server) newConn(tcpConn net.Conn, driver Driver) *Conn {
c.sessionID = newSessionID()
c.logger = server.logger
c.tlsConfig = server.tlsConfig
driver.Init(c)
return c
}

View File

@ -87,14 +87,13 @@ func TestFileDriver(t *testing.T) {
assert.NoError(t, err)
assert.EqualValues(t, 4, size)
/*resp, err := f.RetrFrom("/server_test.go", 0)
r, err := f.RetrFrom("/server_test.go", 2)
assert.NoError(t, err)
var buf []byte
l, err := resp.Read(buf)
buf, err := ioutil.ReadAll(r)
r.Close()
assert.NoError(t, err)
assert.EqualValues(t, 4, l)
assert.EqualValues(t, 4, len(buf))
assert.EqualValues(t, content, string(buf))*/
assert.EqualValues(t, "st", string(buf))
err = f.Rename("/server_test.go", "/test.go")
assert.NoError(t, err)
@ -105,6 +104,13 @@ func TestFileDriver(t *testing.T) {
err = f.Delete("/test.go")
assert.NoError(t, err)
err = f.ChangeDir("/src")
assert.NoError(t, err)
curDir, err = f.CurrentDir()
assert.NoError(t, err)
assert.EqualValues(t, "/src", curDir)
err = f.RemoveDir("/src")
assert.NoError(t, err)