2 * Copyright (c) 1999 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.1 (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>
51 #include <bsd/dev/disk.h>
52 #include <sys/loadable_fs.h>
53 #include <hfs/hfs_format.h>
63 #include <CoreFoundation/CFString.h>
65 #define READ_DEFAULT_ENCODING 1
67 #ifndef FSUR_MOUNT_HIDDEN
68 #define FSUR_MOUNT_HIDDEN (-9)
72 #define FSUC_GETKEY 'k'
76 #define FSUC_ADOPT 'a'
80 #define FSUC_DISOWN 'd'
84 #define FSUC_NEWUUID 'n'
89 /* **************************************** L O C A L S ******************************************* */
91 #define HFS_BLOCK_SIZE 512
93 char gHFS_FS_NAME
[] = "hfs";
94 char gHFS_FS_NAME_NAME
[] = "HFS";
96 char gFS_UUID_SUFFIX
[] = ".uuid";
97 char gNewlineString
[] = "\n";
99 char gMountCommand
[] = "/sbin/mount";
101 char gUnmountCommand
[] = "/sbin/umount";
103 char gReadOnlyOption
[] = "-r";
104 char gReadWriteOption
[] = "-w";
106 char gUsePermissionsOption
[] = "perm";
107 char gIgnorePermissionsOption
[] = "noperm";
109 boolean_t gIsEjectable
= 0;
111 #define AUTO_ADOPT_FIXED 1
112 #define AUTO_ENTER_FIXED 0
115 #define VOLUMEUUIDVALUESIZE 2
116 typedef union VolumeUUID
{
117 unsigned long value
[VOLUMEUUIDVALUESIZE
];
124 #define VOLUMEUUIDLENGTH 16
125 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1];
127 #define VOLUME_RECORDED 0x80000000
128 #define VOLUME_USEPERMISSIONS 0x00000001
129 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
131 typedef void *VolumeStatusDBHandle
;
133 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
);
134 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
);
135 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
);
137 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
);
138 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
);
139 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
);
140 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
);
141 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
);
143 /* ************************************ P R O T O T Y P E S *************************************** */
145 static void DoDisplayUsage( const char * argv
[] );
146 static void DoFileSystemFile( char * theFileNameSuffixPtr
, char * theContentsPtr
);
147 static int DoMount( char * theDeviceNamePtr
, const char * theMountPointPtr
, boolean_t isLocked
);
148 static int DoProbe( char * theDeviceNamePtr
);
149 static int DoUnmount( const char * theMountPointPtr
);
150 static int DoGetUUIDKey( const char * theDeviceNamePtr
);
151 static int DoChangeUUIDKey( const char * theDeviceNamePtr
);
152 static int DoAdopt( const char * theDeviceNamePtr
);
153 static int DoDisown( const char * theDeviceNamePtr
);
154 static int ParseArgs( int argc
, const char * argv
[], const char ** actionPtr
, const char ** mountPointPtr
, boolean_t
* isEjectablePtr
, boolean_t
* isLockedPtr
);
156 static int GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
);
157 static int SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
);
158 static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
);
159 static int GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, char * name_o
);
160 static int GetBTreeNodeInfo(int fd
, off_t btreeOffset
, u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
);
161 static off_t
CalcLeafNodeOffset(off_t fileOffset
, u_int32_t blockSize
, u_int32_t extentCount
,
162 HFSPlusExtentDescriptor
*extentList
);
163 static int GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader
*volHdrPtr
,
164 HFSPlusExtentDescriptor
**catalogExtents
, u_int32_t
*catalogExtCount
);
165 static ssize_t
readAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
166 static ssize_t
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length
);
170 * The fuction CFStringGetSystemEncoding does not work correctly in
171 * our context (autodiskmount deamon). We include a local copy here
172 * so that we can derive the default encoding. Radar 2516316.
174 #if READ_DEFAULT_ENCODING
175 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
177 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
178 struct passwd
*passwdp
;
180 if ((passwdp
= getpwuid(0))) { // root account
181 char buffer
[MAXPATHLEN
+ 1];
184 strcpy(buffer
, passwdp
->pw_dir
);
185 strcat(buffer
, __kCFUserEncodingFileName
);
187 if ((fd
= open(buffer
, O_RDONLY
, 0)) > 0) {
190 readSize
= read(fd
, buffer
, MAXPATHLEN
);
191 buffer
[(readSize
< 0 ? 0 : readSize
)] = '\0';
193 return strtol(buffer
, NULL
, 0);
196 return 0; // Fallback to smRoman
200 /* ******************************************** main ************************************************
202 This our main entry point to this utility. We get called by the WorkSpace. See ParseArgs
203 for detail info on input arguments.
205 argc - the number of arguments in argv.
206 argv - array of arguments.
208 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h.
209 *************************************************************************************************** */
211 int main (int argc
, const char *argv
[])
213 const char * actionPtr
= NULL
;
214 char rawDeviceName
[MAXPATHLEN
];
215 char blockDeviceName
[MAXPATHLEN
];
216 const char * mountPointPtr
= NULL
;
217 int result
= FSUR_IO_SUCCESS
;
218 boolean_t isLocked
= 0; /* reasonable assumptions */
220 /* Verify our arguments */
221 if ( (result
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
)) != 0 ) {
226 -- Build our device name (full path), should end up with something like:
230 sprintf(rawDeviceName
, "/dev/r%s", argv
[2]);
231 sprintf(blockDeviceName
, "/dev/%s", argv
[2]);
233 /* call the appropriate routine to handle the given action argument after becoming root */
235 result
= seteuid( 0 );
241 result
= setegid( 0 ); // PPD - is this necessary?
243 switch( * actionPtr
) {
245 result
= DoProbe(rawDeviceName
);
249 case FSUC_MOUNT_FORCE
:
250 result
= DoMount(blockDeviceName
, mountPointPtr
, isLocked
);
254 result
= DoUnmount( mountPointPtr
);
258 result
= DoGetUUIDKey( blockDeviceName
);
262 result
= DoChangeUUIDKey( blockDeviceName
);
266 result
= DoAdopt( blockDeviceName
);
270 result
= DoDisown( blockDeviceName
);
274 /* should never get here since ParseArgs should handle this situation */
275 DoDisplayUsage( argv
);
284 return result
; /*...and make main fit the ANSI spec. */
288 /* ***************************** DoMount ********************************
290 This routine will fire off a system command to mount the given device at the given mountpoint.
291 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time.
293 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
294 mountPointPtr - pointer to the mount point.
297 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes.
298 *********************************************************************** */
300 DoMount(char *deviceNamePtr
, const char *mountPointPtr
, boolean_t isLocked
)
304 char *permissionsOption
;
305 int result
= FSUR_IO_FAIL
;
307 char encodeopt
[16] = "";
308 CFStringEncoding encoding
;
309 VolumeUUID targetVolumeUUID
;
310 VolumeStatusDBHandle vsdbhandle
= NULL
;
311 unsigned long targetVolumeStatus
;
313 if (mountPointPtr
== NULL
|| *mountPointPtr
== '\0')
314 return (FSUR_IO_FAIL
);
316 /* get the volume UUID to check if permissions should be used: */
317 targetVolumeStatus
= 0;
318 if (((result
= GetVolumeUUID(deviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) ||
319 (targetVolumeUUID
.v
.high
==0) ||
320 (targetVolumeUUID
.v
.low
== 0)) {
322 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
);
325 if (gIsEjectable
== 0) {
326 result
= DoAdopt( deviceNamePtr
);
328 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
330 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
333 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
335 targetVolumeStatus
= 0;
339 /* We've got a real volume UUID! */
341 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
);
343 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) {
344 /* Can't even get access to the volume info db; assume permissions are OK. */
346 fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
);
348 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
351 fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n");
353 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
355 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
);
358 if (gIsEjectable
== 0) {
359 result
= DoAdopt( deviceNamePtr
);
361 fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
);
363 targetVolumeStatus
= VOLUME_USEPERMISSIONS
;
366 fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
);
368 targetVolumeStatus
= 0;
371 targetVolumeStatus
= 0;
374 (void)CloseVolumeStatusDB(vsdbhandle
);
381 isLockedstr
= isLocked
? gReadOnlyOption
: gReadWriteOption
;
384 (targetVolumeStatus
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption
: gIgnorePermissionsOption
;
386 /* get default encoding value (for hfs volumes) */
387 #if READ_DEFAULT_ENCODING
388 encoding
= __CFStringGetDefaultEncodingForHFSUtil();
390 encoding
= CFStringGetSystemEncoding();
392 sprintf(encodeopt
, "-e=%d", (int)encoding
);
394 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
395 gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
);
397 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
,
398 "-o", encodeopt
, "-o", permissionsOption
,
399 "-o", "-u=unknown,-g=unknown,-m=0777",
400 "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
);
402 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
403 return (FSUR_IO_FAIL
);
407 return (FSUR_IO_FAIL
);
410 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
411 result
= status
.w_retcode
;
415 return (result
== 0) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
419 /* ****************************************** DoUnmount *********************************************
421 This routine will fire off a system command to unmount the given device.
423 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
425 returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL.
426 *************************************************************************************************** */
428 DoUnmount(const char * theMountPointPtr
)
434 if (theMountPointPtr
== NULL
|| *theMountPointPtr
== '\0') return (FSUR_IO_FAIL
);
439 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
);
441 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
);
443 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */
444 return (FSUR_IO_FAIL
);
448 return (FSUR_IO_FAIL
);
451 if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
)))
452 result
= status
.w_retcode
;
456 return (result
== 0 ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
);
461 /* ******************************************* DoProbe **********************************************
463 This routine will open the given raw device and check to make sure there is media that looks
466 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
468 returns FSUR_MOUNT_HIDDEN (previously FSUR_RECOGNIZED) if we can handle the media else one of the FSUR_xyz error codes.
469 *************************************************************************************************** */
471 DoProbe(char *deviceNamePtr
)
473 int result
= FSUR_UNRECOGNIZED
;
476 HFSMasterDirectoryBlock
* mdbPtr
;
477 HFSPlusVolumeHeader
* volHdrPtr
;
478 u_char volnameUTF8
[NAME_MAX
+1];
480 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
482 result
= FSUR_UNRECOGNIZED
;
486 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
487 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
489 fd
= open( deviceNamePtr
, O_RDONLY
, 0 );
491 result
= FSUR_IO_FAIL
;
496 * Read the HFS Master Directory Block from sector 2
498 result
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
499 if (FSUR_IO_FAIL
== result
)
502 /* get classic HFS volume name (from MDB) */
503 if (mdbPtr
->drSigWord
== kHFSSigWord
&&
504 mdbPtr
->drEmbedSigWord
!= kHFSPlusSigWord
) {
507 CFStringEncoding encoding
;
509 encoding
= CFStringGetSystemEncoding();
510 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
511 mdbPtr
->drVN
, encoding
);
512 cfOK
= CFStringGetCString(cfstr
, volnameUTF8
, NAME_MAX
,
513 kCFStringEncodingUTF8
);
516 if (!cfOK
&& encoding
!= kCFStringEncodingMacRoman
) {
518 /* default to MacRoman on conversion errors */
519 cfstr
= CFStringCreateWithPascalString(kCFAllocatorDefault
,
520 mdbPtr
->drVN
, kCFStringEncodingMacRoman
);
521 CFStringGetCString(cfstr
, volnameUTF8
, NAME_MAX
,
522 kCFStringEncodingUTF8
);
526 /* get HFS Plus volume name (from Catalog) */
527 } else if ((volHdrPtr
->signature
== kHFSPlusSigWord
) ||
528 (mdbPtr
->drSigWord
== kHFSSigWord
&&
529 mdbPtr
->drEmbedSigWord
== kHFSPlusSigWord
)) {
532 if (volHdrPtr
->signature
== kHFSPlusSigWord
) {
534 } else {/* embedded volume, first find offset */
535 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
536 if ( result
!= FSUR_IO_SUCCESS
)
540 result
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
,
543 result
= FSUR_UNRECOGNIZED
;
546 if (FSUR_IO_SUCCESS
== result
) {
549 CFMutableStringRef volumeName
;
550 CFIndex volumeNameLength
;
551 CFRange foundSubString
;
553 slash
= CFStringCreateWithCString(kCFAllocatorDefault
, "/", kCFStringEncodingUTF8
);
555 result
= FSUR_IO_FAIL
;
559 colon
= CFStringCreateWithCString(kCFAllocatorDefault
, ":", kCFStringEncodingUTF8
);
561 result
= FSUR_IO_FAIL
;
565 volumeName
= CFStringCreateMutableCopy(
568 CFStringCreateWithCString(kCFAllocatorDefault
, volnameUTF8
, kCFStringEncodingUTF8
));
569 if (volumeName
== NULL
) {
570 result
= FSUR_IO_FAIL
;
573 volumeNameLength
= CFStringGetLength(volumeName
);
575 while (CFStringFindWithOptions(volumeName
, slash
, CFRangeMake(0, volumeNameLength
-1), 0, &foundSubString
)) {
576 CFStringReplace(volumeName
, foundSubString
, colon
);
579 CFStringGetCString(volumeName
, volnameUTF8
, NAME_MAX
, kCFStringEncodingUTF8
);
581 DoFileSystemFile( FS_NAME_SUFFIX
, gHFS_FS_NAME_NAME
);
582 DoFileSystemFile( FS_LABEL_SUFFIX
, volnameUTF8
);
583 result
= FSUR_MOUNT_HIDDEN
;
600 /* **************************************** DoGetUUIDKey *******************************************
602 This routine will open the given block device and return the volume UUID in text form.
604 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
606 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
607 *************************************************************************************************** */
609 DoGetUUIDKey( const char * theDeviceNamePtr
) {
611 VolumeUUID targetVolumeUUID
;
612 VolumeUUIDString UUIDString
;
613 char uuidLine
[VOLUMEUUIDLENGTH
+2];
615 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
;
617 ConvertVolumeUUIDToString( &targetVolumeUUID
, UUIDString
);
618 strncpy(uuidLine
, UUIDString
, VOLUMEUUIDLENGTH
+1);
619 strcat(uuidLine
, gNewlineString
);
620 DoFileSystemFile( gFS_UUID_SUFFIX
, uuidLine
);
621 result
= FSUR_IO_SUCCESS
;
629 /* *************************************** DoChangeUUIDKey ******************************************
631 This routine will change the UUID on the specified block device.
633 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
635 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
636 *************************************************************************************************** */
638 DoChangeUUIDKey( const char * theDeviceNamePtr
) {
640 VolumeUUID newVolumeUUID
;
642 GenerateVolumeUUID(&newVolumeUUID
);
643 result
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
);
650 /* **************************************** DoAdopt *******************************************
652 This routine will add the UUID of the specified block device to the list of local volumes.
654 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
656 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
657 *************************************************************************************************** */
659 DoAdopt( const char * theDeviceNamePtr
) {
660 int result
, closeresult
;
661 VolumeUUID targetVolumeUUID
;
662 VolumeStatusDBHandle vsdbhandle
= NULL
;
663 unsigned long targetVolumeStatus
;
665 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
667 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
668 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
669 targetVolumeStatus
= 0;
671 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
;
672 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
674 result
= FSUR_IO_SUCCESS
;
678 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
680 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
683 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) result
= FSUR_IO_FAIL
;
687 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
);
694 /* **************************************** DoDisown *******************************************
696 This routine will change the status of the specified block device to ignore its permissions.
698 theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2).
700 returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes.
701 *************************************************************************************************** */
703 DoDisown( const char * theDeviceNamePtr
) {
704 int result
, closeresult
;
705 VolumeUUID targetVolumeUUID
;
706 VolumeStatusDBHandle vsdbhandle
= NULL
;
707 unsigned long targetVolumeStatus
;
709 if ((result
= GetVolumeUUID(theDeviceNamePtr
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
;
711 if ((result
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
;
712 if ((result
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) {
713 targetVolumeStatus
= 0;
715 targetVolumeStatus
= (targetVolumeStatus
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
;
716 if ((result
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
;
718 result
= FSUR_IO_SUCCESS
;
722 closeresult
= CloseVolumeStatusDB(vsdbhandle
);
724 if (result
== FSUR_IO_SUCCESS
) result
= closeresult
;
727 if ((result
!= 0) && (result
!= FSUR_IO_SUCCESS
)) {
729 if (result
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
);
731 result
= FSUR_IO_FAIL
;
736 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
);
743 /* **************************************** ParseArgs ********************************************
745 This routine will make sure the arguments passed in to us are cool.
746 Here is how this utility is used:
748 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg]
750 -p (Probe for mounting)
751 -P (Probe for initializing - not supported)
753 -r (Repair - not supported)
756 -i (Initialize - not supported)
759 disk0s2 (for example)
762 /foo/bar/ (required for Mount and Force Mount actions)
765 (these are ignored for CDROMs)
766 either "readonly" OR "writable"
767 either "removable" OR "fixed"
770 hfs.util -p disk0s2 removable writable
771 hfs.util -p disk0s2 removable readonly
772 hfs.util -m disk0s2 /my/hfs
775 argc - the number of arguments in argv.
776 argv - array of arguments.
778 returns FSUR_INVAL if we find a bad argument else 0.
779 *************************************************************************************************** */
781 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
,
782 const char ** mountPointPtr
, boolean_t
* isEjectablePtr
,
783 boolean_t
* isLockedPtr
)
785 int result
= FSUR_INVAL
;
789 /* Must have at least 3 arguments and the action argument must start with a '-' */
790 if ( (argc
< 3) || (argv
[1][0] != '-') ) {
791 DoDisplayUsage( argv
);
795 /* we only support actions Probe, Mount, Force Mount, and Unmount */
797 * actionPtr
= & argv
[1][1];
799 switch ( argv
[1][1] ) {
801 /* action Probe and requires 5 arguments (need the flags) */
803 DoDisplayUsage( argv
);
811 /* Note: the device argument in argv[2] is checked further down but ignored. */
812 * mountPointPtr
= argv
[3];
813 index
= 0; /* No isEjectable/isLocked flags for unmount. */
817 case FSUC_MOUNT_FORCE
:
818 /* action Mount and ForceMount require 6 arguments (need the mountpoint and the flags) */
820 DoDisplayUsage( argv
);
823 * mountPointPtr
= argv
[3];
845 DoDisplayUsage( argv
);
850 /* Make sure device (argv[2]) is something reasonable */
851 deviceLength
= strlen( argv
[2] );
852 if ( deviceLength
< 3 || deviceLength
> 10 ) {
853 DoDisplayUsage( argv
);
858 /* Flags: removable/fixed. */
859 if ( 0 == strcmp(argv
[index
],"removable") ) {
860 * isEjectablePtr
= 1;
861 } else if ( 0 == strcmp(argv
[index
],"fixed") ) {
862 * isEjectablePtr
= 0;
864 printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]);
867 /* Flags: readonly/writable. */
868 if ( 0 == strcmp(argv
[index
+1],"readonly") ) {
870 } else if ( 0 == strcmp(argv
[index
+1],"writable") ) {
873 printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]);
885 /* *************************************** DoDisplayUsage ********************************************
887 This routine will do a printf of the correct usage for this utility.
889 argv - array of arguments.
892 *************************************************************************************************** */
894 DoDisplayUsage(const char *argv
[])
896 printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]);
897 printf("action_arg:\n");
898 printf(" -%c (Probe for mounting)\n", FSUC_PROBE
);
899 printf(" -%c (Mount)\n", FSUC_MOUNT
);
900 printf(" -%c (Unmount)\n", FSUC_UNMOUNT
);
901 printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE
);
902 printf(" -%c (Get UUID Key)\n", FSUC_GETKEY
);
903 printf(" -%c (New UUID Key)\n", FSUC_NEWUUID
);
904 printf(" -%c (Adopt permissions)\n", FSUC_ADOPT
);
905 printf("device_arg:\n");
906 printf(" device we are acting upon (for example, 'disk0s2')\n");
907 printf("mount_point_arg:\n");
908 printf(" required for Mount and Force Mount \n");
910 printf(" required for Mount, Force Mount and Probe\n");
911 printf(" indicates removable or fixed (for example 'fixed')\n");
912 printf(" indicates readonly or writable (for example 'readonly')\n");
913 printf("Examples:\n");
914 printf(" %s -p disk0s2 fixed writable\n", argv
[0]);
915 printf(" %s -m disk0s2 /my/hfs removable readonly\n", argv
[0]);
919 } /* DoDisplayUsage */
922 /* ************************************** DoFileSystemFile *******************************************
924 This routine will create a file system info file that is used by WorkSpace. After creating the
925 file it will write whatever theContentsPtr points to the new file.
926 We end up with a file something like:
927 /System/Library/Filesystems/hfs.fs/hfs.name
928 when our file system name is "hfs" and theFileNameSuffixPtr points to ".name"
930 theFileNameSuffixPtr - pointer to a suffix we add to the file name we're creating.
931 theContentsPtr - pointer to C string to write into the file.
934 *************************************************************************************************** */
936 DoFileSystemFile(char *fileNameSuffixPtr
, char *contentsPtr
)
939 char fileName
[MAXPATHLEN
];
941 sprintf(fileName
, "%s/%s%s/%s", FS_DIR_LOCATION
, gHFS_FS_NAME
,
942 FS_DIR_SUFFIX
, gHFS_FS_NAME
);
943 strcat(fileName
, fileNameSuffixPtr
);
944 unlink(fileName
); /* delete existing string */
946 if ( strlen( fileNameSuffixPtr
) ) {
947 int oldMask
= umask(0);
949 fd
= open( & fileName
[0], O_CREAT
| O_TRUNC
| O_WRONLY
, 0644 );
952 write( fd
, contentsPtr
, strlen( contentsPtr
) );
961 } /* DoFileSystemFile */
969 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
973 GetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
, boolean_t generate
) {
976 HFSMasterDirectoryBlock
* mdbPtr
;
977 HFSPlusVolumeHeader
* volHdrPtr
;
978 VolumeUUID
*finderInfoUUIDPtr
;
981 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
983 result
= FSUR_UNRECOGNIZED
;
987 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
988 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
990 fd
= open( deviceNamePtr
, O_RDWR
, 0 );
992 fd
= open( deviceNamePtr
, O_RDONLY
, 0);
995 fprintf(stderr
, "hfs.util: GetVolumeUUID: device open failed (errno = %d).\n", errno
);
997 result
= FSUR_IO_FAIL
;
1003 * Read the HFS Master Directory Block from sector 2
1005 result
= readAt(fd
, volHdrPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1006 if (result
!= FSUR_IO_SUCCESS
) {
1010 if (mdbPtr
->drSigWord
== kHFSSigWord
&&
1011 mdbPtr
->drEmbedSigWord
!= kHFSPlusSigWord
) {
1012 finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1013 if (generate
&& ((finderInfoUUIDPtr
->v
.high
== 0) || (finderInfoUUIDPtr
->v
.low
== 0))) {
1014 GenerateVolumeUUID(volumeUUIDPtr
);
1015 bcopy(volumeUUIDPtr
, finderInfoUUIDPtr
, sizeof(*finderInfoUUIDPtr
));
1016 result
= writeAt(fd
, volHdrPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1017 if (result
!= FSUR_IO_SUCCESS
) goto Err_Exit
;
1019 bcopy(finderInfoUUIDPtr
, volumeUUIDPtr
, sizeof(*volumeUUIDPtr
));
1020 result
= FSUR_IO_SUCCESS
;
1021 } else if ((volHdrPtr
->signature
== kHFSPlusSigWord
) ||
1022 ((mdbPtr
->drSigWord
== kHFSSigWord
) &&
1023 (mdbPtr
->drEmbedSigWord
== kHFSPlusSigWord
))) {
1026 if (volHdrPtr
->signature
== kHFSPlusSigWord
) {
1028 } else {/* embedded volume, first find offset */
1029 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
1030 if ( result
!= FSUR_IO_SUCCESS
) {
1035 result
= readAt( fd
, volHdrPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1036 if (result
!= FSUR_IO_SUCCESS
) {
1037 goto Err_Exit
; // return FSUR_IO_FAIL
1040 /* Verify that it is an HFS+ volume. */
1042 if (volHdrPtr
->signature
!= kHFSPlusSigWord
) {
1043 result
= FSUR_IO_FAIL
;
1047 finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1048 if (generate
&& ((finderInfoUUIDPtr
->v
.high
== 0) || (finderInfoUUIDPtr
->v
.low
== 0))) {
1049 GenerateVolumeUUID(volumeUUIDPtr
);
1050 bcopy(volumeUUIDPtr
, finderInfoUUIDPtr
, sizeof(*finderInfoUUIDPtr
));
1051 result
= writeAt( fd
, volHdrPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1052 if (result
!= FSUR_IO_SUCCESS
) {
1056 bcopy(finderInfoUUIDPtr
, volumeUUIDPtr
, sizeof(*volumeUUIDPtr
));
1057 result
= FSUR_IO_SUCCESS
;
1059 result
= FSUR_UNRECOGNIZED
;
1063 if (fd
> 0) close(fd
);
1064 if (bufPtr
) free(bufPtr
);
1067 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUID: result = %d...\n", result
);
1069 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1077 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1081 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID
*volumeUUIDPtr
) {
1084 HFSMasterDirectoryBlock
* mdbPtr
;
1085 HFSPlusVolumeHeader
* volHdrPtr
;
1086 VolumeUUID
*finderInfoUUIDPtr
;
1089 bufPtr
= (char *)malloc(HFS_BLOCK_SIZE
);
1091 result
= FSUR_UNRECOGNIZED
;
1095 mdbPtr
= (HFSMasterDirectoryBlock
*) bufPtr
;
1096 volHdrPtr
= (HFSPlusVolumeHeader
*) bufPtr
;
1098 fd
= open( deviceNamePtr
, O_RDWR
, 0 );
1101 fprintf(stderr
, "hfs.util: SetVolumeUUID: device open failed (errno = %d).\n", errno
);
1103 result
= FSUR_IO_FAIL
;
1108 * Read the HFS Master Directory Block from sector 2
1110 result
= readAt(fd
, volHdrPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1111 if (result
!= FSUR_IO_SUCCESS
) {
1115 if (mdbPtr
->drSigWord
== kHFSSigWord
&&
1116 mdbPtr
->drEmbedSigWord
!= kHFSPlusSigWord
) {
1117 finderInfoUUIDPtr
= (VolumeUUID
*)(&mdbPtr
->drFndrInfo
[6]);
1118 bcopy(volumeUUIDPtr
, finderInfoUUIDPtr
, sizeof(*finderInfoUUIDPtr
));
1119 result
= writeAt(fd
, volHdrPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1120 } else if ((volHdrPtr
->signature
== kHFSPlusSigWord
) ||
1121 ((mdbPtr
->drSigWord
== kHFSSigWord
) &&
1122 (mdbPtr
->drEmbedSigWord
== kHFSPlusSigWord
))) {
1125 if (volHdrPtr
->signature
== kHFSPlusSigWord
) {
1127 } else {/* embedded volume, first find offset */
1128 result
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
);
1129 if ( result
!= FSUR_IO_SUCCESS
) {
1134 result
= readAt( fd
, volHdrPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1135 if (result
!= FSUR_IO_SUCCESS
) {
1136 goto Err_Exit
; // return FSUR_IO_FAIL
1139 /* Verify that it is an HFS+ volume. */
1141 if (volHdrPtr
->signature
!= kHFSPlusSigWord
) {
1142 result
= FSUR_IO_FAIL
;
1146 finderInfoUUIDPtr
= (VolumeUUID
*)&volHdrPtr
->finderInfo
[24];
1147 bcopy(volumeUUIDPtr
, finderInfoUUIDPtr
, sizeof(*finderInfoUUIDPtr
));
1148 result
= writeAt( fd
, volHdrPtr
, startOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1149 if (result
!= FSUR_IO_SUCCESS
) {
1152 result
= FSUR_IO_SUCCESS
;
1154 result
= FSUR_UNRECOGNIZED
;
1158 if (fd
> 0) close(fd
);
1159 if (bufPtr
) free(bufPtr
);
1162 if (result
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUID: result = %d...\n", result
);
1164 return (result
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS
: FSUR_IO_FAIL
;
1170 -- GetEmbeddedHFSPlusVol
1172 -- In: hfsMasterDirectoryBlockPtr
1173 -- Out: startOffsetPtr - the disk offset at which the HFS+ volume starts
1174 (that is, 2 blocks before the volume header)
1179 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock
* hfsMasterDirectoryBlockPtr
, off_t
* startOffsetPtr
)
1181 int result
= FSUR_IO_SUCCESS
;
1182 u_int32_t allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
;
1184 if (hfsMasterDirectoryBlockPtr
->drSigWord
!= kHFSSigWord
) {
1185 result
= FSUR_UNRECOGNIZED
;
1189 allocationBlockSize
= hfsMasterDirectoryBlockPtr
->drAlBlkSiz
;
1190 firstAllocationBlock
= hfsMasterDirectoryBlockPtr
->drAlBlSt
;
1192 if (hfsMasterDirectoryBlockPtr
->drEmbedSigWord
!= kHFSPlusSigWord
) {
1193 result
= FSUR_UNRECOGNIZED
;
1197 startBlock
= hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
;
1198 blockCount
= hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
;
1200 if ( startOffsetPtr
)
1201 *startOffsetPtr
= ((u_int64_t
)startBlock
* (u_int64_t
)allocationBlockSize
) +
1202 ((u_int64_t
)firstAllocationBlock
* (u_int64_t
)HFS_BLOCK_SIZE
);
1212 -- GetNameFromHFSPlusVolumeStartingAt
1214 -- Caller's responsibility to allocate and release memory for the converted string.
1216 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1220 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, char * name_o
)
1222 int result
= FSUR_IO_SUCCESS
;
1223 u_int32_t blockSize
;
1224 char * bufPtr
= NULL
;
1225 HFSPlusVolumeHeader
* volHdrPtr
;
1227 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1228 u_int32_t catalogNodeSize
;
1230 u_int32_t catalogExtCount
;
1231 HFSPlusExtentDescriptor
*catalogExtents
= NULL
;
1233 volHdrPtr
= (HFSPlusVolumeHeader
*)malloc(HFS_BLOCK_SIZE
);
1234 if ( ! volHdrPtr
) {
1235 result
= FSUR_IO_FAIL
;
1240 * Read the Volume Header
1241 * (This is a little redundant for a pure, unwrapped HFS+ volume)
1243 result
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
);
1244 if (result
== FSUR_IO_FAIL
) {
1246 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
1248 goto Return
; // return FSUR_IO_FAIL
1251 /* Verify that it is an HFS+ volume. */
1253 if (volHdrPtr
->signature
!= kHFSPlusSigWord
) {
1254 result
= FSUR_IO_FAIL
;
1256 fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
1261 blockSize
= volHdrPtr
->blockSize
;
1262 catalogExtents
= (HFSPlusExtentDescriptor
*) malloc(sizeof(HFSPlusExtentRecord
));
1263 if ( ! catalogExtents
) {
1264 result
= FSUR_IO_FAIL
;
1267 bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
));
1268 catalogExtCount
= kHFSPlusExtentDensity
;
1270 /* if there are overflow catalog extents, then go get them */
1271 if (catalogExtents
[7].blockCount
!= 0) {
1272 result
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
);
1273 if (result
!= FSUR_IO_SUCCESS
)
1277 offset
= (off_t
)catalogExtents
[0].startBlock
* (off_t
)blockSize
;
1279 /* Read the header node of the catalog B-Tree */
1281 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
+ offset
, &catalogNodeSize
, &leafNode
);
1282 if (result
!= FSUR_IO_SUCCESS
)
1285 /* Calculate the starting block of the first leaf node */
1287 offset
= CalcLeafNodeOffset((leafNode
* catalogNodeSize
), blockSize
, catalogExtCount
, catalogExtents
);
1289 if ( offset
== 0 ) {
1290 result
= FSUR_IO_FAIL
;
1292 fprintf(stderr
, "hfs.util: ERROR: can't find leaf block\n");
1297 /* Read the first leaf node of the catalog b-tree */
1299 bufPtr
= (char *)malloc(catalogNodeSize
);
1301 result
= FSUR_IO_FAIL
;
1305 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1307 result
= readAt( fd
, bufPtr
, hfsPlusVolumeOffset
+ offset
, catalogNodeSize
);
1308 if (result
== FSUR_IO_FAIL
) {
1310 fprintf(stderr
, "hfs.util: ERROR: readAt (first leaf) failed\n");
1312 goto Return
; // return FSUR_IO_FAIL
1318 HFSPlusCatalogKey
* k
;
1321 if ( bTreeNodeDescriptorPtr
->numRecords
< 1) {
1322 result
= FSUR_IO_FAIL
;
1324 fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
1329 // Get the offset (in bytes) of the first record from the list of offsets at the end of the node.
1331 p
= bufPtr
+ catalogNodeSize
- sizeof(u_int16_t
); // pointer arithmetic in bytes
1334 // Get a pointer to the first record.
1336 p
= bufPtr
+ *v
; // pointer arithmetic in bytes
1337 k
= (HFSPlusCatalogKey
*)p
;
1339 // There should be only one record whose parent is the root parent. It should be the first record.
1341 if (k
->parentID
!= kHFSRootParentID
) {
1342 result
= FSUR_IO_FAIL
;
1344 fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
1349 /* Extract the name of the root directory */
1351 cfstr
= CFStringCreateWithCharacters(kCFAllocatorDefault
, k
->nodeName
.unicode
, k
->nodeName
.length
);
1352 (void) CFStringGetCString(cfstr
, name_o
, NAME_MAX
, kCFStringEncodingUTF8
);
1356 result
= FSUR_IO_SUCCESS
;
1360 free((char*) volHdrPtr
);
1363 free((char*) catalogExtents
);
1366 free((char*)bufPtr
);
1370 } /* GetNameFromHFSPlusVolumeStartingAt */
1373 #pragma options align=mac68k
1375 BTNodeDescriptor node
;
1377 } HeaderRec
, *HeaderPtr
;
1378 #pragma options align=reset
1383 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1387 GetBTreeNodeInfo(int fd
, off_t btreeOffset
, u_int32_t
*nodeSize
, u_int32_t
*firstLeafNode
)
1390 HeaderRec
* bTreeHeaderPtr
= NULL
;
1392 bTreeHeaderPtr
= (HeaderRec
*) malloc(HFS_BLOCK_SIZE
);
1393 if (bTreeHeaderPtr
== NULL
)
1394 return (FSUR_IO_FAIL
);
1396 /* Read the b-tree header node */
1398 result
= readAt( fd
, bTreeHeaderPtr
, btreeOffset
, HFS_BLOCK_SIZE
);
1399 if ( result
== FSUR_IO_FAIL
) {
1401 fprintf(stderr
, "hfs.util: ERROR: readAt (header node) failed\n");
1406 if ( bTreeHeaderPtr
->node
.kind
!= kBTHeaderNode
) {
1407 result
= FSUR_IO_FAIL
;
1409 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
1414 *nodeSize
= bTreeHeaderPtr
->header
.nodeSize
;
1416 if (bTreeHeaderPtr
->header
.leafRecords
== 0)
1419 *firstLeafNode
= bTreeHeaderPtr
->header
.firstLeafNode
;
1422 free((char*) bTreeHeaderPtr
);
1426 } /* GetBTreeNodeInfo */
1432 -- Returns: byte offset to first leaf node
1436 CalcLeafNodeOffset(off_t fileOffset
, u_int32_t blockSize
, u_int32_t extentCount
,
1437 HFSPlusExtentDescriptor
*extentList
)
1444 /* Find this block in the list of extents */
1446 leafblk
= fileOffset
/ blockSize
;
1449 for (i
= 0; i
< extentCount
; ++i
) {
1450 if (extentList
[i
].startBlock
== 0 || extentList
[i
].blockCount
== 0)
1451 break; /* done when we reach empty extents */
1453 extblks
+= extentList
[i
].blockCount
;
1455 if (extblks
> leafblk
) {
1456 offset
= (off_t
) extentList
[i
].startBlock
* (off_t
) blockSize
;
1457 offset
+= fileOffset
- (off_t
) ((extblks
- extentList
[i
].blockCount
) * blockSize
);
1464 } /* CalcLeafNodeOffset */
1470 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1474 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
,
1475 HFSPlusVolumeHeader
*volHdrPtr
,
1476 HFSPlusExtentDescriptor
**catalogExtents
,
1477 u_int32_t
*catalogExtCount
)
1482 BTNodeDescriptor
* bTreeNodeDescriptorPtr
;
1483 HFSPlusExtentDescriptor
* extents
;
1485 char * bufPtr
= NULL
;
1489 listsize
= *catalogExtCount
* sizeof(HFSPlusExtentDescriptor
);
1490 extents
= *catalogExtents
;
1491 offset
= (off_t
)volHdrPtr
->extentsFile
.extents
[0].startBlock
*
1492 (off_t
)volHdrPtr
->blockSize
;
1494 /* Read the header node of the extents B-Tree */
1496 result
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
+ offset
,
1497 &nodeSize
, &leafNode
);
1498 if (result
!= FSUR_IO_SUCCESS
|| leafNode
== 0)
1501 /* Calculate the starting block of the first leaf node */
1503 offset
= CalcLeafNodeOffset((leafNode
* nodeSize
), volHdrPtr
->blockSize
,
1504 kHFSPlusExtentDensity
, &volHdrPtr
->extentsFile
.extents
[0]);
1507 result
= FSUR_IO_FAIL
;
1509 fprintf(stderr
, "hfs.util: ERROR: can't find extents b-tree leaf block\n");
1514 /* Read the first leaf node of the extents b-tree */
1516 bufPtr
= (char *)malloc(nodeSize
);
1518 result
= FSUR_IO_FAIL
;
1522 bTreeNodeDescriptorPtr
= (BTNodeDescriptor
*)bufPtr
;
1525 result
= readAt(fd
, bufPtr
, hfsPlusVolumeOffset
+ offset
, nodeSize
);
1526 if ( result
== FSUR_IO_FAIL
) {
1528 fprintf(stderr
, "hfs.util: ERROR: readAt (first leaf) failed\n");
1533 if (bTreeNodeDescriptorPtr
->kind
!= kBTLeafNode
) {
1534 result
= FSUR_IO_FAIL
;
1538 for (i
= 1; i
<= bTreeNodeDescriptorPtr
->numRecords
; ++i
) {
1541 HFSPlusExtentKey
* k
;
1544 * Get the offset (in bytes) of the record from the
1545 * list of offsets at the end of the node
1547 p
= bufPtr
+ nodeSize
- (sizeof(u_int16_t
) * i
);
1550 /* Get a pointer to the record */
1552 p
= bufPtr
+ *v
; /* pointer arithmetic in bytes */
1553 k
= (HFSPlusExtentKey
*)p
;
1555 if (k
->fileID
!= kHFSCatalogFileID
)
1558 /* grow list and copy additional extents */
1559 listsize
+= sizeof(HFSPlusExtentRecord
);
1560 extents
= (HFSPlusExtentDescriptor
*) realloc(extents
, listsize
);
1561 bcopy(p
+ k
->keyLength
+ sizeof(u_int16_t
),
1562 &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
));
1564 *catalogExtCount
+= kHFSPlusExtentDensity
;
1565 *catalogExtents
= extents
;
1568 if ((leafNode
= bTreeNodeDescriptorPtr
->fLink
) != 0) {
1570 offset
= CalcLeafNodeOffset((leafNode
* nodeSize
),
1571 volHdrPtr
->blockSize
, kHFSPlusExtentDensity
,
1572 &volHdrPtr
->extentsFile
.extents
[0]);
1575 result
= FSUR_IO_FAIL
;
1577 fprintf(stderr
, "hfs.util: ERROR: can't find extents b-tree leaf block\n");
1593 -- readAt = lseek() + read()
1595 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1600 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
1605 void * rawData
= NULL
;
1608 int result
= FSUR_IO_SUCCESS
;
1610 if (ioctl(fd
, DKIOCBLKSIZE
, &blocksize
) < 0) {
1612 fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n");
1614 result
= FSUR_IO_FAIL
;
1617 /* put offset and length in terms of device blocksize */
1618 rawOffset
= offset
/ blocksize
* blocksize
;
1619 rawLength
= ((length
+ blocksize
- 1) / blocksize
) * blocksize
;
1620 rawData
= malloc(rawLength
);
1621 if (rawData
== NULL
) {
1622 result
= FSUR_IO_FAIL
;
1626 lseekResult
= lseek( fd
, rawOffset
, SEEK_SET
);
1627 if ( lseekResult
!= rawOffset
) {
1628 result
= FSUR_IO_FAIL
;
1632 readResult
= read(fd
, rawData
, rawLength
);
1633 if ( readResult
!= rawLength
) {
1635 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
);
1637 result
= FSUR_IO_FAIL
;
1640 bcopy(rawData
+ (offset
- rawOffset
), bufPtr
, length
);
1651 -- writeAt = lseek() + write()
1653 -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL
1658 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length
)
1662 ssize_t bytestransferred
;
1663 void * rawData
= NULL
;
1666 int result
= FSUR_IO_SUCCESS
;
1668 if (ioctl(fd
, DKIOCBLKSIZE
, &blocksize
) < 0) {
1670 fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n");
1672 result
= FSUR_IO_FAIL
;
1675 /* put offset and length in terms of device blocksize */
1676 rawOffset
= offset
/ blocksize
* blocksize
;
1677 rawLength
= ((length
+ blocksize
- 1) / blocksize
) * blocksize
;
1678 rawData
= malloc(rawLength
);
1679 if (rawData
== NULL
) {
1680 result
= FSUR_IO_FAIL
;
1684 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
1685 if ( deviceoffset
!= rawOffset
) {
1686 result
= FSUR_IO_FAIL
;
1690 /* If the write isn't block-aligned, read the existing data before writing the new data: */
1691 if (((rawOffset
% blocksize
) != 0) || ((rawLength
% blocksize
) != 0)) {
1692 bytestransferred
= read(fd
, rawData
, rawLength
);
1693 if ( bytestransferred
!= rawLength
) {
1695 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
);
1697 result
= FSUR_IO_FAIL
;
1702 bcopy(bufPtr
, rawData
+ (offset
- rawOffset
), length
); /* Copy in the new data */
1704 deviceoffset
= lseek( fd
, rawOffset
, SEEK_SET
);
1705 if ( deviceoffset
!= rawOffset
) {
1706 result
= FSUR_IO_FAIL
;
1710 bytestransferred
= write(fd
, rawData
, rawLength
);
1711 if ( bytestransferred
!= rawLength
) {
1713 fprintf(stderr
, "writeAt: attempt to write data to device failed?!");
1715 result
= FSUR_IO_FAIL
;
1720 if (rawData
) free(rawData
);
1727 /******************************************************************************
1729 * 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
1731 *****************************************************************************/
1733 #define DBHANDLESIGNATURE 0x75917737
1735 /* Flag values for operation options: */
1736 #define DBMARKPOSITION 1
1738 static char gVSDBPath
[] = "/var/db/volinfo.database";
1740 #define MAXIOMALLOC 16384
1742 /* Database layout: */
1749 char statusFlags
[8];
1756 struct VSDBRecord record
;
1760 #define DBKEYSEPARATOR ':'
1761 #define DBBLANKSPACE ' '
1762 #define DBRECORDTERMINATOR '\n'
1764 /* In-memory data structures: */
1767 unsigned long signature
;
1770 off_t recordPosition
;
1773 typedef struct VSDBState
*VSDBStatePtr
;
1776 unsigned long state
[5];
1777 unsigned long count
[2];
1778 unsigned char buffer
[64];
1783 /* Internal function prototypes: */
1784 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
);
1785 static int UnlockDB(VSDBStatePtr dbstateptr
);
1787 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*dbentry
, unsigned long options
);
1788 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
1789 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
1790 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
);
1791 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
);
1793 static void FormatULong(unsigned long u
, char *s
);
1794 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
);
1795 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
);
1796 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
);
1797 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
);
1798 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
);
1800 static void SHA1Transform(unsigned long state
[5], unsigned char buffer
[64]);
1801 static void SHA1Init(SHA1_CTX
* context
);
1802 static void SHA1Update(SHA1_CTX
* context
, void* data
, size_t len
);
1803 static void SHA1Final(unsigned char digest
[20], SHA1_CTX
* context
);
1807 /******************************************************************************
1809 * 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
1811 *****************************************************************************/
1813 void GenerateVolumeUUID(VolumeUUID
*newVolumeID
) {
1815 char randomInputBuffer
[26];
1816 unsigned char digest
[20];
1821 char sysctlstring
[128];
1823 struct loadavg sysloadavg
;
1824 struct vmtotal sysvmtotal
;
1827 /* Initialize the SHA-1 context for processing: */
1830 /* Now process successive bits of "random" input to seed the process: */
1832 /* The current system's uptime: */
1834 SHA1Update(&context
, &uptime
, sizeof(uptime
));
1836 /* The kernel's boot time: */
1838 mib
[1] = KERN_BOOTTIME
;
1839 datalen
= sizeof(sysdata
);
1840 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
1841 SHA1Update(&context
, &sysdata
, datalen
);
1843 /* The system's host id: */
1845 mib
[1] = KERN_HOSTID
;
1846 datalen
= sizeof(sysdata
);
1847 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0);
1848 SHA1Update(&context
, &sysdata
, datalen
);
1850 /* The system's host name: */
1852 mib
[1] = KERN_HOSTNAME
;
1853 datalen
= sizeof(sysctlstring
);
1854 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
1855 SHA1Update(&context
, sysctlstring
, datalen
);
1857 /* The running kernel's OS release string: */
1859 mib
[1] = KERN_OSRELEASE
;
1860 datalen
= sizeof(sysctlstring
);
1861 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
1862 SHA1Update(&context
, sysctlstring
, datalen
);
1864 /* The running kernel's version string: */
1866 mib
[1] = KERN_VERSION
;
1867 datalen
= sizeof(sysctlstring
);
1868 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0);
1869 SHA1Update(&context
, sysctlstring
, datalen
);
1871 /* The system's load average: */
1873 mib
[1] = VM_LOADAVG
;
1874 datalen
= sizeof(sysloadavg
);
1875 sysctl(mib
, 2, &sysloadavg
, &datalen
, NULL
, 0);
1876 SHA1Update(&context
, &sysloadavg
, datalen
);
1878 /* The system's VM statistics: */
1881 datalen
= sizeof(sysvmtotal
);
1882 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0);
1883 SHA1Update(&context
, &sysvmtotal
, datalen
);
1885 /* The current GMT (26 ASCII characters): */
1887 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26); /* "Mon Mar 27 13:46:26 2000" */
1888 SHA1Update(&context
, randomInputBuffer
, 26);
1890 /* Pad the accumulated input and extract the final digest hash: */
1891 SHA1Final(digest
, &context
);
1893 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
));
1894 } while ((newVolumeID
->v
.high
== 0) || (newVolumeID
->v
.low
== 0));
1899 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID
*volumeID
) {
1902 unsigned long nextdigit
;
1903 unsigned long high
= 0;
1904 unsigned long low
= 0;
1905 unsigned long carry
;
1907 for (i
= 0; (i
< VOLUMEUUIDLENGTH
) && ((c
= UUIDString
[i
]) != (char)0) ; ++i
) {
1908 if ((c
>= '0') && (c
<= '9')) {
1909 nextdigit
= c
- '0';
1910 } else if ((c
>= 'A') && (c
<= 'F')) {
1911 nextdigit
= c
- 'A' + 10;
1912 } else if ((c
>= 'a') && (c
<= 'f')) {
1913 nextdigit
= c
- 'a' + 10;
1917 carry
= ((low
& 0xF0000000) >> 28) & 0x0000000F;
1918 high
= (high
<< 4) | carry
;
1919 low
= (low
<< 4) | nextdigit
;
1922 volumeID
->v
.high
= high
;
1923 volumeID
->v
.low
= low
;
1928 void ConvertVolumeUUIDToString(VolumeUUID
*volumeID
, char *UUIDString
) {
1929 FormatUUID(volumeID
, UUIDString
);
1930 *(UUIDString
+16) = (char)0; /* Append a terminating null character */
1935 int OpenVolumeStatusDB(VolumeStatusDBHandle
*DBHandlePtr
) {
1936 VSDBStatePtr dbstateptr
;
1938 *DBHandlePtr
= NULL
;
1940 dbstateptr
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
));
1941 if (dbstateptr
== NULL
) {
1945 dbstateptr
->dbmode
= O_RDWR
;
1946 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDWR
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
1947 if (dbstateptr
->dbfile
== -1) {
1949 The file couldn't be opened for read/write access:
1950 try read-only access before giving up altogether.
1952 dbstateptr
->dbmode
= O_RDONLY
;
1953 dbstateptr
->dbfile
= open(gVSDBPath
, O_RDONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
1954 if (dbstateptr
->dbfile
== -1) {
1959 dbstateptr
->signature
= DBHANDLESIGNATURE
;
1960 *DBHandlePtr
= (VolumeStatusDBHandle
)dbstateptr
;
1966 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long *VolumeStatus
) {
1967 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
1968 struct VSDBEntry dbentry
;
1971 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
1973 if ((result
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
;
1975 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) {
1978 *VolumeStatus
= VOLUME_RECORDED
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
));
1983 UnlockDB(dbstateptr
);
1989 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
, unsigned long VolumeStatus
) {
1990 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
1991 struct VSDBEntry dbentry
;
1994 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
1995 if (VolumeStatus
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
;
1997 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
1999 FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
);
2000 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) {
2002 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n");
2004 result
= UpdateVolumeRecord(dbstateptr
, &dbentry
);
2005 } else if (result
== -1) {
2007 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
2009 result
= AddVolumeRecord(dbstateptr
, &dbentry
);
2014 fsync(dbstateptr
->dbfile
);
2019 UnlockDB(dbstateptr
);
2025 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID
*volumeID
) {
2026 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2029 unsigned long iobuffersize
;
2030 void *iobuffer
= NULL
;
2032 unsigned long iotransfersize
;
2033 unsigned long bytestransferred
;
2035 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2037 if ((result
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
;
2039 if ((result
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) {
2041 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
);
2043 if (result
== -1) result
= 0; /* Entry wasn't in the database to begin with? */
2047 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
2049 if ((result
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
;
2050 if ((dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) {
2051 iobuffersize
= dbinfo
.st_size
- dbstateptr
->recordPosition
- sizeof(struct VSDBEntry
);
2053 iobuffersize
= MAXIOMALLOC
;
2056 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
2057 (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
);
2058 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
);
2060 if (iobuffersize
> 0) {
2061 iobuffer
= malloc(iobuffersize
);
2062 if (iobuffer
== NULL
) {
2067 dataoffset
= dbstateptr
->recordPosition
+ sizeof(struct VSDBEntry
);
2069 iotransfersize
= dbinfo
.st_size
- dataoffset
;
2070 if (iotransfersize
> 0) {
2071 if (iotransfersize
> iobuffersize
) iotransfersize
= iobuffersize
;
2074 fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
);
2076 lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
);
2077 bytestransferred
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2078 if (bytestransferred
!= iotransfersize
) {
2084 fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset
- (off_t
)sizeof(struct VSDBEntry
)));
2086 lseek(dbstateptr
->dbfile
, dataoffset
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
);
2087 bytestransferred
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
);
2088 if (bytestransferred
!= iotransfersize
) {
2093 dataoffset
+= (off_t
)iotransfersize
;
2095 } while (iotransfersize
> 0);
2098 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
))));
2100 if ((result
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) {
2104 fsync(dbstateptr
->dbfile
);
2110 if (iobuffer
) free(iobuffer
);
2111 UnlockDB(dbstateptr
);
2119 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) {
2120 VSDBStatePtr dbstateptr
= (VSDBStatePtr
)DBHandle
;
2122 if (dbstateptr
->signature
!= DBHANDLESIGNATURE
) return EINVAL
;
2124 dbstateptr
->signature
= 0;
2126 close(dbstateptr
->dbfile
); /* Nothing we can do about any errors... */
2127 dbstateptr
->dbfile
= 0;
2136 /******************************************************************************
2138 * 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
2140 *****************************************************************************/
2142 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) {
2144 fprintf(stderr
, "LockDB: Locking VSDB file...\n");
2146 return flock(dbstateptr
->dbfile
, lockmode
);
2151 static int UnlockDB(VSDBStatePtr dbstateptr
) {
2153 fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n");
2155 return flock(dbstateptr
->dbfile
, LOCK_UN
);
2160 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID
*volumeID
, struct VSDBEntry
*targetEntry
, unsigned long options
) {
2161 struct VSDBKey searchkey
;
2162 struct VSDBEntry dbentry
;
2165 FormatDBKey(volumeID
, &searchkey
);
2166 lseek(dbstateptr
->dbfile
, 0, SEEK_SET
);
2169 result
= GetVSDBEntry(dbstateptr
, &dbentry
);
2170 if ((result
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) {
2171 if (targetEntry
!= NULL
) {
2173 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
);
2175 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
));
2179 } while (result
== 0);
2186 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2188 VolumeUUIDString id
;
2192 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2193 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2194 fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
);
2196 lseek(dbstateptr
->dbfile
, 0, SEEK_END
);
2197 return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
));
2203 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2205 VolumeUUIDString id
;
2209 strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
));
2210 id
[sizeof(dbentry
->key
.uuid
)] = (char)0;
2211 fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
);
2213 lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
);
2215 fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
));
2217 return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
));
2222 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry
*dbentry
) {
2223 struct VSDBEntry entry
;
2226 VolumeUUIDString id
;
2229 dbstateptr
->recordPosition
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
);
2230 #if 0 // DEBUG_TRACE
2231 fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
);
2233 result
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
));
2234 if ((result
!= sizeof(entry
)) ||
2235 (entry
.keySeparator
!= DBKEYSEPARATOR
) ||
2236 (entry
.space
!= DBBLANKSPACE
) ||
2237 (entry
.terminator
!= DBRECORDTERMINATOR
)) {
2242 strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
));
2243 id
[sizeof(entry
.key
.uuid
)] = (char)0;
2244 fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
);
2246 memcpy(dbentry
, &entry
, sizeof(*dbentry
));
2252 static int CompareVSDBKeys(struct VSDBKey
*key1
, struct VSDBKey
*key2
) {
2253 #if 0 // DEBUG_TRACE
2254 VolumeUUIDString id
;
2256 strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
));
2257 id
[sizeof(key1
->uuid
)] = (char)0;
2258 fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
);
2259 strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
));
2260 id
[sizeof(key2
->uuid
)] = (char)0;
2261 fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
));
2264 return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
));
2269 /******************************************************************************
2271 * 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
2273 *****************************************************************************/
2275 static void FormatULong(unsigned long u
, char *s
) {
2280 for (i
= 0; i
< 8; ++i
) {
2281 d
= ((u
& 0xF0000000) >> 28) & 0x0000000F;
2283 *digitptr
++ = (char)(d
+ '0');
2285 *digitptr
++ = (char)(d
- 10 + 'A');
2293 static void FormatUUID(VolumeUUID
*volumeID
, char *UUIDField
) {
2294 FormatULong(volumeID
->v
.high
, UUIDField
);
2295 FormatULong(volumeID
->v
.low
, UUIDField
+8);
2301 static void FormatDBKey(VolumeUUID
*volumeID
, struct VSDBKey
*dbkey
) {
2302 FormatUUID(volumeID
, dbkey
->uuid
);
2307 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord
*dbrecord
) {
2308 FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
);
2313 static void FormatDBEntry(VolumeUUID
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry
*dbentry
) {
2314 FormatDBKey(volumeID
, &dbentry
->key
);
2315 dbentry
->keySeparator
= DBKEYSEPARATOR
;
2316 dbentry
->space
= DBBLANKSPACE
;
2317 FormatDBRecord(volumeStatusFlags
, &dbentry
->record
);
2318 #if 0 // DEBUG_TRACE
2319 dbentry
->terminator
= (char)0;
2320 fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
));
2322 dbentry
->terminator
= DBRECORDTERMINATOR
;
2327 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) {
2330 unsigned long nextdigit
;
2334 for (i
= 0; (i
< 8) && ((c
= hs
[i
]) != (char)0) ; ++i
) {
2335 if ((c
>= '0') && (c
<= '9')) {
2336 nextdigit
= c
- '0';
2337 } else if ((c
>= 'A') && (c
<= 'F')) {
2338 nextdigit
= c
- 'A' + 10;
2339 } else if ((c
>= 'a') && (c
<= 'f')) {
2340 nextdigit
= c
- 'a' + 10;
2344 n
= (n
<< 4) + nextdigit
;
2352 /******************************************************************************
2354 * S H A - 1 I M P L E M E N T A T I O N R O U T I N E S
2356 *****************************************************************************/
2359 Derived from SHA-1 in C
2360 By Steve Reid <steve@edmweb.com>
2363 Test Vectors (from FIPS PUB 180-1)
2365 A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
2366 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
2367 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
2368 A million repetitions of "a"
2369 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
2372 /* #define LITTLE_ENDIAN * This should be #define'd if true. */
2373 /* #define SHA1HANDSOFF * Copies data before messing with it. */
2375 #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
2377 /* blk0() and blk() perform the initial expand. */
2378 /* I got the idea of expanding during the round function from SSLeay */
2379 #ifdef LITTLE_ENDIAN
2380 #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
2381 |(rol(block->l[i],8)&0x00FF00FF))
2383 #define blk0(i) block->l[i]
2385 #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
2386 ^block->l[(i+2)&15]^block->l[i&15],1))
2388 /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
2390 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e);
2391 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e);
2392 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e);
2393 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e);
2394 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e);
2396 #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
2397 #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
2398 #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
2399 #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
2400 #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
2404 /* Hash a single 512-bit block. This is the core of the algorithm. */
2406 static void SHA1Transform(unsigned long state
[5], unsigned char buffer
[64])
2408 unsigned long a
, b
, c
, d
, e
;
2410 unsigned char c
[64];
2411 unsigned long l
[16];
2413 CHAR64LONG16
* block
;
2415 static unsigned char workspace
[64];
2416 block
= (CHAR64LONG16
*)workspace
;
2417 memcpy(block
, buffer
, 64);
2419 block
= (CHAR64LONG16
*)buffer
;
2421 /* Copy context->state[] to working vars */
2428 printf(" A B C D E\n");
2429 printf(" -------- -------- -------- -------- --------\n");
2430 printf(" %08lX %08lX %08lX %08lX %08lX\n", a
, b
, c
, d
, e
);
2432 /* 4 rounds of 20 operations each. Loop unrolled. */
2433 R0(a
,b
,c
,d
,e
, 0); R0(e
,a
,b
,c
,d
, 1); R0(d
,e
,a
,b
,c
, 2); R0(c
,d
,e
,a
,b
, 3);
2434 R0(b
,c
,d
,e
,a
, 4); R0(a
,b
,c
,d
,e
, 5); R0(e
,a
,b
,c
,d
, 6); R0(d
,e
,a
,b
,c
, 7);
2435 R0(c
,d
,e
,a
,b
, 8); R0(b
,c
,d
,e
,a
, 9); R0(a
,b
,c
,d
,e
,10); R0(e
,a
,b
,c
,d
,11);
2436 R0(d
,e
,a
,b
,c
,12); R0(c
,d
,e
,a
,b
,13); R0(b
,c
,d
,e
,a
,14); R0(a
,b
,c
,d
,e
,15);
2437 R1(e
,a
,b
,c
,d
,16); R1(d
,e
,a
,b
,c
,17); R1(c
,d
,e
,a
,b
,18); R1(b
,c
,d
,e
,a
,19);
2438 R2(a
,b
,c
,d
,e
,20); R2(e
,a
,b
,c
,d
,21); R2(d
,e
,a
,b
,c
,22); R2(c
,d
,e
,a
,b
,23);
2439 R2(b
,c
,d
,e
,a
,24); R2(a
,b
,c
,d
,e
,25); R2(e
,a
,b
,c
,d
,26); R2(d
,e
,a
,b
,c
,27);
2440 R2(c
,d
,e
,a
,b
,28); R2(b
,c
,d
,e
,a
,29); R2(a
,b
,c
,d
,e
,30); R2(e
,a
,b
,c
,d
,31);
2441 R2(d
,e
,a
,b
,c
,32); R2(c
,d
,e
,a
,b
,33); R2(b
,c
,d
,e
,a
,34); R2(a
,b
,c
,d
,e
,35);
2442 R2(e
,a
,b
,c
,d
,36); R2(d
,e
,a
,b
,c
,37); R2(c
,d
,e
,a
,b
,38); R2(b
,c
,d
,e
,a
,39);
2443 R3(a
,b
,c
,d
,e
,40); R3(e
,a
,b
,c
,d
,41); R3(d
,e
,a
,b
,c
,42); R3(c
,d
,e
,a
,b
,43);
2444 R3(b
,c
,d
,e
,a
,44); R3(a
,b
,c
,d
,e
,45); R3(e
,a
,b
,c
,d
,46); R3(d
,e
,a
,b
,c
,47);
2445 R3(c
,d
,e
,a
,b
,48); R3(b
,c
,d
,e
,a
,49); R3(a
,b
,c
,d
,e
,50); R3(e
,a
,b
,c
,d
,51);
2446 R3(d
,e
,a
,b
,c
,52); R3(c
,d
,e
,a
,b
,53); R3(b
,c
,d
,e
,a
,54); R3(a
,b
,c
,d
,e
,55);
2447 R3(e
,a
,b
,c
,d
,56); R3(d
,e
,a
,b
,c
,57); R3(c
,d
,e
,a
,b
,58); R3(b
,c
,d
,e
,a
,59);
2448 R4(a
,b
,c
,d
,e
,60); R4(e
,a
,b
,c
,d
,61); R4(d
,e
,a
,b
,c
,62); R4(c
,d
,e
,a
,b
,63);
2449 R4(b
,c
,d
,e
,a
,64); R4(a
,b
,c
,d
,e
,65); R4(e
,a
,b
,c
,d
,66); R4(d
,e
,a
,b
,c
,67);
2450 R4(c
,d
,e
,a
,b
,68); R4(b
,c
,d
,e
,a
,69); R4(a
,b
,c
,d
,e
,70); R4(e
,a
,b
,c
,d
,71);
2451 R4(d
,e
,a
,b
,c
,72); R4(c
,d
,e
,a
,b
,73); R4(b
,c
,d
,e
,a
,74); R4(a
,b
,c
,d
,e
,75);
2452 R4(e
,a
,b
,c
,d
,76); R4(d
,e
,a
,b
,c
,77); R4(c
,d
,e
,a
,b
,78); R4(b
,c
,d
,e
,a
,79);
2453 /* Add the working vars back into context.state[] */
2459 /* Wipe variables */
2460 a
= b
= c
= d
= e
= 0;
2464 /* SHA1Init - Initialize new context */
2466 static void SHA1Init(SHA1_CTX
* context
)
2468 /* SHA1 initialization constants */
2469 context
->state
[0] = 0x67452301;
2470 context
->state
[1] = 0xEFCDAB89;
2471 context
->state
[2] = 0x98BADCFE;
2472 context
->state
[3] = 0x10325476;
2473 context
->state
[4] = 0xC3D2E1F0;
2474 context
->count
[0] = context
->count
[1] = 0;
2478 /* Run your data through this. */
2480 static void SHA1Update(SHA1_CTX
* context
, void* data
, size_t len
)
2482 unsigned char *dataptr
= (char *)data
;
2485 j
= (context
->count
[0] >> 3) & 63;
2486 if ((context
->count
[0] += len
<< 3) < (len
<< 3)) context
->count
[1]++;
2487 context
->count
[1] += (len
>> 29);
2488 if ((j
+ len
) > 63) {
2489 memcpy(&context
->buffer
[j
], dataptr
, (i
= 64-j
));
2490 SHA1Transform(context
->state
, context
->buffer
);
2491 for ( ; i
+ 63 < len
; i
+= 64) {
2492 SHA1Transform(context
->state
, &dataptr
[i
]);
2497 memcpy(&context
->buffer
[j
], &dataptr
[i
], len
- i
);
2501 /* Add padding and return the message digest. */
2503 static void SHA1Final(unsigned char digest
[20], SHA1_CTX
* context
)
2506 unsigned char finalcount
[8];
2508 for (i
= 0; i
< 8; i
++) {
2509 finalcount
[i
] = (unsigned char)((context
->count
[(i
>= 4 ? 0 : 1)]
2510 >> ((3-(i
& 3)) * 8) ) & 255); /* Endian independent */
2512 SHA1Update(context
, (unsigned char *)"\200", 1);
2513 while ((context
->count
[0] & 504) != 448) {
2514 SHA1Update(context
, (unsigned char *)"\0", 1);
2516 SHA1Update(context
, finalcount
, 8); /* Should cause a SHA1Transform() */
2517 for (i
= 0; i
< 20; i
++) {
2518 digest
[i
] = (unsigned char)
2519 ((context
->state
[i
>>2] >> ((3-(i
& 3)) * 8) ) & 255);
2521 /* Wipe variables */
2523 memset(context
->buffer
, 0, 64);
2524 memset(context
->state
, 0, 20);
2525 memset(context
->count
, 0, 8);
2526 memset(&finalcount
, 0, 8);
2527 #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */
2528 SHA1Transform(context
->state
, context
->buffer
);