1/*
2 * Copyright (c) 1999-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.2 (the
8 * "License").  You may not use this file except in compliance with the
9 * License.  Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22/*
23 Copyright (c) 1987-99 Apple Computer, Inc.
24 All Rights Reserved.
25
26 About hfs.util.m:
27 Contains code to implement hfs utility used by the WorkSpace to mount HFS.
28
29 Change History:
30     5-Jan-1999 Don Brady       Write hfs.label names in UTF-8.
31    10-Dec-1998 Pat Dirks       Changed to try built-in hfs filesystem first.
32     3-Sep-1998	Don Brady	Disable the daylight savings time stuff.
33    28-Aug-1998 chw		Fixed parse args and verify args to indicate that the
34			        flags (fixed or removable) are required in the probe case.
35    22-Jun-1998	Pat Dirks	Changed HFSToUFSStr table to switch ":" and "/".
36    13-Jan-1998 jwc 		first cut (derived from old NextStep macfs.util code and cdrom.util code).
37 */
38
39
40/* ************************************** I N C L U D E S ***************************************** */
41
42#include <sys/types.h>
43#include <sys/stat.h>
44#include <sys/time.h>
45#include <sys/sysctl.h>
46#include <sys/resource.h>
47#include <sys/vmmeter.h>
48#include <sys/mount.h>
49#include <sys/wait.h>
50#include <sys/param.h>
51#include <sys/ucred.h>
52#include <sys/disk.h>
53#include <sys/loadable_fs.h>
54#include <sys/attr.h>
55#include <hfs/hfs_format.h>
56#include <hfs/hfs_mount.h>
57
58#include <ctype.h>
59#include <errno.h>
60#include <fcntl.h>
61#include <pwd.h>
62#include <stdio.h>
63#include <stdlib.h>
64#include <string.h>
65#include <unistd.h>
66#include <syslog.h>
67
68/*
69 * CommonCrypto provides a more stable API than OpenSSL guarantees;
70 * the #define causes it to use the same API for MD5 and SHA1, so the rest of
71 * the code need not change.
72 */
73#define COMMON_DIGEST_FOR_OPENSSL
74#include  <CommonCrypto/CommonDigest.h>
75
76#include <libkern/OSByteOrder.h>
77
78#include <CoreFoundation/CFString.h>
79
80#include <System/uuid/uuid.h>
81#include <System/uuid/namespace.h>
82
83#define READ_DEFAULT_ENCODING 1
84
85#ifndef FSUC_ADOPT
86#define FSUC_ADOPT 'a'
87#endif
88
89#ifndef FSUC_DISOWN
90#define FSUC_DISOWN 'd'
91#endif
92
93#ifndef FSUC_GETUUID
94#define FSUC_GETUUID 'k'
95#endif
96
97#ifndef FSUC_SETUUID
98#define FSUC_SETUUID 's'
99#endif
100
101#ifndef FSUC_MKJNL
102#define FSUC_MKJNL   'J'
103#endif
104
105#ifndef FSUC_UNJNL
106#define FSUC_UNJNL   'U'
107#endif
108
109#ifndef FSUC_UNJNL_RAW
110#define FSUC_UNJNL_RAW 'N'
111#endif
112
113#ifndef FSUC_JNLINFS_RAW
114#define FSUC_JNLINFS_RAW 'e'
115#endif
116
117#ifndef FSUC_EXTJNL_RAW
118#define FSUC_EXTJNL_RAW 'E'
119#endif
120
121#ifndef FSUC_JNLINFO
122#define FSUC_JNLINFO 'I'
123#endif
124
125
126/* **************************************** L O C A L S ******************************************* */
127
128#define kHFSPlusMaxFileNameBytes	(3 * 255 + 1)	/* 255 unicode characters, plus 1 NUL byte */
129
130#define	HFS_BLOCK_SIZE			512
131
132char gHFS_FS_NAME[] = "hfs";
133char gHFS_FS_NAME_NAME[] = "HFS";
134
135char gNewlineString[] = "\n";
136
137char gMountCommand[] = "/sbin/mount";
138
139char gUnmountCommand[] = "/sbin/umount";
140
141char gReadOnlyOption[] = "-r";
142char gReadWriteOption[] = "-w";
143
144char gSuidOption[] = "suid";
145char gNoSuidOption[] = "nosuid";
146
147char gDevOption[] = "dev";
148char gNoDevOption[] = "nodev";
149
150char gUsePermissionsOption[] = "perm";
151char gIgnorePermissionsOption[] = "noperm";
152
153boolean_t gIsEjectable = 0;
154
155int gJournalSize = 0;
156
157#define AUTO_ADOPT_FIXED 1
158#define AUTO_ENTER_FIXED 0
159
160
161struct FinderAttrBuf {
162	u_int32_t info_length;
163	u_int32_t finderinfo[8];
164};
165
166
167#define VOLUMEUUIDVALUESIZE 2
168typedef union VolumeUUID {
169	u_int32_t value[VOLUMEUUIDVALUESIZE];
170	struct {
171		u_int32_t high;
172		u_int32_t low;
173	} v;
174} VolumeUUID;
175
176#define VOLUMEUUIDLENGTH 16
177typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1];
178
179#define VOLUME_RECORDED 0x80000000
180#define VOLUME_USEPERMISSIONS 0x00000001
181#define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
182
183typedef void *VolumeStatusDBHandle;
184
185void GenerateVolumeUUID(VolumeUUID *newVolumeID);
186void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID);
187void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString);
188int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr);
189int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus);
190int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus);
191int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID);
192int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle);
193
194/* ************************************ P R O T O T Y P E S *************************************** */
195static void	DoDisplayUsage( const char * argv[] );
196static int	DoMount( char * theDeviceNamePtr, const char *rawName, const char * theMountPointPtr,
197					boolean_t isLocked, boolean_t isSetuid, boolean_t isDev );
198static int 	DoProbe( char * rawDeviceNamePtr, char * blockDeviceNamePtr );
199static int 	DoUnmount( const char * theMountPointPtr );
200static int	DoGetUUIDKey( const char * theDeviceNamePtr, const char *rawName );
201static int	DoChangeUUIDKey( const char * theDeviceNamePtr );
202static int	DoAdopt( const char * theDeviceNamePtr, const char *rawName);
203static int	DoDisown( const char * theDeviceNamePtr, const char *rawName);
204
205extern int  DoMakeJournaled( const char * volNamePtr, int journalSize );  // XXXdbg
206extern int  DoUnJournal( const char * volNamePtr );      // XXXdbg
207extern int  DoGetJournalInfo( const char * volNamePtr );
208extern int  RawDisableJournaling( const char *devname );
209extern int  SetJournalInFSState( const char *devname, int journal_in_fs);
210
211static int	ParseArgs( int argc, const char * argv[], const char ** actionPtr, const char ** mountPointPtr, boolean_t * isEjectablePtr, boolean_t * isLockedPtr, boolean_t * isSetuidPtr, boolean_t * isDevPtr );
212
213static int	GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr);
214static int	ReadHeaderBlock(int fd, void *bufptr, off_t *startOffset, VolumeUUID **finderInfoUUIDPtr);
215static int	GetVolumeUUIDRaw(const char *deviceNamePtr, const char *rawName, VolumeUUID *volumeUUIDPtr);
216static int	GetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr);
217static int	GetVolumeUUID(const char *deviceNamePtr, const char *rawName, VolumeUUID *volumeUUIDPtr, boolean_t generate);
218static int	SetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr);
219static int	SetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr);
220static int	SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr);
221static int	GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr);
222static int	GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o);
223static int	GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize,
224							u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
225							u_int32_t *nodeSize, u_int32_t *firstLeafNode);
226static int	GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset, HFSPlusVolumeHeader *volHdrPtr,
227									 HFSPlusExtentDescriptor **catalogExtents, u_int32_t *catalogExtCount);
228static int	LogicalToPhysical(off_t logicalOffset, ssize_t length, u_int32_t blockSize,
229							u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
230							off_t *physicalOffset, ssize_t *availableBytes);
231static int	ReadFile(int fd, void *buffer, off_t offset, ssize_t length,
232					off_t volOffset, u_int32_t blockSize,
233					u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList);
234static ssize_t	readAt( int fd, void * buf, off_t offset, ssize_t length );
235static ssize_t	writeAt( int fd, void * buf, off_t offset, ssize_t length );
236
237static int	GetEncodingBias(void);
238
239
240CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, UInt8 *buffer, CFIndex maxBufLen);
241
242static void     uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen);
243
244/*
245 * The fuction CFStringGetSystemEncoding does not work correctly in
246 * our context (autodiskmount deamon).  We include a local copy here
247 * so that we can derive the default encoding. Radar 2516316.
248 */
249#if READ_DEFAULT_ENCODING
250#define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
251
252static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
253    struct passwd *passwdp;
254
255    if ((passwdp = getpwuid(0))) { // root account
256        char buffer[MAXPATHLEN + 1];
257        int fd;
258
259        strlcpy(buffer, passwdp->pw_dir, sizeof(buffer));
260        strlcat(buffer, __kCFUserEncodingFileName, sizeof(buffer));
261
262        if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
263            size_t readSize;
264
265            readSize = read(fd, buffer, MAXPATHLEN);
266            buffer[(readSize < 0 ? 0 : readSize)] = '\0';
267            close(fd);
268            return strtol(buffer, NULL, 0);
269        }
270    }
271    return 0; // Fallback to smRoman
272}
273#endif
274
275
276#define MXENCDNAMELEN	16	/* Maximun length of encoding name string */
277
278struct hfs_mnt_encoding {
279	char				encoding_name[MXENCDNAMELEN];	/* encoding type name */
280	CFStringEncoding	encoding_id;					/* encoding type number */
281};
282
283static struct hfs_mnt_encoding hfs_mnt_encodinglist[] = {
284	{ "Arabic",	          4 },
285	{ "Armenian",        24 },
286	{ "Bengali",         13 },
287	{ "Burmese",         19 },
288	{ "Celtic",          39 },
289	{ "CentralEurRoman", 29 },
290	{ "ChineseSimp",     25 },
291	{ "ChineseTrad",      2 },
292	{ "Croatian",	     36 },
293	{ "Cyrillic",	      7 },
294	{ "Devanagari",       9 },
295	{ "Ethiopic",        28 },
296	{ "Farsi",          140 },
297	{ "Gaelic",          40 },
298	{ "Georgian",        23 },
299	{ "Greek",	          6 },
300	{ "Gujarati",        11 },
301	{ "Gurmukhi",        10 },
302	{ "Hebrew",	          5 },
303	{ "Icelandic",	     37 },
304	{ "Japanese",	      1 },
305	{ "Kannada",         16 },
306	{ "Khmer",           20 },
307	{ "Korean",	          3 },
308	{ "Laotian",         22 },
309	{ "Malayalam",       17 },
310	{ "Mongolian",       27 },
311	{ "Oriya",           12 },
312	{ "Roman",	          0 },	/* default */
313	{ "Romanian",	     38 },
314	{ "Sinhalese",       18 },
315	{ "Tamil",           14 },
316	{ "Telugu",          15 },
317	{ "Thai",	         21 },
318	{ "Tibetan",         26 },
319	{ "Turkish",	     35 },
320	{ "Ukrainian",      152 },
321	{ "Vietnamese",      30 },
322};
323
324#define KEXT_LOAD_COMMAND	"/sbin/kextload"
325#define ENCODING_MODULE_PATH	"/System/Library/Filesystems/hfs.fs/Encodings/"
326
327static int load_encoding(CFStringEncoding encoding)
328{
329	int i;
330	int numEncodings;
331	int pid;
332	char *encodingName;
333	struct stat sb;
334	union wait status;
335	char kmodfile[MAXPATHLEN];
336
337	/* Find the encoding that matches the one passed in */
338	numEncodings = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding);
339	encodingName = NULL;
340	for (i=0; i<numEncodings; ++i)
341	{
342		if (hfs_mnt_encodinglist[i].encoding_id == encoding)
343		{
344			encodingName = hfs_mnt_encodinglist[i].encoding_name;
345			break;
346		}
347	}
348
349	if (encodingName == NULL)
350	{
351		/* Couldn't figure out which encoding KEXT to load */
352		syslog(LOG_ERR, "Couldn't find name for encoding #%d", encoding);
353		return FSUR_LOADERR;
354	}
355
356	snprintf(kmodfile, sizeof(kmodfile), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH, encodingName);
357	if (stat(kmodfile, &sb) == -1)
358	{
359		/* We recognized the encoding, but couldn't find the KEXT */
360		syslog(LOG_ERR, "Couldn't stat HFS_Mac%s.kext: %s", encodingName, strerror(errno));
361		return FSUR_LOADERR;
362	}
363
364	pid = fork();
365	if (pid == 0)
366	{
367		(void) execl(KEXT_LOAD_COMMAND, KEXT_LOAD_COMMAND, "-q", kmodfile, NULL);
368
369		exit(1);	/* We can only get here if the exec failed */
370	}
371	else if (pid != -1)
372	{
373		if ((waitpid(pid, (int *)&status, 0) == pid) && WIFEXITED(status))
374		{
375			if (WEXITSTATUS(status) != 0)
376			{
377				/* kextload returned an error.  Too bad its output doesn't get logged. */
378				syslog(LOG_ERR, "Couldn't load HFS_Mac%s.kext", encodingName);
379				return FSUR_LOADERR;
380			}
381		}
382	}
383
384	return FSUR_IO_SUCCESS;
385}
386
387
388/* ******************************************** main ************************************************
389Purpose -
390This our main entry point to this utility.  We get called by the WorkSpace.  See ParseArgs
391for detail info on input arguments.
392Input -
393argc - the number of arguments in argv.
394argv - array of arguments.
395Output -
396returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
397*************************************************************************************************** */
398
399int main (int argc, const char *argv[])
400{
401    const char			*	actionPtr = NULL;
402    char				rawDeviceName[MAXPATHLEN];
403    char				blockDeviceName[MAXPATHLEN];
404    const char			*	mountPointPtr = NULL;
405    int						result = FSUR_IO_SUCCESS;
406    boolean_t				isLocked = 0;	/* reasonable assumptions */
407    boolean_t				isSetuid = 0;	/* reasonable assumptions */
408    boolean_t				isDev = 0;	/* reasonable assumptions */
409
410	openlog("hfs.util", LOG_PID, LOG_DAEMON);
411
412    /* Verify our arguments */
413    if ( (result = ParseArgs( argc, argv, & actionPtr, & mountPointPtr, & gIsEjectable, & isLocked, &isSetuid, &isDev )) != 0 ) {
414        goto AllDone;
415    }
416
417    /*
418    -- Build our device name (full path), should end up with something like:
419    --   "/dev/disk0s2"
420    */
421
422    snprintf(rawDeviceName, sizeof(rawDeviceName), "/dev/r%s", argv[2]);
423    snprintf(blockDeviceName, sizeof(blockDeviceName), "/dev/%s", argv[2]);
424
425    /* call the appropriate routine to handle the given action argument after becoming root */
426
427    switch( * actionPtr ) {
428        case FSUC_PROBE:
429            result = DoProbe(rawDeviceName, blockDeviceName);
430            break;
431
432        case FSUC_MOUNT:
433        case FSUC_MOUNT_FORCE:
434            result = DoMount(blockDeviceName, rawDeviceName, mountPointPtr, isLocked, isSetuid, isDev);
435            break;
436
437        case FSUC_UNMOUNT:
438            result = DoUnmount( mountPointPtr );
439            break;
440		case FSUC_GETUUID:
441			result = DoGetUUIDKey( blockDeviceName, rawDeviceName);
442			break;
443
444		case FSUC_SETUUID:
445			result = DoChangeUUIDKey( blockDeviceName );
446			break;
447		case FSUC_ADOPT:
448			result = DoAdopt( blockDeviceName, rawDeviceName);
449			break;
450
451		case FSUC_DISOWN:
452			result = DoDisown( blockDeviceName, rawDeviceName );
453			break;
454
455		case FSUC_MKJNL:
456			if (gJournalSize) {
457				result = DoMakeJournaled( argv[3], gJournalSize );
458			} else {
459				result = DoMakeJournaled( argv[2], gJournalSize );
460			}
461			break;
462
463		case FSUC_UNJNL:
464			result = DoUnJournal( argv[2] );
465			break;
466
467		case FSUC_UNJNL_RAW:
468			result = RawDisableJournaling( argv[2] );
469			break;
470
471		case FSUC_JNLINFS_RAW:
472			// argv[2] has the device for the external journal.  however
473			// we don't need it so we ignore it and just pass argv[3]
474			// which is the hfs volume whose state we're going to change
475			//
476			result = SetJournalInFSState( argv[3], 1 );
477			break;
478
479		case FSUC_EXTJNL_RAW:
480			// see the comment for FSUC_JNLINFS_RAW
481			result = SetJournalInFSState( argv[3], 0 );
482			break;
483
484		case FSUC_JNLINFO:
485			result = DoGetJournalInfo( argv[2] );
486			break;
487
488        default:
489            /* should never get here since ParseArgs should handle this situation */
490            DoDisplayUsage( argv );
491            result = FSUR_INVAL;
492            break;
493    }
494
495AllDone:
496
497    exit(result);
498
499    return result;	/*...and make main fit the ANSI spec. */
500}
501
502
503/* ***************************** DoMount ********************************
504Purpose -
505This routine will fire off a system command to mount the given device at the given mountpoint.
506autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
507Input -
508deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
509mountPointPtr - pointer to the mount point.
510isLocked - a flag
511Output -
512returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
513*********************************************************************** */
514static int
515DoMount(char *deviceNamePtr, const char *rawName, const char *mountPointPtr,
516		boolean_t isLocked, boolean_t isSetuid, boolean_t isDev)
517{
518	int pid;
519        char *isLockedstr;
520        char *isSetuidstr;
521        char *isDevstr;
522	char *permissionsOption;
523	int result = FSUR_IO_FAIL;
524	union wait status;
525	char encodeopt[16] = "";
526	CFStringEncoding encoding;
527	VolumeUUID targetVolumeUUID;
528	VolumeStatusDBHandle vsdbhandle = NULL;
529	unsigned long targetVolumeStatus;
530
531	if (mountPointPtr == NULL || *mountPointPtr == '\0')
532		return (FSUR_IO_FAIL);
533
534	/* get the volume UUID to check if permissions should be used: */
535	targetVolumeStatus = 0;
536	if (((result = GetVolumeUUID(deviceNamePtr, rawName, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) ||
537		(targetVolumeUUID.v.high ==0) ||
538		(targetVolumeUUID.v.low == 0)) {
539#if TRACE_HFS_UTIL
540		fprintf(stderr, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result);
541#endif
542#if AUTO_ADOPT_FIXED
543		if (gIsEjectable == 0) {
544			result = DoAdopt( deviceNamePtr, rawName);
545#if TRACE_HFS_UTIL
546			fprintf(stderr, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr, result);
547#endif
548			targetVolumeStatus = VOLUME_USEPERMISSIONS;
549		} else {
550#if TRACE_HFS_UTIL
551			fprintf(stderr, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr);
552#endif
553			targetVolumeStatus = 0;
554		}
555#endif
556	} else {
557		/* We've got a real volume UUID! */
558#if TRACE_HFS_UTIL
559		fprintf(stderr, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID.v.high, targetVolumeUUID.v.low);
560#endif
561		if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) {
562			/* Can't even get access to the volume info db; assume permissions are OK. */
563#if TRACE_HFS_UTIL
564			fprintf(stderr, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result);
565#endif
566			targetVolumeStatus = VOLUME_USEPERMISSIONS;
567		} else {
568#if TRACE_HFS_UTIL
569			fprintf(stderr, "hfs.util: DoMount: Looking up volume status...\n");
570#endif
571			if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
572#if TRACE_HFS_UTIL
573				fprintf(stderr, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result);
574#endif
575#if AUTO_ENTER_FIXED
576				if (gIsEjectable == 0) {
577					result = DoAdopt( deviceNamePtr, rawName );
578#if TRACE_HFS_UTIL
579					fprintf(stderr, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr, result);
580#endif
581					targetVolumeStatus = VOLUME_USEPERMISSIONS;
582				} else {
583#if TRACE_HFS_UTIL
584					fprintf(stderr, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr);
585#endif
586					targetVolumeStatus = 0;
587				}
588#else
589				targetVolumeStatus = 0;
590#endif
591			}
592			(void)CloseVolumeStatusDB(vsdbhandle);
593			vsdbhandle = NULL;
594		}
595	}
596
597	pid = fork();
598	if (pid == 0) {
599                isLockedstr = isLocked ? gReadOnlyOption : gReadWriteOption;
600                isSetuidstr = isSetuid ? gSuidOption : gNoSuidOption;
601                isDevstr = isDev ? gDevOption : gNoDevOption;
602
603		permissionsOption =
604			(targetVolumeStatus & VOLUME_USEPERMISSIONS) ? gUsePermissionsOption : gIgnorePermissionsOption;
605
606		/* get default encoding value (for hfs volumes) */
607#if READ_DEFAULT_ENCODING
608		encoding = __CFStringGetDefaultEncodingForHFSUtil();
609#else
610		encoding = CFStringGetSystemEncoding();
611#endif
612		snprintf(encodeopt, sizeof(encodeopt), "-e=%d", (int)encoding);
613#if TRACE_HFS_UTIL
614		fprintf(stderr, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
615							gMountCommand, isLockedstr, encodeopt, permissionsOption, gHFS_FS_NAME, deviceNamePtr, mountPointPtr);
616#endif
617                (void) execl(gMountCommand, gMountCommand, isLockedstr, "-o", isSetuidstr, "-o", isDevstr,
618                                        "-o", encodeopt, "-o", permissionsOption,
619                                        "-o", "-u=unknown,-g=unknown,-m=0777",
620                                        "-t", gHFS_FS_NAME, deviceNamePtr, mountPointPtr, NULL);
621
622
623		/* IF WE ARE HERE, WE WERE UNSUCCESFULL */
624		return (FSUR_IO_FAIL);
625	}
626
627	if (pid == -1)
628		return (FSUR_IO_FAIL);
629
630	/* Success! */
631	if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status)))
632		result = status.w_retcode;
633	else
634		result = -1;
635
636	return (result == 0) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
637}
638
639
640/* ****************************************** DoUnmount *********************************************
641Purpose -
642    This routine will fire off a system command to unmount the given device.
643Input -
644    theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
645Output -
646    returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
647*************************************************************************************************** */
648static int
649DoUnmount(const char * theMountPointPtr)
650{
651	int pid;
652	union wait status;
653	int result;
654
655	if (theMountPointPtr == NULL || *theMountPointPtr == '\0') return (FSUR_IO_FAIL);
656
657	pid = fork();
658	if (pid == 0) {
659#if TRACE_HFS_UTIL
660		fprintf(stderr, "hfs.util: %s %s ...\n", gUnmountCommand, theMountPointPtr);
661#endif
662		(void) execl(gUnmountCommand, gUnmountCommand, theMountPointPtr, NULL);
663
664		/* IF WE ARE HERE, WE WERE UNSUCCESFULL */
665		return (FSUR_IO_FAIL);
666	}
667
668	if (pid == -1)
669		return (FSUR_IO_FAIL);
670
671	/* Success! */
672	if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status)))
673		result = status.w_retcode;
674	else
675		result = -1;
676
677	return (result == 0 ? FSUR_IO_SUCCESS : FSUR_IO_FAIL);
678
679} /* DoUnmount */
680
681
682/*
683	PrintVolumeNameAttr
684
685	Get the volume name of the volume mounted at "path".  Print that volume
686	name to standard out.
687
688	Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
689*/
690struct VolumeNameBuf {
691	u_int32_t	info_length;
692	attrreference_t	name_ref;
693	char		buffer[1024];
694};
695
696static int
697PrintVolumeNameAttr(const char *path)
698{
699	struct attrlist alist;
700	struct VolumeNameBuf volNameInfo;
701	int result;
702
703	/* Set up the attrlist structure to get the volume's Finder Info */
704	alist.bitmapcount = 5;
705	alist.reserved = 0;
706	alist.commonattr = 0;
707	alist.volattr = ATTR_VOL_INFO | ATTR_VOL_NAME;
708	alist.dirattr = 0;
709	alist.fileattr = 0;
710	alist.forkattr = 0;
711
712	/* Get the Finder Info */
713	result = getattrlist(path, &alist, &volNameInfo, sizeof(volNameInfo), 0);
714	if (result) {
715		result = FSUR_IO_FAIL;
716		goto Err_Exit;
717	}
718
719	/* Print the name to standard out */
720	printf("%.*s", (int) volNameInfo.name_ref.attr_length, ((char *) &volNameInfo.name_ref) + volNameInfo.name_ref.attr_dataoffset);
721	result = FSUR_RECOGNIZED;
722
723Err_Exit:
724	return result;
725}
726
727
728/* ******************************************* DoProbe **********************************************
729Purpose -
730    This routine will open the given device and check to make sure there is media that looks
731    like an HFS.  If it is HFS, then print the volume name to standard output.
732Input -
733    rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
734    blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
735Output -
736    returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
737*************************************************************************************************** */
738static int
739DoProbe(char *rawDeviceNamePtr, char *blockDeviceNamePtr)
740{
741	int result = FSUR_UNRECOGNIZED;
742	int fd = 0;
743	char * bufPtr;
744	HFSMasterDirectoryBlock * mdbPtr;
745	HFSPlusVolumeHeader * volHdrPtr;
746	u_char volnameUTF8[kHFSPlusMaxFileNameBytes];
747
748	/*
749	 * Determine if there is a volume already mounted from this device.  If
750	 * there is, and it is HFS, then we need to get the volume name via
751	 * getattrlist.
752	 *
753	 * NOTE: We're using bufPtr to hold a pointer to a path.
754	 */
755	bufPtr = NULL;
756	result = GetHFSMountPoint(blockDeviceNamePtr, &bufPtr);
757	if (result != FSUR_IO_SUCCESS) {
758		goto Err_Exit;
759	}
760	if (bufPtr != NULL) {
761		/* There is an HFS volume mounted from the device. */
762		result = PrintVolumeNameAttr(bufPtr);
763		goto Err_Exit;
764	}
765
766	/*
767	 * If we get here, there is no volume mounted from this device, so
768	 * go probe the raw device directly.
769	 */
770
771	bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
772	if ( ! bufPtr ) {
773		result = FSUR_UNRECOGNIZED;
774		goto Return;
775	}
776
777	mdbPtr = (HFSMasterDirectoryBlock *) bufPtr;
778	volHdrPtr = (HFSPlusVolumeHeader *) bufPtr;
779
780	fd = open( rawDeviceNamePtr, O_RDONLY, 0 );
781	if( fd <= 0 ) {
782		result = FSUR_IO_FAIL;
783		goto Return;
784	}
785
786	/*
787	 * Read the HFS Master Directory Block from sector 2
788	 */
789	result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
790	if (FSUR_IO_FAIL == result)
791		goto Return;
792
793	/* get classic HFS volume name (from MDB) */
794	if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
795	    OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) != kHFSPlusSigWord) {
796	    	Boolean cfOK;
797		CFStringRef cfstr;
798		CFStringEncoding encoding;
799
800		/* Some poorly mastered HFS CDs have an empty MDB name field! */
801		if (mdbPtr->drVN[0] == '\0') {
802			strcpy((char *)&mdbPtr->drVN[1], gHFS_FS_NAME_NAME);
803			mdbPtr->drVN[0] = strlen(gHFS_FS_NAME_NAME);
804		}
805
806		/* Check for an encoding hint in the Finder Info (field 4). */
807		encoding = GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr->drFndrInfo[4]));
808		if (encoding == kCFStringEncodingInvalidId) {
809			/* Next try the encoding bias in the kernel. */
810			encoding = GetEncodingBias();
811			if (encoding == 0 || encoding == kCFStringEncodingInvalidId)
812				encoding = __CFStringGetDefaultEncodingForHFSUtil();
813		}
814
815		cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
816			    mdbPtr->drVN, encoding);
817		if (cfstr == NULL) {
818			result = FSUR_INVAL;
819			goto Return;
820		}
821		cfOK = _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
822		CFRelease(cfstr);
823
824		if (!cfOK && encoding != kCFStringEncodingMacRoman) {
825
826		    	/* default to MacRoman on conversion errors */
827			cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
828				    mdbPtr->drVN, kCFStringEncodingMacRoman);
829			_CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
830			CFRelease(cfstr);
831			encoding = kCFStringEncodingMacRoman;
832		}
833
834 		/* Preload the encoding converter so mount_hfs can run as an ordinary user. */
835 		if (encoding != kCFStringEncodingMacRoman) {
836 			if (load_encoding(encoding) != FSUR_IO_SUCCESS) {
837 				encoding = kCFStringEncodingMacRoman;
838				cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault, mdbPtr->drVN, encoding);
839				_CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX);
840				CFRelease(cfstr);
841 			}
842 		}
843
844 	/* get HFS Plus volume name (from Catalog) */
845	} else if ((OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord)  ||
846	           (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord)  ||
847		   (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
848		    OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord)) {
849		off_t startOffset;
850
851		if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSSigWord) {
852			/* embedded volume, first find offset */
853			result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset);
854			if ( result != FSUR_IO_SUCCESS )
855				goto Return;
856		} else {
857			startOffset = 0;
858		}
859
860		result = GetNameFromHFSPlusVolumeStartingAt(fd, startOffset,
861			    volnameUTF8);
862	} else {
863		result = FSUR_UNRECOGNIZED;
864	}
865
866	if (FSUR_IO_SUCCESS == result) {
867		/* Print the volume name to standard output */
868		write(1, volnameUTF8, strlen((char *)volnameUTF8));
869		result = FSUR_RECOGNIZED;
870	}
871
872Return:
873
874	if ( bufPtr )
875		free( bufPtr );
876
877	if (fd > 0)
878		close(fd);
879Err_Exit:
880	return result;
881
882} /* DoProbe */
883
884/*
885 * Create a version 3 UUID from a unique "name" in the given "name space".
886 * Version 3 UUID are derived using "name" via MD5 checksum.
887 *
888 * Parameters:
889 *	result_uuid	- resulting UUID.
890 *	namespace	- namespace in which given name exists and UUID should be created.
891 *	name		- unique string used to create version 3 UUID.
892 *	namelen     - length of the name string.
893 */
894static void
895uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen)
896{
897    MD5_CTX c;
898
899    MD5_Init(&c);
900    MD5_Update(&c, namespace, sizeof(uuid_t));
901    MD5_Update(&c, name, namelen);
902    MD5_Final(result_uuid, &c);
903
904    result_uuid[6] = (result_uuid[6] & 0x0F) | 0x30;
905    result_uuid[8] = (result_uuid[8] & 0x3F) | 0x80;
906}
907
908
909/* **************************************** DoGetUUIDKey *******************************************
910Purpose -
911    This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
912Input -
913    theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
914Output -
915    returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
916*************************************************************************************************** */
917static int
918DoGetUUIDKey( const char * theDeviceNamePtr, const char *rawName) {
919	int result;
920	VolumeUUID targetVolumeUUID;
921	uuid_t uuid;
922	char uuidLine[40];
923
924	unsigned char rawUUID[8];
925
926	if ((result = GetVolumeUUID(theDeviceNamePtr, rawName, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) goto Err_Exit;
927
928	((uint32_t *)rawUUID)[0] = OSSwapHostToBigInt32(targetVolumeUUID.v.high);
929	((uint32_t *)rawUUID)[1] = OSSwapHostToBigInt32(targetVolumeUUID.v.low);
930
931	uuid_create_md5_from_name(uuid, kFSUUIDNamespaceSHA1, rawUUID, sizeof(rawUUID));
932	uuid_unparse(uuid, uuidLine);
933	write(1, uuidLine, strlen(uuidLine));
934	result = FSUR_IO_SUCCESS;
935
936Err_Exit:
937	return result;
938}
939
940
941
942/* *************************************** DoChangeUUIDKey ******************************************
943Purpose -
944    This routine will change the UUID on the specified block device.
945Input -
946    theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
947Output -
948    returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
949*************************************************************************************************** */
950static int
951DoChangeUUIDKey( const char * theDeviceNamePtr ) {
952	int result;
953	VolumeUUID newVolumeUUID;
954
955	GenerateVolumeUUID(&newVolumeUUID);
956	result = SetVolumeUUID(theDeviceNamePtr, &newVolumeUUID);
957
958	return result;
959}
960
961
962
963/* **************************************** DoAdopt *******************************************
964Purpose -
965    This routine will add the UUID of the specified block device to the list of local volumes.
966Input -
967    theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
968Output -
969    returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
970*************************************************************************************************** */
971static int
972DoAdopt( const char * theDeviceNamePtr, const char *rawName) {
973	int result, closeresult;
974	VolumeUUID targetVolumeUUID;
975	VolumeStatusDBHandle vsdbhandle = NULL;
976	unsigned long targetVolumeStatus;
977
978	if ((result = GetVolumeUUID(theDeviceNamePtr, rawName, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return;
979
980	if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit;
981	if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
982		targetVolumeStatus = 0;
983	}
984	targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;
985	if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit;
986
987	result = FSUR_IO_SUCCESS;
988
989Err_Exit:
990	if (vsdbhandle) {
991		closeresult = CloseVolumeStatusDB(vsdbhandle);
992		vsdbhandle = NULL;
993		if (result == FSUR_IO_SUCCESS) result = closeresult;
994	}
995
996	if ((result != 0) && (result != FSUR_IO_SUCCESS)) result = FSUR_IO_FAIL;
997
998Err_Return:
999#if TRACE_HFS_UTIL
1000	if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoAdopt: returning %d...\n", result);
1001#endif
1002	return result;
1003}
1004
1005
1006
1007/* **************************************** DoDisown *******************************************
1008Purpose -
1009    This routine will change the status of the specified block device to ignore its permissions.
1010Input -
1011    theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
1012Output -
1013    returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
1014*************************************************************************************************** */
1015static int
1016DoDisown( const char * theDeviceNamePtr, const char *rawName) {
1017	int result, closeresult;
1018	VolumeUUID targetVolumeUUID;
1019	VolumeStatusDBHandle vsdbhandle = NULL;
1020	unsigned long targetVolumeStatus;
1021
1022	if ((result = GetVolumeUUID(theDeviceNamePtr, rawName, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return;
1023
1024	if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit;
1025	if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
1026		targetVolumeStatus = 0;
1027	}
1028	targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;
1029	if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit;
1030
1031	result = FSUR_IO_SUCCESS;
1032
1033Err_Exit:
1034	if (vsdbhandle) {
1035		closeresult = CloseVolumeStatusDB(vsdbhandle);
1036		vsdbhandle = NULL;
1037		if (result == FSUR_IO_SUCCESS) result = closeresult;
1038	}
1039
1040	if ((result != 0) && (result != FSUR_IO_SUCCESS)) {
1041#if TRACE_HFS_UTIL
1042		if (result != 0) fprintf(stderr, "DoDisown: result = %d; changing to %d...\n", result, FSUR_IO_FAIL);
1043#endif
1044		result = FSUR_IO_FAIL;
1045	}
1046
1047Err_Return:
1048#if TRACE_HFS_UTIL
1049	if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoDisown: returning %d...\n", result);
1050#endif
1051	return result;
1052}
1053
1054
1055static int
1056get_multiplier(char c)
1057{
1058	if (tolower(c) == 'k') {
1059		return 1024;
1060	} else if (tolower(c) == 'm') {
1061		return 1024 * 1024;
1062	} else if (tolower(c) == 'g') {
1063		return 1024 * 1024 * 1024;
1064	}
1065
1066	return 1;
1067}
1068
1069/* **************************************** ParseArgs ********************************************
1070Purpose -
1071    This routine will make sure the arguments passed in to us are cool.
1072    Here is how this utility is used:
1073
1074usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1075actionArg:
1076        -p (Probe for mounting)
1077       	-P (Probe for initializing - not supported)
1078       	-m (Mount)
1079        -r (Repair - not supported)
1080        -u (Unmount)
1081        -M (Force Mount)
1082        -i (Initialize - not supported)
1083
1084deviceArg:
1085        disk0s2 (for example)
1086
1087mountPointArg:
1088        /foo/bar/ (required for Mount and Force Mount actions)
1089
1090flagsArg:
1091        (these are ignored for CDROMs)
1092        either "readonly" OR "writable"
1093        either "removable" OR "fixed"
1094        either "nosuid" or "suid"
1095	either "nodev" or "dev"
1096
1097examples:
1098	hfs.util -p disk0s2 removable writable
1099	hfs.util -p disk0s2 removable readonly
1100	hfs.util -m disk0s2 /my/hfs
1101
1102Input -
1103    argc - the number of arguments in argv.
1104    argv - array of arguments.
1105Output -
1106    returns FSUR_INVAL if we find a bad argument else 0.
1107*************************************************************************************************** */
1108static int
1109ParseArgs(int argc, const char *argv[], const char ** actionPtr,
1110	const char ** mountPointPtr, boolean_t * isEjectablePtr,
1111          boolean_t * isLockedPtr, boolean_t * isSetuidPtr, boolean_t * isDevPtr)
1112{
1113    int			result = FSUR_INVAL;
1114    int			deviceLength, doLengthCheck = 1;
1115    int			index;
1116    int 		mounting = 0;
1117
1118    /* Must have at least 3 arguments and the action argument must start with a '-' */
1119    if ( (argc < 3) || (argv[1][0] != '-') ) {
1120        DoDisplayUsage( argv );
1121        goto Return;
1122    }
1123
1124    /* we only support actions Probe, Mount, Force Mount, and Unmount */
1125
1126    * actionPtr = & argv[1][1];
1127
1128    switch ( argv[1][1] ) {
1129        case FSUC_PROBE:
1130        /* action Probe and requires 5 arguments (need the flags) */
1131	     if ( argc < 5 ) {
1132             	DoDisplayUsage( argv );
1133             	goto Return;
1134	     } else {
1135            	index = 3;
1136	     }
1137            break;
1138
1139        case FSUC_UNMOUNT:
1140        	/* Note: the device argument in argv[2] is checked further down but ignored. */
1141            * mountPointPtr = argv[3];
1142            index = 0; /* No isEjectable/isLocked flags for unmount. */
1143            break;
1144
1145        case FSUC_MOUNT:
1146        case FSUC_MOUNT_FORCE:
1147            /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1148            if ( argc < 8 ) {
1149                DoDisplayUsage( argv );
1150                goto Return;
1151            } else {
1152                * mountPointPtr = argv[3];
1153                index = 4;
1154                mounting = 1;
1155            }
1156            break;
1157
1158        case FSUC_GETUUID:
1159        	index = 0;
1160			break;
1161
1162		case FSUC_SETUUID:
1163			index = 0;
1164			break;
1165
1166		case FSUC_ADOPT:
1167			index = 0;
1168			break;
1169
1170		case FSUC_DISOWN:
1171			index = 0;
1172			break;
1173
1174		// XXXdbg
1175		case FSUC_MKJNL:
1176			index = 0;
1177			doLengthCheck = 0;
1178			if (isdigit(argv[2][0])) {
1179				char *ptr;
1180				gJournalSize = strtoul(argv[2], &ptr, 0);
1181				if (ptr) {
1182					gJournalSize *= get_multiplier(*ptr);
1183				}
1184				return 0;
1185			}
1186			break;
1187
1188		case FSUC_UNJNL:
1189			index = 0;
1190			doLengthCheck = 0;
1191			break;
1192
1193		case FSUC_UNJNL_RAW:
1194			index = 0;
1195			doLengthCheck = 0;
1196			break;
1197
1198		case FSUC_JNLINFS_RAW:
1199			index = 0;
1200			doLengthCheck = 0;
1201			break;
1202
1203		case FSUC_EXTJNL_RAW:
1204			index = 0;
1205			doLengthCheck = 0;
1206			break;
1207
1208		case FSUC_JNLINFO:
1209			index = 0;
1210			doLengthCheck = 0;
1211			break;
1212		// XXXdbg
1213
1214        default:
1215            DoDisplayUsage( argv );
1216            goto Return;
1217            break;
1218    }
1219
1220    /* Make sure device (argv[2]) is something reasonable */
1221    deviceLength = strlen( argv[2] );
1222    if ( doLengthCheck && (deviceLength < 3 || deviceLength > NAME_MAX) ) {
1223        DoDisplayUsage( argv );
1224        goto Return;
1225    }
1226
1227    if ( index ) {
1228        /* Flags: removable/fixed. */
1229        if ( 0 == strcmp(argv[index],"removable") ) {
1230            * isEjectablePtr = 1;
1231        } else if ( 0 == strcmp(argv[index],"fixed") ) {
1232            * isEjectablePtr = 0;
1233        } else {
1234            printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index,argv[index]);
1235        }
1236
1237        /* Flags: readonly/writable. */
1238        if ( 0 == strcmp(argv[index+1],"readonly") ) {
1239            * isLockedPtr = 1;
1240        } else if ( 0 == strcmp(argv[index+1],"writable") ) {
1241            * isLockedPtr = 0;
1242        } else {
1243            printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index,argv[index+1]);
1244        }
1245
1246        if (mounting) {
1247                    /* Flags: suid/nosuid. */
1248                    if ( 0 == strcmp(argv[index+2],"suid") ) {
1249                        * isSetuidPtr = 1;
1250                    } else if ( 0 == strcmp(argv[index+2],"nosuid") ) {
1251                        * isSetuidPtr = 0;
1252                    } else {
1253                        printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index,argv[index+2]);
1254                    }
1255
1256                    /* Flags: dev/nodev. */
1257                    if ( 0 == strcmp(argv[index+3],"dev") ) {
1258                        * isDevPtr = 1;
1259                    } else if ( 0 == strcmp(argv[index+3],"nodev") ) {
1260                        * isDevPtr = 0;
1261                    } else {
1262                        printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index,argv[index+3]);
1263                    }
1264        }
1265
1266
1267    }
1268
1269    result = 0;
1270
1271Return:
1272        return result;
1273
1274} /* ParseArgs */
1275
1276
1277/* *************************************** DoDisplayUsage ********************************************
1278Purpose -
1279    This routine will do a printf of the correct usage for this utility.
1280Input -
1281    argv - array of arguments.
1282Output -
1283    NA.
1284*************************************************************************************************** */
1285static void
1286DoDisplayUsage(const char *argv[])
1287{
1288    printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv[0]);
1289    printf("action_arg:\n");
1290    printf("       -%c (Probe for mounting)\n", FSUC_PROBE);
1291    printf("       -%c (Mount)\n", FSUC_MOUNT);
1292    printf("       -%c (Unmount)\n", FSUC_UNMOUNT);
1293    printf("       -%c (Force Mount)\n", FSUC_MOUNT_FORCE);
1294#ifdef HFS_UUID_SUPPORT
1295    printf("       -%c (Get UUID Key)\n", FSUC_GETUUID);
1296    printf("       -%c (Set UUID Key)\n", FSUC_SETUUID);
1297#endif HFS_UUID_SUPPORT
1298    printf("       -%c (Adopt permissions)\n", FSUC_ADOPT);
1299	printf("       -%c (Make a file system journaled)\n", FSUC_MKJNL);
1300	printf("       -%c (Turn off journaling on a file system)\n", FSUC_UNJNL);
1301	printf("       -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW);
1302	printf("       -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW);
1303	printf("       -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW);
1304	printf("       -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO);
1305    printf("device_arg:\n");
1306    printf("       device we are acting upon (for example, 'disk0s2')\n");
1307    printf("       if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL, FSUC_UNJNL);
1308	printf("       name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1309    printf("mount_point_arg:\n");
1310    printf("       required for Mount and Force Mount \n");
1311    printf("Flags:\n");
1312    printf("       required for Mount, Force Mount and Probe\n");
1313    printf("       indicates removable or fixed (for example 'fixed')\n");
1314    printf("       indicates readonly or writable (for example 'readonly')\n");
1315    printf("       indicates suid or nosuid (for example 'suid')\n");
1316    printf("       indicates dev or nodev (for example 'dev')\n");
1317    printf("Examples:\n");
1318    printf("       %s -p disk0s2 fixed writable\n", argv[0]);
1319    printf("       %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv[0]);
1320
1321    return;
1322
1323} /* DoDisplayUsage */
1324
1325
1326/*
1327	GetHFSMountPoint
1328
1329	Given a path to a device, determine if a volume is mounted on that
1330	device.  If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1331	If there is a non-HFS volume, return FSUR_UNRECOGNIZED.  If there is
1332	no volume mounted on the device, set *pathPtr to NULL and return
1333	FSUR_IO_SUCCESS.
1334
1335	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1336*/
1337static int
1338GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr)
1339{
1340	int result;
1341	int i, numMounts;
1342	struct statfs *buf;
1343
1344	/* Assume no mounted volume found */
1345	*pathPtr = NULL;
1346	result = FSUR_IO_SUCCESS;
1347
1348	numMounts = getmntinfo(&buf, MNT_NOWAIT);
1349	if (numMounts == 0)
1350		return FSUR_IO_FAIL;
1351
1352	for (i=0; i<numMounts; ++i) {
1353		if (!strcmp(deviceNamePtr, buf[i].f_mntfromname)) {
1354			/* Found a mounted volume; check the type */
1355			if (!strcmp(buf[i].f_fstypename, "hfs")) {
1356				*pathPtr = buf[i].f_mntonname;
1357				/* result = FSUR_IO_SUCCESS, above */
1358			} else {
1359				result = FSUR_UNRECOGNIZED;
1360			}
1361			break;
1362		}
1363	}
1364
1365	return result;
1366}
1367
1368
1369/*
1370	ReadHeaderBlock
1371
1372	Read the Master Directory Block or Volume Header Block from an HFS,
1373	HFS Plus, or HFSX volume into a caller-supplied buffer.  Return the
1374	offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1375	Return a pointer to the volume UUID in the Finder Info.
1376
1377	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1378*/
1379static int
1380ReadHeaderBlock(int fd, void *bufPtr, off_t *startOffset, VolumeUUID **finderInfoUUIDPtr)
1381{
1382	int result;
1383	HFSMasterDirectoryBlock * mdbPtr;
1384	HFSPlusVolumeHeader * volHdrPtr;
1385
1386	mdbPtr = bufPtr;
1387	volHdrPtr = bufPtr;
1388
1389	/*
1390	 * Read the HFS Master Directory Block or Volume Header from sector 2
1391	 */
1392	*startOffset = 0;
1393	result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1394	if (result != FSUR_IO_SUCCESS)
1395		goto Err_Exit;
1396
1397	/*
1398	 * If this is a wrapped HFS Plus volume, read the Volume Header from
1399	 * sector 2 of the embedded volume.
1400	 */
1401	if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord &&
1402		OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord) {
1403		result = GetEmbeddedHFSPlusVol(mdbPtr, startOffset);
1404		if (result != FSUR_IO_SUCCESS)
1405			goto Err_Exit;
1406		result = readAt(fd, bufPtr, *startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1407		if (result != FSUR_IO_SUCCESS)
1408			goto Err_Exit;
1409	}
1410
1411	/*
1412	 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1413	 * volumes (including wrapped HFS Plus).  Verify the signature and grab the
1414	 * UUID from the Finder Info.
1415	 */
1416	if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord) {
1417	    *finderInfoUUIDPtr = (VolumeUUID *)(&mdbPtr->drFndrInfo[6]);
1418	} else if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord ||
1419				OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) {
1420	    *finderInfoUUIDPtr = (VolumeUUID *)&volHdrPtr->finderInfo[24];
1421	} else {
1422		result = FSUR_UNRECOGNIZED;
1423	}
1424
1425Err_Exit:
1426	return result;
1427}
1428
1429
1430/*
1431	GetVolumeUUIDRaw
1432
1433	Read the UUID from an unmounted volume, by doing direct access to the device.
1434	Assumes the caller has already determined that a volume is not mounted
1435	on the device.
1436
1437	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1438*/
1439static int
1440GetVolumeUUIDRaw(const char *deviceNamePtr, const char *rawName, VolumeUUID *volumeUUIDPtr)
1441{
1442	int fd = 0;
1443	char * bufPtr;
1444	off_t startOffset;
1445	VolumeUUID *finderInfoUUIDPtr;
1446	int result;
1447	int error;
1448
1449	bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
1450	if ( ! bufPtr ) {
1451		result = FSUR_UNRECOGNIZED;
1452		goto Err_Exit;
1453	}
1454
1455	fd = open( deviceNamePtr, O_RDONLY, 0);
1456	if (fd <= 0) {
1457		error = errno;
1458#if TRACE_HFS_UTIL
1459		fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: device (%s)  open failed (errno = %d).\n", deviceNamePtr, errno);
1460#endif
1461		if (error == EBUSY) {
1462			/* If it was busy, then retry, this time using the raw device */
1463			fd = open (rawName, O_RDONLY, 0);
1464			if (fd <= 0) {
1465#if TRACE_HFS_UTIL
1466				fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName, errno);
1467#endif
1468				result = FSUR_IO_FAIL;
1469				goto Err_Exit;
1470			}
1471		}
1472		else {
1473			result = FSUR_IO_FAIL;
1474			goto Err_Exit;
1475		}
1476	}
1477
1478	/*
1479	 * Get the pointer to the volume UUID in the Finder Info
1480	 */
1481	result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr);
1482	if (result != FSUR_IO_SUCCESS)
1483		goto Err_Exit;
1484
1485	/*
1486	 * Copy the volume UUID out of the Finder Info
1487	 */
1488	volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
1489	volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
1490
1491Err_Exit:
1492	if (fd > 0) close(fd);
1493	if (bufPtr) free(bufPtr);
1494
1495#if TRACE_HFS_UTIL
1496	if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result);
1497#endif
1498	return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
1499}
1500
1501
1502/*
1503	SetVolumeUUIDRaw
1504
1505	Write a previously generated UUID to an unmounted volume, by doing direct
1506	access to the device.  Assumes the caller has already determined that a
1507	volume is not mounted on the device.
1508
1509	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1510*/
1511static int
1512SetVolumeUUIDRaw(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr)
1513{
1514	int fd = 0;
1515	char * bufPtr;
1516	off_t startOffset;
1517	VolumeUUID *finderInfoUUIDPtr;
1518	int result;
1519
1520	bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
1521	if ( ! bufPtr ) {
1522		result = FSUR_UNRECOGNIZED;
1523		goto Err_Exit;
1524	}
1525
1526	fd = open( deviceNamePtr, O_RDWR, 0);
1527	if (fd <= 0) {
1528#if TRACE_HFS_UTIL
1529		fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno);
1530#endif
1531		result = FSUR_IO_FAIL;
1532		goto Err_Exit;
1533	}
1534
1535	/*
1536	 * Get the pointer to the volume UUID in the Finder Info
1537	 */
1538	result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr);
1539	if (result != FSUR_IO_SUCCESS)
1540		goto Err_Exit;
1541
1542	/*
1543	 * Update the UUID in the Finder Info
1544	 */
1545	finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
1546	finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
1547
1548	/*
1549	 * Write the modified MDB or VHB back to disk
1550	 */
1551	result = writeAt(fd, bufPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
1552
1553Err_Exit:
1554	if (fd > 0) close(fd);
1555	if (bufPtr) free(bufPtr);
1556
1557#if TRACE_HFS_UTIL
1558	if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result);
1559#endif
1560	return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
1561}
1562
1563
1564/*
1565	GetVolumeUUIDAttr
1566
1567	Read the UUID from a mounted volume, by calling getattrlist().
1568	Assumes the path is the mount point of an HFS volume.
1569
1570	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1571*/
1572static int
1573GetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr)
1574{
1575	struct attrlist alist;
1576	struct FinderAttrBuf volFinderInfo;
1577	VolumeUUID *finderInfoUUIDPtr;
1578	int result;
1579
1580	/* Set up the attrlist structure to get the volume's Finder Info */
1581	alist.bitmapcount = 5;
1582	alist.reserved = 0;
1583	alist.commonattr = ATTR_CMN_FNDRINFO;
1584	alist.volattr = ATTR_VOL_INFO;
1585	alist.dirattr = 0;
1586	alist.fileattr = 0;
1587	alist.forkattr = 0;
1588
1589	/* Get the Finder Info */
1590	result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
1591	if (result) {
1592		result = FSUR_IO_FAIL;
1593		goto Err_Exit;
1594	}
1595
1596	/* Copy the UUID from the Finder Into to caller's buffer */
1597	finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
1598	volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
1599	volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
1600	result = FSUR_IO_SUCCESS;
1601
1602Err_Exit:
1603	return result;
1604}
1605
1606
1607/*
1608	SetVolumeUUIDAttr
1609
1610	Write a UUID to a mounted volume, by calling setattrlist().
1611	Assumes the path is the mount point of an HFS volume.
1612
1613	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1614*/
1615static int
1616SetVolumeUUIDAttr(const char *path, VolumeUUID *volumeUUIDPtr)
1617{
1618	struct attrlist alist;
1619	struct FinderAttrBuf volFinderInfo;
1620	VolumeUUID *finderInfoUUIDPtr;
1621	int result;
1622
1623	/* Set up the attrlist structure to get the volume's Finder Info */
1624	alist.bitmapcount = 5;
1625	alist.reserved = 0;
1626	alist.commonattr = ATTR_CMN_FNDRINFO;
1627	alist.volattr = ATTR_VOL_INFO;
1628	alist.dirattr = 0;
1629	alist.fileattr = 0;
1630	alist.forkattr = 0;
1631
1632	/* Get the Finder Info */
1633	result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
1634	if (result) {
1635		result = FSUR_IO_FAIL;
1636		goto Err_Exit;
1637	}
1638
1639	/* Update the UUID in the Finder Info */
1640	finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
1641	finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
1642	finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
1643
1644	/* Write the Finder Info back to the volume */
1645	result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
1646	if (result) {
1647		result = FSUR_IO_FAIL;
1648		goto Err_Exit;
1649	}
1650
1651	result = FSUR_IO_SUCCESS;
1652
1653Err_Exit:
1654	return result;
1655}
1656
1657
1658/*
1659	GetVolumeUUID
1660
1661	Return the UUID of an HFS, HFS Plus or HFSX volume.  If there is no UUID and
1662	we were asked to generate one, then generate a new UUID and write it to the
1663	volume.
1664
1665	Determine whether an HFS volume is mounted on the given device.  If so, we
1666	need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1667	the filesystem.  If there is no mounted volume, then do direct device access
1668	with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1669
1670	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1671 */
1672
1673static int
1674GetVolumeUUID(const char *deviceNamePtr, const char *rawName, VolumeUUID *volumeUUIDPtr, boolean_t generate)
1675{
1676	int result;
1677	char *path = NULL;
1678
1679	/*
1680	 * Determine whether a volume is mounted on this device.  If it is HFS, then
1681	 * get the mount point's path.  If it is non-HFS, then we can exit immediately
1682	 * with FSUR_UNRECOGNIZED.
1683	 */
1684	result = GetHFSMountPoint(deviceNamePtr, &path);
1685	if (result != FSUR_IO_SUCCESS)
1686		goto Err_Exit;
1687
1688	/*
1689	 * Get any existing UUID.
1690	 */
1691	if (path)
1692		result = GetVolumeUUIDAttr(path, volumeUUIDPtr);
1693	else
1694		result = GetVolumeUUIDRaw(deviceNamePtr, rawName, volumeUUIDPtr);
1695	if (result != FSUR_IO_SUCCESS)
1696		goto Err_Exit;
1697
1698	/*
1699	 * If there was no valid UUID, and we were asked to generate one, then
1700	 * generate it and write it back to disk.
1701	 */
1702	if (generate && (volumeUUIDPtr->v.high == 0 || volumeUUIDPtr->v.low == 0)) {
1703		GenerateVolumeUUID(volumeUUIDPtr);
1704		if (path)
1705			result = SetVolumeUUIDAttr(path, volumeUUIDPtr);
1706		else
1707			result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1708		/* Fall through to Err_Exit */
1709	}
1710
1711Err_Exit:
1712	return result;
1713}
1714
1715
1716
1717/*
1718	SetVolumeUUID
1719
1720	Write a UUID to an HFS, HFS Plus or HFSX volume.
1721
1722	Determine whether an HFS volume is mounted on the given device.  If so, we
1723	need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1724	If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1725
1726	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1727 */
1728static int
1729SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr) {
1730	int result;
1731	char *path = NULL;
1732
1733	/*
1734	 * Determine whether a volume is mounted on this device.  If it is HFS, then
1735	 * get the mount point's path.  If it is non-HFS, then we can exit immediately
1736	 * with FSUR_UNRECOGNIZED.
1737	 */
1738	result = GetHFSMountPoint(deviceNamePtr, &path);
1739	if (result != FSUR_IO_SUCCESS)
1740		goto Err_Exit;
1741
1742	/*
1743	 * Update the UUID.
1744	 */
1745	if (path)
1746		result = SetVolumeUUIDAttr(path, volumeUUIDPtr);
1747	else
1748		result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr);
1749
1750Err_Exit:
1751	return result;
1752}
1753
1754
1755
1756/*
1757 --	GetEmbeddedHFSPlusVol
1758 --
1759 --	In: hfsMasterDirectoryBlockPtr
1760 --	Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1761 				(that is, 2 blocks before the volume header)
1762 --
1763 */
1764
1765static int
1766GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr)
1767{
1768    int		result = FSUR_IO_SUCCESS;
1769    u_int32_t	allocationBlockSize, firstAllocationBlock, startBlock, blockCount;
1770
1771    if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drSigWord) != kHFSSigWord) {
1772        result = FSUR_UNRECOGNIZED;
1773        goto Return;
1774    }
1775
1776    allocationBlockSize = OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr->drAlBlkSiz);
1777    firstAllocationBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drAlBlSt);
1778
1779    if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedSigWord) != kHFSPlusSigWord) {
1780        result = FSUR_UNRECOGNIZED;
1781        goto Return;
1782    }
1783
1784    startBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.startBlock);
1785    blockCount = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.blockCount);
1786
1787    if ( startOffsetPtr )
1788        *startOffsetPtr = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) +
1789        	((u_int64_t)firstAllocationBlock * (u_int64_t)HFS_BLOCK_SIZE);
1790
1791Return:
1792        return result;
1793
1794}
1795
1796
1797
1798/*
1799 --	GetNameFromHFSPlusVolumeStartingAt
1800 --
1801 --	Caller's responsibility to allocate and release memory for the converted string.
1802 --
1803 --	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1804 */
1805
1806static int
1807GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o)
1808{
1809    int					result = FSUR_IO_SUCCESS;
1810    u_int32_t				blockSize;
1811    char			*	bufPtr = NULL;
1812    HFSPlusVolumeHeader		*	volHdrPtr;
1813    BTNodeDescriptor		*	bTreeNodeDescriptorPtr;
1814    u_int32_t				catalogNodeSize;
1815	u_int32_t leafNode;
1816	u_int32_t catalogExtCount;
1817	HFSPlusExtentDescriptor *catalogExtents = NULL;
1818
1819    volHdrPtr = (HFSPlusVolumeHeader *)malloc(HFS_BLOCK_SIZE);
1820    if ( ! volHdrPtr ) {
1821        result = FSUR_IO_FAIL;
1822        goto Return;
1823    }
1824
1825    /*
1826     * Read the Volume Header
1827     * (This is a little redundant for a pure, unwrapped HFS+ volume)
1828     */
1829    result = readAt( fd, volHdrPtr, hfsPlusVolumeOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE );
1830    if (result == FSUR_IO_FAIL) {
1831#if TRACE_HFS_UTIL
1832        fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1833#endif
1834        goto Return; // return FSUR_IO_FAIL
1835    }
1836
1837    /* Verify that it is an HFS+ volume. */
1838
1839    if (OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSPlusSigWord &&
1840        OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSXSigWord) {
1841        result = FSUR_IO_FAIL;
1842#if TRACE_HFS_UTIL
1843        fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1844#endif
1845        goto Return;
1846    }
1847
1848    blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize);
1849    catalogExtents = (HFSPlusExtentDescriptor *) malloc(sizeof(HFSPlusExtentRecord));
1850    if ( ! catalogExtents ) {
1851        result = FSUR_IO_FAIL;
1852        goto Return;
1853    }
1854	bcopy(volHdrPtr->catalogFile.extents, catalogExtents, sizeof(HFSPlusExtentRecord));
1855	catalogExtCount = kHFSPlusExtentDensity;
1856
1857	/* if there are overflow catalog extents, then go get them */
1858	if (OSSwapBigToHostInt32(catalogExtents[7].blockCount) != 0) {
1859		result = GetCatalogOverflowExtents(fd, hfsPlusVolumeOffset, volHdrPtr, &catalogExtents, &catalogExtCount);
1860		if (result != FSUR_IO_SUCCESS)
1861			goto Return;
1862	}
1863
1864	/* Read the header node of the catalog B-Tree */
1865
1866	result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize,
1867							catalogExtCount, catalogExtents,
1868							&catalogNodeSize, &leafNode);
1869	if (result != FSUR_IO_SUCCESS)
1870        goto Return;
1871
1872	/* Read the first leaf node of the catalog b-tree */
1873
1874    bufPtr = (char *)malloc(catalogNodeSize);
1875    if ( ! bufPtr ) {
1876        result = FSUR_IO_FAIL;
1877        goto Return;
1878    }
1879
1880    bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
1881
1882	result = ReadFile(fd, bufPtr, (off_t) leafNode * (off_t) catalogNodeSize, catalogNodeSize,
1883						hfsPlusVolumeOffset, blockSize,
1884						catalogExtCount, catalogExtents);
1885    if (result == FSUR_IO_FAIL) {
1886#if TRACE_HFS_UTIL
1887        fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n");
1888#endif
1889        goto Return; // return FSUR_IO_FAIL
1890    }
1891
1892    {
1893        u_int16_t			*	v;
1894        char			*	p;
1895        HFSPlusCatalogKey	*	k;
1896	CFStringRef cfstr;
1897
1898        if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords) < 1) {
1899            result = FSUR_IO_FAIL;
1900#if TRACE_HFS_UTIL
1901			fprintf(stderr, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1902#endif
1903            goto Return;
1904        }
1905
1906	// Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1907
1908        p = bufPtr + catalogNodeSize - sizeof(u_int16_t); // pointer arithmetic in bytes
1909        v = (u_int16_t *)p;
1910
1911	// Get a pointer to the first record.
1912
1913        p = bufPtr + OSSwapBigToHostInt16(*v); // pointer arithmetic in bytes
1914        k = (HFSPlusCatalogKey *)p;
1915
1916	// There should be only one record whose parent is the root parent.  It should be the first record.
1917
1918        if (OSSwapBigToHostInt32(k->parentID) != kHFSRootParentID) {
1919            result = FSUR_IO_FAIL;
1920#if TRACE_HFS_UTIL
1921            fprintf(stderr, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1922#endif
1923            goto Return;
1924        }
1925
1926	if ((OSSwapBigToHostInt16(k->nodeName.length) >
1927		(sizeof(k->nodeName.unicode) / sizeof(k->nodeName.unicode[0]))) ||
1928		OSSwapBigToHostInt16(k->nodeName.length) > 255) {
1929		result = FSUR_IO_FAIL;
1930#if TRACE_HFS_UTIL
1931		fprintf(stderr, "hfs.util: ERROR:  k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k->nodeName.length));
1932#endif
1933		goto Return;
1934	}
1935
1936	/* Extract the name of the root directory */
1937
1938	{
1939	    HFSUniStr255 *swapped;
1940	    int i;
1941
1942	    swapped = (HFSUniStr255 *)malloc(sizeof(HFSUniStr255));
1943	    if (swapped == NULL) {
1944		result = FSUR_IO_FAIL;
1945		goto Return;
1946	    }
1947	    swapped->length = OSSwapBigToHostInt16(k->nodeName.length);
1948
1949	    for (i=0; i<swapped->length; i++) {
1950		swapped->unicode[i] = OSSwapBigToHostInt16(k->nodeName.unicode[i]);
1951	    }
1952	    swapped->unicode[i] = 0;
1953	    cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, swapped->unicode, swapped->length);
1954	    (void) CFStringGetCString(cfstr, (char *)name_o, NAME_MAX * 3 + 1, kCFStringEncodingUTF8);
1955	    CFRelease(cfstr);
1956	    free(swapped);
1957	}
1958    }
1959
1960    result = FSUR_IO_SUCCESS;
1961
1962Return:
1963	if (volHdrPtr)
1964		free((char*) volHdrPtr);
1965
1966	if (catalogExtents)
1967		free((char*) catalogExtents);
1968
1969	if (bufPtr)
1970		free((char*)bufPtr);
1971
1972    return result;
1973
1974} /* GetNameFromHFSPlusVolumeStartingAt */
1975
1976
1977typedef struct {
1978	BTNodeDescriptor	node;
1979	BTHeaderRec		header;
1980} __attribute__((aligned(2), packed)) HeaderRec, *HeaderPtr;
1981
1982/*
1983 --
1984 --
1985 --	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1986 --
1987 */
1988static int
1989GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize,
1990				u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
1991				u_int32_t *nodeSize, u_int32_t *firstLeafNode)
1992{
1993	int result;
1994	HeaderRec * bTreeHeaderPtr = NULL;
1995
1996	bTreeHeaderPtr = (HeaderRec *) malloc(HFS_BLOCK_SIZE);
1997	if (bTreeHeaderPtr == NULL)
1998		return (FSUR_IO_FAIL);
1999
2000	/* Read the b-tree header node */
2001
2002	result = ReadFile(fd, bTreeHeaderPtr, 0, HFS_BLOCK_SIZE,
2003					hfsPlusVolumeOffset, blockSize,
2004					extentCount, extentList);
2005	if ( result == FSUR_IO_FAIL ) {
2006#if TRACE_HFS_UTIL
2007		fprintf(stderr, "hfs.util: ERROR: reading header node failed\n");
2008#endif
2009		goto free;
2010	}
2011
2012	if ( bTreeHeaderPtr->node.kind != kBTHeaderNode ) {
2013		result = FSUR_IO_FAIL;
2014#if TRACE_HFS_UTIL
2015		fprintf(stderr, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
2016#endif
2017		goto free;
2018	}
2019
2020	*nodeSize = OSSwapBigToHostInt16(bTreeHeaderPtr->header.nodeSize);
2021
2022	if (OSSwapBigToHostInt32(bTreeHeaderPtr->header.leafRecords) == 0)
2023		*firstLeafNode = 0;
2024	else
2025		*firstLeafNode = OSSwapBigToHostInt32(bTreeHeaderPtr->header.firstLeafNode);
2026
2027free:;
2028	free((char*) bTreeHeaderPtr);
2029
2030	return result;
2031
2032} /* GetBTreeNodeInfo */
2033
2034
2035/*
2036 --
2037 --
2038 --	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2039 --
2040 */
2041static int
2042GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset,
2043		HFSPlusVolumeHeader *volHdrPtr,
2044		HFSPlusExtentDescriptor **catalogExtents,
2045		u_int32_t *catalogExtCount)
2046{
2047	off_t offset;
2048	u_int32_t numRecords;
2049	u_int32_t nodeSize;
2050	u_int32_t leafNode;
2051	u_int32_t blockSize;
2052    BTNodeDescriptor * bTreeNodeDescriptorPtr;
2053	HFSPlusExtentDescriptor * extents;
2054	size_t listsize;
2055    char *	bufPtr = NULL;
2056	int i;
2057	int result;
2058
2059	blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize);
2060	listsize = *catalogExtCount * sizeof(HFSPlusExtentDescriptor);
2061	extents = *catalogExtents;
2062	offset = (off_t)OSSwapBigToHostInt32(volHdrPtr->extentsFile.extents[0].startBlock) *
2063		    (off_t)blockSize;
2064
2065	/* Read the header node of the extents B-Tree */
2066
2067	result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize,
2068			kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents,
2069		    &nodeSize, &leafNode);
2070	if (result != FSUR_IO_SUCCESS || leafNode == 0)
2071		goto Return;
2072
2073	/* Calculate the logical position of the first leaf node */
2074
2075	offset = (off_t) leafNode * (off_t) nodeSize;
2076
2077	/* Read the first leaf node of the extents b-tree */
2078
2079    bufPtr = (char *)malloc(nodeSize);
2080	if (! bufPtr) {
2081		result = FSUR_IO_FAIL;
2082		goto Return;
2083	}
2084
2085	bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
2086
2087again:
2088	result = ReadFile(fd, bufPtr, offset, nodeSize,
2089					hfsPlusVolumeOffset, blockSize,
2090					kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents);
2091	if ( result == FSUR_IO_FAIL ) {
2092#if TRACE_HFS_UTIL
2093		fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n");
2094#endif
2095		goto Return;
2096	}
2097
2098	if (bTreeNodeDescriptorPtr->kind != kBTLeafNode) {
2099		result = FSUR_IO_FAIL;
2100		goto Return;
2101	}
2102
2103	numRecords = OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords);
2104	for (i = 1; i <= numRecords; ++i) {
2105		u_int16_t * v;
2106		char * p;
2107		HFSPlusExtentKey * k;
2108
2109		/*
2110		 * Get the offset (in bytes) of the record from the
2111		 * list of offsets at the end of the node
2112		 */
2113		p = bufPtr + nodeSize - (sizeof(u_int16_t) * i);
2114		v = (u_int16_t *)p;
2115
2116		/* Get a pointer to the record */
2117
2118		p = bufPtr + OSSwapBigToHostInt16(*v); /* pointer arithmetic in bytes */
2119		k = (HFSPlusExtentKey *)p;
2120
2121		if (OSSwapBigToHostInt32(k->fileID) != kHFSCatalogFileID)
2122			goto Return;
2123
2124		/* grow list and copy additional extents */
2125		listsize += sizeof(HFSPlusExtentRecord);
2126		extents = (HFSPlusExtentDescriptor *) realloc(extents, listsize);
2127		bcopy(p + OSSwapBigToHostInt16(k->keyLength) + sizeof(u_int16_t),
2128			&extents[*catalogExtCount], sizeof(HFSPlusExtentRecord));
2129
2130		*catalogExtCount += kHFSPlusExtentDensity;
2131		*catalogExtents = extents;
2132	}
2133
2134	if ((leafNode = OSSwapBigToHostInt32(bTreeNodeDescriptorPtr->fLink)) != 0) {
2135
2136		offset = (off_t) leafNode * (off_t) nodeSize;
2137
2138		goto again;
2139	}
2140
2141Return:;
2142	if (bufPtr)
2143		free(bufPtr);
2144
2145	return (result);
2146}
2147
2148
2149
2150/*
2151 *	LogicalToPhysical - Map a logical file position and size to volume-relative physical
2152 *	position and number of contiguous bytes at that position.
2153 *
2154 *	Inputs:
2155 *		logicalOffset	Logical offset in bytes from start of file
2156 *		length			Maximum number of bytes to map
2157 *		blockSize		Number of bytes per allocation block
2158 *		extentCount		Number of extents in file
2159 *		extentList		The file's extents
2160 *
2161 *	Outputs:
2162 *		physicalOffset	Physical offset in bytes from start of volume
2163 *		availableBytes	Number of bytes physically contiguous (up to length)
2164 *
2165 *	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2166 */
2167static int	LogicalToPhysical(off_t offset, ssize_t length, u_int32_t blockSize,
2168							u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList,
2169							off_t *physicalOffset, ssize_t *availableBytes)
2170{
2171	off_t		temp;
2172	u_int32_t	logicalBlock;
2173	u_int32_t	extent;
2174	u_int32_t	blockCount = 0;
2175
2176	/* Determine allocation block containing logicalOffset */
2177	logicalBlock = offset / blockSize;	/* This can't overflow for valid volumes */
2178	offset %= blockSize;	/* Offset from start of allocation block */
2179
2180	/* Find the extent containing logicalBlock */
2181	for (extent = 0; extent < extentCount; ++extent)
2182	{
2183		blockCount = OSSwapBigToHostInt32(extentList[extent].blockCount);
2184
2185		if (blockCount == 0)
2186			return FSUR_IO_FAIL;	/* Tried to map past physical end of file */
2187
2188		if (logicalBlock < blockCount)
2189			break;				/* Found it! */
2190
2191		logicalBlock -= blockCount;
2192	}
2193
2194	if (extent >= extentCount)
2195		return FSUR_IO_FAIL;		/* Tried to map past physical end of file */
2196
2197	/*
2198	 *	When we get here, extentList[extent] is the extent containing logicalOffset.
2199	 *	The desired allocation block is logicalBlock blocks into the extent.
2200	 */
2201
2202	/* Compute the physical starting position */
2203	temp = OSSwapBigToHostInt32(extentList[extent].startBlock) + logicalBlock;	/* First physical block */
2204	temp *= blockSize;	/* Byte offset of first physical block */
2205	*physicalOffset = temp + offset;
2206
2207	/* Compute the available contiguous bytes. */
2208	temp = blockCount - logicalBlock;	/* Number of blocks available in extent */
2209	temp *= blockSize;
2210	temp -= offset;						/* Number of bytes available */
2211
2212	if (temp < length)
2213		*availableBytes = temp;
2214	else
2215		*availableBytes = length;
2216
2217	return FSUR_IO_SUCCESS;
2218}
2219
2220
2221
2222/*
2223 *	ReadFile - Read bytes from a file.  Handles cases where the starting and/or
2224 *	ending position are not allocation or device block aligned.
2225 *
2226 *	Inputs:
2227 *		fd			Descriptor for reading the volume
2228 *		buffer		The bytes are read into here
2229 *		offset		Offset in file to start reading
2230 *		length		Number of bytes to read
2231 *		volOffset	Byte offset from start of device to start of volume
2232 *		blockSize	Number of bytes per allocation block
2233 *		extentCount	Number of extents in file
2234 *		extentList	The file's exents
2235 *
2236 *	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2237 */
2238static int	ReadFile(int fd, void *buffer, off_t offset, ssize_t length,
2239					off_t volOffset, u_int32_t blockSize,
2240					u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList)
2241{
2242	int		result = FSUR_IO_SUCCESS;
2243	off_t	physOffset;
2244	ssize_t	physLength;
2245
2246	while (length > 0)
2247	{
2248		result = LogicalToPhysical(offset, length, blockSize, extentCount, extentList,
2249									&physOffset, &physLength);
2250		if (result != FSUR_IO_SUCCESS)
2251			break;
2252
2253		result = readAt(fd, buffer, volOffset+physOffset, physLength);
2254		if (result != FSUR_IO_SUCCESS)
2255			break;
2256
2257		length -= physLength;
2258		offset += physLength;
2259		buffer = (char *) buffer + physLength;
2260	}
2261
2262	return result;
2263}
2264
2265/*
2266 --	readAt = lseek() + read()
2267 --
2268 --	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2269 --
2270 */
2271
2272static ssize_t
2273readAt( int fd, void * bufPtr, off_t offset, ssize_t length )
2274{
2275    int			blocksize;
2276    off_t		lseekResult;
2277    ssize_t		readResult;
2278    void *		rawData = NULL;
2279    off_t		rawOffset;
2280    ssize_t		rawLength;
2281    ssize_t		dataOffset = 0;
2282    int			result = FSUR_IO_SUCCESS;
2283
2284    if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) {
2285#if TRACE_HFS_UTIL
2286    	fprintf(stderr, "hfs.util: readAt: couldn't determine block size of device.\n");
2287#endif
2288		result = FSUR_IO_FAIL;
2289		goto Return;
2290    }
2291    /* put offset and length in terms of device blocksize */
2292    rawOffset = offset / blocksize * blocksize;
2293    dataOffset = offset - rawOffset;
2294    rawLength = ((length + dataOffset + blocksize - 1) / blocksize) * blocksize;
2295    rawData = malloc(rawLength);
2296    if (rawData == NULL) {
2297		result = FSUR_IO_FAIL;
2298		goto Return;
2299    }
2300
2301    lseekResult = lseek( fd, rawOffset, SEEK_SET );
2302    if ( lseekResult != rawOffset ) {
2303        result = FSUR_IO_FAIL;
2304        goto Return;
2305    }
2306
2307    readResult = read(fd, rawData, rawLength);
2308    if ( readResult != rawLength ) {
2309#if TRACE_HFS_UTIL
2310    		fprintf(stderr, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno);
2311#endif
2312        result = FSUR_IO_FAIL;
2313        goto Return;
2314    }
2315    bcopy(rawData + dataOffset, bufPtr, length);
2316
2317Return:
2318    if (rawData) {
2319        free(rawData);
2320    }
2321    return result;
2322
2323} /* readAt */
2324
2325/*
2326 --	writeAt = lseek() + write()
2327 --
2328 --	Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2329 --
2330 */
2331
2332static ssize_t
2333writeAt( int fd, void * bufPtr, off_t offset, ssize_t length )
2334{
2335    int			blocksize;
2336    off_t		deviceoffset;
2337    ssize_t		bytestransferred;
2338    void *		rawData = NULL;
2339    off_t		rawOffset;
2340    ssize_t		rawLength;
2341    ssize_t		dataOffset = 0;
2342    int			result = FSUR_IO_SUCCESS;
2343
2344    if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) {
2345#if TRACE_HFS_UTIL
2346    	fprintf(stderr, "hfs.util: couldn't determine block size of device.\n");
2347#endif
2348		result = FSUR_IO_FAIL;
2349		goto Return;
2350    }
2351    /* put offset and length in terms of device blocksize */
2352    rawOffset = offset / blocksize * blocksize;
2353    dataOffset = offset - rawOffset;
2354    rawLength = ((length + dataOffset + blocksize - 1) / blocksize) * blocksize;
2355    rawData = malloc(rawLength);
2356    if (rawData == NULL) {
2357		result = FSUR_IO_FAIL;
2358		goto Return;
2359    }
2360
2361    deviceoffset = lseek( fd, rawOffset, SEEK_SET );
2362    if ( deviceoffset != rawOffset ) {
2363        result = FSUR_IO_FAIL;
2364        goto Return;
2365    }
2366
2367	/* If the write isn't block-aligned, read the existing data before writing the new data: */
2368	if (((rawOffset % blocksize) != 0) || ((rawLength % blocksize) != 0)) {
2369		bytestransferred = read(fd, rawData, rawLength);
2370	    if ( bytestransferred != rawLength ) {
2371#if TRACE_HFS_UTIL
2372    		fprintf(stderr, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno);
2373#endif
2374	        result = FSUR_IO_FAIL;
2375	        goto Return;
2376	    }
2377	}
2378
2379    bcopy(bufPtr, rawData + dataOffset, length);	/* Copy in the new data */
2380
2381    deviceoffset = lseek( fd, rawOffset, SEEK_SET );
2382    if ( deviceoffset != rawOffset ) {
2383        result = FSUR_IO_FAIL;
2384        goto Return;
2385    }
2386
2387    bytestransferred = write(fd, rawData, rawLength);
2388    if ( bytestransferred != rawLength ) {
2389#if TRACE_HFS_UTIL
2390    		fprintf(stderr, "writeAt: attempt to write data to device failed?!");
2391#endif
2392        result = FSUR_IO_FAIL;
2393        goto Return;
2394    }
2395
2396Return:
2397    if (rawData) free(rawData);
2398
2399    return result;
2400
2401} /* writeAt */
2402
2403
2404/*
2405 * Get kernel's encoding bias.
2406 */
2407static int
2408GetEncodingBias()
2409{
2410        int mib[3];
2411        size_t buflen = sizeof(int);
2412        struct vfsconf vfc;
2413        int hint = 0;
2414
2415        if (getvfsbyname("hfs", &vfc) < 0)
2416		goto error;
2417
2418        mib[0] = CTL_VFS;
2419        mib[1] = vfc.vfc_typenum;
2420        mib[2] = HFS_ENCODINGBIAS;
2421
2422	if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0)
2423 		goto error;
2424	return (hint);
2425error:
2426	return (-1);
2427}
2428
2429/******************************************************************************
2430 *
2431 *  V O L U M E   S T A T U S   D A T A B A S E   R O U T I N E S
2432 *
2433 *****************************************************************************/
2434
2435#define DBHANDLESIGNATURE 0x75917737
2436
2437/* Flag values for operation options: */
2438#define DBMARKPOSITION 1
2439
2440static char gVSDBPath[] = "/var/db/volinfo.database";
2441
2442#define MAXIOMALLOC 16384
2443
2444/* Database layout: */
2445
2446struct VSDBKey {
2447	char uuid[16];
2448};
2449
2450struct VSDBRecord {
2451	char statusFlags[8];
2452};
2453
2454struct VSDBEntry {
2455	struct VSDBKey key;
2456	char keySeparator;
2457	char space;
2458	struct VSDBRecord record;
2459	char terminator;
2460};
2461
2462#define DBKEYSEPARATOR ':'
2463#define DBBLANKSPACE ' '
2464#define DBRECORDTERMINATOR '\n'
2465
2466/* In-memory data structures: */
2467
2468struct VSDBState {
2469    unsigned long signature;
2470    int dbfile;
2471    int dbmode;
2472    off_t recordPosition;
2473};
2474
2475typedef struct VSDBState *VSDBStatePtr;
2476
2477
2478
2479/* Internal function prototypes: */
2480static int LockDB(VSDBStatePtr dbstateptr, int lockmode);
2481static int UnlockDB(VSDBStatePtr dbstateptr);
2482
2483static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *dbentry, unsigned long options);
2484static int AddVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2485static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2486static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
2487static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2);
2488
2489static void FormatULong(unsigned long u, char *s);
2490static void FormatUUID(VolumeUUID *volumeID, char *UUIDField);
2491static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey);
2492static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord);
2493static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry);
2494static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits);
2495
2496
2497
2498/******************************************************************************
2499 *
2500 *  P U B L I S H E D   I N T E R F A C E   R O U T I N E S
2501 *
2502 *****************************************************************************/
2503
2504void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
2505	SHA_CTX context;
2506	char randomInputBuffer[26];
2507	unsigned char digest[20];
2508	time_t now;
2509	clock_t uptime;
2510	int mib[2];
2511	int sysdata;
2512	char sysctlstring[128];
2513	size_t datalen;
2514	double sysloadavg[3];
2515	struct vmtotal sysvmtotal;
2516
2517	do {
2518		/* Initialize the SHA-1 context for processing: */
2519		SHA1_Init(&context);
2520
2521		/* Now process successive bits of "random" input to seed the process: */
2522
2523		/* The current system's uptime: */
2524		uptime = clock();
2525		SHA1_Update(&context, &uptime, sizeof(uptime));
2526
2527		/* The kernel's boot time: */
2528		mib[0] = CTL_KERN;
2529		mib[1] = KERN_BOOTTIME;
2530		datalen = sizeof(sysdata);
2531		sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2532		SHA1_Update(&context, &sysdata, datalen);
2533
2534		/* The system's host id: */
2535		mib[0] = CTL_KERN;
2536		mib[1] = KERN_HOSTID;
2537		datalen = sizeof(sysdata);
2538		sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
2539		SHA1_Update(&context, &sysdata, datalen);
2540
2541		/* The system's host name: */
2542		mib[0] = CTL_KERN;
2543		mib[1] = KERN_HOSTNAME;
2544		datalen = sizeof(sysctlstring);
2545		sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2546		SHA1_Update(&context, sysctlstring, datalen);
2547
2548		/* The running kernel's OS release string: */
2549		mib[0] = CTL_KERN;
2550		mib[1] = KERN_OSRELEASE;
2551		datalen = sizeof(sysctlstring);
2552		sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2553		SHA1_Update(&context, sysctlstring, datalen);
2554
2555		/* The running kernel's version string: */
2556		mib[0] = CTL_KERN;
2557		mib[1] = KERN_VERSION;
2558		datalen = sizeof(sysctlstring);
2559		sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
2560		SHA1_Update(&context, sysctlstring, datalen);
2561
2562		/* The system's load average: */
2563		datalen = sizeof(sysloadavg);
2564		getloadavg(sysloadavg, 3);
2565		SHA1_Update(&context, &sysloadavg, datalen);
2566
2567		/* The system's VM statistics: */
2568		mib[0] = CTL_VM;
2569		mib[1] = VM_METER;
2570		datalen = sizeof(sysvmtotal);
2571		sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
2572		SHA1_Update(&context, &sysvmtotal, datalen);
2573
2574		/* The current GMT (26 ASCII characters): */
2575		time(&now);
2576		strncpy(randomInputBuffer, asctime(gmtime(&now)), 26);	/* "Mon Mar 27 13:46:26 2000" */
2577		SHA1_Update(&context, randomInputBuffer, 26);
2578
2579		/* Pad the accumulated input and extract the final digest hash: */
2580		SHA1_Final(digest, &context);
2581
2582		memcpy(newVolumeID, digest, sizeof(*newVolumeID));
2583	} while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0));
2584}
2585
2586
2587
2588void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID) {
2589	int i;
2590	char c;
2591	u_int32_t nextdigit;
2592	u_int32_t high = 0;
2593	u_int32_t low = 0;
2594	u_int32_t carry;
2595
2596	for (i = 0; (i < VOLUMEUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) {
2597		if ((c >= '0') && (c <= '9')) {
2598			nextdigit = c - '0';
2599		} else if ((c >= 'A') && (c <= 'F')) {
2600			nextdigit = c - 'A' + 10;
2601		} else if ((c >= 'a') && (c <= 'f')) {
2602			nextdigit = c - 'a' + 10;
2603		} else {
2604			nextdigit = 0;
2605		}
2606		carry = ((low & 0xF0000000) >> 28) & 0x0000000F;
2607		high = (high << 4) | carry;
2608		low = (low << 4) | nextdigit;
2609	}
2610
2611	volumeID->v.high = high;
2612	volumeID->v.low = low;
2613}
2614
2615
2616
2617void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) {
2618	FormatUUID(volumeID, UUIDString);
2619	*(UUIDString+16) = (char)0;		/* Append a terminating null character */
2620}
2621
2622
2623
2624int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
2625	VSDBStatePtr dbstateptr;
2626
2627	*DBHandlePtr = NULL;
2628
2629	dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr));
2630	if (dbstateptr == NULL) {
2631		return ENOMEM;
2632	}
2633
2634	dbstateptr->dbmode = O_RDWR;
2635	dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2636	if (dbstateptr->dbfile == -1) {
2637		/*
2638		   The file couldn't be opened for read/write access:
2639		   try read-only access before giving up altogether.
2640		 */
2641		dbstateptr->dbmode = O_RDONLY;
2642		dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
2643		if (dbstateptr->dbfile == -1) {
2644			return errno;
2645		}
2646	}
2647
2648	dbstateptr->signature = DBHANDLESIGNATURE;
2649	*DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
2650	return 0;
2651}
2652
2653
2654
2655int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus) {
2656	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2657	struct VSDBEntry dbentry;
2658	int result;
2659
2660	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2661
2662	if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
2663
2664	if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
2665		goto ErrExit;
2666	}
2667	*VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
2668
2669	result = 0;
2670
2671ErrExit:
2672	UnlockDB(dbstateptr);
2673	return result;
2674}
2675
2676
2677
2678int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus) {
2679	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2680	struct VSDBEntry dbentry;
2681	int result;
2682
2683	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2684	if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
2685
2686	if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
2687
2688	FormatDBEntry(volumeID, VolumeStatus, &dbentry);
2689	if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) == 0) {
2690#if DEBUG_TRACE
2691		fprintf(stderr,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2692#endif
2693		result = UpdateVolumeRecord(dbstateptr, &dbentry);
2694	} else if (result == -1) {
2695#if DEBUG_TRACE
2696		fprintf(stderr,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2697#endif
2698		result = AddVolumeRecord(dbstateptr, &dbentry);
2699	} else {
2700		goto ErrExit;
2701	}
2702
2703	fsync(dbstateptr->dbfile);
2704
2705	result = 0;
2706
2707ErrExit:
2708	UnlockDB(dbstateptr);
2709	return result;
2710}
2711
2712
2713
2714int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
2715	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2716	struct stat dbinfo;
2717	int result;
2718	unsigned long iobuffersize;
2719	void *iobuffer = NULL;
2720	off_t dataoffset;
2721	unsigned long iotransfersize;
2722	unsigned long bytestransferred;
2723
2724	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2725
2726	if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
2727
2728	if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
2729#if DEBUG_TRACE
2730		fprintf(stderr, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result);
2731#endif
2732		if (result == -1) result = 0;	/* Entry wasn't in the database to begin with? */
2733		goto StdEdit;
2734	} else {
2735#if DEBUG_TRACE
2736		fprintf(stderr, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2737#endif
2738		if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit;
2739		if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry)) <= MAXIOMALLOC) {
2740			iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry);
2741		} else {
2742			iobuffersize = MAXIOMALLOC;
2743		}
2744#if DEBUG_TRACE
2745		fprintf(stderr, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2746							(unsigned long)dbinfo.st_size, (unsigned long)dbstateptr->recordPosition);
2747		fprintf(stderr, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize);
2748#endif
2749		if (iobuffersize > 0) {
2750			iobuffer = malloc(iobuffersize);
2751			if (iobuffer == NULL) {
2752				result = ENOMEM;
2753				goto ErrExit;
2754			}
2755
2756			dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
2757			do {
2758				iotransfersize = dbinfo.st_size - dataoffset;
2759				if (iotransfersize > 0) {
2760					if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
2761
2762	#if DEBUG_TRACE
2763					fprintf(stderr, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)dataoffset);
2764	#endif
2765					lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
2766					bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
2767					if (bytestransferred != iotransfersize) {
2768						result = errno;
2769						goto ErrExit;
2770					}
2771
2772	#if DEBUG_TRACE
2773					fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)(dataoffset - (off_t)sizeof(struct VSDBEntry)));
2774	#endif
2775					lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET);
2776					bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize);
2777					if (bytestransferred != iotransfersize) {
2778						result = errno;
2779						goto ErrExit;
2780					}
2781
2782					dataoffset += (off_t)iotransfersize;
2783				}
2784			} while (iotransfersize > 0);
2785		}
2786#if DEBUG_TRACE
2787		fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry))));
2788#endif
2789		if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
2790			goto ErrExit;
2791		}
2792
2793		fsync(dbstateptr->dbfile);
2794
2795		result = 0;
2796	}
2797
2798ErrExit:
2799	if (iobuffer) free(iobuffer);
2800	UnlockDB(dbstateptr);
2801
2802StdEdit:
2803	return result;
2804}
2805
2806
2807
2808int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
2809	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
2810
2811	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
2812
2813	dbstateptr->signature = 0;
2814
2815	close(dbstateptr->dbfile);		/* Nothing we can do about any errors... */
2816	dbstateptr->dbfile = 0;
2817
2818	free(dbstateptr);
2819
2820	return 0;
2821}
2822
2823
2824
2825/******************************************************************************
2826 *
2827 *  I N T E R N A L   O N L Y   D A T A B A S E   R O U T I N E S
2828 *
2829 *****************************************************************************/
2830
2831static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
2832#if DEBUG_TRACE
2833	fprintf(stderr, "LockDB: Locking VSDB file...\n");
2834#endif
2835	return flock(dbstateptr->dbfile, lockmode);
2836}
2837
2838
2839
2840static int UnlockDB(VSDBStatePtr dbstateptr) {
2841#if DEBUG_TRACE
2842	fprintf(stderr, "UnlockDB: Unlocking VSDB file...\n");
2843#endif
2844	return flock(dbstateptr->dbfile, LOCK_UN);
2845}
2846
2847
2848
2849static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, unsigned long options) {
2850	struct VSDBKey searchkey;
2851	struct VSDBEntry dbentry;
2852	int result;
2853
2854	FormatDBKey(volumeID, &searchkey);
2855	lseek(dbstateptr->dbfile, 0, SEEK_SET);
2856
2857	do {
2858		result = GetVSDBEntry(dbstateptr, &dbentry);
2859		if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
2860			if (targetEntry != NULL) {
2861#if DEBUG_TRACE
2862				fprintf(stderr, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry), &dbentry, targetEntry);
2863#endif
2864				memcpy(targetEntry, &dbentry, sizeof(*targetEntry));
2865			}
2866			return 0;
2867		}
2868	} while (result == 0);
2869
2870	return -1;
2871}
2872
2873
2874
2875static int AddVolumeRecord(VSDBStatePtr dbstateptr , struct VSDBEntry *dbentry) {
2876#if DEBUG_TRACE
2877	VolumeUUIDString id;
2878#endif
2879
2880#if DEBUG_TRACE
2881	strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
2882	id[sizeof(dbentry->key.uuid)] = (char)0;
2883	fprintf(stderr, "AddVolumeRecord: Adding record for UUID #%s...\n", id);
2884#endif
2885	lseek(dbstateptr->dbfile, 0, SEEK_END);
2886	return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry));
2887}
2888
2889
2890
2891
2892static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
2893#if DEBUG_TRACE
2894	VolumeUUIDString id;
2895#endif
2896
2897#if DEBUG_TRACE
2898	strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
2899	id[sizeof(dbentry->key.uuid)] = (char)0;
2900	fprintf(stderr, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id, (unsigned long)dbstateptr->recordPosition);
2901#endif
2902	lseek(dbstateptr->dbfile, dbstateptr->recordPosition, SEEK_SET);
2903#if DEBUG_TRACE
2904	fprintf(stderr, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry));
2905#endif
2906	return write(dbstateptr->dbfile, dbentry, sizeof(*dbentry));
2907}
2908
2909
2910
2911static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
2912	struct VSDBEntry entry;
2913	int result;
2914#if DEBUG_TRACE
2915	VolumeUUIDString id;
2916#endif
2917
2918	dbstateptr->recordPosition = lseek(dbstateptr->dbfile, 0, SEEK_CUR);
2919#if 0 // DEBUG_TRACE
2920	fprintf(stderr, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr->recordPosition);
2921#endif
2922	result = read(dbstateptr->dbfile, &entry, sizeof(entry));
2923	if ((result != sizeof(entry)) ||
2924		(entry.keySeparator != DBKEYSEPARATOR) ||
2925		(entry.space != DBBLANKSPACE) ||
2926		(entry.terminator != DBRECORDTERMINATOR)) {
2927		return -1;
2928	}
2929
2930#if DEBUG_TRACE
2931	strncpy(id, entry.key.uuid, sizeof(entry.key.uuid));
2932	id[sizeof(entry.key.uuid)] = (char)0;
2933	fprintf(stderr, "GetVSDBEntry: returning entry for UUID #%s...\n", id);
2934#endif
2935	memcpy(dbentry, &entry, sizeof(*dbentry));
2936	return 0;
2937}
2938
2939
2940
2941static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) {
2942#if 0 // DEBUG_TRACE
2943	VolumeUUIDString id;
2944
2945	strncpy(id, key1->uuid, sizeof(key1->uuid));
2946	id[sizeof(key1->uuid)] = (char)0;
2947	fprintf(stderr, "CompareVSDBKeys: comparing #%s to ", id);
2948	strncpy(id, key2->uuid, sizeof(key2->uuid));
2949	id[sizeof(key2->uuid)] = (char)0;
2950	fprintf(stderr, "%s (%d.)...\n", id, sizeof(key1->uuid));
2951#endif
2952
2953	return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid));
2954}
2955
2956
2957
2958/******************************************************************************
2959 *
2960 *  F O R M A T T I N G   A N D   C O N V E R S I O N   R O U T I N E S
2961 *
2962 *****************************************************************************/
2963
2964static void FormatULong(unsigned long u, char *s) {
2965	unsigned long d;
2966	int i;
2967	char *digitptr = s;
2968
2969	for (i = 0; i < 8; ++i) {
2970		d = ((u & 0xF0000000) >> 28) & 0x0000000F;
2971		if (d < 10) {
2972			*digitptr++ = (char)(d + '0');
2973		} else {
2974			*digitptr++ = (char)(d - 10 + 'A');
2975		}
2976		u = u << 4;
2977	}
2978}
2979
2980
2981
2982static void FormatUUID(VolumeUUID *volumeID, char *UUIDField) {
2983	FormatULong(volumeID->v.high, UUIDField);
2984	FormatULong(volumeID->v.low, UUIDField+8);
2985
2986}
2987
2988
2989
2990static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) {
2991	FormatUUID(volumeID, dbkey->uuid);
2992}
2993
2994
2995
2996static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord) {
2997	FormatULong(volumeStatusFlags, dbrecord->statusFlags);
2998}
2999
3000
3001
3002static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry) {
3003	FormatDBKey(volumeID, &dbentry->key);
3004	dbentry->keySeparator = DBKEYSEPARATOR;
3005	dbentry->space = DBBLANKSPACE;
3006	FormatDBRecord(volumeStatusFlags, &dbentry->record);
3007#if 0 // DEBUG_TRACE
3008	dbentry->terminator = (char)0;
3009	fprintf(stderr, "FormatDBEntry: '%s' (%d.)\n", dbentry, sizeof(*dbentry));
3010#endif
3011	dbentry->terminator = DBRECORDTERMINATOR;
3012}
3013
3014
3015
3016static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits) {
3017	int i;
3018	char c;
3019	unsigned long nextdigit;
3020	unsigned long n;
3021
3022	n = 0;
3023	for (i = 0; (i < 8) && ((c = hs[i]) != (char)0) ; ++i) {
3024		if ((c >= '0') && (c <= '9')) {
3025			nextdigit = c - '0';
3026		} else if ((c >= 'A') && (c <= 'F')) {
3027			nextdigit = c - 'A' + 10;
3028		} else if ((c >= 'a') && (c <= 'f')) {
3029			nextdigit = c - 'a' + 10;
3030		} else {
3031			nextdigit = 0;
3032		}
3033		n = (n << 4) + nextdigit;
3034	}
3035
3036	return n;
3037}
3038