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