As a set of interesting programming exercises, I've written up a number of examples to calculate the amount of free space on a given disk running on macOS. The first two examples are command line solutions, while the other examples take more programatic approaches.
Update (26 November 2017)
Due to some new approaches I came across from this StackOverflow post, two additional methods have been added by using diskutil
and the private macOS framework DiskManagement
.
df
To easily display the amount of available disk space and how much is free, use the built in utility df
which returns the statistics about the amount of free disk space on a specified filesystem. This is a great little utility to use from the terminal or within a script. To get the details on the root drive, run the command df -kP /
which will return data formatted like the following:
Filesystem 1024-blocks Used Available Capacity Mounted on
/dev/disk2 2018614912 698624560 1319734352 35% /
If you want just the amount of free disk space (calculated in kilobytes), use one of these two options which calls df
and then parses out the pertinent data.
df -kP / | tail -1 | awk '{print $4}'
df -kP / | awk '/[0-9]%/{print $(NF-2)}'
diskutil
diskutil
is a veritable utility for disk related information and maintenance tasks, which serves as the command line version of the Disk Utility app. To retrieve the amount of free space (sometimes referred to as "available space"), run the command. diskutil info / | grep "Available Space"
. Due to changes in the output from diskutil
over the years, on older version of Mac OS X, use the command diskutil info / | grep "Free Space"
.
$ diskutil info / | grep "Available Space"
Volume Available Space: 94.0 GB (93988098048 Bytes) (exactly 183570504 512-Byte-Units) (37.6%)
DiskManagement Framework
Ivan Genchev discovered an interesting approach to determine the available free space by using the private macOS framework DiskManagement
, which is used by diskutil
(and I assume, Disk Utility, as well). Since this framework is not public, you'll need to generate a header file by using Steve Nygard's excellent utility class-dump
, which allows you to examine the Objective-C runtime information stored in a Mach-O file. This is the same approach I used in another project to write a Finder plug-in, which required generating a Finder.h
header file to be able to access the private Finder methods.
Download class-dump and copy the executable onto your system, such as in your /usr/local/bin
directory.
Next, generate the header file with the command:
$ class-dump /System/Library/PrivateFrameworks/DiskManagement.framework/Versions/Current/DiskManagement > DiskManagement.h
This will create the necessary header file to link to the program. This is a fairly large file (the one for macOS High Sierra is 840 lines in length), and contains a number of interesting private methods. The method we are interested in is (id)volumeFreeSpaceForDisk:(struct __DADisk *)arg1 error:(int *)arg2
.
Once we have the DiskManagement.h
file, we can integrate it into our program. The following code is modified from Ivan Genchev's code in the StackOverflow post.
/* dmfreespace.m
*
* Description: Get the available free space on the root drive using the method
* volumeFreeSpaceForDisk from the private framework DiskManagement
*
* Original reference: https://stackoverflow.com/a/20679389/955122
* To compile: clang -g dmfreespace.m -F/System/Library/PrivateFrameworks/ -framework Foundation
* -framework DiskArbitration -framework DiskManagement -o dmfreespace
*/
#import <Foundation/Foundation.h>
#import "DiskManagement.h"
#import <DiskArbitration/DADisk.h>
// For statfs
#include <sys/param.h>
#include <sys/mount.h>
int main(int argc, char *argv[])
{
int err = 0;
const char * bsdName;
DASessionRef session;
DADiskRef disk;
CFDictionaryRef descDict;
NSString *. rootPath = @"/";
session = NULL;
disk = NULL;
descDict = NULL;
// Get the BSD name for the given path
struct statfs devStats;
statfs([rootPath UTF8String], &devStats);
bsdName = devStats.f_mntfromname;
NSLog(@"bsdName: %s", bsdName);
if (err == 0) {session = DASessionCreate(NULL); if (session == NULL) {err = EINVAL;}}
if (err == 0) {disk = DADiskCreateFromBSDName(NULL, session, bsdName); if (disk == NULL) {err = EINVAL;}}
if (err == 0) {descDict = DADiskCopyDescription(disk); if (descDict == NULL) {err = EINVAL;}}
DMManager *dmMan = [DMManager sharedManager];
NSLog(@"blockSizeForDisk: %@", [dmMan blockSizeForDisk:disk error:nil]);
NSLog(@"totalSizeForDisk: %@", [dmMan totalSizeForDisk:disk error:nil]);
NSLog(@"volumeTotalSizeForDisk: %@", [dmMan volumeTotalSizeForDisk:disk error:nil]);
NSLog(@"volumeFreeSpaceForDisk: %@", [dmMan volumeFreeSpaceForDisk:disk error:nil]);
return 0;
}
Run the app and you should get output like the following:
$ ./dmfreespace
2017-11-26 12:35:44.945 dmfreespace[17103:2781845] bsdName: /dev/disk1s1
2017-11-26 12:35:44.969 dmfreespace[17103:2781845] blockSizeForDisk: 4096
2017-11-26 12:35:44.970 dmfreespace[17103:2781845] totalSizeForDisk: 250035572736
2017-11-26 12:35:44.971 dmfreespace[17103:2781845] volumeTotalSizeForDisk: 250035572736
2017-11-26 12:35:44.971 dmfreespace[17103:2781845] volumeFreeSpaceForDisk: 93637120000
A GitHub Gist with dmfreespace.m
and DiskManagement.h
is available here.
Cocoa
The following program freesize.m
takes five different approaches to calculate the free space on a drive. The first two examples use Cocoa's Foundation framework. The first example uses NSFileManager
and asks for the NSFileSystemFreeSize
attribute. Simple and straight forward.
The second example uses NSURL
with some more modern approaches. This example gets a URL's resources and requests the value for the key NSURLVolumeAvailableCapacityKey
. This code also makes use of NSByteCountFormatter
, which was introduced with OS X 10.8 Mountain Lion. This method is a little more useful than the original way, since this can display the total available free space, which tends to be slightly smaller than all of the free space, not all which is available to the user, since some space might be reserved for the system.
getattrlist
getattrlist
is a method I had read about in the books Advanced Mac OS X Programming and Mac OS X Internals before, but the available examples for this tool are quite sparse and seem to be rarely used. I spent several days trying to get this example to work, but the data never seemed to line up properly with the other results I was obtaining via other methods. getattrlist
behaves in a curious and obfuscated way, by returning some blob of data, but one needs to carefully construct a custom structure in which to line up against the data blob. The data I was getting often would come out like the following:
Volume size: 16229794380578291739
Free space: 15103894473735667713
Avail size: 1
This was obviously incorrect, which makes me suspect that the vol_attr
structure was not lining up properly with the returned data, and I might even need to add some sort of padding inside the structure. There are far more easier methods to obtain the free space size than by using getattrlist
.
statfs + statvfs
The final two examples are similar UNIX system calls to get file system information and statistics. These methods statfs
and statvfs
are nearly identical in how a structure is passed into a system call and then the necessary information is parsed out. The one major difference I found between these two system calls was that the f_bsize
returned different values. The statfs
structure returns a value of 4096 for f_bsize
, whereas statvfs
returns a value of 1048576, a value 256 times larger than the other one. To properly calculate the available free space using the statvfs
call, I needed to use f_frsize
instead. Multiply f_frsize
by f_bavail
and that will result in the total number of available bytes. To calculate that number in kilobytes, divide that product by 1024.
freesize.m Gist
Purgeable Space
In more current versions of macOS, if you pull up the Get Info window on a selected disk, the amount of available space might be different from the values which was calculated in the examples above. However, there is a second value which details how much space is purgeable. If you subtract the purgeable space from the available space, you should get a value very similar to the one calculated above. This purgeable space seems to come from snapshots taken by Time Machine or APFS, which are occasionally cleaned up (or purged).
References
- Mac OS X Internals
- Mac OS X 10.4 getattrlist Know File System Type
- permtype-getattrlist.m
- permtype.m
- File Ownership and ACLS
- Converting statvfs to percentage free correctly
- How to Get Available Filesystem Space on Linux: a C Function with a C++ Example
- differ in output of df and statfs
- statfs man page
- mountedVolumeURLsIncludingResourceValuesForKeys:options: reference page
- Cocoa apis reporting incorrect values for free space, what should I use?
- NSByteCountFormatter reference page
- How to get storage capacity of mac programatically?
- How to get diskutil info output in a cocoa application
- class-dump
- DiskManagement.h + dmfreespace.m Gist
- freesize.m Gist
- Mobile Time Machine and its transformation in High Sierra