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 * rawDeviceNamePtr
, char * blockDeviceNamePtr
);
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
, blockDeviceName
);
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
);
659 Get the volume name of the volume mounted at "path". Print that volume
660 name to standard out.
662 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
664 struct VolumeNameBuf
{
665 u_int32_t info_length
;
666 attrreference_t name_ref
;
671 PrintVolumeNameAttr(const char *path
)
673 struct attrlist alist
;
674 struct VolumeNameBuf volNameInfo
;
677 /* Set up the attrlist structure to get the volume's Finder Info */
678 alist
.bitmapcount
= 5;
680 alist
.commonattr
= 0;
681 alist
.volattr
= ATTR_VOL_INFO
| ATTR_VOL_NAME
;
686 /* Get the Finder Info */
687 result
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0);
689 result
= FSUR_IO_FAIL
;
693 /* Print the name to standard out */
694 printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
);
695 result
= FSUR_RECOGNIZED
;
702 /* ******************************************* DoProbe **********************************************
704 This routine will open the given device and check to make sure there is media that looks
705 like an HFS. If it is HFS, then print the volume name to standard output.
707 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
708 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
710 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
711 *************************************************************************************************** */
713 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
)
715 int result
= FSUR_UNRECOGNIZED
;
718 HFSMasterDirectoryBlock
* mdbPtr
;
719 HFSPlusVolumeHeader
* volHdrPtr
;
720 u_char volnameUTF8
[NAME_MAX
+1];
723 * Determine if there is a volume already mounted from this device. If
724 * there is, and it is HFS, then we need to get the volume name via
727 * NOTE: We're using bufPtr to hold a pointer to a path.
730 result
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
);
731 if (result
!= FSUR_IO_SUCCESS
) {
734 if (bufPtr
!= NULL
) {
735 /* There is an HFS volume mounted from the device. */
736 result
= PrintVolumeNameAttr(bufPtr
);
741 * If we get here, there is no volume mounted from this device, so
742 * go probe the raw device directly.
745 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
747 result
= FSUR_UNRECOGNIZED
;
751 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
752 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
754 fd
= open( rawDeviceNamePtr
, O_RDONLY
, 0 );
756 result
= FSUR_IO_FAIL
;
761 * Read the HFS Master Directory Block from sector 2
763 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
764 if (FSUR_IO_FAIL
== result
)
767 /* get classic HFS volume name (from MDB) */
768 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
769 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
772 CFStringEncoding encoding
;
774 /* Some poorly mastered HFS CDs have an empty MDB name field! */
775 if (mdbPtr
->drVN
[0] == '\0') {
776 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
777 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
780 /* Check for an encoding hint in the Finder Info (field 4). */
781 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
782 if (encoding
== kCFStringEncodingInvalidId
) {
783 /* Next try the encoding bias in the kernel. */
784 encoding
= GetEncodingBias();
785 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
786 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
789 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
790 mdbPtr
->drVN
, encoding
);
791 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
794 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
796 /* default to MacRoman on conversion errors */
797 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
798 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
799 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
801 encoding
= kCFStringEncodingMacRoman
;
804 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
805 if (encoding
!= kCFStringEncodingMacRoman
) {
806 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
807 encoding
= kCFStringEncodingMacRoman
;
808 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
809 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
814 /* get HFS Plus volume name (from Catalog) */
815 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
816 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
817 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
818 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
821 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
822 /* embedded volume, first find offset */
823 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
824 if ( result
!= FSUR_IO_SUCCESS
)
830 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
833 result
= FSUR_UNRECOGNIZED
;
836 if (FSUR_IO_SUCCESS
== result
) {
839 /* Change slashes to colons in the volume name */
840 for (s
=volnameUTF8
; *s
; ++s
) {
845 /* Print the volume name to standard output */
846 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
847 result
= FSUR_RECOGNIZED
;
863 * Create a version 3 UUID from a unique "name" in the given "name space".
864 * Version 3 UUID are derived using "name" via MD5 checksum.
867 * result_uuid - resulting UUID.
868 * namespace - namespace in which given name exists and UUID should be created.
869 * name - unique string used to create version 3 UUID.
870 * namelen - length of the name string.
873 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
878 MD5_Update(&c
, namespace, sizeof(uuid_t
));
879 MD5_Update(&c
, name
, namelen
);
880 MD5_Final(result_uuid
, &c
);
882 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
883 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
887 /* **************************************** DoGetUUIDKey *******************************************
889 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
891 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
893 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
894 *************************************************************************************************** */
896 DoGetUUIDKey( const char * theDeviceNamePtr
) {
898 VolumeUUID targetVolumeUUID
;
902 unsigned char rawUUID
[8];
904 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
906 ((uint32_t *)rawUUID
)[0] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.high
);
907 ((uint32_t *)rawUUID
)[1] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.low
);
909 uuid_create_md5_from_name(uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
910 uuid_unparse(uuid
, uuidLine
);
911 write(1, uuidLine
, strlen(uuidLine
));
912 result
= FSUR_IO_SUCCESS
;
920 /* *************************************** DoChangeUUIDKey ******************************************
922 This routine will change the UUID on the specified block device.
924 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
926 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
927 *************************************************************************************************** */
929 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
931 VolumeUUID newVolumeUUID
;
933 GenerateVolumeUUID(&newVolumeUUID
);
934 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
941 /* **************************************** DoAdopt *******************************************
943 This routine will add the UUID of the specified block device to the list of local volumes.
945 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
947 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
948 *************************************************************************************************** */
950 DoAdopt( const char * theDeviceNamePtr
) {
951 int result
, closeresult
;
952 VolumeUUID targetVolumeUUID
;
953 VolumeStatusDBHandle vsdbhandle
= NULL
;
954 unsigned long targetVolumeStatus
;
956 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
958 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
959 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
960 targetVolumeStatus
= 0;
962 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
963 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
965 result
= FSUR_IO_SUCCESS
;
969 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
971 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
974 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
978 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
985 /* **************************************** DoDisown *******************************************
987 This routine will change the status of the specified block device to ignore its permissions.
989 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
991 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
992 *************************************************************************************************** */
994 DoDisown( const char * theDeviceNamePtr
) {
995 int result
, closeresult
;
996 VolumeUUID targetVolumeUUID
;
997 VolumeStatusDBHandle vsdbhandle
= NULL
;
998 unsigned long targetVolumeStatus
;
1000 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1002 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1003 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1004 targetVolumeStatus
= 0;
1006 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
1007 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1009 result
= FSUR_IO_SUCCESS
;
1013 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1015 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1018 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
1020 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
1022 result
= FSUR_IO_FAIL
;
1027 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
1034 get_multiplier(char c
)
1036 if (tolower(c
) == 'k') {
1038 } else if (tolower(c
) == 'm') {
1040 } else if (tolower(c
) == 'g') {
1041 return 1024 * 1024 * 1024;
1047 /* **************************************** ParseArgs ********************************************
1049 This routine will make sure the arguments passed in to us are cool.
1050 Here is how this utility is used:
1052 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1054 -p (Probe for mounting)
1055 -P (Probe for initializing - not supported)
1057 -r (Repair - not supported)
1060 -i (Initialize - not supported)
1063 disk0s2 (for example)
1066 /foo/bar/ (required for Mount and Force Mount actions)
1069 (these are ignored for CDROMs)
1070 either "readonly" OR "writable"
1071 either "removable" OR "fixed"
1072 either "nosuid" or "suid"
1073 either "nodev" or "dev"
1076 hfs.util -p disk0s2 removable writable
1077 hfs.util -p disk0s2 removable readonly
1078 hfs.util -m disk0s2 /my/hfs
1081 argc - the number of arguments in argv.
1082 argv - array of arguments.
1084 returns FSUR_INVAL if we find a bad argument else 0.
1085 *************************************************************************************************** */
1087 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1088 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1089 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1091 int result
= FSUR_INVAL
;
1092 int deviceLength
, doLengthCheck
= 1;
1096 /* Must have at least 3 arguments and the action argument must start with a '-' */
1097 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1098 DoDisplayUsage( argv
);
1102 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1104 * actionPtr
= & argv
[1][1];
1106 switch ( argv
[1][1] ) {
1108 /* action Probe and requires 5 arguments (need the flags) */
1110 DoDisplayUsage( argv
);
1118 /* Note: the device argument in argv[2] is checked further down but ignored. */
1119 * mountPointPtr
= argv
[3];
1120 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1124 case FSUC_MOUNT_FORCE
:
1125 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1127 DoDisplayUsage( argv
);
1130 * mountPointPtr
= argv
[3];
1156 if (isdigit(argv
[2][0])) {
1158 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1160 gJournalSize
*= get_multiplier(*ptr
);
1171 case FSUC_UNJNL_RAW
:
1183 DoDisplayUsage( argv
);
1188 /* Make sure device (argv[2]) is something reasonable */
1189 deviceLength
= strlen( argv
[2] );
1190 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1191 DoDisplayUsage( argv
);
1196 /* Flags: removable/fixed. */
1197 if ( 0 == strcmp(argv
[index
],"removable") ) {
1198 * isEjectablePtr
= 1;
1199 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1200 * isEjectablePtr
= 0;
1202 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1205 /* Flags: readonly/writable. */
1206 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1208 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1211 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1215 /* Flags: suid/nosuid. */
1216 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1218 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1221 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1224 /* Flags: dev/nodev. */
1225 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1227 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1230 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1245 /* *************************************** DoDisplayUsage ********************************************
1247 This routine will do a printf of the correct usage for this utility.
1249 argv - array of arguments.
1252 *************************************************************************************************** */
1254 DoDisplayUsage(const char *argv
[])
1256 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1257 printf("action_arg:\n");
1258 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1259 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1260 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1261 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1262 #ifdef HFS_UUID_SUPPORT
1263 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1264 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1265 #endif HFS_UUID_SUPPORT
1266 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1267 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1268 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1269 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1270 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1271 printf("device_arg:\n");
1272 printf(" device we are acting upon (for example, 'disk0s2')\n");
1273 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1274 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1275 printf("mount_point_arg:\n");
1276 printf(" required for Mount and Force Mount \n");
1278 printf(" required for Mount, Force Mount and Probe\n");
1279 printf(" indicates removable or fixed (for example 'fixed')\n");
1280 printf(" indicates readonly or writable (for example 'readonly')\n");
1281 printf(" indicates suid or nosuid (for example 'suid')\n");
1282 printf(" indicates dev or nodev (for example 'dev')\n");
1283 printf("Examples:\n");
1284 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1285 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1289 } /* DoDisplayUsage */
1295 Given a path to a device, determine if a volume is mounted on that
1296 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1297 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1298 no volume mounted on the device, set *pathPtr to NULL and return
1301 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1304 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1310 /* Assume no mounted volume found */
1312 result
= FSUR_IO_SUCCESS
;
1314 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1316 return FSUR_IO_FAIL
;
1318 for (i
=0; i
<numMounts
; ++i
) {
1319 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1320 /* Found a mounted volume; check the type */
1321 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1322 *pathPtr
= buf
[i
].f_mntonname
;
1323 /* result = FSUR_IO_SUCCESS, above */
1325 result
= FSUR_UNRECOGNIZED
;
1338 Read the Master Directory Block or Volume Header Block from an HFS,
1339 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1340 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1341 Return a pointer to the volume UUID in the Finder Info.
1343 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1346 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1349 HFSMasterDirectoryBlock
* mdbPtr
;
1350 HFSPlusVolumeHeader
* volHdrPtr
;
1356 * Read the HFS Master Directory Block or Volume Header from sector 2
1359 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1360 if (result
!= FSUR_IO_SUCCESS
)
1364 * If this is a wrapped HFS Plus volume, read the Volume Header from
1365 * sector 2 of the embedded volume.
1367 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1368 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1369 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1370 if (result
!= FSUR_IO_SUCCESS
)
1372 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1373 if (result
!= FSUR_IO_SUCCESS
)
1378 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1379 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1380 * UUID from the Finder Info.
1382 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1383 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1384 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1385 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1386 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1388 result
= FSUR_UNRECOGNIZED
;
1399 Read the UUID from an unmounted volume, by doing direct access to the device.
1400 Assumes the caller has already determined that a volume is not mounted
1403 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1406 GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1411 VolumeUUID
*finderInfoUUIDPtr
;
1414 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1416 result
= FSUR_UNRECOGNIZED
;
1420 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1423 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1425 result
= FSUR_IO_FAIL
;
1430 * Get the pointer to the volume UUID in the Finder Info
1432 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1433 if (result
!= FSUR_IO_SUCCESS
)
1437 * Copy the volume UUID out of the Finder Info
1439 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1440 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1443 if (fd
> 0) close(fd
);
1444 if (bufPtr
) free(bufPtr
);
1447 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1449 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1456 Write a previously generated UUID to an unmounted volume, by doing direct
1457 access to the device. Assumes the caller has already determined that a
1458 volume is not mounted on the device.
1460 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1463 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1468 VolumeUUID
*finderInfoUUIDPtr
;
1471 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1473 result
= FSUR_UNRECOGNIZED
;
1477 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1480 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1482 result
= FSUR_IO_FAIL
;
1487 * Get the pointer to the volume UUID in the Finder Info
1489 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1490 if (result
!= FSUR_IO_SUCCESS
)
1494 * Update the UUID in the Finder Info
1496 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1497 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1500 * Write the modified MDB or VHB back to disk
1502 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1505 if (fd
> 0) close(fd
);
1506 if (bufPtr
) free(bufPtr
);
1509 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1511 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1518 Read the UUID from a mounted volume, by calling getattrlist().
1519 Assumes the path is the mount point of an HFS volume.
1521 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1524 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1526 struct attrlist alist
;
1527 struct FinderAttrBuf volFinderInfo
;
1528 VolumeUUID
*finderInfoUUIDPtr
;
1531 /* Set up the attrlist structure to get the volume's Finder Info */
1532 alist
.bitmapcount
= 5;
1534 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1535 alist
.volattr
= ATTR_VOL_INFO
;
1540 /* Get the Finder Info */
1541 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1543 result
= FSUR_IO_FAIL
;
1547 /* Copy the UUID from the Finder Into to caller's buffer */
1548 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1549 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1550 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1551 result
= FSUR_IO_SUCCESS
;
1561 Write a UUID to a mounted volume, by calling setattrlist().
1562 Assumes the path is the mount point of an HFS volume.
1564 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1567 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1569 struct attrlist alist
;
1570 struct FinderAttrBuf volFinderInfo
;
1571 VolumeUUID
*finderInfoUUIDPtr
;
1574 /* Set up the attrlist structure to get the volume's Finder Info */
1575 alist
.bitmapcount
= 5;
1577 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1578 alist
.volattr
= ATTR_VOL_INFO
;
1583 /* Get the Finder Info */
1584 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1586 result
= FSUR_IO_FAIL
;
1590 /* Update the UUID in the Finder Info */
1591 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1592 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1593 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1595 /* Write the Finder Info back to the volume */
1596 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1598 result
= FSUR_IO_FAIL
;
1602 result
= FSUR_IO_SUCCESS
;
1612 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1613 we were asked to generate one, then generate a new UUID and write it to the
1616 Determine whether an HFS volume is mounted on the given device. If so, we
1617 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1618 the filesystem. If there is no mounted volume, then do direct device access
1619 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1621 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1625 GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1631 * Determine whether a volume is mounted on this device. If it is HFS, then
1632 * get the mount point's path. If it is non-HFS, then we can exit immediately
1633 * with FSUR_UNRECOGNIZED.
1635 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1636 if (result
!= FSUR_IO_SUCCESS
)
1640 * Get any existing UUID.
1643 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1645 result
= GetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1646 if (result
!= FSUR_IO_SUCCESS
)
1650 * If there was no valid UUID, and we were asked to generate one, then
1651 * generate it and write it back to disk.
1653 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1654 GenerateVolumeUUID(volumeUUIDPtr
);
1656 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1658 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1659 /* Fall through to Err_Exit */
1671 Write a UUID to an HFS, HFS Plus or HFSX volume.
1673 Determine whether an HFS volume is mounted on the given device. If so, we
1674 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1675 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1677 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1680 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1685 * Determine whether a volume is mounted on this device. If it is HFS, then
1686 * get the mount point's path. If it is non-HFS, then we can exit immediately
1687 * with FSUR_UNRECOGNIZED.
1689 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1690 if (result
!= FSUR_IO_SUCCESS
)
1697 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1699 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1708 -- GetEmbeddedHFSPlusVol
1710 -- In: hfsMasterDirectoryBlockPtr
1711 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1712 (that is, 2 blocks before the volume header)
1717 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1719 int result
= FSUR_IO_SUCCESS
;
1720 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1722 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1723 result
= FSUR_UNRECOGNIZED
;
1727 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1728 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1730 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1731 result
= FSUR_UNRECOGNIZED
;
1735 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1736 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1738 if ( startOffsetPtr
)
1739 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1740 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1750 -- GetNameFromHFSPlusVolumeStartingAt
1752 -- Caller's responsibility to allocate and release memory for the converted string.
1754 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1758 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1760 int result
= FSUR_IO_SUCCESS
;
1761 u_int32_t blockSize
;
1762 char * bufPtr
= NULL
;
1763 HFSPlusVolumeHeader
* volHdrPtr
;
1764 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1765 u_int32_t catalogNodeSize
;
1767 u_int32_t catalogExtCount
;
1768 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1770 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1771 if ( ! volHdrPtr
) {
1772 result
= FSUR_IO_FAIL
;
1777 * Read the Volume Header
1778 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1780 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1781 if (result
== FSUR_IO_FAIL
) {
1783 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1785 goto Return
; // return FSUR_IO_FAIL
1788 /* Verify that it is an HFS+ volume. */
1790 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1791 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
1792 result
= FSUR_IO_FAIL
;
1794 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1799 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1800 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1801 if ( ! catalogExtents
) {
1802 result
= FSUR_IO_FAIL
;
1805 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1806 catalogExtCount
= kHFSPlusExtentDensity
;
1808 /* if there are overflow catalog extents, then go get them */
1809 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
1810 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1811 if (result
!= FSUR_IO_SUCCESS
)
1815 /* Read the header node of the catalog B-Tree */
1817 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1818 catalogExtCount
, catalogExtents
,
1819 &catalogNodeSize
, &leafNode
);
1820 if (result
!= FSUR_IO_SUCCESS
)
1823 /* Read the first leaf node of the catalog b-tree */
1825 bufPtr
= (char *)malloc(catalogNodeSize
);
1827 result
= FSUR_IO_FAIL
;
1831 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1833 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1834 hfsPlusVolumeOffset
, blockSize
,
1835 catalogExtCount
, catalogExtents
);
1836 if (result
== FSUR_IO_FAIL
) {
1838 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1840 goto Return
; // return FSUR_IO_FAIL
1846 HFSPlusCatalogKey
* k
;
1849 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
1850 result
= FSUR_IO_FAIL
;
1852 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1857 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1859 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1862 // Get a pointer to the first record.
1864 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
1865 k
= (HFSPlusCatalogKey
*)p
;
1867 // There should be only one record whose parent is the root parent. It should be the first record.
1869 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
1870 result
= FSUR_IO_FAIL
;
1872 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1877 /* Extract the name of the root directory */
1880 HFSUniStr255
*swapped
;
1883 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1884 if (swapped
== NULL
) {
1885 result
= FSUR_IO_FAIL
;
1888 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
1890 for (i
=0; i
<swapped
->length
; i
++) {
1891 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
1893 swapped
->unicode
[i
] = 0;
1894 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1895 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1901 result
= FSUR_IO_SUCCESS
;
1905 free((char*) volHdrPtr
);
1908 free((char*) catalogExtents
);
1911 free((char*)bufPtr
);
1915 } /* GetNameFromHFSPlusVolumeStartingAt */
1918 #pragma options align=mac68k
1920 BTNodeDescriptor node
;
1922 } HeaderRec
, *HeaderPtr
;
1923 #pragma options align=reset
1928 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1932 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1933 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1934 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1937 HeaderRec
* bTreeHeaderPtr
= NULL
;
1939 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1940 if (bTreeHeaderPtr
== NULL
)
1941 return (FSUR_IO_FAIL
);
1943 /* Read the b-tree header node */
1945 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
1946 hfsPlusVolumeOffset
, blockSize
,
1947 extentCount
, extentList
);
1948 if ( result
== FSUR_IO_FAIL
) {
1950 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
1955 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
1956 result
= FSUR_IO_FAIL
;
1958 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1963 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
1965 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
1968 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
1971 free((char*) bTreeHeaderPtr
);
1975 } /* GetBTreeNodeInfo */
1981 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1985 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
1986 HFSPlusVolumeHeader
*volHdrPtr
,
1987 HFSPlusExtentDescriptor
**catalogExtents
,
1988 u_int32_t
*catalogExtCount
)
1991 u_int32_t numRecords
;
1994 u_int32_t blockSize
;
1995 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1996 HFSPlusExtentDescriptor
* extents
;
1998 char * bufPtr
= NULL
;
2002 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2003 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
2004 extents
= *catalogExtents
;
2005 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
2008 /* Read the header node of the extents B-Tree */
2010 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2011 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
2012 &nodeSize
, &leafNode
);
2013 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
2016 /* Calculate the logical position of the first leaf node */
2018 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2020 /* Read the first leaf node of the extents b-tree */
2022 bufPtr
= (char *)malloc(nodeSize
);
2024 result
= FSUR_IO_FAIL
;
2028 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2031 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
2032 hfsPlusVolumeOffset
, blockSize
,
2033 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
2034 if ( result
== FSUR_IO_FAIL
) {
2036 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2041 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
2042 result
= FSUR_IO_FAIL
;
2046 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
2047 for (i
= 1; i
<= numRecords
; ++i
) {
2050 HFSPlusExtentKey
* k
;
2053 * Get the offset (in bytes) of the record from the
2054 * list of offsets at the end of the node
2056 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
2059 /* Get a pointer to the record */
2061 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
2062 k
= (HFSPlusExtentKey
*)p
;
2064 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
2067 /* grow list and copy additional extents */
2068 listsize
+= sizeof(HFSPlusExtentRecord
);
2069 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2070 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2071 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2073 *catalogExtCount
+= kHFSPlusExtentDensity
;
2074 *catalogExtents
= extents
;
2077 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2079 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2094 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2095 * position and number of contiguous bytes at that position.
2098 * logicalOffset Logical offset in bytes from start of file
2099 * length Maximum number of bytes to map
2100 * blockSize Number of bytes per allocation block
2101 * extentCount Number of extents in file
2102 * extentList The file's extents
2105 * physicalOffset Physical offset in bytes from start of volume
2106 * availableBytes Number of bytes physically contiguous (up to length)
2108 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2110 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2111 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2112 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2115 u_int32_t logicalBlock
;
2117 u_int32_t blockCount
= 0;
2119 /* Determine allocation block containing logicalOffset */
2120 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
2121 offset
%= blockSize
; /* Offset from start of allocation block */
2123 /* Find the extent containing logicalBlock */
2124 for (extent
= 0; extent
< extentCount
; ++extent
)
2126 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2128 if (blockCount
== 0)
2129 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2131 if (logicalBlock
< blockCount
)
2132 break; /* Found it! */
2134 logicalBlock
-= blockCount
;
2137 if (extent
>= extentCount
)
2138 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2141 * When we get here, extentList[extent] is the extent containing logicalOffset.
2142 * The desired allocation block is logicalBlock blocks into the extent.
2145 /* Compute the physical starting position */
2146 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2147 temp
*= blockSize
; /* Byte offset of first physical block */
2148 *physicalOffset
= temp
+ offset
;
2150 /* Compute the available contiguous bytes. */
2151 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2153 temp
-= offset
; /* Number of bytes available */
2156 *availableBytes
= temp
;
2158 *availableBytes
= length
;
2160 return FSUR_IO_SUCCESS
;
2166 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2167 * ending position are not allocation or device block aligned.
2170 * fd Descriptor for reading the volume
2171 * buffer The bytes are read into here
2172 * offset Offset in file to start reading
2173 * length Number of bytes to read
2174 * volOffset Byte offset from start of device to start of volume
2175 * blockSize Number of bytes per allocation block
2176 * extentCount Number of extents in file
2177 * extentList The file's exents
2179 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2181 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2182 off_t volOffset
, u_int32_t blockSize
,
2183 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2185 int result
= FSUR_IO_SUCCESS
;
2191 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2192 &physOffset
, &physLength
);
2193 if (result
!= FSUR_IO_SUCCESS
)
2196 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2197 if (result
!= FSUR_IO_SUCCESS
)
2200 length
-= physLength
;
2201 offset
+= physLength
;
2202 buffer
= (char *) buffer
+ physLength
;
2209 -- readAt = lseek() + read()
2211 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2216 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2221 void * rawData
= NULL
;
2224 ssize_t dataOffset
= 0;
2225 int result
= FSUR_IO_SUCCESS
;
2227 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2229 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2231 result
= FSUR_IO_FAIL
;
2234 /* put offset and length in terms of device blocksize */
2235 rawOffset
= offset
/ blocksize
* blocksize
;
2236 dataOffset
= offset
- rawOffset
;
2237 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2238 rawData
= malloc(rawLength
);
2239 if (rawData
== NULL
) {
2240 result
= FSUR_IO_FAIL
;
2244 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2245 if ( lseekResult
!= rawOffset
) {
2246 result
= FSUR_IO_FAIL
;
2250 readResult
= read(fd
, rawData
, rawLength
);
2251 if ( readResult
!= rawLength
) {
2253 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2255 result
= FSUR_IO_FAIL
;
2258 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2269 -- writeAt = lseek() + write()
2271 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2276 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2280 ssize_t bytestransferred
;
2281 void * rawData
= NULL
;
2284 ssize_t dataOffset
= 0;
2285 int result
= FSUR_IO_SUCCESS
;
2287 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2289 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2291 result
= FSUR_IO_FAIL
;
2294 /* put offset and length in terms of device blocksize */
2295 rawOffset
= offset
/ blocksize
* blocksize
;
2296 dataOffset
= offset
- rawOffset
;
2297 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2298 rawData
= malloc(rawLength
);
2299 if (rawData
== NULL
) {
2300 result
= FSUR_IO_FAIL
;
2304 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2305 if ( deviceoffset
!= rawOffset
) {
2306 result
= FSUR_IO_FAIL
;
2310 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2311 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2312 bytestransferred
= read(fd
, rawData
, rawLength
);
2313 if ( bytestransferred
!= rawLength
) {
2315 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2317 result
= FSUR_IO_FAIL
;
2322 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2324 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2325 if ( deviceoffset
!= rawOffset
) {
2326 result
= FSUR_IO_FAIL
;
2330 bytestransferred
= write(fd
, rawData
, rawLength
);
2331 if ( bytestransferred
!= rawLength
) {
2333 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2335 result
= FSUR_IO_FAIL
;
2340 if (rawData
) free(rawData
);
2348 * Get kernel's encoding bias.
2354 size_t buflen
= sizeof(int);
2358 if (getvfsbyname("hfs", &vfc
) < 0)
2362 mib
[1] = vfc
.vfc_typenum
;
2363 mib
[2] = HFS_ENCODINGBIAS
;
2365 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2372 /******************************************************************************
2374 * 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
2376 *****************************************************************************/
2378 #define DBHANDLESIGNATURE 0x75917737
2380 /* Flag values for operation options: */
2381 #define DBMARKPOSITION 1
2383 static char gVSDBPath
[] = "/var/db/volinfo.database";
2385 #define MAXIOMALLOC 16384
2387 /* Database layout: */
2394 char statusFlags
[8];
2401 struct VSDBRecord record
;
2405 #define DBKEYSEPARATOR ':'
2406 #define DBBLANKSPACE ' '
2407 #define DBRECORDTERMINATOR '\n'
2409 /* In-memory data structures: */
2412 unsigned long signature
;
2415 off_t recordPosition
;
2418 typedef struct VSDBState
*VSDBStatePtr
;
2422 /* Internal function prototypes: */
2423 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2424 static int UnlockDB(VSDBStatePtr dbstateptr
);
2426 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2427 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2428 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2429 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2430 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2432 static void FormatULong(unsigned long u
, char *s
);
2433 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2434 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2435 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2436 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2437 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2441 /******************************************************************************
2443 * 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
2445 *****************************************************************************/
2447 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2449 char randomInputBuffer
[26];
2450 unsigned char digest
[20];
2455 char sysctlstring
[128];
2457 double sysloadavg
[3];
2458 struct vmtotal sysvmtotal
;
2461 /* Initialize the SHA-1 context for processing: */
2462 SHA1_Init(&context
);
2464 /* Now process successive bits of "random" input to seed the process: */
2466 /* The current system's uptime: */
2468 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2470 /* The kernel's boot time: */
2472 mib
[1] = KERN_BOOTTIME
;
2473 datalen
= sizeof(sysdata
);
2474 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2475 SHA1_Update(&context
, &sysdata
, datalen
);
2477 /* The system's host id: */
2479 mib
[1] = KERN_HOSTID
;
2480 datalen
= sizeof(sysdata
);
2481 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2482 SHA1_Update(&context
, &sysdata
, datalen
);
2484 /* The system's host name: */
2486 mib
[1] = KERN_HOSTNAME
;
2487 datalen
= sizeof(sysctlstring
);
2488 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2489 SHA1_Update(&context
, sysctlstring
, datalen
);
2491 /* The running kernel's OS release string: */
2493 mib
[1] = KERN_OSRELEASE
;
2494 datalen
= sizeof(sysctlstring
);
2495 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2496 SHA1_Update(&context
, sysctlstring
, datalen
);
2498 /* The running kernel's version string: */
2500 mib
[1] = KERN_VERSION
;
2501 datalen
= sizeof(sysctlstring
);
2502 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2503 SHA1_Update(&context
, sysctlstring
, datalen
);
2505 /* The system's load average: */
2506 datalen
= sizeof(sysloadavg
);
2507 getloadavg(sysloadavg
, 3);
2508 SHA1_Update(&context
, &sysloadavg
, datalen
);
2510 /* The system's VM statistics: */
2513 datalen
= sizeof(sysvmtotal
);
2514 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2515 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2517 /* The current GMT (26 ASCII characters): */
2519 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2520 SHA1_Update(&context
, randomInputBuffer
, 26);
2522 /* Pad the accumulated input and extract the final digest hash: */
2523 SHA1_Final(digest
, &context
);
2525 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2526 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2531 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2534 unsigned long nextdigit
;
2535 unsigned long high
= 0;
2536 unsigned long low
= 0;
2537 unsigned long carry
;
2539 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2540 if ((c
>= '0') && (c
<= '9')) {
2541 nextdigit
= c
- '0';
2542 } else if ((c
>= 'A') && (c
<= 'F')) {
2543 nextdigit
= c
- 'A' + 10;
2544 } else if ((c
>= 'a') && (c
<= 'f')) {
2545 nextdigit
= c
- 'a' + 10;
2549 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2550 high
= (high
<< 4) | carry
;
2551 low
= (low
<< 4) | nextdigit
;
2554 volumeID
->v
.high
= high
;
2555 volumeID
->v
.low
= low
;
2560 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2561 FormatUUID(volumeID
, UUIDString
);
2562 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2567 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2568 VSDBStatePtr dbstateptr
;
2570 *DBHandlePtr
= NULL
;
2572 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2573 if (dbstateptr
== NULL
) {
2577 dbstateptr
->dbmode
= O_RDWR
;
2578 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2579 if (dbstateptr
->dbfile
== -1) {
2581 The file couldn't be opened for read/write access:
2582 try read-only access before giving up altogether.
2584 dbstateptr
->dbmode
= O_RDONLY
;
2585 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2586 if (dbstateptr
->dbfile
== -1) {
2591 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2592 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2598 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2599 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2600 struct VSDBEntry dbentry
;
2603 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2605 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2607 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2610 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2615 UnlockDB(dbstateptr
);
2621 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2622 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2623 struct VSDBEntry dbentry
;
2626 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2627 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2629 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2631 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2632 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2634 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2636 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2637 } else if (result
== -1) {
2639 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2641 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2646 fsync(dbstateptr
->dbfile
);
2651 UnlockDB(dbstateptr
);
2657 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2658 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2661 unsigned long iobuffersize
;
2662 void *iobuffer
= NULL
;
2664 unsigned long iotransfersize
;
2665 unsigned long bytestransferred
;
2667 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2669 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2671 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2673 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2675 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2679 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2681 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2682 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2683 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2685 iobuffersize
= MAXIOMALLOC
;
2688 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2689 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2690 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2692 if (iobuffersize
> 0) {
2693 iobuffer
= malloc(iobuffersize
);
2694 if (iobuffer
== NULL
) {
2699 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2701 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2702 if (iotransfersize
> 0) {
2703 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2706 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2708 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2709 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2710 if (bytestransferred
!= iotransfersize
) {
2716 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2718 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2719 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2720 if (bytestransferred
!= iotransfersize
) {
2725 dataoffset
+= (off_t
)iotransfersize
;
2727 } while (iotransfersize
> 0);
2730 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2732 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2736 fsync(dbstateptr
->dbfile
);
2742 if (iobuffer
) free(iobuffer
);
2743 UnlockDB(dbstateptr
);
2751 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2752 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2754 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2756 dbstateptr
->signature
= 0;
2758 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2759 dbstateptr
->dbfile
= 0;
2768 /******************************************************************************
2770 * 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
2772 *****************************************************************************/
2774 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2776 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2778 return flock(dbstateptr
->dbfile
, lockmode
);
2783 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2785 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2787 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2792 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2793 struct VSDBKey searchkey
;
2794 struct VSDBEntry dbentry
;
2797 FormatDBKey(volumeID
, &searchkey
);
2798 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2801 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2802 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2803 if (targetEntry
!= NULL
) {
2805 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2807 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2811 } while (result
== 0);
2818 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2820 VolumeUUIDString id
;
2824 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2825 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2826 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2828 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2829 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2835 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2837 VolumeUUIDString id
;
2841 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2842 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2843 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2845 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2847 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2849 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2854 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2855 struct VSDBEntry entry
;
2858 VolumeUUIDString id
;
2861 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2862 #if 0 // DEBUG_TRACE
2863 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2865 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2866 if ((result
!= sizeof(entry
)) ||
2867 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2868 (entry
.space
!= DBBLANKSPACE
) ||
2869 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2874 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2875 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2876 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2878 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2884 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2885 #if 0 // DEBUG_TRACE
2886 VolumeUUIDString id
;
2888 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2889 id
[sizeof(key1
->uuid
)] = (char)0;
2890 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2891 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2892 id
[sizeof(key2
->uuid
)] = (char)0;
2893 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2896 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2901 /******************************************************************************
2903 * 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
2905 *****************************************************************************/
2907 static void FormatULong(unsigned long u
, char *s
) {
2912 for (i
= 0; i
< 8; ++i
) {
2913 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2915 *digitptr
++ = (char)(d
+ '0');
2917 *digitptr
++ = (char)(d
- 10 + 'A');
2925 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2926 FormatULong(volumeID
->v
.high
, UUIDField
);
2927 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2933 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2934 FormatUUID(volumeID
, dbkey
->uuid
);
2939 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2940 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
2945 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
2946 FormatDBKey(volumeID
, &dbentry
->key
);
2947 dbentry
->keySeparator
= DBKEYSEPARATOR
;
2948 dbentry
->space
= DBBLANKSPACE
;
2949 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
2950 #if 0 // DEBUG_TRACE
2951 dbentry
->terminator
= (char)0;
2952 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
2954 dbentry
->terminator
= DBRECORDTERMINATOR
;
2959 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
2962 unsigned long nextdigit
;
2966 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
2967 if ((c
>= '0') && (c
<= '9')) {
2968 nextdigit
= c
- '0';
2969 } else if ((c
>= 'A') && (c
<= 'F')) {
2970 nextdigit
= c
- 'A' + 10;
2971 } else if ((c
>= 'a') && (c
<= 'f')) {
2972 nextdigit
= c
- 'a' + 10;
2976 n
= (n
<< 4) + nextdigit
;