mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 09:00:27 +00:00
improve chtimes handling on open files
This commit is contained in:
parent
f828c58dca
commit
f2480ce5c9
8 changed files with 93 additions and 3 deletions
|
@ -291,6 +291,7 @@ type ActiveTransfer interface {
|
|||
SignalClose()
|
||||
Truncate(fsPath string, size int64) (int64, error)
|
||||
GetRealFsPath(fsPath string) string
|
||||
SetTimes(fsPath string, atime time.Time, mtime time.Time) bool
|
||||
}
|
||||
|
||||
// ActiveConnection defines the interface for the current active connections
|
||||
|
|
|
@ -201,6 +201,17 @@ func (c *BaseConnection) getRealFsPath(fsPath string) string {
|
|||
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) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
|
@ -558,6 +569,7 @@ func (c *BaseConnection) handleChtimes(fs vfs.Fs, fsPath, pathForPerms string, a
|
|||
fsPath, attributes.Atime, attributes.Mtime, err)
|
||||
return c.GetFsError(fs, err)
|
||||
}
|
||||
c.setTimes(fsPath, attributes.Atime, attributes.Mtime)
|
||||
accessTimeString := attributes.Atime.Format(chtimesFormat)
|
||||
modificationTimeString := attributes.Mtime.Format(chtimesFormat)
|
||||
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)
|
||||
}
|
||||
|
||||
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) {
|
||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
|
|
@ -38,6 +38,8 @@ type BaseTransfer struct { //nolint:maligned
|
|||
isNewFile bool
|
||||
transferType int
|
||||
AbortTransfer int32
|
||||
aTime time.Time
|
||||
mTime time.Time
|
||||
sync.Mutex
|
||||
ErrTransfer error
|
||||
}
|
||||
|
@ -115,6 +117,15 @@ func (t *BaseTransfer) GetFsPath() string {
|
|||
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.
|
||||
// If atomic uploads are enabled this differ from fsPath
|
||||
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.updateQuota(numFiles, fileSize)
|
||||
t.updateTimes()
|
||||
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)
|
||||
ExecuteActionNotification(&t.Connection.User, operationUpload, t.fsPath, t.requestPath, "", "", "", t.Connection.protocol,
|
||||
|
@ -266,6 +278,14 @@ func (t *BaseTransfer) Close() error {
|
|||
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 {
|
||||
// S3 uploads are atomic, if there is an error nothing is uploaded
|
||||
if t.File == nil && t.ErrTransfer != nil {
|
||||
|
|
|
@ -84,6 +84,7 @@ Flags:
|
|||
--s3-acl string
|
||||
--s3-bucket string
|
||||
--s3-endpoint string
|
||||
--s3-force-path-style Force path style bucket URL
|
||||
--s3-key-prefix string Allows to restrict access to the
|
||||
virtual folder identified by this
|
||||
prefix and its contents
|
||||
|
|
|
@ -9762,6 +9762,8 @@ func getScpDownloadCommand(localPath, remotePath string, preserveTime, recursive
|
|||
args = append(args, "2022")
|
||||
args = append(args, "-o")
|
||||
args = append(args, "StrictHostKeyChecking=no")
|
||||
args = append(args, "-o")
|
||||
args = append(args, "HostKeyAlgorithms=+ssh-rsa")
|
||||
args = append(args, "-i")
|
||||
args = append(args, privateKeyPath)
|
||||
args = append(args, remotePath)
|
||||
|
@ -9787,6 +9789,8 @@ func getScpUploadCommand(localPath, remotePath string, preserveTime, remoteToRem
|
|||
args = append(args, "2022")
|
||||
args = append(args, "-o")
|
||||
args = append(args, "StrictHostKeyChecking=no")
|
||||
args = append(args, "-o")
|
||||
args = append(args, "HostKeyAlgorithms=+ssh-rsa")
|
||||
args = append(args, "-i")
|
||||
args = append(args, privateKeyPath)
|
||||
args = append(args, localPath)
|
||||
|
|
|
@ -232,7 +232,7 @@ function deleteAction() {
|
|||
},
|
||||
{
|
||||
"targets": [2],
|
||||
"render": $.fn.dataTable.render.ellipsis(50, true),
|
||||
"render": $.fn.dataTable.render.ellipsis(60, true),
|
||||
},
|
||||
{
|
||||
"targets": [3],
|
||||
|
|
|
@ -255,11 +255,11 @@
|
|||
},
|
||||
{
|
||||
"targets": [3],
|
||||
"render": $.fn.dataTable.render.ellipsis(30, true),
|
||||
"render": $.fn.dataTable.render.ellipsis(40, true),
|
||||
},
|
||||
{
|
||||
"targets": [4],
|
||||
"render": $.fn.dataTable.render.ellipsis(40, true),
|
||||
"render": $.fn.dataTable.render.ellipsis(70, true),
|
||||
}
|
||||
],
|
||||
"scrollX": false,
|
||||
|
|
Loading…
Reference in a new issue