DiskArbitrationServerMain.c [plain text]
#include <libc.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/message.h>
#include <servers/bootstrap.h>
#include <mach/mach_error.h>
#include <pthread.h>
#include <assert.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <dev/disk.h>
#include <bsd/grp.h>
#include <bsd/pwd.h>
#include <IOKit/OSMessageNotification.h>
#include <IOKit/IOKitLib.h>
#include "ServerToClient.h"
#include "ClientToServer.h"
#include "DiskArbitrationTypes.h"
#include "GetRegistry.h"
#include "FSParticular.h"
#include <mach/mach_interface.h>
#include <IOKit/IOBSD.h>
#include <IOKit/storage/IOMedia.h>
#include <syslog.h>
#include "DiskArbitrationServerMain.h"
#include "DiskVolume.h"
mach_port_t ioMasterPort;
void autodiskmount(int ownership);
uid_t requestorUID;
gid_t requestorGID;
mach_port_t requestingClientPort;
int DA_postponedDisksExist;
int DA_stateChangedToNew;
#ifdef DEBUG
int gDebug = 1;
#else
int gDebug = 0;
#endif
int gDaemon;
extern void ClientDeath(mach_port_t clientPort);
extern boolean_t ClientToServer_server(mach_msg_header_t * msg, mach_msg_header_t * reply);
enum {
kMsgSize = 2048, };
static char * programName = NULL;
mach_port_t gNotifyPort = MACH_PORT_NULL;
static int gBlueBoxBootVolume = -1;
static kern_return_t InitNotifyPort(void);
static mach_port_t GetNotifyPort(void);
static boolean_t MessageIsNotificationDeath(const mach_msg_header_t *msg, mach_port_t *deadPort);
static int NextSequenceNumber( void );
struct IONotificationMsg {
mach_msg_header_t msgHdr;
OSNotificationHeader notifyHeader;
mach_msg_trailer_t trailer;
};
CFStringRef yankedHeader;
CFStringRef yankedMessage;
CFStringRef unrecognizedHeader;
CFStringRef unrecognizedHeaderNoInitialize;
CFStringRef unrecognizedMessage;
CFStringRef ejectString;
CFStringRef ignoreString;
CFStringRef initString;
CFStringRef launchString;
CFStringRef someDisk;
CFStringRef mountOrFsckFailed;
CFStringRef unknownString;
CFStringRef mountOrFsckFailedWithDiskUtility;
typedef struct IONotificationMsg IONotificationMsg, * IONotificationMsgPtr;
static void HandleIONotificationMsg( IONotificationMsgPtr ioNotificationMsgPtr );
void * YankedDiskThread(void * args)
{
SInt32 retval = ERR_SUCCESS;
CFURLRef daFrameworkURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, CFSTR("/System/Library/PrivateFrameworks/DiskArbitration.framework"), kCFURLPOSIXPathStyle, TRUE);
retval = CFUserNotificationDisplayNotice(0.0, kCFUserNotificationStopAlertLevel, NULL, NULL, daFrameworkURL, yankedHeader, yankedMessage, NULL);
CFRelease(daFrameworkURL);
return NULL;
}
void StartYankedDiskMessage()
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, YankedDiskThread, nil);
pthread_attr_destroy(&attr);
}
static kern_return_t InitNotifyPort(void)
{
kern_return_t r;
r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &gNotifyPort);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return MACH_PORT_NULL;
}
dwarning(("%s: gNotifyPort = $%08x\n", programName, (int)gNotifyPort));
return r;
}
static mach_port_t GetNotifyPort(void)
{
return gNotifyPort;
}
static boolean_t MessageIsNotificationDeath(const mach_msg_header_t *msg, mach_port_t *deadPort)
{
boolean_t result;
if ( GetNotifyPort() == msg->msgh_local_port )
{
if ( MACH_NOTIFY_DEAD_NAME == msg->msgh_id )
{
if ( deadPort != NULL )
{
const mach_dead_name_notification_t *deathMessage = (const mach_dead_name_notification_t *)msg;
*deadPort = deathMessage->not_port;
}
result = TRUE;
goto Return;
}
else if ( INTERNAL_MSG == msg->msgh_id)
{
dwarning(("Internal message received"));
result = FALSE;
goto Return;
}
else
{
LogErrorMessage("(%s:%d) received unrecognized message (id=0x%x) on notify port\n", __FILE__, __LINE__, (int)msg->msgh_id);
result = FALSE;
goto Return;
}
}
else
{
result = FALSE;
goto Return;
}
Return:
return result;
}
void LogErrorMessage(const char *format, ...)
{
va_list ap;
va_start(ap, format);
openlog(programName, LOG_PID | LOG_CONS, LOG_DAEMON);
vsyslog(LOG_ERR, format, ap);
closelog();
vfprintf(stderr, format, ap);
va_end(ap);
}
static void HandleIONotificationMsg( IONotificationMsgPtr msg )
{
kern_return_t r;
unsigned long int notifyType;
unsigned long int ref;
r = OSGetNotificationFromMessage(
&msg->msgHdr, 0, ¬ifyType, &ref, 0, 0 ); if ( r )
{
dwarning(("%s(%d): OSGetNotificationFromMessage returned error %d\n", __FUNCTION__, __LINE__, (int)r));
goto Return;
}
dwarning(("got notification, type=%d(%s), local=%d, remote=%d\n",
(int)notifyType, (char*)ref,
(int)msg->msgHdr.msgh_local_port,
(int)msg->msgHdr.msgh_remote_port ));
if (notifyType != 102) { GetDisksFromRegistry( (io_iterator_t) msg->msgHdr.msgh_remote_port, 0 );
autodiskmount(FALSE);
} else {
io_iterator_t iter = (io_iterator_t) msg->msgHdr.msgh_remote_port;
io_registry_entry_t entry;
while ( entry = IOIteratorNext( iter ) )
{
kern_return_t kr;
CFDictionaryRef properties = 0; CFStringRef string = 0; io_name_t ioMediaName;
char * ioBSDName = NULL;
DiskPtr dp;
kr = IORegistryEntryGetName(entry, ioMediaName);
if ( KERN_SUCCESS != kr )
{
dwarning(("can't obtain name for media object\n"));
continue;
}
kr = IORegistryEntryCreateCFProperties(entry, &properties, kCFAllocatorDefault, kNilOptions);
if ( KERN_SUCCESS != kr )
{
dwarning(("can't obtain properties for '%s'\n", ioMediaName));
continue;
}
assert(CFGetTypeID(properties) == CFDictionaryGetTypeID());
string = (CFStringRef) CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey));
if (!string) {
dwarning(("can't obtain properties for ioBSDName\n"));
continue;
}
ioBSDName = daCreateCStringFromCFString(string);
dp = LookupDiskByIOBSDName( ioBSDName );
if (dp != NULL) { DiskPtr wholeDiskPtr = LookupWholeDiskForThisPartition( dp );
if (dp->mountpoint && strlen(dp->mountpoint)) {
if (wholeDiskPtr && !wholeDiskPtr->wholeDiskHasBeenYanked) {
wholeDiskPtr->wholeDiskHasBeenYanked++;
StartYankedDiskMessage();
}
}
kr = UnmountDisk( dp, TRUE );
SendUnmountPostNotifyMsgsForOnePartition( dp->ioBSDName, 0, 0 );
SendEjectPostNotifyMsgsForOnePartition( dp->ioBSDName, 0, 0 );
if (!dp->wholeDiskHasBeenYanked) {
FreeDisk( dp );
}
}
IOObjectRelease(entry);
if ( properties ) CFRelease( properties );
if ( ioBSDName ) free( ioBSDName );
}
}
Return:
return;
}
ClientPtr NewClient( mach_port_t port, unsigned pid, unsigned flags )
{
ClientPtr result;
result = (ClientPtr) malloc( sizeof( * result ) );
if ( result == NULL )
{
dwarning(("%s(port = $%08x, pid = %d, flags = $%08x): malloc failed!\n", __FUNCTION__, port, pid, flags));
goto Return;
}
result->next = g.Clients;
g.Clients = result;
result->port = port;
result->pid = pid;
result->flags = flags;
result->state = kDiskStateNew;
result->numAcksRequired = 0;
result->notifyOnDiskTypes = 0;
result->unrecognizedPriority = 0;
result->ackOnUnrecognizedDisk = nil;
result->clientAuthRef = nil;
g.NumClients ++ ;
Return:
return result;
}
void PrintClient(ClientPtr clientPtr)
{
if ( ! clientPtr ) return;
dwarning(("port = $%08x, pid = %5d, flags = $%08x, numAcksRequired = %d\n", clientPtr->port, clientPtr->pid, clientPtr->flags, clientPtr->numAcksRequired));
}
void PrintClients(void)
{
ClientPtr clientPtr;
int i;
if ( ! gDebug ) goto Return;
dwarning(("g.NumClients = %d\n", g.NumClients));
for (clientPtr = g.Clients, i = 0; clientPtr != NULL; clientPtr = clientPtr->next, i++)
{
PrintClient( clientPtr );
}
Return:
return;
}
ClientPtr LookupClientByPID( pid_t pid )
{
ClientPtr clientPtr = nil;
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->pid == pid )
{
goto Return;
}
}
Return:
return clientPtr;
}
ClientPtr LookupClientByMachPort( mach_port_t port )
{
ClientPtr clientPtr;
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->port == port )
{
goto Return;
}
}
Return:
return clientPtr;
}
DiskPtr NewDisk( char * ioBSDName,
int ioBSDUnit,
char * ioContentOrNull,
DiskFamily family,
char * mountpoint,
char * ioMediaNameOrNull,
char * ioDeviceTreePathOrNull,
io_object_t service,
int mountingUserFromDevice,
unsigned flags )
{
DiskPtr result;
dwarning(("%s(ioBSDName = '%s', ioContentOrNull = '%s', family = %d, mountpoint = '%s', ioMediaNameOrNull = '%s', ioDeviceTreePathOrNull = '%s', flags = $%08x, owner = %d)\n",
__FUNCTION__,
ioBSDName,
ioContentOrNull ? ioContentOrNull : "(NULL)",
family,
mountpoint,
ioMediaNameOrNull ? ioMediaNameOrNull : "(NULL)",
ioDeviceTreePathOrNull ? ioDeviceTreePathOrNull : "(NULL)",
flags, mountingUserFromDevice ));
result = (DiskPtr) malloc( sizeof( * result ) );
if ( result == NULL )
{
dwarning(("%s(...): malloc failed!\n", __FUNCTION__));
goto Return;
}
bzero( result, sizeof( * result ) );
result->next = g.Disks;
g.Disks = result;
result->mountAttempted = 0;
result->ioBSDName = strdup( ioBSDName ? ioBSDName : "" );
result->ioBSDUnit = ioBSDUnit;
result->ioContent = strdup( ioContentOrNull ? ioContentOrNull : "" );
result->family = family;
result->mountpoint = strdup( mountpoint ? mountpoint : "" );
result->service = service;
result->retainingClient = 0;
result->lastClientAttemptedForUnrecognizedMessages = nil;
result->wholeDiskContainsMountedChild = 0;
result->wholeDiskHasBeenYanked = 0;
result->admCreatedMountPoint = 0;
result->ejectOnLogout = 0;
if ( ioMediaNameOrNull )
{
result->ioMediaNameOrNull = strdup( ioMediaNameOrNull );
}
else
{
result->ioMediaNameOrNull = NULL;
}
result->ioDeviceTreePath = strdup( ioDeviceTreePathOrNull ? ioDeviceTreePathOrNull : "" );
result->flags = flags;
result->sequenceNumber = ( 0 == strcmp( result->mountpoint, "" ) ) ? -1 : NextSequenceNumber();
result->state = kDiskStatePostponed;
result->ackValues = NULL;
if ( mountingUserFromDevice != -1) {
result->mountedUser = mountingUserFromDevice;
} else {
result->mountedUser = currentConsoleUser; }
result->wholeDiskContainsMountedChild = 0;
g.NumDisks ++ ;
g.NumDisksAddedOrDeleted ++;
Return:
return result;
}
boolean_t IsWhole( DiskPtr diskPtr )
{
boolean_t result;
result = ( 0 != ( diskPtr->flags & kDiskArbDiskAppearedWholeDiskMask ) );
return result;
}
boolean_t IsNetwork( DiskPtr diskPtr )
{
boolean_t result;
result = ( 0 != ( diskPtr->flags & kDiskArbDiskAppearedNetworkDiskMask ) );
return result;
}
static int NextSequenceNumber( void )
{
int maxSequenceNumber;
int result;
DiskPtr diskPtr;
dwarning(("%s...\n", __FUNCTION__));
maxSequenceNumber = -1;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( diskPtr->sequenceNumber > maxSequenceNumber )
{
maxSequenceNumber = diskPtr->sequenceNumber;
}
}
result = maxSequenceNumber + 1;
dwarning(("%s => %d\n", __FUNCTION__, result ));
return result;
}
void DiskSetMountpoint( DiskPtr diskPtr, const char * mountpoint )
{
if ( diskPtr->mountpoint )
{
free( diskPtr->mountpoint );
}
diskPtr->mountpoint = strdup( mountpoint ? mountpoint : "" );
diskPtr->sequenceNumber = NextSequenceNumber();
}
DiskPtr LookupDiskByIOBSDName( char * ioBSDName )
{
DiskPtr diskPtr;
if (!ioBSDName) {
pwarning(("%s(null ioBSDName passed! abort!)\n", __FUNCTION__));
return nil;
}
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if (!diskPtr->ioBSDName) {
continue;
}
if ( 0 == strcmp( ioBSDName, diskPtr->ioBSDName ) )
{
goto Return;
}
}
Return:
return diskPtr;
}
void FreeAllDisks( )
{
DiskPtr oldDiskPtr= g.Disks;
DiskPtr newDiskPtr= g.Disks;
while (oldDiskPtr) {
newDiskPtr = oldDiskPtr->next;
FreeDisk(oldDiskPtr);
oldDiskPtr = newDiskPtr;
}
}
int UnmountDisk( DiskPtr diskPtr, int forceUnmount )
{
int result = 0;
struct stat sb;
struct statfs mntbuf;
char cookieFile[MAXPATHLEN];
sprintf(cookieFile, "/%s/%s", diskPtr->mountpoint, ADM_COOKIE_FILE);
dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));
if ( 0 == strcmp( diskPtr->mountpoint, "" ) )
{
dwarning(("%s('%s'): disk not mounted\n", __FUNCTION__, diskPtr->ioBSDName));
SetStateForOnePartition( diskPtr, kDiskStateIdle );
goto Return;
}
{
if (statfs(diskPtr->mountpoint, &mntbuf) == 0) {
if (mntbuf.f_flags & MNT_AUTOMOUNTED) {
goto Return;
}
}
}
result = unmount( diskPtr->mountpoint, (forceUnmount ? MNT_FORCE : 0) );
if ( -1 == result )
{
result = errno;
LogErrorMessage("%s('%s') unmount('%s') failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, diskPtr->mountpoint, result, strerror(result));
SetStateForOnePartition( diskPtr, kDiskStateIdle );
goto Return;
}
if (diskPtr->admCreatedMountPoint) {
result = rmdir( diskPtr->mountpoint );
if ( -1 == result )
{
result = errno;
if (result == ENOTEMPTY) {
if (stat(cookieFile, &sb) == 0) {
if (remove(cookieFile) == 0) {
if (rmdir(diskPtr->mountpoint) != 0) {
LogErrorMessage("%s('%s') rmdir('%s') failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, diskPtr->mountpoint, result, strerror(result));
result = errno;
} else {
result = 0;
}
}
}
}
}
}
DiskSetMountpoint(diskPtr, "");
SetStateForOnePartition( diskPtr, kDiskStateNewlyUnmounted );
Return:
if ( result ) dwarning(("%s('%s') error: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, result, strerror(result)));
return result;
}
int UnmountAllPartitions( DiskPtr diskPtr, int forceUnmount )
{
int result = 0;
DiskPtr dp;
int family1, family2;
int deviceNum1, deviceNum2;
if (diskPtr->ioBSDName && strlen(diskPtr->ioBSDName)) {
dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));
}
if ( IsNetwork( diskPtr ) )
{
result = UnmountDisk( diskPtr, forceUnmount ); goto Return;
}
family1 = diskPtr->family;
deviceNum1 = diskPtr->ioBSDUnit;
for (dp = g.Disks; dp != NULL; dp = dp->next)
{
family2 = dp->family;
deviceNum2 = dp->ioBSDUnit;
if ( family1 == family2 && deviceNum1 == deviceNum2 )
{
int err;
err = UnmountDisk( dp , forceUnmount); if ( err && ! result )
{
result = err;
}
}
}
Return:
return result;
}
int EjectDisk( DiskPtr diskPtr )
{
int result = 0;
int fd = -1;
char livePartitionPathname[PATH_MAX];
DiskPtr wholeDiskPtr;
dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));
if ( IsNetwork( diskPtr ) )
{
if ( 0 == strcmp( "", diskPtr->mountpoint ) )
{
SetStateForOnePartition( diskPtr, kDiskStateNewlyEjected );
}
goto Return;
}
wholeDiskPtr = LookupWholeDiskForThisPartition( diskPtr );
if ( NULL == wholeDiskPtr )
{
result = EINVAL;
LogErrorMessage("%s('%s') can't find whole media\n", __FUNCTION__, diskPtr->ioBSDName);
SetStateForAllPartitions( diskPtr, kDiskStateIdle );
goto Return;
}
sprintf(livePartitionPathname, "/dev/r%s", wholeDiskPtr->ioBSDName);
dwarning(("%s: livePartitionPathname = '%s'\n", __FUNCTION__, livePartitionPathname));
fd = open(livePartitionPathname, O_RDONLY);
if ( -1 == fd )
{
result = errno;
LogErrorMessage("%s('%s') open('%s') failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, livePartitionPathname, result, strerror(result));
SetStateForAllPartitions( diskPtr, kDiskStateIdle );
goto Return;
}
if (diskPtr) {
SetStateForAllPartitions( diskPtr, kDiskStateNewlyEjected );
}
result = ioctl(fd, DKIOCEJECT, 0);
if ( -1 == result )
{
result = errno;
if (result == 45) { result = 0;
goto Return;
}
LogErrorMessage("%s('%s') ioctl(DKIOCEJECT) failed: %d (%s)\n", __FUNCTION__, diskPtr->ioBSDName, result, strerror(result));
SetStateForAllPartitions( diskPtr, kDiskStateIdle );
goto Return;
}
Return:
if ( fd >= 0 )
{
close( fd );
}
return result;
}
void FreeDisk( DiskPtr diskPtr )
{
DiskPtr dp;
DiskPtr * dpp;
dwarning(("%s('%s')\n", __FUNCTION__, diskPtr->ioBSDName));
for ( dpp = & g.Disks, dp = * dpp;
dp != NULL;
dpp = & (dp->next), dp = * dpp )
{
if ( dp == diskPtr )
{
*dpp = dp->next;
free( diskPtr->ioBSDName );
diskPtr->ioBSDName = NULL; free( diskPtr->ioContent );
diskPtr->ioContent = NULL; free( diskPtr->mountpoint );
diskPtr->mountpoint = NULL; if ( diskPtr->ioMediaNameOrNull)
{
free( diskPtr->ioMediaNameOrNull);
diskPtr->ioMediaNameOrNull = NULL; }
free( diskPtr->ioDeviceTreePath );
diskPtr->ioDeviceTreePath = NULL;
if ( diskPtr->mountedFilesystemName)
{
free( diskPtr->mountedFilesystemName);
diskPtr->mountedFilesystemName = NULL; }
if ( diskPtr->ackValues )
{
FreeAckValues( diskPtr->ackValues );
}
diskPtr->ackValues = NULL;
IOObjectRelease(diskPtr->service);
free( diskPtr );
g.NumDisks --;
g.NumDisksAddedOrDeleted ++;
goto Return;
}
}
Return:
return;
}
void PrintDisks(void)
{
DiskPtr diskPtr;
int i;
if ( ! gDebug ) goto Return;
dwarning(("g.NumDisks = %d\n", g.NumDisks));
dwarning(("g.NumDisksAddedOrDeleted = %d\n", g.NumDisksAddedOrDeleted));
dwarning(("%2s %4s %10s %9s %5s %8s %25s %35s %8s %40s %35s %3s\n", "#", "seq#", "BSDName", "flags", "state", "BSDUnit", "Content", "FS Type", "MediaName", "DeviceTreePath", "mountpoint", "uid"));
for (diskPtr = g.Disks, i = 0; diskPtr != NULL; diskPtr = diskPtr->next, i++)
{
dwarning(("%2d %4d %10s $%08x %5d %8d %25s %35s %8s %40s %35s %3d\n",
i,
diskPtr->sequenceNumber,
diskPtr->ioBSDName,
diskPtr->flags,
diskPtr->state,
diskPtr->ioBSDUnit,
diskPtr->ioContent,
diskPtr->mountedFilesystemName,
diskPtr->ioMediaNameOrNull ? diskPtr->ioMediaNameOrNull : "<null>",
diskPtr->ioDeviceTreePath,
diskPtr->mountpoint ? diskPtr->mountpoint : "<not mounted>",
diskPtr->mountedUser
));
PrintAckValues( diskPtr->ackValues );
}
Return:
return;
}
int IsStateNeedingAckValueTable( DiskState diskState );
int IsStateNeedingAckValueTable( DiskState diskState )
{
switch ( diskState )
{
case kDiskStateToBeEjected:
case kDiskStateToBeUnmounted:
case kDiskStateToBeUnmountedAndEjected:
return 1;
break;
default:
return 0;
break;
}
}
void SetStateForOnePartition( DiskPtr diskPtr, DiskState newState )
{
dwarning(("%s('%s',state=%s (%d))\n", __FUNCTION__, diskPtr->ioBSDName, DISKSTATE(newState),newState));
if ( diskPtr->ackValues )
{
FreeAckValues( diskPtr->ackValues );
}
diskPtr->state = newState;
if ( IsStateNeedingAckValueTable( newState ) )
{
diskPtr->ackValues = NewAckValues( NumClientsDesiringAsyncNotification() );
}
else
{
diskPtr->ackValues = NULL;
}
}
void SetStateForAllPartitions( DiskPtr diskPtr, DiskState newState )
{
DiskPtr diskPtr2;
int family1, family2;
int deviceNum1, deviceNum2;
dwarning(("%s('%s',newState=%s (%d))\n", __FUNCTION__, diskPtr->ioBSDName, DISKSTATE(newState),newState));
if ( IsNetwork( diskPtr ) )
{
SetStateForOnePartition( diskPtr, newState );
goto Return;
}
family1 = diskPtr->family;
deviceNum1 = diskPtr->ioBSDUnit;
for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
{
family2 = diskPtr2->family;
deviceNum2 = diskPtr2->ioBSDUnit;
if ( family1 == family2 && deviceNum1 == deviceNum2 )
{
SetStateForOnePartition( diskPtr2, newState );
}
}
Return:
return;
}
int NumPartitionsMountedFromThisDisk( DiskPtr diskPtr )
{
int result = 0; DiskPtr diskPtr2;
int family1, family2;
int deviceNum1, deviceNum2;
if ( IsNetwork( diskPtr ) )
{
result = ( 0 != strcmp("", diskPtr->mountpoint) );
goto Return;
}
family1 = diskPtr->family;
deviceNum1 = diskPtr->ioBSDUnit;
for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
{
family2 = diskPtr2->family;
deviceNum2 = diskPtr2->ioBSDUnit;
if ( family1 == family2 && deviceNum1 == deviceNum2 && 0 != strcmp( "", diskPtr2->mountpoint ) )
{
result ++ ;
}
}
Return:
dwarning(("%s('%s') => %d\n", __FUNCTION__, diskPtr->ioBSDName, result));
return result;
}
int NumPartitionsToBeUnmountedAndEjectedFromThisDisk( DiskPtr diskPtr )
{
int result = 0; DiskPtr diskPtr2;
int family1, family2;
int deviceNum1, deviceNum2;
if ( IsNetwork( diskPtr ) )
{
result = ( 0 != strcmp("", diskPtr->mountpoint) );
goto Return;
}
family1 = diskPtr->family;
deviceNum1 = diskPtr->ioBSDUnit;
for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
{
family2 = diskPtr2->family;
deviceNum2 = diskPtr2->ioBSDUnit;
if ( family1 == family2 && deviceNum1 == deviceNum2 && diskPtr2->state == kDiskStateToBeUnmountedAndEjected )
{
result ++ ;
}
}
Return:
dwarning(("%s('%s') => %d\n", __FUNCTION__, diskPtr->ioBSDName, result));
return result;
}
DiskPtr LookupWholeDiskForThisPartition( DiskPtr diskPtr )
{
DiskPtr diskPtr2;
int family1, family2;
int deviceNum1, deviceNum2;
if ( IsNetwork( diskPtr ) )
{
diskPtr2 = diskPtr;
goto Return;
}
family1 = diskPtr->family;
deviceNum1 = diskPtr->ioBSDUnit;
for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
{
family2 = diskPtr2->family;
deviceNum2 = diskPtr2->ioBSDUnit;
if ( family1 == family2 && deviceNum1 == deviceNum2 && IsWhole( diskPtr2 ) )
{
goto Return;
}
}
Return:
if ( diskPtr2 )
{
dwarning(("%s('%s') => '%s'\n", __FUNCTION__, diskPtr->ioBSDName, STR(diskPtr2->ioBSDName)));
}
else
{
dwarning(("%s('%s') => NULL\n", __FUNCTION__, diskPtr->ioBSDName));
}
return diskPtr2;
}
void LookupWholeDisksForThisPartition(io_registry_entry_t service, LookupWholeDisksForThisPartitionApplierFunction applier)
{
io_iterator_t parents;
kern_return_t status;
if ( IOObjectConformsTo(service, kIOMediaClass) )
{
CFBooleanRef whole = IORegistryEntryCreateCFProperty(service, CFSTR(kIOMediaWholeKey), kCFAllocatorDefault, 0);
if ( whole )
{
if ( whole == kCFBooleanTrue )
{
DiskPtr diskPtr;
for ( diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next )
{
if ( IOObjectIsEqualTo(diskPtr->service, service) )
{
(*applier)(diskPtr);
break;
}
}
}
CFRelease(whole);
}
}
else if ( IOObjectConformsTo(service, "IOBlockStorageDevice") )
{
return;
}
status = IORegistryEntryGetParentIterator(service, kIOServicePlane, &parents);
if ( status == KERN_SUCCESS )
{
while ( (service = IOIteratorNext(parents)) )
{
LookupWholeDisksForThisPartition(service, applier);
IOObjectRelease(service);
}
IOObjectRelease(parents);
}
}
DiskPtr LookupWholeDiskToBeEjected( void )
{
DiskPtr diskPtr;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( ! ( IsWhole( diskPtr) || IsNetwork( diskPtr ) ) )
{
continue;
}
if ( ! ( ( diskPtr->state == kDiskStateToBeUnmountedAndEjected ) || ( diskPtr->state == kDiskStateToBeEjected ) ) )
{
continue;
}
goto Return;
}
Return:
if ( diskPtr )
{
dwarning(("%s() => '%s' (state=%s (%d))\n", __FUNCTION__, STR(diskPtr->ioBSDName), DISKSTATE(diskPtr->state), diskPtr->state));
}
else
{
dwarning(("%s() => NULL\n", __FUNCTION__));
}
return diskPtr;
}
kern_return_t EnableDeathNotifications(mach_port_t port)
{
kern_return_t r;
mach_port_t oldPort;
dwarning(("%s($%08x)\n", __FUNCTION__, port));
r = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 0, GetNotifyPort(), MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldPort);
if ( r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) failed to set up death notifications for port %d: {0x%x} %s\n", __FILE__, __LINE__, (int)port, r, mach_error_string(r));
}
return r;
}
AckValues * NewAckValues( int size )
{
AckValue * v;
AckValues * result;
dwarning(("%s(%d)\n", __FUNCTION__, size));
v = (AckValue *)malloc( sizeof( AckValue ) * size );
if ( v == NULL )
{
result = NULL;
goto Return;
}
bzero( v, sizeof( AckValue ) * size );
result = (AckValues *)malloc( sizeof( AckValues ) );
if ( result == NULL )
{
free( v );
goto Return;
}
bzero ( result, sizeof( AckValues ) );
result->physicalLength = size;
result->logicalLength = 0;
result->ackValues = v;
Return:
return result;
}
void FreeAckValues( AckValues * ackValues )
{
dwarning(("%s(0x%08x)\n", __FUNCTION__, (unsigned)ackValues));
if ( ! ackValues ) goto Return;
if ( ackValues->ackValues )
{
free( ackValues->ackValues );
ackValues->ackValues = NULL;
}
free( ackValues );
Return:
return;
}
void ClearAckValuesForAllDisks()
{
DiskPtr diskPtr;
AckValues * p;
int i;
dwarning(("%s\n", __FUNCTION__));
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if (p = diskPtr->ackValues )
{
for (i = 0; i < p->logicalLength; i++)
{
p->ackValues[ i ].state = kAckReceived;
p->ackValues[ i ].errorCode = 0;
}
}
}
return;
}
void InitAckValue( AckValues * p, pid_t pid )
{
dwarning(("%s(0x%08x, pid=%d)\n", __FUNCTION__, (unsigned)p, (unsigned)pid));
p->ackValues[ p->logicalLength ].pid = pid;
p->ackValues[ p->logicalLength ].state = kSendMsg;
p->ackValues[ p->logicalLength ].errorCode = 0;
if ( p->logicalLength < p->physicalLength )
{
p->logicalLength ++ ;
}
else
{
LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d): error: ! ( p->logicalLength < p->physicalLength )\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid);
}
}
void UpdateAckValue( AckValues * p, pid_t pid, int errorCode )
{
int i;
dwarning(("%s(0x%08x, pid=%d, errorCode=%d)\n", __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode));
if ( ! p )
{
LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d, errorCode=%d): null pointer\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode);
goto Return;
}
for (i = 0; i < p->logicalLength; i++)
{
if ( p->ackValues[ i ].pid == pid )
{
if ( p->ackValues[ i ].state == kWaitingForAck )
{
p->ackValues[ i ].state = kAckReceived;
}
else
{
LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d, errorCode=%d): error: state != kWaitingForAck\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode);
p->ackValues[ i ].state = kWaitingForAck;
}
p->ackValues[ i ].errorCode = errorCode;
goto Return;
}
}
dwarning(("%s: ERROR, pid=%d not found!\n", __FUNCTION__, pid));
LogErrorMessage("(%s:%d) %s(0x%08x, pid=%d, errorCode=%d): error: pid not found\n", __FILE__, __LINE__, __FUNCTION__, (unsigned)p, (unsigned)pid, errorCode);
Return:
return;
}
int NumUnsetAckValues( AckValues * p )
{
int result;
int i;
result = 0;
for (i = 0; i < p->logicalLength; i++)
{
if ( p->ackValues[ i ].state != kAckReceived )
{
result ++ ;
}
}
return result;
}
int NumUnsetAckValuesForAllDisks( void )
{
DiskPtr diskPtr;
int result;
result = 0;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( diskPtr->ackValues )
{
result += NumUnsetAckValues( diskPtr->ackValues );
}
}
dwarning(("%s => %d\n", __FUNCTION__, result));
return result;
}
void PrintAckValues( AckValues * p )
{
int i;
if ( ! gDebug ) goto Return;
if ( ! p ) goto Return;
for (i = 0; i < p->logicalLength; i++)
{
dwarning(("%2d: pid = %d, state = %d, errorCode = %d\n", i, p->ackValues[ i ].pid, p->ackValues[ i ].state, p->ackValues[ i ].errorCode));
}
Return:
return;
}
AckValue * GetDissenterFromAckValues( AckValues * ackValuesPtr )
{
AckValue * result;
int i;
for (i = 0; i < ackValuesPtr->logicalLength; i++)
{
result = & ackValuesPtr->ackValues[ i ];
if ( result->state == kAckReceived && result->errorCode != 0 )
{
goto Return;
}
}
result = NULL;
Return:
return result;
}
AckValue * GetDissenterFromAckValuesForAllDisks( void )
{
AckValue * result;
DiskPtr diskPtr;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( diskPtr->ackValues )
{
result = GetDissenterFromAckValues( diskPtr->ackValues );
if ( result )
{
goto Return;
}
}
}
result = NULL;
Return:
return result;
}
AckValue * GetUnresponderFromAckValues( AckValues * ackValuesPtr )
{
AckValue * result;
int i;
for (i = 0; i < ackValuesPtr->logicalLength; i++)
{
result = & ackValuesPtr->ackValues[ i ];
if ( result->state == kWaitingForAck )
{
goto Return;
}
}
result = NULL;
Return:
return result;
}
AckValue * GetUnresponderFromAckValuesForAllDisks( void )
{
AckValue * result;
DiskPtr diskPtr;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( diskPtr->ackValues )
{
result = GetUnresponderFromAckValues( diskPtr->ackValues );
if ( result )
{
goto Return;
}
}
}
result = NULL;
Return:
return result;
}
void MakeDeadClientAgreeable( ClientPtr clientPtr )
{
DiskPtr diskPtr;
int i;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( diskPtr->ackValues )
{
for (i = 0; i < diskPtr->ackValues->logicalLength; i++)
{
if ( clientPtr->pid == diskPtr->ackValues->ackValues[ i ].pid )
{
diskPtr->ackValues->ackValues[ i ].state = kAckReceived;
diskPtr->ackValues->ackValues[ i ].errorCode = 0;
}
}
}
}
}
unsigned NumClientsDesiringAsyncNotification( void )
{
unsigned result = 0;
ClientPtr clientPtr;
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->flags & kDiskArbNotifyAsync )
{
result ++ ;
}
}
return result;
}
typedef struct {
int diskAppearedType;
mach_port_t port;
char *ioBSDName; unsigned flags;
char *mountpoint; int pid;
char *ioDeviceTreePath;
char *ioContent; int sequenceNumber;
int diskType;
} DiskThreadRecord;
static void * NotifyDiskAppeared(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r = nil;
if ( record->diskAppearedType == kDiskArbNotifyDiskAppearedWithoutMountpoint )
{
r = DiskArbDiskAppearedWithoutMountpoint_rpc(record->port, record->ioBSDName, record->flags);
}
else if ( record->diskAppearedType == kDiskArbNotifyDiskAppearedWithMountpoint )
{
r = DiskArbDiskAppearedWithMountpoint_rpc(record->port, record->ioBSDName, record->flags, record->mountpoint);
}
else if ( record->diskAppearedType == kDiskArbNotifyDiskAppeared )
{
r = DiskArbDiskAppeared_rpc(record->port, record->ioBSDName, record->flags, record->mountpoint, record->ioContent);
}
else if ( record->diskAppearedType == kDiskArbNotifyDiskAppeared2 )
{
r = DiskArbDiskAppeared2_rpc(record->port, record->ioBSDName, record->flags, record->mountpoint, record->ioContent, record->ioDeviceTreePath, record->sequenceNumber);
}
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x (pid=%d)\n", record->port, record->pid));
}
free(record->ioBSDName);
free(record->ioDeviceTreePath);
free(record->ioContent);
free(record->mountpoint);
free(record);
return NULL;
}
static void * NotifyDiskRegistrationComplete(void * args)
{
ClientPtr client = args;
kern_return_t r;
r = DiskArbRegistrationComplete_rpc(client->port);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", client->port));
}
return NULL;
}
static void * NotifyDiskMessagesCompleted(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbNotificationComplete_rpc(record->port, record->sequenceNumber);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyClientDisconnected(void * args)
{
ClientPtr client = args;
kern_return_t r;
r = DiskArbClientDisconnected_rpc(client->port);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", client->port));
}
return NULL;
}
static void * NotifyBlueBoxMessages(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbBlueBoxBootVolumeUpdated_async_rpc(record->port, record->sequenceNumber);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyDiskChanged(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbDiskChanged_rpc(record->port, record->ioBSDName, record->mountpoint, record->ioContent, record->flags, record->sequenceNumber);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyDiskWillBeChecked(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbDiskWillBeChecked_rpc(record->port, record->ioBSDName, record->flags, record->ioContent);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyCallFailed(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbPreviousCallFailed_rpc(record->port, record->ioBSDName, record->flags, record->sequenceNumber);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyUnrecognizedDiskInserted(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbUnknownFileSystemInserted_rpc(record->port, record->ioBSDName, record->mountpoint, record->ioContent, record->flags, record->sequenceNumber, record->diskAppearedType);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyUnrecognizedDiskArbitration(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbWillClientHandleUnrecognizedDisk_rpc(record->port, record->ioBSDName, record->diskType, record->mountpoint, record->ioContent, record->flags, record->sequenceNumber, record->diskAppearedType);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x\n", record->port));
}
free(record);
return NULL;
}
static void * NotifyPreUnmount(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbUnmountPreNotify_async_rpc(record->port, record->ioBSDName, 0);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x (pid=%d)\n", record->port, record->pid));
}
free(record);
return NULL;
}
static void * NotifyPreEjection(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbEjectPreNotify_async_rpc(record->port, record->ioBSDName, 0);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x (pid=%d)\n", record->port, record->pid));
}
free(record);
return NULL;
}
static void * NotifyPostUnmount(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbUnmountPostNotify_async_rpc(record->port, record->ioBSDName, record->sequenceNumber, record->pid);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x (pid=%d)\n", record->port, record->pid));
}
free(record);
return NULL;
}
static void * NotifyPostEjection(void * args)
{
DiskThreadRecord *record = args;
kern_return_t r;
r = DiskArbEjectPostNotify_async_rpc(record->port, record->ioBSDName, record->sequenceNumber, record->pid);
if ( r ) dwarning(("... $%08x: %s\n", r, mach_error_string(r)));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x (pid=%d)\n", record->port, record->pid));
}
free(record);
return NULL;
}
static void StartDiskAppearedThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyDiskAppeared, record);
pthread_attr_destroy(&attr);
}
static void StartDiskMessagesCompleted(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyDiskMessagesCompleted, record);
pthread_attr_destroy(&attr);
}
static void StartBlueBoxNotificationThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyBlueBoxMessages, record);
pthread_attr_destroy(&attr);
}
void StartDiskRegistrationCompleteThread(ClientPtr client)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyDiskRegistrationComplete, client);
pthread_attr_destroy(&attr);
}
void StartClientDisconnectedThread(ClientPtr client)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyClientDisconnected, client);
pthread_attr_destroy(&attr);
}
void StartDiskChangedThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyDiskChanged, record);
pthread_attr_destroy(&attr);
}
void StartDiskWillBeCheckedMessages(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyDiskWillBeChecked, record);
pthread_attr_destroy(&attr);
}
void StartCallFailedThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyCallFailed, record);
pthread_attr_destroy(&attr);
}
void StartUnrecognizedDiskThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyUnrecognizedDiskInserted, record);
pthread_attr_destroy(&attr);
}
void StartUnrecognizedDiskArbitrationThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyUnrecognizedDiskArbitration, record);
pthread_attr_destroy(&attr);
}
void StartPreUnmountNotifyThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyPreUnmount, record);
pthread_attr_destroy(&attr);
}
void StartPreEjectNotifyThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyPreEjection, record);
pthread_attr_destroy(&attr);
}
void StartPostUnmountNotifyThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyPostUnmount, record);
pthread_attr_destroy(&attr);
}
void StartPostEjectNotifyThread(DiskThreadRecord * record)
{
pthread_attr_t attr;
pthread_t tid;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&tid, &attr, NotifyPostEjection, record);
pthread_attr_destroy(&attr);
}
void SendClientWasDisconnectedMsg(ClientPtr clientPtr)
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbClientDisconnected_rpc($%08x) ...\n", clientPtr->port));
record->port = clientPtr->port;
StartClientDisconnectedThread(clientPtr);
}
int SendDiskAppearedMsgs( void )
{
ClientPtr clientPtr;
DiskPtr diskPtr;
int newDiskAppeared = FALSE;
dwarning(("%s\n", __FUNCTION__));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( ( kClientStateNew == clientPtr->state ) && ( clientPtr->flags & kDiskArbNotifyBlueBoxBootVolumeUpdated ) )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbBlueBoxBootVolumeUpdated_async_rpc($%08x (pid=%d), gBlueBoxBootVolume=%d) ...\n", clientPtr->port, clientPtr->pid, gBlueBoxBootVolume));
record->sequenceNumber = gBlueBoxBootVolume;
record->port = clientPtr->port;
StartBlueBoxNotificationThread(record);
}
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( ( kClientStateNew == clientPtr->state ) || ( kDiskStateNew == diskPtr->state ) || ( kDiskStateUnrecognized == diskPtr->state ))
{
if ((diskPtr->flags & kDiskArbDiskAppearedUnrecognizableFormat) && (clientPtr->flags & kDiskArbNotifyUnrecognizedVolumes) ) {
SendUnrecognizedDiskMsgs(clientPtr->port, diskPtr->ioBSDName, "", "", (!((diskPtr->flags & kDiskArbDiskAppearedLockedMask) == kDiskArbDiskAppearedLockedMask)), ((diskPtr->flags & kDiskArbDiskAppearedEjectableMask) == kDiskArbDiskAppearedEjectableMask), IsWhole(diskPtr));
continue;
}
if ( (clientPtr->flags & kDiskArbNotifyDiskAppearedWithoutMountpoint) && 0 == strcmp("",diskPtr->mountpoint) )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbDiskAppearedWithoutMountpoint_rpc($%08x (pid=%d), '%s', $%08x) ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags));
record->diskAppearedType = kDiskArbNotifyDiskAppearedWithoutMountpoint;
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
record->flags = diskPtr->flags;
record->mountpoint = strdup( diskPtr->mountpoint );
record->pid = clientPtr->pid;
record->ioContent = strdup(diskPtr->ioContent);
record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
record->sequenceNumber = diskPtr->sequenceNumber;
StartDiskAppearedThread(record);
if ( kDiskStateNew == diskPtr->state ) newDiskAppeared = TRUE;
}
if ( (clientPtr->flags & kDiskArbNotifyDiskAppearedWithMountpoint) && 0 != strcmp("",diskPtr->mountpoint) )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbDiskAppearedWithMountpoint_rpc($%08x (pid=%d), '%s', $%08x, '%s') ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags, diskPtr->mountpoint));
record->diskAppearedType = kDiskArbNotifyDiskAppearedWithMountpoint;
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
record->flags = diskPtr->flags;
record->mountpoint = strdup( diskPtr->mountpoint );
record->pid = clientPtr->pid;
record->ioContent = strdup(diskPtr->ioContent);
record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
record->sequenceNumber = diskPtr->sequenceNumber;
StartDiskAppearedThread(record);
if ( kDiskStateNew == diskPtr->state ) newDiskAppeared = TRUE;
}
if ( clientPtr->flags & kDiskArbNotifyDiskAppeared )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbDiskAppeared_rpc($%08x (pid=%d), '%s', $%08x, '%s', '%s') ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags, diskPtr->mountpoint, diskPtr->ioContent));
record->diskAppearedType = kDiskArbNotifyDiskAppeared;
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
record->flags = diskPtr->flags;
record->mountpoint = strdup( diskPtr->mountpoint );
record->pid = clientPtr->pid;
record->ioContent = strdup(diskPtr->ioContent);
record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
record->sequenceNumber = diskPtr->sequenceNumber;
StartDiskAppearedThread(record);
if ( kDiskStateNew == diskPtr->state ) newDiskAppeared = TRUE;
}
if ( clientPtr->flags & kDiskArbNotifyDiskAppeared2 )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbDiskAppeared2_rpc($%08x (pid=%d), '%s', $%08x, '%s', '%s', '%s', %d) ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName, diskPtr->flags, diskPtr->mountpoint, diskPtr->ioContent, diskPtr->ioDeviceTreePath, diskPtr->sequenceNumber));
record->diskAppearedType = kDiskArbNotifyDiskAppeared2;
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
record->flags = diskPtr->flags;
record->mountpoint = strdup( diskPtr->mountpoint );
record->pid = clientPtr->pid;
record->ioContent = strdup(diskPtr->ioContent);
record->ioDeviceTreePath = strdup(diskPtr->ioDeviceTreePath);
record->sequenceNumber = diskPtr->sequenceNumber;
StartDiskAppearedThread(record);
if ( kDiskStateNew == diskPtr->state ) newDiskAppeared = TRUE;
}
}
}
}
return newDiskAppeared;
}
void SendUnrecognizedDiskMsgs(mach_port_t port, char *devname, char *fstype, char *deviceType, int isWritable, int isRemovable, int isWhole)
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
record->diskAppearedType = isWhole;
record->port = port;
record->ioBSDName = strdup( devname );
record->flags = isWritable;
record->mountpoint = strdup( fstype );
record->pid = 0;
record->ioContent = strdup(deviceType);
record->ioDeviceTreePath = strdup("");
record->sequenceNumber = isRemovable;
StartUnrecognizedDiskThread(record);
}
void SendUnrecognizedDiskArbitrationMsgs(mach_port_t port, char *devname, char *fstype, char *deviceType, int isWritable, int isRemovable, int isWhole, int diskType)
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
record->diskAppearedType = isWhole;
record->port = port;
record->ioBSDName = strdup( devname );
record->flags = isWritable;
record->mountpoint = strdup( fstype );
record->pid = 0;
record->ioContent = strdup(deviceType);
record->ioDeviceTreePath = strdup("");
record->sequenceNumber = isRemovable;
record->diskType = diskType;
StartUnrecognizedDiskArbitrationThread(record);
}
void SendDiskChangedMsgs(char *devname, char *newMountpoint, char *newVolumeName, int flags, int success)
{
ClientPtr clientPtr;
dwarning(("%s\n", __FUNCTION__));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( ( clientPtr->flags & kDiskArbNotifyChangedDisks ) ) {
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbDiskChanged_rpc($%08x (pid=%d)) ...\n", clientPtr->port, clientPtr->pid));
record->diskAppearedType = 0;
record->port = clientPtr->port;
record->ioBSDName = strdup( devname );
record->flags = flags;
record->mountpoint = strdup( newMountpoint ); record->pid = 0;
record->ioContent = strdup(newVolumeName); record->ioDeviceTreePath = strdup("");
record->sequenceNumber = success;
StartDiskChangedThread(record);
}
}
}
void SendDiskWillBeCheckedMessages(DiskPtr diskPtr)
{
ClientPtr clientPtr;
dwarning(("%s\n", __FUNCTION__));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( ( clientPtr->flags & kDiskArbNotifyDiskWillBeChecked ) ) {
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("SendDiskWillBeCheckedMessages_rpc($%08x (pid=%d)) ...\n", clientPtr->port, clientPtr->pid));
record->diskAppearedType = 0;
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
record->flags = diskPtr->flags;
record->mountpoint = strdup("");
record->pid = clientPtr->pid;
record->ioContent = strdup(diskPtr->ioContent);
record->ioDeviceTreePath = strdup("");
record->sequenceNumber = 0;
StartDiskWillBeCheckedMessages(record);
}
}
}
void SendCallFailedMessage(ClientPtr clientPtr, DiskPtr diskPtr, int failedType, int error)
{
if ( ( clientPtr->flags & kDiskArbNotifyCallFailed ) ) {
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("SendCallFailedMessage($%08x (pid=%d)) ...\n", clientPtr->port, clientPtr->pid));
record->diskAppearedType = 0;
record->port = clientPtr->port;
record->ioBSDName = (diskPtr ? strdup( diskPtr->ioBSDName ) : strdup(""));
record->flags = failedType;
record->mountpoint = strdup("");
record->pid = clientPtr->pid;
record->ioContent = strdup("");
record->ioDeviceTreePath = strdup("");
record->sequenceNumber = error;
StartCallFailedThread(record);
}
}
void SendUnmountCommitMsgs( void )
{
ClientPtr clientPtr;
DiskPtr diskPtr;
kern_return_t r = 0;
dwarning(("%s\n", __FUNCTION__));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( kDiskStateNewlyUnmounted == diskPtr->state || kDiskStateNewlyEjected == diskPtr->state )
{
if ( clientPtr->flags & kDiskArbNotifyUnmount )
{
dwarning(("DiskArbUnmountCommit($%08x (pid=%d), '%s') ...\n", clientPtr->port, clientPtr->pid, diskPtr->ioBSDName));
if ( r == MACH_SEND_INVALID_DEST )
{
dwarning(("Dead client! Port = $%08x (pid=%d)\n", clientPtr->port, clientPtr->pid));
}
}
}
}
}
}
void PrepareToSendPreUnmountMsgs( void )
{
ClientPtr clientPtr;
DiskPtr diskPtr;
dwarning(("%s\n", __FUNCTION__));
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( kDiskStateToBeUnmounted == diskPtr->state || kDiskStateToBeUnmountedAndEjected == diskPtr->state )
{
if (diskPtr->mountpoint && strlen(diskPtr->mountpoint)) {
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->flags & kDiskArbNotifyAsync )
{
InitAckValue( diskPtr->ackValues, clientPtr->pid );
}
} }
}
}
}
void PrepareToSendPreEjectMsgs( void )
{
ClientPtr clientPtr;
DiskPtr diskPtr;
dwarning(("%s\n", __FUNCTION__));
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( kDiskStateToBeEjected == diskPtr->state )
{
if (IsWhole(diskPtr)) {
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->flags & kDiskArbNotifyAsync )
{
InitAckValue( diskPtr->ackValues, clientPtr->pid );
}
} }
}
}
}
void SendPreUnmountMsgs( void )
{
DiskPtr diskPtr;
int i;
dwarning(("%s\n", __FUNCTION__));
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( kDiskStateToBeUnmounted == diskPtr->state || kDiskStateToBeUnmountedAndEjected == diskPtr->state )
{
for (i = 0; i < diskPtr->ackValues->logicalLength; i++)
{
ClientPtr clientPtr;
if ( diskPtr->ackValues->ackValues[ i ].state != kSendMsg )
{
continue;
}
clientPtr = LookupClientByPID( diskPtr->ackValues->ackValues[ i ].pid );
if ( ! clientPtr )
{
pwarning(("%s: pid = %d: no known client with this pid.\n", __FUNCTION__, diskPtr->ackValues->ackValues[ i ].pid));
}
if ( clientPtr->numAcksRequired > 0 )
{
continue;
}
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
StartPreUnmountNotifyThread(record);
}
clientPtr->numAcksRequired++;
diskPtr->ackValues->ackValues[ i ].state = kWaitingForAck;
}
}
}
}
void SendPreEjectMsgs( void )
{
DiskPtr diskPtr;
int i;
dwarning(("%s\n", __FUNCTION__));
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( kDiskStateToBeEjected == diskPtr->state )
{
for (i = 0; i < diskPtr->ackValues->logicalLength; i++)
{
ClientPtr clientPtr;
if ( diskPtr->ackValues->ackValues[ i ].state != kSendMsg )
{
continue;
}
clientPtr = LookupClientByPID( diskPtr->ackValues->ackValues[ i ].pid );
if ( ! clientPtr )
{
pwarning(("%s: pid = %d: no known client with this pid.\n", __FUNCTION__, diskPtr->ackValues->ackValues[ i ].pid));
}
if ( clientPtr->numAcksRequired > 0 )
{
continue;
}
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
record->port = clientPtr->port;
record->ioBSDName = strdup( diskPtr->ioBSDName );
StartPreEjectNotifyThread(record);
}
clientPtr->numAcksRequired++;
diskPtr->ackValues->ackValues[ i ].state = kWaitingForAck;
}
}
}
}
void SendUnmountPostNotifyMsgsForOnePartition( char * ioBSDName, int errorCode, pid_t pid )
{
ClientPtr clientPtr;
dwarning(("%s('%s', errorCode = %d, pid = %d)\n", __FUNCTION__, ioBSDName, errorCode, pid));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->flags & kDiskArbNotifyAsync )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbUnmountPostNotify_async_rpc($%08x (pid=%d), '%s', errorCode=%d, pid=%d) ...\n", clientPtr->port, clientPtr->pid, ioBSDName, errorCode, pid));
record->sequenceNumber = errorCode;
record->port = clientPtr->port;
record->ioBSDName = strdup( ioBSDName );
record->pid = pid;
StartPostUnmountNotifyThread(record);
}
}
}
void SendEjectPostNotifyMsgsForOnePartition( char * ioBSDName, int errorCode, pid_t pid )
{
ClientPtr clientPtr;
dwarning(("%s('%s', errorCode = %d, pid = %d)\n", __FUNCTION__, ioBSDName, errorCode, pid));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->flags & kDiskArbNotifyAsync )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbEjectPostNotify_async_rpc($%08x (pid=%d), '%s', errorCode=%d, pid=%d) ...\n", clientPtr->port, clientPtr->pid, ioBSDName, errorCode, pid));
record->sequenceNumber = errorCode;
record->port = clientPtr->port;
record->ioBSDName = strdup( ioBSDName );
record->pid = pid;
StartPostEjectNotifyThread(record);
}
}
}
void SendEjectPostNotifyMsgsForAllPartitions( DiskPtr diskPtr, int errorCode, pid_t pid )
{
DiskPtr diskPtr2;
int family1, family2;
int deviceNum1, deviceNum2;
dwarning(("%s('%s', errorCode = %d, pid = %d)\n", __FUNCTION__, diskPtr->ioBSDName, errorCode, pid));
if ( IsNetwork( diskPtr ) )
{
SendEjectPostNotifyMsgsForOnePartition( diskPtr->ioBSDName, errorCode, pid );
goto Return;
}
family1 = diskPtr->family;
deviceNum1 = diskPtr->ioBSDUnit;
for (diskPtr2 = g.Disks; diskPtr2 != NULL; diskPtr2 = diskPtr2->next)
{
family2 = diskPtr2->family;
deviceNum2 = diskPtr2->ioBSDUnit;
if ( family1 == family2 && deviceNum1 == deviceNum2 )
{
SendEjectPostNotifyMsgsForOnePartition( diskPtr2->ioBSDName, errorCode, pid );
}
}
Return:
return;
}
void SendBlueBoxBootVolumeUpdatedMsgs( void )
{
ClientPtr clientPtr;
dwarning(("%s()\n", __FUNCTION__));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( clientPtr->flags & kDiskArbNotifyBlueBoxBootVolumeUpdated )
{
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbBlueBoxBootVolumeUpdated_async_rpc($%08x (pid=%d), gBlueBoxBootVolume=%d) ...\n", clientPtr->port, clientPtr->pid, gBlueBoxBootVolume));
record->sequenceNumber = gBlueBoxBootVolume;
record->port = clientPtr->port;
StartBlueBoxNotificationThread(record);
}
}
}
void SendCompletedMsgs( int messageType, int newDisk )
{
ClientPtr clientPtr;
DiskPtr diskPtr;
dwarning(("%s(%d)\n", __FUNCTION__, messageType));
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( ( clientPtr->flags & kDiskArbNotifyCompleted ) ) {
int shouldSend = FALSE;
if ((messageType == kDiskArbCompletedDiskAppeared) && (newDisk || ( kClientStateNew == clientPtr->state ))) {
dwarning(("Sending completed: Disk Appeared: newDisk = %d, clientState = %d\n", newDisk, clientPtr->state));
shouldSend = TRUE;
}
if (((messageType == kDiskArbCompletedPostUnmount) || (messageType == kDiskArbCompletedPostEject)) && clientPtr->flags & kDiskArbNotifyAsync ) {
dwarning(("Sending completed: Disk Unmounted or Ejected\n"));
shouldSend = TRUE;
}
if (shouldSend) {
DiskThreadRecord * record = malloc(sizeof(DiskThreadRecord));
dwarning(("DiskArbNotificationComplete_rpc($%08x (messageType=%d)) ...\n", clientPtr->port, messageType));
record->port = clientPtr->port;
record->sequenceNumber = messageType;
StartDiskMessagesCompleted(record);
}
}
}
for (clientPtr = g.Clients; clientPtr != NULL; clientPtr = clientPtr->next)
{
if ( kClientStateNew == clientPtr->state ) clientPtr->state = kClientStateIdle;
}
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( kDiskStateNew == diskPtr->state ) SetStateForOnePartition( diskPtr, kDiskStateIdle );
}
}
void SetBlueBoxBootVolume( int seqno )
{
dwarning(("%s(seqno=%d)\n", __FUNCTION__, seqno));
gBlueBoxBootVolume = seqno;
SendBlueBoxBootVolumeUpdatedMsgs();
}
int GetBlueBoxBootVolume( void )
{
return gBlueBoxBootVolume;
}
void CompleteUnmount( void )
{
DiskPtr diskPtr;
AckValue * avp;
int errorCode_out = 0;
pid_t pid_out = 0;
DiskPtr wholeDiskToBeEjectedOrNULL;
dwarning(("%s\n", __FUNCTION__));
if ( 0 != NumUnsetAckValuesForAllDisks() )
{
goto Return;
}
wholeDiskToBeEjectedOrNULL = LookupWholeDiskToBeEjected();
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next)
{
if ( diskPtr->state != kDiskStateToBeUnmounted && diskPtr->state != kDiskStateToBeUnmountedAndEjected )
{
continue;
}
avp = GetDissenterFromAckValues( diskPtr->ackValues );
if ( NULL == avp )
{
int err;
dwarning(("%s: there was no dissenter for '%s'\n", __FUNCTION__, diskPtr->ioBSDName));
err = UnmountDisk( diskPtr, FALSE );
if ( err )
{
dwarning(("%s: the unmount failed\n", __FUNCTION__));
errorCode_out = err;
pid_out = -1;
}
else
{
errorCode_out = 0;
pid_out = -1;
}
}
else
{
dwarning(("%s: there was a dissenter for '%s'\n", __FUNCTION__, diskPtr->ioBSDName));
errorCode_out = avp->errorCode;
pid_out = avp->pid;
SetStateForOnePartition( diskPtr, kDiskStateIdle );
}
SendUnmountPostNotifyMsgsForOnePartition( diskPtr->ioBSDName, errorCode_out, pid_out );
}
if ( wholeDiskToBeEjectedOrNULL && ( 0 == NumPartitionsMountedFromThisDisk( wholeDiskToBeEjectedOrNULL ) ) )
{
SetStateForAllPartitions( wholeDiskToBeEjectedOrNULL, kDiskStateToBeEjected );
PrepareToSendPreEjectMsgs();
}
Return:
return;
}
void CompleteEject( void )
{
DiskPtr diskPtr;
AckValue * avp;
int errorCode_out = 0;
pid_t pid_out = 0;
dwarning(("%s\n", __FUNCTION__));
if ( 0 != NumUnsetAckValuesForAllDisks() )
{
goto Return;
}
diskPtr = LookupWholeDiskToBeEjected();
if ( ! diskPtr )
{
LogErrorMessage("%s(): LookupWholeDiskToBeEjected() => NULL\n", __FUNCTION__);
goto Return;
}
avp = GetDissenterFromAckValuesForAllDisks();
if ( NULL == avp )
{
int err;
dwarning(("%s: there was no dissenter\n", __FUNCTION__));
err = EjectDisk( diskPtr );
if ( err )
{
dwarning(("%s: the eject failed\n", __FUNCTION__));
errorCode_out = err;
pid_out = -1;
}
else
{
errorCode_out = 0;
pid_out = -1;
}
}
else
{
dwarning(("%s: there was a dissenter\n", __FUNCTION__));
errorCode_out = avp->errorCode;
pid_out = avp->pid;
SetStateForAllPartitions( diskPtr, kDiskStateIdle );
}
SendEjectPostNotifyMsgsForAllPartitions( diskPtr, errorCode_out, pid_out );
Return:
return;
}
DiskState AreWeBusy( void )
{
DiskState result;
DiskPtr diskPtr;
result = kDiskStateIdle;
for (diskPtr = g.Disks; diskPtr != NULL ; diskPtr = diskPtr->next)
{
switch ( diskPtr->state )
{
case kDiskStateToBeUnmounted:
if ( kDiskStateIdle == result )
{
result = diskPtr->state;
}
else if ( result != diskPtr->state )
{
dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
}
break;
case kDiskStateToBeEjected:
if ( kDiskStateIdle == result )
{
result = diskPtr->state;
}
else if ( result != diskPtr->state )
{
dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
}
break;
case kDiskStateToBeUnmountedAndEjected:
if ( kDiskStateIdle == result || kDiskStateToBeEjected == result )
{
result = diskPtr->state;
}
else if ( result != diskPtr->state )
{
dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
}
break;
case kDiskStateIdle:
case kDiskStateNew:
case kDiskStateNewlyEjected:
case kDiskStateNewlyUnmounted:
default:
break;
}
}
dwarning(("%s() => %s (%d)\n", __FUNCTION__, DISKSTATE(result), result));
return result;
}
DiskState AreWeBusyForDisk( DiskPtr diskPtr )
{
DiskState result;
result = kDiskStateIdle;
switch ( diskPtr->state )
{
case kDiskStateToBeUnmounted:
if ( kDiskStateIdle == result )
{
result = diskPtr->state;
}
else if ( result != diskPtr->state )
{
dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
}
break;
case kDiskStateToBeEjected:
if ( kDiskStateIdle == result )
{
result = diskPtr->state;
}
else if ( result != diskPtr->state )
{
dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
}
break;
case kDiskStateToBeUnmountedAndEjected:
if ( kDiskStateIdle == result || kDiskStateToBeEjected == result )
{
result = diskPtr->state;
}
else if ( result != diskPtr->state )
{
dwarning(("%s: incompatible disk states: %s (%d) vs. %s (%d)\n", __FUNCTION__, DISKSTATE(diskPtr->state), diskPtr->state, DISKSTATE(result), result));
}
break;
case kDiskStateIdle:
case kDiskStateNew:
case kDiskStateNewlyEjected:
case kDiskStateNewlyUnmounted:
default:
break;
}
dwarning(("%s() => %s (%d)\n", __FUNCTION__, DISKSTATE(result), result));
return result;
}
static void
writepid(void)
{
FILE *fp;
fp = fopen(PID_FILE, "w");
if (fp != NULL)
{
fprintf(fp, "%d\n", getpid());
fclose(fp);
}
}
int authorizationAllowedForEvent(ClientPtr client, char *event)
{
OSStatus err = 0;
int authorized = 0;
AuthorizationRights rights;
AuthorizationItem items[1];
if (!client->clientAuthRef) {
return 0;
}
items[0].name = event;
items[0].value = NULL;
items[0].valueLength = 0;
items[0].flags = 0;
rights.count = 1;
rights.items = items;
err = AuthorizationCopyRights(client->clientAuthRef, &rights, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed, NULL);
authorized = (errAuthorizationSuccess == err);
return authorized;
}
int requestingClientHasPermissionToModifyDisk(pid_t pid, DiskPtr diskPtr, char *right)
{
ClientPtr client = nil;
if (pid) {
client = LookupClientByPID(pid);
}
if (requestorUID == 0 || pid == 0) {
dwarning(("autodiskmount: requestor is root\n"));
return TRUE;
}
if (requestorUID == diskPtr->mountedUser) {
dwarning(("autodiskmount: requestor mounted disk\n"));
return TRUE;
}
{
struct stat statbuf;
if ( stat(diskPtr->mountpoint, &statbuf) >= 0 ) {
if (statbuf.st_uid == requestorUID) {
dwarning(("autodiskmount: requestor owns mount point\n"));
return TRUE;
}
}
}
if (!client && !pid){
}
if (client && authorizationAllowedForEvent(client, right)) {
return TRUE;
}
return FALSE;
}
int DiskArbitrationServerMain(int argc, char* argv[])
{
kern_return_t r;
mach_port_t serverPort;
mach_port_t ioNotificationPort;
const char * ioNotificationType;
io_iterator_t ioIterator; io_iterator_t ioIterator2; io_registry_entry_t entry;
mach_port_t portSet;
mach_msg_header_t* msg;
mach_msg_header_t* reply;
mach_msg_timeout_t timeout;
programName = argv[0];
if ((argc > 1) && (argv[1][0] == '-') && (argv[1][1] == 'd'))
{
gDebug = 1;
gDaemon = 0;
dwarning(("DiskArbitration is not a daemon\n"));
}
else
{
gDaemon = 1;
dwarning(("DiskArbitration is a daemon\n"));
}
msg = (mach_msg_header_t*)malloc(kMsgSize);
reply = (mach_msg_header_t*)malloc(kMsgSize);
cacheFileSystemDictionaries();
cacheFileSystemMatchingArray();
if (gDebug) {
}
{
yankedHeader = YANKED_HEADER;
yankedMessage = YANKED_MESSAGE;
unrecognizedHeader = UNINITED_HEADER;
unrecognizedHeaderNoInitialize = UNINITED_HEADER_NO_INIT;
unrecognizedMessage = UNINITED_MESSAGE;
ejectString = EJECT_BUTTON;
ignoreString = IGNORE_BUTTON;
initString = INIT_BUTTON;
launchString = LAUNCH_BUTTON;
someDisk = A_DISK;
mountOrFsckFailedWithDiskUtility = MOUNT_FAILED_WITH_DU;
mountOrFsckFailed = MOUNT_FAILED;
unknownString = UNKNOWN_STRING;
}
r = InitNotifyPort();
if (r != KERN_SUCCESS)
{
return -1;
}
r = IOMasterPort(bootstrap_port, &ioMasterPort);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) IOMasterPort failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ioNotificationPort);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
ioNotificationType = kIOFirstMatchNotification;
r = IOServiceAddNotification( ioMasterPort, ioNotificationType,
IOServiceMatching( "IOMedia" ),
ioNotificationPort, (unsigned int) ioNotificationType,
&ioIterator );
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
GetDisksFromRegistry( ioIterator, 1 );
ioNotificationType = kIOTerminatedNotification;
r = IOServiceAddNotification( ioMasterPort, ioNotificationType,
IOServiceMatching( "IOMedia" ),
ioNotificationPort, (unsigned int) ioNotificationType,
&ioIterator2 );
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
while ( entry = IOIteratorNext( ioIterator2 ) ) { IOObjectRelease( entry );
}
autodiskmount(FALSE);
dwarning(("Non-debug version calls daemon() here...\n"));
if ( gDaemon )
{
FreeAllDisks();
if (-1 == daemon(0, 0))
{
return -1;
}
r = InitNotifyPort();
if (r != KERN_SUCCESS)
{
return -1;
}
r = IOMasterPort(bootstrap_port, &ioMasterPort);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) IOMasterPort failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &ioNotificationPort);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
ioNotificationType = kIOFirstMatchNotification;
r = IOServiceAddNotification( ioMasterPort, ioNotificationType,
IOServiceMatching( "IOMedia" ),
ioNotificationPort, (unsigned int) ioNotificationType,
&ioIterator );
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
GetDisksFromRegistry( ioIterator, 0 );
ioNotificationType = kIOTerminatedNotification;
r = IOServiceAddNotification( ioMasterPort, ioNotificationType,
IOServiceMatching( "IOMedia" ),
ioNotificationPort, (unsigned int) ioNotificationType,
&ioIterator2 );
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) IOServiceAddNotification failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
while ( entry = IOIteratorNext( ioIterator2 ) ) { IOObjectRelease( entry );
}
autodiskmount(FALSE);
}
r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &serverPort);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
r = mach_port_insert_right(mach_task_self(), serverPort, serverPort, MACH_MSG_TYPE_MAKE_SEND);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_insert_right failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
dwarning(("%s: serverPort = %d\n", programName, (int)serverPort));
r = bootstrap_register(bootstrap_port, DISKARB_SERVER_NAME, serverPort);
if (r != KERN_SUCCESS)
{
return -1;
}
r = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &portSet);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_allocate failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
r = mach_port_move_member(mach_task_self(), GetNotifyPort(), portSet);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_move_member (notify port) failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
r = mach_port_move_member(mach_task_self(), serverPort, portSet);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_move_member (server port) failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
r = mach_port_move_member(mach_task_self(), ioNotificationPort, portSet);
if (r != KERN_SUCCESS)
{
LogErrorMessage("(%s:%d) mach_port_move_member (IO notification port) failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
return -1;
}
writepid();
while( 1 )
{
DiskPtr diskPtr;
DiskPtr nextDiskPtr;
mach_port_t deadPort;
DiskState busyState;
mach_msg_format_0_trailer_t *trailer;
busyState = AreWeBusy();
if ( kDiskStateToBeUnmounted == busyState || kDiskStateToBeUnmountedAndEjected == busyState )
{
CompleteUnmount();
if (NumUnsetAckValuesForAllDisks() == 0) {
SendCompletedMsgs(kDiskArbCompletedPostUnmount, 0);
}
busyState = AreWeBusy();
}
if ( kDiskStateToBeEjected == busyState )
{
CompleteEject();
if (NumUnsetAckValuesForAllDisks() == 0) {
SendCompletedMsgs(kDiskArbCompletedPostEject, 0);
}
busyState = AreWeBusy();
}
if ( kDiskStateToBeUnmounted == busyState || kDiskStateToBeUnmountedAndEjected == busyState )
{
SendPreUnmountMsgs();
}
if ( kDiskStateToBeEjected == busyState )
{
SendPreEjectMsgs();
}
nextDiskPtr = g.Disks;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = nextDiskPtr)
{
nextDiskPtr = diskPtr->next;
if ((diskPtr->flags & kDiskArbDiskAppearedUnrecognizableFormat) && (diskPtr->state == kDiskStateNew)) {
ClientPtr nextClientPtr = GetNextClientForDisk(diskPtr);
if (nextClientPtr) {
SendUnrecognizedDiskArbitrationMsgs(nextClientPtr->port, diskPtr->ioBSDName, "", "", (!((diskPtr->flags & kDiskArbDiskAppearedLockedMask) == kDiskArbDiskAppearedLockedMask)), ((diskPtr->flags & kDiskArbDiskAppearedEjectableMask) == kDiskArbDiskAppearedEjectableMask), IsWhole(diskPtr), DiskTypeForDisk(diskPtr));
diskPtr->state = kDiskStateUnrecognized;
} else {
DiskPtr wholePtr = LookupWholeDiskForThisPartition(diskPtr);
int dialogPreviouslyDisplayed = ( wholePtr->flags & kDiskArbDiskAppearedDialogDisplayed );
int neverMount = ( wholePtr->flags & kDiskArbDiskAppearedNoMountMask );
if (!dialogPreviouslyDisplayed && !neverMount) {
int recognizablePartitionsAppeared = ( wholePtr->flags & kDiskArbDiskAppearedRecognizableSectionMounted );
if (!recognizablePartitionsAppeared && wholePtr->mountAttempted && !IsNetwork(wholePtr)) {
if (DiskArbIsHandlingUnrecognizedDisks()) {
StartUnrecognizedDiskDialogThread(diskPtr);
wholePtr->flags |= kDiskArbDiskAppearedDialogDisplayed;
}
}
}
}
}
}
if ( kDiskStateIdle == busyState )
{
int newDisks = SendDiskAppearedMsgs();
SendCompletedMsgs(kDiskArbCompletedDiskAppeared, newDisks);
}
nextDiskPtr = g.Disks;
DA_postponedDisksExist = 0;
DA_stateChangedToNew = 0;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = nextDiskPtr)
{
nextDiskPtr = diskPtr->next;
if (diskPtr->wholeDiskHasBeenYanked) {
FreeDisk(diskPtr);
}
}
nextDiskPtr = g.Disks;
for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = nextDiskPtr)
{
nextDiskPtr = diskPtr->next;
switch ( diskPtr->state )
{
case kDiskStateNewlyUnmounted:
SetStateForOnePartition( diskPtr, kDiskStateIdle );
break;
case kDiskStateNewlyEjected:
FreeDisk( diskPtr );
break;
case kDiskStatePostponed:
{
DiskPtr wholeDiskPtr = LookupWholeDiskForThisPartition(diskPtr);
if (wholeDiskPtr) {
int busy;
IOServiceGetBusyState(wholeDiskPtr->service, &busy);
if (!busy) {
dwarning(("Setting disk state to new, whole is there and not busy\n"));
diskPtr->state = kDiskStateNew;
DA_stateChangedToNew++;
} else {
dwarning(("Some whole disk is busy\n"));
DA_postponedDisksExist++;
}
} else {
dwarning(("Some whole disk is missing\n"));
DA_postponedDisksExist++;
}
}
break;
case kDiskStateIdle:
case kDiskStateNew:
case kDiskStateToBeUnmounted:
case kDiskStateToBeEjected:
case kDiskStateToBeUnmountedAndEjected:
default:
break;
}
}
if ( g.NumDisksAddedOrDeleted )
{
PrintDisks();
g.NumDisksAddedOrDeleted = 0;
}
if (DA_postponedDisksExist) {
io_iterator_t ioIterator;
mach_port_t masterPort;
kern_return_t err;
dwarning(("Some disk is postponed - waiting one second and re-running autodiskmount loop\n"));
err = IOMasterPort(bootstrap_port, &masterPort);
err = IOServiceGetMatchingServices(masterPort, IOServiceMatching("IOMedia"), &ioIterator);
GetDisksFromRegistry( ioIterator, 0 );
continue;
}
if (DA_stateChangedToNew) {
dwarning(("Some disk is now new - sending ourselves a new disk message\n"));
autodiskmount(FALSE);
continue;
}
if (NumUnsetAckValuesForAllDisks() != 0) {
timeout = (mach_msg_timeout_t)(15000);
} else {
timeout = MACH_MSG_TIMEOUT_NONE;
}
dwarning(("%s: mach_msg\n", programName));
r = mach_msg(msg, MACH_RCV_MSG | (MACH_MSG_TIMEOUT_NONE == timeout ? 0 : MACH_RCV_TIMEOUT) | MACH_RCV_TRAILER_TYPE(MACH_RCV_TRAILER_SENDER) | MACH_RCV_TRAILER_ELEMENTS(2), 0, kMsgSize, portSet, timeout, MACH_PORT_NULL);
if (r == MACH_RCV_TIMED_OUT) {
AckValue *unresponder = GetUnresponderFromAckValuesForAllDisks();
if (unresponder && unresponder->pid) {
Client *unrespondingClient = LookupClientByPID(unresponder->pid);
mach_port_t unrespondingPort = unrespondingClient->port;
dwarning(("**********Someone (pid = (%d)) isn't responding to a sent acknowledgement request\n", unresponder->pid));
SendClientWasDisconnectedMsg(unrespondingClient);
ClientDeath(unrespondingPort);
} else {
dwarning(("Someone timed out, but we don't know who ..."));
}
}
if (r != KERN_SUCCESS) {
LogErrorMessage("(%s:%d) mach_msg failed: {0x%x} %s\n", __FILE__, __LINE__, r, mach_error_string(r));
continue;
}
{
mig_reply_error_t *request = (mig_reply_error_t *)msg;
trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
round_msg(request->Head.msgh_size));
requestingClientPort = msg->msgh_local_port;
if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
(trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
dwarning(("set REQUESTOR ID for this mach message to be = %d\n", trailer->msgh_sender.val[0]));
requestorUID = trailer->msgh_sender.val[0];
requestorGID = trailer->msgh_sender.val[1];
} else {
static boolean_t warned = FALSE;
if (!warned) {
pwarning(("Caller credentials not available."));
warned = TRUE;
}
}
}
if ( msg->msgh_local_port == GetNotifyPort() )
{
dwarning(("%s: Message received on notification port\n", programName));
if ( MessageIsNotificationDeath( msg, & deadPort ) == TRUE )
{
dwarning(("Death of port %d\n", (int)deadPort));
{
kern_return_t r;
r = mach_port_deallocate( mach_task_self(), deadPort );
if ( r ) dwarning(("%s(client = $%08x): mach_port_deallocate(...,$%08x) => $%08x: %s\n", __FUNCTION__, (int)deadPort, (int)deadPort, r, mach_error_string(r)));
}
ClientDeath( deadPort );
}
else
{
}
}
else if ( msg->msgh_local_port == serverPort )
{
dwarning(("%s: Message received on server port from port $%08x\n", programName, (int)(msg->msgh_remote_port)));
bzero( reply, sizeof( mach_msg_header_t ) );
(void) ClientToServer_server(msg, reply);
(void) mach_msg_send(reply);
}
else if ( msg->msgh_local_port == ioNotificationPort )
{
dwarning(("%s: Message received on IO notification port from port $%08x\n", programName, (int)(msg->msgh_remote_port)));
HandleIONotificationMsg( (IONotificationMsgPtr) msg );
}
else
{
LogErrorMessage("Unrecognized receive port %d.\n", (int)(msg->msgh_local_port));
}
requestorUID = -1;
requestingClientPort = -1;
}
dwarning(("%s: exit\n", programName));
return -1;
}
int DiskTypeForDisk(DiskPtr diskPtr)
{
int value = 0;
if (diskPtr->flags & kDiskArbDiskAppearedCDROMMask) {
if (diskPtr->flags & kDiskArbDiskAppearedNoSizeMask) {
value = kDiskArbHandlesUninitializedCDMedia;
} else {
value = kDiskArbHandlesUnrecognizedCDMedia;
}
} else if (diskPtr->flags & kDiskArbDiskAppearedDVDROMMask) {
if (diskPtr->flags & kDiskArbDiskAppearedNoSizeMask) {
value = kDiskArbHandlesUninitializedDVDMedia;
} else {
value = kDiskArbHandlesUnrecognizedDVDMedia;
}
} else if (diskPtr->flags & kDiskArbDiskAppearedEjectableMask) {
if (diskPtr->flags & kDiskArbDiskAppearedNoSizeMask) {
value = kDiskArbHandlesUninitializedOtherRemovableMedia;
} else {
value = kDiskArbHandlesUnrecognizedOtherRemovableMedia;
}
} else {
if (diskPtr->flags & kDiskArbDiskAppearedNoSizeMask) {
value = kDiskArbHandlesUninitializedFixedMedia;
} else {
value = kDiskArbHandlesUnrecognizedFixedMedia;
}
}
return value;
}
CFComparisonResult compareClientPriorities(const void *val1, const void *val2, void *context)
{
int val1Priority = ((ClientPtr)val1)->unrecognizedPriority;
int val2Priority = ((ClientPtr)val2)->unrecognizedPriority;
if (val1Priority > val2Priority) {
return kCFCompareLessThan;
} else if (val1Priority < val2Priority) {
return kCFCompareGreaterThan;
}
return kCFCompareEqualTo;
}
ClientPtr GetNextClientForDisk(DiskPtr diskPtr)
{
CFMutableArrayRef array = CFArrayCreateMutable(NULL,0,NULL);
ClientPtr lastClient = diskPtr->lastClientAttemptedForUnrecognizedMessages;
ClientPtr clientPtr;
ClientPtr nextClientPtr;
int i = 0;
int count = 0;
int lastClientIndex = 0;
int nextClientIndex = 0;
if (lastClient && (lastClient->pid == 0 || lastClient->port == 0)) {
lastClient = 0x0;
}
for (clientPtr = g.Clients, i = 0; clientPtr != NULL; clientPtr = clientPtr->next, i++)
{
if ((clientPtr->unrecognizedPriority > 0) && (DiskTypeForDisk(diskPtr) & clientPtr->notifyOnDiskTypes)) {
CFArrayAppendValue(array, clientPtr);
}
}
count = CFArrayGetCount(array);
if (0 == count) {
nextClientPtr = nil;
goto Return;
}
if (lastClient && 1 == count && lastClient ==(ClientPtr)CFArrayGetValueAtIndex(array, 0)) {
nextClientPtr = nil;
goto Return;
}
if (!lastClient && 1 == count) {
nextClientPtr = (ClientPtr)CFArrayGetValueAtIndex(array, 0);
goto Return;
}
CFArraySortValues(array, CFRangeMake(0, CFArrayGetCount(array)), compareClientPriorities, NULL);
if (lastClient) {
lastClientIndex = CFArrayGetFirstIndexOfValue(array, CFRangeMake(0, CFArrayGetCount(array)), lastClient);
dwarning(("client was set, index found, %d\n", lastClientIndex));
nextClientIndex = lastClientIndex + 1;
}
if (nextClientIndex == count ) {
nextClientPtr = nil;
goto Return;
} else {
nextClientPtr = (ClientPtr)CFArrayGetValueAtIndex(array, nextClientIndex);
goto Return;
}
Return:
CFRelease(array);
diskPtr->lastClientAttemptedForUnrecognizedMessages = nextClientPtr;
if (nextClientPtr) {
nextClientPtr->ackOnUnrecognizedDisk = diskPtr;
}
return nextClientPtr;
}