#include "DAMount.h"
#include "DABase.h"
#include "DAInternal.h"
#include "DALog.h"
#include "DAMain.h"
#include "DASupport.h"
#include <fstab.h>
#include <sys/stat.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
struct __DAMountCallbackContext
{
Boolean automatic;
IOPMAssertionID assertionID;
DAMountCallback callback;
void * callbackContext;
DADiskRef disk;
Boolean force;
CFURLRef mountpoint;
CFStringRef options;
};
typedef struct __DAMountCallbackContext __DAMountCallbackContext;
static void __DAMountWithArgumentsCallbackStage1( int status, void * context );
static void __DAMountWithArgumentsCallbackStage2( int status, void * context );
static void __DAMountWithArgumentsCallbackStage3( int status, void * context );
static void __DAMountWithArgumentsCallback( int status, void * parameter )
{
__DAMountCallbackContext * context = parameter;
if ( context->automatic )
{
if ( status == ___EDIRTY )
{
DAMountWithArguments( context->disk, NULL, context->callback, context->callbackContext, kDAFileSystemMountArgumentForce, kDAFileSystemMountArgumentNoWrite, NULL );
context->callback = NULL;
}
}
if ( context->callback )
{
( context->callback )( status, context->mountpoint, context->callbackContext );
}
CFRelease( context->disk );
CFRelease( context->options );
if ( context->mountpoint ) CFRelease( context->mountpoint );
free( context );
}
static void __DAMountWithArgumentsCallbackStage1( int status, void * parameter )
{
__DAMountCallbackContext * context = parameter;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
if ( context->assertionID != kIOPMNullAssertionID )
{
IOPMAssertionRelease( context->assertionID );
context->assertionID = kIOPMNullAssertionID;
}
if ( status )
{
if ( status == ECANCELED )
{
status = 0;
}
else
{
DALogDebug( " repaired disk, id = %@, failure.", context->disk );
DALogError( "unable to repair %@ (status code 0x%08X).", context->disk, status );
if ( context->force )
{
status = 0;
}
else
{
__DAMountWithArgumentsCallback( ___EDIRTY, context );
}
}
}
else
{
DADiskSetState( context->disk, kDADiskStateRequireRepair, FALSE );
DALogDebug( " repaired disk, id = %@, success.", context->disk );
}
if ( status == 0 )
{
if ( context->mountpoint == NULL )
{
context->mountpoint = DAMountCreateMountPointWithAction( context->disk, kDAMountPointActionMake );
}
if ( context->mountpoint )
{
DALogDebug( " mounted disk, id = %@, ongoing.", context->disk );
DAFileSystemMountWithArguments( DADiskGetFileSystem( context->disk ),
DADiskGetDevice( context->disk ),
context->mountpoint,
DADiskGetUserUID( context->disk ),
DADiskGetUserGID( context->disk ),
__DAMountWithArgumentsCallbackStage2,
context,
context->options,
NULL );
}
else
{
__DAMountWithArgumentsCallback( ENOSPC, context );
}
}
}
static void __DAMountWithArgumentsCallbackStage2( int status, void * parameter )
{
__DAMountCallbackContext * context = parameter;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
if ( status )
{
DALogDebug( " mounted disk, id = %@, failure.", context->disk );
DALogError( "unable to mount %@ (status code 0x%08X).", context->disk, status );
DAMountRemoveMountPoint( context->mountpoint );
__DAMountWithArgumentsCallback( status, context );
}
else
{
DALogDebug( " mounted disk, id = %@, success.", context->disk );
if ( DADiskGetState( context->disk, kDADiskStateRequireRepairQuotas ) )
{
DAFileSystemRepairQuotas( DADiskGetFileSystem( context->disk ),
context->mountpoint,
__DAMountWithArgumentsCallbackStage3,
context );
}
else
{
__DAMountWithArgumentsCallbackStage3( 0, context );
}
}
}
static void __DAMountWithArgumentsCallbackStage3( int status, void * parameter )
{
__DAMountCallbackContext * context = parameter;
if ( status )
{
DALogError( "unable to repair quotas on disk %@ (status code 0x%08X).", context->disk, status );
}
else
{
DADiskSetState( context->disk, kDADiskStateRequireRepairQuotas, FALSE );
}
__DAMountWithArgumentsCallback( 0, context );
}
void DAMount( DADiskRef disk, CFURLRef mountpoint, DAMountCallback callback, void * callbackContext )
{
return DAMountWithArguments( disk, mountpoint, callback, callbackContext, NULL );
}
Boolean DAMountContainsArgument( CFStringRef arguments, CFStringRef argument )
{
CFBooleanRef argumentValue;
CFBooleanRef argumentsValue;
argumentsValue = NULL;
if ( CFStringHasPrefix( argument, CFSTR( "no" ) ) )
{
argument = CFStringCreateWithSubstring( kCFAllocatorDefault, argument, CFRangeMake( 2, CFStringGetLength( argument ) - 2 ) );
argumentValue = kCFBooleanFalse;
}
else
{
argument = CFRetain( argument );
argumentValue = kCFBooleanTrue;
}
if ( argument )
{
CFArrayRef argumentList;
CFIndex argumentListCount;
CFIndex argumentListIndex;
argumentList = CFStringCreateArrayBySeparatingStrings( kCFAllocatorDefault, arguments, CFSTR( "," ) );
if ( argumentList )
{
argumentListCount = CFArrayGetCount( argumentList );
for ( argumentListIndex = 0; argumentListIndex < argumentListCount; argumentListIndex++ )
{
CFStringRef compare;
compare = CFArrayGetValueAtIndex( argumentList, argumentListIndex );
if ( compare )
{
CFBooleanRef compareValue;
if ( CFStringHasPrefix( compare, CFSTR( "no" ) ) )
{
compare = CFStringCreateWithSubstring( kCFAllocatorDefault, compare, CFRangeMake( 2, CFStringGetLength( compare ) - 2 ) );
compareValue = kCFBooleanFalse;
}
else
{
compare = CFRetain( compare );
compareValue = kCFBooleanTrue;
}
if ( compare )
{
if ( CFEqual( compare, CFSTR( FSTAB_RO ) ) )
{
CFRelease( compare );
compare = CFRetain( kDAFileSystemMountArgumentNoWrite );
compareValue = compareValue;
}
if ( CFEqual( compare, CFSTR( FSTAB_RW ) ) )
{
CFRelease( compare );
compare = CFRetain( kDAFileSystemMountArgumentNoWrite );
compareValue = ( compareValue == kCFBooleanTrue ) ? kCFBooleanFalse : kCFBooleanTrue;
}
}
if ( compare )
{
if ( CFEqual( argument, compare ) )
{
argumentsValue = compareValue;
}
CFRelease( compare );
}
}
}
CFRelease( argumentList );
}
CFRelease( argument );
}
return ( argumentValue == argumentsValue ) ? TRUE : FALSE;
}
CFURLRef DAMountCreateMountPoint( DADiskRef disk )
{
return DAMountCreateMountPointWithAction( disk, kDAMountPointActionMake );
}
CFURLRef DAMountCreateMountPointWithAction( DADiskRef disk, DAMountPointAction action )
{
CFIndex index;
CFURLRef mountpoint;
char name[MAXPATHLEN];
char path[MAXPATHLEN];
CFStringRef string;
mountpoint = NULL;
string = DADiskGetDescription( disk, kDADiskDescriptionVolumeNameKey );
if ( string )
{
if ( CFStringGetLength( string ) )
{
CFRetain( string );
}
else
{
string = NULL;
}
}
if ( string == NULL )
{
string = ___CFBundleCopyLocalizedStringInDirectory( gDABundlePath, CFSTR( "Untitled" ), CFSTR( "Untitled" ), NULL );
}
if ( ___CFStringGetCString( string, name, MNAMELEN - 20 ) )
{
while ( strchr( name, '/' ) )
{
*strchr( name, '/' ) = ':';
}
for ( index = 0; index < 100; index++ )
{
if ( index == 0 )
{
snprintf( path, sizeof( path ), "%s/%s", kDAMainMountPointFolder, name );
}
else
{
snprintf( path, sizeof( path ), "%s/%s %lu", kDAMainMountPointFolder, name, index );
}
switch ( action )
{
case kDAMountPointActionLink:
{
CFURLRef url;
url = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
if ( url )
{
char source[MAXPATHLEN];
if ( CFURLGetFileSystemRepresentation( url, TRUE, ( void * ) source, sizeof( source ) ) )
{
if ( symlink( source, path ) == 0 )
{
mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
}
}
}
break;
}
case kDAMountPointActionMake:
{
if ( mkdir( path, 0111 ) == 0 )
{
if ( DADiskGetUserUID( disk ) )
{
chown( path, DADiskGetUserUID( disk ), -1 );
}
mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
}
break;
}
case kDAMountPointActionMove:
{
CFURLRef url;
url = DADiskGetBypath( disk );
if ( url )
{
char source[MAXPATHLEN];
if ( CFURLGetFileSystemRepresentation( url, TRUE, ( void * ) source, sizeof( source ) ) )
{
if ( rename( source, path ) == 0 )
{
mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
}
}
}
break;
}
case kDAMountPointActionNone:
{
mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
break;
}
}
if ( mountpoint )
{
break;
}
}
}
CFRelease( string );
return mountpoint;
}
Boolean DAMountGetPreference( DADiskRef disk, DAMountPreference preference )
{
CFBooleanRef value;
switch ( preference )
{
case kDAMountPreferenceDefer:
{
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaRemovableKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferRemovableKey );
value = value ? value : kCFBooleanTrue;
}
else
{
if ( DADiskGetDescription( disk, kDADiskDescriptionDeviceInternalKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferInternalKey );
value = value ? value : kCFBooleanFalse;
}
else
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferExternalKey );
value = value ? value : kCFBooleanTrue;
}
}
break;
}
case kDAMountPreferenceTrust:
{
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaRemovableKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustRemovableKey );
value = value ? value : kCFBooleanFalse;
}
else
{
if ( DADiskGetDescription( disk, kDADiskDescriptionDeviceInternalKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustInternalKey );
value = value ? value : kCFBooleanTrue;
}
else
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustExternalKey );
value = value ? value : kCFBooleanFalse;
}
}
break;
}
default:
{
value = kCFBooleanFalse;
break;
}
}
assert( value );
return CFBooleanGetValue( value );
}
void DAMountRemoveMountPoint( CFURLRef mountpoint )
{
char path[MAXPATHLEN];
if ( CFURLGetFileSystemRepresentation( mountpoint, TRUE, ( void * ) path, sizeof( path ) ) )
{
if ( ___isautofs( path ) == 0 )
{
Boolean remove;
remove = FALSE;
if ( strncmp( path, kDAMainMountPointFolder, strlen( kDAMainMountPointFolder ) ) == 0 )
{
if ( strrchr( path + strlen( kDAMainMountPointFolder ), '/' ) == path + strlen( kDAMainMountPointFolder ) )
{
remove = TRUE;
}
}
{
char file[MAXPATHLEN];
strlcpy( file, path, sizeof( file ) );
strlcat( file, "/", sizeof( file ) );
strlcat( file, kDAMainMountPointFolderCookieFile, sizeof( file ) );
if ( unlink( file ) == 0 )
{
remove = TRUE;
}
}
if ( remove )
{
rmdir( path );
}
}
}
}
void DAMountWithArguments( DADiskRef disk, CFURLRef mountpoint, DAMountCallback callback, void * callbackContext, ... )
{
CFStringRef argument = NULL;
va_list arguments;
CFBooleanRef automatic = kCFBooleanTrue;
CFBooleanRef check = NULL;
__DAMountCallbackContext * context = NULL;
CFIndex count = 0;
DAFileSystemRef filesystem = DADiskGetFileSystem( disk );
Boolean force = FALSE;
CFIndex index = 0;
CFDictionaryRef map = NULL;
CFMutableStringRef options = NULL;
int status = 0;
DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
if ( mountpoint )
{
CFRetain( mountpoint );
}
context = malloc( sizeof( __DAMountCallbackContext ) );
if ( context == NULL )
{
status = ENOMEM;
goto DAMountWithArgumentsErr;
}
options = CFStringCreateMutable( kCFAllocatorDefault, 0 );
if ( options == NULL )
{
status = ENOMEM;
goto DAMountWithArgumentsErr;
}
va_start( arguments, callbackContext );
while ( ( argument = va_arg( arguments, CFStringRef ) ) )
{
if ( CFEqual( argument, kDAFileSystemMountArgumentForce ) )
{
force = TRUE;
}
else if ( CFEqual( argument, CFSTR( "automatic" ) ) )
{
automatic = NULL;
check = kCFBooleanTrue;
}
else
{
CFStringAppend( options, argument );
CFStringAppend( options, CFSTR( "," ) );
}
}
va_end( arguments );
CFStringTrim( options, CFSTR( "," ) );
context->automatic = ( automatic == NULL ) ? TRUE : FALSE;
if ( DAMountContainsArgument( options, kDAFileSystemMountArgumentUpdate ) )
{
if ( mountpoint )
{
status = EINVAL;
goto DAMountWithArgumentsErr;
}
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
if ( mountpoint == NULL )
{
status = EINVAL;
goto DAMountWithArgumentsErr;
}
CFRetain( mountpoint );
}
count = CFArrayGetCount( gDAMountMapList1 );
for ( index = 0; index < count; index++ )
{
map = CFArrayGetValueAtIndex( gDAMountMapList1, index );
if ( map )
{
CFTypeRef id;
CFStringRef kind;
id = CFDictionaryGetValue( map, kDAMountMapProbeIDKey );
kind = CFDictionaryGetValue( map, kDAMountMapProbeKindKey );
if ( kind )
{
if ( CFEqual( kind, DAFileSystemGetKind( filesystem ) ) == FALSE )
{
continue;
}
}
if ( CFGetTypeID( id ) == CFUUIDGetTypeID( ) )
{
if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeUUIDKey, id ) == kCFCompareEqualTo )
{
break;
}
}
else if ( CFGetTypeID( id ) == CFStringGetTypeID( ) )
{
if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeNameKey, id ) == kCFCompareEqualTo )
{
break;
}
}
else if ( CFGetTypeID( id ) == CFDictionaryGetTypeID( ) )
{
boolean_t match = FALSE;
IOServiceMatchPropertyTable( DADiskGetIOMedia( disk ), id, &match );
if ( match )
{
break;
}
}
}
}
if ( index < count )
{
CFStringRef string;
if ( automatic == NULL )
{
automatic = CFDictionaryGetValue( map, kDAMountMapMountAutomaticKey );
if ( automatic == kCFBooleanTrue )
{
DADiskSetState( disk, _kDADiskStateMountAutomatic, TRUE );
DADiskSetState( disk, _kDADiskStateMountAutomaticNoDefer, TRUE );
}
}
string = CFDictionaryGetValue( map, kDAMountMapMountOptionsKey );
if ( string )
{
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, string );
}
if ( mountpoint == NULL )
{
mountpoint = CFDictionaryGetValue( map, kDAMountMapMountPathKey );
if ( mountpoint )
{
CFRetain( mountpoint );
}
}
}
count = CFArrayGetCount( gDAMountMapList2 );
for ( index = 0; index < count; index++ )
{
map = CFArrayGetValueAtIndex( gDAMountMapList2, index );
if ( map )
{
CFTypeRef id;
id = CFDictionaryGetValue( map, kDAMountMapProbeIDKey );
if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeUUIDKey, id ) == kCFCompareEqualTo )
{
break;
}
}
}
if ( index < count )
{
CFStringRef string;
string = CFDictionaryGetValue( map, kDAMountMapMountOptionsKey );
if ( string )
{
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, string );
}
}
if ( automatic == NULL )
{
if ( DADiskGetState( disk, _kDADiskStateMountAutomatic ) )
{
if ( DADiskGetState( disk, _kDADiskStateMountAutomaticNoDefer ) )
{
automatic = kCFBooleanTrue;
}
}
else
{
automatic = kCFBooleanFalse;
}
if ( automatic == NULL )
{
if ( gDAConsoleUserList == NULL )
{
if ( DAMountGetPreference( disk, kDAMountPreferenceDefer ) )
{
automatic = kCFBooleanFalse;
}
}
}
}
if ( automatic == kCFBooleanFalse )
{
status = ECANCELED;
goto DAMountWithArgumentsErr;
}
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanFalse )
{
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, kDAFileSystemMountArgumentNoWrite );
}
if ( DAMountGetPreference( disk, kDAMountPreferenceTrust ) == FALSE )
{
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, kDAFileSystemMountArgumentNoSetUserID );
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, kDAFileSystemMountArgumentNoOwnership );
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, kDAFileSystemMountArgumentNoDevice );
}
if ( CFEqual( DAFileSystemGetKind( filesystem ), CFSTR( "hfs" ) ) )
{
___CFStringInsertFormat( options, 0, CFSTR( "-m=%o," ), 0755 );
if ( DADiskGetUserGID( disk ) )
{
___CFStringInsertFormat( options, 0, CFSTR( "-g=%d," ), DADiskGetUserGID( disk ) );
}
else
{
___CFStringInsertFormat( options, 0, CFSTR( "-g=%d," ), ___GID_UNKNOWN );
}
if ( DADiskGetUserUID( disk ) )
{
___CFStringInsertFormat( options, 0, CFSTR( "-u=%d," ), DADiskGetUserUID( disk ) );
}
else
{
___CFStringInsertFormat( options, 0, CFSTR( "-u=%d," ), ___UID_UNKNOWN );
}
}
CFStringTrim( options, CFSTR( "," ) );
if ( check == NULL )
{
if ( DAMountContainsArgument( options, kDAFileSystemMountArgumentNoWrite ) )
{
check = kCFBooleanFalse;
}
else
{
check = kCFBooleanTrue;
}
}
if ( check == kCFBooleanFalse )
{
if ( DADiskGetState( disk, kDADiskStateRequireRepair ) )
{
if ( force == FALSE )
{
status = ___EDIRTY;
goto DAMountWithArgumentsErr;
}
}
}
if ( check == kCFBooleanTrue )
{
if ( DADiskGetState( disk, kDADiskStateRequireRepair ) == FALSE )
{
check = kCFBooleanFalse;
}
}
CFRetain( disk );
context->assertionID = kIOPMNullAssertionID;
context->callback = callback;
context->callbackContext = callbackContext;
context->disk = disk;
context->force = force;
context->mountpoint = mountpoint;
context->options = options;
if ( check == kCFBooleanTrue )
{
DALogDebug( " repaired disk, id = %@, ongoing.", disk );
IOPMAssertionCreateWithDescription( kIOPMAssertionTypePreventUserIdleSystemSleep,
CFSTR( _kDADaemonName ),
NULL,
NULL,
NULL,
0,
NULL,
&context->assertionID );
DAFileSystemRepair( DADiskGetFileSystem( disk ),
DADiskGetDevice( disk ),
__DAMountWithArgumentsCallbackStage1,
context );
}
else
{
__DAMountWithArgumentsCallbackStage1( ECANCELED, context );
}
DAMountWithArgumentsErr:
if ( status )
{
if ( context )
{
free( context );
}
if ( mountpoint )
{
CFRelease( mountpoint );
}
if ( options )
{
CFRelease( options );
}
if ( callback )
{
( callback )( status, NULL, callbackContext );
}
}
}