2 * Copyright (c) 1999-2009 Apple 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'
113 #ifndef FSUC_JNLINFS_RAW
114 #define FSUC_JNLINFS_RAW 'e'
117 #ifndef FSUC_EXTJNL_RAW
118 #define FSUC_EXTJNL_RAW 'E'
122 #define FSUC_JNLINFO 'I'
126 /* **************************************** L O C A L S ******************************************* */
128 #define HFS_BLOCK_SIZE 512
130 char gHFS_FS_NAME
[] = "hfs";
131 char gHFS_FS_NAME_NAME
[] = "HFS";
133 char gNewlineString
[] = "\n";
135 char gMountCommand
[] = "/sbin/mount";
137 char gUnmountCommand
[] = "/sbin/umount";
139 char gReadOnlyOption
[] = "-r";
140 char gReadWriteOption
[] = "-w";
142 char gSuidOption
[] = "suid";
143 char gNoSuidOption
[] = "nosuid";
145 char gDevOption
[] = "dev";
146 char gNoDevOption
[] = "nodev";
148 char gUsePermissionsOption
[] = "perm";
149 char gIgnorePermissionsOption
[] = "noperm";
151 boolean_t gIsEjectable
= 0;
153 int gJournalSize
= 0;
155 #define AUTO_ADOPT_FIXED 1
156 #define AUTO_ENTER_FIXED 0
159 struct FinderAttrBuf
{
160 u_int32_t info_length
;
161 u_int32_t finderinfo
[8];
165 #define VOLUMEUUIDVALUESIZE 2
166 typedef union VolumeUUID
{
167 u_int32_t value
[VOLUMEUUIDVALUESIZE
];
174 #define VOLUMEUUIDLENGTH 16
175 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1];
177 #define VOLUME_RECORDED 0x80000000
178 #define VOLUME_USEPERMISSIONS 0x00000001
179 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
181 typedef void *VolumeStatusDBHandle
;
183 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
184 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
);
185 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
);
186 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
187 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
);
188 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
);
189 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
190 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
192 /* ************************************ P R O T O T Y P E S *************************************** */
193 static void DoDisplayUsage( const char * argv
[] );
194 static int DoMount( char * theDeviceNamePtr
, const char * theMountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
195 static int DoProbe( char * rawDeviceNamePtr
, char * blockDeviceNamePtr
);
196 static int DoUnmount( const char * theMountPointPtr
);
197 static int DoGetUUIDKey( const char * theDeviceNamePtr
);
198 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
199 static int DoAdopt( const char * theDeviceNamePtr
);
200 static int DoDisown( const char * theDeviceNamePtr
);
202 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
203 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
204 extern int DoGetJournalInfo( const char * volNamePtr
);
205 extern int RawDisableJournaling( const char *devname
);
206 extern int SetJournalInFSState( const char *devname
, int journal_in_fs
);
208 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
);
210 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
211 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
);
212 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
213 static int GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
214 static int GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
215 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
216 static int SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
217 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
218 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
219 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
220 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
221 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
222 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
223 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
224 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
225 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
226 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
227 off_t
*physicalOffset
, ssize_t
*availableBytes
);
228 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
229 off_t volOffset
, u_int32_t blockSize
,
230 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
231 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
232 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
234 static int GetEncodingBias(void);
237 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
239 static void uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
);
242 * The fuction CFStringGetSystemEncoding does not work correctly in
243 * our context (autodiskmount deamon). We include a local copy here
244 * so that we can derive the default encoding. Radar 2516316.
246 #if READ_DEFAULT_ENCODING
247 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
249 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
250 struct passwd
*passwdp
;
252 if ((passwdp
= getpwuid(0))) { // root account
253 char buffer
[MAXPATHLEN
+ 1];
256 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
257 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
259 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
262 readSize
= read(fd
, buffer
, MAXPATHLEN
);
263 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
265 return strtol(buffer
, NULL
, 0);
268 return 0; // Fallback to smRoman
273 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
275 struct hfs_mnt_encoding
{
276 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
277 CFStringEncoding encoding_id
; /* encoding type number */
280 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
286 { "CentralEurRoman", 29 },
287 { "ChineseSimp", 25 },
288 { "ChineseTrad", 2 },
309 { "Roman", 0 }, /* default */
317 { "Ukrainian", 152 },
318 { "Vietnamese", 30 },
321 #define KEXT_LOAD_COMMAND "/sbin/kextload"
322 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
324 static int load_encoding(CFStringEncoding encoding
)
332 char kmodfile
[MAXPATHLEN
];
334 /* Find the encoding that matches the one passed in */
335 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
337 for (i
=0; i
<numEncodings
; ++i
)
339 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
341 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
346 if (encodingName
== NULL
)
348 /* Couldn't figure out which encoding KEXT to load */
349 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
353 snprintf(kmodfile
, sizeof(kmodfile
), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
354 if (stat(kmodfile
, &sb
) == -1)
356 /* We recognized the encoding, but couldn't find the KEXT */
357 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
364 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
366 exit(1); /* We can only get here if the exec failed */
370 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
372 if (WEXITSTATUS(status
) != 0)
374 /* kextload returned an error. Too bad its output doesn't get logged. */
375 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
381 return FSUR_IO_SUCCESS
;
385 /* ******************************************** main ************************************************
387 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
388 for detail info on input arguments.
390 argc - the number of arguments in argv.
391 argv - array of arguments.
393 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
394 *************************************************************************************************** */
396 int main (int argc
, const char *argv
[])
398 const char * actionPtr
= NULL
;
399 char rawDeviceName
[MAXPATHLEN
];
400 char blockDeviceName
[MAXPATHLEN
];
401 const char * mountPointPtr
= NULL
;
402 int result
= FSUR_IO_SUCCESS
;
403 boolean_t isLocked
= 0; /* reasonable assumptions */
404 boolean_t isSetuid
= 0; /* reasonable assumptions */
405 boolean_t isDev
= 0; /* reasonable assumptions */
407 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
409 /* Verify our arguments */
410 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
415 -- Build our device name (full path), should end up with something like:
419 snprintf(rawDeviceName
, sizeof(rawDeviceName
), "/dev/r%s", argv
[2]);
420 snprintf(blockDeviceName
, sizeof(blockDeviceName
), "/dev/%s", argv
[2]);
422 /* call the appropriate routine to handle the given action argument after becoming root */
424 switch( * actionPtr
) {
426 result
= DoProbe(rawDeviceName
, blockDeviceName
);
430 case FSUC_MOUNT_FORCE
:
431 result
= DoMount(blockDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
435 result
= DoUnmount( mountPointPtr
);
438 result
= DoGetUUIDKey( blockDeviceName
);
442 result
= DoChangeUUIDKey( blockDeviceName
);
445 result
= DoAdopt( blockDeviceName
);
449 result
= DoDisown( blockDeviceName
);
454 result
= DoMakeJournaled( argv
[3], gJournalSize
);
456 result
= DoMakeJournaled( argv
[2], gJournalSize
);
461 result
= DoUnJournal( argv
[2] );
465 result
= RawDisableJournaling( argv
[2] );
468 case FSUC_JNLINFS_RAW
:
469 // argv[2] has the device for the external journal. however
470 // we don't need it so we ignore it and just pass argv[3]
471 // which is the hfs volume whose state we're going to change
473 result
= SetJournalInFSState( argv
[3], 1 );
476 case FSUC_EXTJNL_RAW
:
477 // see the comment for FSUC_JNLINFS_RAW
478 result
= SetJournalInFSState( argv
[3], 0 );
482 result
= DoGetJournalInfo( argv
[2] );
486 /* should never get here since ParseArgs should handle this situation */
487 DoDisplayUsage( argv
);
496 return result
; /*...and make main fit the ANSI spec. */
500 /* ***************************** DoMount ********************************
502 This routine will fire off a system command to mount the given device at the given mountpoint.
503 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
505 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
506 mountPointPtr - pointer to the mount point.
509 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
510 *********************************************************************** */
512 DoMount(char *deviceNamePtr
, const char *mountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
518 char *permissionsOption
;
519 int result
= FSUR_IO_FAIL
;
521 char encodeopt
[16] = "";
522 CFStringEncoding encoding
;
523 VolumeUUID targetVolumeUUID
;
524 VolumeStatusDBHandle vsdbhandle
= NULL
;
525 unsigned long targetVolumeStatus
;
527 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
528 return (FSUR_IO_FAIL
);
530 /* get the volume UUID to check if permissions should be used: */
531 targetVolumeStatus
= 0;
532 if (((result
= GetVolumeUUID(deviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
533 (targetVolumeUUID
.v
.high
==0) ||
534 (targetVolumeUUID
.v
.low
== 0)) {
536 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
539 if (gIsEjectable
== 0) {
540 result
= DoAdopt( deviceNamePtr
);
542 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
544 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
547 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
549 targetVolumeStatus
= 0;
553 /* We've got a real volume UUID! */
555 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
557 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
558 /* Can't even get access to the volume info db; assume permissions are OK. */
560 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
562 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
565 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
567 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
569 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
572 if (gIsEjectable
== 0) {
573 result
= DoAdopt( deviceNamePtr
);
575 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
577 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
580 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
582 targetVolumeStatus
= 0;
585 targetVolumeStatus
= 0;
588 (void)CloseVolumeStatusDB(vsdbhandle
);
595 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
596 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
597 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
600 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
602 /* get default encoding value (for hfs volumes) */
603 #if READ_DEFAULT_ENCODING
604 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
606 encoding
= CFStringGetSystemEncoding();
608 snprintf(encodeopt
, sizeof(encodeopt
), "-e=%d", (int)encoding
);
610 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
611 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
613 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
614 "-o", encodeopt
, "-o", permissionsOption
,
615 "-o", "-u=unknown,-g=unknown,-m=0777",
616 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
619 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
620 return (FSUR_IO_FAIL
);
624 return (FSUR_IO_FAIL
);
627 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
628 result
= status
.w_retcode
;
632 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
636 /* ****************************************** DoUnmount *********************************************
638 This routine will fire off a system command to unmount the given device.
640 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
642 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
643 *************************************************************************************************** */
645 DoUnmount(const char * theMountPointPtr
)
651 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
656 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
658 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
660 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
661 return (FSUR_IO_FAIL
);
665 return (FSUR_IO_FAIL
);
668 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
669 result
= status
.w_retcode
;
673 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
681 Get the volume name of the volume mounted at "path". Print that volume
682 name to standard out.
684 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
686 struct VolumeNameBuf
{
687 u_int32_t info_length
;
688 attrreference_t name_ref
;
693 PrintVolumeNameAttr(const char *path
)
695 struct attrlist alist
;
696 struct VolumeNameBuf volNameInfo
;
699 /* Set up the attrlist structure to get the volume's Finder Info */
700 alist
.bitmapcount
= 5;
702 alist
.commonattr
= 0;
703 alist
.volattr
= ATTR_VOL_INFO
| ATTR_VOL_NAME
;
708 /* Get the Finder Info */
709 result
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0);
711 result
= FSUR_IO_FAIL
;
715 /* Print the name to standard out */
716 printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
);
717 result
= FSUR_RECOGNIZED
;
724 /* ******************************************* DoProbe **********************************************
726 This routine will open the given device and check to make sure there is media that looks
727 like an HFS. If it is HFS, then print the volume name to standard output.
729 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
730 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
732 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
733 *************************************************************************************************** */
735 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
)
737 int result
= FSUR_UNRECOGNIZED
;
740 HFSMasterDirectoryBlock
* mdbPtr
;
741 HFSPlusVolumeHeader
* volHdrPtr
;
742 u_char volnameUTF8
[NAME_MAX
+1];
745 * Determine if there is a volume already mounted from this device. If
746 * there is, and it is HFS, then we need to get the volume name via
749 * NOTE: We're using bufPtr to hold a pointer to a path.
752 result
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
);
753 if (result
!= FSUR_IO_SUCCESS
) {
756 if (bufPtr
!= NULL
) {
757 /* There is an HFS volume mounted from the device. */
758 result
= PrintVolumeNameAttr(bufPtr
);
763 * If we get here, there is no volume mounted from this device, so
764 * go probe the raw device directly.
767 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
769 result
= FSUR_UNRECOGNIZED
;
773 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
774 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
776 fd
= open( rawDeviceNamePtr
, O_RDONLY
, 0 );
778 result
= FSUR_IO_FAIL
;
783 * Read the HFS Master Directory Block from sector 2
785 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
786 if (FSUR_IO_FAIL
== result
)
789 /* get classic HFS volume name (from MDB) */
790 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
791 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
794 CFStringEncoding encoding
;
796 /* Some poorly mastered HFS CDs have an empty MDB name field! */
797 if (mdbPtr
->drVN
[0] == '\0') {
798 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
799 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
802 /* Check for an encoding hint in the Finder Info (field 4). */
803 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
804 if (encoding
== kCFStringEncodingInvalidId
) {
805 /* Next try the encoding bias in the kernel. */
806 encoding
= GetEncodingBias();
807 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
808 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
811 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
812 mdbPtr
->drVN
, encoding
);
817 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
820 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
822 /* default to MacRoman on conversion errors */
823 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
824 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
825 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
827 encoding
= kCFStringEncodingMacRoman
;
830 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
831 if (encoding
!= kCFStringEncodingMacRoman
) {
832 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
833 encoding
= kCFStringEncodingMacRoman
;
834 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
835 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
840 /* get HFS Plus volume name (from Catalog) */
841 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
842 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
843 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
844 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
847 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
848 /* embedded volume, first find offset */
849 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
850 if ( result
!= FSUR_IO_SUCCESS
)
856 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
859 result
= FSUR_UNRECOGNIZED
;
862 if (FSUR_IO_SUCCESS
== result
) {
863 /* Print the volume name to standard output */
864 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
865 result
= FSUR_RECOGNIZED
;
881 * Create a version 3 UUID from a unique "name" in the given "name space".
882 * Version 3 UUID are derived using "name" via MD5 checksum.
885 * result_uuid - resulting UUID.
886 * namespace - namespace in which given name exists and UUID should be created.
887 * name - unique string used to create version 3 UUID.
888 * namelen - length of the name string.
891 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
896 MD5_Update(&c
, namespace, sizeof(uuid_t
));
897 MD5_Update(&c
, name
, namelen
);
898 MD5_Final(result_uuid
, &c
);
900 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
901 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
905 /* **************************************** DoGetUUIDKey *******************************************
907 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
909 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
911 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
912 *************************************************************************************************** */
914 DoGetUUIDKey( const char * theDeviceNamePtr
) {
916 VolumeUUID targetVolumeUUID
;
920 unsigned char rawUUID
[8];
922 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
924 ((uint32_t *)rawUUID
)[0] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.high
);
925 ((uint32_t *)rawUUID
)[1] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.low
);
927 uuid_create_md5_from_name(uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
928 uuid_unparse(uuid
, uuidLine
);
929 write(1, uuidLine
, strlen(uuidLine
));
930 result
= FSUR_IO_SUCCESS
;
938 /* *************************************** DoChangeUUIDKey ******************************************
940 This routine will change the UUID on the specified block device.
942 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
944 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
945 *************************************************************************************************** */
947 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
949 VolumeUUID newVolumeUUID
;
951 GenerateVolumeUUID(&newVolumeUUID
);
952 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
959 /* **************************************** DoAdopt *******************************************
961 This routine will add the UUID of the specified block device to the list of local volumes.
963 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
965 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
966 *************************************************************************************************** */
968 DoAdopt( const char * theDeviceNamePtr
) {
969 int result
, closeresult
;
970 VolumeUUID targetVolumeUUID
;
971 VolumeStatusDBHandle vsdbhandle
= NULL
;
972 unsigned long targetVolumeStatus
;
974 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
976 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
977 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
978 targetVolumeStatus
= 0;
980 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
981 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
983 result
= FSUR_IO_SUCCESS
;
987 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
989 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
992 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
996 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
1003 /* **************************************** DoDisown *******************************************
1005 This routine will change the status of the specified block device to ignore its permissions.
1007 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
1009 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
1010 *************************************************************************************************** */
1012 DoDisown( const char * theDeviceNamePtr
) {
1013 int result
, closeresult
;
1014 VolumeUUID targetVolumeUUID
;
1015 VolumeStatusDBHandle vsdbhandle
= NULL
;
1016 unsigned long targetVolumeStatus
;
1018 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1020 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1021 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1022 targetVolumeStatus
= 0;
1024 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
1025 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1027 result
= FSUR_IO_SUCCESS
;
1031 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1033 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1036 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
1038 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
1040 result
= FSUR_IO_FAIL
;
1045 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
1052 get_multiplier(char c
)
1054 if (tolower(c
) == 'k') {
1056 } else if (tolower(c
) == 'm') {
1058 } else if (tolower(c
) == 'g') {
1059 return 1024 * 1024 * 1024;
1065 /* **************************************** ParseArgs ********************************************
1067 This routine will make sure the arguments passed in to us are cool.
1068 Here is how this utility is used:
1070 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1072 -p (Probe for mounting)
1073 -P (Probe for initializing - not supported)
1075 -r (Repair - not supported)
1078 -i (Initialize - not supported)
1081 disk0s2 (for example)
1084 /foo/bar/ (required for Mount and Force Mount actions)
1087 (these are ignored for CDROMs)
1088 either "readonly" OR "writable"
1089 either "removable" OR "fixed"
1090 either "nosuid" or "suid"
1091 either "nodev" or "dev"
1094 hfs.util -p disk0s2 removable writable
1095 hfs.util -p disk0s2 removable readonly
1096 hfs.util -m disk0s2 /my/hfs
1099 argc - the number of arguments in argv.
1100 argv - array of arguments.
1102 returns FSUR_INVAL if we find a bad argument else 0.
1103 *************************************************************************************************** */
1105 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1106 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1107 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1109 int result
= FSUR_INVAL
;
1110 int deviceLength
, doLengthCheck
= 1;
1114 /* Must have at least 3 arguments and the action argument must start with a '-' */
1115 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1116 DoDisplayUsage( argv
);
1120 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1122 * actionPtr
= & argv
[1][1];
1124 switch ( argv
[1][1] ) {
1126 /* action Probe and requires 5 arguments (need the flags) */
1128 DoDisplayUsage( argv
);
1136 /* Note: the device argument in argv[2] is checked further down but ignored. */
1137 * mountPointPtr
= argv
[3];
1138 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1142 case FSUC_MOUNT_FORCE
:
1143 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1145 DoDisplayUsage( argv
);
1148 * mountPointPtr
= argv
[3];
1174 if (isdigit(argv
[2][0])) {
1176 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1178 gJournalSize
*= get_multiplier(*ptr
);
1189 case FSUC_UNJNL_RAW
:
1194 case FSUC_JNLINFS_RAW
:
1199 case FSUC_EXTJNL_RAW
:
1211 DoDisplayUsage( argv
);
1216 /* Make sure device (argv[2]) is something reasonable */
1217 deviceLength
= strlen( argv
[2] );
1218 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1219 DoDisplayUsage( argv
);
1224 /* Flags: removable/fixed. */
1225 if ( 0 == strcmp(argv
[index
],"removable") ) {
1226 * isEjectablePtr
= 1;
1227 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1228 * isEjectablePtr
= 0;
1230 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1233 /* Flags: readonly/writable. */
1234 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1236 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1239 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1243 /* Flags: suid/nosuid. */
1244 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1246 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1249 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1252 /* Flags: dev/nodev. */
1253 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1255 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1258 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1273 /* *************************************** DoDisplayUsage ********************************************
1275 This routine will do a printf of the correct usage for this utility.
1277 argv - array of arguments.
1280 *************************************************************************************************** */
1282 DoDisplayUsage(const char *argv
[])
1284 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1285 printf("action_arg:\n");
1286 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1287 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1288 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1289 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1290 #ifdef HFS_UUID_SUPPORT
1291 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1292 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1293 #endif HFS_UUID_SUPPORT
1294 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1295 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1296 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1297 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1298 printf(" -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW
);
1299 printf(" -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW
);
1300 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1301 printf("device_arg:\n");
1302 printf(" device we are acting upon (for example, 'disk0s2')\n");
1303 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1304 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1305 printf("mount_point_arg:\n");
1306 printf(" required for Mount and Force Mount \n");
1308 printf(" required for Mount, Force Mount and Probe\n");
1309 printf(" indicates removable or fixed (for example 'fixed')\n");
1310 printf(" indicates readonly or writable (for example 'readonly')\n");
1311 printf(" indicates suid or nosuid (for example 'suid')\n");
1312 printf(" indicates dev or nodev (for example 'dev')\n");
1313 printf("Examples:\n");
1314 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1315 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1319 } /* DoDisplayUsage */
1325 Given a path to a device, determine if a volume is mounted on that
1326 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1327 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1328 no volume mounted on the device, set *pathPtr to NULL and return
1331 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1334 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1340 /* Assume no mounted volume found */
1342 result
= FSUR_IO_SUCCESS
;
1344 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1346 return FSUR_IO_FAIL
;
1348 for (i
=0; i
<numMounts
; ++i
) {
1349 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1350 /* Found a mounted volume; check the type */
1351 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1352 *pathPtr
= buf
[i
].f_mntonname
;
1353 /* result = FSUR_IO_SUCCESS, above */
1355 result
= FSUR_UNRECOGNIZED
;
1368 Read the Master Directory Block or Volume Header Block from an HFS,
1369 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1370 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1371 Return a pointer to the volume UUID in the Finder Info.
1373 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1376 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1379 HFSMasterDirectoryBlock
* mdbPtr
;
1380 HFSPlusVolumeHeader
* volHdrPtr
;
1386 * Read the HFS Master Directory Block or Volume Header from sector 2
1389 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1390 if (result
!= FSUR_IO_SUCCESS
)
1394 * If this is a wrapped HFS Plus volume, read the Volume Header from
1395 * sector 2 of the embedded volume.
1397 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1398 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1399 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1400 if (result
!= FSUR_IO_SUCCESS
)
1402 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1403 if (result
!= FSUR_IO_SUCCESS
)
1408 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1409 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1410 * UUID from the Finder Info.
1412 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1413 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1414 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1415 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1416 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1418 result
= FSUR_UNRECOGNIZED
;
1429 Read the UUID from an unmounted volume, by doing direct access to the device.
1430 Assumes the caller has already determined that a volume is not mounted
1433 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1436 GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1441 VolumeUUID
*finderInfoUUIDPtr
;
1444 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1446 result
= FSUR_UNRECOGNIZED
;
1450 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1453 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1455 result
= FSUR_IO_FAIL
;
1460 * Get the pointer to the volume UUID in the Finder Info
1462 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1463 if (result
!= FSUR_IO_SUCCESS
)
1467 * Copy the volume UUID out of the Finder Info
1469 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1470 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1473 if (fd
> 0) close(fd
);
1474 if (bufPtr
) free(bufPtr
);
1477 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1479 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1486 Write a previously generated UUID to an unmounted volume, by doing direct
1487 access to the device. Assumes the caller has already determined that a
1488 volume is not mounted on the device.
1490 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1493 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1498 VolumeUUID
*finderInfoUUIDPtr
;
1501 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1503 result
= FSUR_UNRECOGNIZED
;
1507 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1510 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1512 result
= FSUR_IO_FAIL
;
1517 * Get the pointer to the volume UUID in the Finder Info
1519 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1520 if (result
!= FSUR_IO_SUCCESS
)
1524 * Update the UUID in the Finder Info
1526 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1527 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1530 * Write the modified MDB or VHB back to disk
1532 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1535 if (fd
> 0) close(fd
);
1536 if (bufPtr
) free(bufPtr
);
1539 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1541 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1548 Read the UUID from a mounted volume, by calling getattrlist().
1549 Assumes the path is the mount point of an HFS volume.
1551 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1554 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1556 struct attrlist alist
;
1557 struct FinderAttrBuf volFinderInfo
;
1558 VolumeUUID
*finderInfoUUIDPtr
;
1561 /* Set up the attrlist structure to get the volume's Finder Info */
1562 alist
.bitmapcount
= 5;
1564 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1565 alist
.volattr
= ATTR_VOL_INFO
;
1570 /* Get the Finder Info */
1571 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1573 result
= FSUR_IO_FAIL
;
1577 /* Copy the UUID from the Finder Into to caller's buffer */
1578 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1579 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1580 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1581 result
= FSUR_IO_SUCCESS
;
1591 Write a UUID to a mounted volume, by calling setattrlist().
1592 Assumes the path is the mount point of an HFS volume.
1594 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1597 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1599 struct attrlist alist
;
1600 struct FinderAttrBuf volFinderInfo
;
1601 VolumeUUID
*finderInfoUUIDPtr
;
1604 /* Set up the attrlist structure to get the volume's Finder Info */
1605 alist
.bitmapcount
= 5;
1607 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1608 alist
.volattr
= ATTR_VOL_INFO
;
1613 /* Get the Finder Info */
1614 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1616 result
= FSUR_IO_FAIL
;
1620 /* Update the UUID in the Finder Info */
1621 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1622 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1623 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1625 /* Write the Finder Info back to the volume */
1626 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1628 result
= FSUR_IO_FAIL
;
1632 result
= FSUR_IO_SUCCESS
;
1642 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1643 we were asked to generate one, then generate a new UUID and write it to the
1646 Determine whether an HFS volume is mounted on the given device. If so, we
1647 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1648 the filesystem. If there is no mounted volume, then do direct device access
1649 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1651 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1655 GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1661 * Determine whether a volume is mounted on this device. If it is HFS, then
1662 * get the mount point's path. If it is non-HFS, then we can exit immediately
1663 * with FSUR_UNRECOGNIZED.
1665 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1666 if (result
!= FSUR_IO_SUCCESS
)
1670 * Get any existing UUID.
1673 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1675 result
= GetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1676 if (result
!= FSUR_IO_SUCCESS
)
1680 * If there was no valid UUID, and we were asked to generate one, then
1681 * generate it and write it back to disk.
1683 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1684 GenerateVolumeUUID(volumeUUIDPtr
);
1686 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1688 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1689 /* Fall through to Err_Exit */
1701 Write a UUID to an HFS, HFS Plus or HFSX volume.
1703 Determine whether an HFS volume is mounted on the given device. If so, we
1704 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1705 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1707 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1710 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1715 * Determine whether a volume is mounted on this device. If it is HFS, then
1716 * get the mount point's path. If it is non-HFS, then we can exit immediately
1717 * with FSUR_UNRECOGNIZED.
1719 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1720 if (result
!= FSUR_IO_SUCCESS
)
1727 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1729 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1738 -- GetEmbeddedHFSPlusVol
1740 -- In: hfsMasterDirectoryBlockPtr
1741 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1742 (that is, 2 blocks before the volume header)
1747 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1749 int result
= FSUR_IO_SUCCESS
;
1750 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1752 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1753 result
= FSUR_UNRECOGNIZED
;
1757 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1758 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1760 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1761 result
= FSUR_UNRECOGNIZED
;
1765 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1766 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1768 if ( startOffsetPtr
)
1769 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1770 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1780 -- GetNameFromHFSPlusVolumeStartingAt
1782 -- Caller's responsibility to allocate and release memory for the converted string.
1784 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1788 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1790 int result
= FSUR_IO_SUCCESS
;
1791 u_int32_t blockSize
;
1792 char * bufPtr
= NULL
;
1793 HFSPlusVolumeHeader
* volHdrPtr
;
1794 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1795 u_int32_t catalogNodeSize
;
1797 u_int32_t catalogExtCount
;
1798 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1800 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1801 if ( ! volHdrPtr
) {
1802 result
= FSUR_IO_FAIL
;
1807 * Read the Volume Header
1808 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1810 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1811 if (result
== FSUR_IO_FAIL
) {
1813 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1815 goto Return
; // return FSUR_IO_FAIL
1818 /* Verify that it is an HFS+ volume. */
1820 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1821 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
1822 result
= FSUR_IO_FAIL
;
1824 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1829 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1830 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1831 if ( ! catalogExtents
) {
1832 result
= FSUR_IO_FAIL
;
1835 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1836 catalogExtCount
= kHFSPlusExtentDensity
;
1838 /* if there are overflow catalog extents, then go get them */
1839 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
1840 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1841 if (result
!= FSUR_IO_SUCCESS
)
1845 /* Read the header node of the catalog B-Tree */
1847 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1848 catalogExtCount
, catalogExtents
,
1849 &catalogNodeSize
, &leafNode
);
1850 if (result
!= FSUR_IO_SUCCESS
)
1853 /* Read the first leaf node of the catalog b-tree */
1855 bufPtr
= (char *)malloc(catalogNodeSize
);
1857 result
= FSUR_IO_FAIL
;
1861 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1863 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1864 hfsPlusVolumeOffset
, blockSize
,
1865 catalogExtCount
, catalogExtents
);
1866 if (result
== FSUR_IO_FAIL
) {
1868 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1870 goto Return
; // return FSUR_IO_FAIL
1876 HFSPlusCatalogKey
* k
;
1879 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
1880 result
= FSUR_IO_FAIL
;
1882 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1887 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1889 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1892 // Get a pointer to the first record.
1894 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
1895 k
= (HFSPlusCatalogKey
*)p
;
1897 // There should be only one record whose parent is the root parent. It should be the first record.
1899 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
1900 result
= FSUR_IO_FAIL
;
1902 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1907 if ((OSSwapBigToHostInt16(k
->nodeName
.length
) >
1908 (sizeof(k
->nodeName
.unicode
) / sizeof(k
->nodeName
.unicode
[0]))) ||
1909 OSSwapBigToHostInt16(k
->nodeName
.length
) < 0) {
1910 result
= FSUR_IO_FAIL
;
1912 fprintf(stderr
, "hfs.util: ERROR: k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k
->nodeName
.length
));
1917 /* Extract the name of the root directory */
1920 HFSUniStr255
*swapped
;
1923 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1924 if (swapped
== NULL
) {
1925 result
= FSUR_IO_FAIL
;
1928 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
1930 for (i
=0; i
<swapped
->length
; i
++) {
1931 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
1933 swapped
->unicode
[i
] = 0;
1934 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1935 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1941 result
= FSUR_IO_SUCCESS
;
1945 free((char*) volHdrPtr
);
1948 free((char*) catalogExtents
);
1951 free((char*)bufPtr
);
1955 } /* GetNameFromHFSPlusVolumeStartingAt */
1959 BTNodeDescriptor node
;
1961 } __attribute__((aligned(2), packed
)) HeaderRec
, *HeaderPtr
;
1966 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1970 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1971 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1972 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1975 HeaderRec
* bTreeHeaderPtr
= NULL
;
1977 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1978 if (bTreeHeaderPtr
== NULL
)
1979 return (FSUR_IO_FAIL
);
1981 /* Read the b-tree header node */
1983 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
1984 hfsPlusVolumeOffset
, blockSize
,
1985 extentCount
, extentList
);
1986 if ( result
== FSUR_IO_FAIL
) {
1988 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
1993 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
1994 result
= FSUR_IO_FAIL
;
1996 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
2001 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
2003 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
2006 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
2009 free((char*) bTreeHeaderPtr
);
2013 } /* GetBTreeNodeInfo */
2019 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2023 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
2024 HFSPlusVolumeHeader
*volHdrPtr
,
2025 HFSPlusExtentDescriptor
**catalogExtents
,
2026 u_int32_t
*catalogExtCount
)
2029 u_int32_t numRecords
;
2032 u_int32_t blockSize
;
2033 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
2034 HFSPlusExtentDescriptor
* extents
;
2036 char * bufPtr
= NULL
;
2040 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2041 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
2042 extents
= *catalogExtents
;
2043 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
2046 /* Read the header node of the extents B-Tree */
2048 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2049 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
2050 &nodeSize
, &leafNode
);
2051 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
2054 /* Calculate the logical position of the first leaf node */
2056 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2058 /* Read the first leaf node of the extents b-tree */
2060 bufPtr
= (char *)malloc(nodeSize
);
2062 result
= FSUR_IO_FAIL
;
2066 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2069 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
2070 hfsPlusVolumeOffset
, blockSize
,
2071 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
2072 if ( result
== FSUR_IO_FAIL
) {
2074 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2079 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
2080 result
= FSUR_IO_FAIL
;
2084 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
2085 for (i
= 1; i
<= numRecords
; ++i
) {
2088 HFSPlusExtentKey
* k
;
2091 * Get the offset (in bytes) of the record from the
2092 * list of offsets at the end of the node
2094 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
2097 /* Get a pointer to the record */
2099 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
2100 k
= (HFSPlusExtentKey
*)p
;
2102 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
2105 /* grow list and copy additional extents */
2106 listsize
+= sizeof(HFSPlusExtentRecord
);
2107 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2108 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2109 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2111 *catalogExtCount
+= kHFSPlusExtentDensity
;
2112 *catalogExtents
= extents
;
2115 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2117 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2132 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2133 * position and number of contiguous bytes at that position.
2136 * logicalOffset Logical offset in bytes from start of file
2137 * length Maximum number of bytes to map
2138 * blockSize Number of bytes per allocation block
2139 * extentCount Number of extents in file
2140 * extentList The file's extents
2143 * physicalOffset Physical offset in bytes from start of volume
2144 * availableBytes Number of bytes physically contiguous (up to length)
2146 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2148 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2149 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2150 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2153 u_int32_t logicalBlock
;
2155 u_int32_t blockCount
= 0;
2157 /* Determine allocation block containing logicalOffset */
2158 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
2159 offset
%= blockSize
; /* Offset from start of allocation block */
2161 /* Find the extent containing logicalBlock */
2162 for (extent
= 0; extent
< extentCount
; ++extent
)
2164 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2166 if (blockCount
== 0)
2167 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2169 if (logicalBlock
< blockCount
)
2170 break; /* Found it! */
2172 logicalBlock
-= blockCount
;
2175 if (extent
>= extentCount
)
2176 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2179 * When we get here, extentList[extent] is the extent containing logicalOffset.
2180 * The desired allocation block is logicalBlock blocks into the extent.
2183 /* Compute the physical starting position */
2184 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2185 temp
*= blockSize
; /* Byte offset of first physical block */
2186 *physicalOffset
= temp
+ offset
;
2188 /* Compute the available contiguous bytes. */
2189 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2191 temp
-= offset
; /* Number of bytes available */
2194 *availableBytes
= temp
;
2196 *availableBytes
= length
;
2198 return FSUR_IO_SUCCESS
;
2204 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2205 * ending position are not allocation or device block aligned.
2208 * fd Descriptor for reading the volume
2209 * buffer The bytes are read into here
2210 * offset Offset in file to start reading
2211 * length Number of bytes to read
2212 * volOffset Byte offset from start of device to start of volume
2213 * blockSize Number of bytes per allocation block
2214 * extentCount Number of extents in file
2215 * extentList The file's exents
2217 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2219 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2220 off_t volOffset
, u_int32_t blockSize
,
2221 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2223 int result
= FSUR_IO_SUCCESS
;
2229 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2230 &physOffset
, &physLength
);
2231 if (result
!= FSUR_IO_SUCCESS
)
2234 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2235 if (result
!= FSUR_IO_SUCCESS
)
2238 length
-= physLength
;
2239 offset
+= physLength
;
2240 buffer
= (char *) buffer
+ physLength
;
2247 -- readAt = lseek() + read()
2249 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2254 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2259 void * rawData
= NULL
;
2262 ssize_t dataOffset
= 0;
2263 int result
= FSUR_IO_SUCCESS
;
2265 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2267 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2269 result
= FSUR_IO_FAIL
;
2272 /* put offset and length in terms of device blocksize */
2273 rawOffset
= offset
/ blocksize
* blocksize
;
2274 dataOffset
= offset
- rawOffset
;
2275 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2276 rawData
= malloc(rawLength
);
2277 if (rawData
== NULL
) {
2278 result
= FSUR_IO_FAIL
;
2282 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2283 if ( lseekResult
!= rawOffset
) {
2284 result
= FSUR_IO_FAIL
;
2288 readResult
= read(fd
, rawData
, rawLength
);
2289 if ( readResult
!= rawLength
) {
2291 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2293 result
= FSUR_IO_FAIL
;
2296 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2307 -- writeAt = lseek() + write()
2309 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2314 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2318 ssize_t bytestransferred
;
2319 void * rawData
= NULL
;
2322 ssize_t dataOffset
= 0;
2323 int result
= FSUR_IO_SUCCESS
;
2325 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2327 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2329 result
= FSUR_IO_FAIL
;
2332 /* put offset and length in terms of device blocksize */
2333 rawOffset
= offset
/ blocksize
* blocksize
;
2334 dataOffset
= offset
- rawOffset
;
2335 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2336 rawData
= malloc(rawLength
);
2337 if (rawData
== NULL
) {
2338 result
= FSUR_IO_FAIL
;
2342 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2343 if ( deviceoffset
!= rawOffset
) {
2344 result
= FSUR_IO_FAIL
;
2348 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2349 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2350 bytestransferred
= read(fd
, rawData
, rawLength
);
2351 if ( bytestransferred
!= rawLength
) {
2353 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2355 result
= FSUR_IO_FAIL
;
2360 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2362 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2363 if ( deviceoffset
!= rawOffset
) {
2364 result
= FSUR_IO_FAIL
;
2368 bytestransferred
= write(fd
, rawData
, rawLength
);
2369 if ( bytestransferred
!= rawLength
) {
2371 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2373 result
= FSUR_IO_FAIL
;
2378 if (rawData
) free(rawData
);
2386 * Get kernel's encoding bias.
2392 size_t buflen
= sizeof(int);
2396 if (getvfsbyname("hfs", &vfc
) < 0)
2400 mib
[1] = vfc
.vfc_typenum
;
2401 mib
[2] = HFS_ENCODINGBIAS
;
2403 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2410 /******************************************************************************
2412 * 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
2414 *****************************************************************************/
2416 #define DBHANDLESIGNATURE 0x75917737
2418 /* Flag values for operation options: */
2419 #define DBMARKPOSITION 1
2421 static char gVSDBPath
[] = "/var/db/volinfo.database";
2423 #define MAXIOMALLOC 16384
2425 /* Database layout: */
2432 char statusFlags
[8];
2439 struct VSDBRecord record
;
2443 #define DBKEYSEPARATOR ':'
2444 #define DBBLANKSPACE ' '
2445 #define DBRECORDTERMINATOR '\n'
2447 /* In-memory data structures: */
2450 unsigned long signature
;
2453 off_t recordPosition
;
2456 typedef struct VSDBState
*VSDBStatePtr
;
2460 /* Internal function prototypes: */
2461 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2462 static int UnlockDB(VSDBStatePtr dbstateptr
);
2464 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2465 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2466 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2467 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2468 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2470 static void FormatULong(unsigned long u
, char *s
);
2471 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2472 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2473 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2474 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2475 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2479 /******************************************************************************
2481 * 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
2483 *****************************************************************************/
2485 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2487 char randomInputBuffer
[26];
2488 unsigned char digest
[20];
2493 char sysctlstring
[128];
2495 double sysloadavg
[3];
2496 struct vmtotal sysvmtotal
;
2499 /* Initialize the SHA-1 context for processing: */
2500 SHA1_Init(&context
);
2502 /* Now process successive bits of "random" input to seed the process: */
2504 /* The current system's uptime: */
2506 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2508 /* The kernel's boot time: */
2510 mib
[1] = KERN_BOOTTIME
;
2511 datalen
= sizeof(sysdata
);
2512 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2513 SHA1_Update(&context
, &sysdata
, datalen
);
2515 /* The system's host id: */
2517 mib
[1] = KERN_HOSTID
;
2518 datalen
= sizeof(sysdata
);
2519 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2520 SHA1_Update(&context
, &sysdata
, datalen
);
2522 /* The system's host name: */
2524 mib
[1] = KERN_HOSTNAME
;
2525 datalen
= sizeof(sysctlstring
);
2526 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2527 SHA1_Update(&context
, sysctlstring
, datalen
);
2529 /* The running kernel's OS release string: */
2531 mib
[1] = KERN_OSRELEASE
;
2532 datalen
= sizeof(sysctlstring
);
2533 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2534 SHA1_Update(&context
, sysctlstring
, datalen
);
2536 /* The running kernel's version string: */
2538 mib
[1] = KERN_VERSION
;
2539 datalen
= sizeof(sysctlstring
);
2540 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2541 SHA1_Update(&context
, sysctlstring
, datalen
);
2543 /* The system's load average: */
2544 datalen
= sizeof(sysloadavg
);
2545 getloadavg(sysloadavg
, 3);
2546 SHA1_Update(&context
, &sysloadavg
, datalen
);
2548 /* The system's VM statistics: */
2551 datalen
= sizeof(sysvmtotal
);
2552 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2553 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2555 /* The current GMT (26 ASCII characters): */
2557 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2558 SHA1_Update(&context
, randomInputBuffer
, 26);
2560 /* Pad the accumulated input and extract the final digest hash: */
2561 SHA1_Final(digest
, &context
);
2563 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2564 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2569 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2572 u_int32_t nextdigit
;
2577 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2578 if ((c
>= '0') && (c
<= '9')) {
2579 nextdigit
= c
- '0';
2580 } else if ((c
>= 'A') && (c
<= 'F')) {
2581 nextdigit
= c
- 'A' + 10;
2582 } else if ((c
>= 'a') && (c
<= 'f')) {
2583 nextdigit
= c
- 'a' + 10;
2587 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2588 high
= (high
<< 4) | carry
;
2589 low
= (low
<< 4) | nextdigit
;
2592 volumeID
->v
.high
= high
;
2593 volumeID
->v
.low
= low
;
2598 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2599 FormatUUID(volumeID
, UUIDString
);
2600 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2605 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2606 VSDBStatePtr dbstateptr
;
2608 *DBHandlePtr
= NULL
;
2610 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2611 if (dbstateptr
== NULL
) {
2615 dbstateptr
->dbmode
= O_RDWR
;
2616 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2617 if (dbstateptr
->dbfile
== -1) {
2619 The file couldn't be opened for read/write access:
2620 try read-only access before giving up altogether.
2622 dbstateptr
->dbmode
= O_RDONLY
;
2623 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2624 if (dbstateptr
->dbfile
== -1) {
2629 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2630 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2636 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2637 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2638 struct VSDBEntry dbentry
;
2641 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2643 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2645 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2648 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2653 UnlockDB(dbstateptr
);
2659 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2660 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2661 struct VSDBEntry dbentry
;
2664 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2665 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2667 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2669 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2670 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2672 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2674 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2675 } else if (result
== -1) {
2677 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2679 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2684 fsync(dbstateptr
->dbfile
);
2689 UnlockDB(dbstateptr
);
2695 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2696 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2699 unsigned long iobuffersize
;
2700 void *iobuffer
= NULL
;
2702 unsigned long iotransfersize
;
2703 unsigned long bytestransferred
;
2705 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2707 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2709 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2711 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2713 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2717 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2719 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2720 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2721 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2723 iobuffersize
= MAXIOMALLOC
;
2726 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2727 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2728 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2730 if (iobuffersize
> 0) {
2731 iobuffer
= malloc(iobuffersize
);
2732 if (iobuffer
== NULL
) {
2737 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2739 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2740 if (iotransfersize
> 0) {
2741 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2744 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2746 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2747 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2748 if (bytestransferred
!= iotransfersize
) {
2754 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2756 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2757 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2758 if (bytestransferred
!= iotransfersize
) {
2763 dataoffset
+= (off_t
)iotransfersize
;
2765 } while (iotransfersize
> 0);
2768 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2770 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2774 fsync(dbstateptr
->dbfile
);
2780 if (iobuffer
) free(iobuffer
);
2781 UnlockDB(dbstateptr
);
2789 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2790 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2792 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2794 dbstateptr
->signature
= 0;
2796 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2797 dbstateptr
->dbfile
= 0;
2806 /******************************************************************************
2808 * 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
2810 *****************************************************************************/
2812 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2814 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2816 return flock(dbstateptr
->dbfile
, lockmode
);
2821 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2823 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2825 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2830 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2831 struct VSDBKey searchkey
;
2832 struct VSDBEntry dbentry
;
2835 FormatDBKey(volumeID
, &searchkey
);
2836 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2839 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2840 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2841 if (targetEntry
!= NULL
) {
2843 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2845 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2849 } while (result
== 0);
2856 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2858 VolumeUUIDString id
;
2862 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2863 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2864 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2866 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2867 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2873 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2875 VolumeUUIDString id
;
2879 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2880 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2881 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2883 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2885 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2887 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2892 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2893 struct VSDBEntry entry
;
2896 VolumeUUIDString id
;
2899 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2900 #if 0 // DEBUG_TRACE
2901 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2903 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2904 if ((result
!= sizeof(entry
)) ||
2905 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2906 (entry
.space
!= DBBLANKSPACE
) ||
2907 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2912 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2913 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2914 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2916 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2922 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2923 #if 0 // DEBUG_TRACE
2924 VolumeUUIDString id
;
2926 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2927 id
[sizeof(key1
->uuid
)] = (char)0;
2928 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2929 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2930 id
[sizeof(key2
->uuid
)] = (char)0;
2931 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2934 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2939 /******************************************************************************
2941 * 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
2943 *****************************************************************************/
2945 static void FormatULong(unsigned long u
, char *s
) {
2950 for (i
= 0; i
< 8; ++i
) {
2951 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2953 *digitptr
++ = (char)(d
+ '0');
2955 *digitptr
++ = (char)(d
- 10 + 'A');
2963 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2964 FormatULong(volumeID
->v
.high
, UUIDField
);
2965 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2971 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2972 FormatUUID(volumeID
, dbkey
->uuid
);
2977 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2978 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
2983 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
2984 FormatDBKey(volumeID
, &dbentry
->key
);
2985 dbentry
->keySeparator
= DBKEYSEPARATOR
;
2986 dbentry
->space
= DBBLANKSPACE
;
2987 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
2988 #if 0 // DEBUG_TRACE
2989 dbentry
->terminator
= (char)0;
2990 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
2992 dbentry
->terminator
= DBRECORDTERMINATOR
;
2997 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
3000 unsigned long nextdigit
;
3004 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
3005 if ((c
>= '0') && (c
<= '9')) {
3006 nextdigit
= c
- '0';
3007 } else if ((c
>= 'A') && (c
<= 'F')) {
3008 nextdigit
= c
- 'A' + 10;
3009 } else if ((c
>= 'a') && (c
<= 'f')) {
3010 nextdigit
= c
- 'a' + 10;
3014 n
= (n
<< 4) + nextdigit
;