简体   繁体   中英

Get block file size on MacOs with statfs

I want to find out what is block file size under MacOs. In my example I try to determine /dev/disk0:

diskutil info /dev/disk0
   Device Identifier:         disk0
   Device Node:               /dev/disk0
   Whole:                     Yes
   Part of Whole:             disk0
   Device / Media Name:       APPLE SSD SD0128F

   Volume Name:               Not applicable (no file system)
   Mounted:                   Not applicable (no file system)
   File System:               None

   Content (IOContent):       GUID_partition_scheme
   OS Can Be Installed:       No
   Media Type:                Generic
   Protocol:                  PCI
   SMART Status:              Verified

   Disk Size:                 121.3 GB (121332826112 Bytes) (exactly 236978176 512-Byte-Units)

Diskutil gives correct size "123.3 GB". Now the same thing using statfs:( https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html#//apple_ref/doc/man/2/statfs ):

#include <iostream>

#include <sys/param.h>
#include <sys/mount.h>

int main()
{
        struct statfs s;
        statfs("/dev/disk0", &s);
        std::cout << s.f_bsize * s.f_blocks << " B\n";
}

Output: 196096 B

The documentation of statfs is as follows:

Statfs() returns information about a mounted file system. Path is the path name of any file within the mounted file system. Buf is a pointer to a statfs or statfs64 structure defined as follows...

You are passing /dev/disk0 as the path, which will give you the size of whatever filesystem represents /dev , which is likely some sort of devtmpfs. Your path should be a filename or path within the filesystem held on /dev/disk0 , not the block device itself.

For an arbitrary block device:

Issue IOCTLs as documented :

int fd = open("/dev/whatever", O_RDONLY);
if(fd  < 0) {
    // error handling
}
uint64_t count;
if(ioctl(fd, DKIOCGETBLOCKCOUNT, &count) < 0) {
    // error handling
}
uint32_t bsize;
if(ioctl(fd, DKIOCGETBLOCKSIZE, &bsize) < 0) {
    // error handling
}
return count * bsize;

OK, the original question is weird. Since as nanofarad already stated that we should pass statfs with the path name of any file or directory within the mounted file system instead of the device's bsdname(like /dev/disk1s1).

I saw the the question from Matt:

A way to get the block or "fragment" size for the device on a given path is on (usually from statfs on Linux, but relevant fields seem to be missing on Darwin?).

I presume you are asking "Given a file path, how to get the block size of its physical device?"

Well if so, macOS has a high-level API for this. It's the DiskArbitration framework.

It has a function DADiskCreateFromVolumePath which can get Disk object from any path. Apple Document Here

So to get the block size for the device on a given path is on, you can use below code.

#include <iostream>
#include <DiskArbitration/DADisk.h> //Import DiskArbitration.framework&CoreFoundation.framework in xcode first
#include <DiskArbitration/DASession.h>


#define PATH "/"

int main(int argc, const char * argv[]) {
  //Create DASession
  DASessionRef session = DASessionCreate(kCFAllocatorDefault);

  //Create URL from file path
  CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
                                             CFSTR(PATH),
                                             kCFURLPOSIXPathStyle,
                                             true);
  //Get path's physical disk
  DADiskRef diskRef = DADiskCreateFromVolumePath(kCFAllocatorDefault,
                                               session,
                                               url);
  //Acquire all disk info, see the screenshot below.
  CFDictionaryRef infoRef = DADiskCopyDescription(diskRef);


}

and below is all the info I get from the build-in apfs disk partition:

在此处输入图像描述

It has a device block size of 4096 bytes.

If you want the total size of the device, try this:

// gcc -Wall -framework Foundation -framework DiskArbitration disk_size.m -o disk_size
// Call method : ./disk_size /dev/disk0
#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
    DASessionRef session = DASessionCreate(NULL);

    DADiskRef disk = DADiskCreateFromBSDName(NULL, session, argv[1]);

    CFDictionaryRef diskDescription = DADiskCopyDescription(disk);
    NSDictionary *diskData = (NSDictionary *)diskDescription;
    NSString *diskSizeKey = (NSString *)kDADiskDescriptionMediaSizeKey;
    unsigned long size = [[diskData objectForKey:diskSizeKey] unsignedLongValue];
    NSLog(@"size in bytes = %lu", size);

    CFRelease(diskDescription);
    CFRelease(disk);
    CFRelease(session);

    exit(EXIT_SUCCESS);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM