Section 11.2. Disk Arbitration


11.2. Disk Arbitration

The Mac OS X Disk Arbitration subsystem manages disks and disk images. It consists of the disk arbitration daemon (diskarbitrationd) and a framework (DiskArbitration.framework). diskarbitrationd is the central authority for disk management. Its duties include the following:

  • Processing newly appearing disks to possibly mount any volumes on them

  • Notifying its clients of the appearance or disappearance of disks and volumes

  • Acting as the arbiter for the claiming and unclaiming of disks by its clients

Figure 114 shows a simplified overview of the interactions in the Disk Arbitration subsystem. diskarbitrationd registers for several types of notifications to learn of the appearance and disappearance of disks, unmounting of file systems, and configuration changes. Based on these notifications, it performs actions such as automatic mounting of an incoming disk device's volumes under the default mount point folder (/Volumes/).

Figure 114. An overview of disk arbitration


The Disk Arbitration API provided by the Disk Arbitration framework can be used to access and manipulate disk objects, which are abstractions of disk devices. The following are examples of this API's functions:

  • DADiskMount() mount the volume at the given disk object

  • DADiskUnmount() unmount the volume at the given disk object

  • DADiskEject() eject the given disk object

  • DADiskRename() rename the volume at the given disk object

  • DADiskSetOptions() set or clear disk options

  • DADiskGetOptions() get disk options

  • DADiskCopyDescription() retrieve a disk object's latest description, as maintained by diskarbitrationd

  • DADiskClaim() claim the given disk object for exclusive use

  • DADiskUnclaim() release claim on the given disk object

All DADisk* functions operate on a disk object, which can be created by calling DADiskCreateFromBSDName() or DADiskCreateFromIOMedia(). The former accepts a disk's BSD device name, whereas the latter accepts an I/O Kit media object.

Client programs can also register several types of callbacks with Disk Arbitration. In particular, clients can use approval callbacks to participate in the approval or denial of operations such as mounting, unmounting, and ejecting of disk devices. The callback registration functions are all of the form DARegisterDisk <type>Callback(), where <type> can be Appeared, Disappeared, DescriptionChanged, Peek, MountApproval, UnmountApproval, or EjectApproval.

The Volume Status Database

Mac OS X uses volume UUIDs to track the status of on-disk permissions on removable volumes. The volume status database (vsdb), which is stored in /var/db/volinfo.database, maintains this information. The vsdbutil command-line program can be used to enable or disable permissions on a volume. Besides updating the vsdb entry of the given volume, vsdbutil executes the mount command with the -u option to change the status of the corresponding mounted file system.


Let us now look at some examples of working with disk devices using the Disk Arbitration framework. We will also see an example of how to receive device appearance and disappearance notifications directly from the I/O Kit.

11.2.1. Retrieving a Disk's Description

The program shown in Figure 115 calls DACopyDiskDescription() to obtain and display the description of the given BSD device (or /dev/disk0, if none was specified).

Figure 115. Using Disk Arbitration to obtain a disk's description

