Disk Arbitration Frameworkでディスクのマウントを抑制する

物理HDDをVirtualBoxのVMに割り当てて使用中、Mac OSからそのHDDをマウントされると非常に不味いので、マウントを抑制するコマンドを作ってみた。

引数にBSDデバイス名(/dev/disk5とか)を渡してコマンドを実行すると、Mac OS側からのマウントをブロックする。デバイス名は前方一致で検索しているので、/dev/disk5と書けばdisk5全体が、/dev/disk5s1と書けばdisk5のスライス1がブロック対象となる。終了はCtrl-Cで。

どこか処理が不味いようで、何かの拍子にコマンドを終了してもFinderからはマウント不能?と認識されてしまうことがある(実際はマウントされているのでopenコマンドを使えば開ける)。こうなると再起動するしかなくなる…。AS ISでおながいします。

blockMount.c
// -*- coding: utf-8-unix -*-
// ---------------------------------------------------------
//  Block Mount Utility
//  Copyright (c) 2013 Decomo
// ---------------------------------------------------------
//  License:
//      2-clause BSD license
//  Reference:
//      https://github.com/nanoant/mountblockd
//      http://stackoverflow.com/questions/3720503/how-can-i-prevent-ejection-of-a-disk-during-an-operation-on-mac-os-x
//      http://superuser.com/questions/336455/mac-lion-fstab-is-deprecated-so-what-replaces-it-to-prevent-a-partition-from-m
//      http://www.geekpage.jp/blog/?id=2011/10/4/3
//      http://developer.apple.com/library/mac/#documentation/Darwin/Reference/DiscArbitrationFramework/
//  Compile:
//      cc BlockMount.c -g -o BlockMount -framework CoreFoundation -framework DiskArbitration
// ---------------------------------------------------------
 
#include <CoreFoundation/CoreFoundation.h>
#include <DiskArbitration/DiskArbitration.h>
#include <stdio.h>
#include <stdlib.h>
 
const char **g_blockDeviceList = NULL;
int g_blockDeviceListCount = 0;
int g_verbose = 0;
int g_run = 1;
 
DADissenterRef diskWillMount(DADiskRef disk, void *context)
{
    const char *devName = DADiskGetBSDName(disk);
    DADissenterRef dissenter = NULL;
 
    if (devName)
    {
        int block=0;
        for (int i=0; i<g_blockDeviceListCount; i++)
        {
            if (strstr(devName, g_blockDeviceList[i]))
                block=1;
        }
        if (block)
        {
            if (g_verbose)
                fprintf(stderr, "BLOCKED mount `%s'\n", devName);
            dissenter = DADissenterCreate(kCFAllocatorDefault, kDAReturnNotPermitted, NULL);
        }
    }
 
    return dissenter;
}
 
void signalHandler(int sig)
{
    switch (sig) {
    case SIGHUP:
        if (g_verbose)
            fprintf(stderr, "received SIGHUP, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    case SIGTERM:
        if (g_verbose)
            fprintf(stderr, "received SIGTERM, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    case SIGINT:
        if (g_verbose)
            fprintf(stderr, "received SIGINT, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    case SIGQUIT:
        if (g_verbose)
            fprintf(stderr, "received SIGQUIT, terminating...\n");
        CFRunLoopStop(CFRunLoopGetCurrent());
        g_run=0;
        break;
    default:
        fprintf(stderr, "unhandled signal (%d) %s\n", sig, strsignal(sig));
        break;
    }
}
 
int main(int argc, const char *argv[])
{
    int usage=1;
    if (argc >= 2)
    {
        usage=0;
        if (strcmp(argv[1], "-v") == 0)
        {
            g_verbose=1;
            g_blockDeviceList = &argv[2];
            g_blockDeviceListCount = argc-2;
            if (argc == 2)
            {
                usage=1;
            }
        }
        else
        {
            g_blockDeviceList = &argv[1];
            g_blockDeviceListCount = argc-1;
        }
    }
 
    if (usage)
    {
        fprintf(stderr, "usage: %s [-v] deviceName1 [deviceName2] [deviceName3] [...] \n", argv[0]);
        return EXIT_FAILURE;
    }
 
    fprintf(stderr, "blocking mount device(s) : ");
    for (int i=0; i<g_blockDeviceListCount; i++)
    {
        fprintf(stderr, "%s ", g_blockDeviceList[i]);
    }
    fprintf(stderr, "\n");
 
    signal(SIGHUP,  signalHandler);
    signal(SIGTERM, signalHandler);
    signal(SIGINT,  signalHandler);
    signal(SIGQUIT, signalHandler);
 
    DAApprovalSessionRef session = DAApprovalSessionCreate(kCFAllocatorDefault);
    if (!session)
    {
        fprintf(stderr, "failed to create DAApprovalSession.\n");
        return EXIT_FAILURE;
    }
    else
    {
        DARegisterDiskMountApprovalCallback(session, NULL, diskWillMount, NULL);
        DAApprovalSessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
 
        while (g_run)
        {
            CFRunLoopRun();
        }
 
        DAApprovalSessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        DAUnregisterApprovalCallback(session, diskWillMount, NULL);
        CFRelease(session);
    }
 
    return 0;
}

とまぁ、とりあえず作ってはみたものの、結局仮想マシンはVMware Fusionを常用してるので出番がないっていう。何だかんだでVirtualBoxは不安定なんだよねぇ。8 CPU以上使えるのは魅力的なんだけど。