serve sftp: implement statvfs@openssh.com to report disk usage

The statvfs@openssh.com extension was advertised but returned an
unsupported status, so clients couldn't query the amount of free and
used space. Implement it using the VFS Statfs method, which reports the
backend's usage where the backend supports About.
This commit is contained in:
Nick Craig-Wood
2026-05-25 20:43:23 +01:00
parent 90308de5d1
commit 54cd7d6750
3 changed files with 40 additions and 4 deletions
+20
View File
@@ -116,6 +116,26 @@ func (v vfsHandler) Filecmd(r *sftp.Request) error {
return nil
}
// StatVFS implements the statvfs@openssh.com extension, returning filesystem
// usage information from the VFS. It satisfies sftp.StatVFSFileCmder.
func (v vfsHandler) StatVFS(r *sftp.Request) (*sftp.StatVFS, error) {
const blockSize = 4096
total, _, free := v.Statfs()
blocks := uint64(total) / blockSize
bfree := uint64(free) / blockSize
return &sftp.StatVFS{
Bsize: blockSize,
Frsize: blockSize,
Blocks: blocks,
Bfree: bfree,
Bavail: bfree,
Files: 1e9, // total file inodes - made up as the VFS has no concept of these
Ffree: 1e9, // free file inodes
Favail: 1e9, // free file inodes for non-root
Namemax: 255, // maximum filename length
}, nil
}
type listerat []os.FileInfo
// Modeled after strings.Reader's ReadAt() implementation
+15
View File
@@ -168,6 +168,21 @@ func TestSetstatTruncate(t *testing.T) {
assert.Equal(t, strings.Repeat("A", 10), string(got))
}
// Test that the statvfs@openssh.com extension returns filesystem usage from
// the VFS rather than an unsupported status.
func TestStatVFS(t *testing.T) {
vfsOpt := vfscommon.Opt
vfsOpt.CacheMode = vfscommon.CacheModeWrites
client := startTestServer(t, &vfsOpt)
st, err := client.StatVFS("/")
require.NoError(t, err)
assert.Greater(t, st.TotalSpace(), uint64(0), "expected non-zero total space")
assert.LessOrEqual(t, st.FreeSpace(), st.TotalSpace(), "free space should not exceed total")
assert.Equal(t, uint64(255), st.Namemax)
}
// writeFile writes contents to fileName via the client truncating any existing
// data, the way a normal upload does.
func writeFile(client *sftp.Client, fileName, contents string) error {
+5 -4
View File
@@ -33,10 +33,11 @@ const (
// check interfaces
var (
_ sftp.FileReader = vfsHandler{}
_ sftp.FileWriter = vfsHandler{}
_ sftp.FileCmder = vfsHandler{}
_ sftp.FileLister = vfsHandler{}
_ sftp.FileReader = vfsHandler{}
_ sftp.FileWriter = vfsHandler{}
_ sftp.FileCmder = vfsHandler{}
_ sftp.FileLister = vfsHandler{}
_ sftp.StatVFSFileCmder = vfsHandler{}
)
// TestSftp runs the sftp server then runs the unit tests for the