sftpd: add SSH_FXP_FSETSTAT support

This change will fix file editing from sshfs, we need this patch

https://github.com/pkg/sftp/pull/373

for pkg/sftp to support this feature
This commit is contained in:
Nicola Murino
2020-08-20 13:54:36 +02:00
parent 933427310d
commit f41ce6619f
16 changed files with 222 additions and 45 deletions

View File

@@ -37,6 +37,7 @@ const (
chownLogSender = "Chown" chownLogSender = "Chown"
chmodLogSender = "Chmod" chmodLogSender = "Chmod"
chtimesLogSender = "Chtimes" chtimesLogSender = "Chtimes"
truncateLogSender = "Truncate"
operationDownload = "download" operationDownload = "download"
operationUpload = "upload" operationUpload = "upload"
operationDelete = "delete" operationDelete = "delete"
@@ -52,6 +53,7 @@ const (
StatAttrUIDGID = 1 StatAttrUIDGID = 1
StatAttrPerms = 2 StatAttrPerms = 2
StatAttrTimes = 4 StatAttrTimes = 4
StatAttrSize = 8
) )
// Transfer types // Transfer types
@@ -85,6 +87,8 @@ var (
ErrQuotaExceeded = errors.New("denying write due to space limit") ErrQuotaExceeded = errors.New("denying write due to space limit")
ErrSkipPermissionsCheck = errors.New("permission check skipped") ErrSkipPermissionsCheck = errors.New("permission check skipped")
ErrConnectionDenied = errors.New("You are not allowed to connect") ErrConnectionDenied = errors.New("You are not allowed to connect")
errNoTransfer = errors.New("requested transfer not found")
errTransferMismatch = errors.New("transfer mismatch")
) )
var ( var (
@@ -141,6 +145,7 @@ type ActiveTransfer interface {
GetVirtualPath() string GetVirtualPath() string
GetStartTime() time.Time GetStartTime() time.Time
SignalClose() SignalClose()
Truncate(fsPath string, size int64) error
} }
// ActiveConnection defines the interface for the current active connections // ActiveConnection defines the interface for the current active connections
@@ -168,6 +173,7 @@ type StatAttributes struct {
UID int UID int
GID int GID int
Flags int Flags int
Size int64
} }
// ConnectionTransfer defines the trasfer details to expose // ConnectionTransfer defines the trasfer details to expose

View File

