#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <sys/time.h>
#include <mach/mach_init.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOBSD.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/storage/IOMedia.h>
#include "GetRegistry.h"
#include "FSParticular.h"
#include "DiskArbitrationServerMain.h"
typedef enum {
kDiskTypeUnknown = 0x00,
kDiskTypeHD = 0x01,
kDiskTypeCD = 0x02,
kDiskTypeDVD = 0x04
} DiskType;
DiskType GetDiskType(io_registry_entry_t media);
extern mach_port_t ioMasterPort;
static kern_return_t openHIDService(mach_port_t io_master_port, io_connect_t *connection)
{
kern_return_t kr;
mach_port_t ev, service, iter;
kr = IOServiceGetMatchingServices(io_master_port, IOServiceMatching(kIOHIDSystemClass), &iter);
if (kr != KERN_SUCCESS) {
return kr;
}
service = IOIteratorNext(iter);
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &ev);
IOObjectRelease(service);
IOObjectRelease(iter);
if (kr != KERN_SUCCESS) {
return kr;
}
*connection = ev;
return kr;
}
static void wakeMonitor()
{
IOGPoint loc;
kern_return_t kr;
NXEvent nullEvent = {NX_NULLEVENT, {0, 0}, 0, -1, 0};
static io_connect_t io_connection = MACH_PORT_NULL;
static struct timeval last = {0};
struct timeval current;
enum { kNULLEventPostThrottle = 10 };
if (!io_connection) {
kr = openHIDService(ioMasterPort, &io_connection);
if (kr != KERN_SUCCESS) {
return;
}
}
(void) gettimeofday(¤t, NULL);
if ((current.tv_sec - last.tv_sec) < kNULLEventPostThrottle) {
return;
}
last = current;
kr = IOHIDPostEvent(io_connection, NX_NULLEVENT, loc, &nullEvent.data, FALSE, 0, FALSE);
if (kr != KERN_SUCCESS) {
return;
}
return;
}
void
GetDisksFromRegistry(io_iterator_t iter, int initialRun, int mountExisting)
{
kern_return_t kr;
io_registry_entry_t entry;
io_name_t ioMediaName;
UInt32 ioBSDUnit;
UInt64 ioSize;
int ioWhole, ioWritable, ioEjectable, ioLeaf;
DiskType diskType;
unsigned flags;
mach_port_t masterPort;
mach_timespec_t timeSpec;
timeSpec.tv_sec = (initialRun ? 1 : 10);
timeSpec.tv_nsec = 0;
IOMasterPort(bootstrap_port, &masterPort);
IOKitWaitQuiet(masterPort, &timeSpec);
while ((entry = IOIteratorNext(iter))) {
char *ioBSDName = NULL;
char *ioContent = NULL;
CFBooleanRef boolean = 0;
CFNumberRef number = 0;
CFDictionaryRef properties = 0;
CFStringRef string = 0;
io_string_t ioDeviceTreePath;
char *ioDeviceTreePathPtr;
int ejectOnLogout = 0;
kr = IORegistryEntryGetName(entry, ioMediaName);
if (KERN_SUCCESS != kr) {
dwarning(("can't obtain name for media object\n"));
goto Next;
}
kr = IORegistryEntryCreateCFProperties(entry, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, kNilOptions);
if (KERN_SUCCESS != kr) {
dwarning(("can't obtain properties for '%s'\n", ioMediaName));
goto Next;
}
assert(CFGetTypeID(properties) == CFDictionaryGetTypeID());
string = (CFStringRef) CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey));
if (!string) {
dwarning(("kIOBSDNameKey property missing for '%s'\n", ioMediaName));
goto Next;
}
assert(CFGetTypeID(string) == CFStringGetTypeID());
ioBSDName = daCreateCStringFromCFString(string);
assert(ioBSDName);
dwarning(("ioBSDName = '%s'\t", ioBSDName));
number = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOBSDUnitKey));
if (!number) {
dwarning(("\nkIOBSDUnitKey property missing for '%s'\n", ioBSDName));
goto Next;
}
assert(CFGetTypeID(number) == CFNumberGetTypeID());
if (!CFNumberGetValue(number, kCFNumberSInt32Type, &ioBSDUnit)) {
goto Next;
}
dwarning(("ioBSDUnit = %ld\t", ioBSDUnit));
string = (CFStringRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaContentKey));
if (!string) {
dwarning(("\nkIOMediaContentKey property missing for '%s'\n", ioBSDName));
goto Next;
}
assert(CFGetTypeID(string) == CFStringGetTypeID());
ioContent = daCreateCStringFromCFString(string);
assert(ioContent);
dwarning(("ioContent = '%s'\t", ioContent));
boolean = (CFBooleanRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaLeafKey));
if (!boolean) {
dwarning(("\nkIOMediaLeafKey property missing for '%s'\n", ioBSDName));
goto Next;
}
assert(CFGetTypeID(boolean) == CFBooleanGetTypeID());
ioLeaf = (kCFBooleanTrue == boolean);
dwarning(("ioLeaf = %d\t", ioLeaf));
boolean = (CFBooleanRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaWholeKey));
if (!boolean) {
dwarning(("\nkIOMediaWholeKey property missing for '%s'\n", ioBSDName));
goto Next;
}
assert(CFGetTypeID(boolean) == CFBooleanGetTypeID());
ioWhole = (kCFBooleanTrue == boolean);
dwarning(("ioWhole = %d\t", ioWhole));
boolean = (CFBooleanRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaWritableKey));
if (!boolean) {
dwarning(("\nkIOMediaWritableKey property missing for '%s'\n", ioBSDName));
goto Next;
}
assert(CFGetTypeID(boolean) == CFBooleanGetTypeID());
ioWritable = (kCFBooleanTrue == boolean);
dwarning(("ioWritable = %d\t", ioWritable));
boolean = (CFBooleanRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaEjectableKey));
if (!boolean) {
dwarning(("\nkIOMediaEjectableKey property missing for '%s'\n", ioBSDName));
goto Next;
}
assert(CFGetTypeID(boolean) == CFBooleanGetTypeID());
ioEjectable = (kCFBooleanTrue == boolean);
dwarning(("ioEjectable = %d\t", ioEjectable));
number = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaSizeKey));
if (!number) {
dwarning(("\nkIOMediaSizeKey property missing for '%s'\n", ioBSDName));
}
assert(CFGetTypeID(number) == CFNumberGetTypeID());
if (!CFNumberGetValue(number, kCFNumberLongLongType, &ioSize)) {
goto Next;
}
dwarning(("ioSize = %ld\t", (long int) ioSize));
kr = IORegistryEntryGetPath(entry, kIODeviceTreePlane, ioDeviceTreePath);
if (kr) {
ioDeviceTreePathPtr = NULL;
} else {
dwarning(("ioDeviceTreePath = '%s'\t", ioDeviceTreePath));
if (strlen(ioDeviceTreePath) < strlen("IODeviceTree:")) {
dwarning(("\nERROR: expected leading 'IODeviceTree:' in ioDeviceTreePath\n"));
ioDeviceTreePathPtr = ioDeviceTreePath;
} else {
ioDeviceTreePathPtr = ioDeviceTreePath + strlen("IODeviceTree:");
dwarning(("\ntrimmed ioDeviceTreePath = '%s'\n", ioDeviceTreePathPtr));
}
}
flags = 0;
if (!ioWritable)
flags |= kDiskArbDiskAppearedLockedMask;
if (ioEjectable)
flags |= kDiskArbDiskAppearedEjectableMask;
if (ioWhole)
flags |= kDiskArbDiskAppearedWholeDiskMask;
if (!ioLeaf)
flags |= kDiskArbDiskAppearedNonLeafDiskMask;
if (!ioSize)
flags |= kDiskArbDiskAppearedNoSizeMask;
diskType = GetDiskType(entry);
switch (diskType) {
case kDiskTypeHD:
break;
case kDiskTypeCD:
flags |= kDiskArbDiskAppearedCDROMMask;
wakeMonitor();
break;
case kDiskTypeDVD:
flags |= kDiskArbDiskAppearedDVDROMMask;
wakeMonitor();
break;
case kDiskTypeUnknown:
break;
default:
break;
}
if (diskIsInternal(entry)) {
dwarning(("\nInternal disk appeared ...\n"));
flags |= kDiskArbDiskAppearedInternal;
}
if (!shouldAutomount(entry)) {
dwarning(("\nDo not mount this entry ...\n"));
flags |= kDiskArbDiskAppearedNoMountMask;
}
if (shouldEjectOnLogout(entry)) {
dwarning(("\nEject this entry on logout ...\n"));
ejectOnLogout = 1;
}
{
DiskPtr dp;
dp = LookupDiskByIOBSDName(ioBSDName);
if (dp) {
dwarning(("%s: '%s' already exists\n", __FUNCTION__, ioBSDName));
if (dp->state != kDiskStatePostponed) {
if (mountExisting) {
if (dp->mountpoint && 0 == strcmp(dp->mountpoint, "")) {
dp->state = kDiskStateNew;
dp->approvedForMounting = 0;
}
}
}
} else {
DiskPtr disk = NewDisk(ioBSDName,
ioBSDUnit,
ioContent,
kDiskFamily_SCSI,
NULL,
ioMediaName,
ioDeviceTreePathPtr,
entry,
ownerUIDForMedia(entry),
flags,
ioSize);
if (!disk) {
LogErrorMessage("%s: NewDisk() failed!\n", __FUNCTION__);
}
if (initialRun) {
disk->state = kDiskStateNew;
disk->approvedForMounting = 1;
}
if (ejectOnLogout) {
disk->ejectOnLogout = ejectOnLogout;
}
}
}
Next:
if (properties)
CFRelease(properties);
if (ioBSDName)
free(ioBSDName);
if (ioContent)
free(ioContent);
IOObjectRelease(entry);
}
}
DiskType
GetDiskType(io_registry_entry_t media)
{
io_registry_entry_t parent = 0;
io_registry_entry_t service = media;
DiskType type = kDiskTypeUnknown;
kern_return_t kr;
while (service) {
if (IOObjectConformsTo(service, "IOCDMedia")) {
dwarning(("DiskType = CD\n"));
type = kDiskTypeCD;
break;
} else if (IOObjectConformsTo(service, "IODVDMedia")) {
dwarning(("DiskType = DVD\n"));
type = kDiskTypeDVD;
break;
} else if (IOObjectConformsTo(service, "IOBlockStorageDevice")) {
dwarning(("DiskType = HD\n"));
type = kDiskTypeHD;
break;
}
kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS)
break;
if (service != media)
IOObjectRelease(service);
service = parent;
}
if (service != media)
IOObjectRelease(service);
return type;
}
int
shouldAutomount(io_registry_entry_t media)
{
io_registry_entry_t parent = 0;
io_registry_entry_t parentsParent = 0;
io_registry_entry_t service = media;
kern_return_t kr;
int mount = 1;
kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS)
return mount;
while (parent) {
kr = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parentsParent);
if (kr != KERN_SUCCESS)
break;
{
CFBooleanRef autodiskmountRef = IORegistryEntryCreateCFProperty(parent, CFSTR("autodiskmount"), kCFAllocatorDefault, kNilOptions);
if (autodiskmountRef) {
assert(CFGetTypeID(autodiskmountRef) == CFBooleanGetTypeID());
if (!(kCFBooleanTrue == autodiskmountRef)) {
mount = 0;
break;
}
CFRelease(autodiskmountRef);
}
}
if (parent)
IOObjectRelease(parent);
parent = parentsParent;
parentsParent = 0;
}
if (parent)
IOObjectRelease(parent);
if (parentsParent)
IOObjectRelease(parentsParent);
return mount;
}
int diskIsInternal(io_registry_entry_t media)
{
io_registry_entry_t parent = 0;
io_registry_entry_t parentsParent = 0;
io_registry_entry_t service = media;
kern_return_t kr;
int isInternal = 0;
kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS)
return 1;
while (parent) {
kr = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parentsParent);
if (kr != KERN_SUCCESS)
break;
if (IOObjectConformsTo(parent, "IOBlockStorageDevice"))
{
CFDictionaryRef characteristics = IORegistryEntryCreateCFProperty(parent, CFSTR("Protocol Characteristics"), kCFAllocatorDefault, kNilOptions);
if (characteristics) {
CFStringRef connection;
connection = (CFStringRef) CFDictionaryGetValue(characteristics, CFSTR("Physical Interconnect Location"));
if (connection) {
CFComparisonResult result;
assert(CFGetTypeID(connection) == CFStringGetTypeID());
result = CFStringCompare(connection, CFSTR("Internal"), NULL);
if (result == kCFCompareEqualTo) {
isInternal = 1;
}
}
CFRelease(characteristics);
}
break;
}
if (parent)
IOObjectRelease(parent);
parent = parentsParent;
parentsParent = 0;
}
if (parent)
IOObjectRelease(parent);
if (parentsParent)
IOObjectRelease(parentsParent);
return isInternal;
}
int
shouldEjectOnLogout(io_registry_entry_t media)
{
io_registry_entry_t parent = 0;
io_registry_entry_t parentsParent = 0;
io_registry_entry_t service = media;
kern_return_t kr;
int eject = 0;
kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS)
return eject;
while (parent) {
kr = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parentsParent);
if (kr != KERN_SUCCESS)
break;
{
CFBooleanRef ejectRef = IORegistryEntryCreateCFProperty(parent, CFSTR("eject-upon-logout"), kCFAllocatorDefault, kNilOptions);
if (ejectRef) {
assert(CFGetTypeID(ejectRef) == CFBooleanGetTypeID());
if (kCFBooleanTrue == ejectRef) {
eject = 1;
break;
}
CFRelease(ejectRef);
}
}
if (parent)
IOObjectRelease(parent);
parent = parentsParent;
parentsParent = 0;
}
if (parent)
IOObjectRelease(parent);
if (parentsParent)
IOObjectRelease(parentsParent);
return eject;
}
int
ownerUIDForMedia(io_registry_entry_t media)
{
io_registry_entry_t parent = 0;
io_registry_entry_t parentsParent = 0;
io_registry_entry_t service = media;
kern_return_t kr;
int ownerUID = -1;
kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent);
if (kr != KERN_SUCCESS)
return ownerUID;
while (parent) {
kr = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parentsParent);
if (kr != KERN_SUCCESS)
break;
{
CFNumberRef ownerRef = IORegistryEntryCreateCFProperty(parent, CFSTR("owner-uid"), kCFAllocatorDefault, kNilOptions);
if (ownerRef) {
assert(CFGetTypeID(ownerRef) == CFNumberGetTypeID());
CFNumberGetValue(ownerRef, kCFNumberIntType, &ownerUID);
dwarning(("Owner UID found %d\n", ownerUID));
CFRelease(ownerRef);
break;
}
}
if (parent)
IOObjectRelease(parent);
parent = parentsParent;
parentsParent = 0;
}
if (parent)
IOObjectRelease(parent);
if (parentsParent)
IOObjectRelease(parentsParent);
return ownerUID;
}