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 "DARequest.h"
25
26#include "DABase.h"
27#include "DACallback.h"
28#include "DADialog.h"
29#include "DADissenter.h"
30#include "DAFileSystem.h"
31#include "DALog.h"
32#include "DAMain.h"
33#include "DAMount.h"
34#include "DAPrivate.h"
35#include "DAQueue.h"
36#include "DAStage.h"
37#include "DASupport.h"
38#include "DAThread.h"
39
40#include <fcntl.h>
41#include <libproc.h>
42#include <unistd.h>
43#include <sys/disk.h>
44#include <DiskArbitration/DiskArbitration.h>
45
46static void __DARequestClaimCallback( int status, void * context );
47static void __DARequestClaimReleaseCallback( CFTypeRef response, void * context );
48static void __DARequestEjectCallback( int status, void * context );
49static void __DARequestEjectApprovalCallback( CFTypeRef response, void * context );
50static int  __DARequestEjectEject( void * context );
51static void __DARequestMountCallback( int status, CFURLRef mountpoint, void * context );
52static void __DARequestMountApprovalCallback( CFTypeRef response, void * context );
53static void __DARequestProbeCallback( int status, void * context );
54static void __DARequestRefreshCallback( int status, void * context );
55static void __DARequestRenameCallback( int status, void * context );
56static void __DARequestUnmountCallback( int status, void * context );
57static void __DARequestUnmountApprovalCallback( CFTypeRef response, void * context );
58static int  __DARequestUnmountGetProcessID( void * context );
59///w:start
60static void __DARequestMountAuthorizationCallback( DAReturn status, void * context );
61static int  __DARequestUnmountTickle( void * context );
62static void __DARequestUnmountTickleCallback( int status, void * context );
63
64static void __DARequestAuthorize( DARequestRef        request,
65                                  DAAuthorizeCallback callback,
66                                  void *              callbackContext,
67                                  const char *        right )
68{
69    DASessionRef session;
70    CFArrayRef   sessionList;
71    CFIndex      sessionListCount;
72    CFIndex      sessionListIndex;
73
74    sessionList      = gDASessionList;
75    sessionListCount = CFArrayGetCount( sessionList );
76
77    for ( sessionListIndex = 0; sessionListIndex < sessionListCount; sessionListIndex++ )
78    {
79        session = ( void * ) CFArrayGetValueAtIndex( sessionList, sessionListIndex );
80
81        if ( strcmp( _DASessionGetName( session ), "SystemUIServer" ) == 0 )
82        {
83            break;
84        }
85    }
86
87    if ( sessionListIndex < sessionListCount )
88    {
89        DAAuthorizeWithCallback( session,
90                                 _kDAAuthorizeOptionAuthenticateAdministrator,
91                                 DARequestGetDisk( request ),
92                                 DARequestGetUserUID( request ),
93                                 DARequestGetUserGID( request ),
94                                 callback,
95                                 callbackContext,
96                                 right );
97    }
98    else
99    {
100        ( callback )( kDAReturnNotPrivileged, callbackContext );
101    }
102}
103///w:stop
104static void __DARequestDispatchCallback( DARequestRef request, DADissenterRef dissenter )
105{
106    DACallbackRef callback;
107
108    callback = DARequestGetCallback( request );
109
110    if ( callback )
111    {
112        CFArrayRef link;
113
114        link = DARequestGetLink( request );
115
116        if ( link )
117        {
118            dissenter = DARequestGetDissenter( request );
119
120            if ( dissenter == NULL )
121            {
122                CFIndex count;
123                CFIndex index;
124
125                count = CFArrayGetCount( link );
126
127                for ( index = 0; index < count; index++ )
128                {
129                    DARequestRef subrequest;
130
131                    subrequest = ( void * ) CFArrayGetValueAtIndex( link, index );
132
133                    dissenter = DARequestGetDissenter( subrequest );
134
135                    if ( dissenter )  break;
136                }
137            }
138        }
139
140        DAQueueCallback( callback, DARequestGetDisk( request ), dissenter );
141    }
142}
143
144static Boolean __DARequestClaim( DARequestRef request )
145{
146    DADiskRef disk;
147
148    disk = DARequestGetDisk( request );
149
150    /*
151     * Commence the claim release.
152     */
153
154    if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
155    {
156        DACallbackRef callback;
157
158        callback = DADiskGetClaim( disk );
159
160        CFRetain( request );
161
162        DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
163
164        DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
165
166        if ( callback )
167        {
168            if ( DACallbackGetAddress( callback ) )
169            {
170                DADiskClaimReleaseCallback( disk, callback, __DARequestClaimReleaseCallback, request );
171            }
172            else
173            {
174                DADissenterRef dissenter;
175
176                dissenter = DADissenterCreate( kCFAllocatorDefault, kDAReturnNotPermitted );
177
178                __DARequestClaimReleaseCallback( dissenter, request );
179
180                CFRelease( dissenter );
181            }
182        }
183        else
184        {
185            __DARequestClaimReleaseCallback( NULL, request );
186        }
187
188        return FALSE;
189    }
190
191    if ( DARequestGetDissenter( request ) )
192    {
193        DADissenterRef dissenter;
194
195        dissenter = DARequestGetDissenter( request );
196
197        __DARequestDispatchCallback( request, dissenter );
198
199        DAStageSignal( );
200
201        return TRUE;
202    }
203
204    /*
205     * Commence the claim.
206     */
207
208    {
209        DACallbackRef callback;
210
211        CFRetain( request );
212
213        DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
214
215        DADiskSetClaim( disk, NULL );
216
217        callback = DARequestGetCallback( request );
218
219        if ( callback )
220        {
221            DASessionRef session;
222
223            session = DACallbackGetSession( callback );
224
225            if ( session )
226            {
227                mach_vm_offset_t address;
228                mach_vm_offset_t context;
229
230                address = ___CFNumberGetIntegerValue( DARequestGetArgument2( request ) );
231                context = ___CFNumberGetIntegerValue( DARequestGetArgument3( request ) );
232
233                callback = DACallbackCreate( kCFAllocatorDefault, session, address, context, _kDADiskClaimReleaseCallback, 0, NULL, NULL );
234
235                if ( callback )
236                {
237                    DADiskSetClaim( disk, callback );
238
239                    CFRelease( callback );
240                }
241            }
242        }
243
244        __DARequestClaimCallback( 0, request );
245
246        return TRUE;
247    }
248}
249
250static void __DARequestClaimCallback( int status, void * context )
251{
252    DADiskRef    disk;
253    DARequestRef request = context;
254
255    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
256
257    disk = DARequestGetDisk( request );
258
259    DALogDebug( "  claimed disk, id = %@, success.", disk );
260
261    DARequestDispatchCallback( request, status ? unix_err( status ) : status );
262
263    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
264
265    DAStageSignal( );
266
267    CFRelease( request );
268}
269
270static void __DARequestClaimReleaseCallback( CFTypeRef response, void * context )
271{
272    DARequestRef request = context;
273
274    DARequestSetDissenter( request, response );
275
276    DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
277
278    DAStageSignal( );
279
280    CFRelease( request );
281}
282
283static Boolean __DARequestEject( DARequestRef request )
284{
285    DADiskRef disk;
286
287    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
288
289    disk = DARequestGetDisk( request );
290
291    /*
292     * Commence the eject approval.
293     */
294
295    if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
296    {
297        DAReturn status;
298
299        status = kDAReturnSuccess;
300
301        /*
302         * Determine whether the disk is ejectable.
303         */
304
305        if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWholeKey ) == NULL )
306        {
307            status = kDAReturnUnsupported;
308        }
309
310        if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWholeKey ) == kCFBooleanFalse )
311        {
312            status = kDAReturnUnsupported;
313        }
314
315        if ( status )
316        {
317            DARequestDispatchCallback( request, status );
318
319            DAStageSignal( );
320
321            return TRUE;
322        }
323        else
324        {
325            CFRetain( request );
326
327            DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
328
329            DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
330
331            DADiskEjectApprovalCallback( disk, __DARequestEjectApprovalCallback, request );
332
333            return FALSE;
334        }
335    }
336
337    if ( DARequestGetDissenter( request ) )
338    {
339        DADissenterRef dissenter;
340
341        dissenter = DARequestGetDissenter( request );
342
343        __DARequestDispatchCallback( request, dissenter );
344
345        DAStageSignal( );
346
347        return TRUE;
348    }
349
350    /*
351     * Commence the eject.
352     */
353
354    if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
355    {
356        CFRetain( request );
357
358        DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
359
360        DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
361
362        DALogDebug( "  ejected disk, id = %@, ongoing.", disk );
363
364        DAThreadExecute( __DARequestEjectEject, disk, __DARequestEjectCallback, request );
365
366        return TRUE;
367    }
368    else
369    {
370        return FALSE;
371    }
372}
373
374static void __DARequestEjectCallback( int status, void * context )
375{
376    DADiskRef    disk;
377    DARequestRef request = context;
378
379    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
380
381    disk = DARequestGetDisk( request );
382
383    if ( status )
384    {
385        /*
386         * We were unable to eject the disk.
387         */
388
389        DADissenterRef dissenter;
390
391        DALogDebug( "  ejected disk, id = %@, failure.", disk );
392
393        DALogDebug( "unable to eject %@ (status code 0x%08X).", disk, status );
394
395        dissenter = DADissenterCreate( kCFAllocatorDefault, unix_err( status ) );
396
397        DARequestSetDissenter( request, dissenter );
398
399        CFRelease( dissenter );
400    }
401    else
402    {
403        /*
404         * We were able to eject the disk.
405         */
406
407        DALogDebug( "  ejected disk, id = %@, success.", disk );
408    }
409
410    DARequestDispatchCallback( request, status ? unix_err( status ) : status );
411
412    DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
413
414    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
415
416    DAStageSignal( );
417
418    CFRelease( request );
419}
420
421static void __DARequestEjectApprovalCallback( CFTypeRef response, void * context )
422{
423    DARequestRef request = context;
424
425    DARequestSetDissenter( request, response );
426
427    DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
428
429    DAStageSignal( );
430
431    CFRelease( request );
432}
433
434static int __DARequestEjectEject( void * context )
435{
436    DADiskRef disk = context;
437    int       file;
438    int       status;
439
440    file = open( DADiskGetBSDPath( disk, TRUE ), O_RDONLY );
441
442    if ( file == -1 )
443    {
444        status = errno;
445    }
446    else
447    {
448        status = ioctl( file, DKIOCEJECT, NULL );
449
450        if ( status == -1 )
451        {
452            status = ( errno == ENOTTY ) ? 0 : errno;
453        }
454
455        close( file );
456    }
457
458    return status;
459}
460
461static Boolean __DARequestMount( DARequestRef request )
462{
463    DADiskRef disk;
464
465    disk = DARequestGetDisk( request );
466
467    if ( DARequestGetLink( request ) )
468    {
469        if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) )
470        {
471            return FALSE;
472        }
473    }
474
475    /*
476     * Commence the probe.
477     */
478
479    if ( DARequestGetState( request, kDARequestStateStagedProbe ) == FALSE )
480    {
481        /*
482         * Determine whether the disk is probeable.
483         */
484
485        if ( DADiskGetDescription( disk, kDADiskDescriptionMediaPathKey ) == NULL )
486        {
487            DARequestDispatchCallback( request, kDAReturnUnsupported );
488
489            DAStageSignal( );
490
491            return TRUE;
492        }
493
494        /*
495         * Determine whether the disk is mounted.
496         */
497
498         if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
499         {
500            DARequestSetState( request, kDARequestStateStagedProbe, TRUE );
501
502            DADiskSetState( disk, kDADiskStateStagedProbe, FALSE );
503
504            DAStageSignal( );
505
506            return FALSE;
507        }
508    }
509    else
510    {
511        if ( DADiskGetState( disk, kDADiskStateStagedProbe ) == FALSE )
512        {
513            return FALSE;
514        }
515    }
516
517    /*
518     * Commence the mount approval.
519     */
520
521    if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
522    {
523        DAReturn status;
524
525        status = kDAReturnSuccess;
526
527        /*
528         * Determine whether the disk is mountable.
529         */
530
531        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
532        {
533            status = kDAReturnUnsupported;
534        }
535
536        /*
537         * Determine whether the disk is mounted.
538         */
539
540        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) )
541        {
542            CFStringRef arguments;
543
544            arguments = DARequestGetArgument3( request );
545
546            if ( arguments == NULL || DAMountContainsArgument( arguments, kDAFileSystemMountArgumentUpdate ) == FALSE )
547            {
548                status = kDAReturnBusy;
549            }
550        }
551
552        if ( status )
553        {
554            DARequestDispatchCallback( request, status );
555
556            DAStageSignal( );
557
558            return TRUE;
559        }
560        else
561        {
562            CFRetain( request );
563
564            DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
565
566            DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
567
568            DADiskMountApprovalCallback( disk, __DARequestMountApprovalCallback, request );
569
570            return FALSE;
571        }
572    }
573///w:start
574    /*
575     * Commence the mount authorization.
576     */
577
578    if ( DARequestGetState( request, _kDARequestStateStagedAuthorize ) == FALSE )
579    {
580        DAReturn status;
581
582        status = kDAReturnSuccess;
583
584        if ( DARequestGetDissenter( request ) )
585        {
586            DADissenterRef dissenter;
587
588            dissenter = DARequestGetDissenter( request );
589
590            if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF01 )
591            {
592                DARequestSetDissenter( request, NULL );
593
594                status = kDAReturnNotPrivileged;
595            }
596            else if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF03 )
597            {
598                status = kDAReturnNotPrivileged;
599            }
600        }
601
602        if ( status )
603        {
604            CFRetain( request );
605
606            DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
607
608            DARequestSetState( request, _kDARequestStateStagedAuthorize, TRUE );
609
610            __DARequestAuthorize( request, __DARequestMountAuthorizationCallback, request, _kDAAuthorizeRightMount );
611
612            return FALSE;
613        }
614        else
615        {
616            DARequestSetState( request, _kDARequestStateStagedAuthorize, TRUE );
617        }
618    }
619///w:stop
620
621///w:start
622    if ( DARequestGetDissenter( request ) )
623    {
624        DADissenterRef dissenter;
625
626        dissenter = DARequestGetDissenter( request );
627
628        if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF02 )
629        {
630            DADiskSetState( disk, _kDADiskStateMountPreferenceNoWrite, TRUE );
631
632            DARequestSetDissenter( request, NULL );
633        }
634        else if ( DADissenterGetStatus( dissenter ) == 0xF8DAFF03 )
635        {
636            DADiskSetState( disk, _kDADiskStateMountPreferenceNoWrite, TRUE );
637
638            DARequestSetDissenter( request, NULL );
639        }
640    }
641///w:stop
642    if ( DARequestGetDissenter( request ) )
643    {
644        DADissenterRef dissenter;
645
646        dissenter = DARequestGetDissenter( request );
647
648        __DARequestDispatchCallback( request, dissenter );
649
650        DAStageSignal( );
651
652        return TRUE;
653    }
654
655    /*
656     * Commence the mount.
657     */
658
659    if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
660    {
661        CFTypeRef path;
662
663        path = DARequestGetArgument2( request );
664
665        if ( path )
666        {
667            path = CFURLCreateWithString( kCFAllocatorDefault, path, NULL );
668        }
669
670        CFRetain( request );
671
672        DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
673
674        DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
675
676        DAMountWithArguments( disk, path, __DARequestMountCallback, request, DARequestGetArgument3( request ), NULL );
677
678        if ( path )
679        {
680            CFRelease( path );
681        }
682
683        return TRUE;
684    }
685    else
686    {
687        return FALSE;
688    }
689}
690
691static void __DARequestMountCallback( int status, CFURLRef mountpoint, void * context )
692{
693    DADiskRef    disk;
694    DARequestRef request = context;
695
696    disk = DARequestGetDisk( request );
697
698    if ( status )
699    {
700        /*
701         * We were unable to mount the volume.
702         */
703
704        DADissenterRef dissenter;
705
706        dissenter = DADissenterCreate( kCFAllocatorDefault, unix_err( status ) );
707
708        DARequestSetDissenter( request, dissenter );
709
710        CFRelease( dissenter );
711    }
712    else
713    {
714        /*
715         * We were able to mount the volume.
716         */
717
718        CFStringRef arguments;
719
720        DADiskSetBypath( disk, mountpoint );
721
722        DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, mountpoint );
723
724        arguments = DARequestGetArgument3( request );
725
726        if ( arguments == NULL || DAMountContainsArgument( arguments, kDAFileSystemMountArgumentUpdate ) == FALSE )
727        {
728            DADiskDescriptionChangedCallback( disk, kDADiskDescriptionVolumePathKey );
729        }
730    }
731
732    DARequestDispatchCallback( request, status ? unix_err( status ) : status );
733
734    DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
735
736    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
737
738    DAStageSignal( );
739
740    CFRelease( request );
741}
742
743static void __DARequestMountApprovalCallback( CFTypeRef response, void * context )
744{
745    DARequestRef request = context;
746
747    DARequestSetDissenter( request, response );
748
749    DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
750
751    DAStageSignal( );
752
753    CFRelease( request );
754}
755///w:start
756static void __DARequestMountAuthorizationCallback( DAReturn status, void * context )
757{
758    DARequestRef request = context;
759
760    if ( status )
761    {
762        DADissenterRef dissenter;
763
764        dissenter = DADissenterCreate( kCFAllocatorDefault, status );
765
766        DARequestSetDissenter( request, dissenter );
767
768        CFRelease( dissenter );
769    }
770
771    DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
772
773    DAStageSignal( );
774
775    CFRelease( request );
776}
777///w:stop
778
779static Boolean __DARequestProbe( DARequestRef request )
780{
781    DADiskRef disk;
782
783    disk = DARequestGetDisk( request );
784
785    /*
786     * Commence the probe.
787     */
788
789///w:start
790    if ( DARequestGetState( request, kDARequestStateStagedProbe ) == FALSE )
791    {
792        /*
793         * Determine whether the disk is mounted.
794         */
795
796        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
797        {
798            DARequestSetState( request, kDARequestStateStagedProbe, TRUE );
799
800            DADiskSetState( disk, kDADiskStateStagedProbe, FALSE );
801
802            DAStageSignal( );
803
804            return FALSE;
805        }
806    }
807    else
808    {
809        if ( DADiskGetState( disk, kDADiskStateStagedProbe ) == FALSE )
810        {
811            return FALSE;
812        }
813    }
814///w:stop
815    if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
816    {
817        DAReturn status;
818
819        status = kDAReturnSuccess;
820
821        /*
822         * Determine whether the disk is mounted.
823         */
824
825        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) )
826        {
827            status = kDAReturnBusy;
828        }
829
830        if ( status )
831        {
832            DARequestDispatchCallback( request, status );
833
834            DAStageSignal( );
835
836            return TRUE;
837        }
838        else
839        {
840            CFRetain( request );
841
842            DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
843
844            DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
845
846///w:start
847//          DAProbe( disk, __DARequestProbeCallback, request );
848            __DARequestProbeCallback( 0, request );
849///w:stop
850
851            return TRUE;
852        }
853    }
854    else
855    {
856        return FALSE;
857    }
858}
859
860static void __DARequestProbeCallback( int status, void * context )
861{
862    DADiskRef    disk;
863    DARequestRef request = context;
864
865    disk = DARequestGetDisk( request );
866
867    DARequestDispatchCallback( request, status ? unix_err( status ) : status );
868
869    DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
870
871    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
872
873    DAStageSignal( );
874
875    CFRelease( request );
876}
877
878static Boolean __DARequestRefresh( DARequestRef request )
879{
880    DADiskRef disk;
881
882    disk = DARequestGetDisk( request );
883
884    /*
885     * Commence the refresh.
886     */
887
888    {
889        DAReturn status;
890
891        status = kDAReturnSuccess;
892
893        /*
894         * Determine whether the disk is mountable.
895         */
896
897        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
898        {
899            status = kDAReturnUnsupported;
900        }
901
902        if ( status )
903        {
904            DARequestDispatchCallback( request, status );
905
906            DAStageSignal( );
907
908            return TRUE;
909        }
910        else
911        {
912            CFRetain( request );
913
914            DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
915
916            status = _DADiskRefresh( disk );
917
918            __DARequestRefreshCallback( status ? ENOTSUP : 0, request );
919
920            return TRUE;
921        }
922    }
923}
924
925static void __DARequestRefreshCallback( int status, void * context )
926{
927    DADiskRef    disk;
928    DARequestRef request = context;
929
930    disk = DARequestGetDisk( request );
931
932    DARequestDispatchCallback( request, status ? unix_err( status ) : status );
933
934    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
935
936    DAStageSignal( );
937
938    CFRelease( request );
939}
940
941static Boolean __DARequestRename( DARequestRef request )
942{
943    DADiskRef disk;
944
945    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
946
947    disk = DARequestGetDisk( request );
948
949    /*
950     * Commence the rename.
951     */
952
953    if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
954    {
955        DAReturn status;
956
957        status = kDAReturnSuccess;
958
959        /*
960         * Determine whether the disk is mountable.
961         */
962
963        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
964        {
965            status = kDAReturnUnsupported;
966        }
967
968        /*
969         * Determine whether the disk is mounted.
970         */
971
972        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
973        {
974            status = kDAReturnNotMounted;
975        }
976
977        /*
978         * Determine whether the name is valid.
979         */
980
981        if ( DARequestGetArgument2( request ) == NULL )
982        {
983            status = kDAReturnUnsupported;
984        }
985
986        if ( status )
987        {
988            DARequestDispatchCallback( request, status );
989
990            DAStageSignal( );
991
992            return TRUE;
993        }
994        else
995        {
996            CFRetain( request );
997
998            DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
999
1000            DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
1001
1002            DALogDebug( "  renamed disk, id = %@, ongoing.", disk );
1003
1004            DAFileSystemRename( DADiskGetFileSystem( disk ),
1005                                DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ),
1006                                DARequestGetArgument2( request ),
1007                                __DARequestRenameCallback,
1008                                request );
1009
1010            return TRUE;
1011        }
1012    }
1013    else
1014    {
1015        return FALSE;
1016    }
1017}
1018
1019static void __DARequestRenameCallback( int status, void * context )
1020{
1021    DADiskRef    disk;
1022    DARequestRef request = context;
1023
1024    disk = DARequestGetDisk( request );
1025
1026    if ( status )
1027    {
1028        /*
1029         * We were unable to rename the disk.
1030         */
1031
1032        DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
1033
1034        DALogDebug( "  renamed disk, id = %@, failure.", disk );
1035
1036        DALogDebug( "unable to rename %@ (status code 0x%08X).", disk, status );
1037    }
1038    else
1039    {
1040        /*
1041         * We were able to rename the disk.
1042         */
1043
1044        CFMutableArrayRef keys;
1045
1046        keys = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
1047
1048        if ( keys )
1049        {
1050            CFStringRef name;
1051
1052            name = DARequestGetArgument2( request );
1053
1054            if ( DADiskCompareDescription( disk, kDADiskDescriptionVolumeNameKey, name ) )
1055            {
1056                CFURLRef mountpoint;
1057
1058                DADiskSetDescription( disk, kDADiskDescriptionVolumeNameKey, name );
1059
1060                CFArrayAppendValue( keys, kDADiskDescriptionVolumeNameKey );
1061
1062                /*
1063                 * Rename the mount point.
1064                 */
1065
1066                mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
1067
1068                if ( CFEqual( CFURLGetString( mountpoint ), CFSTR( "file:///" ) ) )
1069                {
1070                    mountpoint = DAMountCreateMountPointWithAction( disk, kDAMountPointActionMove );
1071
1072                    if ( mountpoint )
1073                    {
1074                        DADiskSetBypath( disk, mountpoint );
1075
1076                        CFRelease( mountpoint );
1077                    }
1078                }
1079                else
1080                {
1081                    mountpoint = DAMountCreateMountPointWithAction( disk, kDAMountPointActionMove );
1082
1083                    if ( mountpoint )
1084                    {
1085                        DADiskSetBypath( disk, mountpoint );
1086
1087                        DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, mountpoint );
1088
1089                        CFArrayAppendValue( keys, kDADiskDescriptionVolumePathKey );
1090
1091                        CFRelease( mountpoint );
1092                    }
1093                }
1094
1095                DADiskDescriptionChangedCallback( disk, keys );
1096            }
1097
1098            CFRelease( keys );
1099        }
1100
1101        DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
1102
1103        DALogDebug( "  renamed disk, id = %@, success.", disk );
1104    }
1105
1106    DARequestDispatchCallback( request, status ? unix_err( status ) : status );
1107
1108    DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
1109
1110    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
1111
1112    DAStageSignal( );
1113
1114    CFRelease( request );
1115}
1116
1117static Boolean __DARequestUnmount( DARequestRef request )
1118{
1119    DADiskRef disk;
1120
1121    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
1122
1123    disk = DARequestGetDisk( request );
1124
1125    if ( DARequestGetLink( request ) )
1126    {
1127        if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) )
1128        {
1129            return FALSE;
1130        }
1131    }
1132
1133    /*
1134     * Commence the unmount approval.
1135     */
1136
1137    if ( DARequestGetState( request, kDARequestStateStagedApprove ) == FALSE )
1138    {
1139        DAReturn status;
1140
1141        status = kDAReturnSuccess;
1142
1143        /*
1144         * Determine whether the disk is mountable.
1145         */
1146
1147        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumeMountableKey ) == kCFBooleanFalse )
1148        {
1149            status = kDAReturnUnsupported;
1150        }
1151
1152        /*
1153         * Determine whether the disk is mounted.
1154         */
1155
1156        if ( DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ) == NULL )
1157        {
1158            status = kDAReturnNotMounted;
1159        }
1160        else
1161        {
1162            CFURLRef mountpoint;
1163
1164            mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
1165
1166            if ( CFEqual( CFURLGetString( mountpoint ), CFSTR( "file:///" ) ) )
1167            {
1168                DADissenterRef dissenter;
1169
1170                status = kDAReturnBusy;
1171
1172                dissenter = DADissenterCreate( kCFAllocatorDefault, status );
1173
1174                DARequestSetDissenter( request, dissenter );
1175
1176                CFRelease( dissenter );
1177            }
1178        }
1179
1180        if ( status )
1181        {
1182            DARequestDispatchCallback( request, status );
1183
1184            DAStageSignal( );
1185
1186            return TRUE;
1187        }
1188        else
1189        {
1190            if ( DADiskGetState( disk, kDADiskStateZombie ) )
1191            {
1192                DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
1193
1194                if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue )
1195                {
1196                    DADialogShowDeviceRemoval( disk );
1197                }
1198            }
1199            else
1200            {
1201                CFRetain( request );
1202
1203                DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
1204
1205                DARequestSetState( request, kDARequestStateStagedApprove, TRUE );
1206///w:start
1207                if ( DADiskGetDescription( disk, kDADiskDescriptionMediaWritableKey ) == kCFBooleanTrue )
1208                {
1209                    DAThreadExecute( __DARequestUnmountTickle, disk, __DARequestUnmountTickleCallback, request );
1210
1211                    return FALSE;
1212                }
1213///w:stop
1214
1215                DADiskUnmountApprovalCallback( disk, __DARequestUnmountApprovalCallback, request );
1216
1217                return FALSE;
1218            }
1219        }
1220    }
1221
1222    if ( DARequestGetDissenter( request ) )
1223    {
1224        DADissenterRef dissenter;
1225
1226        dissenter = DARequestGetDissenter( request );
1227
1228        __DARequestDispatchCallback( request, dissenter );
1229
1230        DAStageSignal( );
1231
1232        return TRUE;
1233    }
1234
1235    /*
1236     * Commence the unmount.
1237     */
1238
1239    if ( DAUnitGetState( disk, kDAUnitStateCommandActive ) == FALSE )
1240    {
1241        DADiskUnmountOptions options;
1242
1243        options = DARequestGetArgument1( request );
1244
1245        CFRetain( request );
1246
1247        DADiskSetState( disk, kDADiskStateCommandActive, TRUE );
1248
1249        DAUnitSetState( disk, kDAUnitStateCommandActive, TRUE );
1250
1251        DALogDebug( "  unmounted disk, id = %@, ongoing.", disk );
1252
1253        DAFileSystemUnmountWithArguments( DADiskGetFileSystem( disk ),
1254                                          DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey ),
1255                                          __DARequestUnmountCallback,
1256                                          request,
1257                                          ( options & kDADiskUnmountOptionForce ) ? kDAFileSystemUnmountArgumentForce : NULL,
1258                                          NULL );
1259
1260        return TRUE;
1261    }
1262    else
1263    {
1264        return FALSE;
1265    }
1266}
1267
1268static void __DARequestUnmountCallback( int status, void * context )
1269{
1270    DADiskRef    disk;
1271    DARequestRef request = context;
1272
1273    DALogDebugHeader( "%s -> %s", gDAProcessNameID, gDAProcessNameID );
1274
1275    disk = DARequestGetDisk( request );
1276
1277    if ( status )
1278    {
1279        /*
1280         * We were unable to unmount the volume.
1281         */
1282
1283        DADissenterRef dissenter;
1284
1285        dissenter = DARequestGetDissenter( request );
1286
1287        if ( dissenter == NULL )
1288        {
1289            DALogDebug( "  unmounted disk, id = %@, failure.", disk );
1290
1291            DALogDebug( "unable to unmount %@ (status code 0x%08X).", disk, status );
1292
1293///w:start
1294            status = EBUSY;
1295///w:stop
1296            dissenter = DADissenterCreate( kCFAllocatorDefault, unix_err( status ) );
1297
1298            DARequestSetDissenter( request, dissenter );
1299
1300            DAThreadExecute( __DARequestUnmountGetProcessID, request, __DARequestUnmountCallback, request );
1301
1302            CFRelease( dissenter );
1303
1304            return;
1305        }
1306
1307        __DARequestDispatchCallback( request, dissenter );
1308    }
1309    else
1310    {
1311        /*
1312         * We were able to unmount the volume.
1313         */
1314
1315        CFURLRef mountpoint;
1316
1317        mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
1318
1319        DAMountRemoveMountPoint( mountpoint );
1320
1321        DADiskSetBypath( disk, NULL );
1322
1323        DALogDebug( "  unmounted disk, id = %@, success.", disk );
1324
1325        if ( DADiskGetDescription( disk, kDADiskDescriptionMediaPathKey ) )
1326        {
1327            DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, NULL );
1328
1329            DADiskDescriptionChangedCallback( disk, kDADiskDescriptionVolumePathKey );
1330        }
1331        else
1332        {
1333            DALogDebug( "  removed disk, id = %@.", disk );
1334
1335            DADiskDisappearedCallback( disk );
1336
1337            DADiskSetDescription( disk, kDADiskDescriptionVolumePathKey, NULL );
1338
1339            DADiskSetState( disk, kDADiskStateZombie, TRUE );
1340
1341            ___CFArrayRemoveValue( gDADiskList, disk );
1342        }
1343
1344        __DARequestDispatchCallback( request, NULL );
1345    }
1346
1347    DAUnitSetState( disk, kDAUnitStateCommandActive, FALSE );
1348
1349    DADiskSetState( disk, kDADiskStateCommandActive, FALSE );
1350
1351    DAStageSignal( );
1352
1353    CFRelease( request );
1354}
1355
1356static void __DARequestUnmountApprovalCallback( CFTypeRef response, void * context )
1357{
1358    DARequestRef request = context;
1359
1360    if ( response )
1361    {
1362        DADiskUnmountOptions options;
1363
1364        options = DARequestGetArgument1( request );
1365
1366        if ( ( options & kDADiskUnmountOptionForce ) == 0 )
1367        {
1368            DARequestSetDissenter( request, response );
1369        }
1370///w:start
1371        if ( DADissenterGetStatus( response ) == 0xF8DAFF01 )
1372        {
1373            DARequestSetDissenter( request, response );
1374        }
1375///w:stop
1376    }
1377
1378    DADiskSetState( DARequestGetDisk( request ), kDADiskStateCommandActive, FALSE );
1379
1380    DAStageSignal( );
1381
1382    CFRelease( request );
1383}
1384
1385static int  __DARequestUnmountGetProcessID( void * context )
1386{
1387    DADiskRef    disk;
1388    CFURLRef     mountpoint;
1389    char *       path;
1390    DARequestRef request = context;
1391
1392    disk = DARequestGetDisk( request );
1393
1394    mountpoint = DADiskGetDescription( disk, kDADiskDescriptionVolumePathKey );
1395
1396    path = ___CFURLCopyFileSystemRepresentation( mountpoint );
1397
1398    if ( path )
1399    {
1400        pid_t dissenterPID = 0;
1401
1402        proc_listpidspath( PROC_ALL_PIDS, 0, path, PROC_LISTPIDSPATH_EXCLUDE_EVTONLY | PROC_LISTPIDSPATH_PATH_IS_VOLUME, &dissenterPID, sizeof( dissenterPID ) );
1403
1404        if ( dissenterPID )
1405        {
1406            DADissenterRef dissenter;
1407
1408            dissenter = DARequestGetDissenter( request );
1409
1410            DADissenterSetProcessID( dissenter, dissenterPID );
1411        }
1412
1413        free( path );
1414    }
1415
1416    return -1;
1417}
1418///w:start
1419static int __DARequestUnmountTickle( void * context )
1420{
1421    DADiskRef disk = context;
1422    size_t    size;
1423
1424    size = ___CFNumberGetIntegerValue( DADiskGetDescription( disk, kDADiskDescriptionMediaBlockSizeKey ) );
1425
1426    if ( size )
1427    {
1428        char * buffer;
1429
1430        buffer = malloc( size );
1431
1432        if ( buffer )
1433        {
1434            int file;
1435
1436            file = open( DADiskGetBSDPath( disk, TRUE ), O_RDONLY );
1437
1438            if ( file != -1 )
1439            {
1440                read( file, buffer, size );
1441
1442                close( file );
1443            }
1444
1445            free( buffer );
1446        }
1447    }
1448
1449    return 0;
1450}
1451
1452static void __DARequestUnmountTickleCallback( int status, void * context )
1453{
1454    DADiskUnmountApprovalCallback( DARequestGetDisk( context ), __DARequestUnmountApprovalCallback, context );
1455}
1456///w:stop
1457
1458DARequestRef DARequestCreate( CFAllocatorRef allocator,
1459                              _DARequestKind kind,
1460                              DADiskRef      argument0,
1461                              CFIndex        argument1,
1462                              CFTypeRef      argument2,
1463                              CFTypeRef      argument3,
1464                              uid_t          userUID,
1465                              gid_t          userGID,
1466                              DACallbackRef  callback )
1467{
1468    CFMutableDictionaryRef request;
1469
1470    request = CFDictionaryCreateMutable( kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
1471
1472    if ( request )
1473    {
1474        ___CFDictionarySetIntegerValue( request, _kDARequestKindKey, kind );
1475
1476        if ( argument0 )  CFDictionarySetValue( request, _kDARequestDiskKey, argument0 );
1477        if ( argument1 )  ___CFDictionarySetIntegerValue( request, _kDARequestArgument1Key, argument1 );
1478        if ( argument2 )  CFDictionarySetValue( request, _kDARequestArgument2Key, argument2 );
1479        if ( argument3 )  CFDictionarySetValue( request, _kDARequestArgument3Key, argument3 );
1480
1481        ___CFDictionarySetIntegerValue( request, _kDARequestUserGIDKey, userGID );
1482        ___CFDictionarySetIntegerValue( request, _kDARequestUserUIDKey, userUID );
1483
1484        if ( callback )  CFDictionarySetValue( request, _kDARequestCallbackKey,  callback );
1485    }
1486
1487    return ( void * ) request;
1488}
1489
1490Boolean DARequestDispatch( DARequestRef request )
1491{
1492    Boolean dispatch;
1493
1494    dispatch = FALSE;
1495
1496    if ( request )
1497    {
1498        DADiskRef disk;
1499
1500        disk = DARequestGetDisk( request );
1501
1502        if ( disk )
1503        {
1504            if ( DADiskGetState( disk, kDADiskStateCommandActive ) == FALSE )
1505            {
1506                if ( DADiskGetState( disk, kDADiskStateStagedMount ) )
1507                {
1508                    switch ( DARequestGetKind( request ) )
1509                    {
1510                        case _kDADiskClaim:
1511                        {
1512                            dispatch = __DARequestClaim( request );
1513
1514                            break;
1515                        }
1516                        case _kDADiskEject:
1517                        {
1518                            dispatch = __DARequestEject( request );
1519
1520                            break;
1521                        }
1522                        case _kDADiskMount:
1523                        {
1524                            dispatch = __DARequestMount( request );
1525
1526                            break;
1527                        }
1528                        case _kDADiskProbe:
1529                        {
1530                            dispatch = __DARequestProbe( request );
1531
1532                            break;
1533                        }
1534                        case _kDADiskRefresh:
1535                        {
1536                            dispatch = __DARequestRefresh( request );
1537
1538                            break;
1539                        }
1540                        case _kDADiskRename:
1541                        {
1542                            dispatch = __DARequestRename( request );
1543
1544                            break;
1545                        }
1546                        case _kDADiskUnmount:
1547                        {
1548                            dispatch = __DARequestUnmount( request );
1549
1550                            break;
1551                        }
1552                    }
1553                }
1554            }
1555        }
1556    }
1557
1558    return dispatch;
1559}
1560
1561void DARequestDispatchCallback( DARequestRef request, DAReturn status )
1562{
1563    if ( status )
1564    {
1565        DADissenterRef dissenter;
1566
1567        dissenter = DADissenterCreate( kCFAllocatorDefault, status );
1568
1569        __DARequestDispatchCallback( request, dissenter );
1570
1571        CFRelease( dissenter );
1572    }
1573    else
1574    {
1575        __DARequestDispatchCallback( request, NULL );
1576    }
1577}
1578
1579CFIndex DARequestGetArgument1( DARequestRef request )
1580{
1581    return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestArgument1Key );
1582}
1583
1584CFTypeRef DARequestGetArgument2( DARequestRef request )
1585{
1586    return CFDictionaryGetValue( ( void * ) request, _kDARequestArgument2Key );
1587}
1588
1589CFTypeRef DARequestGetArgument3( DARequestRef request )
1590{
1591    return CFDictionaryGetValue( ( void * ) request, _kDARequestArgument3Key );
1592}
1593
1594DACallbackRef DARequestGetCallback( DARequestRef request )
1595{
1596    return ( void * ) CFDictionaryGetValue( ( void * ) request, _kDARequestCallbackKey );
1597}
1598
1599DADiskRef DARequestGetDisk( DARequestRef request )
1600{
1601    return ( void * ) CFDictionaryGetValue( ( void * ) request, _kDARequestDiskKey );
1602}
1603
1604DADissenterRef DARequestGetDissenter( DARequestRef request )
1605{
1606    return CFDictionaryGetValue( ( void * ) request, _kDARequestDissenterKey );
1607}
1608
1609_DARequestKind DARequestGetKind( DARequestRef request )
1610{
1611    return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestKindKey );
1612}
1613
1614CFArrayRef DARequestGetLink( DARequestRef request )
1615{
1616    return CFDictionaryGetValue( ( void * ) request, _kDARequestLinkKey );
1617}
1618
1619Boolean DARequestGetState( DARequestRef request, DARequestState state )
1620{
1621    return ( ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestStateKey ) & state ) ? TRUE : FALSE;
1622}
1623
1624gid_t DARequestGetUserGID( DARequestRef request )
1625{
1626    return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestUserGIDKey );
1627}
1628
1629uid_t DARequestGetUserUID( DARequestRef request )
1630{
1631    return ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestUserUIDKey );
1632}
1633
1634void DARequestSetCallback( DARequestRef request, DACallbackRef callback )
1635{
1636    if ( callback )
1637    {
1638        CFDictionarySetValue( ( void * ) request, _kDARequestCallbackKey, callback );
1639    }
1640    else
1641    {
1642        CFDictionaryRemoveValue( ( void * ) request, _kDARequestCallbackKey );
1643    }
1644}
1645
1646void DARequestSetDissenter( DARequestRef request, DADissenterRef dissenter )
1647{
1648    if ( dissenter )
1649    {
1650        CFDictionarySetValue( ( void * ) request, _kDARequestDissenterKey, dissenter );
1651    }
1652    else
1653    {
1654        CFDictionaryRemoveValue( ( void * ) request, _kDARequestDissenterKey );
1655    }
1656}
1657
1658void DARequestSetLink( DARequestRef request, CFArrayRef link )
1659{
1660    if ( link )
1661    {
1662        CFDictionarySetValue( ( void * ) request, _kDARequestLinkKey, link );
1663    }
1664    else
1665    {
1666        CFDictionaryRemoveValue( ( void * ) request, _kDARequestLinkKey );
1667    }
1668}
1669
1670void DARequestSetState( DARequestRef request, DARequestState state, Boolean value )
1671{
1672    if ( value )
1673    {
1674        state = ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestStateKey ) | state;
1675    }
1676    else
1677    {
1678        state = ___CFDictionaryGetIntegerValue( ( void * ) request, _kDARequestStateKey ) & ~state;
1679    }
1680
1681    ___CFDictionarySetIntegerValue( ( void * ) request, _kDARequestStateKey, state );
1682}
1683