@@ -172,6 +172,20 @@ func (c *BaseConnection) SignalTransfersAbort() error {
return nil return nil
} }
func (c *BaseConnection) truncateOpenHandle(fsPath string, size int64) error {
c.RLock()
defer c.RUnlock()
for _, t := range c.activeTransfers {
err := t.Truncate(fsPath, size)
if err != errTransferMismatch {
return err
}
}
return errNoTransfer
}
// ListDir reads the directory named by fsPath and returns a list of directory entries // ListDir reads the directory named by fsPath and returns a list of directory entries
func (c *BaseConnection) ListDir(fsPath, virtualPath string) ([]os.FileInfo, error) { func (c *BaseConnection) ListDir(fsPath, virtualPath string) ([]os.FileInfo, error) {
if !c.User.HasPerm(dataprovider.PermListItems, virtualPath) { if !c.User.HasPerm(dataprovider.PermListItems, virtualPath) {
@@ -200,7 +214,7 @@ func (c *BaseConnection) CreateDir(fsPath, virtualPath string) error {
} }
vfs.SetPathPermissions(c.Fs, fsPath, c.User.GetUID(), c.User.GetGID()) vfs.SetPathPermissions(c.Fs, fsPath, c.User.GetUID(), c.User.GetGID())
logger.CommandLog(mkdirLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "") logger.CommandLog(mkdirLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "", -1)
return nil return nil
} }
@@ -233,7 +247,7 @@ func (c *BaseConnection) RemoveFile(fsPath, virtualPath string, info os.FileInfo
} }
} }
logger.CommandLog(removeLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "") logger.CommandLog(removeLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "", -1)
if info.Mode()&os.ModeSymlink != os.ModeSymlink { if info.Mode()&os.ModeSymlink != os.ModeSymlink {
vfolder, err := c.User.GetVirtualFolderForPath(path.Dir(virtualPath)) vfolder, err := c.User.GetVirtualFolderForPath(path.Dir(virtualPath))
if err == nil { if err == nil {
@@ -302,7 +316,7 @@ func (c *BaseConnection) RemoveDir(fsPath, virtualPath string) error {
return c.GetFsError(err) return c.GetFsError(err)
} }
logger.CommandLog(rmdirLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "") logger.CommandLog(rmdirLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "", -1)
return nil return nil
} }
@@ -363,7 +377,7 @@ func (c *BaseConnection) Rename(fsSourcePath, fsTargetPath, virtualSourcePath, v
c.updateQuotaAfterRename(virtualSourcePath, virtualTargetPath, fsTargetPath, initialSize) //nolint:errcheck c.updateQuotaAfterRename(virtualSourcePath, virtualTargetPath, fsTargetPath, initialSize) //nolint:errcheck
} }
logger.CommandLog(renameLogSender, fsSourcePath, fsTargetPath, c.User.Username, "", c.ID, c.protocol, -1, -1, logger.CommandLog(renameLogSender, fsSourcePath, fsTargetPath, c.User.Username, "", c.ID, c.protocol, -1, -1,
"", "", "") "", "", "", -1)
action := newActionNotification(&c.User, operationRename, fsSourcePath, fsTargetPath, "", c.protocol, 0, nil) action := newActionNotification(&c.User, operationRename, fsSourcePath, fsTargetPath, "", c.protocol, 0, nil)
// the returned error is used in test cases only, we already log the error inside action.execute // the returned error is used in test cases only, we already log the error inside action.execute
go action.execute() //nolint:errcheck go action.execute() //nolint:errcheck
@@ -400,21 +414,27 @@ func (c *BaseConnection) CreateSymlink(fsSourcePath, fsTargetPath, virtualSource
c.Log(logger.LevelWarn, "failed to create symlink %#v -> %#v: %+v", fsSourcePath, fsTargetPath, err) c.Log(logger.LevelWarn, "failed to create symlink %#v -> %#v: %+v", fsSourcePath, fsTargetPath, err)
return c.GetFsError(err) return c.GetFsError(err)
} }
logger.CommandLog(symlinkLogSender, fsSourcePath, fsTargetPath, c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "") logger.CommandLog(symlinkLogSender, fsSourcePath, fsTargetPath, c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "", -1)
return nil return nil
} }
func (c *BaseConnection) getPathForSetStatPerms(fsPath, virtualPath string) string {
pathForPerms := virtualPath
if fi, err := c.Fs.Lstat(fsPath); err == nil {
if fi.IsDir() {
pathForPerms = path.Dir(virtualPath)
}
}
return pathForPerms
}
// SetStat set StatAttributes for the specified fsPath // SetStat set StatAttributes for the specified fsPath
func (c *BaseConnection) SetStat(fsPath, virtualPath string, attributes *StatAttributes) error { func (c *BaseConnection) SetStat(fsPath, virtualPath string, attributes *StatAttributes) error {
if Config.SetstatMode == 1 { if Config.SetstatMode == 1 {
return nil return nil
} }
pathForPerms := virtualPath pathForPerms := c.getPathForSetStatPerms(fsPath, virtualPath)
if fi, err := c.Fs.Lstat(fsPath); err == nil {
if fi.IsDir() {
pathForPerms = path.Dir(virtualPath)
}
}
if attributes.Flags&StatAttrPerms != 0 { if attributes.Flags&StatAttrPerms != 0 {
if !c.User.HasPerm(dataprovider.PermChmod, pathForPerms) { if !c.User.HasPerm(dataprovider.PermChmod, pathForPerms) {
return c.GetPermissionDeniedError() return c.GetPermissionDeniedError()
@@ -424,7 +444,7 @@ func (c *BaseConnection) SetStat(fsPath, virtualPath string, attributes *StatAtt
return c.GetFsError(err) return c.GetFsError(err)
} }
logger.CommandLog(chmodLogSender, fsPath, "", c.User.Username, attributes.Mode.String(), c.ID, c.protocol, logger.CommandLog(chmodLogSender, fsPath, "", c.User.Username, attributes.Mode.String(), c.ID, c.protocol,
-1, -1, "", "", "") -1, -1, "", "", "", -1)
} }
if attributes.Flags&StatAttrUIDGID != 0 { if attributes.Flags&StatAttrUIDGID != 0 {
@@ -437,12 +457,12 @@ func (c *BaseConnection) SetStat(fsPath, virtualPath string, attributes *StatAtt
return c.GetFsError(err) return c.GetFsError(err)
} }
logger.CommandLog(chownLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, attributes.UID, attributes.GID, logger.CommandLog(chownLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, attributes.UID, attributes.GID,
"", "", "") "", "", "", -1)
} }
if attributes.Flags&StatAttrTimes != 0 { if attributes.Flags&StatAttrTimes != 0 {
if !c.User.HasPerm(dataprovider.PermChtimes, pathForPerms) { if !c.User.HasPerm(dataprovider.PermChtimes, pathForPerms) {
return sftp.ErrSSHFxPermissionDenied return c.GetPermissionDeniedError()
} }
if err := c.Fs.Chtimes(fsPath, attributes.Atime, attributes.Mtime); err != nil { if err := c.Fs.Chtimes(fsPath, attributes.Atime, attributes.Mtime); err != nil {
@@ -453,12 +473,37 @@ func (c *BaseConnection) SetStat(fsPath, virtualPath string, attributes *StatAtt
accessTimeString := attributes.Atime.Format(chtimesFormat) accessTimeString := attributes.Atime.Format(chtimesFormat)
modificationTimeString := attributes.Mtime.Format(chtimesFormat) modificationTimeString := attributes.Mtime.Format(chtimesFormat)
logger.CommandLog(chtimesLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, logger.CommandLog(chtimesLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1,
accessTimeString, modificationTimeString, "") accessTimeString, modificationTimeString, "", -1)
}
if attributes.Flags&StatAttrSize != 0 {
if !c.User.HasPerm(dataprovider.PermOverwrite, pathForPerms) {
return c.GetPermissionDeniedError()
}
if err := c.truncateFile(fsPath, attributes.Size); err != nil {
c.Log(logger.LevelWarn, "failed to truncate path %#v, size: %v, err: %+v", fsPath, attributes.Size, err)
return c.GetFsError(err)
}
logger.CommandLog(truncateLogSender, fsPath, "", c.User.Username, "", c.ID, c.protocol, -1, -1, "", "", "", attributes.Size)
} }
return nil return nil
} }
func (c *BaseConnection) truncateFile(fsPath string, size int64) error {
// check first if we have an open transfer for the given path and try to truncate the file already opened
// if we found no transfer we truncate by path.
// pkg/sftp should expose an optional interface and call truncate directly on the opened handle ...
// If we try to truncate by path an already opened file we get an error on Windows
err := c.truncateOpenHandle(fsPath, size)
if err == errNoTransfer {
c.Log(logger.LevelDebug, "file path %#v not found in active transfers, execute trucate by path", fsPath)
err = c.Fs.Truncate(fsPath, size)
}
return err
}
func (c *BaseConnection) checkRecursiveRenameDirPermissions(sourcePath, targetPath string) error { func (c *BaseConnection) checkRecursiveRenameDirPermissions(sourcePath, targetPath string) error {
dstPerms := []string{ dstPerms := []string{
dataprovider.PermCreateDirs, dataprovider.PermCreateDirs,

View File

@@ -529,6 +529,30 @@ func TestSetStat(t *testing.T) {
Flags: StatAttrTimes, Flags: StatAttrTimes,
}) })
assert.Error(t, err) assert.Error(t, err)
// truncate
err = c.SetStat(filepath.Join(user.GetHomeDir(), "missing"), "/missing", &StatAttributes{
Size: 1,
Flags: StatAttrSize,
})
assert.Error(t, err)
err = c.SetStat(filepath.Join(dir3, "afile.txt"), "/dir3/afile.txt", &StatAttributes{
Size: 1,
Flags: StatAttrSize,
})
assert.Error(t, err)
filePath := filepath.Join(user.GetHomeDir(), "afile.txt")
err = ioutil.WriteFile(filePath, []byte("hello"), os.ModePerm)
assert.NoError(t, err)
err = c.SetStat(filePath, "/afile.txt", &StatAttributes{
Flags: StatAttrSize,
Size: 1,
})
assert.NoError(t, err)
fi, err := os.Stat(filePath)
if assert.NoError(t, err) {
assert.Equal(t, int64(1), fi.Size())
}
err = os.RemoveAll(user.GetHomeDir()) err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err) assert.NoError(t, err)

View File

@@ -108,6 +108,22 @@ func (t *BaseTransfer) SetCancelFn(cancelFn func()) {
t.cancelFn = cancelFn t.cancelFn = cancelFn
} }
// Truncate changes the size of the opened file.
// Supported for local fs only
func (t *BaseTransfer) Truncate(fsPath string, size int64) error {
if fsPath == t.GetFsPath() {
if t.File != nil {
return t.File.Truncate(size)
}
if size == 0 {
// for cloud providers the file is always truncated to zero, we don't support append/resume for uploads
return nil
}
return ErrOpUnsupported
}
return errTransferMismatch
}
// TransferError is called if there is an unexpected error. // TransferError is called if there is an unexpected error.
// For example network or client issues // For example network or client issues
func (t *BaseTransfer) TransferError(err error) { func (t *BaseTransfer) TransferError(err error) {

View File

@@ -81,6 +81,53 @@ func TestTransferThrottling(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestTruncate(t *testing.T) {
testFile := filepath.Join(os.TempDir(), "transfer_test_file")
fs := vfs.NewOsFs("123", os.TempDir(), nil)
u := dataprovider.User{
Username: "user",
HomeDir: os.TempDir(),
}
u.Permissions = make(map[string][]string)
u.Permissions["/"] = []string{dataprovider.PermAny}
file, err := os.Create(testFile)
if !assert.NoError(t, err) {
assert.FailNow(t, "unable to open test file")
}
_, err = file.Write([]byte("hello"))
assert.NoError(t, err)
conn := NewBaseConnection(fs.ConnectionID(), ProtocolSFTP, u, fs)
transfer := NewBaseTransfer(file, conn, nil, testFile, "/transfer_test_file", TransferUpload, 0, 0, true)
err = conn.SetStat(testFile, "/transfer_test_file", &StatAttributes{
Size: 2,
Flags: StatAttrSize,
})
assert.NoError(t, err)
err = transfer.Close()
assert.NoError(t, err)
fi, err := os.Stat(testFile)
if assert.NoError(t, err) {
assert.Equal(t, int64(2), fi.Size())
}
transfer = NewBaseTransfer(nil, conn, nil, testFile, "", TransferUpload, 0, 0, true)
err = transfer.Truncate("mismatch", 0)
assert.EqualError(t, err, errTransferMismatch.Error())
err = transfer.Truncate(testFile, 0)
assert.NoError(t, err)
err = transfer.Truncate(testFile, 1)
assert.EqualError(t, err, ErrOpUnsupported.Error())
err = transfer.Close()
assert.NoError(t, err)
err = os.Remove(testFile)
assert.NoError(t, err)
assert.Len(t, conn.GetTransfers(), 0)
}
func TestTransferErrors(t *testing.T) { func TestTransferErrors(t *testing.T) {
isCancelled := false isCancelled := false
cancelFn := func() { cancelFn := func() {

View File

@@ -20,7 +20,7 @@ The logs can be divided into the following categories:
- `connection_id` string. Unique connection identifier - `connection_id` string. Unique connection identifier
- `protocol` string. `SFTP` or `SCP` - `protocol` string. `SFTP` or `SCP`
- **"command logs"**, SFTP/SCP command logs: - **"command logs"**, SFTP/SCP command logs:
- `sender` string. `Rename`, `Rmdir`, `Mkdir`, `Symlink`, `Remove`, `Chmod`, `Chown`, `Chtimes`, `SSHCommand` - `sender` string. `Rename`, `Rmdir`, `Mkdir`, `Symlink`, `Remove`, `Chmod`, `Chown`, `Chtimes`, `Truncate`, `SSHCommand`
- `level` string - `level` string
- `username`, string - `username`, string
- `file_path` string - `file_path` string
@@ -30,6 +30,7 @@ The logs can be divided into the following categories:
- `gid` integer. Valid for sender `Chown` otherwise -1 - `gid` integer. Valid for sender `Chown` otherwise -1
- `access_time` datetime as YYYY-MM-DDTHH:MM:SS. Valid for sender `Chtimes` otherwise empty - `access_time` datetime as YYYY-MM-DDTHH:MM:SS. Valid for sender `Chtimes` otherwise empty
- `modification_time` datetime as YYYY-MM-DDTHH:MM:SS. Valid for sender `Chtimes` otherwise empty - `modification_time` datetime as YYYY-MM-DDTHH:MM:SS. Valid for sender `Chtimes` otherwise empty
- `size` int64. Valid for sender `Truncate` otherwise -1
- `ssh_command`, string. Valid for sender `SSHCommand` otherwise empty - `ssh_command`, string. Valid for sender `SSHCommand` otherwise empty
- `connection_id` string. Unique connection identifier - `connection_id` string. Unique connection identifier
- `protocol` string. `SFTP`, `SCP` or `SSH` - `protocol` string. `SFTP`, `SCP` or `SSH`

16
go.mod
View File

@@ -3,10 +3,10 @@ module github.com/drakkan/sftpgo
go 1.13 go 1.13
require ( require (
cloud.google.com/go v0.63.0 // indirect cloud.google.com/go v0.64.0 // indirect
cloud.google.com/go/storage v1.10.0 cloud.google.com/go/storage v1.10.0
github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b github.com/alexedwards/argon2id v0.0.0-20200802152012-2464efd3196b
github.com/aws/aws-sdk-go v1.34.5 github.com/aws/aws-sdk-go v1.34.8
github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d github.com/eikenb/pipeat v0.0.0-20200430215831-470df5986b6d
github.com/fclairamb/ftpserverlib v0.8.1-0.20200729230026-7f0ab9d81bb6 github.com/fclairamb/ftpserverlib v0.8.1-0.20200729230026-7f0ab9d81bb6
github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect
@@ -17,7 +17,7 @@ require (
github.com/grandcat/zeroconf v1.0.0 github.com/grandcat/zeroconf v1.0.0
github.com/jlaffaye/ftp v0.0.0-20200720194710-13949d38913e github.com/jlaffaye/ftp v0.0.0-20200720194710-13949d38913e
github.com/lib/pq v1.8.0 github.com/lib/pq v1.8.0
github.com/mattn/go-sqlite3 v1.14.0 github.com/mattn/go-sqlite3 v1.14.1
github.com/miekg/dns v1.1.31 // indirect github.com/miekg/dns v1.1.31 // indirect
github.com/mitchellh/mapstructure v1.3.3 // indirect github.com/mitchellh/mapstructure v1.3.3 // indirect
github.com/nathanaelle/password/v2 v2.0.1 github.com/nathanaelle/password/v2 v2.0.1
@@ -26,7 +26,7 @@ require (
github.com/pires/go-proxyproto v0.1.3 github.com/pires/go-proxyproto v0.1.3
github.com/pkg/sftp v1.11.1-0.20200819110714-3ee8d0ba91c0 github.com/pkg/sftp v1.11.1-0.20200819110714-3ee8d0ba91c0
github.com/prometheus/client_golang v1.7.1 github.com/prometheus/client_golang v1.7.1
github.com/prometheus/common v0.12.0 // indirect github.com/prometheus/common v0.13.0 // indirect
github.com/rs/cors v1.7.1-0.20200626170627-8b4a00bd362b github.com/rs/cors v1.7.1-0.20200626170627-8b4a00bd362b
github.com/rs/xid v1.2.1 github.com/rs/xid v1.2.1
github.com/rs/zerolog v1.19.0 github.com/rs/zerolog v1.19.0
@@ -41,17 +41,17 @@ require (
go.etcd.io/bbolt v1.3.5 go.etcd.io/bbolt v1.3.5
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc
golang.org/x/sys v0.0.0-20200819091447-39769834ee22 golang.org/x/sys v0.0.0-20200819171115-d785dc25833f
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d // indirect golang.org/x/tools v0.0.0-20200820010801-b793a1359eac // indirect
google.golang.org/api v0.30.0 google.golang.org/api v0.30.0
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 // indirect gopkg.in/ini.v1 v1.60.0 // indirect
gopkg.in/ini.v1 v1.58.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 gopkg.in/natefinch/lumberjack.v2 v2.0.0
) )
replace ( replace (
github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20200814103339-511fcfd63dfe github.com/fclairamb/ftpserverlib => github.com/drakkan/ftpserverlib v0.0.0-20200814103339-511fcfd63dfe
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20200730125632-b21eac28818c github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20200730125632-b21eac28818c
github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20200820090459-de8eb908f763
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20200731130417-7674a892f9b1 golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20200731130417-7674a892f9b1
golang.org/x/net => github.com/drakkan/net v0.0.0-20200807161257-daa5cda5ae27 golang.org/x/net => github.com/drakkan/net v0.0.0-20200807161257-daa5cda5ae27
) )

34
go.sum
View File

@@ -12,8 +12,8 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.63.0 h1:A+DfAZQ/eWca7gvu42CS6FNSDX4R8cghF+XfWLn4R6g= cloud.google.com/go v0.64.0 h1:xVP3LPvMjGT4J0a55y02Gw5y/dkY/rxGz58sfK1jqIo=
cloud.google.com/go v0.63.0/go.mod h1:GmezbQc7T2snqkEXWfZ0sy0VfkB/ivI2DdtJL2DEmlg= cloud.google.com/go v0.64.0/go.mod h1:xfORb36jGvE+6EexW71nMEtL025s3x6xvuYUKM4JLv4=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@@ -60,8 +60,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.34.5 h1:FwubVVX9u+kW9qDCjVzyWOdsL+W5wPq683wMk2R2GXk= github.com/aws/aws-sdk-go v1.34.8 h1:GDfVeXG8XQDbpOeAj7415F8qCQZwvY/k/fj+HBqUnBA=
github.com/aws/aws-sdk-go v1.34.5/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0= github.com/aws/aws-sdk-go v1.34.8/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -111,6 +111,8 @@ github.com/drakkan/ftpserverlib v0.0.0-20200814103339-511fcfd63dfe h1:yiliFcCxu5
github.com/drakkan/ftpserverlib v0.0.0-20200814103339-511fcfd63dfe/go.mod h1:ShLpSOXbtoMDYxTb5eRs9wDBfkQ7VINYghclB4P2z4E= github.com/drakkan/ftpserverlib v0.0.0-20200814103339-511fcfd63dfe/go.mod h1:ShLpSOXbtoMDYxTb5eRs9wDBfkQ7VINYghclB4P2z4E=
github.com/drakkan/net v0.0.0-20200807161257-daa5cda5ae27 h1:hh14GxmE3PMKL+4nvMmX7O8CUtbD/52IKDjbMTYX7IY= github.com/drakkan/net v0.0.0-20200807161257-daa5cda5ae27 h1:hh14GxmE3PMKL+4nvMmX7O8CUtbD/52IKDjbMTYX7IY=
github.com/drakkan/net v0.0.0-20200807161257-daa5cda5ae27/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= github.com/drakkan/net v0.0.0-20200807161257-daa5cda5ae27/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
github.com/drakkan/sftp v0.0.0-20200820090459-de8eb908f763 h1:2s1IvldI3h8S4FAF7K9uAB05/L0k8TlYF60GIEc2LUY=
github.com/drakkan/sftp v0.0.0-20200820090459-de8eb908f763/go.mod h1:i24A96cQ6ZvWut9G/Uv3LvC4u3VebGsBR5JFvPyChLc=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
@@ -291,8 +293,8 @@ github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.1 h1:AHx9Ra40wIzl+GelgX2X6AWxmT5tfxhI1PL0523HcSw=
github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= github.com/mattn/go-sqlite3 v1.14.1/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
@@ -364,9 +366,6 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.11.1-0.20200819110714-3ee8d0ba91c0 h1:3LRfXAQrcWKQ0LQZ9wjp9wgzYRsxFKVjP5zZIEv9NFY=
github.com/pkg/sftp v1.11.1-0.20200819110714-3ee8d0ba91c0/go.mod h1:i24A96cQ6ZvWut9G/Uv3LvC4u3VebGsBR5JFvPyChLc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@@ -390,8 +389,8 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA= github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt26CguLLsqA=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.12.0 h1:mj4ewtVukAfkS37JU7IXPJPr7zwLEjwgWO6nZo8ROvk= github.com/prometheus/common v0.13.0 h1:vJlpe9wPgDRM1Z+7Wj3zUUjY1nr6/1jNKyl7llliccg=
github.com/prometheus/common v0.12.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s= github.com/prometheus/common v0.13.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -578,8 +577,8 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200819091447-39769834ee22 h1:YUxhQGxYV280Da2a0XZiHblyJZN6NuXS1f4dahsm0SM= golang.org/x/sys v0.0.0-20200819171115-d785dc25833f h1:KJuwZVtZBVzDmEDtB2zro9CXkD9O0dpCv4o2LHbQIAw=
golang.org/x/sys v0.0.0-20200819091447-39769834ee22/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200819171115-d785dc25833f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -638,8 +637,8 @@ golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200806022845-90696ccdc692/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200817023811-d00afeaade8f/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200814230902-9882f1d1823d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -698,7 +697,6 @@ google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEY
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8= google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70 h1:wboULUXGF3c5qdUnKp+6gLAccE6PRpa/czkYvQ4UXv8=
google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200815001618-f69a88009b70/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -742,8 +740,8 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.58.0 h1:VdDvTzv/005R8vEFyQ56bpEnOKTNPbpJhL0VCohxlQw= gopkg.in/ini.v1 v1.60.0 h1:P5ZzC7RJO04094NJYlEnBdFK2wwmnCAy/+7sAzvWs60=
gopkg.in/ini.v1 v1.58.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.60.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=

View File

@@ -161,7 +161,8 @@ func TransferLog(operation string, path string, elapsed int64, size int64, user
} }
// CommandLog logs an SFTP/SCP/SSH command // CommandLog logs an SFTP/SCP/SSH command
func CommandLog(command, path, target, user, fileMode, connectionID, protocol string, uid, gid int, atime, mtime, sshCommand string) { func CommandLog(command, path, target, user, fileMode, connectionID, protocol string, uid, gid int, atime, mtime,
sshCommand string, size int64) {
logger.Info(). logger.Info().
Timestamp(). Timestamp().
Str("sender", command). Str("sender", command).
@@ -173,6 +174,7 @@ func CommandLog(command, path, target, user, fileMode, connectionID, protocol st
Int("gid", gid). Int("gid", gid).
Str("access_time", atime). Str("access_time", atime).
Str("modification_time", atime). Str("modification_time", atime).
Int64("size", size).
Str("ssh_command", sshCommand). Str("ssh_command", sshCommand).
Str("connection_id", connectionID). Str("connection_id", connectionID).
Str("protocol", protocol). Str("protocol", protocol).

View File

@@ -232,6 +232,10 @@ func (c *Connection) handleSFTPSetstat(filePath string, request *sftp.Request) e
attrs.Atime = time.Unix(int64(request.Attributes().Atime), 0) attrs.Atime = time.Unix(int64(request.Attributes().Atime), 0)
attrs.Mtime = time.Unix(int64(request.Attributes().Mtime), 0) attrs.Mtime = time.Unix(int64(request.Attributes().Mtime), 0)
} }
if request.AttrFlags().Size {
attrs.Flags |= common.StatAttrSize
attrs.Size = int64(request.Attributes().Size)
}
return c.SetStat(filePath, request.Filepath, &attrs) return c.SetStat(filePath, request.Filepath, &attrs)
} }

View File

@@ -657,8 +657,22 @@ func TestStat(t *testing.T) {
assert.Equal(t, newPerm, newFi.Mode().Perm()) assert.Equal(t, newPerm, newFi.Mode().Perm())
_, err = client.ReadLink(testFileName) _, err = client.ReadLink(testFileName)
assert.Error(t, err, "readlink is not supported and must fail") assert.Error(t, err, "readlink is not supported and must fail")
err = client.Truncate(testFileName, 0) newPerm = os.FileMode(0666)
err = client.Chmod(testFileName, newPerm)
assert.NoError(t, err) assert.NoError(t, err)
err = client.Truncate(testFileName, 100)
assert.NoError(t, err)
fi, err := client.Stat(testFileName)
if assert.NoError(t, err) {
assert.Equal(t, int64(100), fi.Size())
}
f, err := client.OpenFile(testFileName, os.O_WRONLY)
if assert.NoError(t, err) {
err = f.Truncate(5)
assert.NoError(t, err)
err = f.Close()
assert.NoError(t, err)
}
err = os.Remove(testFilePath) err = os.Remove(testFilePath)
assert.NoError(t, err) assert.NoError(t, err)
} }

View File

@@ -704,7 +704,7 @@ func (c *sshCommand) sendExitStatus(err error) {
c.command, c.args, c.connection.User.Username, err) c.command, c.args, c.connection.User.Username, err)
} else { } else {
logger.CommandLog(sshCommandLogSender, cmdPath, targetPath, c.connection.User.Username, "", c.connection.ID, logger.CommandLog(sshCommandLogSender, cmdPath, targetPath, c.connection.User.Username, "", c.connection.ID,
common.ProtocolSSH, -1, -1, "", "", c.connection.command) common.ProtocolSSH, -1, -1, "", "", c.connection.command, -1)
} }
exitStatus := sshSubsystemExitStatus{ exitStatus := sshSubsystemExitStatus{
Status: status, Status: status,

View File

@@ -298,6 +298,13 @@ func (GCSFs) Chtimes(name string, atime, mtime time.Time) error {
return errors.New("403 chtimes is not supported") return errors.New("403 chtimes is not supported")
} }
// Truncate changes the size of the named file.
// Truncate by path is not supported, while truncating an opened
// file is handled inside base transfer
func (GCSFs) Truncate(name string, size int64) error {
return errors.New("403 truncate is not supported")
}
// ReadDir reads the directory named by dirname and returns // ReadDir reads the directory named by dirname and returns
// a list of directory entries. // a list of directory entries.
func (fs GCSFs) ReadDir(dirname string) ([]os.FileInfo, error) { func (fs GCSFs) ReadDir(dirname string) ([]os.FileInfo, error) {

View File

@@ -131,6 +131,11 @@ func (OsFs) Chtimes(name string, atime, mtime time.Time) error {
return os.Chtimes(name, atime, mtime) return os.Chtimes(name, atime, mtime)
} }
// Truncate changes the size of the named file
func (OsFs) Truncate(name string, size int64) error {
return os.Truncate(name, size)
}
// ReadDir reads the directory named by dirname and returns // ReadDir reads the directory named by dirname and returns
// a list of directory entries. // a list of directory entries.
func (OsFs) ReadDir(dirname string) ([]os.FileInfo, error) { func (OsFs) ReadDir(dirname string) ([]os.FileInfo, error) {

View File

@@ -330,6 +330,13 @@ func (S3Fs) Chtimes(name string, atime, mtime time.Time) error {
return errors.New("403 chtimes is not supported") return errors.New("403 chtimes is not supported")
} }
// Truncate changes the size of the named file.
// Truncate by path is not supported, while truncating an opened
// file is handled inside base transfer
func (S3Fs) Truncate(name string, size int64) error {
return errors.New("403 truncate is not supported")
}
// ReadDir reads the directory named by dirname and returns // ReadDir reads the directory named by dirname and returns
// a list of directory entries. // a list of directory entries.
func (fs S3Fs) ReadDir(dirname string) ([]os.FileInfo, error) { func (fs S3Fs) ReadDir(dirname string) ([]os.FileInfo, error) {

View File

@@ -31,6 +31,7 @@ type Fs interface {
Chown(name string, uid int, gid int) error Chown(name string, uid int, gid int) error
Chmod(name string, mode os.FileMode) error Chmod(name string, mode os.FileMode) error
Chtimes(name string, atime, mtime time.Time) error Chtimes(name string, atime, mtime time.Time) error
Truncate(name string, size int64) error
ReadDir(dirname string) ([]os.FileInfo, error) ReadDir(dirname string) ([]os.FileInfo, error)
IsUploadResumeSupported() bool IsUploadResumeSupported() bool
IsAtomicUploadSupported() bool IsAtomicUploadSupported() bool