#include "DARequest.h"
#include "DABase.h"
#include "DACallback.h"
#include "DADialog.h"
#include "DADissenter.h"
#include "DAFileSystem.h"
#include "DALog.h"
#include "DAMain.h"
#include "DAMount.h"
#include "DAPrivate.h"
#include "DAQueue.h"
#include "DAStage.h"
#include "DASupport.h"
#include "DAThread.h"
#include <fcntl.h>
#include <libproc.h>
#include <unistd.h>
#include <sys/disk.h>
#include <DiskArbitration/DiskArbitration.h>
static void __DARequestClaimCallback( int status, void * context );
static void __DARequestClaimReleaseCallback( CFTypeRef response, void * context );
static void __DARequestEjectCallback( int status, void * context );
static void __DARequestEjectApprovalCallback( CFTypeRef response, void * context );
static int __DARequestEjectEject( void * context );
static void __DARequestMountCallback( int status, CFURLRef mountpoint, void * context );
static void __DARequestMountApprovalCallback( CFTypeRef response, void * context );
static void __DARequestProbeCallback( int status, void * context );
static void __DARequestRefreshCallback( int status, void * context );
static void __DARequestRenameCallback( int status, void * context );
static void __DARequestUnmountCallback( int status, void * context );
static void __DARequestUnmountApprovalCallback( CFTypeRef response, void * context );
static int __DARequestUnmountGetProcessID( void * context );
static void __DARequestMountAuthorizationCallback( DAReturn status, void * context );
static int __DARequestUnmountTickle( void * context );
static void __DARequestUnmountTickleCallback( int status, void * context );
static void __DARequestAuthorize( DARequestRef request,
DAAuthorizeCallback callback,
void * callbackContext,
const char * right )
{
DASessionRef session;
CFArrayRef sessionList;
CFIndex sessionListCount;
CFIndex sessionListIndex;
sessionList = gDASessionList;
sessionListCount = CFArrayGetCount( sessionList );
for ( sessionListIndex = 0; sessionListIndex < sessionListCount; sessionListIndex++ )
{
session = ( void * ) CFArrayGetValueAtIndex( sessionList, sessionListIndex );
if ( strcmp( _DASessionGetName( session ), "SystemUIServer" ) == 0 )
{
break;
}
}
if ( sessionListIndex < sessionListCount )
{
DAAuthorizeWithCallback( session,
_kDAAuthorizeOptionAuthenticateAdministrator,
DARequestGetDisk( request ),
DARequestGetUserUID( request ),
DARequestGetUserGID( request ),
callback,
callbackContext,
right );
}
else
{
( callback )( kDAReturnNotPrivileged, callbackContext );
}
}
static void __DARequestDispatchCallback( DARequestRef request, DADissenterRef dissenter )
{
DACallbackRef callback;
callback = DARequestGetCallback( request );
if ( callback )
{
CFArrayRef link;
link = DARequestGetLink( request );
if ( link )
{
dissenter = DARequestGetDissenter( request );
if ( dissenter == NULL )
{
CFIndex count;
CFIndex index;
count = CFArrayGetCount( link );
for ( index = 0; index < count; index++ )
{
DARequestRef subrequest;
subrequest = ( void * ) CFArrayGetValueAtIndex( link, index );
dissenter = DARequestGetDissenter( subrequest );
if ( dissenter ) break;
}
}
}
DAQueueCallback( callback, DARequestGetDisk( request ), dissenter );
}
}
static Boolean __DARequestClaim( DARequestRef request )
{
DADiskRef disk;
disk = DARequestGetDisk( request );
if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
{
DACallbackRef callback;
callback = DADiskGetClaim( disk );
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
if ( callback )
{
if ( DACallbackGetAddress( callback ) )
{
DADiskClaimReleaseCallback( disk, callback, __DARequestClaimReleaseCallback, request );
}
else
{
DADissenterRef dissenter;
dissenter = DADissenterCreate( kCFAllocatorDefault, kDAReturnNotPermitted );
__DARequestClaimReleaseCallback( dissenter, request );
CFRelease( dissenter );
}
}
else
{
__DARequestClaimReleaseCallback( NULL, request );
}
return FALSE;
}
if ( DARequestGetDissenter( request ) )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
__DARequestDispatchCallback( request, dissenter );
DAStageSignal( );
return TRUE;
}
{
DACallbackRef callback;
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DADiskSetClaim( disk, NULL );
callback = DARequestGetCallback( request );
if ( callback )
{
DASessionRef session;
session = DACallbackGetSession( callback );
if ( session )
{
mach_vm_offset_t address;
mach_vm_offset_t context;
address = ___CFNumberGetIntegerValue( DARequestGetArgument2( request ) );
context = ___CFNumberGetIntegerValue( DARequestGetArgument3( request ) );
callback = DACallbackCreate( kCFAllocatorDefault, session, address, context, _kDADiskClaimReleaseCallback, 0, NULL, NULL );
if ( callback )
{
DADiskSetClaim( disk, callback );
CFRelease( callback );
}
}
}
__DARequestClaimCallback( 0, request );
return TRUE;
}
}
static void __DARequestClaimCallback( int status, void * context )
{
DADiskRef disk;
DARequestRef request = context;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
disk = DARequestGetDisk( request );
DALogDebug( " claimed disk, id = %@, success.", disk );
DARequestDispatchCallback( request, status ? unix_err( status ) : status );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static void __DARequestClaimReleaseCallback( CFTypeRef response, void * context )
{
DARequestRef request = context;
DARequestSetDissenter( request, response );
DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static Boolean __DARequestEject( DARequestRef request )
{
DADiskRef disk;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
disk = DARequestGetDisk( request );
if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWholeKey ) == NULL )
{
status = kDAReturnUnsupported;
}
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWholeKey ) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
DADiskEjectApprovalCallback( disk, __DARequestEjectApprovalCallback, request );
return FALSE;
}
}
if ( DARequestGetDissenter( request ) )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
__DARequestDispatchCallback( request, dissenter );
DAStageSignal( );
return TRUE;
}
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
DALogDebug( " ejected disk, id = %@, ongoing.", disk );
DAThreadExecute( __DARequestEjectEject, disk, __DARequestEjectCallback, request );
return TRUE;
}
else
{
return FALSE;
}
}
static void __DARequestEjectCallback( int status, void * context )
{
DADiskRef disk;
DARequestRef request = context;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
disk = DARequestGetDisk( request );
if ( status )
{
DADissenterRef dissenter;
DALogDebug( " ejected disk, id = %@, failure.", disk );
DALogDebug( "unable to eject %@ (status code 0x%08X).", disk, status );
dissenter = DADissenterCreate( kCFAllocatorDefault, unix_err( status ) );
DARequestSetDissenter( request, dissenter );
CFRelease( dissenter );
}
else
{
DALogDebug( " ejected disk, id = %@, success.", disk );
}
DARequestDispatchCallback( request, status ? unix_err( status ) : status );
DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static void __DARequestEjectApprovalCallback( CFTypeRef response, void * context )
{
DARequestRef request = context;
DARequestSetDissenter( request, response );
DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static int __DARequestEjectEject( void * context )
{
DADiskRef disk = context;
int file;
int status;
file = open( DADiskGetBSDPath( disk, TRUE ), O_RDONLY );
if ( file == -1 )
{
status = errno;
}
else
{
status = ioctl( file, DKIOCEJECT, NULL );
if ( status == -1 )
{
status = ( errno == ENOTTY ) ? 0 : errno;
}
close( file );
}
return status;
}
static Boolean __DARequestMount( DARequestRef request )
{
DADiskRef disk;
disk = DARequestGetDisk( request );
if ( DARequestGetLink( request ) )
{
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) )
{
return FALSE;
}
}
if ( DARequestGetState( request, kDARequestStateStagedProbe ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaPathKey ) == NULL )
{
status = kDAReturnUnsupported;
}
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) )
{
CFStringRef arguments;
arguments = DARequestGetArgument3( request );
if ( arguments == NULL || DAMountContainsArgument( arguments, kDAFileSystemMountArgumentUpdate ) == FALSE )
{
status = kDAReturnBusy;
}
}
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
{
if ( DARequestGetUserUID( request ) )
{
CFTypeRef mountpoint;
mountpoint = DARequestGetArgument2( request );
if ( mountpoint )
{
mountpoint = CFURLCreateWithString( kCFAllocatorDefault, mountpoint, NULL );
}
if ( mountpoint )
{
char * path;
path = ___CFURLCopyFileSystemRepresentation( mountpoint );
if ( path )
{
struct stat st;
if ( stat( path, &st ) == 0 )
{
if ( st.st_uid != DARequestGetUserUID( request ) )
{
status = kDAReturnNotPermitted;
}
}
free( path );
}
CFRelease( mountpoint );
}
}
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
DARequestSetState( request, kDARequestStateStagedProbe, TRUE );
CFStringRef arguments;
arguments = DARequestGetArgument3( request );
if ( arguments == NULL || CFEqual( arguments, CFSTR( "automatic" ) ) == FALSE )
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
{
DADiskSetState( disk, kDADiskStateStagedProbe, FALSE );
DAStageSignal( );
return FALSE;
}
}
}
else
{
if ( DADiskGetState( disk, kDADiskStateStagedProbe ) == FALSE )
{
return FALSE;
}
}
if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
DADiskMountApprovalCallback( disk, __DARequestMountApprovalCallback, request );
return FALSE;
}
}
if ( DARequestGetState( request, _kDARequestStateStagedAuthorize ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DARequestGetDissenter( request ) )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF01 )
{
DARequestSetDissenter( request, NULL );
status = kDAReturnNotPrivileged;
}
else if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF02 )
{
DARequestSetDissenter( request, NULL );
DARequestSetState( request, _kDARequestStateMountArgumentNoWrite, TRUE );
}
else if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF03 )
{
DARequestSetDissenter( request, NULL );
DARequestSetState( request, _kDARequestStateMountArgumentNoWrite, TRUE );
status = kDAReturnNotPrivileged;
}
}
if ( status )
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DARequestSetState( request, _kDARequestStateStagedAuthorize, TRUE );
__DARequestAuthorize( request, __DARequestMountAuthorizationCallback, request, _kDAAuthorizeRightMount );
return FALSE;
}
else
{
DARequestSetState( request, _kDARequestStateStagedAuthorize, TRUE );
}
}
if ( DARequestGetDissenter( request ) )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
__DARequestDispatchCallback( request, dissenter );
DAStageSignal( );
return TRUE;
}
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
{
CFTypeRef path;
path = DARequestGetArgument2( request );
if ( path )
{
path = CFURLCreateWithString( kCFAllocatorDefault, path, NULL );
}
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
if ( DARequestGetState( request, _kDARequestStateMountArgumentNoWrite ) )
{
DAMountWithArguments( disk, path, __DARequestMountCallback, request, kDAFileSystemMountArgumentNoWrite, DARequestGetArgument3( request ), NULL );
}
else
DAMountWithArguments( disk, path, __DARequestMountCallback, request, DARequestGetArgument3( request ), NULL );
if ( path )
{
CFRelease( path );
}
return TRUE;
}
else
{
return FALSE;
}
}
static void __DARequestMountCallback( int status, CFURLRef mountpoint, void * context )
{
DADiskRef disk;
DARequestRef request = context;
disk = DARequestGetDisk( request );
if ( status )
{
DADissenterRef dissenter;
dissenter = DADissenterCreate( kCFAllocatorDefault, unix_err( status ) );
DARequestSetDissenter( request, dissenter );
CFRelease( dissenter );
}
else
{
CFStringRef arguments;
DADiskSetBypath( disk, mountpoint );
DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, mountpoint );
arguments = DARequestGetArgument3( request );
if ( arguments == NULL || DAMountContainsArgument( arguments, kDAFileSystemMountArgumentUpdate ) == FALSE )
{
DADiskDescriptionChangedCallback( disk, kDADiskDescriptionVolumePathKey );
}
}
DARequestDispatchCallback( request, status ? unix_err( status ) : status );
DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static void __DARequestMountApprovalCallback( CFTypeRef response, void * context )
{
DARequestRef request = context;
DARequestSetDissenter( request, response );
DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static void __DARequestMountAuthorizationCallback( DAReturn status, void * context )
{
DARequestRef request = context;
if ( status )
{
DADissenterRef dissenter;
dissenter = DADissenterCreate( kCFAllocatorDefault, status );
DARequestSetDissenter( request, dissenter );
CFRelease( dissenter );
}
DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static Boolean __DARequestProbe( DARequestRef request )
{
DADiskRef disk;
disk = DARequestGetDisk( request );
if ( DARequestGetState( request, kDARequestStateStagedProbe ) == FALSE )
{
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
{
DARequestSetState( request, kDARequestStateStagedProbe, TRUE );
DADiskSetState( disk, kDADiskStateStagedProbe, FALSE );
DAStageSignal( );
return FALSE;
}
}
else
{
if ( DADiskGetState( disk, kDADiskStateStagedProbe ) == FALSE )
{
return FALSE;
}
}
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) )
{
status = kDAReturnBusy;
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
__DARequestProbeCallback( 0, request );
return TRUE;
}
}
else
{
return FALSE;
}
}
static void __DARequestProbeCallback( int status, void * context )
{
DADiskRef disk;
DARequestRef request = context;
disk = DARequestGetDisk( request );
DARequestDispatchCallback( request, status ? unix_err( status ) : status );
DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static Boolean __DARequestRefresh( DARequestRef request )
{
DADiskRef disk;
disk = DARequestGetDisk( request );
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
status = _DADiskRefresh( disk );
__DARequestRefreshCallback( status ? ENOTSUP : 0, request );
return TRUE;
}
}
}
static void __DARequestRefreshCallback( int status, void * context )
{
DADiskRef disk;
DARequestRef request = context;
disk = DARequestGetDisk( request );
DARequestDispatchCallback( request, status ? unix_err( status ) : status );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static Boolean __DARequestRename( DARequestRef request )
{
DADiskRef disk;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
disk = DARequestGetDisk( request );
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
{
status = kDAReturnNotMounted;
}
if ( DARequestGetArgument2( request ) == NULL )
{
status = kDAReturnUnsupported;
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
DALogDebug( " renamed disk, id = %@, ongoing.", disk );
DAFileSystemRename( DADiskGetFileSystem( disk ),
DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ),
DARequestGetArgument2( request ),
__DARequestRenameCallback,
request );
return TRUE;
}
}
else
{
return FALSE;
}
}
static void __DARequestRenameCallback( int status, void * context )
{
DADiskRef disk;
DARequestRef request = context;
disk = DARequestGetDisk( request );
if ( status )
{
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
DALogDebug( " renamed disk, id = %@, failure.", disk );
DALogDebug( "unable to rename %@ (status code 0x%08X).", disk, status );
}
else
{
CFMutableArrayRef keys;
keys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
if ( keys )
{
CFStringRef name;
name = DARequestGetArgument2( request );
if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeNameKey, name ) )
{
CFURLRef mountpoint;
DADiskSetDescription( disk, kDADiskDescriptionVolumeNameKey, name );
CFArrayAppendValue( keys, kDADiskDescriptionVolumeNameKey );
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
if ( CFEqual( CFURLGetString( mountpoint ), CFSTR( "file:///" ) ) )
{
mountpoint = DAMountCreateMountPointWithAction( disk, kDAMountPointActionMove );
if ( mountpoint )
{
DADiskSetBypath( disk, mountpoint );
CFRelease( mountpoint );
}
}
else
{
mountpoint = DAMountCreateMountPointWithAction( disk, kDAMountPointActionMove );
if ( mountpoint )
{
DADiskSetBypath( disk, mountpoint );
DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, mountpoint );
CFArrayAppendValue( keys, kDADiskDescriptionVolumePathKey );
CFRelease( mountpoint );
}
}
DADiskDescriptionChangedCallback( disk, keys );
}
CFRelease( keys );
}
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
DALogDebug( " renamed disk, id = %@, success.", disk );
}
DARequestDispatchCallback( request, status ? unix_err( status ) : status );
DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static Boolean __DARequestUnmount( DARequestRef request )
{
DADiskRef disk;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
disk = DARequestGetDisk( request );
if ( DARequestGetLink( request ) )
{
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) )
{
return FALSE;
}
}
if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
{
DAReturn status;
status = kDAReturnSuccess;
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
{
status = kDAReturnUnsupported;
}
if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
{
status = kDAReturnNotMounted;
}
else
{
CFURLRef mountpoint;
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
if ( CFEqual( CFURLGetString( mountpoint ), CFSTR( "file:///" ) ) )
{
DADissenterRef dissenter;
status = kDAReturnBusy;
dissenter = DADissenterCreate( kCFAllocatorDefault, status );
DARequestSetDissenter( request, dissenter );
CFRelease( dissenter );
}
}
if ( status )
{
DARequestDispatchCallback( request, status );
DAStageSignal( );
return TRUE;
}
else
{
if ( DADiskGetState( disk, kDADiskStateZombie ) )
{
DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue )
{
Boolean dialog = TRUE;
CFURLRef mountpoint;
char * path;
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
path = ___CFURLCopyFileSystemRepresentation( mountpoint );
if ( path )
{
struct statfs fs;
int status;
status = ___statfs( path, &fs, MNT_NOWAIT );
if ( status == 0 )
{
if ( ( fs.f_flags & MNT_RDONLY ) )
{
dialog = FALSE;
}
}
free( path );
}
if ( dialog )
{
DADialogShowDeviceRemoval( disk );
}
}
}
else
{
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue )
{
DAThreadExecute( __DARequestUnmountTickle, disk, __DARequestUnmountTickleCallback, request );
return FALSE;
}
DADiskUnmountApprovalCallback( disk, __DARequestUnmountApprovalCallback, request );
return FALSE;
}
}
}
if ( DARequestGetDissenter( request ) )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
__DARequestDispatchCallback( request, dissenter );
DAStageSignal( );
return TRUE;
}
if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
{
DADiskUnmountOptions options;
options = DARequestGetArgument1( request );
CFRetain( request );
DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
DALogDebug( " unmounted disk, id = %@, ongoing.", disk );
DAFileSystemUnmountWithArguments( DADiskGetFileSystem( disk ),
DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ),
__DARequestUnmountCallback,
request,
( options & kDADiskUnmountOptionForce ) ? kDAFileSystemUnmountArgumentForce : NULL,
NULL );
return TRUE;
}
else
{
return FALSE;
}
}
static void __DARequestUnmountCallback( int status, void * context )
{
DADiskRef disk;
DARequestRef request = context;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
disk = DARequestGetDisk( request );
if ( status )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
if ( dissenter == NULL )
{
DALogDebug( " unmounted disk, id = %@, failure.", disk );
DALogDebug( "unable to unmount %@ (status code 0x%08X).", disk, status );
status = EBUSY;
dissenter = DADissenterCreate( kCFAllocatorDefault, unix_err( status ) );
DARequestSetDissenter( request, dissenter );
DAThreadExecute( __DARequestUnmountGetProcessID, request, __DARequestUnmountCallback, request );
CFRelease( dissenter );
return;
}
__DARequestDispatchCallback( request, dissenter );
}
else
{
CFURLRef mountpoint;
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
DAMountRemoveMountPoint( mountpoint );
DADiskSetBypath( disk, NULL );
DALogDebug( " unmounted disk, id = %@, success.", disk );
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaPathKey ) )
{
DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, NULL );
DADiskDescriptionChangedCallback( disk, kDADiskDescriptionVolumePathKey );
}
else
{
DALogDebug( " removed disk, id = %@.", disk );
DADiskDisappearedCallback( disk );
DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, NULL );
DADiskSetState( disk, kDADiskStateZombie, TRUE );
___CFArrayRemoveValue( gDADiskList, disk );
}
__DARequestDispatchCallback( request, NULL );
}
DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static void __DARequestUnmountApprovalCallback( CFTypeRef response, void * context )
{
DARequestRef request = context;
if ( response )
{
DADiskUnmountOptions options;
options = DARequestGetArgument1( request );
if ( ( options & kDADiskUnmountOptionForce ) == 0 )
{
DARequestSetDissenter( request, response );
}
if ( DADissenterGetStatus( response ) == 0xF8DAFF01 )
{
DARequestSetDissenter( request, response );
}
}
DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
DAStageSignal( );
CFRelease( request );
}
static int __DARequestUnmountGetProcessID( void * context )
{
DADiskRef disk;
CFURLRef mountpoint;
char * path;
DARequestRef request = context;
disk = DARequestGetDisk( request );
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
path = ___CFURLCopyFileSystemRepresentation( mountpoint );
if ( path )
{
pid_t dissenterPID = 0;
proc_listpidspath( PROC_ALL_PIDS, 0, path, PROC_LISTPIDSPATH_EXCLUDE_EVTONLY | PROC_LISTPIDSPATH_PATH_IS_VOLUME, &dissenterPID, sizeof( dissenterPID ) );
if ( dissenterPID )
{
DADissenterRef dissenter;
dissenter = DARequestGetDissenter( request );
DADissenterSetProcessID( dissenter, dissenterPID );
}
free( path );
}
return -1;
}
static int __DARequestUnmountTickle( void * context )
{
DADiskRef disk = context;
size_t size;
size = ___CFNumberGetIntegerValue( DADiskGetDescription( disk, kDADiskDescriptionMediaBlockSizeKey ) );
if ( size )
{
char * buffer;
buffer = malloc( size );
if ( buffer )
{
int file;
file = open( DADiskGetBSDPath( disk, TRUE ), O_RDONLY );
if ( file != -1 )
{
read( file, buffer, size );
close( file );
}
free( buffer );
}
}
return 0;
}
static void __DARequestUnmountTickleCallback( int status, void * context )
{
DADiskUnmountApprovalCallback( DARequestGetDisk( context ), __DARequestUnmountApprovalCallback, context );
}
DARequestRef DARequestCreate( CFAllocatorRef allocator,
_DARequestKind kind,
DADiskRef argument0,
CFIndex argument1,
CFTypeRef argument2,
CFTypeRef argument3,
uid_t userUID,
gid_t userGID,
DACallbackRef callback )
{
CFMutableDictionaryRef request;
request = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
if ( request )
{
___CFDictionarySetIntegerValue( request, _kDARequestKindKey, kind );
if ( argument0 ) CFDictionarySetValue( request, _kDARequestDiskKey, argument0 );
if ( argument1 ) ___CFDictionarySetIntegerValue( request, _kDARequestArgument1Key, argument1 );
if ( argument2 ) CFDictionarySetValue( request, _kDARequestArgument2Key, argument2 );
if ( argument3 ) CFDictionarySetValue( request, _kDARequestArgument3Key, argument3 );
___CFDictionarySetIntegerValue( request, _kDARequestUserGIDKey, userGID );
___CFDictionarySetIntegerValue( request, _kDARequestUserUIDKey, userUID );
if ( callback ) CFDictionarySetValue( request, _kDARequestCallbackKey, callback );
}
return ( void * ) request;
}
Boolean DARequestDispatch( DARequestRef request )
{
Boolean dispatch;
dispatch = FALSE;
if ( request )
{
DADiskRef disk;
disk = DARequestGetDisk( request );
if ( disk )
{
if ( DADiskGetState( disk, kDADiskStateCommandActive ) == FALSE )
{
if ( DADiskGetState( disk, kDADiskStateStagedAppear ) )
{
switch ( DARequestGetKind( request ) )
{
case _kDADiskClaim:
{
dispatch = __DARequestClaim( request );
break;
}
case _kDADiskEject:
{
dispatch = __DARequestEject( request );
break;
}
case _kDADiskMount:
{
dispatch = __DARequestMount( request );
break;
}
case _kDADiskProbe:
{
dispatch = __DARequestProbe( request );
break;
}
case _kDADiskRefresh:
{
dispatch = __DARequestRefresh( request );
break;
}
case _kDADiskRename:
{
dispatch = __DARequestRename( request );
break;
}
case _kDADiskUnmount:
{
dispatch = __DARequestUnmount( request );
break;
}
}
}
}
}
}
return dispatch;
}
void DARequestDispatchCallback( DARequestRef request, DAReturn status )
{
if ( status )
{
DADissenterRef dissenter;
dissenter = DADissenterCreate( kCFAllocatorDefault, status );
__DARequestDispatchCallback( request, dissenter );
CFRelease( dissenter );
}
else
{
__DARequestDispatchCallback( request, NULL );
}
}
CFIndex DARequestGetArgument1( DARequestRef request )
{
return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestArgument1Key );
}
CFTypeRef DARequestGetArgument2( DARequestRef request )
{
return CFDictionaryGetValue( ( void * ) request, _kDARequestArgument2Key );
}
CFTypeRef DARequestGetArgument3( DARequestRef request )
{
return CFDictionaryGetValue( ( void * ) request, _kDARequestArgument3Key );
}
DACallbackRef DARequestGetCallback( DARequestRef request )
{
return ( void * ) CFDictionaryGetValue( ( void * ) request, _kDARequestCallbackKey );
}
DADiskRef DARequestGetDisk( DARequestRef request )
{
return ( void * ) CFDictionaryGetValue( ( void * ) request, _kDARequestDiskKey );
}
DADissenterRef DARequestGetDissenter( DARequestRef request )
{
return CFDictionaryGetValue( ( void * ) request, _kDARequestDissenterKey );
}
_DARequestKind DARequestGetKind( DARequestRef request )
{
return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestKindKey );
}
CFArrayRef DARequestGetLink( DARequestRef request )
{
return CFDictionaryGetValue( ( void * ) request, _kDARequestLinkKey );
}
Boolean DARequestGetState( DARequestRef request, DARequestState state )
{
return ( ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestStateKey ) & state ) ? TRUE : FALSE;
}
gid_t DARequestGetUserGID( DARequestRef request )
{
return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestUserGIDKey );
}
uid_t DARequestGetUserUID( DARequestRef request )
{
return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestUserUIDKey );
}
void DARequestSetCallback( DARequestRef request, DACallbackRef callback )
{
if ( callback )
{
CFDictionarySetValue( ( void * ) request, _kDARequestCallbackKey, callback );
}
else
{
CFDictionaryRemoveValue( ( void * ) request, _kDARequestCallbackKey );
}
}
void DARequestSetDissenter( DARequestRef request, DADissenterRef dissenter )
{
if ( dissenter )
{
CFDictionarySetValue( ( void * ) request, _kDARequestDissenterKey, dissenter );
}
else
{
CFDictionaryRemoveValue( ( void * ) request, _kDARequestDissenterKey );
}
}
void DARequestSetLink( DARequestRef request, CFArrayRef link )
{
if ( link )
{
CFDictionarySetValue( ( void * ) request, _kDARequestLinkKey, link );
}
else
{
CFDictionaryRemoveValue( ( void * ) request, _kDARequestLinkKey );
}
}
void DARequestSetState( DARequestRef request, DARequestState state, Boolean value )
{
if ( value )
{
state = ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestStateKey ) | state;
}
else
{
state = ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestStateKey ) & ~state;
}
___CFDictionarySetIntegerValue( ( void * ) request, _kDARequestStateKey, state );
}