2 * Copyright (c) 1999-2014 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 <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 kHFSPlusMaxFileNameBytes (3 * 255 + 1) /* 255 unicode characters, plus 1 NUL byte */
130 #define HFS_BLOCK_SIZE 512
132 char gHFS_FS_NAME
[] = "hfs";
133 char gHFS_FS_NAME_NAME
[] = "HFS";
135 char gNewlineString
[] = "\n";
137 char gMountCommand
[] = "/sbin/mount";
139 char gUnmountCommand
[] = "/sbin/umount";
141 char gReadOnlyOption
[] = "-r";
142 char gReadWriteOption
[] = "-w";
144 char gSuidOption
[] = "suid";
145 char gNoSuidOption
[] = "nosuid";
147 char gDevOption
[] = "dev";
148 char gNoDevOption
[] = "nodev";
150 char gUsePermissionsOption
[] = "perm";
151 char gIgnorePermissionsOption
[] = "noperm";
153 boolean_t gIsEjectable
= 0;
155 int gJournalSize
= 0;
157 #define AUTO_ADOPT_FIXED 1
158 #define AUTO_ENTER_FIXED 0
161 struct FinderAttrBuf
{
162 u_int32_t info_length
;
163 u_int32_t finderinfo
[8];
167 #define VOLUMEUUIDVALUESIZE 2
168 typedef union VolumeUUID
{
169 u_int32_t value
[VOLUMEUUIDVALUESIZE
];
176 #define VOLUMEUUIDLENGTH 16
177 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1];
179 #define VOLUME_RECORDED 0x80000000
180 #define VOLUME_USEPERMISSIONS 0x00000001
181 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
183 typedef void *VolumeStatusDBHandle
;
185 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
186 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
);
187 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
);
188 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
189 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
);
190 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
);
191 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
192 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
194 /* ************************************ P R O T O T Y P E S *************************************** */
195 static void DoDisplayUsage( const char * argv
[] );
196 static int DoMount( char * theDeviceNamePtr
, const char *rawName
, const char * theMountPointPtr
,
197 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
198 static int DoProbe( char * rawDeviceNamePtr
, char * blockDeviceNamePtr
);
199 static int DoUnmount( const char * theMountPointPtr
);
200 static int DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
);
201 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
202 static int DoAdopt( const char * theDeviceNamePtr
, const char *rawName
);
203 static int DoDisown( const char * theDeviceNamePtr
, const char *rawName
);
205 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
206 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
207 extern int DoGetJournalInfo( const char * volNamePtr
);
208 extern int RawDisableJournaling( const char *devname
);
209 extern int SetJournalInFSState( const char *devname
, int journal_in_fs
);
211 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
);
213 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
214 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
);
215 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
);
216 static int GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
217 static int GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
218 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
219 static int SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
220 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
221 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
222 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
223 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
224 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
225 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
226 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
227 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
228 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
229 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
230 off_t
*physicalOffset
, ssize_t
*availableBytes
);
231 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
232 off_t volOffset
, u_int32_t blockSize
,
233 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
234 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
235 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
237 static int GetEncodingBias(void);
240 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
242 static void uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
);
245 * The fuction CFStringGetSystemEncoding does not work correctly in
246 * our context (autodiskmount deamon). We include a local copy here
247 * so that we can derive the default encoding. Radar 2516316.
249 #if READ_DEFAULT_ENCODING
250 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
252 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
253 struct passwd
*passwdp
;
255 if ((passwdp
= getpwuid(0))) { // root account
256 char buffer
[MAXPATHLEN
+ 1];
259 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
260 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
262 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
265 readSize
= read(fd
, buffer
, MAXPATHLEN
);
266 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
268 return strtol(buffer
, NULL
, 0);
271 return 0; // Fallback to smRoman
276 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
278 struct hfs_mnt_encoding
{
279 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
280 CFStringEncoding encoding_id
; /* encoding type number */
283 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
289 { "CentralEurRoman", 29 },
290 { "ChineseSimp", 25 },
291 { "ChineseTrad", 2 },
312 { "Roman", 0 }, /* default */
320 { "Ukrainian", 152 },
321 { "Vietnamese", 30 },
324 #define KEXT_LOAD_COMMAND "/sbin/kextload"
325 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/"
327 static int load_encoding(CFStringEncoding encoding
)
335 char kmodfile
[MAXPATHLEN
];
337 /* Find the encoding that matches the one passed in */
338 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
340 for (i
=0; i
<numEncodings
; ++i
)
342 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
344 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
349 if (encodingName
== NULL
)
351 /* Couldn't figure out which encoding KEXT to load */
352 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
356 snprintf(kmodfile
, sizeof(kmodfile
), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
357 if (stat(kmodfile
, &sb
) == -1)
359 /* We recognized the encoding, but couldn't find the KEXT */
360 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
367 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
369 exit(1); /* We can only get here if the exec failed */
373 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
375 if (WEXITSTATUS(status
) != 0)
377 /* kextload returned an error. Too bad its output doesn't get logged. */
378 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
384 return FSUR_IO_SUCCESS
;
388 /* ******************************************** main ************************************************
390 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
391 for detail info on input arguments.
393 argc - the number of arguments in argv.
394 argv - array of arguments.
396 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
397 *************************************************************************************************** */
399 int main (int argc
, const char *argv
[])
401 const char * actionPtr
= NULL
;
402 char rawDeviceName
[MAXPATHLEN
];
403 char blockDeviceName
[MAXPATHLEN
];
404 const char * mountPointPtr
= NULL
;
405 int result
= FSUR_IO_SUCCESS
;
406 boolean_t isLocked
= 0; /* reasonable assumptions */
407 boolean_t isSetuid
= 0; /* reasonable assumptions */
408 boolean_t isDev
= 0; /* reasonable assumptions */
410 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
412 /* Verify our arguments */
413 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
418 -- Build our device name (full path), should end up with something like:
422 snprintf(rawDeviceName
, sizeof(rawDeviceName
), "/dev/r%s", argv
[2]);
423 snprintf(blockDeviceName
, sizeof(blockDeviceName
), "/dev/%s", argv
[2]);
425 /* call the appropriate routine to handle the given action argument after becoming root */
427 switch( * actionPtr
) {
429 result
= DoProbe(rawDeviceName
, blockDeviceName
);
433 case FSUC_MOUNT_FORCE
:
434 result
= DoMount(blockDeviceName
, rawDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
438 result
= DoUnmount( mountPointPtr
);
441 result
= DoGetUUIDKey( blockDeviceName
, rawDeviceName
);
445 result
= DoChangeUUIDKey( blockDeviceName
);
448 result
= DoAdopt( blockDeviceName
, rawDeviceName
);
452 result
= DoDisown( blockDeviceName
, rawDeviceName
);
457 result
= DoMakeJournaled( argv
[3], gJournalSize
);
459 result
= DoMakeJournaled( argv
[2], gJournalSize
);
464 result
= DoUnJournal( argv
[2] );
468 result
= RawDisableJournaling( argv
[2] );
471 case FSUC_JNLINFS_RAW
:
472 // argv[2] has the device for the external journal. however
473 // we don't need it so we ignore it and just pass argv[3]
474 // which is the hfs volume whose state we're going to change
476 result
= SetJournalInFSState( argv
[3], 1 );
479 case FSUC_EXTJNL_RAW
:
480 // see the comment for FSUC_JNLINFS_RAW
481 result
= SetJournalInFSState( argv
[3], 0 );
485 result
= DoGetJournalInfo( argv
[2] );
489 /* should never get here since ParseArgs should handle this situation */
490 DoDisplayUsage( argv
);
499 return result
; /*...and make main fit the ANSI spec. */
503 /* ***************************** DoMount ********************************
505 This routine will fire off a system command to mount the given device at the given mountpoint.
506 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
508 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
509 mountPointPtr - pointer to the mount point.
512 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
513 *********************************************************************** */
515 DoMount(char *deviceNamePtr
, const char *rawName
, const char *mountPointPtr
,
516 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
522 char *permissionsOption
;
523 int result
= FSUR_IO_FAIL
;
525 char encodeopt
[16] = "";
526 CFStringEncoding encoding
;
527 VolumeUUID targetVolumeUUID
;
528 VolumeStatusDBHandle vsdbhandle
= NULL
;
529 unsigned long targetVolumeStatus
;
531 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
532 return (FSUR_IO_FAIL
);
534 /* get the volume UUID to check if permissions should be used: */
535 targetVolumeStatus
= 0;
536 if (((result
= GetVolumeUUID(deviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
537 (targetVolumeUUID
.v
.high
==0) ||
538 (targetVolumeUUID
.v
.low
== 0)) {
540 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
543 if (gIsEjectable
== 0) {
544 result
= DoAdopt( deviceNamePtr
, rawName
);
546 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
548 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
551 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
553 targetVolumeStatus
= 0;
557 /* We've got a real volume UUID! */
559 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
561 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
562 /* Can't even get access to the volume info db; assume permissions are OK. */
564 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
566 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
569 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
571 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
573 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
576 if (gIsEjectable
== 0) {
577 result
= DoAdopt( deviceNamePtr
, rawName
);
579 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
581 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
584 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
586 targetVolumeStatus
= 0;
589 targetVolumeStatus
= 0;
592 (void)CloseVolumeStatusDB(vsdbhandle
);
599 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
600 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
601 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
604 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
606 /* get default encoding value (for hfs volumes) */
607 #if READ_DEFAULT_ENCODING
608 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
610 encoding
= CFStringGetSystemEncoding();
612 snprintf(encodeopt
, sizeof(encodeopt
), "-e=%d", (int)encoding
);
614 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
615 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
617 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
618 "-o", encodeopt
, "-o", permissionsOption
,
619 "-o", "-u=unknown,-g=unknown,-m=0777",
620 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
623 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
624 return (FSUR_IO_FAIL
);
628 return (FSUR_IO_FAIL
);
631 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
632 result
= status
.w_retcode
;
636 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
640 /* ****************************************** DoUnmount *********************************************
642 This routine will fire off a system command to unmount the given device.
644 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
646 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
647 *************************************************************************************************** */
649 DoUnmount(const char * theMountPointPtr
)
655 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
660 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
662 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
664 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
665 return (FSUR_IO_FAIL
);
669 return (FSUR_IO_FAIL
);
672 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
673 result
= status
.w_retcode
;
677 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
685 Get the volume name of the volume mounted at "path". Print that volume
686 name to standard out.
688 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
690 struct VolumeNameBuf
{
691 u_int32_t info_length
;
692 attrreference_t name_ref
;
697 PrintVolumeNameAttr(const char *path
)
699 struct attrlist alist
;
700 struct VolumeNameBuf volNameInfo
;
703 /* Set up the attrlist structure to get the volume's Finder Info */
704 alist
.bitmapcount
= 5;
706 alist
.commonattr
= 0;
707 alist
.volattr
= ATTR_VOL_INFO
| ATTR_VOL_NAME
;
712 /* Get the Finder Info */
713 result
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0);
715 result
= FSUR_IO_FAIL
;
719 /* Print the name to standard out */
720 printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
);
721 result
= FSUR_RECOGNIZED
;
728 /* ******************************************* DoProbe **********************************************
730 This routine will open the given device and check to make sure there is media that looks
731 like an HFS. If it is HFS, then print the volume name to standard output.
733 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
734 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
736 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
737 *************************************************************************************************** */
739 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
)
741 int result
= FSUR_UNRECOGNIZED
;
744 HFSMasterDirectoryBlock
* mdbPtr
;
745 HFSPlusVolumeHeader
* volHdrPtr
;
746 u_char volnameUTF8
[kHFSPlusMaxFileNameBytes
];
749 * Determine if there is a volume already mounted from this device. If
750 * there is, and it is HFS, then we need to get the volume name via
753 * NOTE: We're using bufPtr to hold a pointer to a path.
756 result
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
);
757 if (result
!= FSUR_IO_SUCCESS
) {
760 if (bufPtr
!= NULL
) {
761 /* There is an HFS volume mounted from the device. */
762 result
= PrintVolumeNameAttr(bufPtr
);
767 * If we get here, there is no volume mounted from this device, so
768 * go probe the raw device directly.
771 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
773 result
= FSUR_UNRECOGNIZED
;
777 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
778 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
780 fd
= open( rawDeviceNamePtr
, O_RDONLY
, 0 );
782 result
= FSUR_IO_FAIL
;
787 * Read the HFS Master Directory Block from sector 2
789 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
790 if (FSUR_IO_FAIL
== result
)
793 /* get classic HFS volume name (from MDB) */
794 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
795 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
798 CFStringEncoding encoding
;
800 /* Some poorly mastered HFS CDs have an empty MDB name field! */
801 if (mdbPtr
->drVN
[0] == '\0') {
802 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
803 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
806 /* Check for an encoding hint in the Finder Info (field 4). */
807 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
808 if (encoding
== kCFStringEncodingInvalidId
) {
809 /* Next try the encoding bias in the kernel. */
810 encoding
= GetEncodingBias();
811 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
812 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
815 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
816 mdbPtr
->drVN
, encoding
);
821 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
824 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
826 /* default to MacRoman on conversion errors */
827 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
828 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
829 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
831 encoding
= kCFStringEncodingMacRoman
;
834 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
835 if (encoding
!= kCFStringEncodingMacRoman
) {
836 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
837 encoding
= kCFStringEncodingMacRoman
;
838 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
839 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
844 /* get HFS Plus volume name (from Catalog) */
845 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
846 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
847 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
848 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
851 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
852 /* embedded volume, first find offset */
853 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
854 if ( result
!= FSUR_IO_SUCCESS
)
860 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
863 result
= FSUR_UNRECOGNIZED
;
866 if (FSUR_IO_SUCCESS
== result
) {
867 /* Print the volume name to standard output */
868 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
869 result
= FSUR_RECOGNIZED
;
885 * Create a version 3 UUID from a unique "name" in the given "name space".
886 * Version 3 UUID are derived using "name" via MD5 checksum.
889 * result_uuid - resulting UUID.
890 * namespace - namespace in which given name exists and UUID should be created.
891 * name - unique string used to create version 3 UUID.
892 * namelen - length of the name string.
895 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
900 MD5_Update(&c
, namespace, sizeof(uuid_t
));
901 MD5_Update(&c
, name
, namelen
);
902 MD5_Final(result_uuid
, &c
);
904 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
905 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
909 /* **************************************** DoGetUUIDKey *******************************************
911 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
913 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
915 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
916 *************************************************************************************************** */
918 DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
) {
920 VolumeUUID targetVolumeUUID
;
924 unsigned char rawUUID
[8];
926 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
928 ((uint32_t *)rawUUID
)[0] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.high
);
929 ((uint32_t *)rawUUID
)[1] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.low
);
931 uuid_create_md5_from_name(uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
932 uuid_unparse(uuid
, uuidLine
);
933 write(1, uuidLine
, strlen(uuidLine
));
934 result
= FSUR_IO_SUCCESS
;
942 /* *************************************** DoChangeUUIDKey ******************************************
944 This routine will change the UUID on the specified block device.
946 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
948 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
949 *************************************************************************************************** */
951 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
953 VolumeUUID newVolumeUUID
;
955 GenerateVolumeUUID(&newVolumeUUID
);
956 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
963 /* **************************************** DoAdopt *******************************************
965 This routine will add the UUID of the specified block device to the list of local volumes.
967 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
969 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
970 *************************************************************************************************** */
972 DoAdopt( const char * theDeviceNamePtr
, const char *rawName
) {
973 int result
, closeresult
;
974 VolumeUUID targetVolumeUUID
;
975 VolumeStatusDBHandle vsdbhandle
= NULL
;
976 unsigned long targetVolumeStatus
;
978 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
980 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
981 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
982 targetVolumeStatus
= 0;
984 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
985 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
987 result
= FSUR_IO_SUCCESS
;
991 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
993 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
996 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
1000 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
1007 /* **************************************** DoDisown *******************************************
1009 This routine will change the status of the specified block device to ignore its permissions.
1011 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
1013 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
1014 *************************************************************************************************** */
1016 DoDisown( const char * theDeviceNamePtr
, const char *rawName
) {
1017 int result
, closeresult
;
1018 VolumeUUID targetVolumeUUID
;
1019 VolumeStatusDBHandle vsdbhandle
= NULL
;
1020 unsigned long targetVolumeStatus
;
1022 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1024 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1025 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1026 targetVolumeStatus
= 0;
1028 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
1029 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1031 result
= FSUR_IO_SUCCESS
;
1035 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1037 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1040 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
1042 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
1044 result
= FSUR_IO_FAIL
;
1049 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
1056 get_multiplier(char c
)
1058 if (tolower(c
) == 'k') {
1060 } else if (tolower(c
) == 'm') {
1062 } else if (tolower(c
) == 'g') {
1063 return 1024 * 1024 * 1024;
1069 /* **************************************** ParseArgs ********************************************
1071 This routine will make sure the arguments passed in to us are cool.
1072 Here is how this utility is used:
1074 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1076 -p (Probe for mounting)
1077 -P (Probe for initializing - not supported)
1079 -r (Repair - not supported)
1082 -i (Initialize - not supported)
1085 disk0s2 (for example)
1088 /foo/bar/ (required for Mount and Force Mount actions)
1091 (these are ignored for CDROMs)
1092 either "readonly" OR "writable"
1093 either "removable" OR "fixed"
1094 either "nosuid" or "suid"
1095 either "nodev" or "dev"
1098 hfs.util -p disk0s2 removable writable
1099 hfs.util -p disk0s2 removable readonly
1100 hfs.util -m disk0s2 /my/hfs
1103 argc - the number of arguments in argv.
1104 argv - array of arguments.
1106 returns FSUR_INVAL if we find a bad argument else 0.
1107 *************************************************************************************************** */
1109 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1110 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1111 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1113 int result
= FSUR_INVAL
;
1114 int deviceLength
, doLengthCheck
= 1;
1118 /* Must have at least 3 arguments and the action argument must start with a '-' */
1119 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1120 DoDisplayUsage( argv
);
1124 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1126 * actionPtr
= & argv
[1][1];
1128 switch ( argv
[1][1] ) {
1130 /* action Probe and requires 5 arguments (need the flags) */
1132 DoDisplayUsage( argv
);
1140 /* Note: the device argument in argv[2] is checked further down but ignored. */
1141 * mountPointPtr
= argv
[3];
1142 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1146 case FSUC_MOUNT_FORCE
:
1147 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1149 DoDisplayUsage( argv
);
1152 * mountPointPtr
= argv
[3];
1178 if (isdigit(argv
[2][0])) {
1180 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1182 gJournalSize
*= get_multiplier(*ptr
);
1193 case FSUC_UNJNL_RAW
:
1198 case FSUC_JNLINFS_RAW
:
1203 case FSUC_EXTJNL_RAW
:
1215 DoDisplayUsage( argv
);
1220 /* Make sure device (argv[2]) is something reasonable */
1221 deviceLength
= strlen( argv
[2] );
1222 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1223 DoDisplayUsage( argv
);
1228 /* Flags: removable/fixed. */
1229 if ( 0 == strcmp(argv
[index
],"removable") ) {
1230 * isEjectablePtr
= 1;
1231 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1232 * isEjectablePtr
= 0;
1234 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1237 /* Flags: readonly/writable. */
1238 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1240 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1243 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1247 /* Flags: suid/nosuid. */
1248 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1250 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1253 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1256 /* Flags: dev/nodev. */
1257 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1259 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1262 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1277 /* *************************************** DoDisplayUsage ********************************************
1279 This routine will do a printf of the correct usage for this utility.
1281 argv - array of arguments.
1284 *************************************************************************************************** */
1286 DoDisplayUsage(const char *argv
[])
1288 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1289 printf("action_arg:\n");
1290 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1291 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1292 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1293 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1294 #ifdef HFS_UUID_SUPPORT
1295 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1296 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1297 #endif HFS_UUID_SUPPORT
1298 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1299 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1300 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1301 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1302 printf(" -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW
);
1303 printf(" -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW
);
1304 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1305 printf("device_arg:\n");
1306 printf(" device we are acting upon (for example, 'disk0s2')\n");
1307 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1308 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1309 printf("mount_point_arg:\n");
1310 printf(" required for Mount and Force Mount \n");
1312 printf(" required for Mount, Force Mount and Probe\n");
1313 printf(" indicates removable or fixed (for example 'fixed')\n");
1314 printf(" indicates readonly or writable (for example 'readonly')\n");
1315 printf(" indicates suid or nosuid (for example 'suid')\n");
1316 printf(" indicates dev or nodev (for example 'dev')\n");
1317 printf("Examples:\n");
1318 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1319 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1323 } /* DoDisplayUsage */
1329 Given a path to a device, determine if a volume is mounted on that
1330 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1331 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1332 no volume mounted on the device, set *pathPtr to NULL and return
1335 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1338 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1344 /* Assume no mounted volume found */
1346 result
= FSUR_IO_SUCCESS
;
1348 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1350 return FSUR_IO_FAIL
;
1352 for (i
=0; i
<numMounts
; ++i
) {
1353 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1354 /* Found a mounted volume; check the type */
1355 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1356 *pathPtr
= buf
[i
].f_mntonname
;
1357 /* result = FSUR_IO_SUCCESS, above */
1359 result
= FSUR_UNRECOGNIZED
;
1372 Read the Master Directory Block or Volume Header Block from an HFS,
1373 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1374 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1375 Return a pointer to the volume UUID in the Finder Info.
1377 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1380 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1383 HFSMasterDirectoryBlock
* mdbPtr
;
1384 HFSPlusVolumeHeader
* volHdrPtr
;
1390 * Read the HFS Master Directory Block or Volume Header from sector 2
1393 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1394 if (result
!= FSUR_IO_SUCCESS
)
1398 * If this is a wrapped HFS Plus volume, read the Volume Header from
1399 * sector 2 of the embedded volume.
1401 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1402 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1403 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1404 if (result
!= FSUR_IO_SUCCESS
)
1406 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1407 if (result
!= FSUR_IO_SUCCESS
)
1412 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1413 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1414 * UUID from the Finder Info.
1416 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1417 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1418 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1419 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1420 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1422 result
= FSUR_UNRECOGNIZED
;
1433 Read the UUID from an unmounted volume, by doing direct access to the device.
1434 Assumes the caller has already determined that a volume is not mounted
1437 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1440 GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
)
1445 VolumeUUID
*finderInfoUUIDPtr
;
1449 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1451 result
= FSUR_UNRECOGNIZED
;
1455 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1459 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", deviceNamePtr
, errno
);
1461 if (error
== EBUSY
) {
1462 /* If it was busy, then retry, this time using the raw device */
1463 fd
= open (rawName
, O_RDONLY
, 0);
1466 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName
, errno
);
1468 result
= FSUR_IO_FAIL
;
1473 result
= FSUR_IO_FAIL
;
1479 * Get the pointer to the volume UUID in the Finder Info
1481 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1482 if (result
!= FSUR_IO_SUCCESS
)
1486 * Copy the volume UUID out of the Finder Info
1488 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1489 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1492 if (fd
> 0) close(fd
);
1493 if (bufPtr
) free(bufPtr
);
1496 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1498 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1505 Write a previously generated UUID to an unmounted volume, by doing direct
1506 access to the device. Assumes the caller has already determined that a
1507 volume is not mounted on the device.
1509 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1512 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1517 VolumeUUID
*finderInfoUUIDPtr
;
1520 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1522 result
= FSUR_UNRECOGNIZED
;
1526 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1529 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1531 result
= FSUR_IO_FAIL
;
1536 * Get the pointer to the volume UUID in the Finder Info
1538 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1539 if (result
!= FSUR_IO_SUCCESS
)
1543 * Update the UUID in the Finder Info
1545 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1546 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1549 * Write the modified MDB or VHB back to disk
1551 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1554 if (fd
> 0) close(fd
);
1555 if (bufPtr
) free(bufPtr
);
1558 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1560 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1567 Read the UUID from a mounted volume, by calling getattrlist().
1568 Assumes the path is the mount point of an HFS volume.
1570 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1573 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1575 struct attrlist alist
;
1576 struct FinderAttrBuf volFinderInfo
;
1577 VolumeUUID
*finderInfoUUIDPtr
;
1580 /* Set up the attrlist structure to get the volume's Finder Info */
1581 alist
.bitmapcount
= 5;
1583 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1584 alist
.volattr
= ATTR_VOL_INFO
;
1589 /* Get the Finder Info */
1590 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1592 result
= FSUR_IO_FAIL
;
1596 /* Copy the UUID from the Finder Into to caller's buffer */
1597 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1598 volumeUUIDPtr
->v
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
);
1599 volumeUUIDPtr
->v
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
);
1600 result
= FSUR_IO_SUCCESS
;
1610 Write a UUID to a mounted volume, by calling setattrlist().
1611 Assumes the path is the mount point of an HFS volume.
1613 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1616 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1618 struct attrlist alist
;
1619 struct FinderAttrBuf volFinderInfo
;
1620 VolumeUUID
*finderInfoUUIDPtr
;
1623 /* Set up the attrlist structure to get the volume's Finder Info */
1624 alist
.bitmapcount
= 5;
1626 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1627 alist
.volattr
= ATTR_VOL_INFO
;
1632 /* Get the Finder Info */
1633 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1635 result
= FSUR_IO_FAIL
;
1639 /* Update the UUID in the Finder Info */
1640 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1641 finderInfoUUIDPtr
->v
.high
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
);
1642 finderInfoUUIDPtr
->v
.low
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
);
1644 /* Write the Finder Info back to the volume */
1645 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1647 result
= FSUR_IO_FAIL
;
1651 result
= FSUR_IO_SUCCESS
;
1661 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1662 we were asked to generate one, then generate a new UUID and write it to the
1665 Determine whether an HFS volume is mounted on the given device. If so, we
1666 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1667 the filesystem. If there is no mounted volume, then do direct device access
1668 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1670 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1674 GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1680 * Determine whether a volume is mounted on this device. If it is HFS, then
1681 * get the mount point's path. If it is non-HFS, then we can exit immediately
1682 * with FSUR_UNRECOGNIZED.
1684 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1685 if (result
!= FSUR_IO_SUCCESS
)
1689 * Get any existing UUID.
1692 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1694 result
= GetVolumeUUIDRaw(deviceNamePtr
, rawName
, volumeUUIDPtr
);
1695 if (result
!= FSUR_IO_SUCCESS
)
1699 * If there was no valid UUID, and we were asked to generate one, then
1700 * generate it and write it back to disk.
1702 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1703 GenerateVolumeUUID(volumeUUIDPtr
);
1705 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1707 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1708 /* Fall through to Err_Exit */
1720 Write a UUID to an HFS, HFS Plus or HFSX volume.
1722 Determine whether an HFS volume is mounted on the given device. If so, we
1723 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1724 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1726 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1729 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1734 * Determine whether a volume is mounted on this device. If it is HFS, then
1735 * get the mount point's path. If it is non-HFS, then we can exit immediately
1736 * with FSUR_UNRECOGNIZED.
1738 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1739 if (result
!= FSUR_IO_SUCCESS
)
1746 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1748 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1757 -- GetEmbeddedHFSPlusVol
1759 -- In: hfsMasterDirectoryBlockPtr
1760 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1761 (that is, 2 blocks before the volume header)
1766 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1768 int result
= FSUR_IO_SUCCESS
;
1769 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1771 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1772 result
= FSUR_UNRECOGNIZED
;
1776 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1777 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1779 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1780 result
= FSUR_UNRECOGNIZED
;
1784 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1785 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1787 if ( startOffsetPtr
)
1788 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1789 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1799 -- GetNameFromHFSPlusVolumeStartingAt
1801 -- Caller's responsibility to allocate and release memory for the converted string.
1803 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1807 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1809 int result
= FSUR_IO_SUCCESS
;
1810 u_int32_t blockSize
;
1811 char * bufPtr
= NULL
;
1812 HFSPlusVolumeHeader
* volHdrPtr
;
1813 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1814 u_int32_t catalogNodeSize
;
1816 u_int32_t catalogExtCount
;
1817 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1819 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1820 if ( ! volHdrPtr
) {
1821 result
= FSUR_IO_FAIL
;
1826 * Read the Volume Header
1827 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1829 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1830 if (result
== FSUR_IO_FAIL
) {
1832 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1834 goto Return
; // return FSUR_IO_FAIL
1837 /* Verify that it is an HFS+ volume. */
1839 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1840 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
1841 result
= FSUR_IO_FAIL
;
1843 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1848 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1849 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1850 if ( ! catalogExtents
) {
1851 result
= FSUR_IO_FAIL
;
1854 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1855 catalogExtCount
= kHFSPlusExtentDensity
;
1857 /* if there are overflow catalog extents, then go get them */
1858 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
1859 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1860 if (result
!= FSUR_IO_SUCCESS
)
1864 /* Read the header node of the catalog B-Tree */
1866 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1867 catalogExtCount
, catalogExtents
,
1868 &catalogNodeSize
, &leafNode
);
1869 if (result
!= FSUR_IO_SUCCESS
)
1872 /* Read the first leaf node of the catalog b-tree */
1874 bufPtr
= (char *)malloc(catalogNodeSize
);
1876 result
= FSUR_IO_FAIL
;
1880 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1882 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1883 hfsPlusVolumeOffset
, blockSize
,
1884 catalogExtCount
, catalogExtents
);
1885 if (result
== FSUR_IO_FAIL
) {
1887 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1889 goto Return
; // return FSUR_IO_FAIL
1895 HFSPlusCatalogKey
* k
;
1898 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
1899 result
= FSUR_IO_FAIL
;
1901 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1906 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1908 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1911 // Get a pointer to the first record.
1913 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
1914 k
= (HFSPlusCatalogKey
*)p
;
1916 // There should be only one record whose parent is the root parent. It should be the first record.
1918 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
1919 result
= FSUR_IO_FAIL
;
1921 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1926 if ((OSSwapBigToHostInt16(k
->nodeName
.length
) >
1927 (sizeof(k
->nodeName
.unicode
) / sizeof(k
->nodeName
.unicode
[0]))) ||
1928 OSSwapBigToHostInt16(k
->nodeName
.length
) > 255) {
1929 result
= FSUR_IO_FAIL
;
1931 fprintf(stderr
, "hfs.util: ERROR: k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k
->nodeName
.length
));
1936 /* Extract the name of the root directory */
1939 HFSUniStr255
*swapped
;
1942 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1943 if (swapped
== NULL
) {
1944 result
= FSUR_IO_FAIL
;
1947 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
1949 for (i
=0; i
<swapped
->length
; i
++) {
1950 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
1952 swapped
->unicode
[i
] = 0;
1953 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1954 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
* 3 + 1, kCFStringEncodingUTF8
);
1960 result
= FSUR_IO_SUCCESS
;
1964 free((char*) volHdrPtr
);
1967 free((char*) catalogExtents
);
1970 free((char*)bufPtr
);
1974 } /* GetNameFromHFSPlusVolumeStartingAt */
1978 BTNodeDescriptor node
;
1980 } __attribute__((aligned(2), packed
)) HeaderRec
, *HeaderPtr
;
1985 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1989 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1990 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1991 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1994 HeaderRec
* bTreeHeaderPtr
= NULL
;
1996 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1997 if (bTreeHeaderPtr
== NULL
)
1998 return (FSUR_IO_FAIL
);
2000 /* Read the b-tree header node */
2002 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
2003 hfsPlusVolumeOffset
, blockSize
,
2004 extentCount
, extentList
);
2005 if ( result
== FSUR_IO_FAIL
) {
2007 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
2012 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
2013 result
= FSUR_IO_FAIL
;
2015 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
2020 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
2022 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
2025 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
2028 free((char*) bTreeHeaderPtr
);
2032 } /* GetBTreeNodeInfo */
2038 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2042 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
2043 HFSPlusVolumeHeader
*volHdrPtr
,
2044 HFSPlusExtentDescriptor
**catalogExtents
,
2045 u_int32_t
*catalogExtCount
)
2048 u_int32_t numRecords
;
2051 u_int32_t blockSize
;
2052 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
2053 HFSPlusExtentDescriptor
* extents
;
2055 char * bufPtr
= NULL
;
2059 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2060 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
2061 extents
= *catalogExtents
;
2062 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
2065 /* Read the header node of the extents B-Tree */
2067 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2068 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
2069 &nodeSize
, &leafNode
);
2070 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
2073 /* Calculate the logical position of the first leaf node */
2075 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2077 /* Read the first leaf node of the extents b-tree */
2079 bufPtr
= (char *)malloc(nodeSize
);
2081 result
= FSUR_IO_FAIL
;
2085 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2088 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
2089 hfsPlusVolumeOffset
, blockSize
,
2090 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
2091 if ( result
== FSUR_IO_FAIL
) {
2093 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2098 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
2099 result
= FSUR_IO_FAIL
;
2103 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
2104 for (i
= 1; i
<= numRecords
; ++i
) {
2107 HFSPlusExtentKey
* k
;
2110 * Get the offset (in bytes) of the record from the
2111 * list of offsets at the end of the node
2113 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
2116 /* Get a pointer to the record */
2118 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
2119 k
= (HFSPlusExtentKey
*)p
;
2121 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
2124 /* grow list and copy additional extents */
2125 listsize
+= sizeof(HFSPlusExtentRecord
);
2126 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2127 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2128 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2130 *catalogExtCount
+= kHFSPlusExtentDensity
;
2131 *catalogExtents
= extents
;
2134 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2136 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2151 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2152 * position and number of contiguous bytes at that position.
2155 * logicalOffset Logical offset in bytes from start of file
2156 * length Maximum number of bytes to map
2157 * blockSize Number of bytes per allocation block
2158 * extentCount Number of extents in file
2159 * extentList The file's extents
2162 * physicalOffset Physical offset in bytes from start of volume
2163 * availableBytes Number of bytes physically contiguous (up to length)
2165 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2167 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2168 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2169 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2172 u_int32_t logicalBlock
;
2174 u_int32_t blockCount
= 0;
2176 /* Determine allocation block containing logicalOffset */
2177 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
2178 offset
%= blockSize
; /* Offset from start of allocation block */
2180 /* Find the extent containing logicalBlock */
2181 for (extent
= 0; extent
< extentCount
; ++extent
)
2183 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2185 if (blockCount
== 0)
2186 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2188 if (logicalBlock
< blockCount
)
2189 break; /* Found it! */
2191 logicalBlock
-= blockCount
;
2194 if (extent
>= extentCount
)
2195 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2198 * When we get here, extentList[extent] is the extent containing logicalOffset.
2199 * The desired allocation block is logicalBlock blocks into the extent.
2202 /* Compute the physical starting position */
2203 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2204 temp
*= blockSize
; /* Byte offset of first physical block */
2205 *physicalOffset
= temp
+ offset
;
2207 /* Compute the available contiguous bytes. */
2208 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2210 temp
-= offset
; /* Number of bytes available */
2213 *availableBytes
= temp
;
2215 *availableBytes
= length
;
2217 return FSUR_IO_SUCCESS
;
2223 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2224 * ending position are not allocation or device block aligned.
2227 * fd Descriptor for reading the volume
2228 * buffer The bytes are read into here
2229 * offset Offset in file to start reading
2230 * length Number of bytes to read
2231 * volOffset Byte offset from start of device to start of volume
2232 * blockSize Number of bytes per allocation block
2233 * extentCount Number of extents in file
2234 * extentList The file's exents
2236 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2238 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2239 off_t volOffset
, u_int32_t blockSize
,
2240 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2242 int result
= FSUR_IO_SUCCESS
;
2248 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2249 &physOffset
, &physLength
);
2250 if (result
!= FSUR_IO_SUCCESS
)
2253 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2254 if (result
!= FSUR_IO_SUCCESS
)
2257 length
-= physLength
;
2258 offset
+= physLength
;
2259 buffer
= (char *) buffer
+ physLength
;
2266 -- readAt = lseek() + read()
2268 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2273 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2278 void * rawData
= NULL
;
2281 ssize_t dataOffset
= 0;
2282 int result
= FSUR_IO_SUCCESS
;
2284 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2286 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2288 result
= FSUR_IO_FAIL
;
2291 /* put offset and length in terms of device blocksize */
2292 rawOffset
= offset
/ blocksize
* blocksize
;
2293 dataOffset
= offset
- rawOffset
;
2294 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2295 rawData
= malloc(rawLength
);
2296 if (rawData
== NULL
) {
2297 result
= FSUR_IO_FAIL
;
2301 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2302 if ( lseekResult
!= rawOffset
) {
2303 result
= FSUR_IO_FAIL
;
2307 readResult
= read(fd
, rawData
, rawLength
);
2308 if ( readResult
!= rawLength
) {
2310 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2312 result
= FSUR_IO_FAIL
;
2315 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2326 -- writeAt = lseek() + write()
2328 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2333 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2337 ssize_t bytestransferred
;
2338 void * rawData
= NULL
;
2341 ssize_t dataOffset
= 0;
2342 int result
= FSUR_IO_SUCCESS
;
2344 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2346 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2348 result
= FSUR_IO_FAIL
;
2351 /* put offset and length in terms of device blocksize */
2352 rawOffset
= offset
/ blocksize
* blocksize
;
2353 dataOffset
= offset
- rawOffset
;
2354 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2355 rawData
= malloc(rawLength
);
2356 if (rawData
== NULL
) {
2357 result
= FSUR_IO_FAIL
;
2361 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2362 if ( deviceoffset
!= rawOffset
) {
2363 result
= FSUR_IO_FAIL
;
2367 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2368 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2369 bytestransferred
= read(fd
, rawData
, rawLength
);
2370 if ( bytestransferred
!= rawLength
) {
2372 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2374 result
= FSUR_IO_FAIL
;
2379 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2381 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2382 if ( deviceoffset
!= rawOffset
) {
2383 result
= FSUR_IO_FAIL
;
2387 bytestransferred
= write(fd
, rawData
, rawLength
);
2388 if ( bytestransferred
!= rawLength
) {
2390 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2392 result
= FSUR_IO_FAIL
;
2397 if (rawData
) free(rawData
);
2405 * Get kernel's encoding bias.
2411 size_t buflen
= sizeof(int);
2415 if (getvfsbyname("hfs", &vfc
) < 0)
2419 mib
[1] = vfc
.vfc_typenum
;
2420 mib
[2] = HFS_ENCODINGBIAS
;
2422 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2429 /******************************************************************************
2431 * V O L U M E S T A T U S D A T A B A S E R O U T I N E S
2433 *****************************************************************************/
2435 #define DBHANDLESIGNATURE 0x75917737
2437 /* Flag values for operation options: */
2438 #define DBMARKPOSITION 1
2440 static char gVSDBPath
[] = "/var/db/volinfo.database";
2442 #define MAXIOMALLOC 16384
2444 /* Database layout: */
2451 char statusFlags
[8];
2458 struct VSDBRecord record
;
2462 #define DBKEYSEPARATOR ':'
2463 #define DBBLANKSPACE ' '
2464 #define DBRECORDTERMINATOR '\n'
2466 /* In-memory data structures: */
2469 unsigned long signature
;
2472 off_t recordPosition
;
2475 typedef struct VSDBState
*VSDBStatePtr
;
2479 /* Internal function prototypes: */
2480 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2481 static int UnlockDB(VSDBStatePtr dbstateptr
);
2483 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2484 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2485 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2486 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2487 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2489 static void FormatULong(unsigned long u
, char *s
);
2490 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2491 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2492 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2493 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2494 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2498 /******************************************************************************
2500 * P U B L I S H E D I N T E R F A C E R O U T I N E S
2502 *****************************************************************************/
2504 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2506 char randomInputBuffer
[26];
2507 unsigned char digest
[20];
2512 char sysctlstring
[128];
2514 double sysloadavg
[3];
2515 struct vmtotal sysvmtotal
;
2518 /* Initialize the SHA-1 context for processing: */
2519 SHA1_Init(&context
);
2521 /* Now process successive bits of "random" input to seed the process: */
2523 /* The current system's uptime: */
2525 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2527 /* The kernel's boot time: */
2529 mib
[1] = KERN_BOOTTIME
;
2530 datalen
= sizeof(sysdata
);
2531 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2532 SHA1_Update(&context
, &sysdata
, datalen
);
2534 /* The system's host id: */
2536 mib
[1] = KERN_HOSTID
;
2537 datalen
= sizeof(sysdata
);
2538 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2539 SHA1_Update(&context
, &sysdata
, datalen
);
2541 /* The system's host name: */
2543 mib
[1] = KERN_HOSTNAME
;
2544 datalen
= sizeof(sysctlstring
);
2545 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2546 SHA1_Update(&context
, sysctlstring
, datalen
);
2548 /* The running kernel's OS release string: */
2550 mib
[1] = KERN_OSRELEASE
;
2551 datalen
= sizeof(sysctlstring
);
2552 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2553 SHA1_Update(&context
, sysctlstring
, datalen
);
2555 /* The running kernel's version string: */
2557 mib
[1] = KERN_VERSION
;
2558 datalen
= sizeof(sysctlstring
);
2559 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2560 SHA1_Update(&context
, sysctlstring
, datalen
);
2562 /* The system's load average: */
2563 datalen
= sizeof(sysloadavg
);
2564 getloadavg(sysloadavg
, 3);
2565 SHA1_Update(&context
, &sysloadavg
, datalen
);
2567 /* The system's VM statistics: */
2570 datalen
= sizeof(sysvmtotal
);
2571 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2572 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2574 /* The current GMT (26 ASCII characters): */
2576 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2577 SHA1_Update(&context
, randomInputBuffer
, 26);
2579 /* Pad the accumulated input and extract the final digest hash: */
2580 SHA1_Final(digest
, &context
);
2582 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2583 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2588 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2591 u_int32_t nextdigit
;
2596 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2597 if ((c
>= '0') && (c
<= '9')) {
2598 nextdigit
= c
- '0';
2599 } else if ((c
>= 'A') && (c
<= 'F')) {
2600 nextdigit
= c
- 'A' + 10;
2601 } else if ((c
>= 'a') && (c
<= 'f')) {
2602 nextdigit
= c
- 'a' + 10;
2606 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2607 high
= (high
<< 4) | carry
;
2608 low
= (low
<< 4) | nextdigit
;
2611 volumeID
->v
.high
= high
;
2612 volumeID
->v
.low
= low
;
2617 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2618 FormatUUID(volumeID
, UUIDString
);
2619 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2624 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2625 VSDBStatePtr dbstateptr
;
2627 *DBHandlePtr
= NULL
;
2629 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2630 if (dbstateptr
== NULL
) {
2634 dbstateptr
->dbmode
= O_RDWR
;
2635 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2636 if (dbstateptr
->dbfile
== -1) {
2638 The file couldn't be opened for read/write access:
2639 try read-only access before giving up altogether.
2641 dbstateptr
->dbmode
= O_RDONLY
;
2642 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2643 if (dbstateptr
->dbfile
== -1) {
2648 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2649 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2655 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2656 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2657 struct VSDBEntry dbentry
;
2660 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2662 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2664 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2667 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2672 UnlockDB(dbstateptr
);
2678 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2679 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2680 struct VSDBEntry dbentry
;
2683 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2684 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2686 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2688 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2689 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2691 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2693 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2694 } else if (result
== -1) {
2696 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2698 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2703 fsync(dbstateptr
->dbfile
);
2708 UnlockDB(dbstateptr
);
2714 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2715 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2718 unsigned long iobuffersize
;
2719 void *iobuffer
= NULL
;
2721 unsigned long iotransfersize
;
2722 unsigned long bytestransferred
;
2724 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2726 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2728 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2730 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2732 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2736 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2738 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2739 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2740 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2742 iobuffersize
= MAXIOMALLOC
;
2745 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2746 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2747 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2749 if (iobuffersize
> 0) {
2750 iobuffer
= malloc(iobuffersize
);
2751 if (iobuffer
== NULL
) {
2756 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2758 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2759 if (iotransfersize
> 0) {
2760 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2763 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2765 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2766 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2767 if (bytestransferred
!= iotransfersize
) {
2773 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2775 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2776 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2777 if (bytestransferred
!= iotransfersize
) {
2782 dataoffset
+= (off_t
)iotransfersize
;
2784 } while (iotransfersize
> 0);
2787 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2789 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2793 fsync(dbstateptr
->dbfile
);
2799 if (iobuffer
) free(iobuffer
);
2800 UnlockDB(dbstateptr
);
2808 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2809 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2811 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2813 dbstateptr
->signature
= 0;
2815 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2816 dbstateptr
->dbfile
= 0;
2825 /******************************************************************************
2827 * I N T E R N A L O N L Y D A T A B A S E R O U T I N E S
2829 *****************************************************************************/
2831 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2833 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2835 return flock(dbstateptr
->dbfile
, lockmode
);
2840 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2842 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2844 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2849 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2850 struct VSDBKey searchkey
;
2851 struct VSDBEntry dbentry
;
2854 FormatDBKey(volumeID
, &searchkey
);
2855 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2858 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2859 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2860 if (targetEntry
!= NULL
) {
2862 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2864 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2868 } while (result
== 0);
2875 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2877 VolumeUUIDString id
;
2881 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2882 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2883 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2885 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2886 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2892 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2894 VolumeUUIDString id
;
2898 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2899 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2900 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2902 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2904 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2906 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2911 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2912 struct VSDBEntry entry
;
2915 VolumeUUIDString id
;
2918 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2919 #if 0 // DEBUG_TRACE
2920 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2922 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2923 if ((result
!= sizeof(entry
)) ||
2924 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2925 (entry
.space
!= DBBLANKSPACE
) ||
2926 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2931 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2932 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2933 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2935 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2941 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2942 #if 0 // DEBUG_TRACE
2943 VolumeUUIDString id
;
2945 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2946 id
[sizeof(key1
->uuid
)] = (char)0;
2947 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2948 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2949 id
[sizeof(key2
->uuid
)] = (char)0;
2950 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2953 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2958 /******************************************************************************
2960 * F O R M A T T I N G A N D C O N V E R S I O N R O U T I N E S
2962 *****************************************************************************/
2964 static void FormatULong(unsigned long u
, char *s
) {
2969 for (i
= 0; i
< 8; ++i
) {
2970 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2972 *digitptr
++ = (char)(d
+ '0');
2974 *digitptr
++ = (char)(d
- 10 + 'A');
2982 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2983 FormatULong(volumeID
->v
.high
, UUIDField
);
2984 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2990 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2991 FormatUUID(volumeID
, dbkey
->uuid
);
2996 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2997 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
3002 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
3003 FormatDBKey(volumeID
, &dbentry
->key
);
3004 dbentry
->keySeparator
= DBKEYSEPARATOR
;
3005 dbentry
->space
= DBBLANKSPACE
;
3006 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
3007 #if 0 // DEBUG_TRACE
3008 dbentry
->terminator
= (char)0;
3009 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
3011 dbentry
->terminator
= DBRECORDTERMINATOR
;
3016 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
3019 unsigned long nextdigit
;
3023 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
3024 if ((c
>= '0') && (c
<= '9')) {
3025 nextdigit
= c
- '0';
3026 } else if ((c
>= 'A') && (c
<= 'F')) {
3027 nextdigit
= c
- 'A' + 10;
3028 } else if ((c
>= 'a') && (c
<= 'f')) {
3029 nextdigit
= c
- 'a' + 10;
3033 n
= (n
<< 4) + nextdigit
;