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 *rawName
, const char * theMountPointPtr
,
195 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
196 static int DoProbe( char * rawDeviceNamePtr
, char * blockDeviceNamePtr
);
197 static int DoUnmount( const char * theMountPointPtr
);
198 static int DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
);
199 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
200 static int DoAdopt( const char * theDeviceNamePtr
, const char *rawName
);
201 static int DoDisown( const char * theDeviceNamePtr
, const char *rawName
);
203 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
204 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
205 extern int DoGetJournalInfo( const char * volNamePtr
);
206 extern int RawDisableJournaling( const char *devname
);
207 extern int SetJournalInFSState( const char *devname
, int journal_in_fs
);
209 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
);
211 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
212 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
);
213 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
);
214 static int GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
215 static int GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
216 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
217 static int SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
218 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
219 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
220 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
221 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
222 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
223 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
224 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
225 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
226 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
227 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
228 off_t
*physicalOffset
, ssize_t
*availableBytes
);
229 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
230 off_t volOffset
, u_int32_t blockSize
,
231 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
232 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
233 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
235 static int GetEncodingBias(void);
238 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
240 static void uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
);
243 * The fuction CFStringGetSystemEncoding does not work correctly in
244 * our context (autodiskmount deamon). We include a local copy here
245 * so that we can derive the default encoding. Radar 2516316.
247 #if READ_DEFAULT_ENCODING
248 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
250 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
251 struct passwd
*passwdp
;
253 if ((passwdp
= getpwuid(0))) { // root account
254 char buffer
[MAXPATHLEN
+ 1];
257 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
258 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
260 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
263 readSize
= read(fd
, buffer
, MAXPATHLEN
);
264 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
266 return strtol(buffer
, NULL
, 0);
269 return 0; // Fallback to smRoman
274 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
276 struct hfs_mnt_encoding
{
277 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
278 CFStringEncoding encoding_id
; /* encoding type number */
281 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
287 { "CentralEurRoman", 29 },
288 { "ChineseSimp", 25 },
289 { "ChineseTrad", 2 },
310 { "Roman", 0 }, /* default */
318 { "Ukrainian", 152 },
319 { "Vietnamese", 30 },
322 #define KEXT_LOAD_COMMAND "/sbin/kextload"
323 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
325 static int load_encoding(CFStringEncoding encoding
)
333 char kmodfile
[MAXPATHLEN
];
335 /* Find the encoding that matches the one passed in */
336 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
338 for (i
=0; i
<numEncodings
; ++i
)
340 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
342 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
347 if (encodingName
== NULL
)
349 /* Couldn't figure out which encoding KEXT to load */
350 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
354 snprintf(kmodfile
, sizeof(kmodfile
), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
355 if (stat(kmodfile
, &sb
) == -1)
357 /* We recognized the encoding, but couldn't find the KEXT */
358 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
365 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
367 exit(1); /* We can only get here if the exec failed */
371 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
373 if (WEXITSTATUS(status
) != 0)
375 /* kextload returned an error. Too bad its output doesn't get logged. */
376 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
382 return FSUR_IO_SUCCESS
;
386 /* ******************************************** main ************************************************
388 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
389 for detail info on input arguments.
391 argc - the number of arguments in argv.
392 argv - array of arguments.
394 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
395 *************************************************************************************************** */
397 int main (int argc
, const char *argv
[])
399 const char * actionPtr
= NULL
;
400 char rawDeviceName
[MAXPATHLEN
];
401 char blockDeviceName
[MAXPATHLEN
];
402 const char * mountPointPtr
= NULL
;
403 int result
= FSUR_IO_SUCCESS
;
404 boolean_t isLocked
= 0; /* reasonable assumptions */
405 boolean_t isSetuid
= 0; /* reasonable assumptions */
406 boolean_t isDev
= 0; /* reasonable assumptions */
408 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
410 /* Verify our arguments */
411 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
416 -- Build our device name (full path), should end up with something like:
420 snprintf(rawDeviceName
, sizeof(rawDeviceName
), "/dev/r%s", argv
[2]);
421 snprintf(blockDeviceName
, sizeof(blockDeviceName
), "/dev/%s", argv
[2]);
423 /* call the appropriate routine to handle the given action argument after becoming root */
425 switch( * actionPtr
) {
427 result
= DoProbe(rawDeviceName
, blockDeviceName
);
431 case FSUC_MOUNT_FORCE
:
432 result
= DoMount(blockDeviceName
, rawDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
436 result
= DoUnmount( mountPointPtr
);
439 result
= DoGetUUIDKey( blockDeviceName
, rawDeviceName
);
443 result
= DoChangeUUIDKey( blockDeviceName
);
446 result
= DoAdopt( blockDeviceName
, rawDeviceName
);
450 result
= DoDisown( blockDeviceName
, rawDeviceName
);
455 result
= DoMakeJournaled( argv
[3], gJournalSize
);
457 result
= DoMakeJournaled( argv
[2], gJournalSize
);
462 result
= DoUnJournal( argv
[2] );
466 result
= RawDisableJournaling( argv
[2] );
469 case FSUC_JNLINFS_RAW
:
470 // argv[2] has the device for the external journal. however
471 // we don't need it so we ignore it and just pass argv[3]
472 // which is the hfs volume whose state we're going to change
474 result
= SetJournalInFSState( argv
[3], 1 );
477 case FSUC_EXTJNL_RAW
:
478 // see the comment for FSUC_JNLINFS_RAW
479 result
= SetJournalInFSState( argv
[3], 0 );
483 result
= DoGetJournalInfo( argv
[2] );
487 /* should never get here since ParseArgs should handle this situation */
488 DoDisplayUsage( argv
);
497 return result
; /*...and make main fit the ANSI spec. */
501 /* ***************************** DoMount ********************************
503 This routine will fire off a system command to mount the given device at the given mountpoint.
504 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
506 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
507 mountPointPtr - pointer to the mount point.
510 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
511 *********************************************************************** */
513 DoMount(char *deviceNamePtr
, const char *rawName
, const char *mountPointPtr
,
514 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
520 char *permissionsOption
;
521 int result
= FSUR_IO_FAIL
;
523 char encodeopt
[16] = "";
524 CFStringEncoding encoding
;
525 VolumeUUID targetVolumeUUID
;
526 VolumeStatusDBHandle vsdbhandle
= NULL
;
527 unsigned long targetVolumeStatus
;
529 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
530 return (FSUR_IO_FAIL
);
532 /* get the volume UUID to check if permissions should be used: */
533 targetVolumeStatus
= 0;
534 if (((result
= GetVolumeUUID(deviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
535 (targetVolumeUUID
.v
.high
==0) ||
536 (targetVolumeUUID
.v
.low
== 0)) {
538 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
541 if (gIsEjectable
== 0) {
542 result
= DoAdopt( deviceNamePtr
, rawName
);
544 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
546 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
549 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
551 targetVolumeStatus
= 0;
555 /* We've got a real volume UUID! */
557 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
559 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
560 /* Can't even get access to the volume info db; assume permissions are OK. */
562 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
564 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
567 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
569 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
571 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
574 if (gIsEjectable
== 0) {
575 result
= DoAdopt( deviceNamePtr
, rawName
);
577 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
579 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
582 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
584 targetVolumeStatus
= 0;
587 targetVolumeStatus
= 0;
590 (void)CloseVolumeStatusDB(vsdbhandle
);
597 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
598 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
599 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
602 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
604 /* get default encoding value (for hfs volumes) */
605 #if READ_DEFAULT_ENCODING
606 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
608 encoding
= CFStringGetSystemEncoding();
610 snprintf(encodeopt
, sizeof(encodeopt
), "-e=%d", (int)encoding
);
612 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
613 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
615 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
616 "-o", encodeopt
, "-o", permissionsOption
,
617 "-o", "-u=unknown,-g=unknown,-m=0777",
618 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
621 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
622 return (FSUR_IO_FAIL
);
626 return (FSUR_IO_FAIL
);
629 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
630 result
= status
.w_retcode
;
634 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
638 /* ****************************************** DoUnmount *********************************************
640 This routine will fire off a system command to unmount the given device.
642 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
644 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
645 *************************************************************************************************** */
647 DoUnmount(const char * theMountPointPtr
)
653 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
658 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
660 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
662 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
663 return (FSUR_IO_FAIL
);
667 return (FSUR_IO_FAIL
);
670 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
671 result
= status
.w_retcode
;
675 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
683 Get the volume name of the volume mounted at "path". Print that volume
684 name to standard out.
686 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
688 struct VolumeNameBuf
{
689 u_int32_t info_length
;
690 attrreference_t name_ref
;
695 PrintVolumeNameAttr(const char *path
)
697 struct attrlist alist
;
698 struct VolumeNameBuf volNameInfo
;
701 /* Set up the attrlist structure to get the volume's Finder Info */
702 alist
.bitmapcount
= 5;
704 alist
.commonattr
= 0;
705 alist
.volattr
= ATTR_VOL_INFO
| ATTR_VOL_NAME
;
710 /* Get the Finder Info */
711 result
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0);
713 result
= FSUR_IO_FAIL
;
717 /* Print the name to standard out */
718 printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
);
719 result
= FSUR_RECOGNIZED
;
726 /* ******************************************* DoProbe **********************************************
728 This routine will open the given device and check to make sure there is media that looks
729 like an HFS. If it is HFS, then print the volume name to standard output.
731 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
732 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
734 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
735 *************************************************************************************************** */
737 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
)
739 int result
= FSUR_UNRECOGNIZED
;
742 HFSMasterDirectoryBlock
* mdbPtr
;
743 HFSPlusVolumeHeader
* volHdrPtr
;
744 u_char volnameUTF8
[NAME_MAX
+1];
747 * Determine if there is a volume already mounted from this device. If
748 * there is, and it is HFS, then we need to get the volume name via
751 * NOTE: We're using bufPtr to hold a pointer to a path.
754 result
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
);
755 if (result
!= FSUR_IO_SUCCESS
) {
758 if (bufPtr
!= NULL
) {
759 /* There is an HFS volume mounted from the device. */
760 result
= PrintVolumeNameAttr(bufPtr
);
765 * If we get here, there is no volume mounted from this device, so
766 * go probe the raw device directly.
769 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
771 result
= FSUR_UNRECOGNIZED
;
775 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
776 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
778 fd
= open( rawDeviceNamePtr
, O_RDONLY
, 0 );
780 result
= FSUR_IO_FAIL
;
785 * Read the HFS Master Directory Block from sector 2
787 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
788 if (FSUR_IO_FAIL
== result
)
791 /* get classic HFS volume name (from MDB) */
792 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
793 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
796 CFStringEncoding encoding
;
798 /* Some poorly mastered HFS CDs have an empty MDB name field! */
799 if (mdbPtr
->drVN
[0] == '\0') {
800 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
801 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
804 /* Check for an encoding hint in the Finder Info (field 4). */
805 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
806 if (encoding
== kCFStringEncodingInvalidId
) {
807 /* Next try the encoding bias in the kernel. */
808 encoding
= GetEncodingBias();
809 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
810 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
813 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
814 mdbPtr
->drVN
, encoding
);
819 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
822 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
824 /* default to MacRoman on conversion errors */
825 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
826 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
827 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
829 encoding
= kCFStringEncodingMacRoman
;
832 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
833 if (encoding
!= kCFStringEncodingMacRoman
) {
834 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
835 encoding
= kCFStringEncodingMacRoman
;
836 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
837 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
842 /* get HFS Plus volume name (from Catalog) */
843 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
844 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
845 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
846 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
849 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
850 /* embedded volume, first find offset */
851 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
852 if ( result
!= FSUR_IO_SUCCESS
)
858 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
861 result
= FSUR_UNRECOGNIZED
;
864 if (FSUR_IO_SUCCESS
== result
) {
865 /* Print the volume name to standard output */
866 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
867 result
= FSUR_RECOGNIZED
;
883 * Create a version 3 UUID from a unique "name" in the given "name space".
884 * Version 3 UUID are derived using "name" via MD5 checksum.
887 * result_uuid - resulting UUID.
888 * namespace - namespace in which given name exists and UUID should be created.
889 * name - unique string used to create version 3 UUID.
890 * namelen - length of the name string.
893 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
898 MD5_Update(&c
, namespace, sizeof(uuid_t
));
899 MD5_Update(&c
, name
, namelen
);
900 MD5_Final(result_uuid
, &c
);
902 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
903 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
907 /* **************************************** DoGetUUIDKey *******************************************
909 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
911 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
913 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
914 *************************************************************************************************** */
916 DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
) {
918 VolumeUUID targetVolumeUUID
;
922 unsigned char rawUUID
[8];
924 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
926 ((uint32_t *)rawUUID
)[0] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.high
);
927 ((uint32_t *)rawUUID
)[1] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.low
);
929 uuid_create_md5_from_name(uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
930 uuid_unparse(uuid
, uuidLine
);
931 write(1, uuidLine
, strlen(uuidLine
));
932 result
= FSUR_IO_SUCCESS
;
940 /* *************************************** DoChangeUUIDKey ******************************************
942 This routine will change the UUID on the specified block device.
944 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
946 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
947 *************************************************************************************************** */
949 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
951 VolumeUUID newVolumeUUID
;
953 GenerateVolumeUUID(&newVolumeUUID
);
954 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
961 /* **************************************** DoAdopt *******************************************
963 This routine will add the UUID of the specified block device to the list of local volumes.
965 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
967 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
968 *************************************************************************************************** */
970 DoAdopt( const char * theDeviceNamePtr
, const char *rawName
) {
971 int result
, closeresult
;
972 VolumeUUID targetVolumeUUID
;
973 VolumeStatusDBHandle vsdbhandle
= NULL
;
974 unsigned long targetVolumeStatus
;
976 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
978 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
979 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
980 targetVolumeStatus
= 0;
982 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
983 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
985 result
= FSUR_IO_SUCCESS
;
989 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
991 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
994 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
998 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
1005 /* **************************************** DoDisown *******************************************
1007 This routine will change the status of the specified block device to ignore its permissions.
1009 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
1011 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
1012 *************************************************************************************************** */
1014 DoDisown( const char * theDeviceNamePtr
, const char *rawName
) {
1015 int result
, closeresult
;
1016 VolumeUUID targetVolumeUUID
;
1017 VolumeStatusDBHandle vsdbhandle
= NULL
;
1018 unsigned long targetVolumeStatus
;
1020 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1022 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1023 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1024 targetVolumeStatus
= 0;
1026 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
1027 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1029 result
= FSUR_IO_SUCCESS
;
1033 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1035 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1038 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
1040 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
1042 result
= FSUR_IO_FAIL
;
1047 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
1054 get_multiplier(char c
)
1056 if (tolower(c
) == 'k') {
1058 } else if (tolower(c
) == 'm') {
1060 } else if (tolower(c
) == 'g') {
1061 return 1024 * 1024 * 1024;
1067 /* **************************************** ParseArgs ********************************************
1069 This routine will make sure the arguments passed in to us are cool.
1070 Here is how this utility is used:
1072 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1074 -p (Probe for mounting)
1075 -P (Probe for initializing - not supported)
1077 -r (Repair - not supported)
1080 -i (Initialize - not supported)
1083 disk0s2 (for example)
1086 /foo/bar/ (required for Mount and Force Mount actions)
1089 (these are ignored for CDROMs)
1090 either "readonly" OR "writable"
1091 either "removable" OR "fixed"
1092 either "nosuid" or "suid"
1093 either "nodev" or "dev"
1096 hfs.util -p disk0s2 removable writable
1097 hfs.util -p disk0s2 removable readonly
1098 hfs.util -m disk0s2 /my/hfs
1101 argc - the number of arguments in argv.
1102 argv - array of arguments.
1104 returns FSUR_INVAL if we find a bad argument else 0.
1105 *************************************************************************************************** */
1107 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1108 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1109 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1111 int result
= FSUR_INVAL
;
1112 int deviceLength
, doLengthCheck
= 1;
1116 /* Must have at least 3 arguments and the action argument must start with a '-' */
1117 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1118 DoDisplayUsage( argv
);
1122 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1124 * actionPtr
= & argv
[1][1];
1126 switch ( argv
[1][1] ) {
1128 /* action Probe and requires 5 arguments (need the flags) */
1130 DoDisplayUsage( argv
);
1138 /* Note: the device argument in argv[2] is checked further down but ignored. */
1139 * mountPointPtr
= argv
[3];
1140 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1144 case FSUC_MOUNT_FORCE
:
1145 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1147 DoDisplayUsage( argv
);
1150 * mountPointPtr
= argv
[3];
1176 if (isdigit(argv
[2][0])) {
1178 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1180 gJournalSize
*= get_multiplier(*ptr
);
1191 case FSUC_UNJNL_RAW
:
1196 case FSUC_JNLINFS_RAW
:
1201 case FSUC_EXTJNL_RAW
:
1213 DoDisplayUsage( argv
);
1218 /* Make sure device (argv[2]) is something reasonable */
1219 deviceLength
= strlen( argv
[2] );
1220 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1221 DoDisplayUsage( argv
);
1226 /* Flags: removable/fixed. */
1227 if ( 0 == strcmp(argv
[index
],"removable") ) {
1228 * isEjectablePtr
= 1;
1229 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1230 * isEjectablePtr
= 0;
1232 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1235 /* Flags: readonly/writable. */
1236 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1238 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1241 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1245 /* Flags: suid/nosuid. */
1246 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1248 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1251 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1254 /* Flags: dev/nodev. */
1255 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1257 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1260 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1275 /* *************************************** DoDisplayUsage ********************************************
1277 This routine will do a printf of the correct usage for this utility.
1279 argv - array of arguments.
1282 *************************************************************************************************** */
1284 DoDisplayUsage(const char *argv
[])
1286 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1287 printf("action_arg:\n");
1288 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1289 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1290 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1291 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1292 #ifdef HFS_UUID_SUPPORT
1293 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1294 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1295 #endif HFS_UUID_SUPPORT
1296 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1297 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1298 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1299 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1300 printf(" -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW
);
1301 printf(" -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW
);
1302 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1303 printf("device_arg:\n");
1304 printf(" device we are acting upon (for example, 'disk0s2')\n");
1305 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1306 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1307 printf("mount_point_arg:\n");
1308 printf(" required for Mount and Force Mount \n");
1310 printf(" required for Mount, Force Mount and Probe\n");
1311 printf(" indicates removable or fixed (for example 'fixed')\n");
1312 printf(" indicates readonly or writable (for example 'readonly')\n");
1313 printf(" indicates suid or nosuid (for example 'suid')\n");
1314 printf(" indicates dev or nodev (for example 'dev')\n");
1315 printf("Examples:\n");
1316 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1317 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1321 } /* DoDisplayUsage */
1327 Given a path to a device, determine if a volume is mounted on that
1328 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1329 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1330 no volume mounted on the device, set *pathPtr to NULL and return
1333 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1336 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1342 /* Assume no mounted volume found */
1344 result
= FSUR_IO_SUCCESS
;
1346 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1348 return FSUR_IO_FAIL
;
1350 for (i
=0; i
<numMounts
; ++i
) {
1351 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1352 /* Found a mounted volume; check the type */
1353 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1354 *pathPtr
= buf
[i
].f_mntonname
;
1355 /* result = FSUR_IO_SUCCESS, above */
1357 result
= FSUR_UNRECOGNIZED
;
1370 Read the Master Directory Block or Volume Header Block from an HFS,
1371 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1372 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1373 Return a pointer to the volume UUID in the Finder Info.
1375 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1378 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1381 HFSMasterDirectoryBlock
* mdbPtr
;
1382 HFSPlusVolumeHeader
* volHdrPtr
;
1388 * Read the HFS Master Directory Block or Volume Header from sector 2
1391 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1392 if (result
!= FSUR_IO_SUCCESS
)
1396 * If this is a wrapped HFS Plus volume, read the Volume Header from
1397 * sector 2 of the embedded volume.
1399 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1400 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1401 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1402 if (result
!= FSUR_IO_SUCCESS
)
1404 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1405 if (result
!= FSUR_IO_SUCCESS
)
1410 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1411 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1412 * UUID from the Finder Info.
1414 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1415 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1416 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1417 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1418 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1420 result
= FSUR_UNRECOGNIZED
;
1431 Read the UUID from an unmounted volume, by doing direct access to the device.
1432 Assumes the caller has already determined that a volume is not mounted
1435 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1438 GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
)
1443 VolumeUUID
*finderInfoUUIDPtr
;
1447 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1449 result
= FSUR_UNRECOGNIZED
;
1453 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1457 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", deviceNamePtr
, errno
);
1459 if (error
== EBUSY
) {
1460 /* If it was busy, then retry, this time using the raw device */
1461 fd
= open (rawName
, O_RDONLY
, 0);
1464 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName
, errno
);
1466 result
= FSUR_IO_FAIL
;
1471 result
= FSUR_IO_FAIL
;
1477 * Get the pointer to the volume UUID in the Finder Info
1479 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1480 if (result
!= FSUR_IO_SUCCESS
)
1484 * Copy the volume UUID out of the Finder Info
1486 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1487 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1490 if (fd
> 0) close(fd
);
1491 if (bufPtr
) free(bufPtr
);
1494 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1496 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1503 Write a previously generated UUID to an unmounted volume, by doing direct
1504 access to the device. Assumes the caller has already determined that a
1505 volume is not mounted on the device.
1507 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1510 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1515 VolumeUUID
*finderInfoUUIDPtr
;
1518 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1520 result
= FSUR_UNRECOGNIZED
;
1524 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1527 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1529 result
= FSUR_IO_FAIL
;
1534 * Get the pointer to the volume UUID in the Finder Info
1536 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1537 if (result
!= FSUR_IO_SUCCESS
)
1541 * Update the UUID in the Finder Info
1543 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1544 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1547 * Write the modified MDB or VHB back to disk
1549 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1552 if (fd
> 0) close(fd
);
1553 if (bufPtr
) free(bufPtr
);
1556 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1558 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1565 Read the UUID from a mounted volume, by calling getattrlist().
1566 Assumes the path is the mount point of an HFS volume.
1568 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1571 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1573 struct attrlist alist
;
1574 struct FinderAttrBuf volFinderInfo
;
1575 VolumeUUID
*finderInfoUUIDPtr
;
1578 /* Set up the attrlist structure to get the volume's Finder Info */
1579 alist
.bitmapcount
= 5;
1581 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1582 alist
.volattr
= ATTR_VOL_INFO
;
1587 /* Get the Finder Info */
1588 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1590 result
= FSUR_IO_FAIL
;
1594 /* Copy the UUID from the Finder Into to caller's buffer */
1595 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1596 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1597 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1598 result
= FSUR_IO_SUCCESS
;
1608 Write a UUID to a mounted volume, by calling setattrlist().
1609 Assumes the path is the mount point of an HFS volume.
1611 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1614 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1616 struct attrlist alist
;
1617 struct FinderAttrBuf volFinderInfo
;
1618 VolumeUUID
*finderInfoUUIDPtr
;
1621 /* Set up the attrlist structure to get the volume's Finder Info */
1622 alist
.bitmapcount
= 5;
1624 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1625 alist
.volattr
= ATTR_VOL_INFO
;
1630 /* Get the Finder Info */
1631 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1633 result
= FSUR_IO_FAIL
;
1637 /* Update the UUID in the Finder Info */
1638 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1639 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1640 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1642 /* Write the Finder Info back to the volume */
1643 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1645 result
= FSUR_IO_FAIL
;
1649 result
= FSUR_IO_SUCCESS
;
1659 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1660 we were asked to generate one, then generate a new UUID and write it to the
1663 Determine whether an HFS volume is mounted on the given device. If so, we
1664 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1665 the filesystem. If there is no mounted volume, then do direct device access
1666 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1668 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1672 GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1678 * Determine whether a volume is mounted on this device. If it is HFS, then
1679 * get the mount point's path. If it is non-HFS, then we can exit immediately
1680 * with FSUR_UNRECOGNIZED.
1682 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1683 if (result
!= FSUR_IO_SUCCESS
)
1687 * Get any existing UUID.
1690 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1692 result
= GetVolumeUUIDRaw(deviceNamePtr
, rawName
, volumeUUIDPtr
);
1693 if (result
!= FSUR_IO_SUCCESS
)
1697 * If there was no valid UUID, and we were asked to generate one, then
1698 * generate it and write it back to disk.
1700 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1701 GenerateVolumeUUID(volumeUUIDPtr
);
1703 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1705 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1706 /* Fall through to Err_Exit */
1718 Write a UUID to an HFS, HFS Plus or HFSX volume.
1720 Determine whether an HFS volume is mounted on the given device. If so, we
1721 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1722 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1724 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1727 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1732 * Determine whether a volume is mounted on this device. If it is HFS, then
1733 * get the mount point's path. If it is non-HFS, then we can exit immediately
1734 * with FSUR_UNRECOGNIZED.
1736 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1737 if (result
!= FSUR_IO_SUCCESS
)
1744 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1746 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1755 -- GetEmbeddedHFSPlusVol
1757 -- In: hfsMasterDirectoryBlockPtr
1758 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1759 (that is, 2 blocks before the volume header)
1764 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1766 int result
= FSUR_IO_SUCCESS
;
1767 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1769 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1770 result
= FSUR_UNRECOGNIZED
;
1774 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1775 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1777 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1778 result
= FSUR_UNRECOGNIZED
;
1782 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1783 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1785 if ( startOffsetPtr
)
1786 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1787 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1797 -- GetNameFromHFSPlusVolumeStartingAt
1799 -- Caller's responsibility to allocate and release memory for the converted string.
1801 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1805 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1807 int result
= FSUR_IO_SUCCESS
;
1808 u_int32_t blockSize
;
1809 char * bufPtr
= NULL
;
1810 HFSPlusVolumeHeader
* volHdrPtr
;
1811 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1812 u_int32_t catalogNodeSize
;
1814 u_int32_t catalogExtCount
;
1815 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1817 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1818 if ( ! volHdrPtr
) {
1819 result
= FSUR_IO_FAIL
;
1824 * Read the Volume Header
1825 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1827 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1828 if (result
== FSUR_IO_FAIL
) {
1830 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1832 goto Return
; // return FSUR_IO_FAIL
1835 /* Verify that it is an HFS+ volume. */
1837 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1838 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
1839 result
= FSUR_IO_FAIL
;
1841 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1846 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1847 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1848 if ( ! catalogExtents
) {
1849 result
= FSUR_IO_FAIL
;
1852 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1853 catalogExtCount
= kHFSPlusExtentDensity
;
1855 /* if there are overflow catalog extents, then go get them */
1856 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
1857 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1858 if (result
!= FSUR_IO_SUCCESS
)
1862 /* Read the header node of the catalog B-Tree */
1864 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1865 catalogExtCount
, catalogExtents
,
1866 &catalogNodeSize
, &leafNode
);
1867 if (result
!= FSUR_IO_SUCCESS
)
1870 /* Read the first leaf node of the catalog b-tree */
1872 bufPtr
= (char *)malloc(catalogNodeSize
);
1874 result
= FSUR_IO_FAIL
;
1878 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1880 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1881 hfsPlusVolumeOffset
, blockSize
,
1882 catalogExtCount
, catalogExtents
);
1883 if (result
== FSUR_IO_FAIL
) {
1885 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1887 goto Return
; // return FSUR_IO_FAIL
1893 HFSPlusCatalogKey
* k
;
1896 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
1897 result
= FSUR_IO_FAIL
;
1899 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1904 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1906 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1909 // Get a pointer to the first record.
1911 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
1912 k
= (HFSPlusCatalogKey
*)p
;
1914 // There should be only one record whose parent is the root parent. It should be the first record.
1916 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
1917 result
= FSUR_IO_FAIL
;
1919 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1924 if ((OSSwapBigToHostInt16(k
->nodeName
.length
) >
1925 (sizeof(k
->nodeName
.unicode
) / sizeof(k
->nodeName
.unicode
[0]))) ||
1926 OSSwapBigToHostInt16(k
->nodeName
.length
) > 255) {
1927 result
= FSUR_IO_FAIL
;
1929 fprintf(stderr
, "hfs.util: ERROR: k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k
->nodeName
.length
));
1934 /* Extract the name of the root directory */
1937 HFSUniStr255
*swapped
;
1940 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1941 if (swapped
== NULL
) {
1942 result
= FSUR_IO_FAIL
;
1945 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
1947 for (i
=0; i
<swapped
->length
; i
++) {
1948 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
1950 swapped
->unicode
[i
] = 0;
1951 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1952 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1958 result
= FSUR_IO_SUCCESS
;
1962 free((char*) volHdrPtr
);
1965 free((char*) catalogExtents
);
1968 free((char*)bufPtr
);
1972 } /* GetNameFromHFSPlusVolumeStartingAt */
1976 BTNodeDescriptor node
;
1978 } __attribute__((aligned(2), packed
)) HeaderRec
, *HeaderPtr
;
1983 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1987 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1988 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1989 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1992 HeaderRec
* bTreeHeaderPtr
= NULL
;
1994 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1995 if (bTreeHeaderPtr
== NULL
)
1996 return (FSUR_IO_FAIL
);
1998 /* Read the b-tree header node */
2000 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
2001 hfsPlusVolumeOffset
, blockSize
,
2002 extentCount
, extentList
);
2003 if ( result
== FSUR_IO_FAIL
) {
2005 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
2010 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
2011 result
= FSUR_IO_FAIL
;
2013 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
2018 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
2020 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
2023 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
2026 free((char*) bTreeHeaderPtr
);
2030 } /* GetBTreeNodeInfo */
2036 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2040 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
2041 HFSPlusVolumeHeader
*volHdrPtr
,
2042 HFSPlusExtentDescriptor
**catalogExtents
,
2043 u_int32_t
*catalogExtCount
)
2046 u_int32_t numRecords
;
2049 u_int32_t blockSize
;
2050 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
2051 HFSPlusExtentDescriptor
* extents
;
2053 char * bufPtr
= NULL
;
2057 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2058 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
2059 extents
= *catalogExtents
;
2060 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
2063 /* Read the header node of the extents B-Tree */
2065 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2066 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
2067 &nodeSize
, &leafNode
);
2068 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
2071 /* Calculate the logical position of the first leaf node */
2073 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2075 /* Read the first leaf node of the extents b-tree */
2077 bufPtr
= (char *)malloc(nodeSize
);
2079 result
= FSUR_IO_FAIL
;
2083 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2086 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
2087 hfsPlusVolumeOffset
, blockSize
,
2088 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
2089 if ( result
== FSUR_IO_FAIL
) {
2091 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2096 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
2097 result
= FSUR_IO_FAIL
;
2101 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
2102 for (i
= 1; i
<= numRecords
; ++i
) {
2105 HFSPlusExtentKey
* k
;
2108 * Get the offset (in bytes) of the record from the
2109 * list of offsets at the end of the node
2111 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
2114 /* Get a pointer to the record */
2116 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
2117 k
= (HFSPlusExtentKey
*)p
;
2119 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
2122 /* grow list and copy additional extents */
2123 listsize
+= sizeof(HFSPlusExtentRecord
);
2124 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2125 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2126 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2128 *catalogExtCount
+= kHFSPlusExtentDensity
;
2129 *catalogExtents
= extents
;
2132 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2134 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2149 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2150 * position and number of contiguous bytes at that position.
2153 * logicalOffset Logical offset in bytes from start of file
2154 * length Maximum number of bytes to map
2155 * blockSize Number of bytes per allocation block
2156 * extentCount Number of extents in file
2157 * extentList The file's extents
2160 * physicalOffset Physical offset in bytes from start of volume
2161 * availableBytes Number of bytes physically contiguous (up to length)
2163 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2165 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2166 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2167 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2170 u_int32_t logicalBlock
;
2172 u_int32_t blockCount
= 0;
2174 /* Determine allocation block containing logicalOffset */
2175 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
2176 offset
%= blockSize
; /* Offset from start of allocation block */
2178 /* Find the extent containing logicalBlock */
2179 for (extent
= 0; extent
< extentCount
; ++extent
)
2181 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2183 if (blockCount
== 0)
2184 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2186 if (logicalBlock
< blockCount
)
2187 break; /* Found it! */
2189 logicalBlock
-= blockCount
;
2192 if (extent
>= extentCount
)
2193 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2196 * When we get here, extentList[extent] is the extent containing logicalOffset.
2197 * The desired allocation block is logicalBlock blocks into the extent.
2200 /* Compute the physical starting position */
2201 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2202 temp
*= blockSize
; /* Byte offset of first physical block */
2203 *physicalOffset
= temp
+ offset
;
2205 /* Compute the available contiguous bytes. */
2206 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2208 temp
-= offset
; /* Number of bytes available */
2211 *availableBytes
= temp
;
2213 *availableBytes
= length
;
2215 return FSUR_IO_SUCCESS
;
2221 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2222 * ending position are not allocation or device block aligned.
2225 * fd Descriptor for reading the volume
2226 * buffer The bytes are read into here
2227 * offset Offset in file to start reading
2228 * length Number of bytes to read
2229 * volOffset Byte offset from start of device to start of volume
2230 * blockSize Number of bytes per allocation block
2231 * extentCount Number of extents in file
2232 * extentList The file's exents
2234 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2236 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2237 off_t volOffset
, u_int32_t blockSize
,
2238 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2240 int result
= FSUR_IO_SUCCESS
;
2246 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2247 &physOffset
, &physLength
);
2248 if (result
!= FSUR_IO_SUCCESS
)
2251 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2252 if (result
!= FSUR_IO_SUCCESS
)
2255 length
-= physLength
;
2256 offset
+= physLength
;
2257 buffer
= (char *) buffer
+ physLength
;
2264 -- readAt = lseek() + read()
2266 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2271 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2276 void * rawData
= NULL
;
2279 ssize_t dataOffset
= 0;
2280 int result
= FSUR_IO_SUCCESS
;
2282 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2284 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2286 result
= FSUR_IO_FAIL
;
2289 /* put offset and length in terms of device blocksize */
2290 rawOffset
= offset
/ blocksize
* blocksize
;
2291 dataOffset
= offset
- rawOffset
;
2292 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2293 rawData
= malloc(rawLength
);
2294 if (rawData
== NULL
) {
2295 result
= FSUR_IO_FAIL
;
2299 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2300 if ( lseekResult
!= rawOffset
) {
2301 result
= FSUR_IO_FAIL
;
2305 readResult
= read(fd
, rawData
, rawLength
);
2306 if ( readResult
!= rawLength
) {
2308 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2310 result
= FSUR_IO_FAIL
;
2313 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2324 -- writeAt = lseek() + write()
2326 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2331 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2335 ssize_t bytestransferred
;
2336 void * rawData
= NULL
;
2339 ssize_t dataOffset
= 0;
2340 int result
= FSUR_IO_SUCCESS
;
2342 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2344 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2346 result
= FSUR_IO_FAIL
;
2349 /* put offset and length in terms of device blocksize */
2350 rawOffset
= offset
/ blocksize
* blocksize
;
2351 dataOffset
= offset
- rawOffset
;
2352 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2353 rawData
= malloc(rawLength
);
2354 if (rawData
== NULL
) {
2355 result
= FSUR_IO_FAIL
;
2359 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2360 if ( deviceoffset
!= rawOffset
) {
2361 result
= FSUR_IO_FAIL
;
2365 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2366 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2367 bytestransferred
= read(fd
, rawData
, rawLength
);
2368 if ( bytestransferred
!= rawLength
) {
2370 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2372 result
= FSUR_IO_FAIL
;
2377 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2379 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2380 if ( deviceoffset
!= rawOffset
) {
2381 result
= FSUR_IO_FAIL
;
2385 bytestransferred
= write(fd
, rawData
, rawLength
);
2386 if ( bytestransferred
!= rawLength
) {
2388 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2390 result
= FSUR_IO_FAIL
;
2395 if (rawData
) free(rawData
);
2403 * Get kernel's encoding bias.
2409 size_t buflen
= sizeof(int);
2413 if (getvfsbyname("hfs", &vfc
) < 0)
2417 mib
[1] = vfc
.vfc_typenum
;
2418 mib
[2] = HFS_ENCODINGBIAS
;
2420 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2427 /******************************************************************************
2429 * 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
2431 *****************************************************************************/
2433 #define DBHANDLESIGNATURE 0x75917737
2435 /* Flag values for operation options: */
2436 #define DBMARKPOSITION 1
2438 static char gVSDBPath
[] = "/var/db/volinfo.database";
2440 #define MAXIOMALLOC 16384
2442 /* Database layout: */
2449 char statusFlags
[8];
2456 struct VSDBRecord record
;
2460 #define DBKEYSEPARATOR ':'
2461 #define DBBLANKSPACE ' '
2462 #define DBRECORDTERMINATOR '\n'
2464 /* In-memory data structures: */
2467 unsigned long signature
;
2470 off_t recordPosition
;
2473 typedef struct VSDBState
*VSDBStatePtr
;
2477 /* Internal function prototypes: */
2478 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2479 static int UnlockDB(VSDBStatePtr dbstateptr
);
2481 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2482 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2483 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2484 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2485 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2487 static void FormatULong(unsigned long u
, char *s
);
2488 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2489 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2490 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2491 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2492 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2496 /******************************************************************************
2498 * 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
2500 *****************************************************************************/
2502 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2504 char randomInputBuffer
[26];
2505 unsigned char digest
[20];
2510 char sysctlstring
[128];
2512 double sysloadavg
[3];
2513 struct vmtotal sysvmtotal
;
2516 /* Initialize the SHA-1 context for processing: */
2517 SHA1_Init(&context
);
2519 /* Now process successive bits of "random" input to seed the process: */
2521 /* The current system's uptime: */
2523 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2525 /* The kernel's boot time: */
2527 mib
[1] = KERN_BOOTTIME
;
2528 datalen
= sizeof(sysdata
);
2529 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2530 SHA1_Update(&context
, &sysdata
, datalen
);
2532 /* The system's host id: */
2534 mib
[1] = KERN_HOSTID
;
2535 datalen
= sizeof(sysdata
);
2536 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2537 SHA1_Update(&context
, &sysdata
, datalen
);
2539 /* The system's host name: */
2541 mib
[1] = KERN_HOSTNAME
;
2542 datalen
= sizeof(sysctlstring
);
2543 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2544 SHA1_Update(&context
, sysctlstring
, datalen
);
2546 /* The running kernel's OS release string: */
2548 mib
[1] = KERN_OSRELEASE
;
2549 datalen
= sizeof(sysctlstring
);
2550 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2551 SHA1_Update(&context
, sysctlstring
, datalen
);
2553 /* The running kernel's version string: */
2555 mib
[1] = KERN_VERSION
;
2556 datalen
= sizeof(sysctlstring
);
2557 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2558 SHA1_Update(&context
, sysctlstring
, datalen
);
2560 /* The system's load average: */
2561 datalen
= sizeof(sysloadavg
);
2562 getloadavg(sysloadavg
, 3);
2563 SHA1_Update(&context
, &sysloadavg
, datalen
);
2565 /* The system's VM statistics: */
2568 datalen
= sizeof(sysvmtotal
);
2569 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2570 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2572 /* The current GMT (26 ASCII characters): */
2574 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2575 SHA1_Update(&context
, randomInputBuffer
, 26);
2577 /* Pad the accumulated input and extract the final digest hash: */
2578 SHA1_Final(digest
, &context
);
2580 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2581 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2586 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2589 u_int32_t nextdigit
;
2594 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2595 if ((c
>= '0') && (c
<= '9')) {
2596 nextdigit
= c
- '0';
2597 } else if ((c
>= 'A') && (c
<= 'F')) {
2598 nextdigit
= c
- 'A' + 10;
2599 } else if ((c
>= 'a') && (c
<= 'f')) {
2600 nextdigit
= c
- 'a' + 10;
2604 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2605 high
= (high
<< 4) | carry
;
2606 low
= (low
<< 4) | nextdigit
;
2609 volumeID
->v
.high
= high
;
2610 volumeID
->v
.low
= low
;
2615 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2616 FormatUUID(volumeID
, UUIDString
);
2617 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2622 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2623 VSDBStatePtr dbstateptr
;
2625 *DBHandlePtr
= NULL
;
2627 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2628 if (dbstateptr
== NULL
) {
2632 dbstateptr
->dbmode
= O_RDWR
;
2633 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2634 if (dbstateptr
->dbfile
== -1) {
2636 The file couldn't be opened for read/write access:
2637 try read-only access before giving up altogether.
2639 dbstateptr
->dbmode
= O_RDONLY
;
2640 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2641 if (dbstateptr
->dbfile
== -1) {
2646 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2647 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2653 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2654 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2655 struct VSDBEntry dbentry
;
2658 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2660 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2662 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2665 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2670 UnlockDB(dbstateptr
);
2676 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2677 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2678 struct VSDBEntry dbentry
;
2681 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2682 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2684 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2686 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2687 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2689 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2691 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2692 } else if (result
== -1) {
2694 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2696 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2701 fsync(dbstateptr
->dbfile
);
2706 UnlockDB(dbstateptr
);
2712 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2713 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2716 unsigned long iobuffersize
;
2717 void *iobuffer
= NULL
;
2719 unsigned long iotransfersize
;
2720 unsigned long bytestransferred
;
2722 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2724 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2726 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2728 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2730 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2734 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2736 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2737 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2738 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2740 iobuffersize
= MAXIOMALLOC
;
2743 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2744 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2745 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2747 if (iobuffersize
> 0) {
2748 iobuffer
= malloc(iobuffersize
);
2749 if (iobuffer
== NULL
) {
2754 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2756 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2757 if (iotransfersize
> 0) {
2758 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2761 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2763 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2764 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2765 if (bytestransferred
!= iotransfersize
) {
2771 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2773 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2774 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2775 if (bytestransferred
!= iotransfersize
) {
2780 dataoffset
+= (off_t
)iotransfersize
;
2782 } while (iotransfersize
> 0);
2785 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2787 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2791 fsync(dbstateptr
->dbfile
);
2797 if (iobuffer
) free(iobuffer
);
2798 UnlockDB(dbstateptr
);
2806 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2807 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2809 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2811 dbstateptr
->signature
= 0;
2813 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2814 dbstateptr
->dbfile
= 0;
2823 /******************************************************************************
2825 * 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
2827 *****************************************************************************/
2829 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2831 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2833 return flock(dbstateptr
->dbfile
, lockmode
);
2838 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2840 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2842 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2847 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2848 struct VSDBKey searchkey
;
2849 struct VSDBEntry dbentry
;
2852 FormatDBKey(volumeID
, &searchkey
);
2853 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2856 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2857 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2858 if (targetEntry
!= NULL
) {
2860 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2862 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2866 } while (result
== 0);
2873 static int AddVolumeRecord(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
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2883 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2884 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2890 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2892 VolumeUUIDString id
;
2896 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2897 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2898 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2900 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2902 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2904 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2909 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2910 struct VSDBEntry entry
;
2913 VolumeUUIDString id
;
2916 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2917 #if 0 // DEBUG_TRACE
2918 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2920 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2921 if ((result
!= sizeof(entry
)) ||
2922 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2923 (entry
.space
!= DBBLANKSPACE
) ||
2924 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2929 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2930 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2931 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2933 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2939 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2940 #if 0 // DEBUG_TRACE
2941 VolumeUUIDString id
;
2943 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2944 id
[sizeof(key1
->uuid
)] = (char)0;
2945 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2946 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2947 id
[sizeof(key2
->uuid
)] = (char)0;
2948 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2951 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2956 /******************************************************************************
2958 * 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
2960 *****************************************************************************/
2962 static void FormatULong(unsigned long u
, char *s
) {
2967 for (i
= 0; i
< 8; ++i
) {
2968 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2970 *digitptr
++ = (char)(d
+ '0');
2972 *digitptr
++ = (char)(d
- 10 + 'A');
2980 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2981 FormatULong(volumeID
->v
.high
, UUIDField
);
2982 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2988 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2989 FormatUUID(volumeID
, dbkey
->uuid
);
2994 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2995 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
3000 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
3001 FormatDBKey(volumeID
, &dbentry
->key
);
3002 dbentry
->keySeparator
= DBKEYSEPARATOR
;
3003 dbentry
->space
= DBBLANKSPACE
;
3004 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
3005 #if 0 // DEBUG_TRACE
3006 dbentry
->terminator
= (char)0;
3007 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
3009 dbentry
->terminator
= DBRECORDTERMINATOR
;
3014 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
3017 unsigned long nextdigit
;
3021 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
3022 if ((c
>= '0') && (c
<= '9')) {
3023 nextdigit
= c
- '0';
3024 } else if ((c
>= 'A') && (c
<= 'F')) {
3025 nextdigit
= c
- 'A' + 10;
3026 } else if ((c
>= 'a') && (c
<= 'f')) {
3027 nextdigit
= c
- 'a' + 10;
3031 n
= (n
<< 4) + nextdigit
;