mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-08 07:10:56 +03:00
improve chtimes handling on open files
This commit is contained in:
@@ -291,6 +291,7 @@ type ActiveTransfer interface {
|
|||||||
SignalClose()
|
SignalClose()
|
||||||
Truncate(fsPath string, size int64) (int64, error)
|
Truncate(fsPath string, size int64) (int64, error)
|
||||||
GetRealFsPath(fsPath string) string
|
GetRealFsPath(fsPath string) string
|
||||||
|
SetTimes(fsPath string, atime time.Time, mtime time.Time) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActiveConnection defines the interface for the current active connections
|
// ActiveConnection defines the interface for the current active connections
|
||||||
|
|||||||
@@ -201,6 +201,17 @@ func (c *BaseConnection) getRealFsPath(fsPath string) string {
|
|||||||
return fsPath
|
return fsPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *BaseConnection) setTimes(fsPath string, atime time.Time, mtime time.Time) {
|
||||||
|
c.RLock()
|
||||||
|
defer c.RUnlock()
|
||||||
|
|
||||||
|
for _, t := range c.activeTransfers {
|
||||||
|
if t.SetTimes(fsPath, atime, mtime) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *BaseConnection) truncateOpenHandle(fsPath string, size int64) (int64, error) {
|
func (c *BaseConnection) truncateOpenHandle(fsPath string, size int64) (int64, error) {
|
||||||
c.RLock()
|
c.RLock()
|
||||||
defer c.RUnlock()
|
defer c.RUnlock()
|
||||||
@@ -558,6 +569,7 @@ func (c *BaseConnection) handleChtimes(fs vfs.Fs, fsPath, pathForPerms string, a
|
|||||||
fsPath, attributes.Atime, attributes.Mtime, err)
|
fsPath, attributes.Atime, attributes.Mtime, err)
|
||||||
return c.GetFsError(fs, err)
|
return c.GetFsError(fs, err)
|
||||||
}
|
}
|
||||||
|
c.setTimes(fsPath, attributes.Atime, attributes.Mtime)
|
||||||
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,
|
||||||
|
|||||||
@@ -321,6 +321,58 @@ func TestSetStat(t *testing.T) {
|
|||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestChtimesOpenHandle(t *testing.T) {
|
||||||
|
localUser, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
sftpUser, _, err := httpdtest.AddUser(getTestSFTPUser(), http.StatusCreated)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
u := getCryptFsUser()
|
||||||
|
u.Username += "_crypt"
|
||||||
|
cryptFsUser, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
for _, user := range []dataprovider.User{localUser, sftpUser, cryptFsUser} {
|
||||||
|
conn, client, err := getSftpClient(user)
|
||||||
|
if assert.NoError(t, err) {
|
||||||
|
defer conn.Close()
|
||||||
|
defer client.Close()
|
||||||
|
|
||||||
|
f, err := client.Create(testFileName)
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
f1, err := client.Create(testFileName + "1")
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
acmodTime := time.Now().Add(36 * time.Hour)
|
||||||
|
err = client.Chtimes(testFileName, acmodTime, acmodTime)
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
_, err = f.Write(testFileContent)
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
err = f.Close()
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
err = f1.Close()
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
info, err := client.Lstat(testFileName)
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
diff := math.Abs(info.ModTime().Sub(acmodTime).Seconds())
|
||||||
|
assert.LessOrEqual(t, diff, float64(1), "user %v", user.Username)
|
||||||
|
info1, err := client.Lstat(testFileName + "1")
|
||||||
|
assert.NoError(t, err, "user %v", user.Username)
|
||||||
|
diff = math.Abs(info1.ModTime().Sub(acmodTime).Seconds())
|
||||||
|
assert.Greater(t, diff, float64(86400), "user %v", user.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = httpdtest.RemoveUser(localUser, http.StatusOK)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = os.RemoveAll(localUser.GetHomeDir())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
_, err = httpdtest.RemoveUser(cryptFsUser, http.StatusOK)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
err = os.RemoveAll(cryptFsUser.GetHomeDir())
|
||||||
|
assert.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
func TestPermissionErrors(t *testing.T) {
|
func TestPermissionErrors(t *testing.T) {
|
||||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ type BaseTransfer struct { //nolint:maligned
|
|||||||
isNewFile bool
|
isNewFile bool
|
||||||
transferType int
|
transferType int
|
||||||
AbortTransfer int32
|
AbortTransfer int32
|
||||||
|
aTime time.Time
|
||||||
|
mTime time.Time
|
||||||
sync.Mutex
|
sync.Mutex
|
||||||
ErrTransfer error
|
ErrTransfer error
|
||||||
}
|
}
|
||||||
@@ -115,6 +117,15 @@ func (t *BaseTransfer) GetFsPath() string {
|
|||||||
return t.fsPath
|
return t.fsPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransfer) SetTimes(fsPath string, atime time.Time, mtime time.Time) bool {
|
||||||
|
if fsPath == t.GetFsPath() {
|
||||||
|
t.aTime = atime
|
||||||
|
t.mTime = mtime
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// GetRealFsPath returns the real transfer filesystem path.
|
// GetRealFsPath returns the real transfer filesystem path.
|
||||||
// If atomic uploads are enabled this differ from fsPath
|
// If atomic uploads are enabled this differ from fsPath
|
||||||
func (t *BaseTransfer) GetRealFsPath(fsPath string) string {
|
func (t *BaseTransfer) GetRealFsPath(fsPath string) string {
|
||||||
@@ -252,6 +263,7 @@ func (t *BaseTransfer) Close() error {
|
|||||||
}
|
}
|
||||||
t.Connection.Log(logger.LevelDebug, "uploaded file size %v", fileSize)
|
t.Connection.Log(logger.LevelDebug, "uploaded file size %v", fileSize)
|
||||||
t.updateQuota(numFiles, fileSize)
|
t.updateQuota(numFiles, fileSize)
|
||||||
|
t.updateTimes()
|
||||||
logger.TransferLog(uploadLogSender, t.fsPath, elapsed, atomic.LoadInt64(&t.BytesReceived), t.Connection.User.Username,
|
logger.TransferLog(uploadLogSender, t.fsPath, elapsed, atomic.LoadInt64(&t.BytesReceived), t.Connection.User.Username,
|
||||||
t.Connection.ID, t.Connection.protocol, t.Connection.localAddr, t.Connection.remoteAddr, t.ftpMode)
|
t.Connection.ID, t.Connection.protocol, t.Connection.localAddr, t.Connection.remoteAddr, t.ftpMode)
|
||||||
ExecuteActionNotification(&t.Connection.User, operationUpload, t.fsPath, t.requestPath, "", "", "", t.Connection.protocol,
|
ExecuteActionNotification(&t.Connection.User, operationUpload, t.fsPath, t.requestPath, "", "", "", t.Connection.protocol,
|
||||||
@@ -266,6 +278,14 @@ func (t *BaseTransfer) Close() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransfer) updateTimes() {
|
||||||
|
if !t.aTime.IsZero() && !t.mTime.IsZero() {
|
||||||
|
err := t.Fs.Chtimes(t.fsPath, t.aTime, t.mTime)
|
||||||
|
t.Connection.Log(logger.LevelDebug, "set times for file %#v, atime: %v, mtime: %v, err: %v",
|
||||||
|
t.fsPath, t.aTime, t.mTime, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (t *BaseTransfer) updateQuota(numFiles int, fileSize int64) bool {
|
func (t *BaseTransfer) updateQuota(numFiles int, fileSize int64) bool {
|
||||||
// S3 uploads are atomic, if there is an error nothing is uploaded
|
// S3 uploads are atomic, if there is an error nothing is uploaded
|
||||||
if t.File == nil && t.ErrTransfer != nil {
|
if t.File == nil && t.ErrTransfer != nil {
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ Flags:
|
|||||||
--s3-acl string
|
--s3-acl string
|
||||||
--s3-bucket string
|
--s3-bucket string
|
||||||
--s3-endpoint string
|
--s3-endpoint string
|
||||||
|
--s3-force-path-style Force path style bucket URL
|
||||||
--s3-key-prefix string Allows to restrict access to the
|
--s3-key-prefix string Allows to restrict access to the
|
||||||
virtual folder identified by this
|
virtual folder identified by this
|
||||||
prefix and its contents
|
prefix and its contents
|
||||||
|
|||||||
@@ -9762,6 +9762,8 @@ func getScpDownloadCommand(localPath, remotePath string, preserveTime, recursive
|
|||||||
args = append(args, "2022")
|
args = append(args, "2022")
|
||||||
args = append(args, "-o")
|
args = append(args, "-o")
|
||||||
args = append(args, "StrictHostKeyChecking=no")
|
args = append(args, "StrictHostKeyChecking=no")
|
||||||
|
args = append(args, "-o")
|
||||||
|
args = append(args, "HostKeyAlgorithms=+ssh-rsa")
|
||||||
args = append(args, "-i")
|
args = append(args, "-i")
|
||||||
args = append(args, privateKeyPath)
|
args = append(args, privateKeyPath)
|
||||||
args = append(args, remotePath)
|
args = append(args, remotePath)
|
||||||
@@ -9787,6 +9789,8 @@ func getScpUploadCommand(localPath, remotePath string, preserveTime, remoteToRem
|
|||||||
args = append(args, "2022")
|
args = append(args, "2022")
|
||||||
args = append(args, "-o")
|
args = append(args, "-o")
|
||||||
args = append(args, "StrictHostKeyChecking=no")
|
args = append(args, "StrictHostKeyChecking=no")
|
||||||
|
args = append(args, "-o")
|
||||||
|
args = append(args, "HostKeyAlgorithms=+ssh-rsa")
|
||||||
args = append(args, "-i")
|
args = append(args, "-i")
|
||||||
args = append(args, privateKeyPath)
|
args = append(args, privateKeyPath)
|
||||||
args = append(args, localPath)
|
args = append(args, localPath)
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ function deleteAction() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"targets": [2],
|
"targets": [2],
|
||||||
"render": $.fn.dataTable.render.ellipsis(50, true),
|
"render": $.fn.dataTable.render.ellipsis(60, true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"targets": [3],
|
"targets": [3],
|
||||||
|
|||||||
@@ -255,11 +255,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"targets": [3],
|
"targets": [3],
|
||||||
"render": $.fn.dataTable.render.ellipsis(30, true),
|
"render": $.fn.dataTable.render.ellipsis(40, true),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"targets": [4],
|
"targets": [4],
|
||||||
"render": $.fn.dataTable.render.ellipsis(40, true),
|
"render": $.fn.dataTable.render.ellipsis(70, true),
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scrollX": false,
|
"scrollX": false,
|
||||||
|
|||||||
Reference in New Issue
Block a user