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>
70 * CommonCrypto provides a more stable API than OpenSSL guarantees;
71 * the #define causes it to use the same API for MD5 and SHA1, so the rest of
72 * the code need not change.
74 #define COMMON_DIGEST_FOR_OPENSSL
75 #include <CommonCrypto/CommonDigest.h>
77 #include <libkern/OSByteOrder.h>
79 #include <CoreFoundation/CFString.h>
81 #include <uuid/uuid.h>
82 #include <System/uuid/namespace.h>
84 #define READ_DEFAULT_ENCODING 1
87 #define FSUC_ADOPT 'a'
91 #define FSUC_DISOWN 'd'
95 #define FSUC_GETUUID 'k'
99 #define FSUC_SETUUID 's'
103 #define FSUC_MKJNL 'J'
107 #define FSUC_UNJNL 'U'
110 #ifndef FSUC_UNJNL_RAW
111 #define FSUC_UNJNL_RAW 'N'
114 #ifndef FSUC_JNLINFS_RAW
115 #define FSUC_JNLINFS_RAW 'e'
118 #ifndef FSUC_EXTJNL_RAW
119 #define FSUC_EXTJNL_RAW 'E'
123 #define FSUC_JNLINFO 'I'
127 /* **************************************** L O C A L S ******************************************* */
129 #define kHFSPlusMaxFileNameBytes (3 * 255 + 1) /* 255 unicode characters, plus 1 NUL byte */
131 #define HFS_BLOCK_SIZE 512
133 char gHFS_FS_NAME
[] = "hfs";
134 char gHFS_FS_NAME_NAME
[] = "HFS";
136 char gNewlineString
[] = "\n";
138 char gMountCommand
[] = "/sbin/mount";
140 char gUnmountCommand
[] = "/sbin/umount";
142 char gReadOnlyOption
[] = "-r";
143 char gReadWriteOption
[] = "-w";
145 char gSuidOption
[] = "suid";
146 char gNoSuidOption
[] = "nosuid";
148 char gDevOption
[] = "dev";
149 char gNoDevOption
[] = "nodev";
151 char gUsePermissionsOption
[] = "perm";
152 char gIgnorePermissionsOption
[] = "noperm";
154 boolean_t gIsEjectable
= 0;
156 int gJournalSize
= 0;
158 #define AUTO_ADOPT_FIXED 1
159 #define AUTO_ENTER_FIXED 0
162 typedef struct FinderAttrBuf
{
163 u_int32_t info_length
;
164 u_int32_t finderinfo
[8];
168 /* For requesting the UUID from the FS */
169 typedef struct UUIDAttrBuf
{
170 uint32_t info_length
;
174 /* HFS+ internal representation of UUID */
175 typedef struct hfs_UUID
{
181 typedef struct volUUID
{
186 #define HFSUUIDLENGTH 16
188 #define VOLUME_RECORDED 0x80000000
189 #define VOLUME_USEPERMISSIONS 0x00000001
190 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
192 typedef void *VolumeStatusDBHandle
;
194 /* UUID generation and conversion functions */
195 void GenerateHFSVolumeUUID(hfs_UUID_t
*hfsuu
);
196 void ConvertHFSUUIDStringToUUID (const char* UUIDString
, volUUID_t
*volid
);
197 void ConvertHFSUUIDToUUID (hfs_UUID_t
*hfsuuid
, volUUID_t
*uu
);
200 * Volume Database manipulation routines
201 * These functions MUST manipulate the VSDB in the same way that vsdbutil does
203 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
204 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
205 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long *VolumeStatus
);
206 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long VolumeStatus
);
207 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
);
208 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
210 /* ************************************ P R O T O T Y P E S *************************************** */
211 static void DoDisplayUsage( const char * argv
[] );
212 static int DoMount( char * theDeviceNamePtr
, const char *rawName
, const char * theMountPointPtr
,
213 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
214 static int DoProbe( char * rawDeviceNamePtr
, char * blockDeviceNamePtr
);
215 static int DoUnmount( const char * theMountPointPtr
);
216 static int DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
);
217 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
218 static int DoAdopt( const char * theDeviceNamePtr
, const char *rawName
);
219 static int DoDisown( const char * theDeviceNamePtr
, const char *rawName
);
221 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
222 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
223 extern int DoGetJournalInfo( const char * volNamePtr
);
224 extern int RawDisableJournaling( const char *devname
);
225 extern int SetJournalInFSState( const char *devname
, int journal_in_fs
);
227 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
);
228 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
230 /* Helper functions for manipulating HFS and full UUIDs */
231 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, hfs_UUID_t
**finderInfoUUIDPtr
);
232 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*volumeUUIDPtr
);
233 static int GetVolumeUUIDAttr(const char *path
, volUUID_t
*volumeUUIDPtr
);
234 static int GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*volumeUUIDPtr
, boolean_t generate
);
235 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, hfs_UUID_t
*hfsuu
);
236 static int SetVolumeUUIDAttr(const char *path
, hfs_UUID_t
*hfsuu
);
237 static int SetVolumeUUID(const char *deviceNamePtr
, hfs_UUID_t
*hfsuu
);
240 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
241 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
242 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
243 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
244 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
245 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
246 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
247 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
248 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
249 off_t
*physicalOffset
, ssize_t
*availableBytes
);
250 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
251 off_t volOffset
, u_int32_t blockSize
,
252 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
253 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
254 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
256 static int GetEncodingBias(void);
259 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
261 static void uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
);
264 * The fuction CFStringGetSystemEncoding does not work correctly in
265 * our context (autodiskmount deamon). We include a local copy here
266 * so that we can derive the default encoding. Radar 2516316.
268 #if READ_DEFAULT_ENCODING
269 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
271 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
272 struct passwd
*passwdp
;
274 if ((passwdp
= getpwuid(0))) { // root account
275 char buffer
[MAXPATHLEN
+ 1];
278 strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
));
279 strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
));
281 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
284 readSize
= read(fd
, buffer
, MAXPATHLEN
);
285 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
287 return strtol(buffer
, NULL
, 0);
290 return 0; // Fallback to smRoman
295 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
297 struct hfs_mnt_encoding
{
298 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
299 CFStringEncoding encoding_id
; /* encoding type number */
302 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
308 { "CentralEurRoman", 29 },
309 { "ChineseSimp", 25 },
310 { "ChineseTrad", 2 },
331 { "Roman", 0 }, /* default */
339 { "Ukrainian", 152 },
340 { "Vietnamese", 30 },
343 #define KEXT_LOAD_COMMAND "/sbin/kextload"
344 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/"
346 static int load_encoding(CFStringEncoding encoding
)
354 char kmodfile
[MAXPATHLEN
];
356 /* Find the encoding that matches the one passed in */
357 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
359 for (i
=0; i
<numEncodings
; ++i
)
361 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
363 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
368 if (encodingName
== NULL
)
370 /* Couldn't figure out which encoding KEXT to load */
371 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
375 snprintf(kmodfile
, sizeof(kmodfile
), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
376 if (stat(kmodfile
, &sb
) == -1)
378 /* We recognized the encoding, but couldn't find the KEXT */
379 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
386 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
388 exit(1); /* We can only get here if the exec failed */
392 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
394 if (WEXITSTATUS(status
) != 0)
396 /* kextload returned an error. Too bad its output doesn't get logged. */
397 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
403 return FSUR_IO_SUCCESS
;
407 /* ******************************************** main ************************************************
409 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
410 for detail info on input arguments.
412 argc - the number of arguments in argv.
413 argv - array of arguments.
415 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
416 *************************************************************************************************** */
418 int main (int argc
, const char *argv
[])
420 const char * actionPtr
= NULL
;
421 char rawDeviceName
[MAXPATHLEN
];
422 char blockDeviceName
[MAXPATHLEN
];
423 const char * mountPointPtr
= NULL
;
424 int result
= FSUR_IO_SUCCESS
;
425 boolean_t isLocked
= 0; /* reasonable assumptions */
426 boolean_t isSetuid
= 0; /* reasonable assumptions */
427 boolean_t isDev
= 0; /* reasonable assumptions */
429 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
431 /* Verify our arguments */
432 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
437 -- Build our device name (full path), should end up with something like:
441 snprintf(rawDeviceName
, sizeof(rawDeviceName
), "/dev/r%s", argv
[2]);
442 snprintf(blockDeviceName
, sizeof(blockDeviceName
), "/dev/%s", argv
[2]);
444 /* call the appropriate routine to handle the given action argument after becoming root */
446 switch( * actionPtr
) {
448 result
= DoProbe(rawDeviceName
, blockDeviceName
);
452 case FSUC_MOUNT_FORCE
:
453 result
= DoMount(blockDeviceName
, rawDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
457 result
= DoUnmount( mountPointPtr
);
460 result
= DoGetUUIDKey( blockDeviceName
, rawDeviceName
);
464 result
= DoChangeUUIDKey( blockDeviceName
);
467 result
= DoAdopt( blockDeviceName
, rawDeviceName
);
471 result
= DoDisown( blockDeviceName
, rawDeviceName
);
476 result
= DoMakeJournaled( argv
[3], gJournalSize
);
478 result
= DoMakeJournaled( argv
[2], gJournalSize
);
483 result
= DoUnJournal( argv
[2] );
487 result
= RawDisableJournaling( argv
[2] );
490 case FSUC_JNLINFS_RAW
:
491 // argv[2] has the device for the external journal. however
492 // we don't need it so we ignore it and just pass argv[3]
493 // which is the hfs volume whose state we're going to change
495 result
= SetJournalInFSState( argv
[3], 1 );
498 case FSUC_EXTJNL_RAW
:
499 // see the comment for FSUC_JNLINFS_RAW
500 result
= SetJournalInFSState( argv
[3], 0 );
504 result
= DoGetJournalInfo( argv
[2] );
508 /* should never get here since ParseArgs should handle this situation */
509 DoDisplayUsage( argv
);
518 return result
; /*...and make main fit the ANSI spec. */
522 /* ***************************** DoMount ********************************
524 This routine will fire off a system command to mount the given device at the given mountpoint.
525 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
527 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
528 mountPointPtr - pointer to the mount point.
531 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
532 *********************************************************************** */
534 DoMount(char *deviceNamePtr
, const char *rawName
, const char *mountPointPtr
,
535 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
541 char *permissionsOption
;
542 int result
= FSUR_IO_FAIL
;
544 char encodeopt
[16] = "";
545 CFStringEncoding encoding
;
546 volUUID_t targetVolUUID
;
547 VolumeStatusDBHandle vsdbhandle
= NULL
;
548 unsigned long targetVolumeStatus
;
550 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
551 return (FSUR_IO_FAIL
);
553 /* get the volume UUID to check if permissions should be used: */
554 targetVolumeStatus
= 0;
555 if (((result
= GetVolumeUUID(deviceNamePtr
, rawName
, &targetVolUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
556 (uuid_is_null(targetVolUUID
.uuid
))) {
558 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
561 if (gIsEjectable
== 0) {
562 result
= DoAdopt( deviceNamePtr
, rawName
);
564 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
566 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
569 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
571 targetVolumeStatus
= 0;
575 /* We've got a real volume UUID! */
576 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
577 /* Can't even get access to the volume info db; assume permissions are OK. */
579 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
581 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
584 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
586 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolUUID
, &targetVolumeStatus
)) != 0) {
588 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
591 if (gIsEjectable
== 0) {
592 result
= DoAdopt( deviceNamePtr
, rawName
);
594 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
596 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
599 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
601 targetVolumeStatus
= 0;
604 targetVolumeStatus
= 0;
607 (void)CloseVolumeStatusDB(vsdbhandle
);
614 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
615 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
616 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
619 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
621 /* get default encoding value (for hfs volumes) */
622 #if READ_DEFAULT_ENCODING
623 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
625 encoding
= CFStringGetSystemEncoding();
627 snprintf(encodeopt
, sizeof(encodeopt
), "-e=%d", (int)encoding
);
629 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
630 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
632 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
633 "-o", encodeopt
, "-o", permissionsOption
,
634 "-o", "-u=unknown,-g=unknown,-m=0777",
635 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
638 /* IF WE ARE HERE, WE WERE UNSUCCESFUL */
639 return (FSUR_IO_FAIL
);
643 return (FSUR_IO_FAIL
);
646 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
647 result
= status
.w_retcode
;
651 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
655 /* ****************************************** DoUnmount *********************************************
657 This routine will fire off a system command to unmount the given device.
659 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
661 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
662 *************************************************************************************************** */
664 DoUnmount(const char * theMountPointPtr
)
670 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
675 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
677 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
679 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
680 return (FSUR_IO_FAIL
);
684 return (FSUR_IO_FAIL
);
687 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
688 result
= status
.w_retcode
;
692 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
700 Get the volume name of the volume mounted at "path". Print that volume
701 name to standard out.
703 Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL
705 struct VolumeNameBuf
{
706 u_int32_t info_length
;
707 attrreference_t name_ref
;
712 PrintVolumeNameAttr(const char *path
)
714 struct attrlist alist
;
715 struct VolumeNameBuf volNameInfo
;
718 /* Set up the attrlist structure to get the volume's Finder Info */
719 memset (&alist
, 0, sizeof(alist
));
720 alist
.bitmapcount
= 5;
722 alist
.commonattr
= 0;
723 alist
.volattr
= ATTR_VOL_INFO
| ATTR_VOL_NAME
;
729 /* Get the Finder Info */
730 result
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0);
732 result
= FSUR_IO_FAIL
;
736 /* Print the name to standard out */
737 printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
);
738 result
= FSUR_RECOGNIZED
;
745 /* ******************************************* DoProbe **********************************************
747 This routine will open the given device and check to make sure there is media that looks
748 like an HFS. If it is HFS, then print the volume name to standard output.
750 rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2).
751 blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2).
753 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
754 *************************************************************************************************** */
756 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
)
758 int result
= FSUR_UNRECOGNIZED
;
761 HFSMasterDirectoryBlock
* mdbPtr
;
762 HFSPlusVolumeHeader
* volHdrPtr
;
763 u_char volnameUTF8
[kHFSPlusMaxFileNameBytes
];
766 * Determine if there is a volume already mounted from this device. If
767 * there is, and it is HFS, then we need to get the volume name via
770 * NOTE: We're using bufPtr to hold a pointer to a path.
773 result
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
);
774 if (result
!= FSUR_IO_SUCCESS
) {
777 if (bufPtr
!= NULL
) {
778 /* There is an HFS volume mounted from the device. */
779 result
= PrintVolumeNameAttr(bufPtr
);
784 * If we get here, there is no volume mounted from this device, so
785 * go probe the raw device directly.
788 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
790 result
= FSUR_UNRECOGNIZED
;
794 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
795 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
797 fd
= open( rawDeviceNamePtr
, O_RDONLY
, 0 );
799 result
= FSUR_IO_FAIL
;
804 * Read the HFS Master Directory Block from sector 2
806 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
807 if (FSUR_IO_FAIL
== result
)
810 /* get classic HFS volume name (from MDB) */
811 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
812 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
815 CFStringEncoding encoding
;
817 /* Some poorly mastered HFS CDs have an empty MDB name field! */
818 if (mdbPtr
->drVN
[0] == '\0') {
819 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
820 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
823 /* Check for an encoding hint in the Finder Info (field 4). */
824 encoding
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4]));
825 if (encoding
== kCFStringEncodingInvalidId
) {
826 /* Next try the encoding bias in the kernel. */
827 encoding
= GetEncodingBias();
828 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
829 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
832 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
833 mdbPtr
->drVN
, encoding
);
838 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
841 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
843 /* default to MacRoman on conversion errors */
844 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
845 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
846 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
848 encoding
= kCFStringEncodingMacRoman
;
851 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
852 if (encoding
!= kCFStringEncodingMacRoman
) {
853 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
854 encoding
= kCFStringEncodingMacRoman
;
855 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
856 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
861 /* get HFS Plus volume name (from Catalog) */
862 } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
863 (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) ||
864 (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
865 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
868 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) {
869 /* embedded volume, first find offset */
870 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
871 if ( result
!= FSUR_IO_SUCCESS
)
877 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
880 result
= FSUR_UNRECOGNIZED
;
883 if (FSUR_IO_SUCCESS
== result
) {
884 /* Print the volume name to standard output */
885 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
886 result
= FSUR_RECOGNIZED
;
902 * Create a version 3 UUID from a unique "name" in the given "name space".
903 * Version 3 UUID are derived using "name" via MD5 checksum.
906 * result_uuid - resulting UUID.
907 * namespace - namespace in which given name exists and UUID should be created.
908 * name - unique string used to create version 3 UUID.
909 * namelen - length of the name string.
912 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t
namespace, const void *name
, int namelen
)
917 MD5_Update(&c
, namespace, sizeof(uuid_t
));
918 MD5_Update(&c
, name
, namelen
);
919 MD5_Final(result_uuid
, &c
);
921 result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30;
922 result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80;
926 /* **************************************** DoGetUUIDKey *******************************************
928 This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout.
930 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
932 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
933 *************************************************************************************************** */
935 DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
) {
937 volUUID_t targetVolumeUUID
;
940 result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
);
941 if (result
== FSUR_IO_SUCCESS
) {
942 uuid_unparse (targetVolumeUUID
.uuid
, uustr
);
943 /* for compatibility, must write out the string to stdout, with NO newline */
944 write(STDOUT_FILENO
, uustr
, strlen(uustr
));
947 // fprintf(stderr, "device %s UUID : %s\n", rawName, uustr);
955 /* *************************************** DoChangeUUIDKey ******************************************
957 This routine will change the UUID on the specified block device.
959 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
961 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
962 *************************************************************************************************** */
964 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
966 hfs_UUID_t newVolumeUUID
;
968 GenerateHFSVolumeUUID(&newVolumeUUID
);
970 // for testing purposes, may want to set a NULL UUID from command line.
971 memset (&newVolumeUUID
, 0, sizeof(newVolumeUUID
));
973 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
975 //fprintf(stderr, "device %s has new UUID \n", theDeviceNamePtr);
982 /* **************************************** DoAdopt *******************************************
984 This routine will add the UUID of the specified block device to the list of local volumes.
986 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
988 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
989 *************************************************************************************************** */
991 DoAdopt( const char * theDeviceNamePtr
, const char *rawName
) {
992 int result
, closeresult
;
993 volUUID_t targetVolumeUUID
;
994 VolumeStatusDBHandle vsdbhandle
= NULL
;
995 unsigned long targetVolumeStatus
;
997 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
999 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1000 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1001 targetVolumeStatus
= 0;
1003 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
1004 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1006 result
= FSUR_IO_SUCCESS
;
1010 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1012 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1015 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
1019 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
1026 /* **************************************** DoDisown *******************************************
1028 This routine will change the status of the specified block device to ignore its permissions.
1030 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
1032 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
1033 *************************************************************************************************** */
1035 DoDisown( const char * theDeviceNamePtr
, const char *rawName
) {
1036 int result
, closeresult
;
1037 volUUID_t targetVolumeUUID
;
1038 VolumeStatusDBHandle vsdbhandle
= NULL
;
1039 unsigned long targetVolumeStatus
;
1041 if ((result
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
1043 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
1044 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
1045 targetVolumeStatus
= 0;
1047 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
1048 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
1050 result
= FSUR_IO_SUCCESS
;
1054 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
1056 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
1059 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
1061 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
1063 result
= FSUR_IO_FAIL
;
1068 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
1075 get_multiplier(char c
)
1077 if (tolower(c
) == 'k') {
1079 } else if (tolower(c
) == 'm') {
1081 } else if (tolower(c
) == 'g') {
1082 return 1024 * 1024 * 1024;
1088 /* **************************************** ParseArgs ********************************************
1090 This routine will make sure the arguments passed in to us are cool.
1091 Here is how this utility is used:
1093 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
1095 -p (Probe for mounting)
1096 -P (Probe for initializing - not supported)
1098 -r (Repair - not supported)
1101 -i (Initialize - not supported)
1104 disk0s2 (for example)
1107 /foo/bar/ (required for Mount and Force Mount actions)
1110 (these are ignored for CDROMs)
1111 either "readonly" OR "writable"
1112 either "removable" OR "fixed"
1113 either "nosuid" or "suid"
1114 either "nodev" or "dev"
1117 hfs.util -p disk0s2 removable writable
1118 hfs.util -p disk0s2 removable readonly
1119 hfs.util -m disk0s2 /my/hfs
1122 argc - the number of arguments in argv.
1123 argv - array of arguments.
1125 returns FSUR_INVAL if we find a bad argument else 0.
1126 *************************************************************************************************** */
1128 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
1129 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
1130 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
1132 int result
= FSUR_INVAL
;
1133 int deviceLength
, doLengthCheck
= 1;
1137 /* Must have at least 3 arguments and the action argument must start with a '-' */
1138 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
1139 DoDisplayUsage( argv
);
1143 /* we only support actions Probe, Mount, Force Mount, and Unmount */
1145 * actionPtr
= & argv
[1][1];
1147 switch ( argv
[1][1] ) {
1149 /* action Probe and requires 5 arguments (need the flags) */
1151 DoDisplayUsage( argv
);
1159 /* Note: the device argument in argv[2] is checked further down but ignored. */
1160 * mountPointPtr
= argv
[3];
1161 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1165 case FSUC_MOUNT_FORCE
:
1166 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1168 DoDisplayUsage( argv
);
1171 * mountPointPtr
= argv
[3];
1197 if (isdigit(argv
[2][0])) {
1199 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1201 gJournalSize
*= get_multiplier(*ptr
);
1212 case FSUC_UNJNL_RAW
:
1217 case FSUC_JNLINFS_RAW
:
1222 case FSUC_EXTJNL_RAW
:
1234 DoDisplayUsage( argv
);
1239 /* Make sure device (argv[2]) is something reasonable */
1240 deviceLength
= strlen( argv
[2] );
1241 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1242 DoDisplayUsage( argv
);
1247 /* Flags: removable/fixed. */
1248 if ( 0 == strcmp(argv
[index
],"removable") ) {
1249 * isEjectablePtr
= 1;
1250 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1251 * isEjectablePtr
= 0;
1253 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1256 /* Flags: readonly/writable. */
1257 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1259 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1262 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1266 /* Flags: suid/nosuid. */
1267 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1269 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1272 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1275 /* Flags: dev/nodev. */
1276 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1278 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1281 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1296 /* *************************************** DoDisplayUsage ********************************************
1298 This routine will do a printf of the correct usage for this utility.
1300 argv - array of arguments.
1303 *************************************************************************************************** */
1305 DoDisplayUsage(const char *argv
[])
1307 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1308 printf("action_arg:\n");
1309 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1310 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1311 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1312 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1313 #ifdef HFS_UUID_SUPPORT
1314 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1315 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1316 #endif //HFS_UUID_SUPPORT
1317 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1318 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1319 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1320 printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
);
1321 printf(" -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW
);
1322 printf(" -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW
);
1323 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1324 printf("device_arg:\n");
1325 printf(" device we are acting upon (for example, 'disk0s2')\n");
1326 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1327 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1328 printf("mount_point_arg:\n");
1329 printf(" required for Mount and Force Mount \n");
1331 printf(" required for Mount, Force Mount and Probe\n");
1332 printf(" indicates removable or fixed (for example 'fixed')\n");
1333 printf(" indicates readonly or writable (for example 'readonly')\n");
1334 printf(" indicates suid or nosuid (for example 'suid')\n");
1335 printf(" indicates dev or nodev (for example 'dev')\n");
1336 printf("Examples:\n");
1337 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1338 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1342 } /* DoDisplayUsage */
1348 Given a path to a device, determine if a volume is mounted on that
1349 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1350 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1351 no volume mounted on the device, set *pathPtr to NULL and return
1354 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1357 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1363 /* Assume no mounted volume found */
1365 result
= FSUR_IO_SUCCESS
;
1367 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1369 return FSUR_IO_FAIL
;
1371 for (i
=0; i
<numMounts
; ++i
) {
1372 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1373 /* Found a mounted volume; check the type */
1374 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1375 *pathPtr
= buf
[i
].f_mntonname
;
1376 /* result = FSUR_IO_SUCCESS, above */
1378 result
= FSUR_UNRECOGNIZED
;
1391 Read the Master Directory Block or Volume Header Block from an HFS,
1392 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1393 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1394 Return a pointer to the volume UUID in the Finder Info.
1396 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1399 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, hfs_UUID_t
**finderInfoUUIDPtr
)
1402 HFSMasterDirectoryBlock
* mdbPtr
;
1403 HFSPlusVolumeHeader
* volHdrPtr
;
1409 * Read the HFS Master Directory Block or Volume Header from sector 2
1412 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1413 if (result
!= FSUR_IO_SUCCESS
)
1417 * If this is a wrapped HFS Plus volume, read the Volume Header from
1418 * sector 2 of the embedded volume.
1420 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1421 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1422 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1423 if (result
!= FSUR_IO_SUCCESS
)
1425 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1426 if (result
!= FSUR_IO_SUCCESS
)
1431 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1432 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1433 * UUID from the Finder Info.
1435 if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1436 *finderInfoUUIDPtr
= (hfs_UUID_t
*)(&mdbPtr
->drFndrInfo
[6]);
1437 } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1438 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) {
1439 *finderInfoUUIDPtr
= (hfs_UUID_t
*)&volHdrPtr
->finderInfo
[24];
1441 result
= FSUR_UNRECOGNIZED
;
1452 Read the UUID from an unmounted volume, by doing direct access to the device.
1453 Assumes the caller has already determined that a volume is not mounted
1454 on the device. Once we have the HFS UUID from the finderinfo, convert it to a
1455 full UUID and then write it into the output argument provided (volUUIDPtr)
1457 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1460 GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*volUUIDPtr
)
1465 hfs_UUID_t
*finderInfoUUIDPtr
;
1466 hfs_UUID_t hfs_uuid
;
1471 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1473 result
= FSUR_UNRECOGNIZED
;
1477 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1481 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", deviceNamePtr
, errno
);
1483 if (error
== EBUSY
) {
1484 /* If it was busy, then retry, this time using the raw device */
1485 fd
= open (rawName
, O_RDONLY
, 0);
1488 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName
, errno
);
1490 result
= FSUR_IO_FAIL
;
1495 result
= FSUR_IO_FAIL
;
1501 * Get the pointer to the volume UUID in the Finder Info*/
1502 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1503 if (result
!= FSUR_IO_SUCCESS
)
1507 * Copy the volume UUID out of the Finder Info. Note that the FinderInfo
1508 * stores the UUID in big-endian so we have to convert to native
1511 hfs_uuid
.high
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->high
);
1512 hfs_uuid
.low
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->low
);
1515 * Now convert to a full UUID using the same algorithm as HFS+
1516 * This makes sure to construct a full NULL-UUID if necessary.
1518 ConvertHFSUUIDToUUID (&hfs_uuid
, &fullUUID
);
1520 /* Copy it out into the caller's buffer */
1521 uuid_copy(volUUIDPtr
->uuid
, fullUUID
.uuid
);
1524 if (fd
> 0) close(fd
);
1525 if (bufPtr
) free(bufPtr
);
1528 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1530 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1535 void ConvertHFSUUIDStringToUUID(const char *UUIDString
, volUUID_t
*volumeID
) {
1538 u_int32_t nextdigit
;
1544 for (i
= 0; (i
< HFSUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
1545 if ((c
>= '0') && (c
<= '9')) {
1546 nextdigit
= c
- '0';
1547 } else if ((c
>= 'A') && (c
<= 'F')) {
1548 nextdigit
= c
- 'A' + 10;
1549 } else if ((c
>= 'a') && (c
<= 'f')) {
1550 nextdigit
= c
- 'a' + 10;
1554 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
1555 high
= (high
<< 4) | carry
;
1556 low
= (low
<< 4) | nextdigit
;
1562 /* now convert to a full UUID */
1563 ConvertHFSUUIDToUUID(&hfsuu
, volumeID
);
1571 * Convert an HFS+ UUID in binary form to a full UUID
1573 * Assumes that the HFS UUID argument is stored in native endianness
1574 * If the input UUID is zeroes, then it will emit a NULL'd out UUID.
1576 void ConvertHFSUUIDToUUID (hfs_UUID_t
*hfsuuid
, volUUID_t
*uu
)
1580 /* if either high or low is 0, then return the NULL uuid */
1581 if ((hfsuuid
->high
== 0) || (hfsuuid
->low
== 0)) {
1582 uuid_clear (uu
->uuid
);
1586 * If the input UUID was not zeroes, then run it through the normal md5
1588 * NOTE: When using MD5 to compute the "full" UUID, we must pass in the
1589 * big-endian values of the two 32-bit fields. In the kernel, HFS uses the
1590 * raw 4-byte fields of the finderinfo directly, without running them through
1591 * an endian-swap. As a result, we must endian-swap back to big endian here.
1593 ((uint32_t*)rawUUID
)[0] = OSSwapHostToBigInt32(hfsuuid
->high
);
1594 ((uint32_t*)rawUUID
)[1] = OSSwapHostToBigInt32(hfsuuid
->low
);
1595 uuid_create_md5_from_name(uu
->uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
));
1601 Write a previously generated UUID to an unmounted volume, by doing direct
1602 access to the device. Assumes the caller has already determined that a
1603 volume is not mounted on the device.
1605 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1608 SetVolumeUUIDRaw(const char *deviceNamePtr
, hfs_UUID_t
*volumeUUIDPtr
)
1613 hfs_UUID_t
*finderInfoUUIDPtr
;
1616 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1618 result
= FSUR_UNRECOGNIZED
;
1622 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1625 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1627 result
= FSUR_IO_FAIL
;
1632 * Get the pointer to the volume UUID in the Finder Info
1634 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1635 if (result
!= FSUR_IO_SUCCESS
)
1639 * Update the UUID in the Finder Info. Make sure to write out big endian.
1641 finderInfoUUIDPtr
->high
= OSSwapHostToBigInt32(volumeUUIDPtr
->high
);
1642 finderInfoUUIDPtr
->low
= OSSwapHostToBigInt32(volumeUUIDPtr
->low
);
1645 * Write the modified MDB or VHB back to disk
1647 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1650 if (fd
> 0) close(fd
);
1651 if (bufPtr
) free(bufPtr
);
1654 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1656 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1663 Read the UUID from a mounted volume, by calling getattrlist().
1664 Assumes the path is the mount point of an HFS volume. Note that this will
1665 return the full-length UUID to the caller, as emitted by the underlying
1666 filesystem. On HFS+ this means that we use the hfs_vfsops.c implementation
1667 to construct the UUID
1669 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1672 GetVolumeUUIDAttr(const char *path
, volUUID_t
*volUUIDPtr
)
1674 struct attrlist alist
;
1675 UUIDAttrBuf_t uuidattr
;
1676 FinderAttrBuf_t finderinfo
;
1680 * This is a little bit dodgy. In order to detect whether or not the
1681 * volume has a valid UUID, we need to call getattrlist() and examine
1682 * the FinderInfo, which is what this function has historically done, even if
1683 * we ultimately want the full UUID, which is what is returned if one requests
1686 * The reason is that if the UUID does not exist, it will be stored
1687 * as 8 bytes of zeroes in the UUID portion of the finder info. However, if
1688 * you request ATTR_VOL_UUID, it will run the 8 bytes of zeroes through
1689 * the MD5 function, where they will be manipulated into a full UUID. It
1690 * doesn't look like that guarantees the resulting UUID will also be a
1691 * NULL-uuid (i.e. all zeroes).
1693 * All of this to say we need to check the finder info first, then check
1694 * ATTR_VOL_UUID as needed afterwards.
1697 /* First set up for a call to getattrlist for the finderinfo */
1698 memset (&alist
, 0, sizeof(alist
));
1699 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1701 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1702 alist
.volattr
= ATTR_VOL_INFO
;
1707 /* Get the finderinfo */
1708 result
= getattrlist(path
, &alist
, &finderinfo
, sizeof(finderinfo
), 0);
1710 return FSUR_IO_FAIL
;
1713 /* Now we need to check if the finderinfo UUID is NULL */
1714 hfs_UUID_t
* hfs_finderinfo
= (hfs_UUID_t
*)(&finderinfo
.finderinfo
[6]);
1717 * We should really endian-swap these, but if a uint32_t is 0,
1718 * the endianness doesn't matter
1720 if ((hfs_finderinfo
->high
== 0) || (hfs_finderinfo
->low
== 0)) {
1721 /* Then it is an uninitialized/NULL UUID. Zap the caller buffer and bail out */
1722 uuid_clear (volUUIDPtr
->uuid
);
1723 return FSUR_IO_SUCCESS
;
1726 /* OK, now set up the attrlist structure to get the volume's UUID */
1727 memset (&alist
, 0, sizeof(alist
));
1728 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1730 alist
.commonattr
= 0;
1731 alist
.volattr
= (ATTR_VOL_INFO
| ATTR_VOL_UUID
);
1736 /* Get the full UUID from the kernel */
1737 result
= getattrlist(path
, &alist
, &uuidattr
, sizeof(uuidattr
), 0);
1739 return FSUR_IO_FAIL
;
1742 /* Copy the UUID from the buf to caller's buffer */
1743 uuid_copy (volUUIDPtr
->uuid
, uuidattr
.uu
);
1744 result
= FSUR_IO_SUCCESS
;
1753 Write a UUID to a mounted volume, by calling setattrlist().
1754 Assumes the path is the mount point of an HFS volume.
1756 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1759 SetVolumeUUIDAttr(const char *path
, hfs_UUID_t
*volumeUUIDPtr
)
1761 struct attrlist alist
;
1762 struct FinderAttrBuf volFinderInfo
;
1763 hfs_UUID_t
*finderInfoUUIDPtr
;
1766 /* Set up the attrlist structure to get the volume's Finder Info */
1767 memset (&alist
, 0, sizeof(alist
));
1768 alist
.bitmapcount
= ATTR_BIT_MAP_COUNT
;
1770 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1771 alist
.volattr
= ATTR_VOL_INFO
;
1776 /* Get the Finder Info */
1777 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1779 result
= FSUR_IO_FAIL
;
1783 /* Update the UUID in the Finder Info. Make sure to swap back to big endian */
1784 finderInfoUUIDPtr
= (hfs_UUID_t
*)(&volFinderInfo
.finderinfo
[6]);
1785 finderInfoUUIDPtr
->high
= OSSwapHostToBigInt32(volumeUUIDPtr
->high
);
1786 finderInfoUUIDPtr
->low
= OSSwapHostToBigInt32(volumeUUIDPtr
->low
);
1788 /* Write the Finder Info back to the volume */
1789 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1791 result
= FSUR_IO_FAIL
;
1795 result
= FSUR_IO_SUCCESS
;
1805 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1806 we were asked to generate one, then generate a new UUID and write it to the
1809 Determine whether an HFS volume is mounted on the given device. If so, we
1810 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1811 the filesystem. If there is no mounted volume, then do direct device access
1812 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1814 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1818 GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, volUUID_t
*voluu
, boolean_t generate
)
1824 * Determine whether a volume is mounted on this device. If it is HFS, then
1825 * get the mount point's path. If it is non-HFS, then we can exit immediately
1826 * with FSUR_UNRECOGNIZED.
1828 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1829 if (result
!= FSUR_IO_SUCCESS
) {
1834 * Get any existing UUID.
1837 result
= GetVolumeUUIDAttr(path
, voluu
);
1840 result
= GetVolumeUUIDRaw(deviceNamePtr
, rawName
, voluu
);
1843 if (result
!= FSUR_IO_SUCCESS
) {
1848 * If there was no valid UUID, and we were asked to generate one, then
1849 * generate it and write it back to disk.
1851 if (generate
&& (uuid_is_null(voluu
->uuid
))) {
1854 GenerateHFSVolumeUUID(&hfsuu
);
1856 result
= SetVolumeUUIDAttr(path
, &hfsuu
);
1859 result
= SetVolumeUUIDRaw(deviceNamePtr
, &hfsuu
);
1870 Write a UUID to an HFS, HFS Plus or HFSX volume.
1872 Determine whether an HFS volume is mounted on the given device. If so, we
1873 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1874 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1876 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1879 SetVolumeUUID(const char *deviceNamePtr
, hfs_UUID_t
*volumeUUIDPtr
) {
1884 * Determine whether a volume is mounted on this device. If it is HFS, then
1885 * get the mount point's path. If it is non-HFS, then we can exit immediately
1886 * with FSUR_UNRECOGNIZED.
1888 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1889 if (result
!= FSUR_IO_SUCCESS
)
1896 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1898 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1907 -- GetEmbeddedHFSPlusVol
1909 -- In: hfsMasterDirectoryBlockPtr
1910 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1911 (that is, 2 blocks before the volume header)
1916 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1918 int result
= FSUR_IO_SUCCESS
;
1919 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1921 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1922 result
= FSUR_UNRECOGNIZED
;
1926 allocationBlockSize
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1927 firstAllocationBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1929 if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1930 result
= FSUR_UNRECOGNIZED
;
1934 startBlock
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1935 blockCount
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1937 if ( startOffsetPtr
)
1938 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1939 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1949 -- GetNameFromHFSPlusVolumeStartingAt
1951 -- Caller's responsibility to allocate and release memory for the converted string.
1953 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1957 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1959 int result
= FSUR_IO_SUCCESS
;
1960 u_int32_t blockSize
;
1961 char * bufPtr
= NULL
;
1962 HFSPlusVolumeHeader
* volHdrPtr
;
1963 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1964 u_int32_t catalogNodeSize
;
1966 u_int32_t catalogExtCount
;
1967 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1969 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1970 if ( ! volHdrPtr
) {
1971 result
= FSUR_IO_FAIL
;
1976 * Read the Volume Header
1977 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1979 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1980 if (result
== FSUR_IO_FAIL
) {
1982 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1984 goto Return
; // return FSUR_IO_FAIL
1987 /* Verify that it is an HFS+ volume. */
1989 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1990 OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) {
1991 result
= FSUR_IO_FAIL
;
1993 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1998 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
1999 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
2000 if ( ! catalogExtents
) {
2001 result
= FSUR_IO_FAIL
;
2004 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
2005 catalogExtCount
= kHFSPlusExtentDensity
;
2007 /* if there are overflow catalog extents, then go get them */
2008 if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) {
2009 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
2010 if (result
!= FSUR_IO_SUCCESS
)
2014 /* Read the header node of the catalog B-Tree */
2016 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2017 catalogExtCount
, catalogExtents
,
2018 &catalogNodeSize
, &leafNode
);
2019 if (result
!= FSUR_IO_SUCCESS
)
2022 /* Read the first leaf node of the catalog b-tree */
2024 bufPtr
= (char *)malloc(catalogNodeSize
);
2026 result
= FSUR_IO_FAIL
;
2030 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2032 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
2033 hfsPlusVolumeOffset
, blockSize
,
2034 catalogExtCount
, catalogExtents
);
2035 if (result
== FSUR_IO_FAIL
) {
2037 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2039 goto Return
; // return FSUR_IO_FAIL
2045 HFSPlusCatalogKey
* k
;
2048 if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) {
2049 result
= FSUR_IO_FAIL
;
2051 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
2056 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
2058 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
2061 // Get a pointer to the first record.
2063 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes
2064 k
= (HFSPlusCatalogKey
*)p
;
2066 // There should be only one record whose parent is the root parent. It should be the first record.
2068 if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) {
2069 result
= FSUR_IO_FAIL
;
2071 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
2076 if ((OSSwapBigToHostInt16(k
->nodeName
.length
) >
2077 (sizeof(k
->nodeName
.unicode
) / sizeof(k
->nodeName
.unicode
[0]))) ||
2078 OSSwapBigToHostInt16(k
->nodeName
.length
) > 255) {
2079 result
= FSUR_IO_FAIL
;
2081 fprintf(stderr
, "hfs.util: ERROR: k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k
->nodeName
.length
));
2086 /* Extract the name of the root directory */
2089 HFSUniStr255
*swapped
;
2092 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
2093 if (swapped
== NULL
) {
2094 result
= FSUR_IO_FAIL
;
2097 swapped
->length
= OSSwapBigToHostInt16(k
->nodeName
.length
);
2099 for (i
=0; i
<swapped
->length
; i
++) {
2100 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]);
2102 swapped
->unicode
[i
] = 0;
2103 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
2104 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
* 3 + 1, kCFStringEncodingUTF8
);
2110 result
= FSUR_IO_SUCCESS
;
2114 free((char*) volHdrPtr
);
2117 free((char*) catalogExtents
);
2120 free((char*)bufPtr
);
2124 } /* GetNameFromHFSPlusVolumeStartingAt */
2128 BTNodeDescriptor node
;
2130 } __attribute__((aligned(2), packed
)) HeaderRec
, *HeaderPtr
;
2135 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2139 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
2140 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2141 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
2144 HeaderRec
* bTreeHeaderPtr
= NULL
;
2146 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
2147 if (bTreeHeaderPtr
== NULL
)
2148 return (FSUR_IO_FAIL
);
2150 /* Read the b-tree header node */
2152 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
2153 hfsPlusVolumeOffset
, blockSize
,
2154 extentCount
, extentList
);
2155 if ( result
== FSUR_IO_FAIL
) {
2157 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
2162 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
2163 result
= FSUR_IO_FAIL
;
2165 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
2170 *nodeSize
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
);
2172 if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0)
2175 *firstLeafNode
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
);
2178 free((char*) bTreeHeaderPtr
);
2182 } /* GetBTreeNodeInfo */
2188 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2192 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
2193 HFSPlusVolumeHeader
*volHdrPtr
,
2194 HFSPlusExtentDescriptor
**catalogExtents
,
2195 u_int32_t
*catalogExtCount
)
2198 u_int32_t numRecords
;
2201 u_int32_t blockSize
;
2202 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
2203 HFSPlusExtentDescriptor
* extents
;
2205 char * bufPtr
= NULL
;
2209 blockSize
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
);
2210 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
2211 extents
= *catalogExtents
;
2212 offset
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) *
2215 /* Read the header node of the extents B-Tree */
2217 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
2218 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
2219 &nodeSize
, &leafNode
);
2220 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
2223 /* Calculate the logical position of the first leaf node */
2225 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2227 /* Read the first leaf node of the extents b-tree */
2229 bufPtr
= (char *)malloc(nodeSize
);
2231 result
= FSUR_IO_FAIL
;
2235 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
2238 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
2239 hfsPlusVolumeOffset
, blockSize
,
2240 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
2241 if ( result
== FSUR_IO_FAIL
) {
2243 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
2248 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
2249 result
= FSUR_IO_FAIL
;
2253 numRecords
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
);
2254 for (i
= 1; i
<= numRecords
; ++i
) {
2257 HFSPlusExtentKey
* k
;
2260 * Get the offset (in bytes) of the record from the
2261 * list of offsets at the end of the node
2263 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
2266 /* Get a pointer to the record */
2268 p
= bufPtr
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */
2269 k
= (HFSPlusExtentKey
*)p
;
2271 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
)
2274 /* grow list and copy additional extents */
2275 listsize
+= sizeof(HFSPlusExtentRecord
);
2276 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
2277 bcopy(p
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
),
2278 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
2280 *catalogExtCount
+= kHFSPlusExtentDensity
;
2281 *catalogExtents
= extents
;
2284 if ((leafNode
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) {
2286 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
2301 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
2302 * position and number of contiguous bytes at that position.
2305 * logicalOffset Logical offset in bytes from start of file
2306 * length Maximum number of bytes to map
2307 * blockSize Number of bytes per allocation block
2308 * extentCount Number of extents in file
2309 * extentList The file's extents
2312 * physicalOffset Physical offset in bytes from start of volume
2313 * availableBytes Number of bytes physically contiguous (up to length)
2315 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2317 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
2318 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
2319 off_t
*physicalOffset
, ssize_t
*availableBytes
)
2322 u_int32_t logicalBlock
;
2324 u_int32_t blockCount
= 0;
2326 /* Determine allocation block containing logicalOffset */
2327 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
2328 offset
%= blockSize
; /* Offset from start of allocation block */
2330 /* Find the extent containing logicalBlock */
2331 for (extent
= 0; extent
< extentCount
; ++extent
)
2333 blockCount
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
);
2335 if (blockCount
== 0)
2336 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2338 if (logicalBlock
< blockCount
)
2339 break; /* Found it! */
2341 logicalBlock
-= blockCount
;
2344 if (extent
>= extentCount
)
2345 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2348 * When we get here, extentList[extent] is the extent containing logicalOffset.
2349 * The desired allocation block is logicalBlock blocks into the extent.
2352 /* Compute the physical starting position */
2353 temp
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2354 temp
*= blockSize
; /* Byte offset of first physical block */
2355 *physicalOffset
= temp
+ offset
;
2357 /* Compute the available contiguous bytes. */
2358 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2360 temp
-= offset
; /* Number of bytes available */
2363 *availableBytes
= temp
;
2365 *availableBytes
= length
;
2367 return FSUR_IO_SUCCESS
;
2373 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2374 * ending position are not allocation or device block aligned.
2377 * fd Descriptor for reading the volume
2378 * buffer The bytes are read into here
2379 * offset Offset in file to start reading
2380 * length Number of bytes to read
2381 * volOffset Byte offset from start of device to start of volume
2382 * blockSize Number of bytes per allocation block
2383 * extentCount Number of extents in file
2384 * extentList The file's exents
2386 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2388 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2389 off_t volOffset
, u_int32_t blockSize
,
2390 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2392 int result
= FSUR_IO_SUCCESS
;
2398 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2399 &physOffset
, &physLength
);
2400 if (result
!= FSUR_IO_SUCCESS
)
2403 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2404 if (result
!= FSUR_IO_SUCCESS
)
2407 length
-= physLength
;
2408 offset
+= physLength
;
2409 buffer
= (char *) buffer
+ physLength
;
2416 -- readAt = lseek() + read()
2418 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2423 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2428 void * rawData
= NULL
;
2431 ssize_t dataOffset
= 0;
2432 int result
= FSUR_IO_SUCCESS
;
2434 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2436 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2438 result
= FSUR_IO_FAIL
;
2441 /* put offset and length in terms of device blocksize */
2442 rawOffset
= offset
/ blocksize
* blocksize
;
2443 dataOffset
= offset
- rawOffset
;
2444 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2445 rawData
= malloc(rawLength
);
2446 if (rawData
== NULL
) {
2447 result
= FSUR_IO_FAIL
;
2451 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2452 if ( lseekResult
!= rawOffset
) {
2453 result
= FSUR_IO_FAIL
;
2457 readResult
= read(fd
, rawData
, rawLength
);
2458 if ( readResult
!= rawLength
) {
2460 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2462 result
= FSUR_IO_FAIL
;
2465 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2476 -- writeAt = lseek() + write()
2478 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2483 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2487 ssize_t bytestransferred
;
2488 void * rawData
= NULL
;
2491 ssize_t dataOffset
= 0;
2492 int result
= FSUR_IO_SUCCESS
;
2494 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2496 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2498 result
= FSUR_IO_FAIL
;
2501 /* put offset and length in terms of device blocksize */
2502 rawOffset
= offset
/ blocksize
* blocksize
;
2503 dataOffset
= offset
- rawOffset
;
2504 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2505 rawData
= malloc(rawLength
);
2506 if (rawData
== NULL
) {
2507 result
= FSUR_IO_FAIL
;
2511 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2512 if ( deviceoffset
!= rawOffset
) {
2513 result
= FSUR_IO_FAIL
;
2517 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2518 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2519 bytestransferred
= read(fd
, rawData
, rawLength
);
2520 if ( bytestransferred
!= rawLength
) {
2522 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2524 result
= FSUR_IO_FAIL
;
2529 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2531 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2532 if ( deviceoffset
!= rawOffset
) {
2533 result
= FSUR_IO_FAIL
;
2537 bytestransferred
= write(fd
, rawData
, rawLength
);
2538 if ( bytestransferred
!= rawLength
) {
2540 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2542 result
= FSUR_IO_FAIL
;
2547 if (rawData
) free(rawData
);
2555 * Get kernel's encoding bias.
2561 size_t buflen
= sizeof(int);
2565 if (getvfsbyname("hfs", &vfc
) < 0)
2569 mib
[1] = vfc
.vfc_typenum
;
2570 mib
[2] = HFS_ENCODINGBIAS
;
2572 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2579 /******************************************************************************
2581 * 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
2583 *****************************************************************************/
2585 #define DBHANDLESIGNATURE 0x75917737
2587 /* Flag values for operation options: */
2588 #define DBMARKPOSITION 1
2590 static char gVSDBPath
[] = "/var/db/volinfo.database";
2592 #define MAXIOMALLOC 16384
2594 /* Database layout: */
2596 typedef struct VSDBKey
{
2600 typedef struct VSDBKeyUUID
{
2601 uuid_string_t uuid_string
;
2605 char statusFlags
[8];
2608 /* A VSDB Entry using a uuid_str (36 byte) instead of HFS UUID string (8 byte) */
2609 typedef struct VSDBEntryUUID
{
2613 struct VSDBRecord record
;
2617 /* a VSDB entry using the HFS UUID */
2618 typedef struct VSDBEntryHFS
{
2622 struct VSDBRecord record
;
2626 #define DBKEYSEPARATOR ':'
2627 #define DBBLANKSPACE ' '
2628 #define DBRECORDTERMINATOR '\n'
2630 /* In-memory data structures: */
2633 unsigned long signature
;
2636 off_t recordPosition
;
2639 typedef struct VSDBState
*VSDBStatePtr
;
2643 /* Internal function prototypes: */
2644 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2645 static int UnlockDB(VSDBStatePtr dbstateptr
);
2647 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, volUUID_t
*volumeID
, VSDBEntryUUID_t
*dbentry
, unsigned long options
);
2648 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
);
2649 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
);
2650 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
);
2651 static int CompareVSDBKeys(VSDBKeyUUID_t
*key1
, VSDBKeyUUID_t
*key2
);
2654 static void FormatULong(unsigned long u
, char *s
);
2655 static void FormatDBKey(volUUID_t
*volumeID
, VSDBKeyUUID_t
*dbkey
);
2656 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2657 static void FormatDBEntry(volUUID_t
*volumeID
, unsigned long volumeStatusFlags
, VSDBEntryUUID_t
*dbentry
);
2658 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2662 /******************************************************************************
2664 * 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
2666 *****************************************************************************/
2668 void GenerateHFSVolumeUUID(hfs_UUID_t
*newuuid
) {
2670 char randomInputBuffer
[26];
2671 unsigned char digest
[20];
2676 char sysctlstring
[128];
2678 double sysloadavg
[3];
2679 struct vmtotal sysvmtotal
;
2682 memset (&hfsuuid
, 0, sizeof(hfsuuid
));
2685 /* Initialize the SHA-1 context for processing: */
2686 SHA1_Init(&context
);
2688 /* Now process successive bits of "random" input to seed the process: */
2690 /* The current system's uptime: */
2692 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2694 /* The kernel's boot time: */
2696 mib
[1] = KERN_BOOTTIME
;
2697 datalen
= sizeof(sysdata
);
2698 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2699 SHA1_Update(&context
, &sysdata
, datalen
);
2701 /* The system's host id: */
2703 mib
[1] = KERN_HOSTID
;
2704 datalen
= sizeof(sysdata
);
2705 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2706 SHA1_Update(&context
, &sysdata
, datalen
);
2708 /* The system's host name: */
2710 mib
[1] = KERN_HOSTNAME
;
2711 datalen
= sizeof(sysctlstring
);
2712 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2713 SHA1_Update(&context
, sysctlstring
, datalen
);
2715 /* The running kernel's OS release string: */
2717 mib
[1] = KERN_OSRELEASE
;
2718 datalen
= sizeof(sysctlstring
);
2719 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2720 SHA1_Update(&context
, sysctlstring
, datalen
);
2722 /* The running kernel's version string: */
2724 mib
[1] = KERN_VERSION
;
2725 datalen
= sizeof(sysctlstring
);
2726 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2727 SHA1_Update(&context
, sysctlstring
, datalen
);
2729 /* The system's load average: */
2730 datalen
= sizeof(sysloadavg
);
2731 getloadavg(sysloadavg
, 3);
2732 SHA1_Update(&context
, &sysloadavg
, datalen
);
2734 /* The system's VM statistics: */
2737 datalen
= sizeof(sysvmtotal
);
2738 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2739 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2741 /* The current GMT (26 ASCII characters): */
2743 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2744 SHA1_Update(&context
, randomInputBuffer
, 26);
2746 /* Pad the accumulated input and extract the final digest hash: */
2747 SHA1_Final(digest
, &context
);
2749 memcpy(&hfsuuid
, digest
, sizeof(hfsuuid
));
2750 } while ((hfsuuid
.high
== 0) || (hfsuuid
.low
== 0));
2752 /* now copy out the hfs uuid */
2753 memcpy (newuuid
, &hfsuuid
, sizeof (hfsuuid
));
2758 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2759 VSDBStatePtr dbstateptr
;
2761 *DBHandlePtr
= NULL
;
2763 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2764 if (dbstateptr
== NULL
) {
2768 dbstateptr
->dbmode
= O_RDWR
;
2769 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2770 if (dbstateptr
->dbfile
== -1) {
2772 The file couldn't be opened for read/write access:
2773 try read-only access before giving up altogether.
2775 dbstateptr
->dbmode
= O_RDONLY
;
2776 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2777 if (dbstateptr
->dbfile
== -1) {
2778 errno_t local_errno
= errno
;
2784 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2785 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2787 /* VSDBUtil converts the status DB, so we do it here, too */
2788 ConvertVolumeStatusDB(*DBHandlePtr
);
2793 /* Convert the volume status DB from 64-bit (HFS-style) entries into full UUIDs */
2794 int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2795 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2796 struct VSDBEntryHFS entry64
;
2799 u_int32_t iobuffersize
;
2800 void *iobuffer
= NULL
;
2803 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2805 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2809 * This function locks the database file then tries to read in
2810 * the size of a old-style HFS UUID database entry. If what it finds
2811 * is a well-formatted HFS entry, then it will convert the entire database.
2812 * If it finds that the file isn't long enough or isn't actually the
2813 * format for the 64-bit UUID struct on-disk, then it will bail out and not
2814 * touch it. Otherwise it will read the whole file and convert it
2816 * In practice this means that if the file is empty or if we have already
2817 * converted it then do nothing.
2819 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2820 result
= read(dbstateptr
->dbfile
, &entry64
, sizeof(entry64
));
2821 if ((result
!= sizeof(entry64
)) ||
2822 (entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
2823 (entry64
.space
!= DBBLANKSPACE
) ||
2824 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
2828 /* Read in a giant buffer */
2829 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2830 iobuffersize
= dbinfo
.st_size
;
2831 iobuffer
= malloc(iobuffersize
);
2832 if (iobuffer
== NULL
) {
2837 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2838 result
= read(dbstateptr
->dbfile
, iobuffer
, iobuffersize
);
2839 if (result
!= iobuffersize
) {
2843 if ((result
= ftruncate(dbstateptr
->dbfile
, 0)) != 0) {
2846 for (i
= 0; i
< iobuffersize
/ sizeof(entry64
); i
++) {
2848 u_int32_t VolumeStatus
;
2849 struct VSDBEntryUUID dbentry
;
2852 * now iterate through the contents of the 64-bit entries in RAM
2853 * and write them on top of the existing database file
2855 entry64
= *(((struct VSDBEntryHFS
*)iobuffer
) + i
);
2856 if ((entry64
.keySeparator
!= DBKEYSEPARATOR
) ||
2857 (entry64
.space
!= DBBLANKSPACE
) ||
2858 (entry64
.terminator
!= DBRECORDTERMINATOR
)) {
2862 ConvertHFSUUIDToUUID(entry64
.key
.uuid
, &volumeID
);
2863 VolumeStatus
= ConvertHexStringToULong(entry64
.record
.statusFlags
, sizeof(entry64
.record
.statusFlags
));
2865 FormatDBEntry(&volumeID
, VolumeStatus
, &dbentry
);
2866 if ((result
= AddVolumeRecord(dbstateptr
, &dbentry
)) != sizeof(dbentry
)) {
2867 warnx("couldn't convert volume status database: %s", strerror(result
));
2872 fsync(dbstateptr
->dbfile
);
2878 if (iobuffer
) free(iobuffer
);
2879 UnlockDB(dbstateptr
);
2886 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long *VolumeStatus
) {
2887 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2888 struct VSDBEntryUUID dbentry
;
2891 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2893 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2895 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2898 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2903 UnlockDB(dbstateptr
);
2909 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
, unsigned long VolumeStatus
) {
2910 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2911 struct VSDBEntryUUID dbentry
;
2914 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2915 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2917 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2919 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2920 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2922 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2924 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2925 } else if (result
== -1) {
2927 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2929 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2934 fsync(dbstateptr
->dbfile
);
2939 UnlockDB(dbstateptr
);
2945 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, volUUID_t
*volumeID
) {
2946 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2949 unsigned long iobuffersize
;
2950 void *iobuffer
= NULL
;
2952 unsigned long iotransfersize
;
2953 unsigned long bytestransferred
;
2955 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2957 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2959 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2961 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2963 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2967 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2969 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2970 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntryUUID
)) <= MAXIOMALLOC
) {
2971 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntryUUID
);
2973 iobuffersize
= MAXIOMALLOC
;
2976 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2977 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2978 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2980 if (iobuffersize
> 0) {
2981 iobuffer
= malloc(iobuffersize
);
2982 if (iobuffer
== NULL
) {
2987 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntryUUID
);
2989 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2990 if (iotransfersize
> 0) {
2991 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2994 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2996 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2997 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2998 if (bytestransferred
!= iotransfersize
) {
3004 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntryUUID
)));
3006 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntryUUID
), SEEK_SET
);
3007 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
3008 if (bytestransferred
!= iotransfersize
) {
3013 dataoffset
+= (off_t
)iotransfersize
;
3015 } while (iotransfersize
> 0);
3018 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntryUUID
))));
3020 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntryUUID
)))) != 0) {
3024 fsync(dbstateptr
->dbfile
);
3030 if (iobuffer
) free(iobuffer
);
3031 UnlockDB(dbstateptr
);
3039 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
3040 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
3042 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
3044 dbstateptr
->signature
= 0;
3046 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
3047 dbstateptr
->dbfile
= 0;
3056 /******************************************************************************
3058 * 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
3060 *****************************************************************************/
3062 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
3064 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
3066 return flock(dbstateptr
->dbfile
, lockmode
);
3071 static int UnlockDB(VSDBStatePtr dbstateptr
) {
3073 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
3075 return flock(dbstateptr
->dbfile
, LOCK_UN
);
3080 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, volUUID_t
*volumeID
,
3081 VSDBEntryUUID_t
*targetEntry
, unsigned long options
) {
3082 VSDBKeyUUID_t searchkey
;
3083 struct VSDBEntryUUID dbentry
;
3086 FormatDBKey(volumeID
, &searchkey
);
3087 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
3090 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
3091 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
3092 if (targetEntry
!= NULL
) {
3094 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
3096 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
3100 } while (result
== 0);
3107 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
) {
3108 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
3109 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntryUUID
));
3113 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
) {
3114 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
3115 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
3118 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, VSDBEntryUUID_t
*dbentry
) {
3119 struct VSDBEntryUUID entry
;
3122 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
3123 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
3124 if ((result
!= sizeof(entry
)) ||
3125 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
3126 (entry
.space
!= DBBLANKSPACE
) ||
3127 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
3131 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
3137 static int CompareVSDBKeys(VSDBKeyUUID_t
*key1
, VSDBKeyUUID_t
*key2
) {
3139 return strcmp(key1
->uuid_string
, key2
->uuid_string
);
3144 /******************************************************************************
3146 * 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
3148 *****************************************************************************/
3150 static void FormatULong(unsigned long u
, char *s
) {
3155 for (i
= 0; i
< 8; ++i
) {
3156 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
3158 *digitptr
++ = (char)(d
+ '0');
3160 *digitptr
++ = (char)(d
- 10 + 'A');
3167 static void FormatDBKey(volUUID_t
*volumeID
, VSDBKeyUUID_t
*dbkey
) {
3168 uuid_string_t uuid_str
;
3170 uuid_unparse (volumeID
->uuid
, uuid_str
);
3171 memcpy (dbkey
->uuid_string
, uuid_str
, sizeof (uuid_str
));
3176 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
3177 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
3181 static void FormatDBEntry(volUUID_t
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntryUUID
*dbentry
) {
3182 FormatDBKey(volumeID
, &dbentry
->key
);
3183 dbentry
->keySeparator
= DBKEYSEPARATOR
;
3184 dbentry
->space
= DBBLANKSPACE
;
3185 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
3187 dbentry
->terminator
= DBRECORDTERMINATOR
;
3192 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
3195 unsigned long nextdigit
;
3199 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
3200 if ((c
>= '0') && (c
<= '9')) {
3201 nextdigit
= c
- '0';
3202 } else if ((c
>= 'A') && (c
<= 'F')) {
3203 nextdigit
= c
- 'A' + 10;
3204 } else if ((c
>= 'a') && (c
<= 'f')) {
3205 nextdigit
= c
- 'a' + 10;
3209 n
= (n
<< 4) + nextdigit
;