driver/minio: upgrade go-minio to v7 #172

Merged
lunny merged 5 commits from dolmen/goftp.server.fork:minio-upgrade-to-v7 into v2 2025-09-10 04:51:58 +00:00
6 changed files with 304 additions and 231 deletions

261
cmd.go
View File

@@ -6,6 +6,7 @@ package server
import (
"bytes"
"context"
"encoding/binary"
"fmt"
"log"
@@ -129,15 +130,16 @@ func (cmd commandAppe) Execute(sess *Session, param string) {
sess.lastFilePos = -1
}()
ctx := Context{
Sess: sess,
Cmd: "APPE",
Param: param,
Data: make(map[string]interface{}),
}
sess.server.notifiers.BeforePutFile(&ctx, targetPath)
size, err := sess.server.Driver.PutFile(&ctx, targetPath, sess.dataConn, sess.lastFilePos)
sess.server.notifiers.AfterFilePut(&ctx, targetPath, size, err)
ctx := newContext(
context.Background(),
sess,
"APPE",
param,
map[string]interface{}{},
)
sess.server.notifiers.BeforePutFile(ctx, targetPath)
size, err := sess.server.Driver.PutFile(ctx, targetPath, sess.dataConn, sess.lastFilePos)
sess.server.notifiers.AfterFilePut(ctx, targetPath, size, err)
if err == nil {
msg := fmt.Sprintf("OK, received %d bytes", size)
sess.writeMessage(226, msg)
@@ -255,13 +257,14 @@ func (cmd commandCwd) RequireAuth() bool {
func (cmd commandCwd) Execute(sess *Session, param string) {
path := sess.buildPath(param)
ctx := Context{
Sess: sess,
Cmd: "CWD",
Param: param,
Data: make(map[string]interface{}),
}
info, err := sess.server.Driver.Stat(&ctx, path)
ctx := newContext(
context.Background(),
sess,
"CWD",
param,
map[string]interface{}{},
)
info, err := sess.server.Driver.Stat(ctx, path)
if err != nil {
sess.logf("%v", err)
sess.writeMessage(550, fmt.Sprint("Directory change to ", path, " failed."))
@@ -272,9 +275,9 @@ func (cmd commandCwd) Execute(sess *Session, param string) {
return
}
sess.server.notifiers.BeforeChangeCurDir(&ctx, sess.curDir, path)
sess.server.notifiers.BeforeChangeCurDir(ctx, sess.curDir, path)
err = sess.changeCurDir(path)
sess.server.notifiers.AfterCurDirChanged(&ctx, sess.curDir, path, err)
sess.server.notifiers.AfterCurDirChanged(ctx, sess.curDir, path, err)
if err == nil {
sess.writeMessage(250, "Directory changed to "+path)
} else {
@@ -301,15 +304,16 @@ func (cmd commandDele) RequireAuth() bool {
func (cmd commandDele) Execute(sess *Session, param string) {
path := sess.buildPath(param)
ctx := Context{
Sess: sess,
Cmd: "DELE",
Param: param,
Data: make(map[string]interface{}),
}
sess.server.notifiers.BeforeDeleteFile(&ctx, path)
err := sess.server.Driver.DeleteFile(&ctx, path)
sess.server.notifiers.AfterFileDeleted(&ctx, path, err)
ctx := newContext(
context.Background(),
sess,
"DELE",
param,
map[string]interface{}{},
)
sess.server.notifiers.BeforeDeleteFile(ctx, path)
err := sess.server.Driver.DeleteFile(ctx, path)
sess.server.notifiers.AfterFileDeleted(ctx, path, err)
if err == nil {
sess.writeMessage(250, "File deleted")
} else {
@@ -508,12 +512,13 @@ func convertFileInfo(sess *Session, f os.FileInfo, p string) (FileInfo, error) {
}
func list(sess *Session, cmd, p, param string) ([]FileInfo, error) {
ctx := &Context{
Sess: sess,
Cmd: cmd,
Param: param,
Data: make(map[string]interface{}),
}
ctx := newContext(
context.Background(),
sess,
cmd,
param,
map[string]interface{}{},
)
info, err := sess.server.Driver.Stat(ctx, p)
if err != nil {
return nil, err
@@ -594,12 +599,13 @@ func (cmd commandNlst) RequireAuth() bool {
}
func (cmd commandNlst) Execute(sess *Session, param string) {
ctx := &Context{
Sess: sess,
Cmd: "NLST",
Param: param,
Data: make(map[string]interface{}),
}
ctx := newContext(
context.Background(),
sess,
"NLST",
param,
map[string]interface{}{},
)
path := sess.buildPath(parseListParam(param))
info, err := sess.server.Driver.Stat(ctx, path)
if err != nil {
@@ -662,12 +668,13 @@ func (cmd commandMdtm) RequireAuth() bool {
func (cmd commandMdtm) Execute(sess *Session, param string) {
path := sess.buildPath(param)
stat, err := sess.server.Driver.Stat(&Context{
Sess: sess,
Cmd: "MDTM",
Param: param,
Data: make(map[string]interface{}),
}, path)
stat, err := sess.server.Driver.Stat(newContext(
context.Background(),
sess,
"MDTM",
param,
map[string]interface{}{},
), path)
if err == nil {
sess.writeMessage(213, stat.ModTime().Format("20060102150405"))
} else {
@@ -693,15 +700,16 @@ func (cmd commandMkd) RequireAuth() bool {
func (cmd commandMkd) Execute(sess *Session, param string) {
path := sess.buildPath(param)
ctx := Context{
Sess: sess,
Cmd: "MKD",
Param: param,
Data: make(map[string]interface{}),
}
sess.server.notifiers.BeforeCreateDir(&ctx, path)
err := sess.server.Driver.MakeDir(&ctx, path)
sess.server.notifiers.AfterDirCreated(&ctx, path, err)
ctx := newContext(
context.Background(),
sess,
"MKD",
param,
map[string]interface{}{},
)
sess.server.notifiers.BeforeCreateDir(ctx, path)
err := sess.server.Driver.MakeDir(ctx, path)
sess.server.notifiers.AfterDirCreated(ctx, path, err)
if err == nil {
sess.writeMessage(257, "Directory created")
} else {
@@ -781,14 +789,15 @@ func (cmd commandPass) Execute(sess *Session, param string) {
if driverAuth, found := sess.server.Driver.(Auth); found {
auth = driverAuth
}
ctx := Context{
Sess: sess,
Cmd: "PASS",
Param: param,
Data: make(map[string]interface{}),
}
ok, err := auth.CheckPasswd(&ctx, sess.reqUser, param)
sess.server.notifiers.AfterUserLogin(&ctx, sess.reqUser, param, ok, err)
ctx := newContext(
context.Background(),
sess,
"PASS",
param,
map[string]interface{}{},
)
ok, err := auth.CheckPasswd(ctx, sess.reqUser, param)
sess.server.notifiers.AfterUserLogin(ctx, sess.reqUser, param, ok, err)
if err != nil {
sess.writeMessage(550, "Checking password error")
return
@@ -944,28 +953,29 @@ func (cmd commandRetr) Execute(sess *Session, param string) {
defer func() {
sess.lastFilePos = -1
}()
ctx := Context{
Sess: sess,
Cmd: "RETR",
Param: param,
Data: make(map[string]interface{}),
}
sess.server.notifiers.BeforeDownloadFile(&ctx, path)
ctx := newContext(
context.Background(),
sess,
"RETR",
param,
map[string]interface{}{},
)
sess.server.notifiers.BeforeDownloadFile(ctx, path)
readPos := sess.lastFilePos
if readPos < 0 {
readPos = 0
}
size, data, err := sess.server.Driver.GetFile(&ctx, path, readPos)
size, data, err := sess.server.Driver.GetFile(ctx, path, readPos)
if err == nil {
defer data.Close()
sess.writeMessage(150, fmt.Sprintf("Data transfer starting %d bytes", size))
err = sess.sendOutofBandDataWriter(data)
sess.server.notifiers.AfterFileDownloaded(&ctx, path, size, err)
sess.server.notifiers.AfterFileDownloaded(ctx, path, size, err)
if err != nil {
sess.writeMessage(551, "Error reading file")
}
} else {
sess.server.notifiers.AfterFileDownloaded(&ctx, path, size, err)
sess.server.notifiers.AfterFileDownloaded(ctx, path, size, err)
sess.writeMessage(551, "File not available")
}
}
@@ -1014,12 +1024,13 @@ func (cmd commandRnfr) RequireAuth() bool {
func (cmd commandRnfr) Execute(sess *Session, param string) {
sess.renameFrom = ""
p := sess.buildPath(param)
if _, err := sess.server.Driver.Stat(&Context{
Sess: sess,
Cmd: "RNFR",
Param: param,
Data: make(map[string]interface{}),
}, p); err != nil {
if _, err := sess.server.Driver.Stat(newContext(
context.Background(),
sess,
"RNFR",
param,
map[string]interface{}{},
), p); err != nil {
sess.writeMessage(550, fmt.Sprint("Action not taken: ", err))
return
}
@@ -1045,12 +1056,13 @@ func (cmd commandRnto) RequireAuth() bool {
func (cmd commandRnto) Execute(sess *Session, param string) {
toPath := sess.buildPath(param)
err := sess.server.Driver.Rename(&Context{
Sess: sess,
Cmd: "RNTO",
Param: param,
Data: make(map[string]interface{}),
}, sess.renameFrom, toPath)
err := sess.server.Driver.Rename(newContext(
context.Background(),
sess,
"RNTO",
param,
map[string]interface{}{},
), sess.renameFrom, toPath)
defer func() {
sess.renameFrom = ""
}()
@@ -1104,12 +1116,13 @@ func (cmd commandXRmd) Execute(sess *Session, param string) {
func executeRmd(cmd string, sess *Session, param string) {
p := sess.buildPath(param)
ctx := Context{
Sess: sess,
Cmd: cmd,
Param: param,
Data: make(map[string]interface{}),
}
ctx := newContext(
context.Background(),
sess,
cmd,
param,
map[string]interface{}{},
)
if param == "/" || param == "" {
sess.writeMessage(550, "Directory / cannot be deleted")
return
@@ -1117,12 +1130,12 @@ func executeRmd(cmd string, sess *Session, param string) {
needChangeCurDir := strings.HasPrefix(param, sess.curDir)
sess.server.notifiers.BeforeDeleteDir(&ctx, p)
err := sess.server.Driver.DeleteDir(&ctx, p)
sess.server.notifiers.BeforeDeleteDir(ctx, p)
err := sess.server.Driver.DeleteDir(ctx, p)
if needChangeCurDir {
sess.curDir = path.Dir(param)
}
sess.server.notifiers.AfterDirDeleted(&ctx, p, err)
sess.server.notifiers.AfterDirDeleted(ctx, p, err)
if err == nil {
sess.writeMessage(250, "Directory deleted")
} else {
@@ -1364,12 +1377,13 @@ func (cmd commandSize) RequireAuth() bool {
func (cmd commandSize) Execute(sess *Session, param string) {
path := sess.buildPath(param)
stat, err := sess.server.Driver.Stat(&Context{
Sess: sess,
Cmd: "SIZE",
Param: param,
Data: make(map[string]interface{}),
}, path)
stat, err := sess.server.Driver.Stat(newContext(
context.Background(),
sess,
"SIZE",
param,
map[string]interface{}{},
), path)
if err != nil {
log.Printf("Size: error(%s)", err)
sess.writeMessage(450, fmt.Sprintf("path %s not found", param))
@@ -1407,23 +1421,24 @@ func (cmd commandStat) Execute(sess *Session, param string) {
return
}
ctx := Context{
Sess: sess,
Cmd: "STAT",
Param: param,
Data: make(map[string]interface{}),
}
ctx := newContext(
context.Background(),
sess,
"STAT",
param,
map[string]interface{}{},
)
// file or directory stat
path := sess.buildPath(param)
stat, err := sess.server.Driver.Stat(&ctx, path)
stat, err := sess.server.Driver.Stat(ctx, path)
if err != nil {
log.Printf("Size: error(%s)", err)
sess.writeMessage(450, fmt.Sprintf("path %s not found", path))
} else {
var files []FileInfo
if stat.IsDir() {
err = sess.server.Driver.ListDir(&ctx, path, func(f os.FileInfo) error {
err = sess.server.Driver.ListDir(ctx, path, func(f os.FileInfo) error {
info, err := convertFileInfo(sess, f, filepath.Join(path, f.Name()))
if err != nil {
return err
@@ -1477,15 +1492,16 @@ func (cmd commandStor) Execute(sess *Session, param string) {
sess.lastFilePos = -1
}()
ctx := Context{
Sess: sess,
Cmd: "STOR",
Param: param,
Data: make(map[string]interface{}),
}
sess.server.notifiers.BeforePutFile(&ctx, targetPath)
size, err := sess.server.Driver.PutFile(&ctx, targetPath, sess.dataConn, sess.lastFilePos)
sess.server.notifiers.AfterFilePut(&ctx, targetPath, size, err)
ctx := newContext(
context.Background(),
sess,
"STOR",
param,
map[string]interface{}{},
)
sess.server.notifiers.BeforePutFile(ctx, targetPath)
size, err := sess.server.Driver.PutFile(ctx, targetPath, sess.dataConn, sess.lastFilePos)
sess.server.notifiers.AfterFilePut(ctx, targetPath, size, err)
if err == nil {
msg := fmt.Sprintf("OK, received %d bytes", size)
sess.writeMessage(226, msg)
@@ -1595,11 +1611,12 @@ func (cmd commandUser) RequireAuth() bool {
func (cmd commandUser) Execute(sess *Session, param string) {
sess.reqUser = param
sess.server.notifiers.BeforeLoginUser(&Context{
Sess: sess,
Cmd: "USER",
Param: param,
Data: make(map[string]interface{}),
}, sess.reqUser)
sess.server.notifiers.BeforeLoginUser(newContext(
context.Background(),
sess,
"USER",
param,
map[string]interface{}{},
), sess.reqUser)
sess.writeMessage(331, "User name ok, password required")
}

View File

@@ -4,10 +4,48 @@
package server
import (
"context"
"time"
)
// Context represents a context the driver may want to know
type Context struct {
ctx context.Context
Sess *Session
Cmd string // request command on this request
Param string // request param on this request
Data map[string]interface{} // share data between middlewares
}
var _ context.Context = (*Context)(nil)
func newContext(parent context.Context, sess *Session, cmd, param string, data map[string]interface{}) *Context {
return &Context{
ctx: parent,
Sess: sess,
Cmd: cmd,
Param: param,
Data: data,
}
}
// Deadline returns the deadline associated with this context, if any.
func (c *Context) Deadline() (deadline time.Time, ok bool) {
return c.ctx.Deadline()
}
// Done returns a channel that is closed when the work done on behalf of this context is complete.
func (c *Context) Done() <-chan struct{} {
return c.ctx.Done()
}
// Err returns the error, if any, that caused the context to be canceled.
func (c *Context) Err() error {
return c.ctx.Err()
}
// Value returns the value associated with this context.
func (c *Context) Value(key any) any {
return c.ctx.Value(key)
}

View File

@@ -5,6 +5,7 @@
package minio
import (
"context"
"errors"
"fmt"
"io"
@@ -13,13 +14,12 @@ import (
"strings"
"time"
minio "github.com/minio/minio-go/v6"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"goftp.io/server/v2"
)
var (
_ server.Driver = &Driver{}
)
var _ server.Driver = &Driver{}
// Driver implements Driver to store files in minio
type Driver struct {
@@ -30,14 +30,20 @@ type Driver struct {
// NewDriver implements DriverFactory
func NewDriver(endpoint, accessKeyID, secretAccessKey, location, bucket string, useSSL bool) (server.Driver, error) {
// Initialize minio client object.
minioClient, err := minio.New(endpoint, accessKeyID, secretAccessKey, useSSL)
minioClient, err := minio.New(endpoint, &minio.Options{
Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
Secure: useSSL,
})
if err != nil {
return nil, err
}
if err = minioClient.MakeBucket(bucket, location); err != nil {
ctx, cancel := context.WithCancel(context.TODO())
defer cancel()
if err = minioClient.MakeBucket(ctx, bucket, minio.MakeBucketOptions{Region: location}); err != nil {
// Check to see if we already own this bucket (which happens if you run this twice)
exists, errBucketExists := minioClient.BucketExists(bucket)
exists, errBucketExists := minioClient.BucketExists(ctx, bucket)
if !exists || errBucketExists != nil {
return nil, err
}
@@ -91,21 +97,23 @@ func (m *minioFileInfo) Sys() interface{} {
return nil
}
func (driver *Driver) isDir(path string) (bool, error) {
func (driver *Driver) isDir(ctx context.Context, path string) (bool, error) {
p := buildMinioDir(path)
info, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
info, err := driver.client.StatObject(ctx, driver.bucket, p, minio.StatObjectOptions{})
if err != nil {
doneCh := make(chan struct{})
objectCh := driver.client.ListObjects(driver.bucket, p, false, doneCh)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
objectCh := driver.client.ListObjects(ctx, driver.bucket, minio.ListObjectsOptions{
Prefix: p,
Recursive: false,
})
for object := range objectCh {
if strings.HasPrefix(object.Key, p) {
close(doneCh)
return true, nil
}
}
close(doneCh)
return false, nil
}
@@ -122,9 +130,9 @@ func (driver *Driver) Stat(ctx *server.Context, path string) (os.FileInfo, error
}
p := buildMinioPath(path)
objInfo, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
objInfo, err := driver.client.StatObject(ctx, driver.bucket, p, minio.StatObjectOptions{})
if err != nil {
if isDir, err := driver.isDir(p); err != nil {
if isDir, err := driver.isDir(ctx, p); err != nil {
return nil, err
} else if isDir {
return &minioFileInfo{
lunny marked this conversation as resolved Outdated
Outdated
Review

typo?

typo?

I don't see a typo here.

ctx is the usual variable name for context.Context. But here ctx is already used for *server.Context. So I choose another name, ctxx.

I'm open to suggestion for a better name.

I don't see a typo here. `ctx` is the usual variable name for `context.Context`. But here `ctx` is already used for `*server.Context`. So I choose another name, `ctxx`. I'm open to suggestion for a better name.

@lunny What do you think?

@lunny What do you think?
@@ -144,14 +152,14 @@ func (driver *Driver) Stat(ctx *server.Context, path string) (os.FileInfo, error
// ListDir implements Driver
func (driver *Driver) ListDir(ctx *server.Context, path string, callback func(os.FileInfo) error) error {
doneCh := make(chan struct{})
defer close(doneCh)
p := buildMinioDir(path)
if p == "/" {
p = ""
}
objectCh := driver.client.ListObjects(driver.bucket, p, false, doneCh)
objectCh := driver.client.ListObjects(ctx, driver.bucket, minio.ListObjectsOptions{
Prefix: p,
Recursive: false,
})
for object := range objectCh {
if object.Err != nil {
return object.Err
@@ -179,17 +187,17 @@ func (driver *Driver) ListDir(ctx *server.Context, path string, callback func(os
// DeleteDir implements Driver
func (driver *Driver) DeleteDir(ctx *server.Context, path string) error {
doneCh := make(chan struct{})
defer close(doneCh)
p := buildMinioPath(path)
objectCh := driver.client.ListObjects(driver.bucket, p, true, doneCh)
objectCh := driver.client.ListObjects(ctx, driver.bucket, minio.ListObjectsOptions{
Prefix: p,
Recursive: true,
})
for object := range objectCh {
if object.Err != nil {
return object.Err
}
if err := driver.client.RemoveObject(driver.bucket, object.Key); err != nil {
if err := driver.client.RemoveObject(ctx, driver.bucket, object.Key, minio.RemoveObjectOptions{}); err != nil {
return err
}
}
@@ -198,35 +206,32 @@ func (driver *Driver) DeleteDir(ctx *server.Context, path string) error {
// DeleteFile implements Driver
func (driver *Driver) DeleteFile(ctx *server.Context, path string) error {
return driver.client.RemoveObject(driver.bucket, buildMinioPath(path))
return driver.client.RemoveObject(ctx, driver.bucket, buildMinioPath(path), minio.RemoveObjectOptions{})
}
// Rename implements Driver
func (driver *Driver) Rename(ctx *server.Context, fromPath string, toPath string) error {
src := minio.NewSourceInfo(driver.bucket, buildMinioPath(fromPath), nil)
dst, err := minio.NewDestinationInfo(driver.bucket, buildMinioPath(toPath), nil, nil)
if err != nil {
src := minio.CopySrcOptions{Bucket: driver.bucket, Object: buildMinioPath(fromPath)}
dst := minio.CopyDestOptions{Bucket: driver.bucket, Object: buildMinioPath(toPath)}
if _, err := driver.client.CopyObject(ctx, dst, src); err != nil {
return err
}
if err := driver.client.CopyObject(dst, src); err != nil {
return err
}
return driver.client.RemoveObject(driver.bucket, buildMinioPath(fromPath))
return driver.client.RemoveObject(ctx, driver.bucket, buildMinioPath(fromPath), minio.RemoveObjectOptions{})
}
// MakeDir implements Driver
func (driver *Driver) MakeDir(ctx *server.Context, path string) error {
dirPath := buildMinioDir(path)
_, err := driver.client.PutObject(driver.bucket, dirPath, nil, 0, minio.PutObjectOptions{})
_, err := driver.client.PutObject(ctx, driver.bucket, dirPath, nil, 0, minio.PutObjectOptions{})
return err
}
// GetFile implements Driver
func (driver *Driver) GetFile(ctx *server.Context, path string, offset int64) (int64, io.ReadCloser, error) {
var opts = minio.GetObjectOptions{}
object, err := driver.client.GetObject(driver.bucket, buildMinioPath(path), opts)
opts := minio.GetObjectOptions{}
object, err := driver.client.GetObject(ctx, driver.bucket, buildMinioPath(path), opts)
if err != nil {
return 0, nil, err
}
@@ -252,7 +257,11 @@ func (driver *Driver) GetFile(ctx *server.Context, path string, offset int64) (i
func (driver *Driver) PutFile(ctx *server.Context, destPath string, data io.Reader, offset int64) (int64, error) {
p := buildMinioPath(destPath)
if offset == -1 {
return driver.client.PutObject(driver.bucket, p, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
uploadInfo, err := driver.client.PutObject(ctx, driver.bucket, p, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
return 0, err
}
return uploadInfo.Size, nil
}
tempFile := p + ".tmp"
@@ -262,7 +271,7 @@ func (driver *Driver) PutFile(ctx *server.Context, destPath string, data io.Read
}
}()
info, err := driver.client.StatObject(driver.bucket, p, minio.StatObjectOptions{})
info, err := driver.client.StatObject(ctx, driver.bucket, p, minio.StatObjectOptions{})
if err != nil {
return 0, err
}
@@ -270,19 +279,20 @@ func (driver *Driver) PutFile(ctx *server.Context, destPath string, data io.Read
return 0, fmt.Errorf("It's unsupported that offset %d is not equal to %d", offset, info.Size)
}
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)
uploadInfo, err := driver.client.PutObject(ctx, driver.bucket, tempFile, data, -1, minio.PutObjectOptions{ContentType: "application/octet-stream"})
if err != nil {
return 0, err
}
return size, driver.client.ComposeObject(dst, srcs)
srcs := []minio.CopySrcOptions{
{Bucket: driver.bucket, Object: tempFile},
{Bucket: driver.bucket, Object: p},
}
dst := minio.CopyDestOptions{Bucket: driver.bucket, Object: p}
uploadInfo, err = driver.client.ComposeObject(ctx, dst, srcs...)
if err != nil {
return 0, err
}
return uploadInfo.Size, nil
}
lunny marked this conversation as resolved Outdated
Outdated
Review

uploadInfo maybe nil

uploadInfo maybe nil

33
go.mod
View File

@@ -1,21 +1,32 @@
module goftp.io/server/v2
go 1.20
go 1.23.0
require (
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf
github.com/minio/minio-go/v6 v6.0.46
github.com/stretchr/testify v1.3.0
github.com/jlaffaye/ftp v0.2.0
github.com/minio/minio-go/v7 v7.0.95
github.com/stretchr/testify v1.10.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/minio/sha256-simd v0.1.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/minio/crc64nvme v1.0.2 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e // indirect
golang.org/x/text v0.3.2 // indirect
gopkg.in/ini.v1 v1.42.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/tinylib/msgp v1.3.0 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/net v0.41.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.26.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

91
go.sum
View File

@@ -1,50 +1,49 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf h1:2IYBd5TD/maMqTU2YUzp2tJL4cNaOYQ9EBullN9t9pk=
github.com/jlaffaye/ftp v0.0.0-20190624084859-c1312a7102bf/go.mod h1:lli8NYPQOFy3O++YmYbqVgOcQ1JPCwdOy+5zSjKJ9qY=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/minio/minio-go/v6 v6.0.46 h1:waExJtO53xrnsNX//7cSc1h3478wqTryDx4RVD7o26I=
github.com/minio/minio-go/v6 v6.0.46/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg=
github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKUJU=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
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/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg=
github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e h1:D5TXcfTk7xF7hvieo4QErS3qqCB4teTffacDWr7CI+0=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -16,9 +16,7 @@ import (
"goftp.io/server/v2/ratelimit"
)
var (
version = "2.0beta"
)
var version = "2.0beta"
// Options contains parameters for server.NewServer()
type Options struct {