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 "DAFileSystem.h"
25
26#include "DABase.h"
27#include "DACommand.h"
28#include "DAInternal.h"
29
30#include <fsproperties.h>
31#include <paths.h>
32#include <unistd.h>
33#include <sys/attr.h>
34#include <sys/dirent.h>
35#include <sys/loadable_fs.h>
36#include <CoreFoundation/CoreFoundation.h>
37#include <CoreFoundation/CFRuntime.h>
38
39#define __kDAFileSystemUUIDSpaceSHA1 CFUUIDGetConstantUUIDWithBytes( kCFAllocatorDefault,               \
40                                                                     0xB3, 0xE2, 0x0F, 0x39,            \
41                                                                     0xF2, 0x92,                        \
42                                                                     0x11, 0xD6,                        \
43                                                                     0x97, 0xA4,                        \
44                                                                     0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC )
45
46struct __DAFileSystem
47{
48    CFRuntimeBase   _base;
49    CFURLRef        _id;
50    CFDictionaryRef _properties;
51};
52
53typedef struct __DAFileSystem __DAFileSystem;
54
55struct __DAFileSystemContext
56{
57    DAFileSystemCallback callback;
58    void *               callbackContext;
59};
60
61typedef struct __DAFileSystemContext __DAFileSystemContext;
62
63struct __DAFileSystemProbeContext
64{
65    DAFileSystemProbeCallback callback;
66    void *                    callbackContext;
67    CFStringRef               deviceName;
68    CFStringRef               devicePath;
69    CFURLRef                  probeCommand;
70    CFURLRef                  repairCommand;
71    CFBooleanRef              volumeClean;
72    CFStringRef               volumeName;
73    CFUUIDRef                 volumeUUID;
74};
75
76typedef struct __DAFileSystemProbeContext __DAFileSystemProbeContext;
77
78struct __DAFileSystemRenameBuffer
79{
80    attrreference_t data;
81    char            name[MAXNAMLEN + 1];
82};
83
84typedef struct __DAFileSystemRenameBuffer __DAFileSystemRenameBuffer;
85
86static CFStringRef __DAFileSystemCopyDescription( CFTypeRef object );
87static CFStringRef __DAFileSystemCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options );
88static void        __DAFileSystemDeallocate( CFTypeRef object );
89static Boolean     __DAFileSystemEqual( CFTypeRef object1, CFTypeRef object2 );
90static CFHashCode  __DAFileSystemHash( CFTypeRef object );
91
92static const CFRuntimeClass __DAFileSystemClass =
93{
94    0,
95    "DAFileSystem",
96    NULL,
97    NULL,
98    __DAFileSystemDeallocate,
99    __DAFileSystemEqual,
100    __DAFileSystemHash,
101    __DAFileSystemCopyFormattingDescription,
102    __DAFileSystemCopyDescription
103};
104
105static CFTypeID __kDAFileSystemTypeID = _kCFRuntimeNotATypeID;
106
107const CFStringRef kDAFileSystemMountArgumentForce       = CFSTR( "force"    );
108const CFStringRef kDAFileSystemMountArgumentNoDevice    = CFSTR( "nodev"    );
109const CFStringRef kDAFileSystemMountArgumentNoExecute   = CFSTR( "noexec"   );
110const CFStringRef kDAFileSystemMountArgumentNoOwnership = CFSTR( "noowners" );
111const CFStringRef kDAFileSystemMountArgumentNoSetUserID = CFSTR( "nosuid"   );
112const CFStringRef kDAFileSystemMountArgumentNoWrite     = CFSTR( "rdonly"   );
113const CFStringRef kDAFileSystemMountArgumentUnion       = CFSTR( "union"    );
114const CFStringRef kDAFileSystemMountArgumentUpdate      = CFSTR( "update"   );
115
116const CFStringRef kDAFileSystemUnmountArgumentForce     = CFSTR( "force" );
117
118static void __DAFileSystemProbeCallbackStage1( int status, CFDataRef output, void * context );
119static void __DAFileSystemProbeCallbackStage2( int status, CFDataRef output, void * context );
120static void __DAFileSystemProbeCallbackStage3( int status, CFDataRef output, void * context );
121
122static void __DAFileSystemCallback( int status, CFDataRef output, void * parameter )
123{
124    /*
125     * Process the probe request completion.
126     */
127
128    __DAFileSystemContext * context = parameter;
129
130    if ( context->callback )
131    {
132        ( context->callback )( status, context->callbackContext );
133    }
134
135    free( context );
136}
137
138static CFStringRef __DAFileSystemCopyDescription( CFTypeRef object )
139{
140    DAFileSystemRef filesystem = ( DAFileSystemRef ) object;
141
142    return CFStringCreateWithFormat( kCFAllocatorDefault,
143                                     NULL,
144                                     CFSTR( "<DAFileSystem %p [%p]>{id = %@}" ),
145                                     object,
146                                     CFGetAllocator( object ),
147                                     filesystem->_id );
148}
149
150static CFStringRef __DAFileSystemCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options )
151{
152    DAFileSystemRef filesystem = ( DAFileSystemRef ) object;
153
154    return CFStringCreateWithFormat( kCFAllocatorDefault, NULL, CFSTR( "%@" ), filesystem->_id );
155}
156
157static DAFileSystemRef __DAFileSystemCreate( CFAllocatorRef allocator, CFURLRef id, CFDictionaryRef properties )
158{
159    __DAFileSystem * filesystem;
160
161    filesystem = ( void * ) _CFRuntimeCreateInstance( allocator, __kDAFileSystemTypeID, sizeof( __DAFileSystem ) - sizeof( CFRuntimeBase ), NULL );
162
163    if ( filesystem )
164    {
165        filesystem->_id         = CFRetain( id );
166        filesystem->_properties = CFRetain( properties );
167    }
168
169    return filesystem;
170}
171
172static void __DAFileSystemDeallocate( CFTypeRef object )
173{
174    DAFileSystemRef filesystem = ( DAFileSystemRef ) object;
175
176    if ( filesystem->_id         )  CFRelease( filesystem->_id         );
177    if ( filesystem->_properties )  CFRelease( filesystem->_properties );
178}
179
180static Boolean __DAFileSystemEqual( CFTypeRef object1, CFTypeRef object2 )
181{
182    DAFileSystemRef filesystem1 = ( DAFileSystemRef ) object1;
183    DAFileSystemRef filesystem2 = ( DAFileSystemRef ) object2;
184
185    return CFEqual( filesystem1->_id, filesystem2->_id );
186}
187
188static CFHashCode __DAFileSystemHash( CFTypeRef object )
189{
190    DAFileSystemRef filesystem = ( DAFileSystemRef ) object;
191
192    return CFHash( filesystem->_id );
193}
194
195static void __DAFileSystemProbeCallback( int status, void * parameter, CFDataRef output )
196{
197    /*
198     * Process the probe request completion.
199     */
200
201    __DAFileSystemProbeContext * context = parameter;
202
203    if ( context->callback )
204    {
205        if ( status )
206        {
207            ( context->callback )( status, NULL, NULL, NULL, context->callbackContext );
208        }
209        else
210        {
211            ( context->callback )( status, context->volumeClean, context->volumeName, context->volumeUUID, context->callbackContext );
212        }
213    }
214
215    CFRelease( context->deviceName   );
216    CFRelease( context->devicePath   );
217    CFRelease( context->probeCommand );
218
219    if ( context->repairCommand )  CFRelease( context->repairCommand );
220    if ( context->volumeClean   )  CFRelease( context->volumeClean   );
221    if ( context->volumeName    )  CFRelease( context->volumeName    );
222    if ( context->volumeUUID    )  CFRelease( context->volumeUUID    );
223
224    free( context );
225}
226
227static void __DAFileSystemProbeCallbackStage1( int status, CFDataRef output, void * parameter )
228{
229    /*
230     * Process the probe command's completion.
231     */
232
233    __DAFileSystemProbeContext * context = parameter;
234
235    if ( status == FSUR_RECOGNIZED )
236    {
237        /*
238         * Obtain the volume name.
239         */
240
241        if ( output )
242        {
243            CFStringRef string;
244
245            string = CFStringCreateFromExternalRepresentation( kCFAllocatorDefault, output, kCFStringEncodingUTF8 );
246
247            if ( string )
248            {
249                if ( CFStringGetLength( string ) )
250                {
251                    context->volumeName = CFStringCreateMutableCopy( kCFAllocatorDefault, 0, string );
252
253                    if ( context->volumeName )
254                    {
255                        CFStringTrim( ( CFMutableStringRef ) context->volumeName, CFSTR( "\n" ) );
256                    }
257                }
258
259                CFRelease( string );
260            }
261        }
262
263        /*
264         * Execute the "get UUID" command.
265         */
266
267        DACommandExecute( context->probeCommand,
268                          kDACommandExecuteOptionCaptureOutput,
269                          ___UID_ROOT,
270                          ___GID_WHEEL,
271                          __DAFileSystemProbeCallbackStage2,
272                          context,
273                          CFSTR( "-k" ),
274                          context->deviceName,
275                          NULL );
276    }
277    else
278    {
279        __DAFileSystemProbeCallback( status, context, NULL );
280    }
281}
282
283static void __DAFileSystemProbeCallbackStage2( int status, CFDataRef output, void * parameter )
284{
285    /*
286     * Process the "get UUID" command's completion.
287     */
288
289    __DAFileSystemProbeContext * context = parameter;
290
291    if ( status == FSUR_IO_SUCCESS )
292    {
293        /*
294         * Obtain the volume UUID.  The "get UUID" command returns a unique 64-bit number, which
295         * we must map into the official, structured 128-bit UUID format.  One would expect that
296         * the "get UUID" interface return the official UUID format, when it is revised later on,
297         * and we take that into account here.
298         */
299
300        if ( output )
301        {
302            CFStringRef string;
303
304            string = CFStringCreateFromExternalRepresentation( kCFAllocatorDefault, output, kCFStringEncodingUTF8 );
305
306            if ( string )
307            {
308                context->volumeUUID = ___CFUUIDCreateFromString( kCFAllocatorDefault, string );
309
310                CFRelease( string );
311            }
312        }
313    }
314
315    if ( context->repairCommand )
316    {
317        /*
318         * Execute the "is clean" command.
319         */
320
321        DACommandExecute( context->repairCommand,
322                          kDACommandExecuteOptionDefault,
323                          ___UID_ROOT,
324                          ___GID_WHEEL,
325                          __DAFileSystemProbeCallbackStage3,
326                          context,
327                          CFSTR( "-q" ),
328                          context->devicePath,
329                          NULL );
330    }
331    else
332    {
333        /*
334         * Skip the "is clean" command, as it is not applicable.
335         */
336
337        __DAFileSystemProbeCallbackStage3( 0, NULL, context );
338    }
339}
340
341static void __DAFileSystemProbeCallbackStage3( int status, CFDataRef output, void * parameter )
342{
343    /*
344     * Process the "is clean" command's completion.
345     */
346
347    __DAFileSystemProbeContext * context = parameter;
348
349    context->volumeClean = CFRetain( ( status == 0 ) ? kCFBooleanTrue : kCFBooleanFalse );
350
351    __DAFileSystemProbeCallback( 0, context, NULL );
352}
353
354CFStringRef _DAFileSystemCopyName( DAFileSystemRef filesystem, CFURLRef mountpoint )
355{
356    struct attr_name_t
357    {
358        uint32_t        size;
359        attrreference_t data;
360        char            name[MAXNAMLEN + 1];
361    };
362
363    struct attr_name_t attr     = { 0 };
364    struct attrlist    attrlist = { 0 };
365    CFStringRef        name     = NULL;
366    char *             path     = NULL;
367    int                status   = 0;
368
369    attrlist.bitmapcount = ATTR_BIT_MAP_COUNT;
370    attrlist.volattr     = ATTR_VOL_INFO | ATTR_VOL_NAME;
371
372    path = ___CFURLCopyFileSystemRepresentation( mountpoint );
373    if ( path == NULL )  goto _DAFileSystemCopyNameErr;
374
375    status = getattrlist( path, &attrlist, &attr, sizeof( attr ), 0 );
376    if ( status == -1 )  goto _DAFileSystemCopyNameErr;
377
378    if ( attr.data.attr_length )
379    {
380        name = CFStringCreateWithCString( kCFAllocatorDefault, ( ( char * ) &attr.data ) + attr.data.attr_dataoffset, kCFStringEncodingUTF8 );
381    }
382
383_DAFileSystemCopyNameErr:
384
385    if ( path )  free( path );
386
387    return name;
388}
389
390CFUUIDRef _DAFileSystemCreateUUIDFromString( CFAllocatorRef allocator, CFStringRef string )
391{
392    CFDataRef data;
393    CFUUIDRef uuid = NULL;
394
395    data = ___CFDataCreateFromString( allocator, string );
396
397    if ( data )
398    {
399        if ( CFDataGetLength( data ) == 8 )
400        {
401            if ( *( ( UInt64 * ) CFDataGetBytePtr( data ) ) )
402            {
403                uuid = ___CFUUIDCreateFromName( allocator, __kDAFileSystemUUIDSpaceSHA1, data );
404            }
405            else
406            {
407                uuid = CFRetain( ___kCFUUIDNull );
408            }
409        }
410
411        CFRelease( data );
412    }
413
414    return uuid;
415}
416
417DAFileSystemRef DAFileSystemCreate( CFAllocatorRef allocator, CFURLRef path )
418{
419    DAFileSystemRef filesystem = NULL;
420    CFDictionaryRef properties;
421
422    /*
423     * Obtain the file system properties.
424     */
425
426    properties = CFBundleCopyInfoDictionaryInDirectory( path );
427
428    if ( properties )
429    {
430        CFURLRef id;
431
432        /*
433         * Create the file system object's unique identifier.
434         */
435
436        id = CFURLCopyAbsoluteURL( path );
437
438        if ( id )
439        {
440            /*
441             * Create the file system object.
442             */
443
444            filesystem = __DAFileSystemCreate( allocator, id, properties );
445
446            CFRelease( id );
447        }
448
449        CFRelease( properties );
450    }
451
452    return filesystem;
453}
454
455CFRunLoopSourceRef DAFileSystemCreateRunLoopSource( CFAllocatorRef allocator, CFIndex order )
456{
457    return DACommandCreateRunLoopSource( allocator, order );
458}
459
460CFStringRef DAFileSystemGetKind( DAFileSystemRef filesystem )
461{
462    return CFDictionaryGetValue( filesystem->_properties, kCFBundleNameKey );
463}
464
465CFDictionaryRef DAFileSystemGetProbeList( DAFileSystemRef filesystem )
466{
467    return CFDictionaryGetValue( filesystem->_properties, CFSTR( kFSMediaTypesKey ) );
468}
469
470CFTypeID DAFileSystemGetTypeID( void )
471{
472    return __kDAFileSystemTypeID;
473}
474
475void DAFileSystemInitialize( void )
476{
477    __kDAFileSystemTypeID = _CFRuntimeRegisterClass( &__DAFileSystemClass );
478}
479
480void DAFileSystemMount( DAFileSystemRef      filesystem,
481                        CFURLRef             device,
482                        CFURLRef             mountpoint,
483                        uid_t                userUID,
484                        gid_t                userGID,
485                        DAFileSystemCallback callback,
486                        void *               callbackContext )
487{
488    /*
489     * Mount the specified volume.  A status of 0 indicates success.
490     */
491
492    DAFileSystemMountWithArguments( filesystem, device, mountpoint, userUID, userGID, callback, callbackContext, NULL );
493}
494
495void DAFileSystemMountWithArguments( DAFileSystemRef      filesystem,
496                                     CFURLRef             device,
497                                     CFURLRef             mountpoint,
498                                     uid_t                userUID,
499                                     gid_t                userGID,
500                                     DAFileSystemCallback callback,
501                                     void *               callbackContext,
502                                     ... )
503{
504    /*
505     * Mount the specified volume.  A status of 0 indicates success.  All arguments in
506     * the argument list shall be of type CFStringRef.  The argument list must be NULL
507     * terminated.
508     */
509
510    CFStringRef             argument       = NULL;
511    va_list                 arguments;
512    CFURLRef                command        = NULL;
513    __DAFileSystemContext * context        = NULL;
514    CFStringRef             devicePath     = NULL;
515    CFStringRef             mountpointPath = NULL;
516    CFMutableStringRef      options        = NULL;
517    int                     status         = 0;
518
519    /*
520     * Prepare to mount the volume.
521     */
522
523    command = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, CFSTR( "/sbin/mount" ), kCFURLPOSIXPathStyle, FALSE );
524    if ( command == NULL )  { status = ENOTSUP; goto DAFileSystemMountErr; }
525
526    context = malloc( sizeof( __DAFileSystemContext ) );
527    if ( context == NULL )  { status = ENOMEM; goto DAFileSystemMountErr; }
528
529    devicePath = CFURLCopyFileSystemPath( device, kCFURLPOSIXPathStyle );
530    if ( devicePath == NULL )  { status = EINVAL; goto DAFileSystemMountErr; }
531
532    mountpointPath = CFURLCopyFileSystemPath( mountpoint, kCFURLPOSIXPathStyle );
533    if ( mountpointPath == NULL )  { status = EINVAL; goto DAFileSystemMountErr; }
534
535    options = CFStringCreateMutable( kCFAllocatorDefault, 0 );
536    if ( options == NULL )  { status = ENOMEM; goto DAFileSystemMountErr; }
537
538    /*
539     * Prepare the mount options.
540     */
541
542    va_start( arguments, callbackContext );
543
544    while ( ( argument = va_arg( arguments, CFStringRef ) ) )
545    {
546        CFStringAppend( options, argument );
547        CFStringAppend( options, CFSTR( "," ) );
548    }
549
550    va_end( arguments );
551
552    CFStringTrim( options, CFSTR( "," ) );
553
554    /*
555     * Execute the mount command.
556     */
557
558    context->callback        = callback;
559    context->callbackContext = callbackContext;
560
561    if ( CFStringGetLength( options ) )
562    {
563        DACommandExecute( command,
564                          kDACommandExecuteOptionDefault,
565                          userUID,
566                          userGID,
567                          __DAFileSystemCallback,
568                          context,
569                          CFSTR( "-t" ),
570                          DAFileSystemGetKind( filesystem ),
571                          CFSTR( "-o" ),
572                          options,
573                          devicePath,
574                          mountpointPath,
575                          NULL );
576    }
577    else
578    {
579        DACommandExecute( command,
580                          kDACommandExecuteOptionDefault,
581                          userUID,
582                          userGID,
583                          __DAFileSystemCallback,
584                          context,
585                          CFSTR( "-t" ),
586                          DAFileSystemGetKind( filesystem ),
587                          devicePath,
588                          mountpointPath,
589                          NULL );
590    }
591
592DAFileSystemMountErr:
593
594    if ( command        )  CFRelease( command        );
595    if ( devicePath     )  CFRelease( devicePath     );
596    if ( mountpointPath )  CFRelease( mountpointPath );
597    if ( options        )  CFRelease( options        );
598
599    if ( status )
600    {
601        if ( context )  free( context );
602
603        if ( callback )
604        {
605            ( callback )( status, callbackContext );
606        }
607    }
608}
609
610void DAFileSystemProbe( DAFileSystemRef           filesystem,
611                        CFURLRef                  device,
612                        DAFileSystemProbeCallback callback,
613                        void *                    callbackContext )
614{
615    /*
616     * Probe the specified volume.  A status of 0 indicates success.
617     */
618
619    __DAFileSystemProbeContext * context           = NULL;
620    CFStringRef                  deviceName        = NULL;
621    CFStringRef                  devicePath        = NULL;
622    CFDictionaryRef              mediaType         = NULL;
623    CFDictionaryRef              mediaTypes        = NULL;
624    CFDictionaryRef              personality       = NULL;
625    CFDictionaryRef              personalities     = NULL;
626    CFURLRef                     probeCommand      = NULL;
627    CFStringRef                  probeCommandName  = NULL;
628    CFURLRef                     repairCommand     = NULL;
629    CFStringRef                  repairCommandName = NULL;
630    int                          status            = 0;
631
632    /*
633     * Prepare to probe.
634     */
635
636    personalities = CFDictionaryGetValue( filesystem->_properties, CFSTR( kFSPersonalitiesKey ) );
637    if ( personalities == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
638
639    personality = ___CFDictionaryGetAnyValue( personalities );
640    if ( personality == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
641
642    mediaTypes = CFDictionaryGetValue( filesystem->_properties, CFSTR( kFSMediaTypesKey ) );
643    if ( mediaTypes == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
644
645    mediaType = ___CFDictionaryGetAnyValue( mediaTypes );
646    if ( mediaType == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
647
648    probeCommandName = CFDictionaryGetValue( mediaType, CFSTR( kFSProbeExecutableKey ) );
649    if ( probeCommandName == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
650
651    probeCommand = ___CFBundleCopyResourceURLInDirectory( filesystem->_id, probeCommandName );
652    if ( probeCommand == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
653
654    repairCommandName = CFDictionaryGetValue( personality, CFSTR( kFSRepairExecutableKey ) );
655
656    if ( repairCommandName )
657    {
658        repairCommand = ___CFBundleCopyResourceURLInDirectory( filesystem->_id, repairCommandName );
659        if ( repairCommand == NULL )  { status = ENOTSUP; goto DAFileSystemProbeErr; }
660    }
661
662    deviceName = CFURLCopyLastPathComponent( device );
663    if ( deviceName == NULL )  { status = EINVAL; goto DAFileSystemProbeErr; }
664
665    devicePath = ___CFURLCopyRawDeviceFileSystemPath( device, kCFURLPOSIXPathStyle );
666    if ( devicePath == NULL )  { status = EINVAL; goto DAFileSystemProbeErr; }
667
668    context = malloc( sizeof( __DAFileSystemProbeContext ) );
669    if ( context == NULL )  { status = ENOMEM; goto DAFileSystemProbeErr; }
670
671    /*
672     * Execute the probe command.
673     */
674
675    context->callback        = callback;
676    context->callbackContext = callbackContext;
677    context->deviceName      = deviceName;
678    context->devicePath      = devicePath;
679    context->probeCommand    = probeCommand;
680    context->repairCommand   = repairCommand;
681    context->volumeClean     = NULL;
682    context->volumeName      = NULL;
683    context->volumeUUID      = NULL;
684
685    DACommandExecute( probeCommand,
686                      kDACommandExecuteOptionCaptureOutput,
687                      ___UID_ROOT,
688                      ___GID_WHEEL,
689                      __DAFileSystemProbeCallbackStage1,
690                      context,
691                      CFSTR( "-p" ),
692                      deviceName,
693                      CFSTR( "removable" ),
694                      CFSTR( "readonly"  ),
695                      NULL );
696
697DAFileSystemProbeErr:
698
699    if ( status )
700    {
701        if ( deviceName    )  CFRelease( deviceName    );
702        if ( devicePath    )  CFRelease( devicePath    );
703        if ( probeCommand  )  CFRelease( probeCommand  );
704        if ( repairCommand )  CFRelease( repairCommand );
705
706        if ( context )  free( context );
707
708        if ( callback )
709        {
710            ( callback )( status, NULL, NULL, NULL, callbackContext );
711        }
712    }
713}
714
715void DAFileSystemRename( DAFileSystemRef      filesystem,
716                         CFURLRef             mountpoint,
717                         CFStringRef          name,
718                         DAFileSystemCallback callback,
719                         void *               callbackContext )
720{
721    /*
722     * Rename the specified volume.  A status of 0 indicates success.
723     */
724
725    struct attrlist              attributes     = { 0 };
726    __DAFileSystemRenameBuffer * buffer         = NULL;
727    char *                       mountpointPath = NULL;
728    int                          status         = 0;
729
730    /*
731     * Prepare to set the name.
732     */
733
734    attributes.bitmapcount = ATTR_BIT_MAP_COUNT;
735    attributes.commonattr  = 0;
736    attributes.dirattr     = 0;
737    attributes.fileattr    = 0;
738    attributes.forkattr    = 0;
739    attributes.volattr     = ATTR_VOL_INFO | ATTR_VOL_NAME;
740
741    buffer = malloc( sizeof( __DAFileSystemRenameBuffer ) );
742    if ( buffer == NULL )  { status = ENOMEM; goto DAFileSystemRenameErr; }
743
744    mountpointPath = ___CFURLCopyFileSystemRepresentation( mountpoint );
745    if ( mountpointPath == NULL )  { status = EINVAL; goto DAFileSystemRenameErr; }
746
747    /*
748     * Set the name.
749     */
750
751    status = CFStringGetCString( name, buffer->name, sizeof( buffer->name ), kCFStringEncodingUTF8 );
752    if ( status == FALSE )  { status = EINVAL; goto DAFileSystemRenameErr; }
753
754    buffer->data.attr_dataoffset = sizeof( buffer->data );
755    buffer->data.attr_length     = strlen( buffer->name ) + 1;
756
757    status = setattrlist( mountpointPath, &attributes, buffer, sizeof( __DAFileSystemRenameBuffer ), 0 );
758    if ( status == -1 )  { status = errno; goto DAFileSystemRenameErr; }
759
760DAFileSystemRenameErr:
761
762    if ( buffer         )  free( buffer );
763    if ( mountpointPath )  free( mountpointPath );
764
765    if ( callback )
766    {
767        ( callback )( status, callbackContext );
768    }
769}
770
771void DAFileSystemRepair( DAFileSystemRef      filesystem,
772                         CFURLRef             device,
773                         DAFileSystemCallback callback,
774                         void *               callbackContext )
775{
776    /*
777     * Repair the specified volume.  A status of 0 indicates success.
778     */
779
780    CFURLRef                command       = NULL;
781    CFStringRef             commandName   = NULL;
782    __DAFileSystemContext * context       = NULL;
783    CFStringRef             devicePath    = NULL;
784    CFDictionaryRef         personality   = NULL;
785    CFDictionaryRef         personalities = NULL;
786    int                     status        = 0;
787
788    /*
789     * Prepare to repair.
790     */
791
792    personalities = CFDictionaryGetValue( filesystem->_properties, CFSTR( kFSPersonalitiesKey ) );
793    if ( personalities == NULL )  { status = ENOTSUP; goto DAFileSystemRepairErr; }
794
795    personality = ___CFDictionaryGetAnyValue( personalities );
796    if ( personality == NULL )  { status = ENOTSUP; goto DAFileSystemRepairErr; }
797
798    commandName = CFDictionaryGetValue( personality, CFSTR( kFSRepairExecutableKey ) );
799    if ( commandName == NULL )  { status = ENOTSUP; goto DAFileSystemRepairErr; }
800
801    command = ___CFBundleCopyResourceURLInDirectory( filesystem->_id, commandName );
802    if ( command == NULL )  { status = ENOTSUP; goto DAFileSystemRepairErr; }
803
804    devicePath = ___CFURLCopyRawDeviceFileSystemPath( device, kCFURLPOSIXPathStyle );
805    if ( devicePath == NULL )  { status = EINVAL; goto DAFileSystemRepairErr; }
806
807    context = malloc( sizeof( __DAFileSystemContext ) );
808    if ( context == NULL )  { status = ENOMEM; goto DAFileSystemRepairErr; }
809
810    /*
811     * Execute the repair command.
812     */
813
814    context->callback        = callback;
815    context->callbackContext = callbackContext;
816
817    DACommandExecute( command,
818                      kDACommandExecuteOptionDefault,
819                      ___UID_ROOT,
820                      ___GID_WHEEL,
821                      __DAFileSystemCallback,
822                      context,
823                      CFSTR( "-y" ),
824                      devicePath,
825                      NULL );
826
827DAFileSystemRepairErr:
828
829    if ( command    )  CFRelease( command    );
830    if ( devicePath )  CFRelease( devicePath );
831
832    if ( status )
833    {
834        if ( context )  free( context );
835
836        if ( callback )
837        {
838            ( callback )( status, callbackContext );
839        }
840    }
841}
842
843void DAFileSystemRepairQuotas( DAFileSystemRef      filesystem,
844                               CFURLRef             mountpoint,
845                               DAFileSystemCallback callback,
846                               void *               callbackContext )
847{
848    /*
849     * Repair the quotas on specified volume.  A status of 0 indicates success.
850     */
851
852    CFURLRef                command        = NULL;
853    __DAFileSystemContext * context        = NULL;
854    CFStringRef             mountpointPath = NULL;
855    int                     status         = 0;
856
857    /*
858     * Prepare to repair quotas.
859     */
860
861    command = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, CFSTR( "/sbin/quotacheck" ), kCFURLPOSIXPathStyle, FALSE );
862    if ( command == NULL )  { status = ENOTSUP; goto DAFileSystemRepairQuotasErr; }
863
864    context = malloc( sizeof( __DAFileSystemContext ) );
865    if ( context == NULL )  { status = ENOMEM; goto DAFileSystemRepairQuotasErr; }
866
867    mountpointPath = CFURLCopyFileSystemPath( mountpoint, kCFURLPOSIXPathStyle );
868    if ( mountpointPath == NULL )  { status = EINVAL; goto DAFileSystemRepairQuotasErr; }
869
870    /*
871     * Execute the repair quotas command.
872     */
873
874    context->callback        = callback;
875    context->callbackContext = callbackContext;
876
877    DACommandExecute( command,
878                      kDACommandExecuteOptionDefault,
879                      ___UID_ROOT,
880                      ___GID_WHEEL,
881                      __DAFileSystemCallback,
882                      context,
883                      CFSTR( "-g" ),
884                      CFSTR( "-u" ),
885                      mountpointPath,
886                      NULL );
887
888DAFileSystemRepairQuotasErr:
889
890    if ( command        )  CFRelease( command        );
891    if ( mountpointPath )  CFRelease( mountpointPath );
892
893    if ( status )
894    {
895        if ( context )  free( context );
896
897        if ( callback )
898        {
899            ( callback )( status, callbackContext );
900        }
901    }
902}
903
904void DAFileSystemUnmount( DAFileSystemRef      filesystem,
905                          CFURLRef             mountpoint,
906                          DAFileSystemCallback callback,
907                          void *               callbackContext )
908{
909    DAFileSystemUnmountWithArguments( filesystem, mountpoint, callback, callbackContext, NULL );
910}
911
912void DAFileSystemUnmountWithArguments( DAFileSystemRef      filesystem,
913                                       CFURLRef             mountpoint,
914                                       DAFileSystemCallback callback,
915                                       void *               callbackContext,
916                                       ... )
917{
918    /*
919     * Unmount the specified volume.  A status of 0 indicates success.  All arguments in the
920     * argument list must be of type CFStringRef.  The argument list must be NULL terminated.
921     */
922
923    CFStringRef             argument       = NULL;
924    va_list                 arguments;
925    CFURLRef                command        = NULL;
926    __DAFileSystemContext * context        = NULL;
927    CFStringRef             mountpointPath = NULL;
928    int                     options        = 0;
929    int                     status         = 0;
930
931    /*
932     * Prepare to unmount the volume.
933     */
934
935    command = CFURLCreateWithFileSystemPath( kCFAllocatorDefault, CFSTR( "/sbin/umount" ), kCFURLPOSIXPathStyle, FALSE );
936    if ( command == NULL )  { status = ENOTSUP; goto DAFileSystemUnmountErr; }
937
938    context = malloc( sizeof( __DAFileSystemContext ) );
939    if ( context == NULL )  { status = ENOMEM; goto DAFileSystemUnmountErr; }
940
941    mountpointPath = CFURLCopyFileSystemPath( mountpoint, kCFURLPOSIXPathStyle );
942    if ( mountpointPath == NULL )  { status = EINVAL; goto DAFileSystemUnmountErr; }
943
944    /*
945     * Prepare the unmount options.
946     */
947
948    va_start( arguments, callbackContext );
949
950    while ( ( argument = va_arg( arguments, CFStringRef ) ) )
951    {
952        if ( CFEqual( argument, kDAFileSystemUnmountArgumentForce ) )
953        {
954            options |= MNT_FORCE;
955        }
956    }
957
958    va_end( arguments );
959
960    /*
961     * Execute the unmount command.
962     */
963
964    context->callback        = callback;
965    context->callbackContext = callbackContext;
966
967    if ( ( options & MNT_FORCE ) )
968    {
969        DACommandExecute( command,
970                          kDACommandExecuteOptionDefault,
971                          ___UID_ROOT,
972                          ___GID_WHEEL,
973                          __DAFileSystemCallback,
974                          context,
975                          CFSTR( "-f" ),
976                          mountpointPath,
977                          NULL );
978    }
979    else
980    {
981        DACommandExecute( command,
982                          kDACommandExecuteOptionDefault,
983                          ___UID_ROOT,
984                          ___GID_WHEEL,
985                          __DAFileSystemCallback,
986                          context,
987                          mountpointPath,
988                          NULL );
989    }
990
991DAFileSystemUnmountErr:
992
993    if ( command        )  CFRelease( command        );
994    if ( mountpointPath )  CFRelease( mountpointPath );
995
996    if ( status )
997    {
998        if ( context )  free( context );
999
1000        if ( callback )
1001        {
1002            ( callback )( status, callbackContext );
1003        }
1004    }
1005}
1006