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