#include "DAMount.h"
#include "DABase.h"
#include "DAInternal.h"
#include "DALog.h"
#include "DAMain.h"
#include "DASupport.h"
#include <libgen.h>
#include <unistd.h>
#include <sys/param.h>
#include <sys/stat.h>
struct __DAMountCallbackContext
{
DAMountCallback callback;
void * callbackContext;
DADiskRef disk;
CFURLRef mountpoint;
CFStringRef options;
};
typedef struct __DAMountCallbackContext __DAMountCallbackContext;
static void __DAMountWithArgumentsCallbackStage1( int status, void * context );
static void __DAMountWithArgumentsCallbackStage2( int status, void * context );
static void __DAMountWithArgumentsCallback( int status, void * parameter )
{
__DAMountCallbackContext * context = parameter;
if ( context->callback )
{
( context->callback )( status, context->mountpoint, context->callbackContext );
}
CFRelease( context->disk );
CFRelease( context->mountpoint );
CFRelease( context->options );
free( context );
}
static void __DAMountWithArgumentsCallbackStage1( int status, void * parameter )
{
__DAMountCallbackContext * context = parameter;
if ( status == 0 )
{
_DAMountCreateTrashFolder( context->disk, context->mountpoint );
if ( DADiskGetState( context->disk, kDADiskStateRequireRepairQuotas ) )
{
DAFileSystemRepairQuotas( DADiskGetFileSystem( context->disk ),
context->mountpoint,
__DAMountWithArgumentsCallbackStage2,
context );
return;
}
}
else
{
DAMountRemoveMountPoint( context->mountpoint );
}
__DAMountWithArgumentsCallback( status, context );
}
static void __DAMountWithArgumentsCallbackStage2( 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 _DAMountCreateTrashFolder( DADiskRef disk, CFURLRef mountpoint )
{
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue )
{
char path[MAXPATHLEN];
if ( CFURLGetFileSystemRepresentation( mountpoint, TRUE, ( void * ) path, sizeof( path ) ) )
{
struct stat status;
strlcat( path, "/.Trashes", sizeof( path ) );
if ( stat( path, &status ) )
{
if ( ___mkdir( path, 01333 ) == 0 )
{
___chattr( path, ___ATTR_INVISIBLE, 0 );
}
}
}
}
}
void DAMount( DADiskRef disk, CFURLRef mountpoint, DAMountCallback callback, void * callbackContext )
{
return DAMountWithArguments( disk, mountpoint, callback, callbackContext, NULL );
}
Boolean DAMountContainsArgument( CFStringRef arguments, CFStringRef argument )
{
CFRange range;
range = CFStringFind( arguments, argument, 0 );
if ( range.length )
{
if ( range.location )
{
if ( CFStringGetCharacterAtIndex( arguments, range.location - 1 ) != ',' )
{
range.length = 0;
}
}
if ( range.location + range.length < CFStringGetLength( arguments ) )
{
if ( CFStringGetCharacterAtIndex( arguments, range.location + range.length ) != ',' )
{
range.length = 0;
}
}
}
return range.length ? TRUE : FALSE;
}
CFURLRef DAMountCreateMountPoint( DADiskRef disk )
{
return DAMountCreateMountPointWithAction( disk, kDAMountPointActionMake );
}
CFURLRef DAMountCreateMountPointWithAction( DADiskRef disk, DAMountPointAction action )
{
FILE * file;
CFIndex index;
CFURLRef mountpoint;
char name[MAXPATHLEN];
char path[MAXPATHLEN];
struct stat status;
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 );
}
if ( stat( path, &status ) )
{
if ( errno == ENOENT )
{
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 ( DADiskGetUserEUID( disk ) )
{
chown( path, DADiskGetUserEUID( disk ), -1 );
}
mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
strlcat( path, "/", sizeof( path ) );
strlcat( path, kDAMainMountPointFolderCookieFile, sizeof( path ) );
file = fopen( path, "w" );
if ( file )
{
fclose( file );
}
}
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 );
}
else
{
if ( DADiskGetDescription( disk, kDADiskDescriptionDeviceInternalKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferInternalKey );
}
else
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferExternalKey );
}
}
break;
}
case kDAMountPreferenceTrust:
{
if ( DADiskGetDescription( disk, kDADiskDescriptionMediaRemovableKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustRemovableKey );
}
else
{
if ( DADiskGetDescription( disk, kDADiskDescriptionDeviceInternalKey ) == kCFBooleanTrue )
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustInternalKey );
}
else
{
value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustExternalKey );
}
}
break;
}
case kDAMountPreferenceWrite:
{
value = kCFBooleanTrue;
if ( DADiskGetState( disk, _kDADiskStateMountPreferenceNoWrite ) )
{
DADiskSetState( disk, _kDADiskStateMountPreferenceNoWrite, FALSE );
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;
struct stat status;
remove = FALSE;
if ( strcmp( dirname( path ), kDAMainMountPointFolder ) == 0 )
{
remove = TRUE;
}
strlcat( path, "/", sizeof( path ) );
strlcat( path, kDAMainMountPointFolderCookieFile, sizeof( path ) );
if ( stat( path, &status ) == 0 )
{
if ( unlink( path ) == 0 )
{
remove = TRUE;
}
}
if ( remove )
{
rmdir( dirname( path ) );
}
}
}
}
void DAMountWithArguments( DADiskRef disk, CFURLRef mountpoint, DAMountCallback callback, void * callbackContext, ... )
{
CFStringRef argument = NULL;
va_list arguments;
CFBooleanRef automatic = kCFBooleanTrue;
__DAMountCallbackContext * context = NULL;
CFIndex count = 0;
DAFileSystemRef filesystem = DADiskGetFileSystem( disk );
CFIndex index = 0;
CFDictionaryRef map = NULL;
CFMutableStringRef options = NULL;
int status = 0;
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 ) ) )
{
CFStringAppend( options, argument );
CFStringAppend( options, CFSTR( "," ) );
}
va_end( arguments );
CFStringTrim( options, CFSTR( "," ) );
if ( CFEqual( options, CFSTR( "automatic" ) ) )
{
automatic = NULL;
CFStringReplaceAll( options, CFSTR( "" ) );
}
if ( DAMountContainsArgument( options, kDAFileSystemMountArgumentUpdate ) )
{
if ( mountpoint )
{
status = EINVAL;
goto DAMountWithArgumentsErr;
}
mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
if ( mountpoint == NULL )
{
status = EINVAL;
goto DAMountWithArgumentsErr;
}
CFRetain( mountpoint );
}
if ( automatic == NULL )
{
if ( DADiskGetState( disk, kDADiskStateRequireRepair ) )
{
CFStringInsert( options, 0, CFSTR( "," ) );
CFStringInsert( options, 0, kDAFileSystemMountArgumentNoWrite );
}
}
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 )
{
DADiskSetOption( disk, kDADiskOptionMountAutomatic, TRUE );
DADiskSetOption( disk, kDADiskOptionMountAutomaticNoDefer, 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 ( DADiskGetOption( disk, kDADiskOptionMountAutomatic ) )
{
if ( DADiskGetOption( disk, kDADiskOptionMountAutomaticNoDefer ) )
{
automatic = kCFBooleanTrue;
}
}
else
{
automatic = kCFBooleanFalse;
}
if ( automatic == NULL )
{
if ( gDAConsoleUser == 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, kDAMountPreferenceWrite ) == FALSE )
{
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 ( DADiskGetUserRGID( disk ) )
{
___CFStringInsertFormat( options, 0, CFSTR( "-g=%d," ), DADiskGetUserRGID( disk ) );
}
else
{
___CFStringInsertFormat( options, 0, CFSTR( "-g=%d," ), ___GID_UNKNOWN );
}
if ( DADiskGetUserRUID( disk ) )
{
___CFStringInsertFormat( options, 0, CFSTR( "-u=%d," ), DADiskGetUserRUID( disk ) );
}
else
{
___CFStringInsertFormat( options, 0, CFSTR( "-u=%d," ), ___UID_UNKNOWN );
}
}
CFStringTrim( options, CFSTR( "," ) );
if ( mountpoint == NULL )
{
mountpoint = DAMountCreateMountPointWithAction( disk, kDAMountPointActionMake );
if ( mountpoint == NULL )
{
status = ENOSPC;
goto DAMountWithArgumentsErr;
}
}
CFRetain( disk );
context->callback = callback;
context->callbackContext = callbackContext;
context->disk = disk;
context->mountpoint = mountpoint;
context->options = options;
if ( CFEqual( DAFileSystemGetKind( filesystem ), CFSTR( "hfs" ) ) == FALSE )
{
DAFileSystemMountWithArguments( DADiskGetFileSystem( disk ),
DADiskGetDevice( disk ),
mountpoint,
___UID_ROOT,
___GID_ADMIN,
__DAMountWithArgumentsCallbackStage1,
context,
options,
NULL );
}
else
DAFileSystemMountWithArguments( DADiskGetFileSystem( disk ),
DADiskGetDevice( disk ),
mountpoint,
DADiskGetUserEUID( disk ),
DADiskGetUserEGID( disk ),
__DAMountWithArgumentsCallbackStage1,
context,
options,
NULL );
DAMountWithArgumentsErr:
if ( status )
{
if ( context )
{
free( context );
}
if ( mountpoint )
{
CFRelease( mountpoint );
}
if ( options )
{
CFRelease( options );
}
if ( callback )
{
( callback )( status, NULL, callbackContext );
}
}
}