2 * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 Copyright (c) 1987-99 Apple Computer, Inc.
30 Contains code to implement hfs utility used by the WorkSpace to mount HFS.
33 5-Jan-1999 Don Brady Write hfs.label names in UTF-8.
34 10-Dec-1998 Pat Dirks Changed to try built-in hfs filesystem first.
35 3-Sep-1998 Don Brady Disable the daylight savings time stuff.
36 28-Aug-1998 chw Fixed parse args and verify args to indicate that the
37 flags (fixed or removable) are required in the probe case.
38 22-Jun-1998 Pat Dirks Changed HFSToUFSStr table to switch ":" and "/".
39 13-Jan-1998 jwc first cut (derived from old NextStep macfs.util code and cdrom.util code).
43 /* ************************************** I N C L U D E S ***************************************** */
45 #include <sys/types.h>
48 #include <sys/sysctl.h>
49 #include <sys/resource.h>
50 #include <sys/vmmeter.h>
51 #include <sys/mount.h>
53 #include <sys/param.h>
54 #include <sys/ucred.h>
56 #include <sys/loadable_fs.h>
58 #include <hfs/hfs_format.h>
59 #include <hfs/hfs_mount.h>
71 #include <openssl/sha.h>
73 #include <architecture/byte_order.h>
75 #include <CoreFoundation/CFString.h>
77 #define READ_DEFAULT_ENCODING 1
80 #define FSUC_ADOPT 'a'
84 #define FSUC_DISOWN 'd'
88 #define FSUC_GETUUID 'k'
92 #define FSUC_SETUUID 's'
96 #define FSUC_MKJNL 'J'
100 #define FSUC_UNJNL 'U'
104 #define FSUC_JNLINFO 'I'
108 /* **************************************** L O C A L S ******************************************* */
110 #define HFS_BLOCK_SIZE 512
112 char gHFS_FS_NAME
[] = "hfs";
113 char gHFS_FS_NAME_NAME
[] = "HFS";
115 char gNewlineString
[] = "\n";
117 char gMountCommand
[] = "/sbin/mount";
119 char gUnmountCommand
[] = "/sbin/umount";
121 char gReadOnlyOption
[] = "-r";
122 char gReadWriteOption
[] = "-w";
124 char gSuidOption
[] = "suid";
125 char gNoSuidOption
[] = "nosuid";
127 char gDevOption
[] = "dev";
128 char gNoDevOption
[] = "nodev";
130 char gUsePermissionsOption
[] = "perm";
131 char gIgnorePermissionsOption
[] = "noperm";
133 boolean_t gIsEjectable
= 0;
135 int gJournalSize
= 0;
137 #define AUTO_ADOPT_FIXED 1
138 #define AUTO_ENTER_FIXED 0
141 struct FinderAttrBuf
{
142 unsigned long info_length
;
143 unsigned long finderinfo
[8];
147 #define VOLUMEUUIDVALUESIZE 2
148 typedef union VolumeUUID
{
149 unsigned long value
[VOLUMEUUIDVALUESIZE
];
156 #define VOLUMEUUIDLENGTH 16
157 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1];
159 #define VOLUME_RECORDED 0x80000000
160 #define VOLUME_USEPERMISSIONS 0x00000001
161 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
163 typedef void *VolumeStatusDBHandle
;
165 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
166 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
);
167 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
);
168 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
169 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
);
170 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
);
171 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
172 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
174 /* ************************************ P R O T O T Y P E S *************************************** */
175 static void DoDisplayUsage( const char * argv
[] );
176 static int DoMount( char * theDeviceNamePtr
, const char * theMountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
177 static int DoProbe( char * theDeviceNamePtr
);
178 static int DoUnmount( const char * theMountPointPtr
);
179 static int DoGetUUIDKey( const char * theDeviceNamePtr
);
180 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
181 static int DoAdopt( const char * theDeviceNamePtr
);
182 static int DoDisown( const char * theDeviceNamePtr
);
184 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
185 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
186 extern int DoGetJournalInfo( const char * volNamePtr
);
188 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
);
190 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
191 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
);
192 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
193 static int GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
194 static int GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
195 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
196 static int SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
197 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
198 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
199 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, char * name_o
);
200 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
201 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
202 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
203 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
204 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
205 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
206 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
207 off_t
*physicalOffset
, ssize_t
*availableBytes
);
208 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
209 off_t volOffset
, u_int32_t blockSize
,
210 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
211 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
212 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
214 static int GetEncodingBias(void);
217 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
220 * The fuction CFStringGetSystemEncoding does not work correctly in
221 * our context (autodiskmount deamon). We include a local copy here
222 * so that we can derive the default encoding. Radar 2516316.
224 #if READ_DEFAULT_ENCODING
225 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
227 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
228 struct passwd
*passwdp
;
230 if ((passwdp
= getpwuid(0))) { // root account
231 char buffer
[MAXPATHLEN
+ 1];
234 strcpy(buffer
, passwdp
->pw_dir
);
235 strcat(buffer
, __kCFUserEncodingFileName
);
237 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
240 readSize
= read(fd
, buffer
, MAXPATHLEN
);
241 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
243 return strtol(buffer
, NULL
, 0);
246 return 0; // Fallback to smRoman
251 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
253 struct hfs_mnt_encoding
{
254 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
255 CFStringEncoding encoding_id
; /* encoding type number */
258 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
264 { "CentralEurRoman", 29 },
265 { "ChineseSimp", 25 },
266 { "ChineseTrad", 2 },
287 { "Roman", 0 }, /* default */
295 { "Ukrainian", 152 },
296 { "Vietnamese", 30 },
299 #define KEXT_LOAD_COMMAND "/sbin/kextload"
300 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
302 static int load_encoding(CFStringEncoding encoding
)
310 char kmodfile
[MAXPATHLEN
];
312 /* Find the encoding that matches the one passed in */
313 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
315 for (i
=0; i
<numEncodings
; ++i
)
317 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
319 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
324 if (encodingName
== NULL
)
326 /* Couldn't figure out which encoding KEXT to load */
327 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
331 sprintf(kmodfile
, "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
332 if (stat(kmodfile
, &sb
) == -1)
334 /* We recognized the encoding, but couldn't find the KEXT */
335 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
342 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
344 exit(1); /* We can only get here if the exec failed */
348 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
350 if (WEXITSTATUS(status
) != 0)
352 /* kextload returned an error. Too bad its output doesn't get logged. */
353 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
359 return FSUR_IO_SUCCESS
;
363 /* ******************************************** main ************************************************
365 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
366 for detail info on input arguments.
368 argc - the number of arguments in argv.
369 argv - array of arguments.
371 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
372 *************************************************************************************************** */
374 int main (int argc
, const char *argv
[])
376 const char * actionPtr
= NULL
;
377 char rawDeviceName
[MAXPATHLEN
];
378 char blockDeviceName
[MAXPATHLEN
];
379 const char * mountPointPtr
= NULL
;
380 int result
= FSUR_IO_SUCCESS
;
381 boolean_t isLocked
= 0; /* reasonable assumptions */
382 boolean_t isSetuid
= 0; /* reasonable assumptions */
383 boolean_t isDev
= 0; /* reasonable assumptions */
385 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
387 /* Verify our arguments */
388 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
393 -- Build our device name (full path), should end up with something like:
397 sprintf(rawDeviceName
, "/dev/r%s", argv
[2]);
398 sprintf(blockDeviceName
, "/dev/%s", argv
[2]);
400 /* call the appropriate routine to handle the given action argument after becoming root */
402 switch( * actionPtr
) {
404 result
= DoProbe(rawDeviceName
);
408 case FSUC_MOUNT_FORCE
:
409 result
= DoMount(blockDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
413 result
= DoUnmount( mountPointPtr
);
416 result
= DoGetUUIDKey( blockDeviceName
);
420 result
= DoChangeUUIDKey( blockDeviceName
);
423 result
= DoAdopt( blockDeviceName
);
427 result
= DoDisown( blockDeviceName
);
432 result
= DoMakeJournaled( argv
[3], gJournalSize
);
434 result
= DoMakeJournaled( argv
[2], gJournalSize
);
439 result
= DoUnJournal( argv
[2] );
443 result
= DoGetJournalInfo( argv
[2] );
447 /* should never get here since ParseArgs should handle this situation */
448 DoDisplayUsage( argv
);
457 return result
; /*...and make main fit the ANSI spec. */
461 /* ***************************** DoMount ********************************
463 This routine will fire off a system command to mount the given device at the given mountpoint.
464 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
466 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
467 mountPointPtr - pointer to the mount point.
470 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
471 *********************************************************************** */
473 DoMount(char *deviceNamePtr
, const char *mountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
479 char *permissionsOption
;
480 int result
= FSUR_IO_FAIL
;
482 char encodeopt
[16] = "";
483 CFStringEncoding encoding
;
484 VolumeUUID targetVolumeUUID
;
485 VolumeStatusDBHandle vsdbhandle
= NULL
;
486 unsigned long targetVolumeStatus
;
488 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
489 return (FSUR_IO_FAIL
);
491 /* get the volume UUID to check if permissions should be used: */
492 targetVolumeStatus
= 0;
493 if (((result
= GetVolumeUUID(deviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
494 (targetVolumeUUID
.v
.high
==0) ||
495 (targetVolumeUUID
.v
.low
== 0)) {
497 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
500 if (gIsEjectable
== 0) {
501 result
= DoAdopt( deviceNamePtr
);
503 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
505 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
508 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
510 targetVolumeStatus
= 0;
514 /* We've got a real volume UUID! */
516 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
518 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
519 /* Can't even get access to the volume info db; assume permissions are OK. */
521 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
523 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
526 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
528 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
530 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
533 if (gIsEjectable
== 0) {
534 result
= DoAdopt( deviceNamePtr
);
536 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
538 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
541 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
543 targetVolumeStatus
= 0;
546 targetVolumeStatus
= 0;
549 (void)CloseVolumeStatusDB(vsdbhandle
);
556 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
557 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
558 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
561 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
563 /* get default encoding value (for hfs volumes) */
564 #if READ_DEFAULT_ENCODING
565 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
567 encoding
= CFStringGetSystemEncoding();
569 sprintf(encodeopt
, "-e=%d", (int)encoding
);
571 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
572 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
574 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
575 "-o", encodeopt
, "-o", permissionsOption
,
576 "-o", "-u=unknown,-g=unknown,-m=0777",
577 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
580 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
581 return (FSUR_IO_FAIL
);
585 return (FSUR_IO_FAIL
);
588 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
589 result
= status
.w_retcode
;
593 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
597 /* ****************************************** DoUnmount *********************************************
599 This routine will fire off a system command to unmount the given device.
601 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
603 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
604 *************************************************************************************************** */
606 DoUnmount(const char * theMountPointPtr
)
612 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
617 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
619 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
621 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
622 return (FSUR_IO_FAIL
);
626 return (FSUR_IO_FAIL
);
629 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
630 result
= status
.w_retcode
;
634 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
639 /* ******************************************* DoProbe **********************************************
641 This routine will open the given raw device and check to make sure there is media that looks
644 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
646 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
647 *************************************************************************************************** */
649 DoProbe(char *deviceNamePtr
)
651 int result
= FSUR_UNRECOGNIZED
;
654 HFSMasterDirectoryBlock
* mdbPtr
;
655 HFSPlusVolumeHeader
* volHdrPtr
;
656 u_char volnameUTF8
[NAME_MAX
+1];
658 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
660 result
= FSUR_UNRECOGNIZED
;
664 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
665 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
667 fd
= open( deviceNamePtr
, O_RDONLY
, 0 );
669 result
= FSUR_IO_FAIL
;
674 * Read the HFS Master Directory Block from sector 2
676 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
677 if (FSUR_IO_FAIL
== result
)
680 /* get classic HFS volume name (from MDB) */
681 if (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
&&
682 NXSwapBigShortToHost(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
685 CFStringEncoding encoding
;
687 /* Some poorly mastered HFS CDs have an empty MDB name field! */
688 if (mdbPtr
->drVN
[0] == '\0') {
689 strcpy(&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
690 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
693 /* Check for an encoding hint in the Finder Info (field 4). */
694 encoding
= GET_HFS_TEXT_ENCODING(NXSwapBigLongToHost(mdbPtr
->drFndrInfo
[4]));
695 if (encoding
== kCFStringEncodingInvalidId
) {
696 /* Next try the encoding bias in the kernel. */
697 encoding
= GetEncodingBias();
698 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
699 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
702 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
703 mdbPtr
->drVN
, encoding
);
704 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
707 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
709 /* default to MacRoman on conversion errors */
710 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
711 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
712 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
714 encoding
= kCFStringEncodingMacRoman
;
717 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
718 if (encoding
!= kCFStringEncodingMacRoman
)
719 result
= load_encoding(encoding
);
721 /* get HFS Plus volume name (from Catalog) */
722 } else if ((NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
723 (NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSXSigWord
) ||
724 (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
&&
725 NXSwapBigShortToHost(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
728 if (NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSSigWord
) {
729 /* embedded volume, first find offset */
730 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
731 if ( result
!= FSUR_IO_SUCCESS
)
737 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
740 result
= FSUR_UNRECOGNIZED
;
743 if (FSUR_IO_SUCCESS
== result
) {
746 /* Change slashes to colons in the volume name */
747 for (s
=volnameUTF8
; *s
; ++s
) {
752 /* Print the volume name to standard output */
753 write(1, volnameUTF8
, strlen(volnameUTF8
));
754 result
= FSUR_RECOGNIZED
;
771 /* **************************************** DoGetUUIDKey *******************************************
773 This routine will open the given block device and return the volume UUID in text form written to stdout.
775 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
777 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
778 *************************************************************************************************** */
780 DoGetUUIDKey( const char * theDeviceNamePtr
) {
782 VolumeUUID targetVolumeUUID
;
783 VolumeUUIDString UUIDString
;
784 char uuidLine
[VOLUMEUUIDLENGTH
+2];
786 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
788 ConvertVolumeUUIDToString( &targetVolumeUUID
, UUIDString
);
789 strncpy(uuidLine
, UUIDString
, VOLUMEUUIDLENGTH
+1);
790 write(1, uuidLine
, strlen(uuidLine
));
791 result
= FSUR_IO_SUCCESS
;
799 /* *************************************** DoChangeUUIDKey ******************************************
801 This routine will change the UUID on the specified block device.
803 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
805 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
806 *************************************************************************************************** */
808 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
810 VolumeUUID newVolumeUUID
;
812 GenerateVolumeUUID(&newVolumeUUID
);
813 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
820 /* **************************************** DoAdopt *******************************************
822 This routine will add the UUID of the specified block device to the list of local volumes.
824 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
826 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
827 *************************************************************************************************** */
829 DoAdopt( const char * theDeviceNamePtr
) {
830 int result
, closeresult
;
831 VolumeUUID targetVolumeUUID
;
832 VolumeStatusDBHandle vsdbhandle
= NULL
;
833 unsigned long targetVolumeStatus
;
835 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
837 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
838 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
839 targetVolumeStatus
= 0;
841 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
842 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
844 result
= FSUR_IO_SUCCESS
;
848 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
850 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
853 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
857 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
864 /* **************************************** DoDisown *******************************************
866 This routine will change the status of the specified block device to ignore its permissions.
868 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
870 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
871 *************************************************************************************************** */
873 DoDisown( const char * theDeviceNamePtr
) {
874 int result
, closeresult
;
875 VolumeUUID targetVolumeUUID
;
876 VolumeStatusDBHandle vsdbhandle
= NULL
;
877 unsigned long targetVolumeStatus
;
879 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
881 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
882 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
883 targetVolumeStatus
= 0;
885 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
886 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
888 result
= FSUR_IO_SUCCESS
;
892 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
894 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
897 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
899 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
901 result
= FSUR_IO_FAIL
;
906 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
913 get_multiplier(char c
)
915 if (tolower(c
) == 'k') {
917 } else if (tolower(c
) == 'm') {
919 } else if (tolower(c
) == 'g') {
920 return 1024 * 1024 * 1024;
926 /* **************************************** ParseArgs ********************************************
928 This routine will make sure the arguments passed in to us are cool.
929 Here is how this utility is used:
931 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
933 -p (Probe for mounting)
934 -P (Probe for initializing - not supported)
936 -r (Repair - not supported)
939 -i (Initialize - not supported)
942 disk0s2 (for example)
945 /foo/bar/ (required for Mount and Force Mount actions)
948 (these are ignored for CDROMs)
949 either "readonly" OR "writable"
950 either "removable" OR "fixed"
951 either "nosuid" or "suid"
952 either "nodev" or "dev"
955 hfs.util -p disk0s2 removable writable
956 hfs.util -p disk0s2 removable readonly
957 hfs.util -m disk0s2 /my/hfs
960 argc - the number of arguments in argv.
961 argv - array of arguments.
963 returns FSUR_INVAL if we find a bad argument else 0.
964 *************************************************************************************************** */
966 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
967 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
968 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
970 int result
= FSUR_INVAL
;
971 int deviceLength
, doLengthCheck
= 1;
975 /* Must have at least 3 arguments and the action argument must start with a '-' */
976 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
977 DoDisplayUsage( argv
);
981 /* we only support actions Probe, Mount, Force Mount, and Unmount */
983 * actionPtr
= & argv
[1][1];
985 switch ( argv
[1][1] ) {
987 /* action Probe and requires 5 arguments (need the flags) */
989 DoDisplayUsage( argv
);
997 /* Note: the device argument in argv[2] is checked further down but ignored. */
998 * mountPointPtr
= argv
[3];
999 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1003 case FSUC_MOUNT_FORCE
:
1004 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1006 DoDisplayUsage( argv
);
1009 * mountPointPtr
= argv
[3];
1035 if (isdigit(argv
[2][0])) {
1037 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1039 gJournalSize
*= get_multiplier(*ptr
);
1057 DoDisplayUsage( argv
);
1062 /* Make sure device (argv[2]) is something reasonable */
1063 deviceLength
= strlen( argv
[2] );
1064 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1065 DoDisplayUsage( argv
);
1070 /* Flags: removable/fixed. */
1071 if ( 0 == strcmp(argv
[index
],"removable") ) {
1072 * isEjectablePtr
= 1;
1073 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1074 * isEjectablePtr
= 0;
1076 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1079 /* Flags: readonly/writable. */
1080 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1082 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1085 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1089 /* Flags: suid/nosuid. */
1090 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1092 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1095 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1098 /* Flags: dev/nodev. */
1099 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1101 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1104 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1119 /* *************************************** DoDisplayUsage ********************************************
1121 This routine will do a printf of the correct usage for this utility.
1123 argv - array of arguments.
1126 *************************************************************************************************** */
1128 DoDisplayUsage(const char *argv
[])
1130 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1131 printf("action_arg:\n");
1132 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1133 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1134 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1135 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1136 #ifdef HFS_UUID_SUPPORT
1137 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1138 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1139 #endif HFS_UUID_SUPPORT
1140 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1141 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1142 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1143 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1144 printf("device_arg:\n");
1145 printf(" device we are acting upon (for example, 'disk0s2')\n");
1146 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1147 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1148 printf("mount_point_arg:\n");
1149 printf(" required for Mount and Force Mount \n");
1151 printf(" required for Mount, Force Mount and Probe\n");
1152 printf(" indicates removable or fixed (for example 'fixed')\n");
1153 printf(" indicates readonly or writable (for example 'readonly')\n");
1154 printf(" indicates suid or nosuid (for example 'suid')\n");
1155 printf(" indicates dev or nodev (for example 'dev')\n");
1156 printf("Examples:\n");
1157 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1158 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1162 } /* DoDisplayUsage */
1168 Given a path to a device, determine if a volume is mounted on that
1169 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1170 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1171 no volume mounted on the device, set *pathPtr to NULL and return
1174 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1177 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1183 /* Assume no mounted volume found */
1185 result
= FSUR_IO_SUCCESS
;
1187 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1189 return FSUR_IO_FAIL
;
1191 for (i
=0; i
<numMounts
; ++i
) {
1192 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1193 /* Found a mounted volume; check the type */
1194 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1195 *pathPtr
= buf
[i
].f_mntonname
;
1196 /* result = FSUR_IO_SUCCESS, above */
1198 result
= FSUR_UNRECOGNIZED
;
1211 Read the Master Directory Block or Volume Header Block from an HFS,
1212 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1213 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1214 Return a pointer to the volume UUID in the Finder Info.
1216 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1219 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1222 HFSMasterDirectoryBlock
* mdbPtr
;
1223 HFSPlusVolumeHeader
* volHdrPtr
;
1229 * Read the HFS Master Directory Block or Volume Header from sector 2
1232 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1233 if (result
!= FSUR_IO_SUCCESS
)
1237 * If this is a wrapped HFS Plus volume, read the Volume Header from
1238 * sector 2 of the embedded volume.
1240 if (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1241 NXSwapBigShortToHost(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1242 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1243 if (result
!= FSUR_IO_SUCCESS
)
1245 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1246 if (result
!= FSUR_IO_SUCCESS
)
1251 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1252 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1253 * UUID from the Finder Info.
1255 if (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1256 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1257 } else if (NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1258 NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSXSigWord
) {
1259 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1261 result
= FSUR_UNRECOGNIZED
;
1272 Read the UUID from an unmounted volume, by doing direct access to the device.
1273 Assumes the caller has already determined that a volume is not mounted
1276 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1279 GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1284 VolumeUUID
*finderInfoUUIDPtr
;
1287 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1289 result
= FSUR_UNRECOGNIZED
;
1293 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1296 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1298 result
= FSUR_IO_FAIL
;
1303 * Get the pointer to the volume UUID in the Finder Info
1305 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1306 if (result
!= FSUR_IO_SUCCESS
)
1310 * Copy the volume UUID out of the Finder Info
1312 volumeUUIDPtr
->v
.high
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.high
);
1313 volumeUUIDPtr
->v
.low
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.low
);
1316 if (fd
> 0) close(fd
);
1317 if (bufPtr
) free(bufPtr
);
1320 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1322 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1329 Write a previously generated UUID to an unmounted volume, by doing direct
1330 access to the device. Assumes the caller has already determined that a
1331 volume is not mounted on the device.
1333 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1336 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1341 VolumeUUID
*finderInfoUUIDPtr
;
1344 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1346 result
= FSUR_UNRECOGNIZED
;
1350 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1353 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1355 result
= FSUR_IO_FAIL
;
1360 * Get the pointer to the volume UUID in the Finder Info
1362 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1363 if (result
!= FSUR_IO_SUCCESS
)
1367 * Update the UUID in the Finder Info
1369 finderInfoUUIDPtr
->v
.high
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.high
);
1370 finderInfoUUIDPtr
->v
.low
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.low
);
1373 * Write the modified MDB or VHB back to disk
1375 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1378 if (fd
> 0) close(fd
);
1379 if (bufPtr
) free(bufPtr
);
1382 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1384 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1391 Read the UUID from a mounted volume, by calling getattrlist().
1392 Assumes the path is the mount point of an HFS volume.
1394 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1397 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1399 struct attrlist alist
;
1400 struct FinderAttrBuf volFinderInfo
;
1401 VolumeUUID
*finderInfoUUIDPtr
;
1404 /* Set up the attrlist structure to get the volume's Finder Info */
1405 alist
.bitmapcount
= 5;
1407 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1408 alist
.volattr
= ATTR_VOL_INFO
;
1413 /* Get the Finder Info */
1414 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1416 result
= FSUR_IO_FAIL
;
1420 /* Copy the UUID from the Finder Into to caller's buffer */
1421 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1422 volumeUUIDPtr
->v
.high
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.high
);
1423 volumeUUIDPtr
->v
.low
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.low
);
1424 result
= FSUR_IO_SUCCESS
;
1434 Write a UUID to a mounted volume, by calling setattrlist().
1435 Assumes the path is the mount point of an HFS volume.
1437 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1440 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1442 struct attrlist alist
;
1443 struct FinderAttrBuf volFinderInfo
;
1444 VolumeUUID
*finderInfoUUIDPtr
;
1447 /* Set up the attrlist structure to get the volume's Finder Info */
1448 alist
.bitmapcount
= 5;
1450 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1451 alist
.volattr
= ATTR_VOL_INFO
;
1456 /* Get the Finder Info */
1457 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1459 result
= FSUR_IO_FAIL
;
1463 /* Update the UUID in the Finder Info */
1464 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1465 finderInfoUUIDPtr
->v
.high
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.high
);
1466 finderInfoUUIDPtr
->v
.low
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.low
);
1468 /* Write the Finder Info back to the volume */
1469 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1471 result
= FSUR_IO_FAIL
;
1475 result
= FSUR_IO_SUCCESS
;
1485 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1486 we were asked to generate one, then generate a new UUID and write it to the
1489 Determine whether an HFS volume is mounted on the given device. If so, we
1490 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1491 the filesystem. If there is no mounted volume, then do direct device access
1492 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1494 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1498 GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1504 * Determine whether a volume is mounted on this device. If it is HFS, then
1505 * get the mount point's path. If it is non-HFS, then we can exit immediately
1506 * with FSUR_UNRECOGNIZED.
1508 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1509 if (result
!= FSUR_IO_SUCCESS
)
1513 * Get any existing UUID.
1516 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1518 result
= GetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1519 if (result
!= FSUR_IO_SUCCESS
)
1523 * If there was no valid UUID, and we were asked to generate one, then
1524 * generate it and write it back to disk.
1526 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1527 GenerateVolumeUUID(volumeUUIDPtr
);
1529 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1531 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1532 /* Fall through to Err_Exit */
1544 Write a UUID to an HFS, HFS Plus or HFSX volume.
1546 Determine whether an HFS volume is mounted on the given device. If so, we
1547 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1548 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1550 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1553 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1558 * Determine whether a volume is mounted on this device. If it is HFS, then
1559 * get the mount point's path. If it is non-HFS, then we can exit immediately
1560 * with FSUR_UNRECOGNIZED.
1562 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1563 if (result
!= FSUR_IO_SUCCESS
)
1570 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1572 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1581 -- GetEmbeddedHFSPlusVol
1583 -- In: hfsMasterDirectoryBlockPtr
1584 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1585 (that is, 2 blocks before the volume header)
1590 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1592 int result
= FSUR_IO_SUCCESS
;
1593 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1595 if (NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1596 result
= FSUR_UNRECOGNIZED
;
1600 allocationBlockSize
= NXSwapBigLongToHost(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1601 firstAllocationBlock
= NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1603 if (NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1604 result
= FSUR_UNRECOGNIZED
;
1608 startBlock
= NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1609 blockCount
= NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1611 if ( startOffsetPtr
)
1612 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1613 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1623 -- GetNameFromHFSPlusVolumeStartingAt
1625 -- Caller's responsibility to allocate and release memory for the converted string.
1627 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1631 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, char * name_o
)
1633 int result
= FSUR_IO_SUCCESS
;
1634 u_int32_t blockSize
;
1635 char * bufPtr
= NULL
;
1636 HFSPlusVolumeHeader
* volHdrPtr
;
1637 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1638 u_int32_t catalogNodeSize
;
1640 u_int32_t catalogExtCount
;
1641 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1643 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1644 if ( ! volHdrPtr
) {
1645 result
= FSUR_IO_FAIL
;
1650 * Read the Volume Header
1651 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1653 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1654 if (result
== FSUR_IO_FAIL
) {
1656 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1658 goto Return
; // return FSUR_IO_FAIL
1661 /* Verify that it is an HFS+ volume. */
1663 if (NXSwapBigShortToHost(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1664 NXSwapBigShortToHost(volHdrPtr
->signature
) != kHFSXSigWord
) {
1665 result
= FSUR_IO_FAIL
;
1667 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1672 blockSize
= NXSwapBigLongToHost(volHdrPtr
->blockSize
);
1673 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1674 if ( ! catalogExtents
) {
1675 result
= FSUR_IO_FAIL
;
1678 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1679 catalogExtCount
= kHFSPlusExtentDensity
;
1681 /* if there are overflow catalog extents, then go get them */
1682 if (NXSwapBigLongToHost(catalogExtents
[7].blockCount
) != 0) {
1683 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1684 if (result
!= FSUR_IO_SUCCESS
)
1688 /* Read the header node of the catalog B-Tree */
1690 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1691 catalogExtCount
, catalogExtents
,
1692 &catalogNodeSize
, &leafNode
);
1693 if (result
!= FSUR_IO_SUCCESS
)
1696 /* Read the first leaf node of the catalog b-tree */
1698 bufPtr
= (char *)malloc(catalogNodeSize
);
1700 result
= FSUR_IO_FAIL
;
1704 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1706 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1707 hfsPlusVolumeOffset
, blockSize
,
1708 catalogExtCount
, catalogExtents
);
1709 if (result
== FSUR_IO_FAIL
) {
1711 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1713 goto Return
; // return FSUR_IO_FAIL
1719 HFSPlusCatalogKey
* k
;
1722 if ( bTreeNodeDescriptorPtr
->numRecords
< 1) {
1723 result
= FSUR_IO_FAIL
;
1725 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1730 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1732 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1735 // Get a pointer to the first record.
1737 p
= bufPtr
+ NXSwapBigShortToHost(*v
); // pointer arithmetic in bytes
1738 k
= (HFSPlusCatalogKey
*)p
;
1740 // There should be only one record whose parent is the root parent. It should be the first record.
1742 if (NXSwapBigLongToHost(k
->parentID
) != kHFSRootParentID
) {
1743 result
= FSUR_IO_FAIL
;
1745 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1750 /* Extract the name of the root directory */
1753 HFSUniStr255
*swapped
;
1756 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1757 if (swapped
== NULL
) {
1758 result
= FSUR_IO_FAIL
;
1761 swapped
->length
= NXSwapBigShortToHost(k
->nodeName
.length
);
1763 for (i
=0; i
<swapped
->length
; i
++) {
1764 swapped
->unicode
[i
] = NXSwapBigShortToHost(k
->nodeName
.unicode
[i
]);
1766 swapped
->unicode
[i
] = 0;
1767 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1768 (void) CFStringGetCString(cfstr
, name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1774 result
= FSUR_IO_SUCCESS
;
1778 free((char*) volHdrPtr
);
1781 free((char*) catalogExtents
);
1784 free((char*)bufPtr
);
1788 } /* GetNameFromHFSPlusVolumeStartingAt */
1791 #pragma options align=mac68k
1793 BTNodeDescriptor node
;
1795 } HeaderRec
, *HeaderPtr
;
1796 #pragma options align=reset
1801 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1805 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1806 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1807 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1810 HeaderRec
* bTreeHeaderPtr
= NULL
;
1812 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1813 if (bTreeHeaderPtr
== NULL
)
1814 return (FSUR_IO_FAIL
);
1816 /* Read the b-tree header node */
1818 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
1819 hfsPlusVolumeOffset
, blockSize
,
1820 extentCount
, extentList
);
1821 if ( result
== FSUR_IO_FAIL
) {
1823 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
1828 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
1829 result
= FSUR_IO_FAIL
;
1831 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1836 *nodeSize
= NXSwapBigShortToHost(bTreeHeaderPtr
->header
.nodeSize
);
1838 if (NXSwapBigLongToHost(bTreeHeaderPtr
->header
.leafRecords
) == 0)
1841 *firstLeafNode
= NXSwapBigLongToHost(bTreeHeaderPtr
->header
.firstLeafNode
);
1844 free((char*) bTreeHeaderPtr
);
1848 } /* GetBTreeNodeInfo */
1854 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1858 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
1859 HFSPlusVolumeHeader
*volHdrPtr
,
1860 HFSPlusExtentDescriptor
**catalogExtents
,
1861 u_int32_t
*catalogExtCount
)
1866 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1867 HFSPlusExtentDescriptor
* extents
;
1869 char * bufPtr
= NULL
;
1873 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
1874 extents
= *catalogExtents
;
1875 offset
= (off_t
)volHdrPtr
->extentsFile
.extents
[0].startBlock
*
1876 (off_t
)volHdrPtr
->blockSize
;
1878 /* Read the header node of the extents B-Tree */
1880 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, volHdrPtr
->blockSize
,
1881 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
1882 &nodeSize
, &leafNode
);
1883 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
1886 /* Calculate the logical position of the first leaf node */
1888 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
1890 /* Read the first leaf node of the extents b-tree */
1892 bufPtr
= (char *)malloc(nodeSize
);
1894 result
= FSUR_IO_FAIL
;
1898 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1901 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
1902 hfsPlusVolumeOffset
, volHdrPtr
->blockSize
,
1903 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
1904 if ( result
== FSUR_IO_FAIL
) {
1906 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1911 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
1912 result
= FSUR_IO_FAIL
;
1916 for (i
= 1; i
<= bTreeNodeDescriptorPtr
->numRecords
; ++i
) {
1919 HFSPlusExtentKey
* k
;
1922 * Get the offset (in bytes) of the record from the
1923 * list of offsets at the end of the node
1925 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
1928 /* Get a pointer to the record */
1930 p
= bufPtr
+ NXSwapBigShortToHost(*v
); /* pointer arithmetic in bytes */
1931 k
= (HFSPlusExtentKey
*)p
;
1933 if (NXSwapBigLongToHost(k
->fileID
) != kHFSCatalogFileID
)
1936 /* grow list and copy additional extents */
1937 listsize
+= sizeof(HFSPlusExtentRecord
);
1938 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
1939 bcopy(p
+ k
->keyLength
+ sizeof(u_int16_t
),
1940 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
1942 *catalogExtCount
+= kHFSPlusExtentDensity
;
1943 *catalogExtents
= extents
;
1946 if ((leafNode
= bTreeNodeDescriptorPtr
->fLink
) != 0) {
1948 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
1963 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
1964 * position and number of contiguous bytes at that position.
1967 * logicalOffset Logical offset in bytes from start of file
1968 * length Maximum number of bytes to map
1969 * blockSize Number of bytes per allocation block
1970 * extentCount Number of extents in file
1971 * extentList The file's extents
1974 * physicalOffset Physical offset in bytes from start of volume
1975 * availableBytes Number of bytes physically contiguous (up to length)
1977 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1979 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
1980 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1981 off_t
*physicalOffset
, ssize_t
*availableBytes
)
1984 u_int32_t logicalBlock
;
1986 u_int32_t blockCount
= 0;
1988 /* Determine allocation block containing logicalOffset */
1989 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
1990 offset
%= blockSize
; /* Offset from start of allocation block */
1992 /* Find the extent containing logicalBlock */
1993 for (extent
= 0; extent
< extentCount
; ++extent
)
1995 blockCount
= NXSwapBigLongToHost(extentList
[extent
].blockCount
);
1997 if (blockCount
== 0)
1998 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2000 if (logicalBlock
< blockCount
)
2001 break; /* Found it! */
2003 logicalBlock
-= blockCount
;
2006 if (extent
>= extentCount
)
2007 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2010 * When we get here, extentList[extent] is the extent containing logicalOffset.
2011 * The desired allocation block is logicalBlock blocks into the extent.
2014 /* Compute the physical starting position */
2015 temp
= NXSwapBigLongToHost(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2016 temp
*= blockSize
; /* Byte offset of first physical block */
2017 *physicalOffset
= temp
+ offset
;
2019 /* Compute the available contiguous bytes. */
2020 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2022 temp
-= offset
; /* Number of bytes available */
2025 *availableBytes
= temp
;
2027 *availableBytes
= length
;
2029 return FSUR_IO_SUCCESS
;
2035 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2036 * ending position are not allocation or device block aligned.
2039 * fd Descriptor for reading the volume
2040 * buffer The bytes are read into here
2041 * offset Offset in file to start reading
2042 * length Number of bytes to read
2043 * volOffset Byte offset from start of device to start of volume
2044 * blockSize Number of bytes per allocation block
2045 * extentCount Number of extents in file
2046 * extentList The file's exents
2048 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2050 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2051 off_t volOffset
, u_int32_t blockSize
,
2052 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2054 int result
= FSUR_IO_SUCCESS
;
2060 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2061 &physOffset
, &physLength
);
2062 if (result
!= FSUR_IO_SUCCESS
)
2065 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2066 if (result
!= FSUR_IO_SUCCESS
)
2069 length
-= physLength
;
2070 offset
+= physLength
;
2071 buffer
= (char *) buffer
+ physLength
;
2078 -- readAt = lseek() + read()
2080 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2085 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2090 void * rawData
= NULL
;
2093 ssize_t dataOffset
= 0;
2094 int result
= FSUR_IO_SUCCESS
;
2096 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2098 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2100 result
= FSUR_IO_FAIL
;
2103 /* put offset and length in terms of device blocksize */
2104 rawOffset
= offset
/ blocksize
* blocksize
;
2105 dataOffset
= offset
- rawOffset
;
2106 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2107 rawData
= malloc(rawLength
);
2108 if (rawData
== NULL
) {
2109 result
= FSUR_IO_FAIL
;
2113 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2114 if ( lseekResult
!= rawOffset
) {
2115 result
= FSUR_IO_FAIL
;
2119 readResult
= read(fd
, rawData
, rawLength
);
2120 if ( readResult
!= rawLength
) {
2122 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2124 result
= FSUR_IO_FAIL
;
2127 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2138 -- writeAt = lseek() + write()
2140 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2145 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2149 ssize_t bytestransferred
;
2150 void * rawData
= NULL
;
2153 ssize_t dataOffset
= 0;
2154 int result
= FSUR_IO_SUCCESS
;
2156 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2158 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2160 result
= FSUR_IO_FAIL
;
2163 /* put offset and length in terms of device blocksize */
2164 rawOffset
= offset
/ blocksize
* blocksize
;
2165 dataOffset
= offset
- rawOffset
;
2166 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2167 rawData
= malloc(rawLength
);
2168 if (rawData
== NULL
) {
2169 result
= FSUR_IO_FAIL
;
2173 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2174 if ( deviceoffset
!= rawOffset
) {
2175 result
= FSUR_IO_FAIL
;
2179 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2180 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2181 bytestransferred
= read(fd
, rawData
, rawLength
);
2182 if ( bytestransferred
!= rawLength
) {
2184 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2186 result
= FSUR_IO_FAIL
;
2191 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2193 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2194 if ( deviceoffset
!= rawOffset
) {
2195 result
= FSUR_IO_FAIL
;
2199 bytestransferred
= write(fd
, rawData
, rawLength
);
2200 if ( bytestransferred
!= rawLength
) {
2202 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2204 result
= FSUR_IO_FAIL
;
2209 if (rawData
) free(rawData
);
2217 * Get kernel's encoding bias.
2223 size_t buflen
= sizeof(int);
2227 if (getvfsbyname("hfs", &vfc
) < 0)
2231 mib
[1] = vfc
.vfc_typenum
;
2232 mib
[2] = HFS_ENCODINGBIAS
;
2234 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2241 /******************************************************************************
2243 * 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
2245 *****************************************************************************/
2247 #define DBHANDLESIGNATURE 0x75917737
2249 /* Flag values for operation options: */
2250 #define DBMARKPOSITION 1
2252 static char gVSDBPath
[] = "/var/db/volinfo.database";
2254 #define MAXIOMALLOC 16384
2256 /* Database layout: */
2263 char statusFlags
[8];
2270 struct VSDBRecord record
;
2274 #define DBKEYSEPARATOR ':'
2275 #define DBBLANKSPACE ' '
2276 #define DBRECORDTERMINATOR '\n'
2278 /* In-memory data structures: */
2281 unsigned long signature
;
2284 off_t recordPosition
;
2287 typedef struct VSDBState
*VSDBStatePtr
;
2291 /* Internal function prototypes: */
2292 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2293 static int UnlockDB(VSDBStatePtr dbstateptr
);
2295 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2296 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2297 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2298 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2299 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2301 static void FormatULong(unsigned long u
, char *s
);
2302 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2303 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2304 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2305 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2306 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2310 /******************************************************************************
2312 * 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
2314 *****************************************************************************/
2316 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2318 char randomInputBuffer
[26];
2319 unsigned char digest
[20];
2324 char sysctlstring
[128];
2326 struct loadavg sysloadavg
;
2327 struct vmtotal sysvmtotal
;
2330 /* Initialize the SHA-1 context for processing: */
2331 SHA1_Init(&context
);
2333 /* Now process successive bits of "random" input to seed the process: */
2335 /* The current system's uptime: */
2337 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2339 /* The kernel's boot time: */
2341 mib
[1] = KERN_BOOTTIME
;
2342 datalen
= sizeof(sysdata
);
2343 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2344 SHA1_Update(&context
, &sysdata
, datalen
);
2346 /* The system's host id: */
2348 mib
[1] = KERN_HOSTID
;
2349 datalen
= sizeof(sysdata
);
2350 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2351 SHA1_Update(&context
, &sysdata
, datalen
);
2353 /* The system's host name: */
2355 mib
[1] = KERN_HOSTNAME
;
2356 datalen
= sizeof(sysctlstring
);
2357 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2358 SHA1_Update(&context
, sysctlstring
, datalen
);
2360 /* The running kernel's OS release string: */
2362 mib
[1] = KERN_OSRELEASE
;
2363 datalen
= sizeof(sysctlstring
);
2364 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2365 SHA1_Update(&context
, sysctlstring
, datalen
);
2367 /* The running kernel's version string: */
2369 mib
[1] = KERN_VERSION
;
2370 datalen
= sizeof(sysctlstring
);
2371 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2372 SHA1_Update(&context
, sysctlstring
, datalen
);
2374 /* The system's load average: */
2376 mib
[1] = VM_LOADAVG
;
2377 datalen
= sizeof(sysloadavg
);
2378 sysctl(mib
, 2, &sysloadavg
, &datalen
, NULL
, 0);
2379 SHA1_Update(&context
, &sysloadavg
, datalen
);
2381 /* The system's VM statistics: */
2384 datalen
= sizeof(sysvmtotal
);
2385 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2386 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2388 /* The current GMT (26 ASCII characters): */
2390 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2391 SHA1_Update(&context
, randomInputBuffer
, 26);
2393 /* Pad the accumulated input and extract the final digest hash: */
2394 SHA1_Final(digest
, &context
);
2396 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2397 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2402 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2405 unsigned long nextdigit
;
2406 unsigned long high
= 0;
2407 unsigned long low
= 0;
2408 unsigned long carry
;
2410 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2411 if ((c
>= '0') && (c
<= '9')) {
2412 nextdigit
= c
- '0';
2413 } else if ((c
>= 'A') && (c
<= 'F')) {
2414 nextdigit
= c
- 'A' + 10;
2415 } else if ((c
>= 'a') && (c
<= 'f')) {
2416 nextdigit
= c
- 'a' + 10;
2420 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2421 high
= (high
<< 4) | carry
;
2422 low
= (low
<< 4) | nextdigit
;
2425 volumeID
->v
.high
= high
;
2426 volumeID
->v
.low
= low
;
2431 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2432 FormatUUID(volumeID
, UUIDString
);
2433 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2438 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2439 VSDBStatePtr dbstateptr
;
2441 *DBHandlePtr
= NULL
;
2443 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2444 if (dbstateptr
== NULL
) {
2448 dbstateptr
->dbmode
= O_RDWR
;
2449 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2450 if (dbstateptr
->dbfile
== -1) {
2452 The file couldn't be opened for read/write access:
2453 try read-only access before giving up altogether.
2455 dbstateptr
->dbmode
= O_RDONLY
;
2456 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2457 if (dbstateptr
->dbfile
== -1) {
2462 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2463 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2469 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2470 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2471 struct VSDBEntry dbentry
;
2474 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2476 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2478 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2481 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2486 UnlockDB(dbstateptr
);
2492 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2493 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2494 struct VSDBEntry dbentry
;
2497 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2498 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2500 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2502 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2503 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2505 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2507 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2508 } else if (result
== -1) {
2510 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2512 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2517 fsync(dbstateptr
->dbfile
);
2522 UnlockDB(dbstateptr
);
2528 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2529 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2532 unsigned long iobuffersize
;
2533 void *iobuffer
= NULL
;
2535 unsigned long iotransfersize
;
2536 unsigned long bytestransferred
;
2538 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2540 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2542 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2544 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2546 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2550 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2552 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2553 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2554 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2556 iobuffersize
= MAXIOMALLOC
;
2559 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2560 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2561 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2563 if (iobuffersize
> 0) {
2564 iobuffer
= malloc(iobuffersize
);
2565 if (iobuffer
== NULL
) {
2570 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2572 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2573 if (iotransfersize
> 0) {
2574 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2577 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2579 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2580 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2581 if (bytestransferred
!= iotransfersize
) {
2587 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2589 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2590 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2591 if (bytestransferred
!= iotransfersize
) {
2596 dataoffset
+= (off_t
)iotransfersize
;
2598 } while (iotransfersize
> 0);
2601 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2603 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2607 fsync(dbstateptr
->dbfile
);
2613 if (iobuffer
) free(iobuffer
);
2614 UnlockDB(dbstateptr
);
2622 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2623 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2625 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2627 dbstateptr
->signature
= 0;
2629 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2630 dbstateptr
->dbfile
= 0;
2639 /******************************************************************************
2641 * 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
2643 *****************************************************************************/
2645 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2647 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2649 return flock(dbstateptr
->dbfile
, lockmode
);
2654 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2656 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2658 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2663 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2664 struct VSDBKey searchkey
;
2665 struct VSDBEntry dbentry
;
2668 FormatDBKey(volumeID
, &searchkey
);
2669 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2672 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2673 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2674 if (targetEntry
!= NULL
) {
2676 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2678 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2682 } while (result
== 0);
2689 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2691 VolumeUUIDString id
;
2695 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2696 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2697 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2699 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2700 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2706 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2708 VolumeUUIDString id
;
2712 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2713 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2714 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2716 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2718 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2720 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2725 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2726 struct VSDBEntry entry
;
2729 VolumeUUIDString id
;
2732 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2733 #if 0 // DEBUG_TRACE
2734 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2736 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2737 if ((result
!= sizeof(entry
)) ||
2738 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2739 (entry
.space
!= DBBLANKSPACE
) ||
2740 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2745 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2746 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2747 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2749 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2755 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2756 #if 0 // DEBUG_TRACE
2757 VolumeUUIDString id
;
2759 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2760 id
[sizeof(key1
->uuid
)] = (char)0;
2761 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2762 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2763 id
[sizeof(key2
->uuid
)] = (char)0;
2764 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2767 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2772 /******************************************************************************
2774 * 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
2776 *****************************************************************************/
2778 static void FormatULong(unsigned long u
, char *s
) {
2783 for (i
= 0; i
< 8; ++i
) {
2784 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2786 *digitptr
++ = (char)(d
+ '0');
2788 *digitptr
++ = (char)(d
- 10 + 'A');
2796 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2797 FormatULong(volumeID
->v
.high
, UUIDField
);
2798 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2804 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2805 FormatUUID(volumeID
, dbkey
->uuid
);
2810 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2811 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
2816 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
2817 FormatDBKey(volumeID
, &dbentry
->key
);
2818 dbentry
->keySeparator
= DBKEYSEPARATOR
;
2819 dbentry
->space
= DBBLANKSPACE
;
2820 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
2821 #if 0 // DEBUG_TRACE
2822 dbentry
->terminator
= (char)0;
2823 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
2825 dbentry
->terminator
= DBRECORDTERMINATOR
;
2830 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
2833 unsigned long nextdigit
;
2837 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
2838 if ((c
>= '0') && (c
<= '9')) {
2839 nextdigit
= c
- '0';
2840 } else if ((c
>= 'A') && (c
<= 'F')) {
2841 nextdigit
= c
- 'A' + 10;
2842 } else if ((c
>= 'a') && (c
<= 'f')) {
2843 nextdigit
= c
- 'a' + 10;
2847 n
= (n
<< 4) + nextdigit
;