diff --git a/internal/common/connection.go b/internal/common/connection.go index b569be68..99efc1a5 100644 --- a/internal/common/connection.go +++ b/internal/common/connection.go @@ -21,6 +21,7 @@ import ( "io/fs" "os" "path" + "slices" "strings" "sync" "sync/atomic" @@ -1795,12 +1796,12 @@ type DirListerAt struct { lister vfs.DirLister } -// Add adds the given os.FileInfo to the internal cache -func (l *DirListerAt) Add(fi os.FileInfo) { +// Prepend adds the given os.FileInfo as first element of the internal cache +func (l *DirListerAt) Prepend(fi os.FileInfo) { l.mu.Lock() defer l.mu.Unlock() - l.info = append(l.info, fi) + l.info = slices.Insert(l.info, 0, fi) } // ListAt implements sftp.ListerAt @@ -1813,10 +1814,10 @@ func (l *DirListerAt) ListAt(f []os.FileInfo, _ int64) (int, error) { } if len(f) <= len(l.info) { files := make([]os.FileInfo, 0, len(f)) - for idx := len(l.info) - 1; idx >= 0; idx-- { + for idx := range l.info { files = append(files, l.info[idx]) if len(files) == len(f) { - l.info = l.info[:idx] + l.info = l.info[idx+1:] n := copy(f, files) return n, nil } @@ -1838,9 +1839,7 @@ func (l *DirListerAt) Next(limit int) ([]os.FileInfo, error) { } files = l.user.FilterListDir(files, l.virtualPath) if len(l.info) > 0 { - for _, fi := range l.info { - files = util.PrependFileInfo(files, fi) - } + files = slices.Concat(l.info, files) l.info = nil } if err != nil || len(files) > 0 { diff --git a/internal/common/connection_test.go b/internal/common/connection_test.go index 8b9d44c2..06b3fa4b 100644 --- a/internal/common/connection_test.go +++ b/internal/common/connection_test.go @@ -1134,8 +1134,8 @@ func TestListerAt(t *testing.T) { require.Equal(t, 0, n) lister, err = conn.ListDir("/") require.NoError(t, err) - lister.Add(vfs.NewFileInfo("..", true, 0, time.Unix(0, 0), false)) - lister.Add(vfs.NewFileInfo(".", true, 0, time.Unix(0, 0), false)) + lister.Prepend(vfs.NewFileInfo("..", true, 0, time.Unix(0, 0), false)) + lister.Prepend(vfs.NewFileInfo(".", true, 0, time.Unix(0, 0), false)) files = make([]os.FileInfo, 1) n, err = lister.ListAt(files, 0) require.NoError(t, err) diff --git a/internal/sftpd/handler.go b/internal/sftpd/handler.go index ea65f394..742663cf 100644 --- a/internal/sftpd/handler.go +++ b/internal/sftpd/handler.go @@ -221,9 +221,9 @@ func (c *Connection) Filelist(request *sftp.Request) (sftp.ListerAt, error) { } modTime := time.Unix(0, 0) if request.Filepath != "/" { - lister.Add(vfs.NewFileInfo("..", true, 0, modTime, false)) + lister.Prepend(vfs.NewFileInfo("..", true, 0, modTime, false)) } - lister.Add(vfs.NewFileInfo(".", true, 0, modTime, false)) + lister.Prepend(vfs.NewFileInfo(".", true, 0, modTime, false)) return lister, nil case "Stat": if !c.User.HasPerm(dataprovider.PermListItems, path.Dir(request.Filepath)) { diff --git a/internal/util/util.go b/internal/util/util.go index 54dd0bcd..0274d97d 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -809,15 +809,6 @@ func GetRedactedURL(rawurl string) string { return u.Redacted() } -// PrependFileInfo prepends a file info to a slice in an efficient way. -// We, optimistically, assume that the slice has enough capacity -func PrependFileInfo(files []os.FileInfo, info os.FileInfo) []os.FileInfo { - files = append(files, nil) - copy(files[1:], files) - files[0] = info - return files -} - // GetTLSVersion returns the TLS version for integer: // - 12 means TLS 1.2 // - 13 means TLS 1.3