I want to mount an APFS snapshot using code running on macOS, using the equivalent of this tool invocation:
mount_apfs -o ro,nobrowse -s snapshot_name snapshot_volume destpath
As there's no mount_apfs()
function, I'm stuck with mount()
. I also cannot use fs_snapshot_mount()
because that requires root permissions, and my app doesn't have those.
man 2 mount
states:
int mount(const char *type, const char *dir, int flags, void *data);
Data is a pointer to a structure that contains the type specific
arguments to mount. The format for these argument structures is
described in the manual page for each filesystem.
The problem is that I am unable to find a definition for this data
structure for APFS. E.g., where's that "manual page"?
In short, how do I pass the root path of the volume and its snapshot name to the mount()
function?
The structure can be found at: mount_args.h from palera1n/jbinit jailbreak repository
struct apfs_mount_args {
#ifndef KERNEL
char* fspec; /* path to device to mount from */
#endif
uint64_t apfs_flags; /* The standard mount flags, OR'd with apfs-specific flags (APFS_FLAGS_* above) */
uint32_t mount_mode; /* APFS_MOUNT_* */
uint32_t pad1; /* padding */
uint32_t unk_flags; /* yet another type some sort of flags (bitfield), possibly volume role related */
union {
char snapshot[256]; /* snapshot name */
struct {
char tier1_dev[128]; /* Tier 1 device (Fusion mount) */
char tier2_dev[128]; /* Tier 2 device (Fusion mount) */
};
};
void* im4p_ptr;
uint32_t im4p_size;
uint32_t pad2; /* padding */
void* im4m_ptr;
uint32_t im4m_size;
uint32_t pad3; /* padding */
uint32_t cryptex_type; /* APFS_CRYPTEX_TYPE_* */
int32_t auth_mode; /* APFS_AUTH_ENV_* */
uid_t uid;
gid_t gid;
}__attribute__((packed, aligned(4)));
With that the call to mount looks like this:
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("call with snapshot name and mount-point\n");
return 0;
}
char* snapshotName = argv[1];
char* mountPath = argv[2];
struct apfs_mount_args mnt_args = {};
mnt_args.apfs_flags = (MNT_RDONLY | MNT_DONTBROWSE);
mnt_args.mount_mode = APFS_MOUNT_SNAPSHOT;
mnt_args.unk_flags = 0x0100003F; // no idea what that is
mnt_args.uid = getuid();
mnt_args.gid = getgid();
strcpy(mnt_args.snapshot, snapshotName);
int result = mount("apfs", mountPath, MNT_RDONLY | MNT_DONTBROWSE, &mnt_args);
if (result == 0)
printf("success!\n");
else
printf("error: %i - %s\n",errno,strerror(errno));
return 1;
}
The snapshot name is part of the structure, there're only 256 bytes reserved for it.
The unk_flags
are unknown flags and i don't know why these specific flags are required, but that's what's being passed when you use the arguments that you provided in your question.
What surprises me a bit is the lack of source-volume. In theory that's the first member of the struct apfs_mount_args
but in practice it's a null-pointer.