2 * Copyright (c) 1999-2004 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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.
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
20 * @APPLE_LICENSE_HEADER_END@
23 Copyright (c) 1987-99 Apple Computer, Inc.
27 Contains code to implement hfs utility used by the WorkSpace to mount HFS.
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).
40 /* ************************************** I N C L U D E S ***************************************** */
42 #include <sys/types.h>
45 #include <sys/sysctl.h>
46 #include <sys/resource.h>
47 #include <sys/vmmeter.h>
48 #include <sys/mount.h>
50 #include <sys/param.h>
51 #include <sys/ucred.h>
53 #include <sys/loadable_fs.h>
55 #include <hfs/hfs_format.h>
56 #include <hfs/hfs_mount.h>
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.
73 #define COMMON_DIGEST_FOR_OPENSSL
74 #include <CommonCrypto/CommonDigest.h>
76 #include <libkern/OSByteOrder.h>
78 #include <CoreFoundation/CFString.h>
80 #include <System/uuid/uuid.h>
81 #include <System/uuid/namespace.h>
83 #define READ_DEFAULT_ENCODING 1
86 #define FSUC_ADOPT 'a'
90 #define FSUC_DISOWN 'd'
94 #define FSUC_GETUUID 'k'
98 #define FSUC_SETUUID 's'
102 #define FSUC_MKJNL 'J'
106 #define FSUC_UNJNL 'U'
109 #ifndef FSUC_UNJNL_RAW
110 #define FSUC_UNJNL_RAW 'N'
114 #define FSUC_JNLINFO 'I'
118 /* **************************************** L O C A L S ******************************************* */
120 #define HFS_BLOCK_SIZE 512
122 char gHFS_FS_NAME
[] = "hfs";
123 char gHFS_FS_NAME_NAME
[] = "HFS";
125 char gNewlineString
[] = "\n";
127 char gMountCommand
[] = "/sbin/mount";
129 char gUnmountCommand
[] = "/sbin/umount";
131 char gReadOnlyOption
[] = "-r";
132 char gReadWriteOption
[] = "-w";
134 char gSuidOption
[] = "suid";
135 char gNoSuidOption
[] = "nosuid";
137 char gDevOption
[] = "dev";
138 char gNoDevOption
[] = "nodev";
140 char gUsePermissionsOption
[] = "perm";
141 char gIgnorePermissionsOption
[] = "noperm";
143 boolean_t gIsEjectable
= 0;
145 int gJournalSize
= 0;
147 #define AUTO_ADOPT_FIXED 1
148 #define AUTO_ENTER_FIXED 0
151 struct FinderAttrBuf
{
152 unsigned long info_length
;
153 unsigned long finderinfo
[8];
157 #define VOLUMEUUIDVALUESIZE 2
158 typedef union VolumeUUID
{
159 unsigned long value
[VOLUMEUUIDVALUESIZE
];
166 #define VOLUMEUUIDLENGTH 16
167 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1];
169 #define VOLUME_RECORDED 0x80000000
170 #define VOLUME_USEPERMISSIONS 0x00000001
171 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
173 typedef void *VolumeStatusDBHandle
;
175 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
176 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
);
177 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
);
178 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
179 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
);
180 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
);
181 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
182 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
184 /* ************************************ P R O T O T Y P E S *************************************** */
185 static void DoDisplayUsage( const char * argv
[] );
186 static int DoMount( char * theDeviceNamePtr
, const char * theMountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
187 static int DoProbe( char * theDeviceNamePtr
);
188 static int DoUnmount( const char * theMountPointPtr
);
189 static int DoGetUUIDKey( const char * theDeviceNamePtr
);
190 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
191 static int DoAdopt( const char * theDeviceNamePtr
);
192 static int DoDisown( const char * theDeviceNamePtr
);
194 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
195 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
196 extern int DoGetJournalInfo( const char * volNamePtr
);
197 extern int RawDisableJournaling( const char *devname
);
199 static 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
);
201 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
202 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
);
203 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
204 static int GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
205 static int GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
206 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
207 static int SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
208 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
209 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
210 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
211 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
212 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
213 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
214 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
215 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
216 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
217 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
218 off_t
*physicalOffset
, ssize_t
*availableBytes
);
219 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
220 off_t volOffset
, u_int32_t blockSize
,
221 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
222 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
223 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
225 static int GetEncodingBias(void);
228 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
230 static void uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
);
233 * The fuction CFStringGetSystemEncoding does not work correctly in
234 * our context (autodiskmount deamon). We include a local copy here
235 * so that we can derive the default encoding. Radar 2516316.
237 #if READ_DEFAULT_ENCODING
238 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
240 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
241 struct passwd
*passwdp
;
243 if ((passwdp
= getpwuid(0))) { // root account
244 char buffer
[MAXPATHLEN
+ 1];
247 strcpy(buffer
, passwdp
->pw_dir
);
248 strcat(buffer
, __kCFUserEncodingFileName
);
250 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
253 readSize
= read(fd
, buffer
, MAXPATHLEN
);
254 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
256 return strtol(buffer
, NULL
, 0);
259 return 0; // Fallback to smRoman
264 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
266 struct hfs_mnt_encoding
{
267 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
268 CFStringEncoding encoding_id
; /* encoding type number */
271 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
277 { "CentralEurRoman", 29 },
278 { "ChineseSimp", 25 },
279 { "ChineseTrad", 2 },
300 { "Roman", 0 }, /* default */
308 { "Ukrainian", 152 },
309 { "Vietnamese", 30 },
312 #define KEXT_LOAD_COMMAND "/sbin/kextload"
313 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
315 static int load_encoding(CFStringEncoding encoding
)
323 char kmodfile
[MAXPATHLEN
];
325 /* Find the encoding that matches the one passed in */
326 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
328 for (i
=0; i
<numEncodings
; ++i
)
330 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
332 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
337 if (encodingName
== NULL
)
339 /* Couldn't figure out which encoding KEXT to load */
340 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
344 sprintf(kmodfile
, "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
345 if (stat(kmodfile
, &sb
) == -1)
347 /* We recognized the encoding, but couldn't find the KEXT */
348 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
355 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
357 exit(1); /* We can only get here if the exec failed */
361 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
363 if (WEXITSTATUS(status
) != 0)
365 /* kextload returned an error. Too bad its output doesn't get logged. */
366 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
372 return FSUR_IO_SUCCESS
;
376 /* ******************************************** main ************************************************
378 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
379 for detail info on input arguments.
381 argc - the number of arguments in argv.
382 argv - array of arguments.
384 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
385 *************************************************************************************************** */
387 int main (int argc
, const char *argv
[])
389 const char * actionPtr
= NULL
;
390 char rawDeviceName
[MAXPATHLEN
];
391 char blockDeviceName
[MAXPATHLEN
];
392 const char * mountPointPtr
= NULL
;
393 int result
= FSUR_IO_SUCCESS
;
394 boolean_t isLocked
= 0; /* reasonable assumptions */
395 boolean_t isSetuid
= 0; /* reasonable assumptions */
396 boolean_t isDev
= 0; /* reasonable assumptions */
398 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
400 /* Verify our arguments */
401 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
406 -- Build our device name (full path), should end up with something like:
410 sprintf(rawDeviceName
, "/dev/r%s", argv
[2]);
411 sprintf(blockDeviceName
, "/dev/%s", argv
[2]);
413 /* call the appropriate routine to handle the given action argument after becoming root */
415 switch( * actionPtr
) {
417 result
= DoProbe(rawDeviceName
);
421 case FSUC_MOUNT_FORCE
:
422 result
= DoMount(blockDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
426 result
= DoUnmount( mountPointPtr
);
429 result
= DoGetUUIDKey( blockDeviceName
);
433 result
= DoChangeUUIDKey( blockDeviceName
);
436 result
= DoAdopt( blockDeviceName
);
440 result
= DoDisown( blockDeviceName
);
445 result
= DoMakeJournaled( argv
[3], gJournalSize
);
447 result
= DoMakeJournaled( argv
[2], gJournalSize
);
452 result
= DoUnJournal( argv
[2] );
456 result
= RawDisableJournaling( argv
[2] );
460 result
= DoGetJournalInfo( argv
[2] );
464 /* should never get here since ParseArgs should handle this situation */
465 DoDisplayUsage( argv
);
474 return result
; /*...and make main fit the ANSI spec. */
478 /* ***************************** DoMount ********************************
480 This routine will fire off a system command to mount the given device at the given mountpoint.
481 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
483 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
484 mountPointPtr - pointer to the mount point.
487 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
488 *********************************************************************** */
490 DoMount(char *deviceNamePtr
, const char *mountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
496 char *permissionsOption
;
497 int result
= FSUR_IO_FAIL
;
499 char encodeopt
[16] = "";
500 CFStringEncoding encoding
;
501 VolumeUUID targetVolumeUUID
;
502 VolumeStatusDBHandle vsdbhandle
= NULL
;
503 unsigned long targetVolumeStatus
;
505 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
506 return (FSUR_IO_FAIL
);
508 /* get the volume UUID to check if permissions should be used: */
509 targetVolumeStatus
= 0;
510 if (((result
= GetVolumeUUID(deviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
511 (targetVolumeUUID
.v
.high
==0) ||
512 (targetVolumeUUID
.v
.low
== 0)) {
514 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
517 if (gIsEjectable
== 0) {
518 result
= DoAdopt( deviceNamePtr
);
520 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
522 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
525 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
527 targetVolumeStatus
= 0;
531 /* We've got a real volume UUID! */
533 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
535 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
536 /* Can't even get access to the volume info db; assume permissions are OK. */
538 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
540 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
543 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
545 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
547 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
550 if (gIsEjectable
== 0) {
551 result
= DoAdopt( deviceNamePtr
);
553 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
555 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
558 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
560 targetVolumeStatus
= 0;
563 targetVolumeStatus
= 0;
566 (void)CloseVolumeStatusDB(vsdbhandle
);
573 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
574 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
575 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
578 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
580 /* get default encoding value (for hfs volumes) */
581 #if READ_DEFAULT_ENCODING
582 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
584 encoding
= CFStringGetSystemEncoding();
586 sprintf(encodeopt
, "-e=%d", (int)encoding
);
588 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
589 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
591 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
592 "-o", encodeopt
, "-o", permissionsOption
,
593 "-o", "-u=unknown,-g=unknown,-m=0777",
594 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
597 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
598 return (FSUR_IO_FAIL
);
602 return (FSUR_IO_FAIL
);
605 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
606 result
= status
.w_retcode
;
610 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
614 /* ****************************************** DoUnmount *********************************************
616 This routine will fire off a system command to unmount the given device.
618 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
620 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
621 *************************************************************************************************** */
623 DoUnmount(const char * theMountPointPtr
)
629 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
634 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
636 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
638 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
639 return (FSUR_IO_FAIL
);
643 return (FSUR_IO_FAIL
);
646 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
647 result
= status
.w_retcode
;
651 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
656 /* ******************************************* DoProbe **********************************************
658 This routine will open the given raw device and check to make sure there is media that looks
661 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
663 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
664 *************************************************************************************************** */
666 DoProbe(char *deviceNamePtr
)
668 int result
= FSUR_UNRECOGNIZED
;
671 HFSMasterDirectoryBlock
* mdbPtr
;
672 HFSPlusVolumeHeader
* volHdrPtr
;
673 u_char volnameUTF8
[NAME_MAX
+1];
675 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
677 result
= FSUR_UNRECOGNIZED
;
681 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
682 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
684 fd
= open( deviceNamePtr
, O_RDONLY
, 0 );
686 result
= FSUR_IO_FAIL
;
691 * Read the HFS Master Directory Block from sector 2
693 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
694 if (FSUR_IO_FAIL
== result
)
697 /* get classic HFS volume name (from MDB) */
698 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
699 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
702 CFStringEncoding encoding
;
704 /* Some poorly mastered HFS CDs have an empty MDB name field! */
705 if (mdbPtr
->drVN
[0] == '\0') {
706 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
707 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
710 /* Check for an encoding hint in the Finder Info (field 4). */
711 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
712 if (encoding
== kCFStringEncodingInvalidId
) {
713 /* Next try the encoding bias in the kernel. */
714 encoding
= GetEncodingBias();
715 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
716 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
719 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
720 mdbPtr
->drVN
, encoding
);
721 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
724 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
726 /* default to MacRoman on conversion errors */
727 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
728 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
729 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
731 encoding
= kCFStringEncodingMacRoman
;
734 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
735 if (encoding
!= kCFStringEncodingMacRoman
) {
736 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
737 encoding
= kCFStringEncodingMacRoman
;
738 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
739 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
744 /* get HFS Plus volume name (from Catalog) */
745 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
746 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
747 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
748 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
751 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
752 /* embedded volume, first find offset */
753 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
754 if ( result
!= FSUR_IO_SUCCESS
)
760 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
763 result
= FSUR_UNRECOGNIZED
;
766 if (FSUR_IO_SUCCESS
== result
) {
769 /* Change slashes to colons in the volume name */
770 for (s
=volnameUTF8
; *s
; ++s
) {
775 /* Print the volume name to standard output */
776 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
777 result
= FSUR_RECOGNIZED
;
793 * Create a version 3 UUID from a unique "name" in the given "name space".
794 * Version 3 UUID are derived using "name" via MD5 checksum.
797 * result_uuid - resulting UUID.
798 * namespace - namespace in which given name exists and UUID should be created.
799 * name - unique string used to create version 3 UUID.
800 * namelen - length of the name string.
803 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
808 MD5_Update(&c
, namespace, sizeof(uuid_t
));
809 MD5_Update(&c
, name
, namelen
);
810 MD5_Final(result_uuid
, &c
);
812 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
813 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
817 /* **************************************** DoGetUUIDKey *******************************************
819 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
821 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
823 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
824 *************************************************************************************************** */
826 DoGetUUIDKey( const char * theDeviceNamePtr
) {
828 VolumeUUID targetVolumeUUID
;
832 unsigned char rawUUID
[8];
834 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
836 ((uint32_t *)rawUUID
)[0] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.high
);
837 ((uint32_t *)rawUUID
)[1] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.low
);
839 uuid_create_md5_from_name(uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
840 uuid_unparse(uuid
, uuidLine
);
841 write(1, uuidLine
, strlen(uuidLine
));
842 result
= FSUR_IO_SUCCESS
;
850 /* *************************************** DoChangeUUIDKey ******************************************
852 This routine will change the UUID on the specified block device.
854 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
856 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
857 *************************************************************************************************** */
859 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
861 VolumeUUID newVolumeUUID
;
863 GenerateVolumeUUID(&newVolumeUUID
);
864 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
871 /* **************************************** DoAdopt *******************************************
873 This routine will add the UUID of the specified block device to the list of local volumes.
875 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
877 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
878 *************************************************************************************************** */
880 DoAdopt( const char * theDeviceNamePtr
) {
881 int result
, closeresult
;
882 VolumeUUID targetVolumeUUID
;
883 VolumeStatusDBHandle vsdbhandle
= NULL
;
884 unsigned long targetVolumeStatus
;
886 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
888 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
889 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
890 targetVolumeStatus
= 0;
892 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
893 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
895 result
= FSUR_IO_SUCCESS
;
899 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
901 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
904 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
908 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
915 /* **************************************** DoDisown *******************************************
917 This routine will change the status of the specified block device to ignore its permissions.
919 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
921 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
922 *************************************************************************************************** */
924 DoDisown( const char * theDeviceNamePtr
) {
925 int result
, closeresult
;
926 VolumeUUID targetVolumeUUID
;
927 VolumeStatusDBHandle vsdbhandle
= NULL
;
928 unsigned long targetVolumeStatus
;
930 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
932 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
933 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
934 targetVolumeStatus
= 0;
936 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
937 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
939 result
= FSUR_IO_SUCCESS
;
943 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
945 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
948 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
950 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
952 result
= FSUR_IO_FAIL
;
957 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
964 get_multiplier(char c
)
966 if (tolower(c
) == 'k') {
968 } else if (tolower(c
) == 'm') {
970 } else if (tolower(c
) == 'g') {
971 return 1024 * 1024 * 1024;
977 /* **************************************** ParseArgs ********************************************
979 This routine will make sure the arguments passed in to us are cool.
980 Here is how this utility is used:
982 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
984 -p (Probe for mounting)
985 -P (Probe for initializing - not supported)
987 -r (Repair - not supported)
990 -i (Initialize - not supported)
993 disk0s2 (for example)
996 /foo/bar/ (required for Mount and Force Mount actions)
999 (these are ignored for CDROMs)
1000 either "readonly" OR "writable"
1001 either "removable" OR "fixed"
1002 either "nosuid" or "suid"
1003 either "nodev" or "dev"
1006 hfs.util -p disk0s2 removable writable
1007 hfs.util -p disk0s2 removable readonly
1008 hfs.util -m disk0s2 /my/hfs
1011 argc - the number of arguments in argv.
1012 argv - array of arguments.
1014 returns FSUR_INVAL if we find a bad argument else 0.
1015 *************************************************************************************************** */
1017 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1018 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1019 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1021 int result
= FSUR_INVAL
;
1022 int deviceLength
, doLengthCheck
= 1;
1026 /* Must have at least 3 arguments and the action argument must start with a '-' */
1027 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1028 DoDisplayUsage( argv
);
1032 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1034 * actionPtr
= & argv
[1][1];
1036 switch ( argv
[1][1] ) {
1038 /* action Probe and requires 5 arguments (need the flags) */
1040 DoDisplayUsage( argv
);
1048 /* Note: the device argument in argv[2] is checked further down but ignored. */
1049 * mountPointPtr
= argv
[3];
1050 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1054 case FSUC_MOUNT_FORCE
:
1055 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1057 DoDisplayUsage( argv
);
1060 * mountPointPtr
= argv
[3];
1086 if (isdigit(argv
[2][0])) {
1088 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1090 gJournalSize
*= get_multiplier(*ptr
);
1101 case FSUC_UNJNL_RAW
:
1113 DoDisplayUsage( argv
);
1118 /* Make sure device (argv[2]) is something reasonable */
1119 deviceLength
= strlen( argv
[2] );
1120 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1121 DoDisplayUsage( argv
);
1126 /* Flags: removable/fixed. */
1127 if ( 0 == strcmp(argv
[index
],"removable") ) {
1128 * isEjectablePtr
= 1;
1129 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1130 * isEjectablePtr
= 0;
1132 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1135 /* Flags: readonly/writable. */
1136 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1138 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1141 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1145 /* Flags: suid/nosuid. */
1146 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1148 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1151 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1154 /* Flags: dev/nodev. */
1155 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1157 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1160 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1175 /* *************************************** DoDisplayUsage ********************************************
1177 This routine will do a printf of the correct usage for this utility.
1179 argv - array of arguments.
1182 *************************************************************************************************** */
1184 DoDisplayUsage(const char *argv
[])
1186 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1187 printf("action_arg:\n");
1188 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1189 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1190 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1191 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1192 #ifdef HFS_UUID_SUPPORT
1193 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1194 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1195 #endif HFS_UUID_SUPPORT
1196 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1197 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1198 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1199 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1200 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1201 printf("device_arg:\n");
1202 printf(" device we are acting upon (for example, 'disk0s2')\n");
1203 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1204 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1205 printf("mount_point_arg:\n");
1206 printf(" required for Mount and Force Mount \n");
1208 printf(" required for Mount, Force Mount and Probe\n");
1209 printf(" indicates removable or fixed (for example 'fixed')\n");
1210 printf(" indicates readonly or writable (for example 'readonly')\n");
1211 printf(" indicates suid or nosuid (for example 'suid')\n");
1212 printf(" indicates dev or nodev (for example 'dev')\n");
1213 printf("Examples:\n");
1214 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1215 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1219 } /* DoDisplayUsage */
1225 Given a path to a device, determine if a volume is mounted on that
1226 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1227 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1228 no volume mounted on the device, set *pathPtr to NULL and return
1231 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1234 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1240 /* Assume no mounted volume found */
1242 result
= FSUR_IO_SUCCESS
;
1244 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1246 return FSUR_IO_FAIL
;
1248 for (i
=0; i
<numMounts
; ++i
) {
1249 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1250 /* Found a mounted volume; check the type */
1251 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1252 *pathPtr
= buf
[i
].f_mntonname
;
1253 /* result = FSUR_IO_SUCCESS, above */
1255 result
= FSUR_UNRECOGNIZED
;
1268 Read the Master Directory Block or Volume Header Block from an HFS,
1269 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1270 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1271 Return a pointer to the volume UUID in the Finder Info.
1273 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1276 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1279 HFSMasterDirectoryBlock
* mdbPtr
;
1280 HFSPlusVolumeHeader
* volHdrPtr
;
1286 * Read the HFS Master Directory Block or Volume Header from sector 2
1289 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1290 if (result
!= FSUR_IO_SUCCESS
)
1294 * If this is a wrapped HFS Plus volume, read the Volume Header from
1295 * sector 2 of the embedded volume.
1297 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1298 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1299 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1300 if (result
!= FSUR_IO_SUCCESS
)
1302 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1303 if (result
!= FSUR_IO_SUCCESS
)
1308 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1309 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1310 * UUID from the Finder Info.
1312 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1313 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1314 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1315 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1316 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1318 result
= FSUR_UNRECOGNIZED
;
1329 Read the UUID from an unmounted volume, by doing direct access to the device.
1330 Assumes the caller has already determined that a volume is not mounted
1333 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1336 GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1341 VolumeUUID
*finderInfoUUIDPtr
;
1344 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1346 result
= FSUR_UNRECOGNIZED
;
1350 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1353 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1355 result
= FSUR_IO_FAIL
;
1360 * Get the pointer to the volume UUID in the Finder Info
1362 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1363 if (result
!= FSUR_IO_SUCCESS
)
1367 * Copy the volume UUID out of the Finder Info
1369 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1370 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1373 if (fd
> 0) close(fd
);
1374 if (bufPtr
) free(bufPtr
);
1377 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1379 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1386 Write a previously generated UUID to an unmounted volume, by doing direct
1387 access to the device. Assumes the caller has already determined that a
1388 volume is not mounted on the device.
1390 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1393 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1398 VolumeUUID
*finderInfoUUIDPtr
;
1401 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1403 result
= FSUR_UNRECOGNIZED
;
1407 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1410 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1412 result
= FSUR_IO_FAIL
;
1417 * Get the pointer to the volume UUID in the Finder Info
1419 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1420 if (result
!= FSUR_IO_SUCCESS
)
1424 * Update the UUID in the Finder Info
1426 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1427 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1430 * Write the modified MDB or VHB back to disk
1432 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1435 if (fd
> 0) close(fd
);
1436 if (bufPtr
) free(bufPtr
);
1439 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1441 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1448 Read the UUID from a mounted volume, by calling getattrlist().
1449 Assumes the path is the mount point of an HFS volume.
1451 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1454 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1456 struct attrlist alist
;
1457 struct FinderAttrBuf volFinderInfo
;
1458 VolumeUUID
*finderInfoUUIDPtr
;
1461 /* Set up the attrlist structure to get the volume's Finder Info */
1462 alist
.bitmapcount
= 5;
1464 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1465 alist
.volattr
= ATTR_VOL_INFO
;
1470 /* Get the Finder Info */
1471 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1473 result
= FSUR_IO_FAIL
;
1477 /* Copy the UUID from the Finder Into to caller's buffer */
1478 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1479 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1480 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1481 result
= FSUR_IO_SUCCESS
;
1491 Write a UUID to a mounted volume, by calling setattrlist().
1492 Assumes the path is the mount point of an HFS volume.
1494 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1497 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1499 struct attrlist alist
;
1500 struct FinderAttrBuf volFinderInfo
;
1501 VolumeUUID
*finderInfoUUIDPtr
;
1504 /* Set up the attrlist structure to get the volume's Finder Info */
1505 alist
.bitmapcount
= 5;
1507 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1508 alist
.volattr
= ATTR_VOL_INFO
;
1513 /* Get the Finder Info */
1514 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1516 result
= FSUR_IO_FAIL
;
1520 /* Update the UUID in the Finder Info */
1521 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1522 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1523 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1525 /* Write the Finder Info back to the volume */
1526 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1528 result
= FSUR_IO_FAIL
;
1532 result
= FSUR_IO_SUCCESS
;
1542 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1543 we were asked to generate one, then generate a new UUID and write it to the
1546 Determine whether an HFS volume is mounted on the given device. If so, we
1547 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1548 the filesystem. If there is no mounted volume, then do direct device access
1549 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1551 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1555 GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1561 * Determine whether a volume is mounted on this device. If it is HFS, then
1562 * get the mount point's path. If it is non-HFS, then we can exit immediately
1563 * with FSUR_UNRECOGNIZED.
1565 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1566 if (result
!= FSUR_IO_SUCCESS
)
1570 * Get any existing UUID.
1573 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1575 result
= GetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1576 if (result
!= FSUR_IO_SUCCESS
)
1580 * If there was no valid UUID, and we were asked to generate one, then
1581 * generate it and write it back to disk.
1583 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1584 GenerateVolumeUUID(volumeUUIDPtr
);
1586 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1588 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1589 /* Fall through to Err_Exit */
1601 Write a UUID to an HFS, HFS Plus or HFSX volume.
1603 Determine whether an HFS volume is mounted on the given device. If so, we
1604 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1605 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1607 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1610 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1615 * Determine whether a volume is mounted on this device. If it is HFS, then
1616 * get the mount point's path. If it is non-HFS, then we can exit immediately
1617 * with FSUR_UNRECOGNIZED.
1619 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1620 if (result
!= FSUR_IO_SUCCESS
)
1627 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1629 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1638 -- GetEmbeddedHFSPlusVol
1640 -- In: hfsMasterDirectoryBlockPtr
1641 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1642 (that is, 2 blocks before the volume header)
1647 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1649 int result
= FSUR_IO_SUCCESS
;
1650 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1652 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1653 result
= FSUR_UNRECOGNIZED
;
1657 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1658 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1660 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1661 result
= FSUR_UNRECOGNIZED
;
1665 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1666 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1668 if ( startOffsetPtr
)
1669 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1670 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1680 -- GetNameFromHFSPlusVolumeStartingAt
1682 -- Caller's responsibility to allocate and release memory for the converted string.
1684 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1688 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1690 int result
= FSUR_IO_SUCCESS
;
1691 u_int32_t blockSize
;
1692 char * bufPtr
= NULL
;
1693 HFSPlusVolumeHeader
* volHdrPtr
;
1694 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1695 u_int32_t catalogNodeSize
;
1697 u_int32_t catalogExtCount
;
1698 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1700 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1701 if ( ! volHdrPtr
) {
1702 result
= FSUR_IO_FAIL
;
1707 * Read the Volume Header
1708 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1710 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1711 if (result
== FSUR_IO_FAIL
) {
1713 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1715 goto Return
; // return FSUR_IO_FAIL
1718 /* Verify that it is an HFS+ volume. */
1720 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1721 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
1722 result
= FSUR_IO_FAIL
;
1724 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1729 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1730 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1731 if ( ! catalogExtents
) {
1732 result
= FSUR_IO_FAIL
;
1735 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1736 catalogExtCount
= kHFSPlusExtentDensity
;
1738 /* if there are overflow catalog extents, then go get them */
1739 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
1740 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1741 if (result
!= FSUR_IO_SUCCESS
)
1745 /* Read the header node of the catalog B-Tree */
1747 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1748 catalogExtCount
, catalogExtents
,
1749 &catalogNodeSize
, &leafNode
);
1750 if (result
!= FSUR_IO_SUCCESS
)
1753 /* Read the first leaf node of the catalog b-tree */
1755 bufPtr
= (char *)malloc(catalogNodeSize
);
1757 result
= FSUR_IO_FAIL
;
1761 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1763 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1764 hfsPlusVolumeOffset
, blockSize
,
1765 catalogExtCount
, catalogExtents
);
1766 if (result
== FSUR_IO_FAIL
) {
1768 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1770 goto Return
; // return FSUR_IO_FAIL
1776 HFSPlusCatalogKey
* k
;
1779 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
1780 result
= FSUR_IO_FAIL
;
1782 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1787 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1789 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1792 // Get a pointer to the first record.
1794 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
1795 k
= (HFSPlusCatalogKey
*)p
;
1797 // There should be only one record whose parent is the root parent. It should be the first record.
1799 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
1800 result
= FSUR_IO_FAIL
;
1802 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1807 /* Extract the name of the root directory */
1810 HFSUniStr255
*swapped
;
1813 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1814 if (swapped
== NULL
) {
1815 result
= FSUR_IO_FAIL
;
1818 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
1820 for (i
=0; i
<swapped
->length
; i
++) {
1821 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
1823 swapped
->unicode
[i
] = 0;
1824 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1825 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1831 result
= FSUR_IO_SUCCESS
;
1835 free((char*) volHdrPtr
);
1838 free((char*) catalogExtents
);
1841 free((char*)bufPtr
);
1845 } /* GetNameFromHFSPlusVolumeStartingAt */
1848 #pragma options align=mac68k
1850 BTNodeDescriptor node
;
1852 } HeaderRec
, *HeaderPtr
;
1853 #pragma options align=reset
1858 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1862 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1863 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1864 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1867 HeaderRec
* bTreeHeaderPtr
= NULL
;
1869 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1870 if (bTreeHeaderPtr
== NULL
)
1871 return (FSUR_IO_FAIL
);
1873 /* Read the b-tree header node */
1875 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
1876 hfsPlusVolumeOffset
, blockSize
,
1877 extentCount
, extentList
);
1878 if ( result
== FSUR_IO_FAIL
) {
1880 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
1885 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
1886 result
= FSUR_IO_FAIL
;
1888 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1893 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
1895 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
1898 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
1901 free((char*) bTreeHeaderPtr
);
1905 } /* GetBTreeNodeInfo */
1911 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1915 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
1916 HFSPlusVolumeHeader
*volHdrPtr
,
1917 HFSPlusExtentDescriptor
**catalogExtents
,
1918 u_int32_t
*catalogExtCount
)
1921 u_int32_t numRecords
;
1924 u_int32_t blockSize
;
1925 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1926 HFSPlusExtentDescriptor
* extents
;
1928 char * bufPtr
= NULL
;
1932 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1933 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
1934 extents
= *catalogExtents
;
1935 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
1938 /* Read the header node of the extents B-Tree */
1940 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1941 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
1942 &nodeSize
, &leafNode
);
1943 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
1946 /* Calculate the logical position of the first leaf node */
1948 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
1950 /* Read the first leaf node of the extents b-tree */
1952 bufPtr
= (char *)malloc(nodeSize
);
1954 result
= FSUR_IO_FAIL
;
1958 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1961 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
1962 hfsPlusVolumeOffset
, blockSize
,
1963 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
1964 if ( result
== FSUR_IO_FAIL
) {
1966 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1971 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
1972 result
= FSUR_IO_FAIL
;
1976 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
1977 for (i
= 1; i
<= numRecords
; ++i
) {
1980 HFSPlusExtentKey
* k
;
1983 * Get the offset (in bytes) of the record from the
1984 * list of offsets at the end of the node
1986 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
1989 /* Get a pointer to the record */
1991 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
1992 k
= (HFSPlusExtentKey
*)p
;
1994 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
1997 /* grow list and copy additional extents */
1998 listsize
+= sizeof(HFSPlusExtentRecord
);
1999 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2000 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2001 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2003 *catalogExtCount
+= kHFSPlusExtentDensity
;
2004 *catalogExtents
= extents
;
2007 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2009 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2024 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2025 * position and number of contiguous bytes at that position.
2028 * logicalOffset Logical offset in bytes from start of file
2029 * length Maximum number of bytes to map
2030 * blockSize Number of bytes per allocation block
2031 * extentCount Number of extents in file
2032 * extentList The file's extents
2035 * physicalOffset Physical offset in bytes from start of volume
2036 * availableBytes Number of bytes physically contiguous (up to length)
2038 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2040 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2041 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2042 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2045 u_int32_t logicalBlock
;
2047 u_int32_t blockCount
= 0;
2049 /* Determine allocation block containing logicalOffset */
2050 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
2051 offset
%= blockSize
; /* Offset from start of allocation block */
2053 /* Find the extent containing logicalBlock */
2054 for (extent
= 0; extent
< extentCount
; ++extent
)
2056 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2058 if (blockCount
== 0)
2059 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2061 if (logicalBlock
< blockCount
)
2062 break; /* Found it! */
2064 logicalBlock
-= blockCount
;
2067 if (extent
>= extentCount
)
2068 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2071 * When we get here, extentList[extent] is the extent containing logicalOffset.
2072 * The desired allocation block is logicalBlock blocks into the extent.
2075 /* Compute the physical starting position */
2076 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2077 temp
*= blockSize
; /* Byte offset of first physical block */
2078 *physicalOffset
= temp
+ offset
;
2080 /* Compute the available contiguous bytes. */
2081 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2083 temp
-= offset
; /* Number of bytes available */
2086 *availableBytes
= temp
;
2088 *availableBytes
= length
;
2090 return FSUR_IO_SUCCESS
;
2096 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2097 * ending position are not allocation or device block aligned.
2100 * fd Descriptor for reading the volume
2101 * buffer The bytes are read into here
2102 * offset Offset in file to start reading
2103 * length Number of bytes to read
2104 * volOffset Byte offset from start of device to start of volume
2105 * blockSize Number of bytes per allocation block
2106 * extentCount Number of extents in file
2107 * extentList The file's exents
2109 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2111 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2112 off_t volOffset
, u_int32_t blockSize
,
2113 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2115 int result
= FSUR_IO_SUCCESS
;
2121 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2122 &physOffset
, &physLength
);
2123 if (result
!= FSUR_IO_SUCCESS
)
2126 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2127 if (result
!= FSUR_IO_SUCCESS
)
2130 length
-= physLength
;
2131 offset
+= physLength
;
2132 buffer
= (char *) buffer
+ physLength
;
2139 -- readAt = lseek() + read()
2141 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2146 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2151 void * rawData
= NULL
;
2154 ssize_t dataOffset
= 0;
2155 int result
= FSUR_IO_SUCCESS
;
2157 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2159 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2161 result
= FSUR_IO_FAIL
;
2164 /* put offset and length in terms of device blocksize */
2165 rawOffset
= offset
/ blocksize
* blocksize
;
2166 dataOffset
= offset
- rawOffset
;
2167 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2168 rawData
= malloc(rawLength
);
2169 if (rawData
== NULL
) {
2170 result
= FSUR_IO_FAIL
;
2174 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2175 if ( lseekResult
!= rawOffset
) {
2176 result
= FSUR_IO_FAIL
;
2180 readResult
= read(fd
, rawData
, rawLength
);
2181 if ( readResult
!= rawLength
) {
2183 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2185 result
= FSUR_IO_FAIL
;
2188 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2199 -- writeAt = lseek() + write()
2201 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2206 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2210 ssize_t bytestransferred
;
2211 void * rawData
= NULL
;
2214 ssize_t dataOffset
= 0;
2215 int result
= FSUR_IO_SUCCESS
;
2217 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2219 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2221 result
= FSUR_IO_FAIL
;
2224 /* put offset and length in terms of device blocksize */
2225 rawOffset
= offset
/ blocksize
* blocksize
;
2226 dataOffset
= offset
- rawOffset
;
2227 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2228 rawData
= malloc(rawLength
);
2229 if (rawData
== NULL
) {
2230 result
= FSUR_IO_FAIL
;
2234 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2235 if ( deviceoffset
!= rawOffset
) {
2236 result
= FSUR_IO_FAIL
;
2240 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2241 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2242 bytestransferred
= read(fd
, rawData
, rawLength
);
2243 if ( bytestransferred
!= rawLength
) {
2245 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2247 result
= FSUR_IO_FAIL
;
2252 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2254 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2255 if ( deviceoffset
!= rawOffset
) {
2256 result
= FSUR_IO_FAIL
;
2260 bytestransferred
= write(fd
, rawData
, rawLength
);
2261 if ( bytestransferred
!= rawLength
) {
2263 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2265 result
= FSUR_IO_FAIL
;
2270 if (rawData
) free(rawData
);
2278 * Get kernel's encoding bias.
2284 size_t buflen
= sizeof(int);
2288 if (getvfsbyname("hfs", &vfc
) < 0)
2292 mib
[1] = vfc
.vfc_typenum
;
2293 mib
[2] = HFS_ENCODINGBIAS
;
2295 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2302 /******************************************************************************
2304 * 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
2306 *****************************************************************************/
2308 #define DBHANDLESIGNATURE 0x75917737
2310 /* Flag values for operation options: */
2311 #define DBMARKPOSITION 1
2313 static char gVSDBPath
[] = "/var/db/volinfo.database";
2315 #define MAXIOMALLOC 16384
2317 /* Database layout: */
2324 char statusFlags
[8];
2331 struct VSDBRecord record
;
2335 #define DBKEYSEPARATOR ':'
2336 #define DBBLANKSPACE ' '
2337 #define DBRECORDTERMINATOR '\n'
2339 /* In-memory data structures: */
2342 unsigned long signature
;
2345 off_t recordPosition
;
2348 typedef struct VSDBState
*VSDBStatePtr
;
2352 /* Internal function prototypes: */
2353 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2354 static int UnlockDB(VSDBStatePtr dbstateptr
);
2356 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2357 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2358 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2359 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2360 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2362 static void FormatULong(unsigned long u
, char *s
);
2363 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2364 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2365 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2366 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2367 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2371 /******************************************************************************
2373 * 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
2375 *****************************************************************************/
2377 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2379 char randomInputBuffer
[26];
2380 unsigned char digest
[20];
2385 char sysctlstring
[128];
2387 double sysloadavg
[3];
2388 struct vmtotal sysvmtotal
;
2391 /* Initialize the SHA-1 context for processing: */
2392 SHA1_Init(&context
);
2394 /* Now process successive bits of "random" input to seed the process: */
2396 /* The current system's uptime: */
2398 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2400 /* The kernel's boot time: */
2402 mib
[1] = KERN_BOOTTIME
;
2403 datalen
= sizeof(sysdata
);
2404 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2405 SHA1_Update(&context
, &sysdata
, datalen
);
2407 /* The system's host id: */
2409 mib
[1] = KERN_HOSTID
;
2410 datalen
= sizeof(sysdata
);
2411 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2412 SHA1_Update(&context
, &sysdata
, datalen
);
2414 /* The system's host name: */
2416 mib
[1] = KERN_HOSTNAME
;
2417 datalen
= sizeof(sysctlstring
);
2418 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2419 SHA1_Update(&context
, sysctlstring
, datalen
);
2421 /* The running kernel's OS release string: */
2423 mib
[1] = KERN_OSRELEASE
;
2424 datalen
= sizeof(sysctlstring
);
2425 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2426 SHA1_Update(&context
, sysctlstring
, datalen
);
2428 /* The running kernel's version string: */
2430 mib
[1] = KERN_VERSION
;
2431 datalen
= sizeof(sysctlstring
);
2432 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2433 SHA1_Update(&context
, sysctlstring
, datalen
);
2435 /* The system's load average: */
2436 datalen
= sizeof(sysloadavg
);
2437 getloadavg(sysloadavg
, 3);
2438 SHA1_Update(&context
, &sysloadavg
, datalen
);
2440 /* The system's VM statistics: */
2443 datalen
= sizeof(sysvmtotal
);
2444 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2445 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2447 /* The current GMT (26 ASCII characters): */
2449 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2450 SHA1_Update(&context
, randomInputBuffer
, 26);
2452 /* Pad the accumulated input and extract the final digest hash: */
2453 SHA1_Final(digest
, &context
);
2455 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2456 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2461 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2464 unsigned long nextdigit
;
2465 unsigned long high
= 0;
2466 unsigned long low
= 0;
2467 unsigned long carry
;
2469 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2470 if ((c
>= '0') && (c
<= '9')) {
2471 nextdigit
= c
- '0';
2472 } else if ((c
>= 'A') && (c
<= 'F')) {
2473 nextdigit
= c
- 'A' + 10;
2474 } else if ((c
>= 'a') && (c
<= 'f')) {
2475 nextdigit
= c
- 'a' + 10;
2479 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2480 high
= (high
<< 4) | carry
;
2481 low
= (low
<< 4) | nextdigit
;
2484 volumeID
->v
.high
= high
;
2485 volumeID
->v
.low
= low
;
2490 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2491 FormatUUID(volumeID
, UUIDString
);
2492 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2497 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2498 VSDBStatePtr dbstateptr
;
2500 *DBHandlePtr
= NULL
;
2502 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2503 if (dbstateptr
== NULL
) {
2507 dbstateptr
->dbmode
= O_RDWR
;
2508 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2509 if (dbstateptr
->dbfile
== -1) {
2511 The file couldn't be opened for read/write access:
2512 try read-only access before giving up altogether.
2514 dbstateptr
->dbmode
= O_RDONLY
;
2515 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2516 if (dbstateptr
->dbfile
== -1) {
2521 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2522 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2528 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2529 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2530 struct VSDBEntry dbentry
;
2533 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2535 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2537 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2540 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2545 UnlockDB(dbstateptr
);
2551 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2552 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2553 struct VSDBEntry dbentry
;
2556 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2557 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2559 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2561 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2562 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2564 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2566 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2567 } else if (result
== -1) {
2569 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2571 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2576 fsync(dbstateptr
->dbfile
);
2581 UnlockDB(dbstateptr
);
2587 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2588 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2591 unsigned long iobuffersize
;
2592 void *iobuffer
= NULL
;
2594 unsigned long iotransfersize
;
2595 unsigned long bytestransferred
;
2597 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2599 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2601 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2603 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2605 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2609 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2611 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2612 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2613 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2615 iobuffersize
= MAXIOMALLOC
;
2618 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2619 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2620 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2622 if (iobuffersize
> 0) {
2623 iobuffer
= malloc(iobuffersize
);
2624 if (iobuffer
== NULL
) {
2629 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2631 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2632 if (iotransfersize
> 0) {
2633 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2636 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2638 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2639 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2640 if (bytestransferred
!= iotransfersize
) {
2646 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2648 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2649 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2650 if (bytestransferred
!= iotransfersize
) {
2655 dataoffset
+= (off_t
)iotransfersize
;
2657 } while (iotransfersize
> 0);
2660 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2662 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2666 fsync(dbstateptr
->dbfile
);
2672 if (iobuffer
) free(iobuffer
);
2673 UnlockDB(dbstateptr
);
2681 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2682 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2684 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2686 dbstateptr
->signature
= 0;
2688 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2689 dbstateptr
->dbfile
= 0;
2698 /******************************************************************************
2700 * 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
2702 *****************************************************************************/
2704 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2706 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2708 return flock(dbstateptr
->dbfile
, lockmode
);
2713 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2715 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2717 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2722 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2723 struct VSDBKey searchkey
;
2724 struct VSDBEntry dbentry
;
2727 FormatDBKey(volumeID
, &searchkey
);
2728 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2731 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2732 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2733 if (targetEntry
!= NULL
) {
2735 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2737 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2741 } while (result
== 0);
2748 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2750 VolumeUUIDString id
;
2754 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2755 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2756 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2758 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2759 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2765 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2767 VolumeUUIDString id
;
2771 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2772 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2773 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2775 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2777 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2779 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2784 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2785 struct VSDBEntry entry
;
2788 VolumeUUIDString id
;
2791 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2792 #if 0 // DEBUG_TRACE
2793 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2795 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2796 if ((result
!= sizeof(entry
)) ||
2797 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2798 (entry
.space
!= DBBLANKSPACE
) ||
2799 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2804 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2805 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2806 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2808 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2814 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2815 #if 0 // DEBUG_TRACE
2816 VolumeUUIDString id
;
2818 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2819 id
[sizeof(key1
->uuid
)] = (char)0;
2820 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2821 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2822 id
[sizeof(key2
->uuid
)] = (char)0;
2823 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2826 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2831 /******************************************************************************
2833 * 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
2835 *****************************************************************************/
2837 static void FormatULong(unsigned long u
, char *s
) {
2842 for (i
= 0; i
< 8; ++i
) {
2843 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2845 *digitptr
++ = (char)(d
+ '0');
2847 *digitptr
++ = (char)(d
- 10 + 'A');
2855 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2856 FormatULong(volumeID
->v
.high
, UUIDField
);
2857 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2863 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2864 FormatUUID(volumeID
, dbkey
->uuid
);
2869 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2870 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
2875 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
2876 FormatDBKey(volumeID
, &dbentry
->key
);
2877 dbentry
->keySeparator
= DBKEYSEPARATOR
;
2878 dbentry
->space
= DBBLANKSPACE
;
2879 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
2880 #if 0 // DEBUG_TRACE
2881 dbentry
->terminator
= (char)0;
2882 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
2884 dbentry
->terminator
= DBRECORDTERMINATOR
;
2889 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
2892 unsigned long nextdigit
;
2896 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
2897 if ((c
>= '0') && (c
<= '9')) {
2898 nextdigit
= c
- '0';
2899 } else if ((c
>= 'A') && (c
<= 'F')) {
2900 nextdigit
= c
- 'A' + 10;
2901 } else if ((c
>= 'a') && (c
<= 'f')) {
2902 nextdigit
= c
- 'a' + 10;
2906 n
= (n
<< 4) + nextdigit
;