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 <sys/types.h> 25#include <CoreFoundation/CoreFoundation.h> 26#include <DiskArbitration/DiskArbitrationPrivate.h> 27#include <IOKit/IOKitLib.h> 28 29#include <fcntl.h> 30#include <dirent.h> 31#include <fsproperties.h> 32#include <paths.h> 33#include <unistd.h> 34#include <sys/ioctl.h> 35#include <sys/loadable_fs.h> 36#include <sys/mount.h> 37#include <sys/param.h> 38#include <sys/stat.h> 39#include <sys/ttycom.h> 40#include <sys/wait.h> 41#include <IOKit/IOBSD.h> 42#include <IOKit/storage/IOMedia.h> 43 44#define dwarning(a) { if (g.debug) { printf a; fflush(stdout); } } 45#define pwarning(a) { printf a; fflush(stdout); } 46 47mach_port_t ioMasterPort; 48 49CFMutableDictionaryRef plistDict = nil; 50CFMutableArrayRef matchingArray = nil; 51 52struct Disk { 53 struct Disk * next; 54 char * ioBSDName; 55 unsigned flags; 56 io_object_t service; 57 UInt64 ioSize; 58}; 59 60typedef struct Disk Disk; 61typedef struct Disk * DiskPtr; 62 63typedef struct { 64 boolean_t verbose; 65 boolean_t debug; 66 DiskPtr Disks; 67 unsigned NumDisks; 68} GlobalStruct; 69 70GlobalStruct g; 71 72struct DiskVolume 73{ 74 char * fs_type; 75 char * disk_dev_name; 76 char * disk_name; 77 boolean_t removable; 78 boolean_t writable; 79 boolean_t internal; 80 boolean_t dirty; 81 boolean_t mounted; 82 UInt64 size; 83}; 84 85typedef struct DiskVolume DiskVolume, *DiskVolumePtr; 86 87struct DiskVolumes 88{ 89 CFMutableArrayRef list; 90}; 91typedef struct DiskVolumes DiskVolumes, *DiskVolumesPtr; 92 93 94char * 95daCreateCStringFromCFString(CFStringRef string) 96{ 97 /* 98 * result of daCreateCStringFromCFString should be released with free() 99 */ 100 101 CFStringEncoding encoding = kCFStringEncodingMacRoman; 102 CFIndex bufferLength = CFStringGetLength(string) + 1; 103 char *buffer = malloc(bufferLength); 104 105 if (buffer) { 106 if (CFStringGetCString(string, buffer, bufferLength, encoding) == FALSE) { 107 free(buffer); 108 buffer = malloc(1); 109 //See Radar 2457357. 110 buffer[0] = '\0'; 111 //See Radar 2457357. 112 } 113 } 114 return buffer; 115} /* daCreateCStringFromCFString */ 116 117char * 118fsDirForFS(char *fsname) 119{ 120 char *fsDir = malloc(MAXPATHLEN); 121 sprintf(fsDir, "%s/%s%s", FS_DIR_LOCATION, fsname, FS_DIR_SUFFIX); 122 return fsDir; 123 124} 125 126int 127suffixfs(const struct dirent * dp) 128{ 129 char *s; 130 if ((s = strstr(&dp->d_name[0], FS_DIR_SUFFIX))) 131 if (strlen(s) == strlen(FS_DIR_SUFFIX)) 132 return (1); 133 return (0); 134} 135 136void 137cacheFileSystemDictionaries() 138{ 139 140 if (!plistDict) { 141 142 struct dirent **fsdirs = NULL; 143 int nfs = 0; /* # filesystems defined in 144 * /usr/filesystems */ 145 int n; 146 147 plistDict = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 148 149 /* discover known filesystem types */ 150 nfs = scandir(FS_DIR_LOCATION, &fsdirs, suffixfs, NULL); 151 152 dwarning(("%d filesystems known:\n", nfs)); 153 for (n = 0; n < nfs; n++) { 154 char buf[MAXPATHLEN]; 155 CFDictionaryRef fsdict; 156 CFStringRef str; 157 CFURLRef bar; 158 CFStringRef zaz; 159 160 dwarning(("%s\n", &fsdirs[n]->d_name[0])); 161 sprintf(buf, "%s/%s", FS_DIR_LOCATION, &fsdirs[n]->d_name[0]); 162 //get their dictionaries, test that they are okay and add them into the plistDict 163 164 str = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); 165 bar = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, 1); 166 167 fsdict = CFBundleCopyInfoDictionaryInDirectory(bar); 168 169 zaz = CFStringCreateWithCString(NULL, &fsdirs[n]->d_name[0], kCFStringEncodingUTF8); 170 171 CFDictionaryAddValue(plistDict, zaz, fsdict); 172 173 CFRelease(zaz); 174 CFRelease(bar); 175 CFRelease(str); 176 CFRelease(fsdict); 177 178 } 179 if (fsdirs) { 180 for (n = 0; n < nfs; n++) { 181 free((void *) fsdirs[n]); 182 } 183 free((void *) fsdirs); 184 } 185 } 186} 187 188CFComparisonResult 189compareDicts(const void *val1, const void *val2, void *context) 190{ 191 int val1ProbeOrder; 192 int val2ProbeOrder; 193 194 CFNumberRef val1Number = CFDictionaryGetValue(val1, CFSTR(kFSProbeOrderKey)); 195 CFNumberRef val2Number = CFDictionaryGetValue(val2, CFSTR(kFSProbeOrderKey)); 196 197 CFNumberGetValue(val1Number, kCFNumberIntType, &val1ProbeOrder); 198 CFNumberGetValue(val2Number, kCFNumberIntType, &val2ProbeOrder); 199 200 //printf("%d, %d\n", val1ProbeOrder, val2ProbeOrder); 201 202 if (val1ProbeOrder > val2ProbeOrder) { 203 return kCFCompareGreaterThan; 204 } else if (val1ProbeOrder < val2ProbeOrder) { 205 return kCFCompareLessThan; 206 } 207 return kCFCompareEqualTo; 208} 209 210void 211cacheFileSystemMatchingArray() 212{ 213 if (!matchingArray) { 214 215 struct dirent **fsdirs = NULL; 216 int nfs = 0; /* # filesystems defined in 217 * /usr/filesystems */ 218 int n; 219 int i = 0; 220 221 matchingArray = CFArrayCreateMutable(NULL, 0, NULL); 222 223 /* discover known filesystem types */ 224 nfs = scandir(FS_DIR_LOCATION, &fsdirs, suffixfs, NULL); 225 226 for (n = 0; n < nfs; n++) { 227 char buf[MAXPATHLEN]; 228 CFDictionaryRef fsdict; 229 CFDictionaryRef mediaTypeDict; 230 CFStringRef str; 231 CFURLRef bar; 232 233 sprintf(buf, "%s/%s", FS_DIR_LOCATION, &fsdirs[n]->d_name[0]); 234 //get their dictionaries, test that they are okay and add them into the plistDict 235 236 str = CFStringCreateWithCString(NULL, buf, kCFStringEncodingUTF8); 237 bar = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, 1); 238 239 fsdict = CFBundleCopyInfoDictionaryInDirectory(bar); 240 241 mediaTypeDict = CFDictionaryGetValue(fsdict, CFSTR(kFSMediaTypesKey)); 242 243 244 if (mediaTypeDict != NULL) { 245 int j = CFDictionaryGetCount(mediaTypeDict); 246 CFDictionaryRef *dicts = (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef) * j); 247 CFDictionaryGetKeysAndValues(mediaTypeDict, NULL, (const void **) dicts); 248 249 for (i = 0; i < j; i++) { 250 CFStringRef zaz; 251 252 CFMutableDictionaryRef newDict = CFDictionaryCreateMutableCopy(NULL, 0, dicts[i]); 253 zaz = CFStringCreateWithCString(NULL, &fsdirs[n]->d_name[0], kCFStringEncodingUTF8); 254 CFDictionaryAddValue(newDict, CFSTR("FSName"), zaz); 255 CFArrayAppendValue(matchingArray, newDict); 256 CFRelease(zaz); 257 } 258 free(dicts); 259 260 } 261 CFRelease(fsdict); 262 CFRelease(str); 263 CFRelease(bar); 264 265 } 266 if (fsdirs) { 267 for (n = 0; n < nfs; n++) { 268 free((void *) fsdirs[n]); 269 } 270 free((void *) fsdirs); 271 } 272 CFArraySortValues(matchingArray, CFRangeMake(0, CFArrayGetCount(matchingArray)), compareDicts, NULL); 273 } 274} 275 276char * 277resourcePathForFSName(char *fs) 278{ 279 char bundlePath[MAXPATHLEN]; 280 CFBundleRef bundle; 281 CFURLRef bundleUrl; 282 CFURLRef resourceUrl; 283 CFStringRef resourceString; 284 char *path; 285 char *resourcePath = malloc(MAXPATHLEN); 286 CFStringRef str; 287 288 sprintf(bundlePath, "%s/%s", FS_DIR_LOCATION, fs); 289 290 str = CFStringCreateWithCString(NULL, bundlePath, kCFStringEncodingMacRoman); 291 292 bundleUrl = CFURLCreateWithFileSystemPath(NULL, str, kCFURLPOSIXPathStyle, 1); 293 CFRelease(str); 294 bundle = CFBundleCreate(NULL, bundleUrl); 295 resourceUrl = CFBundleCopyResourcesDirectoryURL(bundle); 296 resourceString = CFURLCopyPath(resourceUrl); 297 298 path = daCreateCStringFromCFString(resourceString); 299 300 sprintf(resourcePath, "%s/%s", bundlePath, path); 301 302 CFRelease(bundleUrl); 303 CFRelease(bundle); 304 CFRelease(resourceUrl); 305 CFRelease(resourceString); 306 free(path); 307 308 return resourcePath; 309} 310 311char * 312repairPathForFileSystem(char *fsname) 313{ 314 CFDictionaryRef fsDict; 315 CFDictionaryRef personalities; 316 CFDictionaryRef personality; 317 CFStringRef fsckPath1; 318 char fs[128]; 319 char *fsckPath; 320 char *finalPath = malloc(MAXPATHLEN); 321 CFStringRef str; 322 323 if (strlen(fsname) == 0) { 324 return finalPath; 325 } 326 sprintf(fs, "%s%s", fsname, FS_DIR_SUFFIX); 327 str = CFStringCreateWithCString(NULL, fs, kCFStringEncodingMacRoman); 328 fsDict = (CFDictionaryRef) CFDictionaryGetValue(plistDict, str); 329 CFRelease(str); 330 331 if (!fsDict) { 332 return finalPath; 333 } 334 personalities = (CFDictionaryRef) CFDictionaryGetValue(fsDict, CFSTR(kFSPersonalitiesKey)); 335 336 { 337 int persCount = CFDictionaryGetCount(personalities); 338 CFDictionaryRef *dicts = (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef) * persCount); 339 CFDictionaryGetKeysAndValues(personalities, NULL, (const void **) dicts); 340 personality = (CFDictionaryRef) dicts[0]; 341 free(dicts); 342 //(CFDictionaryRef) CFArrayGetValueAtIndex(personalities, 0); 343 344 } 345 346 fsckPath1 = (CFStringRef) CFDictionaryGetValue(personality, CFSTR(kFSRepairExecutableKey)); 347 348 if (fsckPath1) { 349 char *resourcePath = resourcePathForFSName(fs); 350 fsckPath = daCreateCStringFromCFString(fsckPath1); 351 352 sprintf(finalPath, "%s%s", resourcePath, fsckPath); 353 354 free(resourcePath); 355 free(fsckPath); 356 } 357 return finalPath; 358 359} 360 361char * 362repairArgsForFileSystem(char *fsname) 363{ 364 CFDictionaryRef fsDict; 365 CFDictionaryRef personalities; 366 CFDictionaryRef personality; 367 CFStringRef repairArgs1; 368 char fs[128]; 369 char *repairArgs; 370 CFStringRef str; 371 372 if (strlen(fsname) == 0) { 373 repairArgs = malloc(MAXPATHLEN); 374 return repairArgs; 375 } 376 sprintf(fs, "%s%s", fsname, FS_DIR_SUFFIX); 377 str = CFStringCreateWithCString(NULL, fs, kCFStringEncodingMacRoman); 378 fsDict = (CFDictionaryRef) CFDictionaryGetValue(plistDict, str); 379 CFRelease(str); 380 381 if (!fsDict) { 382 repairArgs = malloc(MAXPATHLEN); 383 return repairArgs; 384 } 385 personalities = (CFDictionaryRef) CFDictionaryGetValue(fsDict, CFSTR(kFSPersonalitiesKey)); 386 387 { 388 int persCount = CFDictionaryGetCount(personalities); 389 CFDictionaryRef *dicts = (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef) * persCount); 390 CFDictionaryGetKeysAndValues(personalities, NULL, (const void **) dicts); 391 personality = (CFDictionaryRef) dicts[0]; 392 free(dicts); 393 //(CFDictionaryRef) CFArrayGetValueAtIndex(personalities, 0); 394 395 } 396 397 repairArgs1 = (CFStringRef) CFDictionaryGetValue(personality, CFSTR(kFSRepairArgumentsKey)); 398 399 if (repairArgs1) { 400 repairArgs = daCreateCStringFromCFString(repairArgs1); 401 } else { 402 repairArgs = malloc(MAXPATHLEN); 403 } 404 405 406 return repairArgs; 407 408} 409 410#define PIPEFULL (4 * 1024) 411static char * 412read_output(int fd) 413{ 414 char * buf = NULL; 415 ssize_t count; 416 ssize_t where = 0; 417 418 buf = malloc(PIPEFULL); 419 if (buf == NULL) { 420 return (NULL); 421 } 422 423 /* this handles up to PIPEFULL - 1 bytes */ 424 while (where < (PIPEFULL - 1) 425 && (count = read(fd, buf + where, PIPEFULL - 1 - where))) { 426 if (count == -1) { 427 free(buf); 428 return (NULL); 429 } 430 where += count; 431 } 432 buf[where] = '\0'; 433 return (buf); 434} 435 436void 437cleanUpAfterFork(int fdp[]) 438{ 439 int fd, maxfd = getdtablesize(); 440 441 /* Close all inherited file descriptors */ 442 443 for (fd = 0; fd < maxfd; fd++) 444 { 445 if (fdp == NULL || (fdp[0] != fd && fdp[1] != fd)) { 446 close(fd); 447 } 448 } 449 450 /* Disassociate ourselves from any controlling tty */ 451 452 if ((fd = open("/dev/tty", O_NDELAY)) >= 0) 453 { 454 ioctl(fd, TIOCNOTTY, 0); 455 close(fd); 456 } 457 458 /* Reset the user and group id's to their real values */ 459 460 setgid(getgid()); 461 setuid(getuid()); 462 463 (void)setsid(); 464 465 (void)chdir("/"); 466 467 if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { 468 /* point stdin -> /dev/null */ 469 (void)dup2(fd, STDIN_FILENO); 470 if (fdp != NULL) { 471 /* point stdout to one end of pipe fdp[1] */ 472 (void)dup2(fdp[1], STDOUT_FILENO); 473 474 /* close the other end */ 475 close(fdp[0]); 476 } 477 else { 478 (void)dup2(fd, STDOUT_FILENO); 479 } 480 (void)dup2(fd, STDERR_FILENO); 481 if (fd > 2) 482 (void)close (fd); 483 } 484 return; 485} 486 487boolean_t 488do_exec(const char * dir, const char * argv[], int * result, 489 char * * output) 490{ 491 int fdp[2]; 492 boolean_t got_result = FALSE; 493 int pid; 494 495 if (g.debug) { 496 const char * * scan; 497 printf("do_exec("); 498 for (scan = argv; *scan; scan++) { 499 printf("%s%s", (scan != argv) ? " " : "", *scan); 500 } 501 printf(")\n"); 502 } 503 if (output != NULL) { 504 *output = NULL; 505 if (pipe(fdp) == -1) { 506 pwarning(("do_exec(): pipe() failed, %s", 507 strerror(errno))); 508 return (FALSE); 509 } 510 } 511 if (access(argv[0], F_OK) == 0) { 512 pid = fork(); 513 if (pid == 0) { 514 /* CHILD PROCESS */ 515 if (output == NULL) 516 cleanUpAfterFork(NULL); 517 else 518 cleanUpAfterFork(fdp); 519 520 if (dir) { 521 chdir(dir); 522 } 523 execve(argv[0], (char * const *)argv, 0); 524 exit(-127); 525 } 526 else if (pid > 0) { /* PARENT PROCESS */ 527 int statusp; 528 int waitResult; 529 530 if (output != NULL) { 531 close(fdp[1]); 532 *output = read_output(fdp[0]); 533 close(fdp[0]); 534 } 535 dwarning(("wait4(pid=%d,&statusp,0,NULL)...\n", pid)); 536 waitResult = wait4(pid,&statusp,0,NULL); 537 dwarning(("wait4(pid=%d,&statusp,0,NULL) => %d\n", 538 pid, waitResult)); 539 if (waitResult > 0 540 && WIFEXITED(statusp)) { 541 got_result = TRUE; 542 *result = (int)(char)(WEXITSTATUS(statusp)); 543 } 544 } 545 else { 546 pwarning(("do_exec: fork() failed, %s", 547 strerror(errno))); 548 } 549 } 550 return (got_result); 551} 552 553DiskPtr NewDisk( char * ioBSDName, 554 io_object_t service, 555 unsigned flags, 556 UInt64 ioSize) 557{ 558 DiskPtr result; 559 560 dwarning(("%s(ioBSDName = '%s', flags = $%08x)\n", 561 __FUNCTION__, 562 ioBSDName, 563 flags )); 564 565 /* Allocate memory */ 566 567 result = (DiskPtr) malloc( sizeof( * result ) ); 568 if ( result == NULL ) 569 { 570 dwarning(("%s(...): malloc failed!\n", __FUNCTION__)); 571 /* result = NULL; */ 572 goto Return; 573 } 574 575 bzero( result, sizeof( * result ) ); 576 577 /* Link it onto the front of the list */ 578 579 result->next = g.Disks; 580 g.Disks = result; 581 582 /* Fill in the fields */ 583 584 result->ioBSDName = strdup( ioBSDName ? ioBSDName : "" ); 585 result->service = service; 586 result->ioSize = ioSize; 587 588 result->flags = flags; 589 590 /* Increment count */ 591 592 g.NumDisks ++ ; 593 594 /* Retain service */ 595 596 if ( service ) 597 { 598 IOObjectRetain( service ); 599 } 600 601Return: 602 return result; 603 604} // NewDisk 605 606static struct statfs * 607get_fsstat_list(int * number) 608{ 609 int n; 610 struct statfs * stat_p; 611 612 n = getfsstat(NULL, 0, MNT_NOWAIT); 613 if (n <= 0) 614 { 615 return (NULL); 616 } 617 618 stat_p = (struct statfs *)malloc(n * sizeof(*stat_p)); 619 if (stat_p == NULL) 620 { 621 return (NULL); 622 } 623 624 if (getfsstat(stat_p, n * sizeof(*stat_p), MNT_NOWAIT) <= 0) 625 { 626 free(stat_p); 627 return (NULL); 628 } 629 *number = n; 630 631 return (stat_p); 632} 633 634static struct statfs * 635fsstat_lookup_spec(struct statfs * list_p, int n, dev_t dev, char * fstype) 636{ 637 int i; 638 struct statfs * scan; 639 640 for (i = 0, scan = list_p; i < n; i++, scan++) 641 { 642 if (strcmp(scan->f_fstypename, fstype) == 0 643 && scan->f_fsid.val[0] == dev) { 644 return (scan); 645 } 646 } 647 return (NULL); 648} 649 650boolean_t 651fsck_needed(char * devname, char * fstype) 652{ 653 const char * argv[] = { 654 NULL, 655 "-q", 656 NULL, 657 NULL, 658 }; 659 char devpath[64]; 660 int result; 661 char *fsckCmd = repairPathForFileSystem(fstype); 662 663 snprintf(devpath, sizeof(devpath), "/dev/r%s", devname); 664 argv[0] = fsckCmd; 665 argv[2]= devpath; 666 if (do_exec(NULL, argv, &result, NULL) == FALSE) { 667 result = -1; 668 } 669 dwarning(("%s('%s'): '%s' => %d\n", __FUNCTION__, devname, fsckCmd, 670 result)); 671 free(fsckCmd); 672 673 if (result <= 0) { 674 return (FALSE); 675 } 676 return (TRUE); 677} 678 679/* foreignProbe: run the -p(robe) option of the given <fsName>.util program in a child process */ 680/* returns the volume name in volname_p */ 681 682static int 683foreignProbe(const char *fsName, const char *execPath, const char *probeCmd, const char *devName, int removable, int writable, char * * volname_p) 684{ 685 int result; 686 const char *childArgv[] = { execPath, 687 probeCmd, 688 devName, 689 removable ? DEVICE_REMOVABLE : DEVICE_FIXED, 690 writable? DEVICE_WRITABLE : DEVICE_READONLY, 691 0 }; 692 char *fsDir = fsDirForFS((char *)fsName); 693 694 dwarning(("%s('%s', '%s', removable=%d, writable=%d):\n'%s %s %s %s %s'\n", 695 __FUNCTION__, fsName, devName, removable, writable, execPath, childArgv[1], childArgv[2], childArgv[3], childArgv[4])); 696 697 698 if (do_exec(fsDir, childArgv, &result, volname_p) == FALSE) { 699 result = FSUR_IO_FAIL; 700 } 701 dwarning(("%s(...) => %d\n", __FUNCTION__, result)); 702 free(fsDir); 703 return result; 704} 705 706void setVar(char **var,char *val) 707{ 708 if (*var) 709 { 710 free(*var); 711 } 712 if (val == NULL) 713 { 714 *var = NULL; 715 } 716 else 717 { 718 *var = strdup(val); 719 } 720 721} 722void DiskVolume_setFSType(DiskVolumePtr diskVolume,char *t) 723{ 724 setVar(&(diskVolume->fs_type),t); 725} 726void DiskVolume_setDiskName(DiskVolumePtr diskVolume,char *t) 727{ 728 setVar(&(diskVolume->disk_name),t); 729} 730void DiskVolume_setDiskDevName(DiskVolumePtr diskVolume,char *t) 731{ 732 setVar(&(diskVolume->disk_dev_name),t); 733} 734void DiskVolume_setMounted(DiskVolumePtr diskVolume,boolean_t val) 735{ 736 diskVolume->mounted = val; 737} 738 739void DiskVolume_setWritable(DiskVolumePtr diskVolume,boolean_t val) 740{ 741 diskVolume->writable = val; 742} 743void DiskVolume_setRemovable(DiskVolumePtr diskVolume,boolean_t val) 744{ 745 diskVolume->removable = val; 746} 747void DiskVolume_setInternal(DiskVolumePtr diskVolume,boolean_t val) 748{ 749 diskVolume->internal = val; 750} 751void DiskVolume_setDirtyFS(DiskVolumePtr diskVolume,boolean_t val) 752{ 753 diskVolume->dirty = val; 754} 755void DiskVolume_new(DiskVolumePtr *diskVolume) 756{ 757 *diskVolume = malloc(sizeof(DiskVolume)); 758 (*diskVolume)->fs_type = nil; 759 (*diskVolume)->disk_dev_name = nil; 760 (*diskVolume)->disk_name = nil; 761} 762void DiskVolume_delete(DiskVolumePtr diskVolume) 763{ 764 int i; 765 char * * l[] = { &(diskVolume->fs_type), 766 &(diskVolume->disk_dev_name), 767 &(diskVolume->disk_name), 768 NULL }; 769 770 771 if(!diskVolume) 772 return; 773 774 for (i = 0; l[i] != NULL; i++) 775 { 776 if (*(l[i])) 777 { 778 free(*(l[i])); 779 } 780 *(l[i]) = NULL; 781 } 782 783 free(diskVolume); 784} 785 786static dev_t 787dev_from_spec(const char * specName) 788{ 789 struct stat sb; 790 791 if (stat(specName, &sb)) { 792 return (-1); 793 } 794 if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { 795 return (sb.st_rdev); 796 } 797 return (-1); 798} 799 800#define MAXNAMELEN 256 801DiskVolumePtr 802DiskVolumes_newVolume(DiskVolumesPtr diskList, DiskPtr media, boolean_t isRemovable, 803 boolean_t isWritable, boolean_t isInternal, 804 struct statfs * stat_p, int stat_number, UInt64 ioSize) 805{ 806 char * devname = media->ioBSDName; 807 struct statfs * fs_p; 808 dev_t fs_dev; 809 char * fsname = NULL; 810 int ret; 811 char specName[MAXNAMELEN]; 812 DiskVolumePtr volume = 0x0; 813 int matchingPointer = 0; 814 int count = CFArrayGetCount(matchingArray); 815 816 for (matchingPointer = 0;matchingPointer < count;matchingPointer++) { 817 818 // see if the diskPtr->service matches any of the filesystem types 819 // if it does test that first 820 // otherwise, start at the top of the list and test them alls 821 int matches; 822 CFDictionaryRef dictPointer = CFArrayGetValueAtIndex(matchingArray, matchingPointer); 823 CFDictionaryRef mediaProps = CFDictionaryGetValue(dictPointer, CFSTR(kFSMediaPropertiesKey)); 824 kern_return_t error; 825 826 error = IOServiceMatchPropertyTable(media->service, mediaProps, &matches); 827 828 if (error) { 829 dwarning(("some kind of error while matching service to array... %d\n", error)); 830 } 831 832 if (matches) { 833 CFStringRef utilArgsFromDict; 834 CFStringRef fsNameFromDict; 835 CFArrayRef fsNameArray; 836 CFStringRef utilPathFromDict; 837 838 char *utilPathFromDict2; 839 char *utilArgsFromDict2; 840 char *fsNameFromDict2; 841 char *fstype; 842 char *resourcePath; 843 844 char utilPath[MAXPATHLEN]; 845 846 dwarning(("********We have a match for devname = %s!!!**********\n", devname)); 847 848 utilArgsFromDict = CFDictionaryGetValue(dictPointer, CFSTR(kFSProbeArgumentsKey)); 849 fsNameFromDict = CFDictionaryGetValue(dictPointer, CFSTR("FSName")); 850 fsNameArray = CFStringCreateArrayBySeparatingStrings(NULL, fsNameFromDict, CFSTR(".")); 851 utilPathFromDict = CFDictionaryGetValue(dictPointer, CFSTR(kFSProbeExecutableKey)); 852 853 utilPathFromDict2 = daCreateCStringFromCFString(utilPathFromDict); 854 utilArgsFromDict2 = daCreateCStringFromCFString(utilArgsFromDict); 855 fsNameFromDict2 = daCreateCStringFromCFString(fsNameFromDict); 856 fstype = daCreateCStringFromCFString(CFArrayGetValueAtIndex(fsNameArray, 0)); 857 resourcePath = resourcePathForFSName(fsNameFromDict2); 858 859 sprintf(utilPath, "%s%s", resourcePath, utilPathFromDict2); 860 861 // clean up 862 CFRelease(fsNameArray); 863 free(utilPathFromDict2); 864 free(fsNameFromDict2); 865 free(resourcePath); 866 867 ret = foreignProbe(fstype, utilPath, utilArgsFromDict2, devname, isRemovable, isWritable, &fsname); 868 869 free(utilArgsFromDict2); 870 871 if (ret == FSUR_RECOGNIZED || ret == -9) 872 { 873 if (fsname == NULL) { 874 fsname = strdup(fstype); 875 } 876 877 DiskVolume_new(&volume); 878 DiskVolume_setDiskDevName(volume,devname); 879 DiskVolume_setFSType(volume,fstype); 880 DiskVolume_setDiskName(volume,fsname); 881 DiskVolume_setWritable(volume,isWritable); 882 DiskVolume_setRemovable(volume,isRemovable); 883 DiskVolume_setInternal(volume,isInternal); 884 DiskVolume_setMounted(volume,FALSE); 885 DiskVolume_setDirtyFS(volume,FALSE); 886 volume->size = ioSize; 887 888 sprintf(specName,"/dev/%s",devname); 889 fs_dev = dev_from_spec(specName); 890 891 fs_p = fsstat_lookup_spec(stat_p, stat_number, fs_dev, fstype); 892 if (fs_p) 893 { 894 /* already mounted */ 895 DiskVolume_setMounted(volume,TRUE); 896 } 897 else if (isWritable) 898 { 899 DiskVolume_setDirtyFS(volume,fsck_needed(devname,fstype)); 900 } 901 free(fstype); 902 if (fsname) 903 free(fsname); 904 fsname = NULL; 905 break; 906 } else { 907 free(fstype); 908 if (fsname) 909 free(fsname); 910 fsname = NULL; 911 dwarning(("Volume is bad\n")); 912 volume = 0x0; 913 } 914 915 } 916 } 917 918 return volume; 919} 920void DiskVolumes_new(DiskVolumesPtr *diskList) 921{ 922 *diskList = malloc(sizeof(DiskVolumes)); 923 (*diskList)->list = CFArrayCreateMutable(NULL,0,NULL); 924} 925void DiskVolumes_delete(DiskVolumesPtr diskList) 926{ 927 int i; 928 int count = CFArrayGetCount(diskList->list); 929 if(!diskList) 930 return; 931 932 for (i = 0; i < count; i++) 933 { 934 DiskVolume_delete((DiskVolumePtr)CFArrayGetValueAtIndex(diskList->list,i)); 935 } 936 937 CFArrayRemoveAllValues(diskList->list); 938 939 CFRelease(diskList->list); 940 941 free(diskList); 942} 943 944DiskVolumesPtr DiskVolumes_do_volumes(DiskVolumesPtr diskList) 945{ 946 DiskPtr diskPtr; 947 boolean_t success = FALSE; 948 struct statfs * stat_p; 949 int stat_number; 950 int nfs = 0; /* # filesystems defined in /usr/filesystems */ 951 struct dirent **fsdirs = NULL; 952 int n; /* iterator for nfs/fsdirs */ 953 954 stat_p = get_fsstat_list(&stat_number); 955 if (stat_p == NULL || stat_number == 0) 956 { 957 goto Return; 958 } 959 960 /* discover known filesystem types */ 961 nfs = scandir(FS_DIR_LOCATION, &fsdirs, suffixfs, NULL); 962 /* 963 * suffixfs ensured we have only names ending in ".fs" 964 * now we convert the periods to nulls to give us 965 * filesystem type strings. 966 */ 967 for (n = 0; n < nfs; n++) 968 { 969 *strrchr(&fsdirs[n]->d_name[0], '.') = '\0'; 970 } 971 if ( g.debug ) { 972 dwarning(("%d filesystems known:\n", nfs)); 973 for (n=0; n<nfs; n++) 974 { 975 dwarning(("%s\n", &fsdirs[n]->d_name[0])); 976 } 977 } 978 979 for (diskPtr = g.Disks; diskPtr != NULL; diskPtr = diskPtr->next ) 980 { 981 int isWritable, isRemovable, isInternal; 982 DiskVolumePtr volume = 0x0; 983 984 /* Initialize some convenient flags */ 985 986 isWritable = ( diskPtr->flags & kDiskArbDiskAppearedLockedMask ) == 0; 987 isRemovable = ( diskPtr->flags & kDiskArbDiskAppearedEjectableMask ) != 0; 988 isInternal = ( diskPtr->flags & kDiskArbDiskAppearedInternal ) != 0; 989 990 if ((diskPtr->flags & kDiskArbDiskAppearedNoSizeMask) != 0) { 991 continue; // if it's zero length, skip it 992 }; 993 994 volume = DiskVolumes_newVolume(diskList, 995 diskPtr, 996 isRemovable, 997 isWritable, 998 isInternal, 999 stat_p, 1000 stat_number, 1001 diskPtr->ioSize); 1002 1003 if (volume != nil) { 1004 CFArrayAppendValue(diskList->list,volume); 1005 } 1006 1007 } /* for */ 1008 1009 success = TRUE; 1010 1011Return: 1012 if (fsdirs) { 1013 for (n = 0; n < nfs; n++) { 1014 free((void *)fsdirs[n]); 1015 } 1016 free((void *)fsdirs); 1017 } 1018 if (stat_p) 1019 { 1020 free(stat_p); 1021 } 1022 1023 if (success) 1024 { 1025 return diskList; 1026 } 1027 1028 DiskVolumes_delete(diskList); 1029 return nil; 1030 1031} 1032 1033boolean_t 1034DiskVolumes_findDisk(DiskVolumesPtr diskList, boolean_t all, 1035 const char * volume_name) 1036{ 1037 DiskVolumePtr best = NULL; 1038 boolean_t found = FALSE; 1039 boolean_t best_is_internal = FALSE; 1040 int i; 1041 int count = CFArrayGetCount(diskList->list); 1042 1043 for (i = 0; i < count; i++) { 1044 DiskVolumePtr vol; 1045 1046 vol = (DiskVolumePtr)CFArrayGetValueAtIndex(diskList->list,i); 1047 if (vol->removable 1048 || vol->writable == FALSE 1049 || vol->mounted == TRUE 1050 || vol->dirty == TRUE 1051 || vol->fs_type == NULL 1052 || !(strcmp(vol->fs_type, "hfs") == 0 1053 || strcmp(vol->fs_type, "ufs") == 0)) { 1054 continue; 1055 } 1056 if (volume_name != NULL 1057 && strcmp(volume_name, vol->disk_name)) { 1058 continue; 1059 } 1060 found = TRUE; 1061 if (all == TRUE) { 1062 printf("%s %s\n", vol->disk_dev_name, vol->fs_type); 1063 } 1064 else if (best_is_internal && vol->internal == FALSE) { 1065 continue; 1066 } 1067 else { 1068 if (best == NULL || vol->size > best->size) { 1069 best_is_internal = vol->internal; 1070 best = vol; 1071 } 1072 } 1073 } 1074 if (best) { 1075 printf("%s %s\n", best->disk_dev_name, best->fs_type); 1076 } 1077 return (found); 1078} 1079 1080int DiskVolumes_count(DiskVolumesPtr diskList) 1081{ 1082 return CFArrayGetCount(diskList->list); 1083} 1084DiskVolumePtr DiskVolumes_objectAtIndex(DiskVolumesPtr diskList,int index) 1085{ 1086 return (DiskVolumePtr)CFArrayGetValueAtIndex(diskList->list,index); 1087} 1088 1089int diskIsInternal(io_registry_entry_t media) 1090{ 1091 io_registry_entry_t parent = 0; 1092 //(needs release 1093 io_registry_entry_t parentsParent = 0; 1094 //(needs release) 1095 io_registry_entry_t service = media; 1096 //mandatory initialization 1097 kern_return_t kr; 1098 1099 int isInternal = 0; 1100 //by default inited 1101 1102 kr = IORegistryEntryGetParentEntry(service, kIOServicePlane, &parent); 1103 if (kr != KERN_SUCCESS) 1104 return 1; 1105 1106 while (parent) { 1107 1108 kr = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parentsParent); 1109 if (kr != KERN_SUCCESS) 1110 break; 1111 1112 if (IOObjectConformsTo(parent, "IOBlockStorageDevice")) 1113 { 1114 CFDictionaryRef characteristics = IORegistryEntryCreateCFProperty(parent, CFSTR("Protocol Characteristics"), kCFAllocatorDefault, kNilOptions); 1115 1116 if (characteristics) { 1117 CFStringRef connection; 1118 // CFShow(characteristics); 1119 connection = (CFStringRef) CFDictionaryGetValue(characteristics, CFSTR("Physical Interconnect Location")); 1120 if (connection) { 1121 CFComparisonResult result; 1122 assert(CFGetTypeID(connection) == CFStringGetTypeID()); 1123 1124 result = CFStringCompare(connection, CFSTR("Internal"), 0); 1125 if (result == kCFCompareEqualTo) { 1126 isInternal = 1; 1127 } 1128 } 1129 1130 CFRelease(characteristics); 1131 } 1132 break; 1133 } 1134 if (parent) 1135 IOObjectRelease(parent); 1136 parent = parentsParent; 1137 parentsParent = 0; 1138 1139 } 1140 1141 if (parent) 1142 IOObjectRelease(parent); 1143 if (parentsParent) 1144 IOObjectRelease(parentsParent); 1145 1146 return isInternal; 1147} 1148 1149void 1150GetDisksFromRegistry(io_iterator_t iter, int initialRun, int mountExisting) 1151{ 1152 kern_return_t kr; 1153 io_registry_entry_t entry; 1154 1155 io_name_t ioMediaName; 1156 UInt64 ioSize; 1157 int ioWritable, ioEjectable; 1158 unsigned flags; 1159 mach_port_t masterPort; 1160 mach_timespec_t timeSpec; 1161 1162 1163 timeSpec.tv_sec = (initialRun ? 10 : 1); 1164 timeSpec.tv_nsec = 0; 1165 1166 IOMasterPort(bootstrap_port, &masterPort); 1167 1168 //sleep(1); 1169 IOKitWaitQuiet(masterPort, &timeSpec); 1170 1171 while ((entry = IOIteratorNext(iter))) { 1172 char *ioBSDName = NULL; 1173 //(needs release) 1174 CFBooleanRef boolean = 0; 1175 //(don 't release) 1176 CFNumberRef number = 0; 1177 //(don 't release) 1178 CFDictionaryRef properties = 0; 1179 //(needs release) 1180 CFStringRef string = 0; 1181 //(don 't release) 1182 1183 //MediaName 1184 1185 kr = IORegistryEntryGetName(entry, ioMediaName); 1186 if (KERN_SUCCESS != kr) { 1187 dwarning(("can't obtain name for media object\n")); 1188 goto Next; 1189 } 1190 //Get Properties 1191 1192 kr = IORegistryEntryCreateCFProperties(entry, (CFMutableDictionaryRef *)&properties, kCFAllocatorDefault, kNilOptions); 1193 if (KERN_SUCCESS != kr) { 1194 dwarning(("can't obtain properties for '%s'\n", ioMediaName)); 1195 goto Next; 1196 } 1197 assert(CFGetTypeID(properties) == CFDictionaryGetTypeID()); 1198 1199 //BSDName 1200 1201 string = (CFStringRef) CFDictionaryGetValue(properties, CFSTR(kIOBSDNameKey)); 1202 if (!string) { 1203 /* We're only interested in disks accessible via BSD */ 1204 dwarning(("kIOBSDNameKey property missing for '%s'\n", ioMediaName)); 1205 goto Next; 1206 } 1207 assert(CFGetTypeID(string) == CFStringGetTypeID()); 1208 1209 ioBSDName = daCreateCStringFromCFString(string); 1210 assert(ioBSDName); 1211 1212 dwarning(("ioBSDName = '%s'\t", ioBSDName)); 1213 1214 //Content 1215 1216 string = (CFStringRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaContentKey)); 1217 if (!string) { 1218 dwarning(("\nkIOMediaContentKey property missing for '%s'\n", ioBSDName)); 1219 goto Next; 1220 } 1221 assert(CFGetTypeID(string) == CFStringGetTypeID()); 1222 1223 //Writable 1224 1225 boolean = (CFBooleanRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaWritableKey)); 1226 if (!boolean) { 1227 dwarning(("\nkIOMediaWritableKey property missing for '%s'\n", ioBSDName)); 1228 goto Next; 1229 } 1230 assert(CFGetTypeID(boolean) == CFBooleanGetTypeID()); 1231 1232 ioWritable = (kCFBooleanTrue == boolean); 1233 1234 dwarning(("ioWritable = %d\t", ioWritable)); 1235 1236 //Ejectable 1237 1238 boolean = (CFBooleanRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaEjectableKey)); 1239 if (!boolean) { 1240 dwarning(("\nkIOMediaEjectableKey property missing for '%s'\n", ioBSDName)); 1241 goto Next; 1242 } 1243 assert(CFGetTypeID(boolean) == CFBooleanGetTypeID()); 1244 1245 ioEjectable = (kCFBooleanTrue == boolean); 1246 1247 dwarning(("ioEjectable = %d\t", ioEjectable)); 1248 1249 //ioSize 1250 1251 number = (CFNumberRef) CFDictionaryGetValue(properties, CFSTR(kIOMediaSizeKey)); 1252 if (!number) { 1253 dwarning(("\nkIOMediaSizeKey property missing for '%s'\n", ioBSDName)); 1254 } 1255 assert(CFGetTypeID(number) == CFNumberGetTypeID()); 1256 1257 if (!CFNumberGetValue(number, kCFNumberLongLongType, &ioSize)) { 1258 goto Next; 1259 } 1260 dwarning(("ioSize = %ld\t", (long int) ioSize)); 1261 1262 //Construct the < flags > word 1263 1264 flags = 0; 1265 1266 if (!ioWritable) 1267 flags |= kDiskArbDiskAppearedLockedMask; 1268 1269 if (ioEjectable) 1270 flags |= kDiskArbDiskAppearedEjectableMask; 1271 1272 if (!ioSize) 1273 flags |= kDiskArbDiskAppearedNoSizeMask; 1274 //blank media 1275 1276 if (diskIsInternal(entry)) { 1277 dwarning(("\nInternal disk appeared ...\n")); 1278 flags |= kDiskArbDiskAppearedInternal; 1279 } 1280 1281 //Create a disk record 1282 1283 { 1284 /* 1285 * Create a new disk 1286 */ 1287 DiskPtr disk = NewDisk(ioBSDName, 1288 entry, 1289 flags, 1290 ioSize); 1291 if (!disk) { 1292 pwarning(("%s: NewDisk() failed!\n", __FUNCTION__)); 1293 } 1294 } 1295 1296Next: 1297 1298 if (properties) 1299 CFRelease(properties); 1300 if (ioBSDName) 1301 free(ioBSDName); 1302 1303 IOObjectRelease(entry); 1304 1305 } /* while */ 1306 1307} /* GetDisksFromRegistry */ 1308 1309/* 1310 * Function: string_to_argv 1311 * Purpose: 1312 * The given string "str" looks like a set of command-line arguments, space-separated e.g. 1313 * "-v -d -s", or, "-y", or "". Turn that into an array of string pointers using the given 1314 * "argv" array to store up to "n" of them. 1315 * 1316 * The given string is modified, as each space is replaced by a nul. 1317 */ 1318int 1319string_to_argv(char * str, char * * argv, int n) 1320{ 1321 int count; 1322 char * scan; 1323 1324 if (str == NULL) 1325 return (0); 1326 1327 for (count = 0, scan = str; count < n; ) { 1328 char * space; 1329 1330 argv[count++] = scan; 1331 space = strchr(scan, ' '); 1332 if (space == NULL) 1333 break; 1334 *space = '\0'; 1335 scan = space + 1; 1336 } 1337 return (count); 1338} 1339 1340/* 1341 * We only want to trigger the quotacheck command 1342 * on a volume when we fsck it and mark it clean. 1343 * The quotacheck must be done post mount. 1344 */ 1345boolean_t 1346fsck_vols(DiskVolumesPtr vols) 1347{ 1348 boolean_t result = TRUE; /* mandatory initialization */ 1349 int i; 1350 1351 for (i = 0; i < DiskVolumes_count(vols); i++) { 1352 1353 DiskVolumePtr vol = (DiskVolumePtr) DiskVolumes_objectAtIndex(vols, i); 1354 if (!vol) { 1355 return FALSE; 1356 } 1357 1358 if (vol->writable && vol->dirty) { 1359#define NUM_ARGV 6 1360 const char * argv[NUM_ARGV] = { 1361 NULL, /* fsck */ 1362 NULL, /* -y */ 1363 NULL, /* /dev/rdisk0s8 */ 1364 NULL, /* termination */ 1365 NULL, /* 2 extra args in case someone wants to pass */ 1366 NULL /* extra args beyond -y */ 1367 }; 1368 int argc; 1369 char *fsckCmd = repairPathForFileSystem(vol->fs_type); 1370 char *rprCmd = repairArgsForFileSystem(vol->fs_type); 1371 char devpath[64]; 1372 int ret; 1373 1374 snprintf(devpath, sizeof(devpath), "/dev/r%s", vol->disk_dev_name); 1375 argv[0] = fsckCmd; 1376 argc = string_to_argv(rprCmd, (char * *)argv + 1, NUM_ARGV - 3); 1377 argv[1 + argc] = devpath; 1378 1379 if (do_exec(NULL, argv, &ret, NULL) == FALSE) { 1380 /* failed to get a result, assume the volume is clean */ 1381 dwarning(("*** vol dirty? ***\n")); 1382 vol->dirty = FALSE; 1383 ret = 0; 1384 } 1385 else if (ret == 0) { 1386 /* Mark the volume as clean so that it will be mounted */ 1387 vol->dirty = FALSE; 1388 } 1389 else { 1390 dwarning(("'%s' failed: %d\n", fsckCmd, ret)); 1391 } 1392 1393 /* 1394 * Result will be TRUE iff each fsck command 1395 * is successful 1396 */ 1397 dwarning(("*** result? ***\n")); 1398 result = result && (ret == 0); 1399 1400 dwarning(("*** freeing? ***\n")); 1401 1402 free(fsckCmd); 1403 free(rprCmd); 1404 1405 } //if dirty 1406 } //for each 1407 return result; 1408 1409} 1410 1411boolean_t 1412autodiskmount_findDisk(boolean_t all, const char * volume_name) 1413{ 1414 boolean_t found = TRUE; 1415 DiskVolumesPtr vols; 1416 1417 DiskVolumes_new(&vols); 1418 DiskVolumes_do_volumes(vols); 1419 1420 (void)fsck_vols(vols); 1421 found = DiskVolumes_findDisk(vols, all, volume_name); 1422 1423 DiskVolumes_delete(vols); 1424 return (found); 1425} 1426 1427int 1428findDiskInit() 1429{ 1430 kern_return_t r; 1431 io_iterator_t ioIterator; // first match 1432 1433 r = IOMasterPort(bootstrap_port, &ioMasterPort); 1434 if (r != KERN_SUCCESS) 1435 { 1436 pwarning(("(%s:%d) IOMasterPort failed: {0x%x}\n", __FILE__, __LINE__, r)); 1437 return -1; 1438 } 1439 1440 r = IOServiceGetMatchingServices(ioMasterPort, 1441 IOServiceMatching( "IOMedia" ), 1442 &ioIterator); 1443 if (r != KERN_SUCCESS) 1444 { 1445 pwarning(("(%s:%d) IOServiceGetMatching Services: {0x%x}\n", __FILE__, __LINE__, r)); 1446 return -1; 1447 } 1448 GetDisksFromRegistry(ioIterator, 1, 0); 1449 IOObjectRelease(ioIterator); 1450 cacheFileSystemDictionaries(); 1451 cacheFileSystemMatchingArray(); 1452 return (0); 1453} 1454 1455int 1456main(int argc, char * argv[]) 1457{ 1458 const char * volume_name = NULL; 1459 char * progname; 1460 char ch; 1461 boolean_t find = FALSE; 1462 boolean_t all = FALSE; 1463 1464 /* Initialize globals */ 1465 1466 g.Disks = NULL; 1467 g.NumDisks = 0; 1468 1469 g.verbose = FALSE; 1470 g.debug = FALSE; 1471 1472 /* Initialize <progname> */ 1473 1474 progname = argv[0]; 1475 1476 /* Must run as root */ 1477 if (getuid() != 0) { 1478 pwarning(("%s: must be run as root\n", progname)); 1479 exit(1); 1480 } 1481 1482 /* Parse command-line arguments */ 1483 while ((ch = getopt(argc, argv, "avdFV:")) != -1) { 1484 switch (ch) { 1485 case 'a': 1486 all = TRUE; 1487 break; 1488 case 'v': 1489 g.verbose = TRUE; 1490 break; 1491 case 'd': 1492 g.debug = TRUE; 1493 break; 1494 case 'F': 1495 find = TRUE; 1496 break; 1497 case 'V': 1498 volume_name = optarg; 1499 break; 1500 } 1501 } 1502 1503 if (find == TRUE) { 1504 extern int findDiskInit(); 1505 1506 if (findDiskInit() < 0) { 1507 exit(2); 1508 } 1509 if (autodiskmount_findDisk(all, volume_name) == FALSE) { 1510 exit(1); 1511 } 1512 exit(0); 1513 } 1514 1515 exit(0); 1516} 1517