Skip to content

Commit f07f64f

Browse files
committed
libfile: fix ETA on macOS and BSD-based systems
macOS and BSD-based systems have a statvfs(3) wrapper bug where fs.f_bfree is returned in units of 512-byte sectors while fs.f_frsize is returned in allocation block units (e.g. 4KB). This mismatch inflates ETA calculations by up to 8x. This commit closes #277.
1 parent 3ec5ee9 commit f07f64f

2 files changed

Lines changed: 42 additions & 3 deletions

File tree

src/f3write.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include <limits.h>
1313
#include <sys/stat.h>
1414
#include <fcntl.h>
15-
#include <sys/statvfs.h>
1615
#include <sys/types.h>
1716
#include <errno.h>
1817
#include <unistd.h>

src/libfile.c

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,23 @@
2020
#include <errno.h>
2121
#include <err.h>
2222
#include <unistd.h>
23+
24+
#if (__APPLE__ && __MACH__) || defined(__FreeBSD__) || defined(__OpenBSD__)
25+
/*
26+
* macOS and BSD-based systems have a statvfs(3) wrapper bug where fs.f_bfree
27+
* is returned in units of 512-byte sectors while fs.f_frsize is returned
28+
* in allocation block units (e.g. 4KB). This mismatch inflates remaining time
29+
* and ETA calculations by up to 8x. See issue #277.
30+
* Calling native statfs(2) directly bypasses this buggy conversion wrapper and
31+
* returns consistent allocation units.
32+
*/
33+
#define USE_STATFS 1
34+
#include <sys/param.h>
35+
#include <sys/mount.h>
36+
#else
37+
#define USE_STATFS 0
2338
#include <sys/statvfs.h>
39+
#endif
2440

2541
#include "libfile.h"
2642
#include "libutils.h"
@@ -41,20 +57,44 @@ void adjust_dev_path(const char **dev_path)
4157

4258
unsigned int get_block_order(const char *path)
4359
{
44-
struct statvfs fs;
4560
unsigned int block_size;
46-
61+
#if USE_STATFS
62+
struct statfs fs;
63+
assert(!statfs(path, &fs));
64+
/*
65+
* On Apple/BSD systems, statfs.f_bsize provides the optimal transfer
66+
* block size.
67+
*/
68+
block_size = fs.f_bsize;
69+
#else
70+
struct statvfs fs;
4771
assert(!statvfs(path, &fs));
72+
/*
73+
* On POSIX compliant systems (Linux/Cygwin), statvfs.f_frsize provides
74+
* the fundamental block size.
75+
*/
4876
block_size = fs.f_frsize;
77+
#endif
4978
assert(is_power_of_2(block_size));
5079
return ilog2(block_size);
5180
}
5281

5382
uint64_t get_free_blocks(const char *path)
5483
{
84+
#if USE_STATFS
85+
struct statfs fs;
86+
assert(!statfs(path, &fs));
87+
/*
88+
* Native statfs returns f_bfree in units of f_bsize,
89+
* avoiding the buggy unit conversion wrapper of statvfs.
90+
*/
91+
return fs.f_bfree;
92+
#else
5593
struct statvfs fs;
5694
assert(!statvfs(path, &fs));
95+
/* Standard POSIX statvfs returns f_bfree in units of f_frsize. */
5796
return fs.f_bfree;
97+
#endif
5898
}
5999

60100
int is_my_file(const char *filename)

0 commit comments

Comments
 (0)