From c96d308a7a0233893f9166208b06bfe16d687096 Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Wed, 9 Sep 2015 22:04:10 +1000 Subject: [PATCH] Set f_frsize, fixing free space calculation by `df` on OS X. --- conversions.go | 28 ++++++++++++++++++++++++++++ fuseops/ops.go | 29 ++++++++++++++++++++++------- samples/statfs/statfs_test.go | 2 +- 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/conversions.go b/conversions.go index bd414ee..4ccbbcd 100644 --- a/conversions.go +++ b/conversions.go @@ -564,7 +564,35 @@ func (c *Connection) kernelResponseForOp( out.St.Bavail = o.BlocksAvailable out.St.Files = o.Inodes out.St.Ffree = o.InodesFree + + // The posix spec for sys/statvfs.h defines the following fields, among + // others: + // + // f_bsize File system block size. + // f_frsize Fundamental file system block size. + // f_blocks Total number of blocks on file system in units of f_frsize. + // + // It appears as though f_bsize was the only thing supported by most unixes + // originally, but then f_frsize was added when new sorts of file systems + // came about. Quoth The Linux Programming Interface by Michael Kerrisk + // (https://goo.gl/5LZMxQ): + // + // For most Linux file systems, the values of f_bsize and f_frsize are + // the same. However, some file systems support the notion of block + // fragments, which can be used to allocate a smaller unit of storage + // at the end of the file if if a full block is not required. This + // avoids the waste of space that would otherwise occur if a full block + // was allocated. On such file systems, f_frsize is the size of a + // fragment, and f_bsize is the size of a whole block. (The notion of + // fragments in UNIX file systems first appeared in the early 1980s + // with the 4.2BSD Fast File System.) + // + // Confusingly, it appears as though osxfuse surfaces f_bsize as f_iosize + // (of advisory use only), and f_frsize as f_bsize (which affects free + // space display in the Finder). In any case, we don't care to let the user + // distinguish, so set both to the same value. out.St.Bsize = o.BlockSize + out.St.Frsize = o.BlockSize case *initOp: out := (*fusekernel.InitOut)(m.Grow(unsafe.Sizeof(fusekernel.InitOut{}))) diff --git a/fuseops/ops.go b/fuseops/ops.go index 70f9622..aa56176 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -38,16 +38,31 @@ import ( // convert_fuse_statfs to convert the response in a straightforward // manner. // -// Note that this op is particularly important on OS X: if you don't implement -// it, the file system will not successfully mount. If you don't model a sane -// amount of free space, the Finder will refuse to copy files into the file -// system. +// This op is particularly important on OS X: if you don't implement it, the +// file system will not successfully mount. If you don't model a sane amount of +// free space, the Finder will refuse to copy files into the file system. type StatFSOp struct { - // The size of the file system's blocks, and how many there are in total. + // The size of the file system's blocks. This may be used, in combination + // with the block counts below, by callers of statfs(2) to infer the file + // system's capacity and space availability. + // + // TODO(jacobsa): Document the range of values accepted on OS X and Linux. + // Cite sources in Linux if possible. + // + // On OS X this also affects statfs::f_iosize, which is documented as the + // "optimal transfer block size". It does not appear to cause osxfuse to + // change the size of data in WriteFile ops, though. + // + // This interface does not distinguish between blocks and block fragments. BlockSize uint32 - Blocks uint64 - // The number of blocks free, and how many are available to non-root users. + // The total number of blocks in the file system, the number of unused + // blocks, and the count of the latter that are available for use by non-root + // users. + // + // For each category, the corresponding number of bytes is derived by + // multiplying by BlockSize. + Blocks uint64 BlocksFree uint64 BlocksAvailable uint64 diff --git a/samples/statfs/statfs_test.go b/samples/statfs/statfs_test.go index 6eeb8d0..e6f4cd9 100644 --- a/samples/statfs/statfs_test.go +++ b/samples/statfs/statfs_test.go @@ -203,7 +203,7 @@ func (t *StatFSTest) Syscall_NonZeroValues() { err = syscall.Statfs(t.Dir, &stat) AssertEq(nil, err) - ExpectEq(4096, stat.Bsize) // OS X seems to always make this 4096. + ExpectEq(canned.BlockSize, stat.Bsize) ExpectEq(canned.BlockSize, stat.Iosize) ExpectEq(canned.Blocks, stat.Blocks) ExpectEq(canned.BlocksFree, stat.Bfree)