1/*
2 * Copyright (c) 1998-2013 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include "DAMount.h"
25
26#include "DABase.h"
27#include "DAInternal.h"
28#include "DALog.h"
29#include "DAMain.h"
30#include "DASupport.h"
31
32#include <fstab.h>
33#include <libgen.h>
34#include <unistd.h>
35#include <sys/param.h>
36#include <sys/stat.h>
37
38struct __DAMountCallbackContext
39{
40///w:start
41    Boolean         automatic;
42///w:stop
43    DAMountCallback callback;
44    void *          callbackContext;
45    DADiskRef       disk;
46    Boolean         force;
47    CFURLRef        mountpoint;
48    CFStringRef     options;
49};
50
51typedef struct __DAMountCallbackContext __DAMountCallbackContext;
52
53static void __DAMountWithArgumentsCallbackStage1( int status, void * context );
54static void __DAMountWithArgumentsCallbackStage2( int status, void * context );
55static void __DAMountWithArgumentsCallbackStage3( int status, void * context );
56
57static void __DAMountWithArgumentsCallback( int status, void * parameter )
58{
59    /*
60     * Process the mount request completion.
61     */
62
63    __DAMountCallbackContext * context = parameter;
64
65///w:start
66    if ( context->automatic )
67    {
68        if ( status == ___EDIRTY )
69        {
70            DAMountWithArguments( context->disk, NULL, context->callback, context->callbackContext, kDAFileSystemMountArgumentForce, kDAFileSystemMountArgumentNoWrite, NULL );
71
72            context->callback = NULL;
73        }
74    }
75///w:stop
76    if ( context->callback )
77    {
78        ( context->callback )( status, context->mountpoint, context->callbackContext );
79    }
80
81    CFRelease( context->disk    );
82    CFRelease( context->options );
83
84    if ( context->mountpoint )  CFRelease( context->mountpoint );
85
86    free( context );
87}
88
89static void __DAMountWithArgumentsCallbackStage1( int status, void * parameter )
90{
91    /*
92     * Process the repair command's completion.
93     */
94
95    __DAMountCallbackContext * context = parameter;
96
97    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
98
99    if ( status )
100    {
101        /*
102         * We were unable to repair the volume.
103         */
104
105        if ( status == ECANCELED )
106        {
107            status = 0;
108        }
109        else
110        {
111            DALogDebug( "  repaired disk, id = %@, failure.", context->disk );
112
113            DALogError( "unable to repair %@ (status code 0x%08X).", context->disk, status );
114
115            if ( context->force )
116            {
117                status = 0;
118            }
119            else
120            {
121                __DAMountWithArgumentsCallback( ___EDIRTY, context );
122            }
123        }
124    }
125    else
126    {
127        /*
128         * We were able to repair the volume.
129         */
130
131        DADiskSetState( context->disk, kDADiskStateRequireRepair, FALSE );
132
133        DALogDebug( "  repaired disk, id = %@, success.", context->disk );
134    }
135
136    /*
137     * Mount the volume.
138     */
139
140    if ( status == 0 )
141    {
142        /*
143         * Create the mount point, in case one needs to be created.
144         */
145
146        if ( context->mountpoint == NULL )
147        {
148            context->mountpoint = DAMountCreateMountPointWithAction( context->disk, kDAMountPointActionMake );
149        }
150
151        /*
152         * Execute the mount command.
153         */
154
155        if ( context->mountpoint )
156        {
157            DALogDebug( "  mounted disk, id = %@, ongoing.", context->disk );
158
159            DAFileSystemMountWithArguments( DADiskGetFileSystem( context->disk ),
160                                            DADiskGetDevice( context->disk ),
161                                            context->mountpoint,
162                                            DADiskGetUserUID( context->disk ),
163                                            DADiskGetUserGID( context->disk ),
164                                            __DAMountWithArgumentsCallbackStage2,
165                                            context,
166                                            context->options,
167                                            NULL );
168        }
169        else
170        {
171            __DAMountWithArgumentsCallback( ENOSPC, context );
172        }
173    }
174}
175
176static void __DAMountWithArgumentsCallbackStage2( int status, void * parameter )
177{
178    /*
179     * Process the mount command's completion.
180     */
181
182    __DAMountCallbackContext * context = parameter;
183
184    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
185
186    if ( status )
187    {
188        /*
189         * We were unable to mount the volume.
190         */
191
192        DALogDebug( "  mounted disk, id = %@, failure.", context->disk );
193
194        DALogError( "unable to mount %@ (status code 0x%08X).", context->disk, status );
195
196        DAMountRemoveMountPoint( context->mountpoint );
197
198        __DAMountWithArgumentsCallback( status, context );
199    }
200    else
201    {
202        /*
203         * We were able to mount the volume.
204         */
205
206        DALogDebug( "  mounted disk, id = %@, success.", context->disk );
207
208        _DAMountCreateTrashFolder( context->disk, context->mountpoint );
209
210        /*
211         * Execute the "repair quotas" command.
212         */
213
214        if ( DADiskGetState( context->disk, kDADiskStateRequireRepairQuotas ) )
215        {
216            DAFileSystemRepairQuotas( DADiskGetFileSystem( context->disk ),
217                                      context->mountpoint,
218                                      __DAMountWithArgumentsCallbackStage3,
219                                      context );
220        }
221        else
222        {
223            __DAMountWithArgumentsCallbackStage3( 0, context );
224        }
225    }
226}
227
228static void __DAMountWithArgumentsCallbackStage3( int status, void * parameter )
229{
230    /*
231     * Process the "repair quotas" command's completion.
232     */
233
234    __DAMountCallbackContext * context = parameter;
235
236    if ( status )
237    {
238        DALogError( "unable to repair quotas on disk %@ (status code 0x%08X).", context->disk, status );
239    }
240    else
241    {
242        DADiskSetState( context->disk, kDADiskStateRequireRepairQuotas, FALSE );
243    }
244
245    __DAMountWithArgumentsCallback( 0, context );
246}
247
248void _DAMountCreateTrashFolder( DADiskRef disk, CFURLRef mountpoint )
249{
250    /*
251     * Create the trash folder in which the user trashes will be stored.
252     */
253
254    /*
255     * Determine whether the disk is writable.
256     */
257
258    if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue )
259    {
260        char path[MAXPATHLEN];
261
262        /*
263         * Obtain the mount point path.
264         */
265
266        if ( CFURLGetFileSystemRepresentation( mountpoint, TRUE, ( void * ) path, sizeof( path ) ) )
267        {
268            struct stat status;
269
270            /*
271             * Determine whether the trash folder exists.
272             */
273
274            strlcat( path, "/.Trashes", sizeof( path ) );
275
276            if ( stat( path, &status ) )
277            {
278                /*
279                 * Create the trash folder.
280                 */
281
282                if ( ___mkdir( path, 01333 ) == 0 )
283                {
284                    /*
285                     * Correct the trash folder's attributes.
286                     */
287
288                    ___chattr( path, ___ATTR_INVISIBLE, 0 );
289                }
290            }
291        }
292    }
293}
294
295void DAMount( DADiskRef disk, CFURLRef mountpoint, DAMountCallback callback, void * callbackContext )
296{
297    /*
298     * Mount the specified volume.  A status of 0 indicates success.
299     */
300
301    return DAMountWithArguments( disk, mountpoint, callback, callbackContext, NULL );
302
303}
304
305Boolean DAMountContainsArgument( CFStringRef arguments, CFStringRef argument )
306{
307    CFBooleanRef argumentValue;
308    CFBooleanRef argumentsValue;
309
310    argumentsValue = NULL;
311
312    if ( CFStringHasPrefix( argument, CFSTR( "no" ) ) )
313    {
314        argument      = CFStringCreateWithSubstring( kCFAllocatorDefault, argument, CFRangeMake( 2, CFStringGetLength( argument ) - 2 ) );
315        argumentValue = kCFBooleanFalse;
316    }
317    else
318    {
319        argument      = CFRetain( argument );
320        argumentValue = kCFBooleanTrue;
321    }
322
323    if ( argument )
324    {
325        CFArrayRef argumentList;
326        CFIndex    argumentListCount;
327        CFIndex    argumentListIndex;
328
329        argumentList = CFStringCreateArrayBySeparatingStrings( kCFAllocatorDefault, arguments, CFSTR( "," ) );
330
331        if ( argumentList )
332        {
333            argumentListCount = CFArrayGetCount( argumentList );
334
335            for ( argumentListIndex = 0; argumentListIndex < argumentListCount; argumentListIndex++ )
336            {
337                CFStringRef compare;
338
339                compare = CFArrayGetValueAtIndex( argumentList, argumentListIndex );
340
341                if ( compare )
342                {
343                    CFBooleanRef compareValue;
344
345                    if ( CFStringHasPrefix( compare, CFSTR( "no" ) ) )
346                    {
347                        compare      = CFStringCreateWithSubstring( kCFAllocatorDefault, compare, CFRangeMake( 2, CFStringGetLength( compare ) - 2 ) );
348                        compareValue = kCFBooleanFalse;
349                    }
350                    else
351                    {
352                        compare      = CFRetain( compare );
353                        compareValue = kCFBooleanTrue;
354                    }
355
356                    if ( compare )
357                    {
358                        if ( CFEqual( compare, CFSTR( FSTAB_RO ) ) )
359                        {
360                            CFRelease( compare );
361
362                            compare      = CFRetain( kDAFileSystemMountArgumentNoWrite );
363                            compareValue = compareValue;
364                        }
365
366                        if ( CFEqual( compare, CFSTR( FSTAB_RW ) ) )
367                        {
368                            CFRelease( compare );
369
370                            compare      = CFRetain( kDAFileSystemMountArgumentNoWrite );
371                            compareValue = ( compareValue == kCFBooleanTrue ) ? kCFBooleanFalse : kCFBooleanTrue;
372                        }
373                    }
374
375                    if ( compare )
376                    {
377                        if ( CFEqual( argument, compare ) )
378                        {
379                            argumentsValue = compareValue;
380                        }
381
382                        CFRelease( compare );
383                    }
384                }
385            }
386
387            CFRelease( argumentList );
388        }
389
390        CFRelease( argument );
391    }
392
393    return ( argumentValue == argumentsValue ) ? TRUE : FALSE;
394}
395
396CFURLRef DAMountCreateMountPoint( DADiskRef disk )
397{
398    return DAMountCreateMountPointWithAction( disk, kDAMountPointActionMake );
399}
400
401CFURLRef DAMountCreateMountPointWithAction( DADiskRef disk, DAMountPointAction action )
402{
403    FILE *      file;
404    CFIndex     index;
405    CFURLRef    mountpoint;
406    char        name[MAXPATHLEN];
407    char        path[MAXPATHLEN];
408    CFStringRef string;
409
410    mountpoint = NULL;
411
412    /*
413     * Obtain the volume name.
414     */
415
416    string = DADiskGetDescription( disk, kDADiskDescriptionVolumeNameKey );
417
418    if ( string )
419    {
420        if ( CFStringGetLength( string ) )
421        {
422            CFRetain( string );
423        }
424        else
425        {
426            string = NULL;
427        }
428    }
429
430    if ( string == NULL )
431    {
432        string = ___CFBundleCopyLocalizedStringInDirectory( gDABundlePath, CFSTR( "Untitled" ), CFSTR( "Untitled" ), NULL );
433    }
434
435    if ( ___CFStringGetCString( string, name, MNAMELEN - 20 ) )
436    {
437        /*
438         * Adjust the volume name.
439         */
440
441        while ( strchr( name, '/' ) )
442        {
443            *strchr( name, '/' ) = ':';
444        }
445
446        /*
447         * Create the mount point path.
448         */
449
450        for ( index = 0; index < 100; index++ )
451        {
452            if ( index == 0 )
453            {
454                snprintf( path, sizeof( path ), "%s/%s", kDAMainMountPointFolder, name );
455            }
456            else
457            {
458                snprintf( path, sizeof( path ), "%s/%s %lu", kDAMainMountPointFolder, name, index );
459            }
460
461            switch ( action )
462            {
463                case kDAMountPointActionLink:
464                {
465                    /*
466                     * Link the mount point.
467                     */
468
469                    CFURLRef url;
470
471                    url = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
472
473                    if ( url )
474                    {
475                        char source[MAXPATHLEN];
476
477                        if ( CFURLGetFileSystemRepresentation( url, TRUE, ( void * ) source, sizeof( source ) ) )
478                        {
479                            if ( symlink( source, path ) == 0 )
480                            {
481                                mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
482                            }
483                        }
484                    }
485
486                    break;
487                }
488                case kDAMountPointActionMake:
489                {
490                    /*
491                     * Create the mount point.
492                     */
493
494                    if ( mkdir( path, 0111 ) == 0 )
495                    {
496                        if ( DADiskGetUserUID( disk ) )
497                        {
498                            chown( path, DADiskGetUserUID( disk ), -1 );
499                        }
500
501                        mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
502
503                        /*
504                         * Create the mount point cookie file.
505                         */
506
507                        strlcat( path, "/",                               sizeof( path ) );
508                        strlcat( path, kDAMainMountPointFolderCookieFile, sizeof( path ) );
509
510                        file = fopen( path, "w" );
511
512                        if ( file )
513                        {
514                            fclose( file );
515                        }
516                    }
517
518                    break;
519                }
520                case kDAMountPointActionMove:
521                {
522                    /*
523                     * Move the mount point.
524                     */
525
526                    CFURLRef url;
527
528                    url = DADiskGetBypath( disk );
529
530                    if ( url )
531                    {
532                        char source[MAXPATHLEN];
533
534                        if ( CFURLGetFileSystemRepresentation( url, TRUE, ( void * ) source, sizeof( source ) ) )
535                        {
536                            if ( rename( source, path ) == 0 )
537                            {
538                                mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
539                            }
540                        }
541                    }
542
543                    break;
544                }
545                case kDAMountPointActionNone:
546                {
547                    mountpoint = CFURLCreateFromFileSystemRepresentation( kCFAllocatorDefault, ( void * ) path, strlen( path ), TRUE );
548
549                    break;
550                }
551            }
552
553            if ( mountpoint )
554            {
555                break;
556            }
557        }
558    }
559
560    CFRelease( string );
561
562    return mountpoint;
563}
564
565Boolean DAMountGetPreference( DADiskRef disk, DAMountPreference preference )
566{
567    CFBooleanRef value;
568
569    switch ( preference )
570    {
571        case kDAMountPreferenceDefer:
572        {
573            /*
574             * Determine whether the media is removable.
575             */
576
577            if ( DADiskGetDescription( disk, kDADiskDescriptionMediaRemovableKey ) == kCFBooleanTrue )
578            {
579                value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferRemovableKey );
580
581                value = value ? value : kCFBooleanTrue;
582            }
583            else
584            {
585                /*
586                 * Determine whether the device is internal.
587                 */
588
589                if ( DADiskGetDescription( disk, kDADiskDescriptionDeviceInternalKey ) == kCFBooleanTrue )
590                {
591                    value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferInternalKey );
592
593                    value = value ? value : kCFBooleanFalse;
594                }
595                else
596                {
597                    value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountDeferExternalKey );
598
599                    value = value ? value : kCFBooleanTrue;
600                }
601            }
602
603            break;
604        }
605        case kDAMountPreferenceTrust:
606        {
607            /*
608             * Determine whether the media is removable.
609             */
610
611            if ( DADiskGetDescription( disk, kDADiskDescriptionMediaRemovableKey ) == kCFBooleanTrue )
612            {
613                value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustRemovableKey );
614
615                value = value ? value : kCFBooleanFalse;
616            }
617            else
618            {
619                /*
620                 * Determine whether the device is internal.
621                 */
622
623                if ( DADiskGetDescription( disk, kDADiskDescriptionDeviceInternalKey ) == kCFBooleanTrue )
624                {
625                    value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustInternalKey );
626
627                    value = value ? value : kCFBooleanTrue;
628                }
629                else
630                {
631                    value = CFDictionaryGetValue( gDAPreferenceList, kDAPreferenceMountTrustExternalKey );
632
633                    value = value ? value : kCFBooleanFalse;
634                }
635            }
636
637            break;
638        }
639        case kDAMountPreferenceWrite:
640        {
641            value = kCFBooleanTrue;
642///w:start
643            if ( DADiskGetState( disk, _kDADiskStateMountPreferenceNoWrite ) )
644            {
645                DADiskSetState( disk, _kDADiskStateMountPreferenceNoWrite, FALSE );
646
647                value = kCFBooleanFalse;
648            }
649///w:stop
650
651            break;
652        }
653        default:
654        {
655            value = kCFBooleanFalse;
656
657            break;
658        }
659    }
660
661    assert( value );
662
663    return CFBooleanGetValue( value );
664}
665
666void DAMountRemoveMountPoint( CFURLRef mountpoint )
667{
668    char path[MAXPATHLEN];
669
670    /*
671     * Obtain the mount point path.
672     */
673
674    if ( CFURLGetFileSystemRepresentation( mountpoint, TRUE, ( void * ) path, sizeof( path ) ) )
675    {
676        if ( ___isautofs( path ) == 0 )
677        {
678            Boolean remove;
679
680            remove = FALSE;
681
682            if ( strncmp( path, kDAMainMountPointFolder, strlen( kDAMainMountPointFolder ) ) == 0 )
683            {
684                if ( strrchr( path + strlen( kDAMainMountPointFolder ), '/' ) == path + strlen( kDAMainMountPointFolder ) )
685                {
686                    remove = TRUE;
687                }
688            }
689
690///w:start
691//          if ( remove == FALSE )
692///w:stop
693            {
694                char file[MAXPATHLEN];
695
696                strlcpy( file, path,                              sizeof( file ) );
697                strlcat( file, "/",                               sizeof( file ) );
698                strlcat( file, kDAMainMountPointFolderCookieFile, sizeof( file ) );
699
700                /*
701                 * Remove the mount point cookie file.
702                 */
703
704                if ( unlink( file ) == 0 )
705                {
706                    remove = TRUE;
707                }
708            }
709
710            if ( remove )
711            {
712                /*
713                 * Remove the mount point.
714                 */
715
716                rmdir( path );
717            }
718        }
719    }
720}
721
722void DAMountWithArguments( DADiskRef disk, CFURLRef mountpoint, DAMountCallback callback, void * callbackContext, ... )
723{
724    /*
725     * Mount the specified volume.  A status of 0 indicates success.  All arguments in
726     * the argument list shall be of type CFStringRef.  The argument list must be NULL
727     * terminated.
728     */
729
730    CFStringRef                argument   = NULL;
731    va_list                    arguments;
732    CFBooleanRef               automatic  = kCFBooleanTrue;
733    CFBooleanRef               check      = NULL;
734    __DAMountCallbackContext * context    = NULL;
735    CFIndex                    count      = 0;
736    DAFileSystemRef            filesystem = DADiskGetFileSystem( disk );
737    Boolean                    force      = FALSE;
738    CFIndex                    index      = 0;
739    CFDictionaryRef            map        = NULL;
740    CFMutableStringRef         options    = NULL;
741    int                        status     = 0;
742
743    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
744
745    /*
746     * Initialize our minimal state.
747     */
748
749    if ( mountpoint )
750    {
751        CFRetain( mountpoint );
752    }
753
754    /*
755     * Prepare the mount context.
756     */
757
758    context = malloc( sizeof( __DAMountCallbackContext ) );
759
760    if ( context == NULL )
761    {
762        status = ENOMEM;
763
764        goto DAMountWithArgumentsErr;
765    }
766
767    /*
768     * Prepare the mount options.
769     */
770
771    options = CFStringCreateMutable( kCFAllocatorDefault, 0 );
772
773    if ( options == NULL )
774    {
775        status = ENOMEM;
776
777        goto DAMountWithArgumentsErr;
778    }
779
780    va_start( arguments, callbackContext );
781
782    while ( ( argument = va_arg( arguments, CFStringRef ) ) )
783    {
784        if ( CFEqual( argument, kDAFileSystemMountArgumentForce ) )
785        {
786            force = TRUE;
787        }
788        else
789        {
790            CFStringAppend( options, argument );
791            CFStringAppend( options, CFSTR( "," ) );
792        }
793    }
794
795    va_end( arguments );
796
797    CFStringTrim( options, CFSTR( "," ) );
798
799    if ( CFEqual( options, CFSTR( "automatic" ) ) )
800    {
801        automatic = NULL;
802
803        check = kCFBooleanTrue;
804
805        CFStringReplaceAll( options, CFSTR( "" ) );
806    }
807///w:start
808    context->automatic = ( automatic == NULL ) ? TRUE : FALSE;
809///w:stop
810
811    /*
812     * Determine whether the volume is to be updated.
813     */
814
815    if ( DAMountContainsArgument( options, kDAFileSystemMountArgumentUpdate ) )
816    {
817        if ( mountpoint )
818        {
819            status = EINVAL;
820
821            goto DAMountWithArgumentsErr;
822        }
823
824        mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
825
826        if ( mountpoint == NULL )
827        {
828            status = EINVAL;
829
830            goto DAMountWithArgumentsErr;
831        }
832
833        CFRetain( mountpoint );
834    }
835
836    /*
837     * Scan the mount map list.
838     */
839
840    count = CFArrayGetCount( gDAMountMapList1 );
841
842    for ( index = 0; index < count; index++ )
843    {
844        map = CFArrayGetValueAtIndex( gDAMountMapList1, index );
845
846        if ( map )
847        {
848            CFTypeRef   id;
849            CFStringRef kind;
850
851            id   = CFDictionaryGetValue( map, kDAMountMapProbeIDKey );
852            kind = CFDictionaryGetValue( map, kDAMountMapProbeKindKey );
853
854            if ( kind )
855            {
856                /*
857                 * Determine whether the volume kind matches.
858                 */
859
860                if ( CFEqual( kind, DAFileSystemGetKind( filesystem ) ) == FALSE )
861                {
862                    continue;
863                }
864            }
865
866            if ( CFGetTypeID( id ) == CFUUIDGetTypeID( ) )
867            {
868                /*
869                 * Determine whether the volume UUID matches.
870                 */
871
872                if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeUUIDKey, id ) == kCFCompareEqualTo )
873                {
874                    break;
875                }
876            }
877            else if ( CFGetTypeID( id ) == CFStringGetTypeID( ) )
878            {
879                /*
880                 * Determine whether the volume name matches.
881                 */
882
883                if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeNameKey, id ) == kCFCompareEqualTo )
884                {
885                    break;
886                }
887            }
888            else if ( CFGetTypeID( id ) == CFDictionaryGetTypeID( ) )
889            {
890                boolean_t match = FALSE;
891
892                /*
893                 * Determine whether the device description matches.
894                 */
895
896                IOServiceMatchPropertyTable( DADiskGetIOMedia( disk ), id, &match );
897
898                if ( match )
899                {
900                    break;
901                }
902            }
903        }
904    }
905
906    /*
907     * Process the map.
908     */
909
910    if ( index < count )
911    {
912        CFStringRef string;
913
914        /*
915         * Determine whether the volume is to be mounted.
916         */
917
918        if ( automatic == NULL )
919        {
920            automatic = CFDictionaryGetValue( map, kDAMountMapMountAutomaticKey );
921
922            if ( automatic == kCFBooleanTrue )
923            {
924                DADiskSetOption( disk, kDADiskOptionMountAutomatic,        TRUE );
925                DADiskSetOption( disk, kDADiskOptionMountAutomaticNoDefer, TRUE );
926            }
927        }
928
929        /*
930         * Prepare the mount options.
931         */
932
933        string = CFDictionaryGetValue( map, kDAMountMapMountOptionsKey );
934
935        if ( string )
936        {
937            CFStringInsert( options, 0, CFSTR( "," ) );
938            CFStringInsert( options, 0, string );
939        }
940
941        /*
942         * Prepare the mount point.
943         */
944
945        if ( mountpoint == NULL )
946        {
947            mountpoint = CFDictionaryGetValue( map, kDAMountMapMountPathKey );
948
949            if ( mountpoint )
950            {
951                CFRetain( mountpoint );
952            }
953        }
954    }
955
956    /*
957     * Scan the mount map list.
958     */
959
960    count = CFArrayGetCount( gDAMountMapList2 );
961
962    for ( index = 0; index < count; index++ )
963    {
964        map = CFArrayGetValueAtIndex( gDAMountMapList2, index );
965
966        if ( map )
967        {
968            CFTypeRef id;
969
970            id = CFDictionaryGetValue( map, kDAMountMapProbeIDKey );
971
972            /*
973             * Determine whether the volume UUID matches.
974             */
975
976            if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeUUIDKey, id ) == kCFCompareEqualTo )
977            {
978                break;
979            }
980        }
981    }
982
983    /*
984     * Process the map.
985     */
986
987    if ( index < count )
988    {
989        CFStringRef string;
990
991        /*
992         * Prepare the mount options.
993         */
994
995        string = CFDictionaryGetValue( map, kDAMountMapMountOptionsKey );
996
997        if ( string )
998        {
999            CFStringInsert( options, 0, CFSTR( "," ) );
1000            CFStringInsert( options, 0, string );
1001        }
1002    }
1003
1004    /*
1005     * Determine whether the volume is to be mounted.
1006     */
1007
1008    if ( automatic == NULL )
1009    {
1010        if ( DADiskGetOption( disk, kDADiskOptionMountAutomatic ) )
1011        {
1012            if ( DADiskGetOption( disk, kDADiskOptionMountAutomaticNoDefer ) )
1013            {
1014                automatic = kCFBooleanTrue;
1015            }
1016        }
1017        else
1018        {
1019            automatic = kCFBooleanFalse;
1020        }
1021
1022        if ( automatic == NULL )
1023        {
1024            if ( gDAConsoleUserList == NULL )
1025            {
1026                if ( DAMountGetPreference( disk, kDAMountPreferenceDefer ) )
1027                {
1028                    automatic = kCFBooleanFalse;
1029                }
1030            }
1031        }
1032    }
1033
1034    if ( automatic == kCFBooleanFalse )
1035    {
1036        status = ECANCELED;
1037
1038        goto DAMountWithArgumentsErr;
1039    }
1040
1041    /*
1042     * Prepare the mount options.
1043     */
1044
1045    if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanFalse )
1046    {
1047        CFStringInsert( options, 0, CFSTR( "," ) );
1048        CFStringInsert( options, 0, kDAFileSystemMountArgumentNoWrite );
1049    }
1050
1051    if ( DAMountGetPreference( disk, kDAMountPreferenceWrite ) == FALSE )
1052    {
1053        CFStringInsert( options, 0, CFSTR( "," ) );
1054        CFStringInsert( options, 0, kDAFileSystemMountArgumentNoWrite );
1055    }
1056
1057    if ( DAMountGetPreference( disk, kDAMountPreferenceTrust ) == FALSE )
1058    {
1059        CFStringInsert( options, 0, CFSTR( "," ) );
1060        CFStringInsert( options, 0, kDAFileSystemMountArgumentNoSetUserID );
1061
1062        CFStringInsert( options, 0, CFSTR( "," ) );
1063        CFStringInsert( options, 0, kDAFileSystemMountArgumentNoOwnership );
1064
1065        CFStringInsert( options, 0, CFSTR( "," ) );
1066        CFStringInsert( options, 0, kDAFileSystemMountArgumentNoDevice );
1067    }
1068///w:start
1069    if ( CFEqual( DAFileSystemGetKind( filesystem ), CFSTR( "hfs" ) ) )
1070    {
1071        ___CFStringInsertFormat( options, 0, CFSTR( "-m=%o," ), 0755 );
1072
1073        if ( DADiskGetUserGID( disk ) )
1074        {
1075            ___CFStringInsertFormat( options, 0, CFSTR( "-g=%d," ), DADiskGetUserGID( disk ) );
1076        }
1077        else
1078        {
1079            ___CFStringInsertFormat( options, 0, CFSTR( "-g=%d," ), ___GID_UNKNOWN );
1080        }
1081
1082        if ( DADiskGetUserUID( disk ) )
1083        {
1084            ___CFStringInsertFormat( options, 0, CFSTR( "-u=%d," ), DADiskGetUserUID( disk ) );
1085        }
1086        else
1087        {
1088            ___CFStringInsertFormat( options, 0, CFSTR( "-u=%d," ), ___UID_UNKNOWN );
1089        }
1090    }
1091///w:stop
1092
1093    CFStringTrim( options, CFSTR( "," ) );
1094
1095    /*
1096     * Determine whether the volume is to be repaired.
1097     */
1098
1099    if ( check == NULL )
1100    {
1101        if ( DAMountContainsArgument( options, kDAFileSystemMountArgumentNoWrite ) )
1102        {
1103            check = kCFBooleanFalse;
1104        }
1105        else
1106        {
1107            check = kCFBooleanTrue;
1108        }
1109    }
1110
1111    if ( check == kCFBooleanFalse )
1112    {
1113        if ( DADiskGetState( disk, kDADiskStateRequireRepair ) )
1114        {
1115            if ( force == FALSE )
1116            {
1117                status = ___EDIRTY;
1118
1119                goto DAMountWithArgumentsErr;
1120            }
1121        }
1122    }
1123
1124    if ( check == kCFBooleanTrue )
1125    {
1126        if ( DADiskGetState( disk, kDADiskStateRequireRepair ) == FALSE )
1127        {
1128            check = kCFBooleanFalse;
1129        }
1130    }
1131
1132    /*
1133     * Repair the volume.
1134     */
1135
1136    CFRetain( disk );
1137
1138    context->callback        = callback;
1139    context->callbackContext = callbackContext;
1140    context->disk            = disk;
1141    context->force           = force;
1142    context->mountpoint      = mountpoint;
1143    context->options         = options;
1144
1145    if ( check == kCFBooleanTrue )
1146    {
1147        DALogDebug( "  repaired disk, id = %@, ongoing.", disk );
1148
1149        DAFileSystemRepair( DADiskGetFileSystem( disk ),
1150                            DADiskGetDevice( disk ),
1151                            __DAMountWithArgumentsCallbackStage1,
1152                            context );
1153    }
1154    else
1155    {
1156        __DAMountWithArgumentsCallbackStage1( ECANCELED, context );
1157    }
1158
1159DAMountWithArgumentsErr:
1160
1161    if ( status )
1162    {
1163        if ( context )
1164        {
1165            free( context );
1166        }
1167
1168        if ( mountpoint )
1169        {
1170            CFRelease( mountpoint );
1171        }
1172
1173        if ( options )
1174        {
1175            CFRelease( options );
1176        }
1177
1178        if ( callback )
1179        {
1180            ( callback )( status, NULL, callbackContext );
1181        }
1182    }
1183}
1184