// diskarb_info.c #include <unistd.h> #include <DiskArbitration/DiskArbitration.h> #define DEFAULT_DISK_NAME "/dev/disk0" int printDictionaryAsXML(CFDictionaryRef dict) {     CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,                                                 (CFPropertyListRef)dict);     if (!xml)         return -1;     write(STDOUT_FILENO, CFDataGetBytePtr(xml), CFDataGetLength(xml));     CFRelease(xml);     return 0; } #define OUT_ON_NULL(ptr, msg) \     if (!ptr) { fprintf(stderr, "%s\n", msg); goto out; } int main(int argc, char **argv) {     int              ret      = -1;     DASessionRef     session  = NULL;     DADiskRef        disk     = NULL;     CFDictionaryRef  diskInfo = NULL;     char            *diskName = DEFAULT_DISK_NAME;     // create a new Disk Arbitration session     session = DASessionCreate(kCFAllocatorDefault);     OUT_ON_NULL(session, "failed to create Disk Arbitration session");     if (argc == 2)         diskName = argv[1];     // create a new disk object from the given BSD device name     disk = DADiskCreateFromBSDName(kCFAllocatorDefault, session, diskName);     OUT_ON_NULL(disk, "failed to create disk object");     // obtain disk's description     diskInfo = DADiskCopyDescription(disk);     OUT_ON_NULL(diskInfo, "failed to retrieve disk description");     ret = printDictionaryAsXML(diskInfo); out:     if (diskInfo)         CFRelease(diskInfo);     if (disk)         CFRelease(disk);     if (session)         CFRelease(session);     exit(ret); } $ gcc -Wall -o diskarb_info diskarb_info.c \     -framework DiskArbitration -framework CoreFoundation $ ./diskarb_info ... <dict>         <key>DAAppearanceTime</key>         <real>151748243.60000801</real>         <key>DABusName</key>         <string>k2-sata</string>         <key>DABusPath</key>         <string>IODeviceTree:sata/k2-sata@1</string>         <key>DADeviceInternal</key>         <true/>         ...         <key>DADeviceProtocol</key>         <string>ATA</string>         <key>DADeviceRevision</key>         <string>V36OA63A</string>         <key>DADeviceUnit</key>         <integer>0</integer>         ...

11.2.2. Participating in Disk Mounting Decisions

The program in Figure 116 registers a mount-approval callback with Disk Arbitration. Thereafter, when a device is to be mounted, the callback function can either allow the mount to proceed by returning NULL or cause it to fail by returning a reference to a dissenter object (DADissenterRef). Our example runs for a limited time, after which it deregisters the callback. While the program is running, Disk Arbitration will not be permitted to mount any disk devices.[4]

[4] It will still be possible to mount devices manuallyby running the mount command, for example.

Figure 116. Expressing dissent against a mount operation

// dissent_mount.c #include <DiskArbitration/DiskArbitration.h> #define OUT_ON_NULL(ptr, msg) \     if (!ptr) { fprintf(stderr, "%s\n", msg); goto out; } DADissenterRef mountApprovalCallback(DADiskRef disk, void *context) {     DADissenterRef dissenter = DADissenterCreate(kCFAllocatorDefault,                                                  kDAReturnNotPermitted,                                                  CFSTR("mount disallowed"));     printf("%s: mount disallowed\n", DADiskGetBSDName(disk));     return dissenter; } int main(void) {     DAApprovalSessionRef session = DAApprovalSessionCreate(kCFAllocatorDefault);     OUT_ON_NULL(session, "failed to create Disk Arbitration session");     DARegisterDiskMountApprovalCallback(session,                                         NULL,  // matches all disk objects                                         mountApprovalCallback,                                         NULL); // context     DAApprovalSessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(),                                          kCFRunLoopDefaultMode);     CFRunLoopRunInMode(kCFRunLoopDefaultMode, 30 /* seconds */, false);     DAApprovalSessionUnscheduleFromRunLoop(session, CFRunLoopGetCurrent(),                                          kCFRunLoopDefaultMode);     DAUnregisterApprovalCallback(session, mountApprovalCallback, NULL); out:    if (session)         CFRelease(session);     exit(0); } $ gcc -Wall -o dissent_mount dissent_mount.c \     -framework DiskArbitration -framework CoreFoundation $ ./dissent_mount         # another shell         $ open /tmp/somediskimage.dmg disk10s2: mount disallowed ...

11.2.3. Receiving Media Notifications from the I/O Kit

The program in Figure 117 requests the I/O Kit to send it notifications when removable storage devices appear or disappear. More precisely, appearance means that the IOService matching the given matching dictionary has had all relevant drivers probed and started. Similarly, disappearance means that the IOService has terminated. Our example's matching dictionary looks for all IOMedia objects. Optionally, we could refine the dictionary by adding other key-value pairs using CFDictionaryAddValue(). For example, the following will limit matching to only whole media devices (and not partitions):

... CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); ...


Figure 117. Monitoring the appearance and disappearance of storage devices

// mediamon.c #include <unistd.h> #include <IOKit/IOKitLib.h> #include <IOKit/storage/IOMedia.h> #include <CoreFoundation/CoreFoundation.h> int printDictionaryAsXML(CFDictionaryRef dict) {     CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault,                                                 (CFPropertyListRef)dict);     if (!xml)         return -1;     write(STDOUT_FILENO, CFDataGetBytePtr(xml), CFDataGetLength(xml));     CFRelease(xml);     return 0; } void matchingCallback(void *refcon, io_iterator_t deviceList) {     kern_return_t       kr;     CFDictionaryRef     properties;     io_registry_entry_t device;     // Iterate over each device in this notification.     while ((device = IOIteratorNext(deviceList))) {         // Populate a dictionary with device's properties.         kr = IORegistryEntryCreateCFProperties(                  device, (CFMutableDictionaryRef *)&properties,                  kCFAllocatorDefault, kNilOptions);         if (kr == KERN_SUCCESS)             printDictionaryAsXML(properties);         if (properties)             CFRelease(properties);         if (device)             IOObjectRelease(device);     } } int main(void) {     CFMutableDictionaryRef match;     IONotificationPortRef  notifyPort;     CFRunLoopSourceRef     notificationRunLoopSource;     io_iterator_t          notificationIn, notificationOut;     // Create a matching dictionary for all IOMedia objects.     if (!(match = IOServiceMatching("IOMedia"))) {         fprintf(stderr, "*** failed to create matching dictionary.\n");         exit(1);     }     // Create a notification object for receiving I/O Kit notifications.     notifyPort = IONotificationPortCreate(kIOMasterPortDefault);     // Get a CFRunLoopSource that we will use to listen for notifications.     notificationRunLoopSource = IONotificationPortGetRunLoopSource(notifyPort);     // Add the CFRunLoopSource to the default mode of our current run loop.     CFRunLoopAddSource(CFRunLoopGetCurrent(), notificationRunLoopSource,                        kCFRunLoopDefaultMode);     // One reference of the matching dictionary will be consumed when we install     // a notification request. Since we need to install two such requests (one     // for ejectable media coming in and another for it going out), we need     // to increment the reference count on our matching dictionary.     CFRetain(match);     // Install notification request for matching objects coming in.     // Note that this will also look up already existing objects.     IOServiceAddMatchingNotification(         notifyPort,             // notification port reference         kIOMatchedNotification, // notification type         match,                  // matching dictionary         matchingCallback,       // this is called when notification fires         NULL,                   // reference constant         &notificationIn);       // iterator handle     // Install notification request for matching objects going out.     IOServiceAddMatchingNotification(         notifyPort,         kIOTerminatedNotification,         match,         matchingCallback,         NULL,         &notificationOut);     // Invoke callbacks explicitly to empty the iterators/arm the notifications.     matchingCallback(0, notificationIn);     matchingCallback(0, notificationOut);     CFRunLoopRun(); // run     exit(0); } $ gcc -Wall -o mediamon mediamon.c -framework IOKit -framework CoreFoundation $ ./mediamon         # some disk is attached, probed for volumes, and a volume is mounted ... <dict>         <key>BSD Major</key>         <integer>14</integer>         <key>BSD Minor</key>         <integer>3</integer>         <key>BSD Name</key>         <string>disk0s3</string> ...

A notification we receive will provide us an iterator (io_iterator_t) containing one or more I/O Registry entries (io_registry_entry_t), each corresponding to a device. We will fetch and display the properties of each device.




Mac OS X Internals. A Systems Approach
Mac OS X Internals: A Systems Approach
ISBN: 0321278542
EAN: 2147483647
Year: 2006
Pages: 161
Authors: Amit Singh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net