2 * Copyright (c) 1999-2016 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>
71 * CommonCrypto provides a more stable API than OpenSSL guarantees;
72 * the #define causes it to use the same API for MD5 and SHA1, so the rest of
73 * the code need not change.
75 #define COMMON_DIGEST_FOR_OPENSSL
76 #include <CommonCrypto/CommonDigest.h>
78 #include <libkern/OSByteOrder.h>
80 #include <CoreFoundation/CFString.h>
82 #include <uuid/uuid.h>
83 #include <System/uuid/namespace.h>
85 #define READ_DEFAULT_ENCODING 1
88 #define FSUC_ADOPT 'a'
92 #define FSUC_DISOWN 'd'
96 #define FSUC_GETUUID 'k'
100 #define FSUC_SETUUID 's'
104 #define FSUC_MKJNL 'J'
108 #define FSUC_UNJNL 'U'
111 #ifndef FSUC_UNJNL_RAW
112 #define FSUC_UNJNL_RAW 'N'
115 #ifndef FSUC_JNLINFS_RAW
116 #define FSUC_JNLINFS_RAW 'e'
119 #ifndef FSUC_EXTJNL_RAW
120 #define FSUC_EXTJNL_RAW 'E'
124 #define FSUC_JNLINFO 'I'
128 /* **************************************** L O C A L S ******************************************* */
130 #define kHFSPlusMaxFileNameBytes (3 * 255 + 1) /* 255 unicode characters, plus 1 NUL byte */
132 #define HFS_BLOCK_SIZE 512
134 char gHFS_FS_NAME
[] = "hfs";
135 char gHFS_FS_NAME_NAME
[] = "HFS";
137 char gNewlineString
[] = "\n";
139 char gMountCommand
[] = "/sbin/mount";
141 char gUnmountCommand
[] = "/sbin/umount";
143 char gReadOnlyOption
[] = "-r";
144 char gReadWriteOption
[] = "-w";
146 char gSuidOption
[] = "suid";
147 char gNoSuidOption
[] = "nosuid";
149 char gDevOption
[] = "dev";
150 char gNoDevOption
[] = "nodev";
152 char gUsePermissionsOption
[] = "perm";
153 char gIgnorePermissionsOption
[] = "noperm";
155 boolean_t gIsEjectable
= 0;
157 int gJournalSize
= 0;
159 #define AUTO_ADOPT_FIXED 1
160 #define AUTO_ENTER_FIXED 0
163 typedef struct FinderAttrBuf
{
164 u_int32_t info_length
;
165 u_int32_t finderinfo
[8];
169 /* For requesting the UUID from the FS */
170 typedef struct UUIDAttrBuf
{
171 uint32_t info_length
;
175 /* HFS+ internal representation of UUID */
176 typedef struct hfs_UUID
{
182 typedef struct volUUID
{
187 #define HFSUUIDLENGTH 16
189 #define VOLUME_RECORDED 0x80000000
190 #define VOLUME_USEPERMISSIONS 0x00000001
191 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
193 typedef void *VolumeStatusDBHandle
;
195 /* UUID generation and conversion functions */
196 void GenerateHFSVolumeUUID(hfs_UUID_t
*hfsuu
);
197 void ConvertHFSUUIDStringToUUID (const char* UUIDString
, volUUID_t
*volid
);
198 void ConvertHFSUUIDToUUID (hfs_UUID_t
*hfsuuid
, volUUID_t
*uu
);
201 * Volume Database manipulation routines
202 * These functions MUST manipulate the VSDB in the same way that vsdbutil does
204 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
205 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
206 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long *VolumeStatus
);
207 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long VolumeStatus
);
208 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
);
209 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
211 /* ************************************ P R O T O T Y P E S *************************************** */
212 static void DoDisplayUsage( const char * argv
[] );
213 static int DoMount( char * theDeviceNamePtr
, const char *rawName
, const char * theMountPointPtr
,
214 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
215 static int DoProbe( char * rawDeviceNamePtr
, char * blockDeviceNamePtr
);
216 static int DoUnmount( const char * theMountPointPtr
);
217 static int DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
);
218 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
219 static int DoAdopt( const char * theDeviceNamePtr
, const char *rawName
);
220 static int DoDisown( const char * theDeviceNamePtr
, const char *rawName
);
222 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
223 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
224 extern int DoGetJournalInfo( const char * volNamePtr
);
225 extern int RawDisableJournaling( const char *devname
);
226 extern int SetJournalInFSState( const char *devname
, int journal_in_fs
);
228 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
);
229 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
231 /* Helper functions for manipulating HFS and full UUIDs */
232 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, hfs_UUID_t
**finderInfoUUIDPtr
);
233 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*volumeUUIDPtr
);
234 static int GetVolumeUUIDAttr(const char *path
, volUUID_t
*volumeUUIDPtr
);
235 static int GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*volumeUUIDPtr
, boolean_t generate
);
236 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, hfs_UUID_t
*hfsuu
);
237 static int SetVolumeUUIDAttr(const char *path
, hfs_UUID_t
*hfsuu
);
238 static int SetVolumeUUID(const char *deviceNamePtr
, hfs_UUID_t
*hfsuu
);
241 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
242 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
243 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
244 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
245 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
246 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
247 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
248 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
249 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
250 off_t
*physicalOffset
, ssize_t
*availableBytes
);
251 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
252 off_t volOffset
, u_int32_t blockSize
,
253 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
254 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
255 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
257 static int GetEncodingBias(void);
260 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
262 static void uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
);
265 * The fuction CFStringGetSystemEncoding does not work correctly in
266 * our context (autodiskmount deamon). We include a local copy here
267 * so that we can derive the default encoding. Radar 2516316.
269 #if READ_DEFAULT_ENCODING
270 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
272 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
273 struct passwd
*passwdp
;
275 if ((passwdp
= getpwuid(0))) { // root account
276 char buffer
[MAXPATHLEN
+ 1];
279 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
280 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
282 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
286 readSize
= read(fd
, buffer
, MAXPATHLEN
);
287 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
290 encoding
= strtol(buffer
, NULL
, 0);
291 assert(encoding
> -1 && encoding
<= UINT_MAX
);
292 return (unsigned int)encoding
;
295 return 0; // Fallback to smRoman
300 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
302 struct hfs_mnt_encoding
{
303 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
304 CFStringEncoding encoding_id
; /* encoding type number */
307 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
313 { "CentralEurRoman", 29 },
314 { "ChineseSimp", 25 },
315 { "ChineseTrad", 2 },
336 { "Roman", 0 }, /* default */
344 { "Ukrainian", 152 },
345 { "Vietnamese", 30 },
348 #define KEXT_LOAD_COMMAND "/sbin/kextload"
349 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/"
351 static int load_encoding(CFStringEncoding encoding
)
359 char kmodfile
[MAXPATHLEN
];
361 /* Find the encoding that matches the one passed in */
362 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
364 for (i
=0; i
<numEncodings
; ++i
)
366 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
368 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
373 if (encodingName
== NULL
)
375 /* Couldn't figure out which encoding KEXT to load */
376 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
380 snprintf(kmodfile
, sizeof(kmodfile
), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
381 if (stat(kmodfile
, &sb
) == -1)
383 /* We recognized the encoding, but couldn't find the KEXT */
384 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
391 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
393 exit(1); /* We can only get here if the exec failed */
397 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
399 if (WEXITSTATUS(status
) != 0)
401 /* kextload returned an error. Too bad its output doesn't get logged. */
402 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
408 return FSUR_IO_SUCCESS
;
412 /* ******************************************** main ************************************************
414 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
415 for detail info on input arguments.
417 argc - the number of arguments in argv.
418 argv - array of arguments.
420 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
421 *************************************************************************************************** */
423 int main (int argc
, const char *argv
[])
425 const char * actionPtr
= NULL
;
426 char rawDeviceName
[MAXPATHLEN
];
427 char blockDeviceName
[MAXPATHLEN
];
428 const char * mountPointPtr
= NULL
;
429 int result
= FSUR_IO_SUCCESS
;
430 boolean_t isLocked
= 0; /* reasonable assumptions */
431 boolean_t isSetuid
= 0; /* reasonable assumptions */
432 boolean_t isDev
= 0; /* reasonable assumptions */
434 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
436 /* Verify our arguments */
437 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
442 -- Build our device name (full path), should end up with something like:
446 snprintf(rawDeviceName
, sizeof(rawDeviceName
), "/dev/r%s", argv
[2]);
447 snprintf(blockDeviceName
, sizeof(blockDeviceName
), "/dev/%s", argv
[2]);
449 /* call the appropriate routine to handle the given action argument after becoming root */
451 switch( * actionPtr
) {
453 result
= DoProbe(rawDeviceName
, blockDeviceName
);
457 case FSUC_MOUNT_FORCE
:
458 result
= DoMount(blockDeviceName
, rawDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
462 result
= DoUnmount( mountPointPtr
);
465 result
= DoGetUUIDKey( blockDeviceName
, rawDeviceName
);
469 result
= DoChangeUUIDKey( blockDeviceName
);
472 result
= DoAdopt( blockDeviceName
, rawDeviceName
);
476 result
= DoDisown( blockDeviceName
, rawDeviceName
);
481 result
= DoMakeJournaled( argv
[3], gJournalSize
);
483 result
= DoMakeJournaled( argv
[2], gJournalSize
);
488 result
= DoUnJournal( argv
[2] );
492 result
= RawDisableJournaling( argv
[2] );
495 case FSUC_JNLINFS_RAW
:
496 // argv[2] has the device for the external journal. however
497 // we don't need it so we ignore it and just pass argv[3]
498 // which is the hfs volume whose state we're going to change
500 result
= SetJournalInFSState( argv
[3], 1 );
503 case FSUC_EXTJNL_RAW
:
504 // see the comment for FSUC_JNLINFS_RAW
505 result
= SetJournalInFSState( argv
[3], 0 );
509 result
= DoGetJournalInfo( argv
[2] );
513 /* should never get here since ParseArgs should handle this situation */
514 DoDisplayUsage( argv
);
523 return result
; /*...and make main fit the ANSI spec. */
527 /* ***************************** DoMount ********************************
529 This routine will fire off a system command to mount the given device at the given mountpoint.
530 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
532 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
533 mountPointPtr - pointer to the mount point.
536 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
537 *********************************************************************** */
539 DoMount(char *deviceNamePtr
, const char *rawName
, const char *mountPointPtr
,
540 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
546 char *permissionsOption
;
547 int result
= FSUR_IO_FAIL
;
549 char encodeopt
[16] = "";
550 CFStringEncoding encoding
;
551 volUUID_t targetVolUUID
;
552 VolumeStatusDBHandle vsdbhandle
= NULL
;
553 unsigned long targetVolumeStatus
;
555 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
556 return (FSUR_IO_FAIL
);
558 /* get the volume UUID to check if permissions should be used: */
559 targetVolumeStatus
= 0;
560 if (((result
= GetVolumeUUID(deviceNamePtr
, rawName
, &targetVolUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
561 (uuid_is_null(targetVolUUID
.uuid
))) {
563 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
566 if (gIsEjectable
== 0) {
567 result
= DoAdopt( deviceNamePtr
, rawName
);
569 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
571 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
574 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
576 targetVolumeStatus
= 0;
580 /* We've got a real volume UUID! */
581 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
582 /* Can't even get access to the volume info db; assume permissions are OK. */
584 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
586 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
589 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
591 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolUUID
, &targetVolumeStatus
)) != 0) {
593 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
596 if (gIsEjectable
== 0) {
597 result
= DoAdopt( deviceNamePtr
, rawName
);
599 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
601 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
604 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
606 targetVolumeStatus
= 0;
609 targetVolumeStatus
= 0;
612 (void)CloseVolumeStatusDB(vsdbhandle
);
619 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
620 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
621 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
624 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
626 /* get default encoding value (for hfs volumes) */
627 #if READ_DEFAULT_ENCODING
628 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
630 encoding
= CFStringGetSystemEncoding();
632 snprintf(encodeopt
, sizeof(encodeopt
), "-e=%d", (int)encoding
);
634 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
635 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
637 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
638 "-o", encodeopt
, "-o", permissionsOption
,
639 "-o", "-u=unknown,-g=unknown,-m=0777",
640 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
643 /* IF WE ARE HERE, WE WERE UNSUCCESFUL */
644 return (FSUR_IO_FAIL
);
648 return (FSUR_IO_FAIL
);
651 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
652 result
= status
.w_retcode
;
656 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
660 /* ****************************************** DoUnmount *********************************************
662 This routine will fire off a system command to unmount the given device.
664 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
666 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
667 *************************************************************************************************** */
669 DoUnmount(const char * theMountPointPtr
)
675 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
680 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
682 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
684 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
685 return (FSUR_IO_FAIL
);
689 return (FSUR_IO_FAIL
);
692 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
693 result
= status
.w_retcode
;
697 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
705 Get the volume name of the volume mounted at "path". Print that volume
706 name to standard out.
708 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
710 struct VolumeNameBuf
{
711 u_int32_t info_length
;
712 attrreference_t name_ref
;
717 PrintVolumeNameAttr(const char *path
)
719 struct attrlist alist
;
720 struct VolumeNameBuf volNameInfo
;
723 /* Set up the attrlist structure to get the volume's Finder Info */
724 memset (&alist
, 0, sizeof(alist
));
725 alist
.bitmapcount
= 5;
727 alist
.commonattr
= 0;
728 alist
.volattr
= ATTR_VOL_INFO
| ATTR_VOL_NAME
;
734 /* Get the Finder Info */
735 result
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0);
737 result
= FSUR_IO_FAIL
;
741 /* Print the name to standard out */
742 printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
);
743 result
= FSUR_RECOGNIZED
;
750 /* ******************************************* DoProbe **********************************************
752 This routine will open the given device and check to make sure there is media that looks
753 like an HFS. If it is HFS, then print the volume name to standard output.
755 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
756 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
758 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
759 *************************************************************************************************** */
761 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
)
763 int result
= FSUR_UNRECOGNIZED
;
766 HFSMasterDirectoryBlock
* mdbPtr
;
767 HFSPlusVolumeHeader
* volHdrPtr
;
768 u_char volnameUTF8
[kHFSPlusMaxFileNameBytes
];
771 * Determine if there is a volume already mounted from this device. If
772 * there is, and it is HFS, then we need to get the volume name via
775 * NOTE: We're using bufPtr to hold a pointer to a path.
778 result
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
);
779 if (result
!= FSUR_IO_SUCCESS
) {
782 if (bufPtr
!= NULL
) {
783 /* There is an HFS volume mounted from the device. */
784 result
= PrintVolumeNameAttr(bufPtr
);
789 * If we get here, there is no volume mounted from this device, so
790 * go probe the raw device directly.
793 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
795 result
= FSUR_UNRECOGNIZED
;
799 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
800 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
802 fd
= open( rawDeviceNamePtr
, O_RDONLY
, 0 );
804 result
= FSUR_IO_FAIL
;
809 * Read the HFS Master Directory Block from sector 2
811 result
= (int)readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
812 if (FSUR_IO_FAIL
== result
)
815 /* get classic HFS volume name (from MDB) */
816 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
817 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
820 CFStringEncoding encoding
;
822 /* Some poorly mastered HFS CDs have an empty MDB name field! */
823 if (mdbPtr
->drVN
[0] == '\0') {
824 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
825 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
828 /* Check for an encoding hint in the Finder Info (field 4). */
829 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
830 if (encoding
== kCFStringEncodingInvalidId
) {
831 /* Next try the encoding bias in the kernel. */
832 encoding
= GetEncodingBias();
833 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
834 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
837 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
838 mdbPtr
->drVN
, encoding
);
843 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
846 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
848 /* default to MacRoman on conversion errors */
849 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
850 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
851 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
853 encoding
= kCFStringEncodingMacRoman
;
856 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
857 if (encoding
!= kCFStringEncodingMacRoman
) {
858 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
859 encoding
= kCFStringEncodingMacRoman
;
860 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
861 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
866 /* get HFS Plus volume name (from Catalog) */
867 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
868 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
869 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
870 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
873 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
874 /* embedded volume, first find offset */
875 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
876 if ( result
!= FSUR_IO_SUCCESS
)
882 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
885 result
= FSUR_UNRECOGNIZED
;
888 if (FSUR_IO_SUCCESS
== result
) {
889 /* Print the volume name to standard output */
890 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
891 result
= FSUR_RECOGNIZED
;
907 * Create a version 3 UUID from a unique "name" in the given "name space".
908 * Version 3 UUID are derived using "name" via MD5 checksum.
911 * result_uuid - resulting UUID.
912 * namespace - namespace in which given name exists and UUID should be created.
913 * name - unique string used to create version 3 UUID.
914 * namelen - length of the name string.
917 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
922 MD5_Update(&c
, namespace, sizeof(uuid_t
));
923 MD5_Update(&c
, name
, namelen
);
924 MD5_Final(result_uuid
, &c
);
926 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
927 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
931 /* **************************************** DoGetUUIDKey *******************************************
933 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
935 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
937 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
938 *************************************************************************************************** */
940 DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
) {
942 volUUID_t targetVolumeUUID
;
945 result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
);
946 if (result
== FSUR_IO_SUCCESS
) {
947 uuid_unparse (targetVolumeUUID
.uuid
, uustr
);
948 /* for compatibility, must write out the string to stdout, with NO newline */
949 write(STDOUT_FILENO
, uustr
, strlen(uustr
));
952 // fprintf(stderr, "device %s UUID : %s\n", rawName, uustr);
960 /* *************************************** DoChangeUUIDKey ******************************************
962 This routine will change the UUID on the specified block device.
964 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
966 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
967 *************************************************************************************************** */
969 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
971 hfs_UUID_t newVolumeUUID
;
973 GenerateHFSVolumeUUID(&newVolumeUUID
);
975 // for testing purposes, may want to set a NULL UUID from command line.
976 memset (&newVolumeUUID
, 0, sizeof(newVolumeUUID
));
978 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
980 //fprintf(stderr, "device %s has new UUID \n", theDeviceNamePtr);
987 /* **************************************** DoAdopt *******************************************
989 This routine will add the UUID of the specified block device to the list of local volumes.
991 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
993 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
994 *************************************************************************************************** */
996 DoAdopt( const char * theDeviceNamePtr
, const char *rawName
) {
997 int result
, closeresult
;
998 volUUID_t targetVolumeUUID
;
999 VolumeStatusDBHandle vsdbhandle
= NULL
;
1000 unsigned long targetVolumeStatus
;
1002 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1004 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1005 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1006 targetVolumeStatus
= 0;
1008 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
1009 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1011 result
= FSUR_IO_SUCCESS
;
1015 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1017 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1020 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
1024 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
1031 /* **************************************** DoDisown *******************************************
1033 This routine will change the status of the specified block device to ignore its permissions.
1035 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
1037 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
1038 *************************************************************************************************** */
1040 DoDisown( const char * theDeviceNamePtr
, const char *rawName
) {
1041 int result
, closeresult
;
1042 volUUID_t targetVolumeUUID
;
1043 VolumeStatusDBHandle vsdbhandle
= NULL
;
1044 unsigned long targetVolumeStatus
;
1046 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1048 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1049 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1050 targetVolumeStatus
= 0;
1052 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
1053 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1055 result
= FSUR_IO_SUCCESS
;
1059 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1061 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1064 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
1066 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
1068 result
= FSUR_IO_FAIL
;
1073 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
1080 get_multiplier(char c
)
1082 if (tolower(c
) == 'k') {
1084 } else if (tolower(c
) == 'm') {
1086 } else if (tolower(c
) == 'g') {
1087 return 1024 * 1024 * 1024;
1093 /* **************************************** ParseArgs ********************************************
1095 This routine will make sure the arguments passed in to us are cool.
1096 Here is how this utility is used:
1098 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1100 -p (Probe for mounting)
1101 -P (Probe for initializing - not supported)
1103 -r (Repair - not supported)
1106 -i (Initialize - not supported)
1109 disk0s2 (for example)
1112 /foo/bar/ (required for Mount and Force Mount actions)
1115 (these are ignored for CDROMs)
1116 either "readonly" OR "writable"
1117 either "removable" OR "fixed"
1118 either "nosuid" or "suid"
1119 either "nodev" or "dev"
1122 hfs.util -p disk0s2 removable writable
1123 hfs.util -p disk0s2 removable readonly
1124 hfs.util -m disk0s2 /my/hfs
1127 argc - the number of arguments in argv.
1128 argv - array of arguments.
1130 returns FSUR_INVAL if we find a bad argument else 0.
1131 *************************************************************************************************** */
1133 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1134 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1135 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1137 size_t deviceLength
;
1138 int result
= FSUR_INVAL
;
1139 int doLengthCheck
= 1;
1143 /* Must have at least 3 arguments and the action argument must start with a '-' */
1144 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1145 DoDisplayUsage( argv
);
1149 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1151 * actionPtr
= & argv
[1][1];
1153 switch ( argv
[1][1] ) {
1155 /* action Probe and requires 5 arguments (need the flags) */
1157 DoDisplayUsage( argv
);
1165 /* Note: the device argument in argv[2] is checked further down but ignored. */
1166 * mountPointPtr
= argv
[3];
1167 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1171 case FSUC_MOUNT_FORCE
:
1172 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1174 DoDisplayUsage( argv
);
1177 * mountPointPtr
= argv
[3];
1203 if (isdigit(argv
[2][0])) {
1205 unsigned long size
= strtoul(argv
[2], &ptr
, 0);
1207 assert(size
< INT_MAX
);
1208 gJournalSize
= (int)size
;
1210 gJournalSize
*= get_multiplier(*ptr
);
1221 case FSUC_UNJNL_RAW
:
1226 case FSUC_JNLINFS_RAW
:
1231 case FSUC_EXTJNL_RAW
:
1243 DoDisplayUsage( argv
);
1248 /* Make sure device (argv[2]) is something reasonable */
1249 deviceLength
= strlen( argv
[2] );
1250 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1251 DoDisplayUsage( argv
);
1256 /* Flags: removable/fixed. */
1257 if ( 0 == strcmp(argv
[index
],"removable") ) {
1258 * isEjectablePtr
= 1;
1259 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1260 * isEjectablePtr
= 0;
1262 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1265 /* Flags: readonly/writable. */
1266 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1268 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1271 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1275 /* Flags: suid/nosuid. */
1276 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1278 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1281 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1284 /* Flags: dev/nodev. */
1285 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1287 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1290 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1305 /* *************************************** DoDisplayUsage ********************************************
1307 This routine will do a printf of the correct usage for this utility.
1309 argv - array of arguments.
1312 *************************************************************************************************** */
1314 DoDisplayUsage(const char *argv
[])
1316 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1317 printf("action_arg:\n");
1318 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1319 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1320 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1321 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1322 #ifdef HFS_UUID_SUPPORT
1323 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1324 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1325 #endif //HFS_UUID_SUPPORT
1326 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1327 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1328 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1329 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1330 printf(" -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW
);
1331 printf(" -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW
);
1332 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1333 printf("device_arg:\n");
1334 printf(" device we are acting upon (for example, 'disk0s2')\n");
1335 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1336 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1337 printf("mount_point_arg:\n");
1338 printf(" required for Mount and Force Mount \n");
1340 printf(" required for Mount, Force Mount and Probe\n");
1341 printf(" indicates removable or fixed (for example 'fixed')\n");
1342 printf(" indicates readonly or writable (for example 'readonly')\n");
1343 printf(" indicates suid or nosuid (for example 'suid')\n");
1344 printf(" indicates dev or nodev (for example 'dev')\n");
1345 printf("Examples:\n");
1346 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1347 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1351 } /* DoDisplayUsage */
1357 Given a path to a device, determine if a volume is mounted on that
1358 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1359 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1360 no volume mounted on the device, set *pathPtr to NULL and return
1363 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1366 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1372 /* Assume no mounted volume found */
1374 result
= FSUR_IO_SUCCESS
;
1376 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1378 return FSUR_IO_FAIL
;
1380 for (i
=0; i
<numMounts
; ++i
) {
1381 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1382 /* Found a mounted volume; check the type */
1383 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1384 *pathPtr
= buf
[i
].f_mntonname
;
1385 /* result = FSUR_IO_SUCCESS, above */
1387 result
= FSUR_UNRECOGNIZED
;
1400 Read the Master Directory Block or Volume Header Block from an HFS,
1401 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1402 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1403 Return a pointer to the volume UUID in the Finder Info.
1405 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1408 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, hfs_UUID_t
**finderInfoUUIDPtr
)
1411 HFSMasterDirectoryBlock
* mdbPtr
;
1412 HFSPlusVolumeHeader
* volHdrPtr
;
1418 * Read the HFS Master Directory Block or Volume Header from sector 2
1421 result
= (int)readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1422 if (result
!= FSUR_IO_SUCCESS
)
1426 * If this is a wrapped HFS Plus volume, read the Volume Header from
1427 * sector 2 of the embedded volume.
1429 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1430 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1431 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1432 if (result
!= FSUR_IO_SUCCESS
)
1434 result
= (int)readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1435 if (result
!= FSUR_IO_SUCCESS
)
1440 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1441 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1442 * UUID from the Finder Info.
1444 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1445 *finderInfoUUIDPtr
= (hfs_UUID_t
*)(&mdbPtr
->drFndrInfo
[6]);
1446 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1447 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1448 *finderInfoUUIDPtr
= (hfs_UUID_t
*)&volHdrPtr
->finderInfo
[24];
1450 result
= FSUR_UNRECOGNIZED
;
1461 Read the UUID from an unmounted volume, by doing direct access to the device.
1462 Assumes the caller has already determined that a volume is not mounted
1463 on the device. Once we have the HFS UUID from the finderinfo, convert it to a
1464 full UUID and then write it into the output argument provided (volUUIDPtr)
1466 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1469 GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*volUUIDPtr
)
1474 hfs_UUID_t
*finderInfoUUIDPtr
;
1475 hfs_UUID_t hfs_uuid
;
1480 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1482 result
= FSUR_UNRECOGNIZED
;
1486 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1490 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", deviceNamePtr
, errno
);
1492 if (error
== EBUSY
) {
1493 /* If it was busy, then retry, this time using the raw device */
1494 fd
= open (rawName
, O_RDONLY
, 0);
1497 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName
, errno
);
1499 result
= FSUR_IO_FAIL
;
1504 result
= FSUR_IO_FAIL
;
1510 * Get the pointer to the volume UUID in the Finder Info*/
1511 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1512 if (result
!= FSUR_IO_SUCCESS
)
1516 * Copy the volume UUID out of the Finder Info. Note that the FinderInfo
1517 * stores the UUID in big-endian so we have to convert to native
1520 hfs_uuid
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->high
);
1521 hfs_uuid
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->low
);
1524 * Now convert to a full UUID using the same algorithm as HFS+
1525 * This makes sure to construct a full NULL-UUID if necessary.
1527 ConvertHFSUUIDToUUID (&hfs_uuid
, &fullUUID
);
1529 /* Copy it out into the caller's buffer */
1530 uuid_copy(volUUIDPtr
->uuid
, fullUUID
.uuid
);
1533 if (fd
> 0) close(fd
);
1534 if (bufPtr
) free(bufPtr
);
1537 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1539 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1544 void ConvertHFSUUIDStringToUUID(const char *UUIDString
, volUUID_t
*volumeID
) {
1547 u_int32_t nextdigit
;
1553 for (i
= 0; (i
< HFSUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
1554 if ((c
>= '0') && (c
<= '9')) {
1555 nextdigit
= c
- '0';
1556 } else if ((c
>= 'A') && (c
<= 'F')) {
1557 nextdigit
= c
- 'A' + 10;
1558 } else if ((c
>= 'a') && (c
<= 'f')) {
1559 nextdigit
= c
- 'a' + 10;
1563 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
1564 high
= (high
<< 4) | carry
;
1565 low
= (low
<< 4) | nextdigit
;
1571 /* now convert to a full UUID */
1572 ConvertHFSUUIDToUUID(&hfsuu
, volumeID
);
1580 * Convert an HFS+ UUID in binary form to a full UUID
1582 * Assumes that the HFS UUID argument is stored in native endianness
1583 * If the input UUID is zeroes, then it will emit a NULL'd out UUID.
1585 void ConvertHFSUUIDToUUID (hfs_UUID_t
*hfsuuid
, volUUID_t
*uu
)
1589 /* if either high or low is 0, then return the NULL uuid */
1590 if ((hfsuuid
->high
== 0) || (hfsuuid
->low
== 0)) {
1591 uuid_clear (uu
->uuid
);
1595 * If the input UUID was not zeroes, then run it through the normal md5
1597 * NOTE: When using MD5 to compute the "full" UUID, we must pass in the
1598 * big-endian values of the two 32-bit fields. In the kernel, HFS uses the
1599 * raw 4-byte fields of the finderinfo directly, without running them through
1600 * an endian-swap. As a result, we must endian-swap back to big endian here.
1602 ((uint32_t*)rawUUID
)[0] = OSSwapHostToBigInt32(hfsuuid
->high
);
1603 ((uint32_t*)rawUUID
)[1] = OSSwapHostToBigInt32(hfsuuid
->low
);
1604 uuid_create_md5_from_name(uu
->uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
1610 Write a previously generated UUID to an unmounted volume, by doing direct
1611 access to the device. Assumes the caller has already determined that a
1612 volume is not mounted on the device.
1614 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1617 SetVolumeUUIDRaw(const char *deviceNamePtr
, hfs_UUID_t
*volumeUUIDPtr
)
1622 hfs_UUID_t
*finderInfoUUIDPtr
;
1625 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1627 result
= FSUR_UNRECOGNIZED
;
1631 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1634 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1636 result
= FSUR_IO_FAIL
;
1641 * Get the pointer to the volume UUID in the Finder Info
1643 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1644 if (result
!= FSUR_IO_SUCCESS
)
1648 * Update the UUID in the Finder Info. Make sure to write out big endian.
1650 finderInfoUUIDPtr
->high
= OSSwapHostToBigInt32(volumeUUIDPtr
->high
);
1651 finderInfoUUIDPtr
->low
= OSSwapHostToBigInt32(volumeUUIDPtr
->low
);
1654 * Write the modified MDB or VHB back to disk
1656 result
= (int)writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1659 if (fd
> 0) close(fd
);
1660 if (bufPtr
) free(bufPtr
);
1663 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1665 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1672 Read the UUID from a mounted volume, by calling getattrlist().
1673 Assumes the path is the mount point of an HFS volume. Note that this will
1674 return the full-length UUID to the caller, as emitted by the underlying
1675 filesystem. On HFS+ this means that we use the hfs_vfsops.c implementation
1676 to construct the UUID
1678 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1681 GetVolumeUUIDAttr(const char *path
, volUUID_t
*volUUIDPtr
)
1683 struct attrlist alist
;
1684 UUIDAttrBuf_t uuidattr
;
1685 FinderAttrBuf_t finderinfo
;
1689 * This is a little bit dodgy. In order to detect whether or not the
1690 * volume has a valid UUID, we need to call getattrlist() and examine
1691 * the FinderInfo, which is what this function has historically done, even if
1692 * we ultimately want the full UUID, which is what is returned if one requests
1695 * The reason is that if the UUID does not exist, it will be stored
1696 * as 8 bytes of zeroes in the UUID portion of the finder info. However, if
1697 * you request ATTR_VOL_UUID, it will run the 8 bytes of zeroes through
1698 * the MD5 function, where they will be manipulated into a full UUID. It
1699 * doesn't look like that guarantees the resulting UUID will also be a
1700 * NULL-uuid (i.e. all zeroes).
1702 * All of this to say we need to check the finder info first, then check
1703 * ATTR_VOL_UUID as needed afterwards.
1706 /* First set up for a call to getattrlist for the finderinfo */
1707 memset (&alist
, 0, sizeof(alist
));
1708 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1710 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1711 alist
.volattr
= ATTR_VOL_INFO
;
1716 /* Get the finderinfo */
1717 result
= getattrlist(path
, &alist
, &finderinfo
, sizeof(finderinfo
), 0);
1719 return FSUR_IO_FAIL
;
1722 /* Now we need to check if the finderinfo UUID is NULL */
1723 hfs_UUID_t
* hfs_finderinfo
= (hfs_UUID_t
*)(&finderinfo
.finderinfo
[6]);
1726 * We should really endian-swap these, but if a uint32_t is 0,
1727 * the endianness doesn't matter
1729 if ((hfs_finderinfo
->high
== 0) || (hfs_finderinfo
->low
== 0)) {
1730 /* Then it is an uninitialized/NULL UUID. Zap the caller buffer and bail out */
1731 uuid_clear (volUUIDPtr
->uuid
);
1732 return FSUR_IO_SUCCESS
;
1735 /* OK, now set up the attrlist structure to get the volume's UUID */
1736 memset (&alist
, 0, sizeof(alist
));
1737 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1739 alist
.commonattr
= 0;
1740 alist
.volattr
= (ATTR_VOL_INFO
| ATTR_VOL_UUID
);
1745 /* Get the full UUID from the kernel */
1746 result
= getattrlist(path
, &alist
, &uuidattr
, sizeof(uuidattr
), 0);
1748 return FSUR_IO_FAIL
;
1751 /* Copy the UUID from the buf to caller's buffer */
1752 uuid_copy (volUUIDPtr
->uuid
, uuidattr
.uu
);
1753 result
= FSUR_IO_SUCCESS
;
1762 Write a UUID to a mounted volume, by calling setattrlist().
1763 Assumes the path is the mount point of an HFS volume.
1765 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1768 SetVolumeUUIDAttr(const char *path
, hfs_UUID_t
*volumeUUIDPtr
)
1770 struct attrlist alist
;
1771 struct FinderAttrBuf volFinderInfo
;
1772 hfs_UUID_t
*finderInfoUUIDPtr
;
1775 /* Set up the attrlist structure to get the volume's Finder Info */
1776 memset (&alist
, 0, sizeof(alist
));
1777 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1779 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1780 alist
.volattr
= ATTR_VOL_INFO
;
1785 /* Get the Finder Info */
1786 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1788 result
= FSUR_IO_FAIL
;
1792 /* Update the UUID in the Finder Info. Make sure to swap back to big endian */
1793 finderInfoUUIDPtr
= (hfs_UUID_t
*)(&volFinderInfo
.finderinfo
[6]);
1794 finderInfoUUIDPtr
->high
= OSSwapHostToBigInt32(volumeUUIDPtr
->high
);
1795 finderInfoUUIDPtr
->low
= OSSwapHostToBigInt32(volumeUUIDPtr
->low
);
1797 /* Write the Finder Info back to the volume */
1798 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1800 result
= FSUR_IO_FAIL
;
1804 result
= FSUR_IO_SUCCESS
;
1814 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1815 we were asked to generate one, then generate a new UUID and write it to the
1818 Determine whether an HFS volume is mounted on the given device. If so, we
1819 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1820 the filesystem. If there is no mounted volume, then do direct device access
1821 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1823 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1827 GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*voluu
, boolean_t generate
)
1833 * Determine whether a volume is mounted on this device. If it is HFS, then
1834 * get the mount point's path. If it is non-HFS, then we can exit immediately
1835 * with FSUR_UNRECOGNIZED.
1837 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1838 if (result
!= FSUR_IO_SUCCESS
) {
1843 * Get any existing UUID.
1846 result
= GetVolumeUUIDAttr(path
, voluu
);
1849 result
= GetVolumeUUIDRaw(deviceNamePtr
, rawName
, voluu
);
1852 if (result
!= FSUR_IO_SUCCESS
) {
1857 * If there was no valid UUID, and we were asked to generate one, then
1858 * generate it and write it back to disk.
1860 if (generate
&& (uuid_is_null(voluu
->uuid
))) {
1863 GenerateHFSVolumeUUID(&hfsuu
);
1865 result
= SetVolumeUUIDAttr(path
, &hfsuu
);
1868 result
= SetVolumeUUIDRaw(deviceNamePtr
, &hfsuu
);
1879 Write a UUID to an HFS, HFS Plus or HFSX volume.
1881 Determine whether an HFS volume is mounted on the given device. If so, we
1882 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1883 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1885 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1888 SetVolumeUUID(const char *deviceNamePtr
, hfs_UUID_t
*volumeUUIDPtr
) {
1893 * Determine whether a volume is mounted on this device. If it is HFS, then
1894 * get the mount point's path. If it is non-HFS, then we can exit immediately
1895 * with FSUR_UNRECOGNIZED.
1897 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1898 if (result
!= FSUR_IO_SUCCESS
)
1905 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1907 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1916 -- GetEmbeddedHFSPlusVol
1918 -- In: hfsMasterDirectoryBlockPtr
1919 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1920 (that is, 2 blocks before the volume header)
1925 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1927 int result
= FSUR_IO_SUCCESS
;
1928 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1930 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1931 result
= FSUR_UNRECOGNIZED
;
1935 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1936 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1938 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1939 result
= FSUR_UNRECOGNIZED
;
1943 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1944 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1946 if ( startOffsetPtr
)
1947 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1948 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1958 -- GetNameFromHFSPlusVolumeStartingAt
1960 -- Caller's responsibility to allocate and release memory for the converted string.
1962 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1966 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1968 int result
= FSUR_IO_SUCCESS
;
1969 u_int32_t blockSize
;
1970 char * bufPtr
= NULL
;
1971 HFSPlusVolumeHeader
* volHdrPtr
;
1972 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1973 u_int32_t catalogNodeSize
;
1975 u_int32_t catalogExtCount
;
1976 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1978 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1979 if ( ! volHdrPtr
) {
1980 result
= FSUR_IO_FAIL
;
1985 * Read the Volume Header
1986 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1988 result
= (int)readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1989 if (result
== FSUR_IO_FAIL
) {
1991 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1993 goto Return
; // return FSUR_IO_FAIL
1996 /* Verify that it is an HFS+ volume. */
1998 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1999 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
2000 result
= FSUR_IO_FAIL
;
2002 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
2007 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2008 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
2009 if ( ! catalogExtents
) {
2010 result
= FSUR_IO_FAIL
;
2013 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
2014 catalogExtCount
= kHFSPlusExtentDensity
;
2016 /* if there are overflow catalog extents, then go get them */
2017 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
2018 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
2019 if (result
!= FSUR_IO_SUCCESS
)
2023 /* Read the header node of the catalog B-Tree */
2025 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2026 catalogExtCount
, catalogExtents
,
2027 &catalogNodeSize
, &leafNode
);
2028 if (result
!= FSUR_IO_SUCCESS
)
2031 /* Read the first leaf node of the catalog b-tree */
2033 bufPtr
= (char *)malloc(catalogNodeSize
);
2035 result
= FSUR_IO_FAIL
;
2039 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2041 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
2042 hfsPlusVolumeOffset
, blockSize
,
2043 catalogExtCount
, catalogExtents
);
2044 if (result
== FSUR_IO_FAIL
) {
2046 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2048 goto Return
; // return FSUR_IO_FAIL
2054 HFSPlusCatalogKey
* k
;
2057 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
2058 result
= FSUR_IO_FAIL
;
2060 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
2065 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
2067 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
2070 // Get a pointer to the first record.
2072 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
2073 k
= (HFSPlusCatalogKey
*)p
;
2075 // There should be only one record whose parent is the root parent. It should be the first record.
2077 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
2078 result
= FSUR_IO_FAIL
;
2080 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
2085 if ((OSSwapBigToHostInt16(k
->nodeName
.length
) >
2086 (sizeof(k
->nodeName
.unicode
) / sizeof(k
->nodeName
.unicode
[0]))) ||
2087 OSSwapBigToHostInt16(k
->nodeName
.length
) > 255) {
2088 result
= FSUR_IO_FAIL
;
2090 fprintf(stderr
, "hfs.util: ERROR: k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k
->nodeName
.length
));
2095 /* Extract the name of the root directory */
2098 HFSUniStr255
*swapped
;
2101 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
2102 if (swapped
== NULL
) {
2103 result
= FSUR_IO_FAIL
;
2106 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
2108 for (i
=0; i
<swapped
->length
; i
++) {
2109 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
2111 swapped
->unicode
[i
] = 0;
2112 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
2113 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
* 3 + 1, kCFStringEncodingUTF8
);
2119 result
= FSUR_IO_SUCCESS
;
2123 free((char*) volHdrPtr
);
2126 free((char*) catalogExtents
);
2129 free((char*)bufPtr
);
2133 } /* GetNameFromHFSPlusVolumeStartingAt */
2137 BTNodeDescriptor node
;
2139 } __attribute__((aligned(2), packed
)) HeaderRec
, *HeaderPtr
;
2144 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2148 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
2149 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2150 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
2153 HeaderRec
* bTreeHeaderPtr
= NULL
;
2155 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
2156 if (bTreeHeaderPtr
== NULL
)
2157 return (FSUR_IO_FAIL
);
2159 /* Read the b-tree header node */
2161 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
2162 hfsPlusVolumeOffset
, blockSize
,
2163 extentCount
, extentList
);
2164 if ( result
== FSUR_IO_FAIL
) {
2166 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
2171 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
2172 result
= FSUR_IO_FAIL
;
2174 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
2179 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
2181 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
2184 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
2187 free((char*) bTreeHeaderPtr
);
2191 } /* GetBTreeNodeInfo */
2197 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2201 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
2202 HFSPlusVolumeHeader
*volHdrPtr
,
2203 HFSPlusExtentDescriptor
**catalogExtents
,
2204 u_int32_t
*catalogExtCount
)
2207 u_int32_t numRecords
;
2210 u_int32_t blockSize
;
2211 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
2212 HFSPlusExtentDescriptor
* extents
;
2214 char * bufPtr
= NULL
;
2218 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2219 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
2220 extents
= *catalogExtents
;
2221 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
2224 /* Read the header node of the extents B-Tree */
2226 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2227 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
2228 &nodeSize
, &leafNode
);
2229 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
2232 /* Calculate the logical position of the first leaf node */
2234 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2236 /* Read the first leaf node of the extents b-tree */
2238 bufPtr
= (char *)malloc(nodeSize
);
2240 result
= FSUR_IO_FAIL
;
2244 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2247 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
2248 hfsPlusVolumeOffset
, blockSize
,
2249 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
2250 if ( result
== FSUR_IO_FAIL
) {
2252 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2257 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
2258 result
= FSUR_IO_FAIL
;
2262 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
2263 for (i
= 1; i
<= numRecords
; ++i
) {
2266 HFSPlusExtentKey
* k
;
2269 * Get the offset (in bytes) of the record from the
2270 * list of offsets at the end of the node
2272 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
2275 /* Get a pointer to the record */
2277 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
2278 k
= (HFSPlusExtentKey
*)p
;
2280 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
2283 /* grow list and copy additional extents */
2284 listsize
+= sizeof(HFSPlusExtentRecord
);
2285 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2286 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2287 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2289 *catalogExtCount
+= kHFSPlusExtentDensity
;
2290 *catalogExtents
= extents
;
2293 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2295 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2310 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2311 * position and number of contiguous bytes at that position.
2314 * logicalOffset Logical offset in bytes from start of file
2315 * length Maximum number of bytes to map
2316 * blockSize Number of bytes per allocation block
2317 * extentCount Number of extents in file
2318 * extentList The file's extents
2321 * physicalOffset Physical offset in bytes from start of volume
2322 * availableBytes Number of bytes physically contiguous (up to length)
2324 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2326 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2327 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2328 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2331 u_int32_t logicalBlock
;
2333 u_int32_t blockCount
= 0;
2335 /* Determine allocation block containing logicalOffset */
2336 logicalBlock
= (u_int32_t
)(offset
/ blockSize
); /* This can't overflow for valid volumes */
2337 offset
%= blockSize
; /* Offset from start of allocation block */
2339 /* Find the extent containing logicalBlock */
2340 for (extent
= 0; extent
< extentCount
; ++extent
)
2342 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2344 if (blockCount
== 0)
2345 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2347 if (logicalBlock
< blockCount
)
2348 break; /* Found it! */
2350 logicalBlock
-= blockCount
;
2353 if (extent
>= extentCount
)
2354 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2357 * When we get here, extentList[extent] is the extent containing logicalOffset.
2358 * The desired allocation block is logicalBlock blocks into the extent.
2361 /* Compute the physical starting position */
2362 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2363 temp
*= blockSize
; /* Byte offset of first physical block */
2364 *physicalOffset
= temp
+ offset
;
2366 /* Compute the available contiguous bytes. */
2367 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2369 temp
-= offset
; /* Number of bytes available */
2372 *availableBytes
= temp
;
2374 *availableBytes
= length
;
2376 return FSUR_IO_SUCCESS
;
2382 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2383 * ending position are not allocation or device block aligned.
2386 * fd Descriptor for reading the volume
2387 * buffer The bytes are read into here
2388 * offset Offset in file to start reading
2389 * length Number of bytes to read
2390 * volOffset Byte offset from start of device to start of volume
2391 * blockSize Number of bytes per allocation block
2392 * extentCount Number of extents in file
2393 * extentList The file's exents
2395 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2397 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2398 off_t volOffset
, u_int32_t blockSize
,
2399 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2401 int result
= FSUR_IO_SUCCESS
;
2407 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2408 &physOffset
, &physLength
);
2409 if (result
!= FSUR_IO_SUCCESS
)
2412 result
= (int)readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2413 if (result
!= FSUR_IO_SUCCESS
)
2416 length
-= physLength
;
2417 offset
+= physLength
;
2418 buffer
= (char *) buffer
+ physLength
;
2425 -- readAt = lseek() + read()
2427 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2432 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2437 void * rawData
= NULL
;
2440 ssize_t dataOffset
= 0;
2441 int result
= FSUR_IO_SUCCESS
;
2443 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2445 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2447 result
= FSUR_IO_FAIL
;
2450 /* put offset and length in terms of device blocksize */
2451 rawOffset
= offset
/ blocksize
* blocksize
;
2452 dataOffset
= offset
- rawOffset
;
2453 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2454 rawData
= malloc(rawLength
);
2455 if (rawData
== NULL
) {
2456 result
= FSUR_IO_FAIL
;
2460 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2461 if ( lseekResult
!= rawOffset
) {
2462 result
= FSUR_IO_FAIL
;
2466 readResult
= read(fd
, rawData
, rawLength
);
2467 if ( readResult
!= rawLength
) {
2469 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2471 result
= FSUR_IO_FAIL
;
2474 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2485 -- writeAt = lseek() + write()
2487 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2492 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2496 ssize_t bytestransferred
;
2497 void * rawData
= NULL
;
2500 ssize_t dataOffset
= 0;
2501 int result
= FSUR_IO_SUCCESS
;
2503 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2505 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2507 result
= FSUR_IO_FAIL
;
2510 /* put offset and length in terms of device blocksize */
2511 rawOffset
= offset
/ blocksize
* blocksize
;
2512 dataOffset
= offset
- rawOffset
;
2513 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2514 rawData
= malloc(rawLength
);
2515 if (rawData
== NULL
) {
2516 result
= FSUR_IO_FAIL
;
2520 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2521 if ( deviceoffset
!= rawOffset
) {
2522 result
= FSUR_IO_FAIL
;
2526 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2527 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2528 bytestransferred
= read(fd
, rawData
, rawLength
);
2529 if ( bytestransferred
!= rawLength
) {
2531 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2533 result
= FSUR_IO_FAIL
;
2538 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2540 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2541 if ( deviceoffset
!= rawOffset
) {
2542 result
= FSUR_IO_FAIL
;
2546 bytestransferred
= write(fd
, rawData
, rawLength
);
2547 if ( bytestransferred
!= rawLength
) {
2549 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2551 result
= FSUR_IO_FAIL
;
2556 if (rawData
) free(rawData
);
2564 * Get kernel's encoding bias.
2570 size_t buflen
= sizeof(int);
2574 if (getvfsbyname("hfs", &vfc
) < 0)
2578 mib
[1] = vfc
.vfc_typenum
;
2579 mib
[2] = HFS_ENCODINGBIAS
;
2581 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2588 /******************************************************************************
2590 * 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
2592 *****************************************************************************/
2594 #define DBHANDLESIGNATURE 0x75917737
2596 /* Flag values for operation options: */
2597 #define DBMARKPOSITION 1
2599 static char gVSDBPath
[] = "/var/db/volinfo.database";
2601 #define MAXIOMALLOC 16384
2603 /* Database layout: */
2605 typedef struct VSDBKey
{
2609 typedef struct VSDBKeyUUID
{
2610 uuid_string_t uuid_string
;
2614 char statusFlags
[8];
2617 /* A VSDB Entry using a uuid_str (36 byte) instead of HFS UUID string (8 byte) */
2618 typedef struct VSDBEntryUUID
{
2622 struct VSDBRecord record
;
2626 /* a VSDB entry using the HFS UUID */
2627 typedef struct VSDBEntryHFS
{
2631 struct VSDBRecord record
;
2635 #define DBKEYSEPARATOR ':'
2636 #define DBBLANKSPACE ' '
2637 #define DBRECORDTERMINATOR '\n'
2639 /* In-memory data structures: */
2642 unsigned long signature
;
2645 off_t recordPosition
;
2648 typedef struct VSDBState
*VSDBStatePtr
;
2652 /* Internal function prototypes: */
2653 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2654 static int UnlockDB(VSDBStatePtr dbstateptr
);
2656 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, volUUID_t
*volumeID
, VSDBEntryUUID_t
*dbentry
, unsigned long options
);
2657 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
);
2658 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
);
2659 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
);
2660 static int CompareVSDBKeys(VSDBKeyUUID_t
*key1
, VSDBKeyUUID_t
*key2
);
2663 static void FormatULong(unsigned long u
, char *s
);
2664 static void FormatDBKey(volUUID_t
*volumeID
, VSDBKeyUUID_t
*dbkey
);
2665 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2666 static void FormatDBEntry(volUUID_t
*volumeID
, unsigned long volumeStatusFlags
, VSDBEntryUUID_t
*dbentry
);
2667 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2671 /******************************************************************************
2673 * 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
2675 *****************************************************************************/
2677 void GenerateHFSVolumeUUID(hfs_UUID_t
*newuuid
) {
2679 char randomInputBuffer
[26];
2680 unsigned char digest
[20];
2685 char sysctlstring
[128];
2687 double sysloadavg
[3];
2688 struct vmtotal sysvmtotal
;
2691 memset (&hfsuuid
, 0, sizeof(hfsuuid
));
2694 /* Initialize the SHA-1 context for processing: */
2695 SHA1_Init(&context
);
2697 /* Now process successive bits of "random" input to seed the process: */
2699 /* The current system's uptime: */
2701 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2703 /* The kernel's boot time: */
2705 mib
[1] = KERN_BOOTTIME
;
2706 datalen
= sizeof(sysdata
);
2707 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2708 SHA1_Update(&context
, &sysdata
, datalen
);
2710 /* The system's host id: */
2712 mib
[1] = KERN_HOSTID
;
2713 datalen
= sizeof(sysdata
);
2714 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2715 SHA1_Update(&context
, &sysdata
, datalen
);
2717 /* The system's host name: */
2719 mib
[1] = KERN_HOSTNAME
;
2720 datalen
= sizeof(sysctlstring
);
2721 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2722 SHA1_Update(&context
, sysctlstring
, datalen
);
2724 /* The running kernel's OS release string: */
2726 mib
[1] = KERN_OSRELEASE
;
2727 datalen
= sizeof(sysctlstring
);
2728 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2729 SHA1_Update(&context
, sysctlstring
, datalen
);
2731 /* The running kernel's version string: */
2733 mib
[1] = KERN_VERSION
;
2734 datalen
= sizeof(sysctlstring
);
2735 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2736 SHA1_Update(&context
, sysctlstring
, datalen
);
2738 /* The system's load average: */
2739 datalen
= sizeof(sysloadavg
);
2740 getloadavg(sysloadavg
, 3);
2741 SHA1_Update(&context
, &sysloadavg
, datalen
);
2743 /* The system's VM statistics: */
2746 datalen
= sizeof(sysvmtotal
);
2747 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2748 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2750 /* The current GMT (26 ASCII characters): */
2752 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2753 SHA1_Update(&context
, randomInputBuffer
, 26);
2755 /* Pad the accumulated input and extract the final digest hash: */
2756 SHA1_Final(digest
, &context
);
2758 memcpy(&hfsuuid
, digest
, sizeof(hfsuuid
));
2759 } while ((hfsuuid
.high
== 0) || (hfsuuid
.low
== 0));
2761 /* now copy out the hfs uuid */
2762 memcpy (newuuid
, &hfsuuid
, sizeof (hfsuuid
));
2767 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2768 VSDBStatePtr dbstateptr
;
2770 *DBHandlePtr
= NULL
;
2772 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2773 if (dbstateptr
== NULL
) {
2777 dbstateptr
->dbmode
= O_RDWR
;
2778 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2779 if (dbstateptr
->dbfile
== -1) {
2781 The file couldn't be opened for read/write access:
2782 try read-only access before giving up altogether.
2784 dbstateptr
->dbmode
= O_RDONLY
;
2785 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2786 if (dbstateptr
->dbfile
== -1) {
2787 errno_t local_errno
= errno
;
2793 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2794 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2796 /* VSDBUtil converts the status DB, so we do it here, too */
2797 ConvertVolumeStatusDB(*DBHandlePtr
);
2802 /* Convert the volume status DB from 64-bit (HFS-style) entries into full UUIDs */
2803 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2804 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2805 struct VSDBEntryHFS entry64
;
2808 u_int32_t iobuffersize
;
2809 void *iobuffer
= NULL
;
2812 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2814 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2818 * This function locks the database file then tries to read in
2819 * the size of a old-style HFS UUID database entry. If what it finds
2820 * is a well-formatted HFS entry, then it will convert the entire database.
2821 * If it finds that the file isn't long enough or isn't actually the
2822 * format for the 64-bit UUID struct on-disk, then it will bail out and not
2823 * touch it. Otherwise it will read the whole file and convert it
2825 * In practice this means that if the file is empty or if we have already
2826 * converted it then do nothing.
2828 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2829 result
= (int)read(dbstateptr
->dbfile
, &entry64
, sizeof(entry64
));
2830 if ((result
!= sizeof(entry64
)) ||
2831 (entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
2832 (entry64
.space
!= DBBLANKSPACE
) ||
2833 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
2837 off_t buf_size
= dbinfo
.st_size
;
2839 /* Read in a giant buffer */
2840 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2841 if (buf_size
> UINT32_MAX
) {
2845 iobuffersize
= (u_int32_t
)buf_size
;
2846 iobuffer
= malloc(iobuffersize
);
2847 if (iobuffer
== NULL
) {
2852 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2853 result
= (int)read(dbstateptr
->dbfile
, iobuffer
, iobuffersize
);
2854 if (result
!= iobuffersize
) {
2858 if ((result
= ftruncate(dbstateptr
->dbfile
, 0)) != 0) {
2861 for (i
= 0; i
< iobuffersize
/ sizeof(entry64
); i
++) {
2863 u_int32_t VolumeStatus
;
2864 struct VSDBEntryUUID dbentry
;
2867 * now iterate through the contents of the 64-bit entries in RAM
2868 * and write them on top of the existing database file
2870 entry64
= *(((struct VSDBEntryHFS
*)iobuffer
) + i
);
2871 if ((entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
2872 (entry64
.space
!= DBBLANKSPACE
) ||
2873 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
2877 ConvertHFSUUIDToUUID(entry64
.key
.uuid
, &volumeID
);
2878 VolumeStatus
= (u_int32_t
)ConvertHexStringToULong(entry64
.record
.statusFlags
, sizeof(entry64
.record
.statusFlags
));
2880 FormatDBEntry(&volumeID
, VolumeStatus
, &dbentry
);
2881 if ((result
= AddVolumeRecord(dbstateptr
, &dbentry
)) != sizeof(dbentry
)) {
2882 warnx("couldn't convert volume status database: %s", strerror(result
));
2887 fsync(dbstateptr
->dbfile
);
2893 if (iobuffer
) free(iobuffer
);
2894 UnlockDB(dbstateptr
);
2901 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long *VolumeStatus
) {
2902 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2903 struct VSDBEntryUUID dbentry
;
2906 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2908 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2910 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2913 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2918 UnlockDB(dbstateptr
);
2924 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long VolumeStatus
) {
2925 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2926 struct VSDBEntryUUID dbentry
;
2929 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2930 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2932 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2934 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2935 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2937 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2939 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2940 } else if (result
== -1) {
2942 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2944 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2949 fsync(dbstateptr
->dbfile
);
2954 UnlockDB(dbstateptr
);
2960 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
) {
2961 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2964 unsigned long iobuffersize
;
2965 void *iobuffer
= NULL
;
2967 unsigned long iotransfersize
;
2968 unsigned long bytestransferred
;
2970 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2972 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2974 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2976 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2978 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2982 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2984 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2985 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntryUUID
)) <= MAXIOMALLOC
) {
2986 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntryUUID
);
2988 iobuffersize
= MAXIOMALLOC
;
2991 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2992 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2993 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2995 if (iobuffersize
> 0) {
2996 iobuffer
= malloc(iobuffersize
);
2997 if (iobuffer
== NULL
) {
3002 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntryUUID
);
3004 iotransfersize
= dbinfo
.st_size
- dataoffset
;
3005 if (iotransfersize
> 0) {
3006 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
3009 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
3011 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
3012 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
3013 if (bytestransferred
!= iotransfersize
) {
3019 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntryUUID
)));
3021 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntryUUID
), SEEK_SET
);
3022 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
3023 if (bytestransferred
!= iotransfersize
) {
3028 dataoffset
+= (off_t
)iotransfersize
;
3030 } while (iotransfersize
> 0);
3033 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntryUUID
))));
3035 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntryUUID
)))) != 0) {
3039 fsync(dbstateptr
->dbfile
);
3045 if (iobuffer
) free(iobuffer
);
3046 UnlockDB(dbstateptr
);
3054 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
3055 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
3057 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
3059 dbstateptr
->signature
= 0;
3061 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
3062 dbstateptr
->dbfile
= 0;
3071 /******************************************************************************
3073 * 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
3075 *****************************************************************************/
3077 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
3079 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
3081 return flock(dbstateptr
->dbfile
, lockmode
);
3086 static int UnlockDB(VSDBStatePtr dbstateptr
) {
3088 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
3090 return flock(dbstateptr
->dbfile
, LOCK_UN
);
3095 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, volUUID_t
*volumeID
,
3096 VSDBEntryUUID_t
*targetEntry
, unsigned long options
) {
3097 VSDBKeyUUID_t searchkey
;
3098 struct VSDBEntryUUID dbentry
;
3101 FormatDBKey(volumeID
, &searchkey
);
3102 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
3105 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
3106 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
3107 if (targetEntry
!= NULL
) {
3109 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
3111 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
3115 } while (result
== 0);
3122 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
) {
3123 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
3124 return (int)write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntryUUID
));
3128 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
) {
3129 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
3130 return (int)write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
3133 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
) {
3134 struct VSDBEntryUUID entry
;
3137 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
3138 result
= (int)read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
3139 if ((result
!= sizeof(entry
)) ||
3140 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
3141 (entry
.space
!= DBBLANKSPACE
) ||
3142 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
3146 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
3152 static int CompareVSDBKeys(VSDBKeyUUID_t
*key1
, VSDBKeyUUID_t
*key2
) {
3154 return strcmp(key1
->uuid_string
, key2
->uuid_string
);
3159 /******************************************************************************
3161 * 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
3163 *****************************************************************************/
3165 static void FormatULong(unsigned long u
, char *s
) {
3170 for (i
= 0; i
< 8; ++i
) {
3171 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
3173 *digitptr
++ = (char)(d
+ '0');
3175 *digitptr
++ = (char)(d
- 10 + 'A');
3182 static void FormatDBKey(volUUID_t
*volumeID
, VSDBKeyUUID_t
*dbkey
) {
3183 uuid_string_t uuid_str
;
3185 uuid_unparse (volumeID
->uuid
, uuid_str
);
3186 memcpy (dbkey
->uuid_string
, uuid_str
, sizeof (uuid_str
));
3191 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
3192 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
3196 static void FormatDBEntry(volUUID_t
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntryUUID
*dbentry
) {
3197 FormatDBKey(volumeID
, &dbentry
->key
);
3198 dbentry
->keySeparator
= DBKEYSEPARATOR
;
3199 dbentry
->space
= DBBLANKSPACE
;
3200 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
3202 dbentry
->terminator
= DBRECORDTERMINATOR
;
3207 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
3210 unsigned long nextdigit
;
3214 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
3215 if ((c
>= '0') && (c
<= '9')) {
3216 nextdigit
= c
- '0';
3217 } else if ((c
>= 'A') && (c
<= 'F')) {
3218 nextdigit
= c
- 'A' + 10;
3219 } else if ((c
>= 'a') && (c
<= 'f')) {
3220 nextdigit
= c
- 'a' + 10;
3224 n
= (n
<< 4) + nextdigit
;