I played around with F_PREALLOCATE and it does the job, thanks!
In case anybody else is looking for a working code, here is my little test app:
#import <Foundation/Foundation.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
// some hard coded test file to be created ...
#define FILE_PATH_C "/tmp/test.data"
#define FILE_PATH @FILE_PATH_C
// ... with this hard coded file size
#define FILE_SIZE (400*1024*1024) // 400 MiB
int main() {
// remove file of previous run (if any)
[[NSFileManager defaultManager] removeItemAtPath:FILE_PATH error:nil];
// (re)create file
[[NSFileManager defaultManager] createFileAtPath:FILE_PATH contents:nil attributes:nil];
NSFileHandle* hFile = [NSFileHandle fileHandleForWritingAtPath:FILE_PATH];
// resize file logically (won't allocate any blocks with APFS)
[hFile truncateFileAtOffset:FILE_SIZE];
// force allocation of all physical blocks with APFS
fstore_t store = {
.fst_posmode = F_PEOFPOSMODE,
.fst_flags = F_ALLOCATECONTIG | F_ALLOCATEALL,
.fst_length = FILE_SIZE
};
int res = fcntl(hFile.fileDescriptor, F_PREALLOCATE, &store);
if (res < 0) {
printf("fcntl error: %d -> %s\n", res, strerror(errno));
return -1;
}
[hFile synchronizeFile];
[hFile closeFile];
// print logical file size and actual physical file size on disk
struct stat s;
::stat(FILE_PATH_C, &s);
printf("File Size=%lld, Allocated=%lld\n", s.st_size, store.fst_bytesalloc);
return 0;
}
Usage of flag F_ALLOCATEALL is mandatory here. I also added the flag F_ALLOCATECONTIG to ensure it allocates all blocks as contiguous space, however it also worked without the latter, but I probably leave it there just to be sure.
What I found interesting, if you leave out the truncateFileAtOffset: call, then you end up with a physical file size of 400 MiB on disk, however the logical file size would be zero. I didn't even consider this to be possible before.
What I don't know yet is the behaviour of that fcntl() call on a HFS+ file system, whether it succeeds as with APFS, or whether it throws an error. Would be interesting to know, because obviously such a code should be written to be backward compatible with older systems which are not running APFS yet.
If somebody can try out this code on a HFS+ disk would be very much appreciated!
clang -framework Foundation foo.mm
./a.out