From 2b855751fca766011d3df817ff3926d94f3b02b7 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Tue, 17 Apr 2018 23:19:34 +0100 Subject: [PATCH] vfs,mount,cmount: use About to return the correct disk total/used/free Disks total, used, free now shows correctly for mount and cmount (eg `df` for Unix or in the Windows explorer). --- cmd/cmount/fs.go | 10 ++++++++++ cmd/mount/fs.go | 10 ++++++++++ vfs/vfs.go | 51 +++++++++++++++++++++++++++++++++++++++++++----- vfs/vfs_test.go | 40 +++++++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+), 5 deletions(-) diff --git a/cmd/cmount/fs.go b/cmd/cmount/fs.go index 1b21ca7b5..4a0d071f7 100644 --- a/cmd/cmount/fs.go +++ b/cmd/cmount/fs.go @@ -275,6 +275,16 @@ func (fsys *FS) Statfs(path string, stat *fuse.Statfs_t) (errc int) { stat.Bsize = blockSize // Block size stat.Namemax = 255 // Maximum file name length? stat.Frsize = blockSize // Fragment size, smallest addressable data size in the file system. + total, used, free := fsys.VFS.Statfs() + if total >= 0 { + stat.Blocks = uint64(total) / blockSize + } + if used >= 0 { + stat.Bfree = stat.Blocks - uint64(used)/blockSize + } + if free >= 0 { + stat.Bavail = uint64(free) / blockSize + } return 0 } diff --git a/cmd/mount/fs.go b/cmd/mount/fs.go index 77d84b36f..caab6a060 100644 --- a/cmd/mount/fs.go +++ b/cmd/mount/fs.go @@ -62,6 +62,16 @@ func (f *FS) Statfs(ctx context.Context, req *fuse.StatfsRequest, resp *fuse.Sta resp.Bsize = blockSize // Block size resp.Namelen = 255 // Maximum file name length? resp.Frsize = blockSize // Fragment size, smallest addressable data size in the file system. + total, used, free := f.VFS.Statfs() + if total >= 0 { + resp.Blocks = uint64(total) / blockSize + } + if used >= 0 { + resp.Bfree = resp.Blocks - uint64(used)/blockSize + } + if free >= 0 { + resp.Bavail = uint64(free) / blockSize + } return nil } diff --git a/vfs/vfs.go b/vfs/vfs.go index 923b3e8a6..2ec5c0d73 100644 --- a/vfs/vfs.go +++ b/vfs/vfs.go @@ -24,6 +24,7 @@ import ( "os" "path" "strings" + "sync" "sync/atomic" "time" @@ -165,11 +166,14 @@ var ( // VFS represents the top level filing system type VFS struct { - f fs.Fs - root *Dir - Opt Options - cache *cache - cancel context.CancelFunc + f fs.Fs + root *Dir + Opt Options + cache *cache + cancel context.CancelFunc + usageMu sync.Mutex + usageTime time.Time + usage *fs.Usage } // Options is options for creating the vfs @@ -452,3 +456,40 @@ func (vfs *VFS) Rename(oldName, newName string) error { } return nil } + +// Statfs returns into about the filing system if known +// +// The values will be -1 if they aren't known +// +// This information is cached for the DirCacheTime interval +func (vfs *VFS) Statfs() (total, used, free int64) { + // defer log.Trace("/", "")("total=%d, used=%d, free=%d", &total, &used, &free) + vfs.usageMu.Lock() + defer vfs.usageMu.Unlock() + total, used, free = -1, -1, -1 + doAbout := vfs.f.Features().About + if doAbout == nil { + return + } + if vfs.usageTime.IsZero() || time.Since(vfs.usageTime) >= vfs.Opt.DirCacheTime { + var err error + vfs.usage, err = doAbout() + vfs.usageTime = time.Now() + if err != nil { + fs.Errorf(vfs.f, "Statfs failed: %v", err) + return + } + } + if u := vfs.usage; u != nil { + if u.Total != nil { + total = *u.Total + } + if u.Free != nil { + free = *u.Free + } + if u.Used != nil { + used = *u.Used + } + } + return +} diff --git a/vfs/vfs_test.go b/vfs/vfs_test.go index 3bf10a9a0..b028a1227 100644 --- a/vfs/vfs_test.go +++ b/vfs/vfs_test.go @@ -251,3 +251,43 @@ func TestVFSRename(t *testing.T) { err = vfs.Rename("file0", "not found/file0") assert.Equal(t, os.ErrNotExist, err) } + +func TestVFSStatfs(t *testing.T) { + r := fstest.NewRun(t) + defer r.Finalise() + vfs := New(r.Fremote, nil) + + // pre-conditions + assert.Nil(t, vfs.usage) + assert.True(t, vfs.usageTime.IsZero()) + + // read + total, used, free := vfs.Statfs() + require.NotNil(t, vfs.usage) + assert.False(t, vfs.usageTime.IsZero()) + if vfs.usage.Total != nil { + assert.Equal(t, *vfs.usage.Total, total) + } else { + assert.Equal(t, -1, total) + } + if vfs.usage.Free != nil { + assert.Equal(t, *vfs.usage.Free, free) + } else { + assert.Equal(t, -1, free) + } + if vfs.usage.Used != nil { + assert.Equal(t, *vfs.usage.Used, used) + } else { + assert.Equal(t, -1, used) + } + + // read cached + oldUsage := vfs.usage + oldTime := vfs.usageTime + total2, used2, free2 := vfs.Statfs() + assert.Equal(t, oldUsage, vfs.usage) + assert.Equal(t, total, total2) + assert.Equal(t, used, used2) + assert.Equal(t, free, free2) + assert.Equal(t, oldTime, vfs.usageTime) +}