2 * Copyright (c) 1999-2004 Apple Computer, 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>
68 #include <openssl/sha.h>
70 #include <architecture/byte_order.h>
72 #include <CoreFoundation/CFString.h>
74 #define READ_DEFAULT_ENCODING 1
77 #define FSUC_ADOPT 'a'
81 #define FSUC_DISOWN 'd'
85 #define FSUC_GETUUID 'k'
89 #define FSUC_SETUUID 's'
93 #define FSUC_MKJNL 'J'
97 #define FSUC_UNJNL 'U'
101 #define FSUC_JNLINFO 'I'
105 /* **************************************** L O C A L S ******************************************* */
107 #define HFS_BLOCK_SIZE 512
109 char gHFS_FS_NAME
[] = "hfs";
110 char gHFS_FS_NAME_NAME
[] = "HFS";
112 char gNewlineString
[] = "\n";
114 char gMountCommand
[] = "/sbin/mount";
116 char gUnmountCommand
[] = "/sbin/umount";
118 char gReadOnlyOption
[] = "-r";
119 char gReadWriteOption
[] = "-w";
121 char gSuidOption
[] = "suid";
122 char gNoSuidOption
[] = "nosuid";
124 char gDevOption
[] = "dev";
125 char gNoDevOption
[] = "nodev";
127 char gUsePermissionsOption
[] = "perm";
128 char gIgnorePermissionsOption
[] = "noperm";
130 boolean_t gIsEjectable
= 0;
132 int gJournalSize
= 0;
134 #define AUTO_ADOPT_FIXED 1
135 #define AUTO_ENTER_FIXED 0
138 struct FinderAttrBuf
{
139 unsigned long info_length
;
140 unsigned long finderinfo
[8];
144 #define VOLUMEUUIDVALUESIZE 2
145 typedef union VolumeUUID
{
146 unsigned long value
[VOLUMEUUIDVALUESIZE
];
153 #define VOLUMEUUIDLENGTH 16
154 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1];
156 #define VOLUME_RECORDED 0x80000000
157 #define VOLUME_USEPERMISSIONS 0x00000001
158 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
160 typedef void *VolumeStatusDBHandle
;
162 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
163 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
);
164 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
);
165 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
166 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
);
167 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
);
168 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
169 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
171 /* ************************************ P R O T O T Y P E S *************************************** */
172 static void DoDisplayUsage( const char * argv
[] );
173 static int DoMount( char * theDeviceNamePtr
, const char * theMountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
);
174 static int DoProbe( char * theDeviceNamePtr
);
175 static int DoUnmount( const char * theMountPointPtr
);
176 static int DoGetUUIDKey( const char * theDeviceNamePtr
);
177 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
178 static int DoAdopt( const char * theDeviceNamePtr
);
179 static int DoDisown( const char * theDeviceNamePtr
);
181 extern int DoMakeJournaled( const char * volNamePtr
, int journalSize
); // XXXdbg
182 extern int DoUnJournal( const char * volNamePtr
); // XXXdbg
183 extern int DoGetJournalInfo( const char * volNamePtr
);
185 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
);
187 static int GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
);
188 static int ReadHeaderBlock(int fd
, void *bufptr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
);
189 static int GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
190 static int GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
191 static int GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
192 static int SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
193 static int SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
);
194 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
195 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
196 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
);
197 static int GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
198 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
199 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
200 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
201 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
202 static int LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
,
203 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
204 off_t
*physicalOffset
, ssize_t
*availableBytes
);
205 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
206 off_t volOffset
, u_int32_t blockSize
,
207 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
);
208 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
209 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
211 static int GetEncodingBias(void);
214 CF_EXPORT Boolean
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8
*buffer
, CFIndex maxBufLen
);
217 * The fuction CFStringGetSystemEncoding does not work correctly in
218 * our context (autodiskmount deamon). We include a local copy here
219 * so that we can derive the default encoding. Radar 2516316.
221 #if READ_DEFAULT_ENCODING
222 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
224 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
225 struct passwd
*passwdp
;
227 if ((passwdp
= getpwuid(0))) { // root account
228 char buffer
[MAXPATHLEN
+ 1];
231 strcpy(buffer
, passwdp
->pw_dir
);
232 strcat(buffer
, __kCFUserEncodingFileName
);
234 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
237 readSize
= read(fd
, buffer
, MAXPATHLEN
);
238 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
240 return strtol(buffer
, NULL
, 0);
243 return 0; // Fallback to smRoman
248 #define MXENCDNAMELEN 16 /* Maximun length of encoding name string */
250 struct hfs_mnt_encoding
{
251 char encoding_name
[MXENCDNAMELEN
]; /* encoding type name */
252 CFStringEncoding encoding_id
; /* encoding type number */
255 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = {
261 { "CentralEurRoman", 29 },
262 { "ChineseSimp", 25 },
263 { "ChineseTrad", 2 },
284 { "Roman", 0 }, /* default */
292 { "Ukrainian", 152 },
293 { "Vietnamese", 30 },
296 #define KEXT_LOAD_COMMAND "/sbin/kextload"
297 #define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Encodings/"
299 static int load_encoding(CFStringEncoding encoding
)
307 char kmodfile
[MAXPATHLEN
];
309 /* Find the encoding that matches the one passed in */
310 numEncodings
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
);
312 for (i
=0; i
<numEncodings
; ++i
)
314 if (hfs_mnt_encodinglist
[i
].encoding_id
== encoding
)
316 encodingName
= hfs_mnt_encodinglist
[i
].encoding_name
;
321 if (encodingName
== NULL
)
323 /* Couldn't figure out which encoding KEXT to load */
324 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
);
328 sprintf(kmodfile
, "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
);
329 if (stat(kmodfile
, &sb
) == -1)
331 /* We recognized the encoding, but couldn't find the KEXT */
332 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
));
339 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
);
341 exit(1); /* We can only get here if the exec failed */
345 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
))
347 if (WEXITSTATUS(status
) != 0)
349 /* kextload returned an error. Too bad its output doesn't get logged. */
350 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
);
356 return FSUR_IO_SUCCESS
;
360 /* ******************************************** main ************************************************
362 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
363 for detail info on input arguments.
365 argc - the number of arguments in argv.
366 argv - array of arguments.
368 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
369 *************************************************************************************************** */
371 int main (int argc
, const char *argv
[])
373 const char * actionPtr
= NULL
;
374 char rawDeviceName
[MAXPATHLEN
];
375 char blockDeviceName
[MAXPATHLEN
];
376 const char * mountPointPtr
= NULL
;
377 int result
= FSUR_IO_SUCCESS
;
378 boolean_t isLocked
= 0; /* reasonable assumptions */
379 boolean_t isSetuid
= 0; /* reasonable assumptions */
380 boolean_t isDev
= 0; /* reasonable assumptions */
382 openlog("hfs.util", LOG_PID
, LOG_DAEMON
);
384 /* Verify our arguments */
385 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev
)) != 0 ) {
390 -- Build our device name (full path), should end up with something like:
394 sprintf(rawDeviceName
, "/dev/r%s", argv
[2]);
395 sprintf(blockDeviceName
, "/dev/%s", argv
[2]);
397 /* call the appropriate routine to handle the given action argument after becoming root */
399 switch( * actionPtr
) {
401 result
= DoProbe(rawDeviceName
);
405 case FSUC_MOUNT_FORCE
:
406 result
= DoMount(blockDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
);
410 result
= DoUnmount( mountPointPtr
);
413 result
= DoGetUUIDKey( blockDeviceName
);
417 result
= DoChangeUUIDKey( blockDeviceName
);
420 result
= DoAdopt( blockDeviceName
);
424 result
= DoDisown( blockDeviceName
);
429 result
= DoMakeJournaled( argv
[3], gJournalSize
);
431 result
= DoMakeJournaled( argv
[2], gJournalSize
);
436 result
= DoUnJournal( argv
[2] );
440 result
= DoGetJournalInfo( argv
[2] );
444 /* should never get here since ParseArgs should handle this situation */
445 DoDisplayUsage( argv
);
454 return result
; /*...and make main fit the ANSI spec. */
458 /* ***************************** DoMount ********************************
460 This routine will fire off a system command to mount the given device at the given mountpoint.
461 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
463 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
464 mountPointPtr - pointer to the mount point.
467 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
468 *********************************************************************** */
470 DoMount(char *deviceNamePtr
, const char *mountPointPtr
, boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
)
476 char *permissionsOption
;
477 int result
= FSUR_IO_FAIL
;
479 char encodeopt
[16] = "";
480 CFStringEncoding encoding
;
481 VolumeUUID targetVolumeUUID
;
482 VolumeStatusDBHandle vsdbhandle
= NULL
;
483 unsigned long targetVolumeStatus
;
485 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
486 return (FSUR_IO_FAIL
);
488 /* get the volume UUID to check if permissions should be used: */
489 targetVolumeStatus
= 0;
490 if (((result
= GetVolumeUUID(deviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
491 (targetVolumeUUID
.v
.high
==0) ||
492 (targetVolumeUUID
.v
.low
== 0)) {
494 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
497 if (gIsEjectable
== 0) {
498 result
= DoAdopt( deviceNamePtr
);
500 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
502 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
505 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
507 targetVolumeStatus
= 0;
511 /* We've got a real volume UUID! */
513 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
515 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
516 /* Can't even get access to the volume info db; assume permissions are OK. */
518 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
520 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
523 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
525 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
527 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
530 if (gIsEjectable
== 0) {
531 result
= DoAdopt( deviceNamePtr
);
533 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
535 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
538 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
540 targetVolumeStatus
= 0;
543 targetVolumeStatus
= 0;
546 (void)CloseVolumeStatusDB(vsdbhandle
);
553 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
554 isSetuidstr
= isSetuid
? gSuidOption
: gNoSuidOption
;
555 isDevstr
= isDev
? gDevOption
: gNoDevOption
;
558 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
560 /* get default encoding value (for hfs volumes) */
561 #if READ_DEFAULT_ENCODING
562 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
564 encoding
= CFStringGetSystemEncoding();
566 sprintf(encodeopt
, "-e=%d", (int)encoding
);
568 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
569 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
571 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
,
572 "-o", encodeopt
, "-o", permissionsOption
,
573 "-o", "-u=unknown,-g=unknown,-m=0777",
574 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
577 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
578 return (FSUR_IO_FAIL
);
582 return (FSUR_IO_FAIL
);
585 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
586 result
= status
.w_retcode
;
590 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
594 /* ****************************************** DoUnmount *********************************************
596 This routine will fire off a system command to unmount the given device.
598 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
600 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
601 *************************************************************************************************** */
603 DoUnmount(const char * theMountPointPtr
)
609 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
614 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
616 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
618 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
619 return (FSUR_IO_FAIL
);
623 return (FSUR_IO_FAIL
);
626 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
627 result
= status
.w_retcode
;
631 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
636 /* ******************************************* DoProbe **********************************************
638 This routine will open the given raw device and check to make sure there is media that looks
641 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
643 returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes.
644 *************************************************************************************************** */
646 DoProbe(char *deviceNamePtr
)
648 int result
= FSUR_UNRECOGNIZED
;
651 HFSMasterDirectoryBlock
* mdbPtr
;
652 HFSPlusVolumeHeader
* volHdrPtr
;
653 u_char volnameUTF8
[NAME_MAX
+1];
655 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
657 result
= FSUR_UNRECOGNIZED
;
661 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
662 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
664 fd
= open( deviceNamePtr
, O_RDONLY
, 0 );
666 result
= FSUR_IO_FAIL
;
671 * Read the HFS Master Directory Block from sector 2
673 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
674 if (FSUR_IO_FAIL
== result
)
677 /* get classic HFS volume name (from MDB) */
678 if (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
&&
679 NXSwapBigShortToHost(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
682 CFStringEncoding encoding
;
684 /* Some poorly mastered HFS CDs have an empty MDB name field! */
685 if (mdbPtr
->drVN
[0] == '\0') {
686 strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
);
687 mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
);
690 /* Check for an encoding hint in the Finder Info (field 4). */
691 encoding
= GET_HFS_TEXT_ENCODING(NXSwapBigLongToHost(mdbPtr
->drFndrInfo
[4]));
692 if (encoding
== kCFStringEncodingInvalidId
) {
693 /* Next try the encoding bias in the kernel. */
694 encoding
= GetEncodingBias();
695 if (encoding
== 0 || encoding
== kCFStringEncodingInvalidId
)
696 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
699 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
700 mdbPtr
->drVN
, encoding
);
701 cfOK
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
704 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
706 /* default to MacRoman on conversion errors */
707 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
708 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
709 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
711 encoding
= kCFStringEncodingMacRoman
;
714 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */
715 if (encoding
!= kCFStringEncodingMacRoman
) {
716 if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) {
717 encoding
= kCFStringEncodingMacRoman
;
718 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
);
719 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
);
724 /* get HFS Plus volume name (from Catalog) */
725 } else if ((NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSPlusSigWord
) ||
726 (NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSXSigWord
) ||
727 (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
&&
728 NXSwapBigShortToHost(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) {
731 if (NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSSigWord
) {
732 /* embedded volume, first find offset */
733 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
734 if ( result
!= FSUR_IO_SUCCESS
)
740 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
743 result
= FSUR_UNRECOGNIZED
;
746 if (FSUR_IO_SUCCESS
== result
) {
749 /* Change slashes to colons in the volume name */
750 for (s
=volnameUTF8
; *s
; ++s
) {
755 /* Print the volume name to standard output */
756 write(1, volnameUTF8
, strlen((char *)volnameUTF8
));
757 result
= FSUR_RECOGNIZED
;
774 /* **************************************** DoGetUUIDKey *******************************************
776 This routine will open the given block device and return the volume UUID in text form written to stdout.
778 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
780 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
781 *************************************************************************************************** */
783 DoGetUUIDKey( const char * theDeviceNamePtr
) {
785 VolumeUUID targetVolumeUUID
;
786 VolumeUUIDString UUIDString
;
787 char uuidLine
[VOLUMEUUIDLENGTH
+2];
789 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
791 ConvertVolumeUUIDToString( &targetVolumeUUID
, UUIDString
);
792 strncpy(uuidLine
, UUIDString
, VOLUMEUUIDLENGTH
+1);
793 write(1, uuidLine
, strlen(uuidLine
));
794 result
= FSUR_IO_SUCCESS
;
802 /* *************************************** DoChangeUUIDKey ******************************************
804 This routine will change the UUID on the specified block device.
806 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
808 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
809 *************************************************************************************************** */
811 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
813 VolumeUUID newVolumeUUID
;
815 GenerateVolumeUUID(&newVolumeUUID
);
816 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
823 /* **************************************** DoAdopt *******************************************
825 This routine will add the UUID of the specified block device to the list of local volumes.
827 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
829 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
830 *************************************************************************************************** */
832 DoAdopt( const char * theDeviceNamePtr
) {
833 int result
, closeresult
;
834 VolumeUUID targetVolumeUUID
;
835 VolumeStatusDBHandle vsdbhandle
= NULL
;
836 unsigned long targetVolumeStatus
;
838 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
840 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
841 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
842 targetVolumeStatus
= 0;
844 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
845 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
847 result
= FSUR_IO_SUCCESS
;
851 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
853 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
856 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
860 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
867 /* **************************************** DoDisown *******************************************
869 This routine will change the status of the specified block device to ignore its permissions.
871 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
873 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
874 *************************************************************************************************** */
876 DoDisown( const char * theDeviceNamePtr
) {
877 int result
, closeresult
;
878 VolumeUUID targetVolumeUUID
;
879 VolumeStatusDBHandle vsdbhandle
= NULL
;
880 unsigned long targetVolumeStatus
;
882 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
884 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
885 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
886 targetVolumeStatus
= 0;
888 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
889 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
891 result
= FSUR_IO_SUCCESS
;
895 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
897 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
900 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
902 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
904 result
= FSUR_IO_FAIL
;
909 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
916 get_multiplier(char c
)
918 if (tolower(c
) == 'k') {
920 } else if (tolower(c
) == 'm') {
922 } else if (tolower(c
) == 'g') {
923 return 1024 * 1024 * 1024;
929 /* **************************************** ParseArgs ********************************************
931 This routine will make sure the arguments passed in to us are cool.
932 Here is how this utility is used:
934 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
936 -p (Probe for mounting)
937 -P (Probe for initializing - not supported)
939 -r (Repair - not supported)
942 -i (Initialize - not supported)
945 disk0s2 (for example)
948 /foo/bar/ (required for Mount and Force Mount actions)
951 (these are ignored for CDROMs)
952 either "readonly" OR "writable"
953 either "removable" OR "fixed"
954 either "nosuid" or "suid"
955 either "nodev" or "dev"
958 hfs.util -p disk0s2 removable writable
959 hfs.util -p disk0s2 removable readonly
960 hfs.util -m disk0s2 /my/hfs
963 argc - the number of arguments in argv.
964 argv - array of arguments.
966 returns FSUR_INVAL if we find a bad argument else 0.
967 *************************************************************************************************** */
969 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
970 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
971 boolean_t
* isLockedPtr
, boolean_t
* isSetuidPtr
, boolean_t
* isDevPtr
)
973 int result
= FSUR_INVAL
;
974 int deviceLength
, doLengthCheck
= 1;
978 /* Must have at least 3 arguments and the action argument must start with a '-' */
979 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
980 DoDisplayUsage( argv
);
984 /* we only support actions Probe, Mount, Force Mount, and Unmount */
986 * actionPtr
= & argv
[1][1];
988 switch ( argv
[1][1] ) {
990 /* action Probe and requires 5 arguments (need the flags) */
992 DoDisplayUsage( argv
);
1000 /* Note: the device argument in argv[2] is checked further down but ignored. */
1001 * mountPointPtr
= argv
[3];
1002 index
= 0; /* No isEjectable/isLocked flags for unmount. */
1006 case FSUC_MOUNT_FORCE
:
1007 /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */
1009 DoDisplayUsage( argv
);
1012 * mountPointPtr
= argv
[3];
1038 if (isdigit(argv
[2][0])) {
1040 gJournalSize
= strtoul(argv
[2], &ptr
, 0);
1042 gJournalSize
*= get_multiplier(*ptr
);
1060 DoDisplayUsage( argv
);
1065 /* Make sure device (argv[2]) is something reasonable */
1066 deviceLength
= strlen( argv
[2] );
1067 if ( doLengthCheck
&& (deviceLength
< 3 || deviceLength
> NAME_MAX
) ) {
1068 DoDisplayUsage( argv
);
1073 /* Flags: removable/fixed. */
1074 if ( 0 == strcmp(argv
[index
],"removable") ) {
1075 * isEjectablePtr
= 1;
1076 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
1077 * isEjectablePtr
= 0;
1079 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
1082 /* Flags: readonly/writable. */
1083 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
1085 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
1088 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
1092 /* Flags: suid/nosuid. */
1093 if ( 0 == strcmp(argv
[index
+2],"suid") ) {
1095 } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) {
1098 printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]);
1101 /* Flags: dev/nodev. */
1102 if ( 0 == strcmp(argv
[index
+3],"dev") ) {
1104 } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) {
1107 printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]);
1122 /* *************************************** DoDisplayUsage ********************************************
1124 This routine will do a printf of the correct usage for this utility.
1126 argv - array of arguments.
1129 *************************************************************************************************** */
1131 DoDisplayUsage(const char *argv
[])
1133 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
1134 printf("action_arg:\n");
1135 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
1136 printf(" -%c (Mount)\n", FSUC_MOUNT
);
1137 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
1138 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
1139 #ifdef HFS_UUID_SUPPORT
1140 printf(" -%c (Get UUID Key)\n", FSUC_GETUUID
);
1141 printf(" -%c (Set UUID Key)\n", FSUC_SETUUID
);
1142 #endif HFS_UUID_SUPPORT
1143 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
1144 printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL
);
1145 printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
);
1146 printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
);
1147 printf("device_arg:\n");
1148 printf(" device we are acting upon (for example, 'disk0s2')\n");
1149 printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
);
1150 printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n");
1151 printf("mount_point_arg:\n");
1152 printf(" required for Mount and Force Mount \n");
1154 printf(" required for Mount, Force Mount and Probe\n");
1155 printf(" indicates removable or fixed (for example 'fixed')\n");
1156 printf(" indicates readonly or writable (for example 'readonly')\n");
1157 printf(" indicates suid or nosuid (for example 'suid')\n");
1158 printf(" indicates dev or nodev (for example 'dev')\n");
1159 printf("Examples:\n");
1160 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
1161 printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]);
1165 } /* DoDisplayUsage */
1171 Given a path to a device, determine if a volume is mounted on that
1172 device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS.
1173 If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is
1174 no volume mounted on the device, set *pathPtr to NULL and return
1177 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1180 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
)
1186 /* Assume no mounted volume found */
1188 result
= FSUR_IO_SUCCESS
;
1190 numMounts
= getmntinfo(&buf
, MNT_NOWAIT
);
1192 return FSUR_IO_FAIL
;
1194 for (i
=0; i
<numMounts
; ++i
) {
1195 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) {
1196 /* Found a mounted volume; check the type */
1197 if (!strcmp(buf
[i
].f_fstypename
, "hfs")) {
1198 *pathPtr
= buf
[i
].f_mntonname
;
1199 /* result = FSUR_IO_SUCCESS, above */
1201 result
= FSUR_UNRECOGNIZED
;
1214 Read the Master Directory Block or Volume Header Block from an HFS,
1215 HFS Plus, or HFSX volume into a caller-supplied buffer. Return the
1216 offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus).
1217 Return a pointer to the volume UUID in the Finder Info.
1219 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1222 ReadHeaderBlock(int fd
, void *bufPtr
, off_t
*startOffset
, VolumeUUID
**finderInfoUUIDPtr
)
1225 HFSMasterDirectoryBlock
* mdbPtr
;
1226 HFSPlusVolumeHeader
* volHdrPtr
;
1232 * Read the HFS Master Directory Block or Volume Header from sector 2
1235 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1236 if (result
!= FSUR_IO_SUCCESS
)
1240 * If this is a wrapped HFS Plus volume, read the Volume Header from
1241 * sector 2 of the embedded volume.
1243 if (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
&&
1244 NXSwapBigShortToHost(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) {
1245 result
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
);
1246 if (result
!= FSUR_IO_SUCCESS
)
1248 result
= readAt(fd
, bufPtr
, *startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1249 if (result
!= FSUR_IO_SUCCESS
)
1254 * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX
1255 * volumes (including wrapped HFS Plus). Verify the signature and grab the
1256 * UUID from the Finder Info.
1258 if (NXSwapBigShortToHost(mdbPtr
->drSigWord
) == kHFSSigWord
) {
1259 *finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1260 } else if (NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSPlusSigWord
||
1261 NXSwapBigShortToHost(volHdrPtr
->signature
) == kHFSXSigWord
) {
1262 *finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1264 result
= FSUR_UNRECOGNIZED
;
1275 Read the UUID from an unmounted volume, by doing direct access to the device.
1276 Assumes the caller has already determined that a volume is not mounted
1279 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1282 GetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1287 VolumeUUID
*finderInfoUUIDPtr
;
1290 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1292 result
= FSUR_UNRECOGNIZED
;
1296 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
1299 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1301 result
= FSUR_IO_FAIL
;
1306 * Get the pointer to the volume UUID in the Finder Info
1308 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1309 if (result
!= FSUR_IO_SUCCESS
)
1313 * Copy the volume UUID out of the Finder Info
1315 volumeUUIDPtr
->v
.high
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.high
);
1316 volumeUUIDPtr
->v
.low
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.low
);
1319 if (fd
> 0) close(fd
);
1320 if (bufPtr
) free(bufPtr
);
1323 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
);
1325 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1332 Write a previously generated UUID to an unmounted volume, by doing direct
1333 access to the device. Assumes the caller has already determined that a
1334 volume is not mounted on the device.
1336 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1339 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
)
1344 VolumeUUID
*finderInfoUUIDPtr
;
1347 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1349 result
= FSUR_UNRECOGNIZED
;
1353 fd
= open( deviceNamePtr
, O_RDWR
, 0);
1356 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
);
1358 result
= FSUR_IO_FAIL
;
1363 * Get the pointer to the volume UUID in the Finder Info
1365 result
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
);
1366 if (result
!= FSUR_IO_SUCCESS
)
1370 * Update the UUID in the Finder Info
1372 finderInfoUUIDPtr
->v
.high
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.high
);
1373 finderInfoUUIDPtr
->v
.low
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.low
);
1376 * Write the modified MDB or VHB back to disk
1378 result
= writeAt(fd
, bufPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1381 if (fd
> 0) close(fd
);
1382 if (bufPtr
) free(bufPtr
);
1385 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
);
1387 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1394 Read the UUID from a mounted volume, by calling getattrlist().
1395 Assumes the path is the mount point of an HFS volume.
1397 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1400 GetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1402 struct attrlist alist
;
1403 struct FinderAttrBuf volFinderInfo
;
1404 VolumeUUID
*finderInfoUUIDPtr
;
1407 /* Set up the attrlist structure to get the volume's Finder Info */
1408 alist
.bitmapcount
= 5;
1410 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1411 alist
.volattr
= ATTR_VOL_INFO
;
1416 /* Get the Finder Info */
1417 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1419 result
= FSUR_IO_FAIL
;
1423 /* Copy the UUID from the Finder Into to caller's buffer */
1424 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1425 volumeUUIDPtr
->v
.high
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.high
);
1426 volumeUUIDPtr
->v
.low
= NXSwapBigLongToHost(finderInfoUUIDPtr
->v
.low
);
1427 result
= FSUR_IO_SUCCESS
;
1437 Write a UUID to a mounted volume, by calling setattrlist().
1438 Assumes the path is the mount point of an HFS volume.
1440 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1443 SetVolumeUUIDAttr(const char *path
, VolumeUUID
*volumeUUIDPtr
)
1445 struct attrlist alist
;
1446 struct FinderAttrBuf volFinderInfo
;
1447 VolumeUUID
*finderInfoUUIDPtr
;
1450 /* Set up the attrlist structure to get the volume's Finder Info */
1451 alist
.bitmapcount
= 5;
1453 alist
.commonattr
= ATTR_CMN_FNDRINFO
;
1454 alist
.volattr
= ATTR_VOL_INFO
;
1459 /* Get the Finder Info */
1460 result
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0);
1462 result
= FSUR_IO_FAIL
;
1466 /* Update the UUID in the Finder Info */
1467 finderInfoUUIDPtr
= (VolumeUUID
*)(&volFinderInfo
.finderinfo
[6]);
1468 finderInfoUUIDPtr
->v
.high
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.high
);
1469 finderInfoUUIDPtr
->v
.low
= NXSwapHostLongToBig(volumeUUIDPtr
->v
.low
);
1471 /* Write the Finder Info back to the volume */
1472 result
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0);
1474 result
= FSUR_IO_FAIL
;
1478 result
= FSUR_IO_SUCCESS
;
1488 Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and
1489 we were asked to generate one, then generate a new UUID and write it to the
1492 Determine whether an HFS volume is mounted on the given device. If so, we
1493 need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through
1494 the filesystem. If there is no mounted volume, then do direct device access
1495 with GetVolumeUUIDRaw and SetVolumeUUIDRaw.
1497 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1501 GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
)
1507 * Determine whether a volume is mounted on this device. If it is HFS, then
1508 * get the mount point's path. If it is non-HFS, then we can exit immediately
1509 * with FSUR_UNRECOGNIZED.
1511 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1512 if (result
!= FSUR_IO_SUCCESS
)
1516 * Get any existing UUID.
1519 result
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1521 result
= GetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1522 if (result
!= FSUR_IO_SUCCESS
)
1526 * If there was no valid UUID, and we were asked to generate one, then
1527 * generate it and write it back to disk.
1529 if (generate
&& (volumeUUIDPtr
->v
.high
== 0 || volumeUUIDPtr
->v
.low
== 0)) {
1530 GenerateVolumeUUID(volumeUUIDPtr
);
1532 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1534 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1535 /* Fall through to Err_Exit */
1547 Write a UUID to an HFS, HFS Plus or HFSX volume.
1549 Determine whether an HFS volume is mounted on the given device. If so, we
1550 need to use SetVolumeUUIDAttr to access the UUID through the filesystem.
1551 If there is no mounted volume, then do direct device access SetVolumeUUIDRaw.
1553 Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED
1556 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1561 * Determine whether a volume is mounted on this device. If it is HFS, then
1562 * get the mount point's path. If it is non-HFS, then we can exit immediately
1563 * with FSUR_UNRECOGNIZED.
1565 result
= GetHFSMountPoint(deviceNamePtr
, &path
);
1566 if (result
!= FSUR_IO_SUCCESS
)
1573 result
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
);
1575 result
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
);
1584 -- GetEmbeddedHFSPlusVol
1586 -- In: hfsMasterDirectoryBlockPtr
1587 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1588 (that is, 2 blocks before the volume header)
1593 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1595 int result
= FSUR_IO_SUCCESS
;
1596 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1598 if (NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) {
1599 result
= FSUR_UNRECOGNIZED
;
1603 allocationBlockSize
= NXSwapBigLongToHost(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
);
1604 firstAllocationBlock
= NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drAlBlSt
);
1606 if (NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) {
1607 result
= FSUR_UNRECOGNIZED
;
1611 startBlock
= NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
);
1612 blockCount
= NXSwapBigShortToHost(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
);
1614 if ( startOffsetPtr
)
1615 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1616 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1626 -- GetNameFromHFSPlusVolumeStartingAt
1628 -- Caller's responsibility to allocate and release memory for the converted string.
1630 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1634 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
)
1636 int result
= FSUR_IO_SUCCESS
;
1637 u_int32_t blockSize
;
1638 char * bufPtr
= NULL
;
1639 HFSPlusVolumeHeader
* volHdrPtr
;
1640 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1641 u_int32_t catalogNodeSize
;
1643 u_int32_t catalogExtCount
;
1644 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1646 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1647 if ( ! volHdrPtr
) {
1648 result
= FSUR_IO_FAIL
;
1653 * Read the Volume Header
1654 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1656 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1657 if (result
== FSUR_IO_FAIL
) {
1659 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1661 goto Return
; // return FSUR_IO_FAIL
1664 /* Verify that it is an HFS+ volume. */
1666 if (NXSwapBigShortToHost(volHdrPtr
->signature
) != kHFSPlusSigWord
&&
1667 NXSwapBigShortToHost(volHdrPtr
->signature
) != kHFSXSigWord
) {
1668 result
= FSUR_IO_FAIL
;
1670 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1675 blockSize
= NXSwapBigLongToHost(volHdrPtr
->blockSize
);
1676 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1677 if ( ! catalogExtents
) {
1678 result
= FSUR_IO_FAIL
;
1681 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1682 catalogExtCount
= kHFSPlusExtentDensity
;
1684 /* if there are overflow catalog extents, then go get them */
1685 if (NXSwapBigLongToHost(catalogExtents
[7].blockCount
) != 0) {
1686 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1687 if (result
!= FSUR_IO_SUCCESS
)
1691 /* Read the header node of the catalog B-Tree */
1693 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1694 catalogExtCount
, catalogExtents
,
1695 &catalogNodeSize
, &leafNode
);
1696 if (result
!= FSUR_IO_SUCCESS
)
1699 /* Read the first leaf node of the catalog b-tree */
1701 bufPtr
= (char *)malloc(catalogNodeSize
);
1703 result
= FSUR_IO_FAIL
;
1707 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1709 result
= ReadFile(fd
, bufPtr
, (off_t
) leafNode
* (off_t
) catalogNodeSize
, catalogNodeSize
,
1710 hfsPlusVolumeOffset
, blockSize
,
1711 catalogExtCount
, catalogExtents
);
1712 if (result
== FSUR_IO_FAIL
) {
1714 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1716 goto Return
; // return FSUR_IO_FAIL
1722 HFSPlusCatalogKey
* k
;
1725 if ( bTreeNodeDescriptorPtr
->numRecords
< 1) {
1726 result
= FSUR_IO_FAIL
;
1728 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1733 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1735 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1738 // Get a pointer to the first record.
1740 p
= bufPtr
+ NXSwapBigShortToHost(*v
); // pointer arithmetic in bytes
1741 k
= (HFSPlusCatalogKey
*)p
;
1743 // There should be only one record whose parent is the root parent. It should be the first record.
1745 if (NXSwapBigLongToHost(k
->parentID
) != kHFSRootParentID
) {
1746 result
= FSUR_IO_FAIL
;
1748 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1753 /* Extract the name of the root directory */
1756 HFSUniStr255
*swapped
;
1759 swapped
= (HFSUniStr255
*)malloc(sizeof(HFSUniStr255
));
1760 if (swapped
== NULL
) {
1761 result
= FSUR_IO_FAIL
;
1764 swapped
->length
= NXSwapBigShortToHost(k
->nodeName
.length
);
1766 for (i
=0; i
<swapped
->length
; i
++) {
1767 swapped
->unicode
[i
] = NXSwapBigShortToHost(k
->nodeName
.unicode
[i
]);
1769 swapped
->unicode
[i
] = 0;
1770 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
);
1771 (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1777 result
= FSUR_IO_SUCCESS
;
1781 free((char*) volHdrPtr
);
1784 free((char*) catalogExtents
);
1787 free((char*)bufPtr
);
1791 } /* GetNameFromHFSPlusVolumeStartingAt */
1794 #pragma options align=mac68k
1796 BTNodeDescriptor node
;
1798 } HeaderRec
, *HeaderPtr
;
1799 #pragma options align=reset
1804 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1808 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
,
1809 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1810 u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1813 HeaderRec
* bTreeHeaderPtr
= NULL
;
1815 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1816 if (bTreeHeaderPtr
== NULL
)
1817 return (FSUR_IO_FAIL
);
1819 /* Read the b-tree header node */
1821 result
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
,
1822 hfsPlusVolumeOffset
, blockSize
,
1823 extentCount
, extentList
);
1824 if ( result
== FSUR_IO_FAIL
) {
1826 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n");
1831 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
1832 result
= FSUR_IO_FAIL
;
1834 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1839 *nodeSize
= NXSwapBigShortToHost(bTreeHeaderPtr
->header
.nodeSize
);
1841 if (NXSwapBigLongToHost(bTreeHeaderPtr
->header
.leafRecords
) == 0)
1844 *firstLeafNode
= NXSwapBigLongToHost(bTreeHeaderPtr
->header
.firstLeafNode
);
1847 free((char*) bTreeHeaderPtr
);
1851 } /* GetBTreeNodeInfo */
1857 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1861 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
1862 HFSPlusVolumeHeader
*volHdrPtr
,
1863 HFSPlusExtentDescriptor
**catalogExtents
,
1864 u_int32_t
*catalogExtCount
)
1869 u_int32_t blockSize
;
1870 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1871 HFSPlusExtentDescriptor
* extents
;
1873 char * bufPtr
= NULL
;
1877 blockSize
= NXSwapBigLongToHost(volHdrPtr
->blockSize
);
1878 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
1879 extents
= *catalogExtents
;
1880 offset
= (off_t
)volHdrPtr
->extentsFile
.extents
[0].startBlock
*
1883 /* Read the header node of the extents B-Tree */
1885 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
,
1886 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
,
1887 &nodeSize
, &leafNode
);
1888 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
1891 /* Calculate the logical position of the first leaf node */
1893 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
1895 /* Read the first leaf node of the extents b-tree */
1897 bufPtr
= (char *)malloc(nodeSize
);
1899 result
= FSUR_IO_FAIL
;
1903 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1906 result
= ReadFile(fd
, bufPtr
, offset
, nodeSize
,
1907 hfsPlusVolumeOffset
, blockSize
,
1908 kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
);
1909 if ( result
== FSUR_IO_FAIL
) {
1911 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n");
1916 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
1917 result
= FSUR_IO_FAIL
;
1921 for (i
= 1; i
<= bTreeNodeDescriptorPtr
->numRecords
; ++i
) {
1924 HFSPlusExtentKey
* k
;
1927 * Get the offset (in bytes) of the record from the
1928 * list of offsets at the end of the node
1930 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
1933 /* Get a pointer to the record */
1935 p
= bufPtr
+ NXSwapBigShortToHost(*v
); /* pointer arithmetic in bytes */
1936 k
= (HFSPlusExtentKey
*)p
;
1938 if (NXSwapBigLongToHost(k
->fileID
) != kHFSCatalogFileID
)
1941 /* grow list and copy additional extents */
1942 listsize
+= sizeof(HFSPlusExtentRecord
);
1943 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
1944 bcopy(p
+ NXSwapBigShortToHost(k
->keyLength
) + sizeof(u_int16_t
),
1945 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
1947 *catalogExtCount
+= kHFSPlusExtentDensity
;
1948 *catalogExtents
= extents
;
1951 if ((leafNode
= bTreeNodeDescriptorPtr
->fLink
) != 0) {
1953 offset
= (off_t
) leafNode
* (off_t
) nodeSize
;
1968 * LogicalToPhysical - Map a logical file position and size to volume-relative physical
1969 * position and number of contiguous bytes at that position.
1972 * logicalOffset Logical offset in bytes from start of file
1973 * length Maximum number of bytes to map
1974 * blockSize Number of bytes per allocation block
1975 * extentCount Number of extents in file
1976 * extentList The file's extents
1979 * physicalOffset Physical offset in bytes from start of volume
1980 * availableBytes Number of bytes physically contiguous (up to length)
1982 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1984 static int LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
,
1985 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
,
1986 off_t
*physicalOffset
, ssize_t
*availableBytes
)
1989 u_int32_t logicalBlock
;
1991 u_int32_t blockCount
= 0;
1993 /* Determine allocation block containing logicalOffset */
1994 logicalBlock
= offset
/ blockSize
; /* This can't overflow for valid volumes */
1995 offset
%= blockSize
; /* Offset from start of allocation block */
1997 /* Find the extent containing logicalBlock */
1998 for (extent
= 0; extent
< extentCount
; ++extent
)
2000 blockCount
= NXSwapBigLongToHost(extentList
[extent
].blockCount
);
2002 if (blockCount
== 0)
2003 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2005 if (logicalBlock
< blockCount
)
2006 break; /* Found it! */
2008 logicalBlock
-= blockCount
;
2011 if (extent
>= extentCount
)
2012 return FSUR_IO_FAIL
; /* Tried to map past physical end of file */
2015 * When we get here, extentList[extent] is the extent containing logicalOffset.
2016 * The desired allocation block is logicalBlock blocks into the extent.
2019 /* Compute the physical starting position */
2020 temp
= NXSwapBigLongToHost(extentList
[extent
].startBlock
) + logicalBlock
; /* First physical block */
2021 temp
*= blockSize
; /* Byte offset of first physical block */
2022 *physicalOffset
= temp
+ offset
;
2024 /* Compute the available contiguous bytes. */
2025 temp
= blockCount
- logicalBlock
; /* Number of blocks available in extent */
2027 temp
-= offset
; /* Number of bytes available */
2030 *availableBytes
= temp
;
2032 *availableBytes
= length
;
2034 return FSUR_IO_SUCCESS
;
2040 * ReadFile - Read bytes from a file. Handles cases where the starting and/or
2041 * ending position are not allocation or device block aligned.
2044 * fd Descriptor for reading the volume
2045 * buffer The bytes are read into here
2046 * offset Offset in file to start reading
2047 * length Number of bytes to read
2048 * volOffset Byte offset from start of device to start of volume
2049 * blockSize Number of bytes per allocation block
2050 * extentCount Number of extents in file
2051 * extentList The file's exents
2053 * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2055 static int ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
,
2056 off_t volOffset
, u_int32_t blockSize
,
2057 u_int32_t extentCount
, const HFSPlusExtentDescriptor
*extentList
)
2059 int result
= FSUR_IO_SUCCESS
;
2065 result
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
,
2066 &physOffset
, &physLength
);
2067 if (result
!= FSUR_IO_SUCCESS
)
2070 result
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
);
2071 if (result
!= FSUR_IO_SUCCESS
)
2074 length
-= physLength
;
2075 offset
+= physLength
;
2076 buffer
= (char *) buffer
+ physLength
;
2083 -- readAt = lseek() + read()
2085 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2090 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2095 void * rawData
= NULL
;
2098 ssize_t dataOffset
= 0;
2099 int result
= FSUR_IO_SUCCESS
;
2101 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2103 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
2105 result
= FSUR_IO_FAIL
;
2108 /* put offset and length in terms of device blocksize */
2109 rawOffset
= offset
/ blocksize
* blocksize
;
2110 dataOffset
= offset
- rawOffset
;
2111 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2112 rawData
= malloc(rawLength
);
2113 if (rawData
== NULL
) {
2114 result
= FSUR_IO_FAIL
;
2118 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
2119 if ( lseekResult
!= rawOffset
) {
2120 result
= FSUR_IO_FAIL
;
2124 readResult
= read(fd
, rawData
, rawLength
);
2125 if ( readResult
!= rawLength
) {
2127 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
2129 result
= FSUR_IO_FAIL
;
2132 bcopy(rawData
+ dataOffset
, bufPtr
, length
);
2143 -- writeAt = lseek() + write()
2145 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
2150 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
2154 ssize_t bytestransferred
;
2155 void * rawData
= NULL
;
2158 ssize_t dataOffset
= 0;
2159 int result
= FSUR_IO_SUCCESS
;
2161 if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) {
2163 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
2165 result
= FSUR_IO_FAIL
;
2168 /* put offset and length in terms of device blocksize */
2169 rawOffset
= offset
/ blocksize
* blocksize
;
2170 dataOffset
= offset
- rawOffset
;
2171 rawLength
= ((length
+ dataOffset
+ blocksize
- 1) / blocksize
) * blocksize
;
2172 rawData
= malloc(rawLength
);
2173 if (rawData
== NULL
) {
2174 result
= FSUR_IO_FAIL
;
2178 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2179 if ( deviceoffset
!= rawOffset
) {
2180 result
= FSUR_IO_FAIL
;
2184 /* If the write isn't block-aligned, read the existing data before writing the new data: */
2185 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
2186 bytestransferred
= read(fd
, rawData
, rawLength
);
2187 if ( bytestransferred
!= rawLength
) {
2189 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
2191 result
= FSUR_IO_FAIL
;
2196 bcopy(bufPtr
, rawData
+ dataOffset
, length
); /* Copy in the new data */
2198 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
2199 if ( deviceoffset
!= rawOffset
) {
2200 result
= FSUR_IO_FAIL
;
2204 bytestransferred
= write(fd
, rawData
, rawLength
);
2205 if ( bytestransferred
!= rawLength
) {
2207 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
2209 result
= FSUR_IO_FAIL
;
2214 if (rawData
) free(rawData
);
2222 * Get kernel's encoding bias.
2228 size_t buflen
= sizeof(int);
2232 if (getvfsbyname("hfs", &vfc
) < 0)
2236 mib
[1] = vfc
.vfc_typenum
;
2237 mib
[2] = HFS_ENCODINGBIAS
;
2239 if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0)
2246 /******************************************************************************
2248 * 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
2250 *****************************************************************************/
2252 #define DBHANDLESIGNATURE 0x75917737
2254 /* Flag values for operation options: */
2255 #define DBMARKPOSITION 1
2257 static char gVSDBPath
[] = "/var/db/volinfo.database";
2259 #define MAXIOMALLOC 16384
2261 /* Database layout: */
2268 char statusFlags
[8];
2275 struct VSDBRecord record
;
2279 #define DBKEYSEPARATOR ':'
2280 #define DBBLANKSPACE ' '
2281 #define DBRECORDTERMINATOR '\n'
2283 /* In-memory data structures: */
2286 unsigned long signature
;
2289 off_t recordPosition
;
2292 typedef struct VSDBState
*VSDBStatePtr
;
2296 /* Internal function prototypes: */
2297 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
2298 static int UnlockDB(VSDBStatePtr dbstateptr
);
2300 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
2301 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2302 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2303 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
2304 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
2306 static void FormatULong(unsigned long u
, char *s
);
2307 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
2308 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
2309 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
2310 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
2311 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
2315 /******************************************************************************
2317 * 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
2319 *****************************************************************************/
2321 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
2323 char randomInputBuffer
[26];
2324 unsigned char digest
[20];
2329 char sysctlstring
[128];
2331 double sysloadavg
[3];
2332 struct vmtotal sysvmtotal
;
2335 /* Initialize the SHA-1 context for processing: */
2336 SHA1_Init(&context
);
2338 /* Now process successive bits of "random" input to seed the process: */
2340 /* The current system's uptime: */
2342 SHA1_Update(&context
, &uptime
, sizeof(uptime
));
2344 /* The kernel's boot time: */
2346 mib
[1] = KERN_BOOTTIME
;
2347 datalen
= sizeof(sysdata
);
2348 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2349 SHA1_Update(&context
, &sysdata
, datalen
);
2351 /* The system's host id: */
2353 mib
[1] = KERN_HOSTID
;
2354 datalen
= sizeof(sysdata
);
2355 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
2356 SHA1_Update(&context
, &sysdata
, datalen
);
2358 /* The system's host name: */
2360 mib
[1] = KERN_HOSTNAME
;
2361 datalen
= sizeof(sysctlstring
);
2362 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2363 SHA1_Update(&context
, sysctlstring
, datalen
);
2365 /* The running kernel's OS release string: */
2367 mib
[1] = KERN_OSRELEASE
;
2368 datalen
= sizeof(sysctlstring
);
2369 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2370 SHA1_Update(&context
, sysctlstring
, datalen
);
2372 /* The running kernel's version string: */
2374 mib
[1] = KERN_VERSION
;
2375 datalen
= sizeof(sysctlstring
);
2376 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
2377 SHA1_Update(&context
, sysctlstring
, datalen
);
2379 /* The system's load average: */
2380 datalen
= sizeof(sysloadavg
);
2381 getloadavg(sysloadavg
, 3);
2382 SHA1_Update(&context
, &sysloadavg
, datalen
);
2384 /* The system's VM statistics: */
2387 datalen
= sizeof(sysvmtotal
);
2388 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
2389 SHA1_Update(&context
, &sysvmtotal
, datalen
);
2391 /* The current GMT (26 ASCII characters): */
2393 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
2394 SHA1_Update(&context
, randomInputBuffer
, 26);
2396 /* Pad the accumulated input and extract the final digest hash: */
2397 SHA1_Final(digest
, &context
);
2399 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
2400 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
2405 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
2408 unsigned long nextdigit
;
2409 unsigned long high
= 0;
2410 unsigned long low
= 0;
2411 unsigned long carry
;
2413 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
2414 if ((c
>= '0') && (c
<= '9')) {
2415 nextdigit
= c
- '0';
2416 } else if ((c
>= 'A') && (c
<= 'F')) {
2417 nextdigit
= c
- 'A' + 10;
2418 } else if ((c
>= 'a') && (c
<= 'f')) {
2419 nextdigit
= c
- 'a' + 10;
2423 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
2424 high
= (high
<< 4) | carry
;
2425 low
= (low
<< 4) | nextdigit
;
2428 volumeID
->v
.high
= high
;
2429 volumeID
->v
.low
= low
;
2434 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
2435 FormatUUID(volumeID
, UUIDString
);
2436 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
2441 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
2442 VSDBStatePtr dbstateptr
;
2444 *DBHandlePtr
= NULL
;
2446 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
2447 if (dbstateptr
== NULL
) {
2451 dbstateptr
->dbmode
= O_RDWR
;
2452 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2453 if (dbstateptr
->dbfile
== -1) {
2455 The file couldn't be opened for read/write access:
2456 try read-only access before giving up altogether.
2458 dbstateptr
->dbmode
= O_RDONLY
;
2459 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
2460 if (dbstateptr
->dbfile
== -1) {
2465 dbstateptr
->signature
= DBHANDLESIGNATURE
;
2466 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
2472 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
2473 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2474 struct VSDBEntry dbentry
;
2477 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2479 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
2481 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
2484 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
2489 UnlockDB(dbstateptr
);
2495 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
2496 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2497 struct VSDBEntry dbentry
;
2500 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2501 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
2503 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2505 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2506 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2508 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2510 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2511 } else if (result
== -1) {
2513 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2515 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2520 fsync(dbstateptr
->dbfile
);
2525 UnlockDB(dbstateptr
);
2531 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2532 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2535 unsigned long iobuffersize
;
2536 void *iobuffer
= NULL
;
2538 unsigned long iotransfersize
;
2539 unsigned long bytestransferred
;
2541 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2543 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2545 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2547 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2549 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2553 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2555 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2556 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2557 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2559 iobuffersize
= MAXIOMALLOC
;
2562 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2563 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2564 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2566 if (iobuffersize
> 0) {
2567 iobuffer
= malloc(iobuffersize
);
2568 if (iobuffer
== NULL
) {
2573 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2575 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2576 if (iotransfersize
> 0) {
2577 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2580 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2582 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2583 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2584 if (bytestransferred
!= iotransfersize
) {
2590 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2592 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2593 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2594 if (bytestransferred
!= iotransfersize
) {
2599 dataoffset
+= (off_t
)iotransfersize
;
2601 } while (iotransfersize
> 0);
2604 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2606 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2610 fsync(dbstateptr
->dbfile
);
2616 if (iobuffer
) free(iobuffer
);
2617 UnlockDB(dbstateptr
);
2625 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2626 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2628 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2630 dbstateptr
->signature
= 0;
2632 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2633 dbstateptr
->dbfile
= 0;
2642 /******************************************************************************
2644 * 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
2646 *****************************************************************************/
2648 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2650 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2652 return flock(dbstateptr
->dbfile
, lockmode
);
2657 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2659 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2661 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2666 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2667 struct VSDBKey searchkey
;
2668 struct VSDBEntry dbentry
;
2671 FormatDBKey(volumeID
, &searchkey
);
2672 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2675 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2676 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2677 if (targetEntry
!= NULL
) {
2679 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2681 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2685 } while (result
== 0);
2692 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2694 VolumeUUIDString id
;
2698 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2699 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2700 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2702 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2703 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2709 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2711 VolumeUUIDString id
;
2715 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2716 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2717 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2719 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2721 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2723 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2728 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2729 struct VSDBEntry entry
;
2732 VolumeUUIDString id
;
2735 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2736 #if 0 // DEBUG_TRACE
2737 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2739 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2740 if ((result
!= sizeof(entry
)) ||
2741 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2742 (entry
.space
!= DBBLANKSPACE
) ||
2743 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2748 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2749 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2750 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2752 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2758 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2759 #if 0 // DEBUG_TRACE
2760 VolumeUUIDString id
;
2762 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2763 id
[sizeof(key1
->uuid
)] = (char)0;
2764 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2765 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2766 id
[sizeof(key2
->uuid
)] = (char)0;
2767 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2770 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2775 /******************************************************************************
2777 * 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
2779 *****************************************************************************/
2781 static void FormatULong(unsigned long u
, char *s
) {
2786 for (i
= 0; i
< 8; ++i
) {
2787 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2789 *digitptr
++ = (char)(d
+ '0');
2791 *digitptr
++ = (char)(d
- 10 + 'A');
2799 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2800 FormatULong(volumeID
->v
.high
, UUIDField
);
2801 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2807 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2808 FormatUUID(volumeID
, dbkey
->uuid
);
2813 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2814 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
2819 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
2820 FormatDBKey(volumeID
, &dbentry
->key
);
2821 dbentry
->keySeparator
= DBKEYSEPARATOR
;
2822 dbentry
->space
= DBBLANKSPACE
;
2823 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
2824 #if 0 // DEBUG_TRACE
2825 dbentry
->terminator
= (char)0;
2826 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
2828 dbentry
->terminator
= DBRECORDTERMINATOR
;
2833 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
2836 unsigned long nextdigit
;
2840 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
2841 if ((c
>= '0') && (c
<= '9')) {
2842 nextdigit
= c
- '0';
2843 } else if ((c
>= 'A') && (c
<= 'F')) {
2844 nextdigit
= c
- 'A' + 10;
2845 } else if ((c
>= 'a') && (c
<= 'f')) {
2846 nextdigit
= c
- 'a' + 10;
2850 n
= (n
<< 4) + nextdigit
;