2  * Copyright (c) 1999-2009 Apple Inc. All rights reserved. 
   4  * @APPLE_LICENSE_HEADER_START@ 
   6  * The contents of this file constitute Original Code as defined in and 
   7  * are subject to the Apple Public Source License Version 1.2 (the 
   8  * "License").  You may not use this file except in compliance with the 
   9  * License.  Please obtain a copy of the License at 
  10  * http://www.apple.com/publicsource and read it before using this file. 
  12  * This Original Code and all software distributed under the License are 
  13  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  14  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  15  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 
  16  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the 
  17  * License for the specific language governing rights and limitations 
  20  * @APPLE_LICENSE_HEADER_END@ 
  23  Copyright (c) 1987-99 Apple Computer, Inc. 
  27  Contains code to implement hfs utility used by the WorkSpace to mount HFS. 
  30      5-Jan-1999 Don Brady       Write hfs.label names in UTF-8. 
  31     10-Dec-1998 Pat Dirks       Changed to try built-in hfs filesystem first. 
  32      3-Sep-1998 Don Brady       Disable the daylight savings time stuff. 
  33     28-Aug-1998 chw             Fixed parse args and verify args to indicate that the 
  34                                 flags (fixed or removable) are required in the probe case. 
  35     22-Jun-1998 Pat Dirks       Changed HFSToUFSStr table to switch ":" and "/". 
  36     13-Jan-1998 jwc             first cut (derived from old NextStep macfs.util code and cdrom.util code). 
  40 /* ************************************** I N C L U D E S ***************************************** */ 
  42 #include <sys/types.h> 
  45 #include <sys/sysctl.h> 
  46 #include <sys/resource.h> 
  47 #include <sys/vmmeter.h> 
  48 #include <sys/mount.h> 
  50 #include <sys/param.h> 
  51 #include <sys/ucred.h> 
  53 #include <sys/loadable_fs.h> 
  55 #include <hfs/hfs_format.h> 
  56 #include <hfs/hfs_mount.h> 
  69  * CommonCrypto provides a more stable API than OpenSSL guarantees; 
  70  * the #define causes it to use the same API for MD5 and SHA1, so the rest of 
  71  * the code need not change. 
  73 #define COMMON_DIGEST_FOR_OPENSSL 
  74 #include  <CommonCrypto/CommonDigest.h> 
  76 #include <libkern/OSByteOrder.h> 
  78 #include <CoreFoundation/CFString.h> 
  80 #include <System/uuid/uuid.h> 
  81 #include <System/uuid/namespace.h> 
  83 #define READ_DEFAULT_ENCODING 1 
  86 #define FSUC_ADOPT 'a' 
  90 #define FSUC_DISOWN 'd' 
  94 #define FSUC_GETUUID 'k' 
  98 #define FSUC_SETUUID 's' 
 102 #define FSUC_MKJNL   'J' 
 106 #define FSUC_UNJNL   'U' 
 109 #ifndef FSUC_UNJNL_RAW 
 110 #define FSUC_UNJNL_RAW 'N' 
 113 #ifndef FSUC_JNLINFS_RAW 
 114 #define FSUC_JNLINFS_RAW 'e' 
 117 #ifndef FSUC_EXTJNL_RAW 
 118 #define FSUC_EXTJNL_RAW 'E' 
 122 #define FSUC_JNLINFO 'I' 
 126 /* **************************************** L O C A L S ******************************************* */ 
 128 #define HFS_BLOCK_SIZE                  512 
 130 char gHFS_FS_NAME
[] = "hfs"; 
 131 char gHFS_FS_NAME_NAME
[] = "HFS"; 
 133 char gNewlineString
[] = "\n"; 
 135 char gMountCommand
[] = "/sbin/mount"; 
 137 char gUnmountCommand
[] = "/sbin/umount"; 
 139 char gReadOnlyOption
[] = "-r"; 
 140 char gReadWriteOption
[] = "-w"; 
 142 char gSuidOption
[] = "suid"; 
 143 char gNoSuidOption
[] = "nosuid"; 
 145 char gDevOption
[] = "dev"; 
 146 char gNoDevOption
[] = "nodev"; 
 148 char gUsePermissionsOption
[] = "perm"; 
 149 char gIgnorePermissionsOption
[] = "noperm"; 
 151 boolean_t gIsEjectable 
= 0; 
 153 int gJournalSize 
= 0; 
 155 #define AUTO_ADOPT_FIXED 1 
 156 #define AUTO_ENTER_FIXED 0 
 159 struct FinderAttrBuf 
{ 
 160         u_int32_t info_length
; 
 161         u_int32_t finderinfo
[8]; 
 165 #define VOLUMEUUIDVALUESIZE 2 
 166 typedef union VolumeUUID 
{ 
 167         u_int32_t value
[VOLUMEUUIDVALUESIZE
]; 
 174 #define VOLUMEUUIDLENGTH 16 
 175 typedef char VolumeUUIDString
[VOLUMEUUIDLENGTH
+1]; 
 177 #define VOLUME_RECORDED 0x80000000 
 178 #define VOLUME_USEPERMISSIONS 0x00000001 
 179 #define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS ) 
 181 typedef void *VolumeStatusDBHandle
; 
 183 void GenerateVolumeUUID(VolumeUUID 
*newVolumeID
); 
 184 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID 
*volumeID
); 
 185 void ConvertVolumeUUIDToString(VolumeUUID 
*volumeID
, char *UUIDString
); 
 186 int OpenVolumeStatusDB(VolumeStatusDBHandle 
*DBHandlePtr
); 
 187 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID 
*volumeID
, unsigned long *VolumeStatus
); 
 188 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID 
*volumeID
, unsigned long VolumeStatus
); 
 189 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID 
*volumeID
); 
 190 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
); 
 192 /* ************************************ P R O T O T Y P E S *************************************** */ 
 193 static void     DoDisplayUsage( const char * argv
[] ); 
 194 static int      DoMount( char * theDeviceNamePtr
, const char *rawName
, const char * theMountPointPtr
,  
 195                                         boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev 
); 
 196 static int      DoProbe( char * rawDeviceNamePtr
, char * blockDeviceNamePtr 
); 
 197 static int      DoUnmount( const char * theMountPointPtr 
); 
 198 static int      DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName 
); 
 199 static int      DoChangeUUIDKey( const char * theDeviceNamePtr 
); 
 200 static int      DoAdopt( const char * theDeviceNamePtr
, const char *rawName
); 
 201 static int      DoDisown( const char * theDeviceNamePtr
, const char *rawName
); 
 203 extern int  DoMakeJournaled( const char * volNamePtr
, int journalSize 
);  // XXXdbg 
 204 extern int  DoUnJournal( const char * volNamePtr 
);      // XXXdbg 
 205 extern int  DoGetJournalInfo( const char * volNamePtr 
); 
 206 extern int  RawDisableJournaling( const char *devname 
); 
 207 extern int  SetJournalInFSState( const char *devname
, int journal_in_fs
); 
 209 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 
); 
 211 static int      GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
); 
 212 static int      ReadHeaderBlock(int fd
, void *bufptr
, off_t 
*startOffset
, VolumeUUID 
**finderInfoUUIDPtr
); 
 213 static int      GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, VolumeUUID 
*volumeUUIDPtr
); 
 214 static int      GetVolumeUUIDAttr(const char *path
, VolumeUUID 
*volumeUUIDPtr
); 
 215 static int      GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, VolumeUUID 
*volumeUUIDPtr
, boolean_t generate
); 
 216 static int      SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID 
*volumeUUIDPtr
); 
 217 static int      SetVolumeUUIDAttr(const char *path
, VolumeUUID 
*volumeUUIDPtr
); 
 218 static int      SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID 
*volumeUUIDPtr
); 
 219 static int      GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock 
* hfsMasterDirectoryBlockPtr
, off_t 
* startOffsetPtr
); 
 220 static int      GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
); 
 221 static int      GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
, 
 222                                                         u_int32_t extentCount
, const HFSPlusExtentDescriptor 
*extentList
, 
 223                                                         u_int32_t 
*nodeSize
, u_int32_t 
*firstLeafNode
); 
 224 static int      GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, HFSPlusVolumeHeader 
*volHdrPtr
, 
 225                                                                          HFSPlusExtentDescriptor 
**catalogExtents
, u_int32_t 
*catalogExtCount
); 
 226 static int      LogicalToPhysical(off_t logicalOffset
, ssize_t length
, u_int32_t blockSize
, 
 227                                                         u_int32_t extentCount
, const HFSPlusExtentDescriptor 
*extentList
, 
 228                                                         off_t 
*physicalOffset
, ssize_t 
*availableBytes
); 
 229 static int      ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
, 
 230                                         off_t volOffset
, u_int32_t blockSize
, 
 231                                         u_int32_t extentCount
, const HFSPlusExtentDescriptor 
*extentList
); 
 232 static ssize_t  
readAt( int fd
, void * buf
, off_t offset
, ssize_t length 
); 
 233 static ssize_t  
writeAt( int fd
, void * buf
, off_t offset
, ssize_t length 
); 
 235 static int      GetEncodingBias(void); 
 238 CF_EXPORT Boolean 
_CFStringGetFileSystemRepresentation(CFStringRef string
, UInt8 
*buffer
, CFIndex maxBufLen
); 
 240 static void     uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t 
namespace, const void *name
, int namelen
); 
 243  * The fuction CFStringGetSystemEncoding does not work correctly in 
 244  * our context (autodiskmount deamon).  We include a local copy here 
 245  * so that we can derive the default encoding. Radar 2516316. 
 247 #if READ_DEFAULT_ENCODING 
 248 #define __kCFUserEncodingFileName ("/.CFUserTextEncoding") 
 250 static unsigned int __CFStringGetDefaultEncodingForHFSUtil() { 
 251     struct passwd 
*passwdp
; 
 253     if ((passwdp 
= getpwuid(0))) { // root account 
 254         char buffer
[MAXPATHLEN 
+ 1]; 
 257         strlcpy(buffer
, passwdp
->pw_dir
, sizeof(buffer
)); 
 258         strlcat(buffer
, __kCFUserEncodingFileName
, sizeof(buffer
)); 
 260         if ((fd 
= open(buffer
, O_RDONLY
, 0)) > 0) { 
 263             readSize 
= read(fd
, buffer
, MAXPATHLEN
); 
 264             buffer
[(readSize 
< 0 ? 0 : readSize
)] = '\0'; 
 266             return strtol(buffer
, NULL
, 0); 
 269     return 0; // Fallback to smRoman 
 274 #define MXENCDNAMELEN   16      /* Maximun length of encoding name string */ 
 276 struct hfs_mnt_encoding 
{ 
 277         char                            encoding_name
[MXENCDNAMELEN
];   /* encoding type name */ 
 278         CFStringEncoding        encoding_id
;                                    /* encoding type number */ 
 281 static struct hfs_mnt_encoding hfs_mnt_encodinglist
[] = { 
 287         { "CentralEurRoman", 29 }, 
 288         { "ChineseSimp",     25 }, 
 289         { "ChineseTrad",      2 }, 
 310         { "Roman",                0 },  /* default */ 
 318         { "Ukrainian",      152 }, 
 319         { "Vietnamese",      30 }, 
 322 #define KEXT_LOAD_COMMAND       "/sbin/kextload" 
 323 #define ENCODING_MODULE_PATH    "/System/Library/Filesystems/hfs.fs/Encodings/" 
 325 static int load_encoding(CFStringEncoding encoding
) 
 333         char kmodfile
[MAXPATHLEN
]; 
 335         /* Find the encoding that matches the one passed in */ 
 336         numEncodings 
= sizeof(hfs_mnt_encodinglist
) / sizeof(struct hfs_mnt_encoding
); 
 338         for (i
=0; i
<numEncodings
; ++i
) 
 340                 if (hfs_mnt_encodinglist
[i
].encoding_id 
== encoding
) 
 342                         encodingName 
= hfs_mnt_encodinglist
[i
].encoding_name
; 
 347         if (encodingName 
== NULL
) 
 349                 /* Couldn't figure out which encoding KEXT to load */ 
 350                 syslog(LOG_ERR
, "Couldn't find name for encoding #%d", encoding
); 
 354         snprintf(kmodfile
, sizeof(kmodfile
), "%sHFS_Mac%s.kext", ENCODING_MODULE_PATH
, encodingName
); 
 355         if (stat(kmodfile
, &sb
) == -1) 
 357                 /* We recognized the encoding, but couldn't find the KEXT */ 
 358                 syslog(LOG_ERR
, "Couldn't stat HFS_Mac%s.kext: %s", encodingName
, strerror(errno
)); 
 365                 (void) execl(KEXT_LOAD_COMMAND
, KEXT_LOAD_COMMAND
, "-q", kmodfile
, NULL
); 
 367                 exit(1);        /* We can only get here if the exec failed */ 
 371                 if ((waitpid(pid
, (int *)&status
, 0) == pid
) && WIFEXITED(status
)) 
 373                         if (WEXITSTATUS(status
) != 0) 
 375                                 /* kextload returned an error.  Too bad its output doesn't get logged. */ 
 376                                 syslog(LOG_ERR
, "Couldn't load HFS_Mac%s.kext", encodingName
); 
 382         return FSUR_IO_SUCCESS
; 
 386 /* ******************************************** main ************************************************ 
 388 This our main entry point to this utility.  We get called by the WorkSpace.  See ParseArgs 
 389 for detail info on input arguments. 
 391 argc - the number of arguments in argv. 
 392 argv - array of arguments. 
 394 returns FSUR_IO_SUCCESS if OK else one of the other FSUR_xyz errors in loadable_fs.h. 
 395 *************************************************************************************************** */ 
 397 int main (int argc
, const char *argv
[]) 
 399     const char                  *       actionPtr 
= NULL
; 
 400     char                                rawDeviceName
[MAXPATHLEN
]; 
 401     char                                blockDeviceName
[MAXPATHLEN
]; 
 402     const char                  *       mountPointPtr 
= NULL
; 
 403     int                                         result 
= FSUR_IO_SUCCESS
; 
 404     boolean_t                           isLocked 
= 0;   /* reasonable assumptions */ 
 405     boolean_t                           isSetuid 
= 0;   /* reasonable assumptions */ 
 406     boolean_t                           isDev 
= 0;      /* reasonable assumptions */ 
 408         openlog("hfs.util", LOG_PID
, LOG_DAEMON
); 
 410     /* Verify our arguments */ 
 411     if ( (result 
= ParseArgs( argc
, argv
, & actionPtr
, & mountPointPtr
, & gIsEjectable
, & isLocked
, &isSetuid
, &isDev 
)) != 0 ) { 
 416     -- Build our device name (full path), should end up with something like: 
 420     snprintf(rawDeviceName
, sizeof(rawDeviceName
), "/dev/r%s", argv
[2]); 
 421     snprintf(blockDeviceName
, sizeof(blockDeviceName
), "/dev/%s", argv
[2]); 
 423     /* call the appropriate routine to handle the given action argument after becoming root */ 
 425     switch( * actionPtr 
) { 
 427             result 
= DoProbe(rawDeviceName
, blockDeviceName
); 
 431         case FSUC_MOUNT_FORCE
: 
 432             result 
= DoMount(blockDeviceName
, rawDeviceName
, mountPointPtr
, isLocked
, isSetuid
, isDev
); 
 436             result 
= DoUnmount( mountPointPtr 
); 
 439                         result 
= DoGetUUIDKey( blockDeviceName
, rawDeviceName
); 
 443                         result 
= DoChangeUUIDKey( blockDeviceName 
); 
 446                         result 
= DoAdopt( blockDeviceName
, rawDeviceName
); 
 450                         result 
= DoDisown( blockDeviceName
, rawDeviceName 
); 
 455                                 result 
= DoMakeJournaled( argv
[3], gJournalSize 
); 
 457                                 result 
= DoMakeJournaled( argv
[2], gJournalSize 
); 
 462                         result 
= DoUnJournal( argv
[2] ); 
 466                         result 
= RawDisableJournaling( argv
[2] ); 
 469                 case FSUC_JNLINFS_RAW
: 
 470                         // argv[2] has the device for the external journal.  however 
 471                         // we don't need it so we ignore it and just pass argv[3] 
 472                         // which is the hfs volume whose state we're going to change 
 474                         result 
= SetJournalInFSState( argv
[3], 1 ); 
 477                 case FSUC_EXTJNL_RAW
: 
 478                         // see the comment for FSUC_JNLINFS_RAW 
 479                         result 
= SetJournalInFSState( argv
[3], 0 ); 
 483                         result 
= DoGetJournalInfo( argv
[2] ); 
 487             /* should never get here since ParseArgs should handle this situation */ 
 488             DoDisplayUsage( argv 
); 
 497     return result
;      /*...and make main fit the ANSI spec. */ 
 501 /* ***************************** DoMount ******************************** 
 503 This routine will fire off a system command to mount the given device at the given mountpoint. 
 504 autodiskmount will make sure the mountpoint exists and will remove it at Unmount time. 
 506 deviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). 
 507 mountPointPtr - pointer to the mount point. 
 510 returns FSUR_IO_SUCCESS everything is cool else one of several other FSUR_xyz error codes. 
 511 *********************************************************************** */ 
 513 DoMount(char *deviceNamePtr
, const char *rawName
, const char *mountPointPtr
, 
 514                 boolean_t isLocked
, boolean_t isSetuid
, boolean_t isDev
) 
 520         char *permissionsOption
; 
 521         int result 
= FSUR_IO_FAIL
; 
 523         char encodeopt
[16] = ""; 
 524         CFStringEncoding encoding
; 
 525         VolumeUUID targetVolumeUUID
; 
 526         VolumeStatusDBHandle vsdbhandle 
= NULL
; 
 527         unsigned long targetVolumeStatus
; 
 529         if (mountPointPtr 
== NULL 
|| *mountPointPtr 
== '\0') 
 530                 return (FSUR_IO_FAIL
); 
 532         /* get the volume UUID to check if permissions should be used: */ 
 533         targetVolumeStatus 
= 0; 
 534         if (((result 
= GetVolumeUUID(deviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) || 
 535                 (targetVolumeUUID
.v
.high 
==0) || 
 536                 (targetVolumeUUID
.v
.low 
== 0)) { 
 538                 fprintf(stderr
, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result
); 
 541                 if (gIsEjectable 
== 0) { 
 542                         result 
= DoAdopt( deviceNamePtr
, rawName
); 
 544                         fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
); 
 546                         targetVolumeStatus 
= VOLUME_USEPERMISSIONS
; 
 549                         fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
); 
 551                         targetVolumeStatus 
= 0; 
 555                 /* We've got a real volume UUID! */ 
 557                 fprintf(stderr
, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID
.v
.high
, targetVolumeUUID
.v
.low
); 
 559                 if ((result 
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) { 
 560                         /* Can't even get access to the volume info db; assume permissions are OK. */ 
 562                         fprintf(stderr
, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result
); 
 564                         targetVolumeStatus 
= VOLUME_USEPERMISSIONS
; 
 567                         fprintf(stderr
, "hfs.util: DoMount: Looking up volume status...\n"); 
 569                         if ((result 
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) { 
 571                                 fprintf(stderr
, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result
); 
 574                                 if (gIsEjectable 
== 0) { 
 575                                         result 
= DoAdopt( deviceNamePtr
, rawName 
); 
 577                                         fprintf(stderr
, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr
, result
); 
 579                                         targetVolumeStatus 
= VOLUME_USEPERMISSIONS
; 
 582                                         fprintf(stderr
, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr
); 
 584                                         targetVolumeStatus 
= 0; 
 587                                 targetVolumeStatus 
= 0; 
 590                         (void)CloseVolumeStatusDB(vsdbhandle
); 
 597                 isLockedstr 
= isLocked 
? gReadOnlyOption 
: gReadWriteOption
; 
 598                 isSetuidstr 
= isSetuid 
? gSuidOption 
: gNoSuidOption
; 
 599                 isDevstr 
= isDev 
? gDevOption 
: gNoDevOption
; 
 602                         (targetVolumeStatus 
& VOLUME_USEPERMISSIONS
) ? gUsePermissionsOption 
: gIgnorePermissionsOption
; 
 604                 /* get default encoding value (for hfs volumes) */ 
 605 #if READ_DEFAULT_ENCODING 
 606                 encoding 
= __CFStringGetDefaultEncodingForHFSUtil(); 
 608                 encoding 
= CFStringGetSystemEncoding(); 
 610                 snprintf(encodeopt
, sizeof(encodeopt
), "-e=%d", (int)encoding
); 
 612                 fprintf(stderr
, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n", 
 613                                                         gMountCommand
, isLockedstr
, encodeopt
, permissionsOption
, gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
); 
 615                 (void) execl(gMountCommand
, gMountCommand
, isLockedstr
, "-o", isSetuidstr
, "-o", isDevstr
, 
 616                                         "-o", encodeopt
, "-o", permissionsOption
, 
 617                                         "-o", "-u=unknown,-g=unknown,-m=0777", 
 618                                         "-t", gHFS_FS_NAME
, deviceNamePtr
, mountPointPtr
, NULL
); 
 621                 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */ 
 622                 return (FSUR_IO_FAIL
); 
 626                 return (FSUR_IO_FAIL
); 
 629         if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
))) 
 630                 result 
= status
.w_retcode
; 
 634         return (result 
== 0) ? FSUR_IO_SUCCESS 
: FSUR_IO_FAIL
; 
 638 /* ****************************************** DoUnmount ********************************************* 
 640     This routine will fire off a system command to unmount the given device. 
 642     theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). 
 644     returns FSUR_IO_SUCCESS everything is cool else FSUR_IO_FAIL. 
 645 *************************************************************************************************** */ 
 647 DoUnmount(const char * theMountPointPtr
) 
 653         if (theMountPointPtr 
== NULL 
|| *theMountPointPtr 
== '\0') return (FSUR_IO_FAIL
); 
 658                 fprintf(stderr
, "hfs.util: %s %s ...\n", gUnmountCommand
, theMountPointPtr
); 
 660                 (void) execl(gUnmountCommand
, gUnmountCommand
, theMountPointPtr
, NULL
); 
 662                 /* IF WE ARE HERE, WE WERE UNSUCCESFULL */ 
 663                 return (FSUR_IO_FAIL
); 
 667                 return (FSUR_IO_FAIL
); 
 670         if ((wait4(pid
, (int *)&status
, 0, NULL
) == pid
) && (WIFEXITED(status
))) 
 671                 result 
= status
.w_retcode
; 
 675         return (result 
== 0 ? FSUR_IO_SUCCESS 
: FSUR_IO_FAIL
); 
 683         Get the volume name of the volume mounted at "path".  Print that volume 
 684         name to standard out. 
 686         Returns: FSUR_RECOGNIZED, FSUR_IO_FAIL 
 688 struct VolumeNameBuf 
{ 
 689         u_int32_t       info_length
; 
 690         attrreference_t name_ref
; 
 695 PrintVolumeNameAttr(const char *path
) 
 697         struct attrlist alist
; 
 698         struct VolumeNameBuf volNameInfo
; 
 701         /* Set up the attrlist structure to get the volume's Finder Info */ 
 702         alist
.bitmapcount 
= 5; 
 704         alist
.commonattr 
= 0; 
 705         alist
.volattr 
= ATTR_VOL_INFO 
| ATTR_VOL_NAME
; 
 710         /* Get the Finder Info */ 
 711         result 
= getattrlist(path
, &alist
, &volNameInfo
, sizeof(volNameInfo
), 0); 
 713                 result 
= FSUR_IO_FAIL
; 
 717         /* Print the name to standard out */ 
 718         printf("%.*s", (int) volNameInfo
.name_ref
.attr_length
, ((char *) &volNameInfo
.name_ref
) + volNameInfo
.name_ref
.attr_dataoffset
); 
 719         result 
= FSUR_RECOGNIZED
; 
 726 /* ******************************************* DoProbe ********************************************** 
 728     This routine will open the given device and check to make sure there is media that looks 
 729     like an HFS.  If it is HFS, then print the volume name to standard output. 
 731     rawDeviceNamePtr - pointer to the full path of the raw device (like /dev/rdisk0s2). 
 732     blockDeviceNamePtr - pointer to the full path of the non-raw device (like /dev/disk0s2). 
 734     returns FSUR_RECOGNIZED if we can handle the media else one of the FSUR_xyz error codes. 
 735 *************************************************************************************************** */ 
 737 DoProbe(char *rawDeviceNamePtr
, char *blockDeviceNamePtr
) 
 739         int result 
= FSUR_UNRECOGNIZED
; 
 742         HFSMasterDirectoryBlock 
* mdbPtr
; 
 743         HFSPlusVolumeHeader 
* volHdrPtr
; 
 744         u_char volnameUTF8
[NAME_MAX
+1]; 
 747          * Determine if there is a volume already mounted from this device.  If 
 748          * there is, and it is HFS, then we need to get the volume name via 
 751          * NOTE: We're using bufPtr to hold a pointer to a path. 
 754         result 
= GetHFSMountPoint(blockDeviceNamePtr
, &bufPtr
); 
 755         if (result 
!= FSUR_IO_SUCCESS
) { 
 758         if (bufPtr 
!= NULL
) { 
 759                 /* There is an HFS volume mounted from the device. */ 
 760                 result 
= PrintVolumeNameAttr(bufPtr
); 
 765          * If we get here, there is no volume mounted from this device, so 
 766          * go probe the raw device directly. 
 769         bufPtr 
= (char *)malloc(HFS_BLOCK_SIZE
); 
 771                 result 
= FSUR_UNRECOGNIZED
; 
 775         mdbPtr 
= (HFSMasterDirectoryBlock 
*) bufPtr
; 
 776         volHdrPtr 
= (HFSPlusVolumeHeader 
*) bufPtr
; 
 778         fd 
= open( rawDeviceNamePtr
, O_RDONLY
, 0 ); 
 780                 result 
= FSUR_IO_FAIL
; 
 785          * Read the HFS Master Directory Block from sector 2 
 787         result 
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
); 
 788         if (FSUR_IO_FAIL 
== result
) 
 791         /* get classic HFS volume name (from MDB) */ 
 792         if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord 
&& 
 793             OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) { 
 796                 CFStringEncoding encoding
; 
 798                 /* Some poorly mastered HFS CDs have an empty MDB name field! */ 
 799                 if (mdbPtr
->drVN
[0] == '\0') { 
 800                         strcpy((char *)&mdbPtr
->drVN
[1], gHFS_FS_NAME_NAME
); 
 801                         mdbPtr
->drVN
[0] = strlen(gHFS_FS_NAME_NAME
); 
 804                 /* Check for an encoding hint in the Finder Info (field 4). */ 
 805                 encoding 
= GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr
->drFndrInfo
[4])); 
 806                 if (encoding 
== kCFStringEncodingInvalidId
) { 
 807                         /* Next try the encoding bias in the kernel. */ 
 808                         encoding 
= GetEncodingBias(); 
 809                         if (encoding 
== 0 || encoding 
== kCFStringEncodingInvalidId
) 
 810                                 encoding 
= __CFStringGetDefaultEncodingForHFSUtil(); 
 813                 cfstr 
= CFStringCreateWithPascalString(kCFAllocatorDefault
, 
 814                             mdbPtr
->drVN
, encoding
); 
 819                 cfOK 
= _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
); 
 822                 if (!cfOK 
&& encoding 
!= kCFStringEncodingMacRoman
) { 
 824                         /* default to MacRoman on conversion errors */ 
 825                         cfstr 
= CFStringCreateWithPascalString(kCFAllocatorDefault
, 
 826                                     mdbPtr
->drVN
, kCFStringEncodingMacRoman
); 
 827                         _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
); 
 829                         encoding 
= kCFStringEncodingMacRoman
; 
 832                 /* Preload the encoding converter so mount_hfs can run as an ordinary user. */ 
 833                 if (encoding 
!= kCFStringEncodingMacRoman
) { 
 834                         if (load_encoding(encoding
) != FSUR_IO_SUCCESS
) { 
 835                                 encoding 
= kCFStringEncodingMacRoman
; 
 836                                 cfstr 
= CFStringCreateWithPascalString(kCFAllocatorDefault
, mdbPtr
->drVN
, encoding
); 
 837                                 _CFStringGetFileSystemRepresentation(cfstr
, volnameUTF8
, NAME_MAX
); 
 842         /* get HFS Plus volume name (from Catalog) */ 
 843         } else if ((OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord
)  || 
 844                    (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
)  || 
 845                    (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord 
&& 
 846                     OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
)) { 
 849                 if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSSigWord
) { 
 850                         /* embedded volume, first find offset */ 
 851                         result 
= GetEmbeddedHFSPlusVol(mdbPtr
, &startOffset
); 
 852                         if ( result 
!= FSUR_IO_SUCCESS 
) 
 858                 result 
= GetNameFromHFSPlusVolumeStartingAt(fd
, startOffset
, 
 861                 result 
= FSUR_UNRECOGNIZED
; 
 864         if (FSUR_IO_SUCCESS 
== result
) { 
 865                 /* Print the volume name to standard output */ 
 866                 write(1, volnameUTF8
, strlen((char *)volnameUTF8
)); 
 867                 result 
= FSUR_RECOGNIZED
; 
 883  * Create a version 3 UUID from a unique "name" in the given "name space".   
 884  * Version 3 UUID are derived using "name" via MD5 checksum. 
 887  *      result_uuid     - resulting UUID. 
 888  *      namespace       - namespace in which given name exists and UUID should be created. 
 889  *      name            - unique string used to create version 3 UUID. 
 890  *      namelen     - length of the name string. 
 893 uuid_create_md5_from_name(uuid_t result_uuid
, const uuid_t 
namespace, const void *name
, int namelen
) 
 898     MD5_Update(&c
, namespace, sizeof(uuid_t
)); 
 899     MD5_Update(&c
, name
, namelen
); 
 900     MD5_Final(result_uuid
, &c
); 
 902     result_uuid
[6] = (result_uuid
[6] & 0x0F) | 0x30; 
 903     result_uuid
[8] = (result_uuid
[8] & 0x3F) | 0x80; 
 907 /* **************************************** DoGetUUIDKey ******************************************* 
 909     This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout. 
 911     theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). 
 913     returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. 
 914 *************************************************************************************************** */ 
 916 DoGetUUIDKey( const char * theDeviceNamePtr
, const char *rawName
) { 
 918         VolumeUUID targetVolumeUUID
; 
 922         unsigned char rawUUID
[8]; 
 924         if ((result 
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, FALSE
)) != FSUR_IO_SUCCESS
) goto Err_Exit
; 
 926         ((uint32_t *)rawUUID
)[0] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.high
); 
 927         ((uint32_t *)rawUUID
)[1] = OSSwapHostToBigInt32(targetVolumeUUID
.v
.low
); 
 929         uuid_create_md5_from_name(uuid
, kFSUUIDNamespaceSHA1
, rawUUID
, sizeof(rawUUID
)); 
 930         uuid_unparse(uuid
, uuidLine
); 
 931         write(1, uuidLine
, strlen(uuidLine
)); 
 932         result 
= FSUR_IO_SUCCESS
; 
 940 /* *************************************** DoChangeUUIDKey ****************************************** 
 942     This routine will change the UUID on the specified block device. 
 944     theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). 
 946     returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. 
 947 *************************************************************************************************** */ 
 949 DoChangeUUIDKey( const char * theDeviceNamePtr 
) { 
 951         VolumeUUID newVolumeUUID
; 
 953         GenerateVolumeUUID(&newVolumeUUID
); 
 954         result 
= SetVolumeUUID(theDeviceNamePtr
, &newVolumeUUID
); 
 961 /* **************************************** DoAdopt ******************************************* 
 963     This routine will add the UUID of the specified block device to the list of local volumes. 
 965     theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). 
 967     returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. 
 968 *************************************************************************************************** */ 
 970 DoAdopt( const char * theDeviceNamePtr
, const char *rawName
) { 
 971         int result
, closeresult
; 
 972         VolumeUUID targetVolumeUUID
; 
 973         VolumeStatusDBHandle vsdbhandle 
= NULL
; 
 974         unsigned long targetVolumeStatus
; 
 976         if ((result 
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
; 
 978         if ((result 
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
; 
 979         if ((result 
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) { 
 980                 targetVolumeStatus 
= 0; 
 982         targetVolumeStatus 
= (targetVolumeStatus 
& VOLUME_VALIDSTATUSBITS
) | VOLUME_USEPERMISSIONS
; 
 983         if ((result 
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
; 
 985         result 
= FSUR_IO_SUCCESS
; 
 989                 closeresult 
= CloseVolumeStatusDB(vsdbhandle
); 
 991                 if (result 
== FSUR_IO_SUCCESS
) result 
= closeresult
; 
 994         if ((result 
!= 0) && (result 
!= FSUR_IO_SUCCESS
)) result 
= FSUR_IO_FAIL
; 
 998         if (result 
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoAdopt: returning %d...\n", result
); 
1005 /* **************************************** DoDisown ******************************************* 
1007     This routine will change the status of the specified block device to ignore its permissions. 
1009     theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). 
1011     returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. 
1012 *************************************************************************************************** */ 
1014 DoDisown( const char * theDeviceNamePtr
, const char *rawName
) { 
1015         int result
, closeresult
; 
1016         VolumeUUID targetVolumeUUID
; 
1017         VolumeStatusDBHandle vsdbhandle 
= NULL
; 
1018         unsigned long targetVolumeStatus
; 
1020         if ((result 
= GetVolumeUUID(theDeviceNamePtr
, rawName
, &targetVolumeUUID
, TRUE
)) != FSUR_IO_SUCCESS
) goto Err_Return
; 
1022         if ((result 
= OpenVolumeStatusDB(&vsdbhandle
)) != 0) goto Err_Exit
; 
1023         if ((result 
= GetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, &targetVolumeStatus
)) != 0) { 
1024                 targetVolumeStatus 
= 0; 
1026         targetVolumeStatus 
= (targetVolumeStatus 
& VOLUME_VALIDSTATUSBITS
) & ~VOLUME_USEPERMISSIONS
; 
1027         if ((result 
= SetVolumeStatusDBEntry(vsdbhandle
, &targetVolumeUUID
, targetVolumeStatus
)) != 0) goto Err_Exit
; 
1029         result 
= FSUR_IO_SUCCESS
; 
1033                 closeresult 
= CloseVolumeStatusDB(vsdbhandle
); 
1035                 if (result 
== FSUR_IO_SUCCESS
) result 
= closeresult
; 
1038         if ((result 
!= 0) && (result 
!= FSUR_IO_SUCCESS
)) { 
1040                 if (result 
!= 0) fprintf(stderr
, "DoDisown: result = %d; changing to %d...\n", result
, FSUR_IO_FAIL
); 
1042                 result 
= FSUR_IO_FAIL
; 
1047         if (result 
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "DoDisown: returning %d...\n", result
); 
1054 get_multiplier(char c
) 
1056         if (tolower(c
) == 'k') { 
1058         } else if (tolower(c
) == 'm') { 
1060         } else if (tolower(c
) == 'g') { 
1061                 return 1024 * 1024 * 1024; 
1067 /* **************************************** ParseArgs ******************************************** 
1069     This routine will make sure the arguments passed in to us are cool. 
1070     Here is how this utility is used: 
1072 usage: hfs.util actionArg deviceArg [mountPointArg] [flagsArg] 
1074         -p (Probe for mounting) 
1075         -P (Probe for initializing - not supported) 
1077         -r (Repair - not supported) 
1080         -i (Initialize - not supported) 
1083         disk0s2 (for example) 
1086         /foo/bar/ (required for Mount and Force Mount actions) 
1089         (these are ignored for CDROMs) 
1090         either "readonly" OR "writable" 
1091         either "removable" OR "fixed" 
1092         either "nosuid" or "suid" 
1093         either "nodev" or "dev" 
1096         hfs.util -p disk0s2 removable writable 
1097         hfs.util -p disk0s2 removable readonly 
1098         hfs.util -m disk0s2 /my/hfs 
1101     argc - the number of arguments in argv. 
1102     argv - array of arguments. 
1104     returns FSUR_INVAL if we find a bad argument else 0. 
1105 *************************************************************************************************** */ 
1107 ParseArgs(int argc
, const char *argv
[], const char ** actionPtr
, 
1108         const char ** mountPointPtr
, boolean_t 
* isEjectablePtr
, 
1109           boolean_t 
* isLockedPtr
, boolean_t 
* isSetuidPtr
, boolean_t 
* isDevPtr
) 
1111     int                 result 
= FSUR_INVAL
; 
1112     int                 deviceLength
, doLengthCheck 
= 1; 
1116     /* Must have at least 3 arguments and the action argument must start with a '-' */ 
1117     if ( (argc 
< 3) || (argv
[1][0] != '-') ) { 
1118         DoDisplayUsage( argv 
); 
1122     /* we only support actions Probe, Mount, Force Mount, and Unmount */ 
1124     * actionPtr 
= & argv
[1][1]; 
1126     switch ( argv
[1][1] ) { 
1128         /* action Probe and requires 5 arguments (need the flags) */ 
1130                 DoDisplayUsage( argv 
); 
1138                 /* Note: the device argument in argv[2] is checked further down but ignored. */ 
1139             * mountPointPtr 
= argv
[3]; 
1140             index 
= 0; /* No isEjectable/isLocked flags for unmount. */ 
1144         case FSUC_MOUNT_FORCE
: 
1145             /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */ 
1147                 DoDisplayUsage( argv 
); 
1150                 * mountPointPtr 
= argv
[3]; 
1176                         if (isdigit(argv
[2][0])) { 
1178                                 gJournalSize 
= strtoul(argv
[2], &ptr
, 0); 
1180                                         gJournalSize 
*= get_multiplier(*ptr
); 
1191                 case FSUC_UNJNL_RAW
: 
1196                 case FSUC_JNLINFS_RAW
: 
1201                 case FSUC_EXTJNL_RAW
: 
1213             DoDisplayUsage( argv 
); 
1218     /* Make sure device (argv[2]) is something reasonable */ 
1219     deviceLength 
= strlen( argv
[2] ); 
1220     if ( doLengthCheck 
&& (deviceLength 
< 3 || deviceLength 
> NAME_MAX
) ) { 
1221         DoDisplayUsage( argv 
); 
1226         /* Flags: removable/fixed. */ 
1227         if ( 0 == strcmp(argv
[index
],"removable") ) { 
1228             * isEjectablePtr 
= 1; 
1229         } else if ( 0 == strcmp(argv
[index
],"fixed") ) { 
1230             * isEjectablePtr 
= 0; 
1232             printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index
,argv
[index
]); 
1235         /* Flags: readonly/writable. */ 
1236         if ( 0 == strcmp(argv
[index
+1],"readonly") ) { 
1238         } else if ( 0 == strcmp(argv
[index
+1],"writable") ) { 
1241             printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index
,argv
[index
+1]); 
1245                     /* Flags: suid/nosuid. */ 
1246                     if ( 0 == strcmp(argv
[index
+2],"suid") ) { 
1248                     } else if ( 0 == strcmp(argv
[index
+2],"nosuid") ) { 
1251                         printf("hfs.util: ERROR: unrecognized flag (suid/nosuid) argv[%d]='%s'\n",index
,argv
[index
+2]); 
1254                     /* Flags: dev/nodev. */ 
1255                     if ( 0 == strcmp(argv
[index
+3],"dev") ) { 
1257                     } else if ( 0 == strcmp(argv
[index
+3],"nodev") ) { 
1260                         printf("hfs.util: ERROR: unrecognized flag (dev/nodev) argv[%d]='%s'\n",index
,argv
[index
+3]); 
1275 /* *************************************** DoDisplayUsage ******************************************** 
1277     This routine will do a printf of the correct usage for this utility. 
1279     argv - array of arguments. 
1282 *************************************************************************************************** */ 
1284 DoDisplayUsage(const char *argv
[]) 
1286     printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv
[0]); 
1287     printf("action_arg:\n"); 
1288     printf("       -%c (Probe for mounting)\n", FSUC_PROBE
); 
1289     printf("       -%c (Mount)\n", FSUC_MOUNT
); 
1290     printf("       -%c (Unmount)\n", FSUC_UNMOUNT
); 
1291     printf("       -%c (Force Mount)\n", FSUC_MOUNT_FORCE
); 
1292 #ifdef HFS_UUID_SUPPORT 
1293     printf("       -%c (Get UUID Key)\n", FSUC_GETUUID
); 
1294     printf("       -%c (Set UUID Key)\n", FSUC_SETUUID
); 
1295 #endif HFS_UUID_SUPPORT 
1296     printf("       -%c (Adopt permissions)\n", FSUC_ADOPT
); 
1297         printf("       -%c (Make a file system journaled)\n", FSUC_MKJNL
); 
1298         printf("       -%c (Turn off journaling on a file system)\n", FSUC_UNJNL
); 
1299         printf("       -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW
); 
1300         printf("       -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW
); 
1301         printf("       -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW
); 
1302         printf("       -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO
); 
1303     printf("device_arg:\n"); 
1304     printf("       device we are acting upon (for example, 'disk0s2')\n"); 
1305     printf("       if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL
, FSUC_UNJNL
); 
1306         printf("       name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n"); 
1307     printf("mount_point_arg:\n"); 
1308     printf("       required for Mount and Force Mount \n"); 
1310     printf("       required for Mount, Force Mount and Probe\n"); 
1311     printf("       indicates removable or fixed (for example 'fixed')\n"); 
1312     printf("       indicates readonly or writable (for example 'readonly')\n"); 
1313     printf("       indicates suid or nosuid (for example 'suid')\n"); 
1314     printf("       indicates dev or nodev (for example 'dev')\n"); 
1315     printf("Examples:\n"); 
1316     printf("       %s -p disk0s2 fixed writable\n", argv
[0]); 
1317     printf("       %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv
[0]); 
1321 } /* DoDisplayUsage */ 
1327         Given a path to a device, determine if a volume is mounted on that 
1328         device.  If there is an HFS volume, return its path and FSUR_IO_SUCCESS. 
1329         If there is a non-HFS volume, return FSUR_UNRECOGNIZED.  If there is 
1330         no volume mounted on the device, set *pathPtr to NULL and return 
1333         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED 
1336 GetHFSMountPoint(const char *deviceNamePtr
, char **pathPtr
) 
1342         /* Assume no mounted volume found */     
1344         result 
= FSUR_IO_SUCCESS
;        
1346         numMounts 
= getmntinfo(&buf
, MNT_NOWAIT
); 
1348                 return FSUR_IO_FAIL
; 
1350         for (i
=0; i
<numMounts
; ++i
) { 
1351                 if (!strcmp(deviceNamePtr
, buf
[i
].f_mntfromname
)) { 
1352                         /* Found a mounted volume; check the type */ 
1353                         if (!strcmp(buf
[i
].f_fstypename
, "hfs")) { 
1354                                 *pathPtr 
= buf
[i
].f_mntonname
; 
1355                                 /* result = FSUR_IO_SUCCESS, above */ 
1357                                 result 
= FSUR_UNRECOGNIZED
; 
1370         Read the Master Directory Block or Volume Header Block from an HFS, 
1371         HFS Plus, or HFSX volume into a caller-supplied buffer.  Return the 
1372         offset of an embedded HFS Plus volume (or 0 if not embedded HFS Plus). 
1373         Return a pointer to the volume UUID in the Finder Info. 
1375         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED 
1378 ReadHeaderBlock(int fd
, void *bufPtr
, off_t 
*startOffset
, VolumeUUID 
**finderInfoUUIDPtr
) 
1381         HFSMasterDirectoryBlock 
* mdbPtr
; 
1382         HFSPlusVolumeHeader 
* volHdrPtr
; 
1388          * Read the HFS Master Directory Block or Volume Header from sector 2 
1391         result 
= readAt(fd
, bufPtr
, (off_t
)(2 * HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
); 
1392         if (result 
!= FSUR_IO_SUCCESS
) 
1396          * If this is a wrapped HFS Plus volume, read the Volume Header from 
1397          * sector 2 of the embedded volume. 
1399         if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord 
&& 
1400                 OSSwapBigToHostInt16(mdbPtr
->drEmbedSigWord
) == kHFSPlusSigWord
) { 
1401                 result 
= GetEmbeddedHFSPlusVol(mdbPtr
, startOffset
); 
1402                 if (result 
!= FSUR_IO_SUCCESS
) 
1404                 result 
= readAt(fd
, bufPtr
, *startOffset 
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
); 
1405                 if (result 
!= FSUR_IO_SUCCESS
) 
1410          * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX 
1411          * volumes (including wrapped HFS Plus).  Verify the signature and grab the 
1412          * UUID from the Finder Info. 
1414         if (OSSwapBigToHostInt16(mdbPtr
->drSigWord
) == kHFSSigWord
) { 
1415             *finderInfoUUIDPtr 
= (VolumeUUID 
*)(&mdbPtr
->drFndrInfo
[6]); 
1416         } else if (OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSPlusSigWord 
|| 
1417                                 OSSwapBigToHostInt16(volHdrPtr
->signature
) == kHFSXSigWord
) { 
1418             *finderInfoUUIDPtr 
= (VolumeUUID 
*)&volHdrPtr
->finderInfo
[24]; 
1420                 result 
= FSUR_UNRECOGNIZED
; 
1431         Read the UUID from an unmounted volume, by doing direct access to the device. 
1432         Assumes the caller has already determined that a volume is not mounted 
1435         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED 
1438 GetVolumeUUIDRaw(const char *deviceNamePtr
, const char *rawName
, VolumeUUID 
*volumeUUIDPtr
) 
1443         VolumeUUID 
*finderInfoUUIDPtr
; 
1447         bufPtr 
= (char *)malloc(HFS_BLOCK_SIZE
); 
1449                 result 
= FSUR_UNRECOGNIZED
; 
1453         fd 
= open( deviceNamePtr
, O_RDONLY
, 0); 
1457                 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s)  open failed (errno = %d).\n", deviceNamePtr
, errno
); 
1459                 if (error 
== EBUSY
) { 
1460                         /* If it was busy, then retry, this time using the raw device */ 
1461                         fd 
= open (rawName
, O_RDONLY
, 0); 
1464                                 fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName
, errno
); 
1466                                 result 
= FSUR_IO_FAIL
; 
1471                         result 
= FSUR_IO_FAIL
; 
1477          * Get the pointer to the volume UUID in the Finder Info 
1479         result 
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
); 
1480         if (result 
!= FSUR_IO_SUCCESS
) 
1484          * Copy the volume UUID out of the Finder Info 
1486         volumeUUIDPtr
->v
.high 
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
); 
1487         volumeUUIDPtr
->v
.low 
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
); 
1490         if (fd 
> 0) close(fd
); 
1491         if (bufPtr
) free(bufPtr
); 
1494         if (result 
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result
); 
1496         return (result 
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS 
: FSUR_IO_FAIL
; 
1503         Write a previously generated UUID to an unmounted volume, by doing direct 
1504         access to the device.  Assumes the caller has already determined that a 
1505         volume is not mounted on the device. 
1507         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED 
1510 SetVolumeUUIDRaw(const char *deviceNamePtr
, VolumeUUID 
*volumeUUIDPtr
) 
1515         VolumeUUID 
*finderInfoUUIDPtr
; 
1518         bufPtr 
= (char *)malloc(HFS_BLOCK_SIZE
); 
1520                 result 
= FSUR_UNRECOGNIZED
; 
1524         fd 
= open( deviceNamePtr
, O_RDWR
, 0); 
1527                 fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno
); 
1529                 result 
= FSUR_IO_FAIL
; 
1534          * Get the pointer to the volume UUID in the Finder Info 
1536         result 
= ReadHeaderBlock(fd
, bufPtr
, &startOffset
, &finderInfoUUIDPtr
); 
1537         if (result 
!= FSUR_IO_SUCCESS
) 
1541          * Update the UUID in the Finder Info 
1543         finderInfoUUIDPtr
->v
.high 
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
); 
1544         finderInfoUUIDPtr
->v
.low 
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
); 
1547          * Write the modified MDB or VHB back to disk 
1549         result 
= writeAt(fd
, bufPtr
, startOffset 
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE
); 
1552         if (fd 
> 0) close(fd
); 
1553         if (bufPtr
) free(bufPtr
); 
1556         if (result 
!= FSUR_IO_SUCCESS
) fprintf(stderr
, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result
); 
1558         return (result 
== FSUR_IO_SUCCESS
) ? FSUR_IO_SUCCESS 
: FSUR_IO_FAIL
; 
1565         Read the UUID from a mounted volume, by calling getattrlist(). 
1566         Assumes the path is the mount point of an HFS volume. 
1568         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
1571 GetVolumeUUIDAttr(const char *path
, VolumeUUID 
*volumeUUIDPtr
) 
1573         struct attrlist alist
; 
1574         struct FinderAttrBuf volFinderInfo
; 
1575         VolumeUUID 
*finderInfoUUIDPtr
; 
1578         /* Set up the attrlist structure to get the volume's Finder Info */ 
1579         alist
.bitmapcount 
= 5; 
1581         alist
.commonattr 
= ATTR_CMN_FNDRINFO
; 
1582         alist
.volattr 
= ATTR_VOL_INFO
; 
1587         /* Get the Finder Info */ 
1588         result 
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0); 
1590                 result 
= FSUR_IO_FAIL
; 
1594         /* Copy the UUID from the Finder Into to caller's buffer */ 
1595         finderInfoUUIDPtr 
= (VolumeUUID 
*)(&volFinderInfo
.finderinfo
[6]); 
1596         volumeUUIDPtr
->v
.high 
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.high
); 
1597         volumeUUIDPtr
->v
.low 
= OSSwapBigToHostInt32(finderInfoUUIDPtr
->v
.low
); 
1598         result 
= FSUR_IO_SUCCESS
; 
1608         Write a UUID to a mounted volume, by calling setattrlist(). 
1609         Assumes the path is the mount point of an HFS volume. 
1611         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
1614 SetVolumeUUIDAttr(const char *path
, VolumeUUID 
*volumeUUIDPtr
) 
1616         struct attrlist alist
; 
1617         struct FinderAttrBuf volFinderInfo
; 
1618         VolumeUUID 
*finderInfoUUIDPtr
; 
1621         /* Set up the attrlist structure to get the volume's Finder Info */ 
1622         alist
.bitmapcount 
= 5; 
1624         alist
.commonattr 
= ATTR_CMN_FNDRINFO
; 
1625         alist
.volattr 
= ATTR_VOL_INFO
; 
1630         /* Get the Finder Info */ 
1631         result 
= getattrlist(path
, &alist
, &volFinderInfo
, sizeof(volFinderInfo
), 0); 
1633                 result 
= FSUR_IO_FAIL
; 
1637         /* Update the UUID in the Finder Info */ 
1638         finderInfoUUIDPtr 
= (VolumeUUID 
*)(&volFinderInfo
.finderinfo
[6]); 
1639         finderInfoUUIDPtr
->v
.high 
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.high
); 
1640         finderInfoUUIDPtr
->v
.low 
= OSSwapHostToBigInt32(volumeUUIDPtr
->v
.low
); 
1642         /* Write the Finder Info back to the volume */ 
1643         result 
= setattrlist(path
, &alist
, &volFinderInfo
.finderinfo
, sizeof(volFinderInfo
.finderinfo
), 0); 
1645                 result 
= FSUR_IO_FAIL
; 
1649         result 
= FSUR_IO_SUCCESS
; 
1659         Return the UUID of an HFS, HFS Plus or HFSX volume.  If there is no UUID and 
1660         we were asked to generate one, then generate a new UUID and write it to the 
1663         Determine whether an HFS volume is mounted on the given device.  If so, we 
1664         need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through 
1665         the filesystem.  If there is no mounted volume, then do direct device access 
1666         with GetVolumeUUIDRaw and SetVolumeUUIDRaw. 
1668         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED 
1672 GetVolumeUUID(const char *deviceNamePtr
, const char *rawName
, VolumeUUID 
*volumeUUIDPtr
, boolean_t generate
) 
1678          * Determine whether a volume is mounted on this device.  If it is HFS, then 
1679          * get the mount point's path.  If it is non-HFS, then we can exit immediately 
1680          * with FSUR_UNRECOGNIZED. 
1682         result 
= GetHFSMountPoint(deviceNamePtr
, &path
); 
1683         if (result 
!= FSUR_IO_SUCCESS
) 
1687          * Get any existing UUID. 
1690                 result 
= GetVolumeUUIDAttr(path
, volumeUUIDPtr
); 
1692                 result 
= GetVolumeUUIDRaw(deviceNamePtr
, rawName
, volumeUUIDPtr
); 
1693         if (result 
!= FSUR_IO_SUCCESS
) 
1697          * If there was no valid UUID, and we were asked to generate one, then 
1698          * generate it and write it back to disk. 
1700         if (generate 
&& (volumeUUIDPtr
->v
.high 
== 0 || volumeUUIDPtr
->v
.low 
== 0)) { 
1701                 GenerateVolumeUUID(volumeUUIDPtr
); 
1703                         result 
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
); 
1705                         result 
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
); 
1706                 /* Fall through to Err_Exit */ 
1718         Write a UUID to an HFS, HFS Plus or HFSX volume. 
1720         Determine whether an HFS volume is mounted on the given device.  If so, we 
1721         need to use SetVolumeUUIDAttr to access the UUID through the filesystem. 
1722         If there is no mounted volume, then do direct device access SetVolumeUUIDRaw. 
1724         Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED 
1727 SetVolumeUUID(const char *deviceNamePtr
, VolumeUUID 
*volumeUUIDPtr
) { 
1732          * Determine whether a volume is mounted on this device.  If it is HFS, then 
1733          * get the mount point's path.  If it is non-HFS, then we can exit immediately 
1734          * with FSUR_UNRECOGNIZED. 
1736         result 
= GetHFSMountPoint(deviceNamePtr
, &path
); 
1737         if (result 
!= FSUR_IO_SUCCESS
) 
1744                 result 
= SetVolumeUUIDAttr(path
, volumeUUIDPtr
); 
1746                 result 
= SetVolumeUUIDRaw(deviceNamePtr
, volumeUUIDPtr
); 
1755  --     GetEmbeddedHFSPlusVol 
1757  --     In: hfsMasterDirectoryBlockPtr 
1758  --     Out: startOffsetPtr - the disk offset at which the HFS+ volume starts 
1759                                 (that is, 2 blocks before the volume header) 
1764 GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock 
* hfsMasterDirectoryBlockPtr
, off_t 
* startOffsetPtr
) 
1766     int         result 
= FSUR_IO_SUCCESS
; 
1767     u_int32_t   allocationBlockSize
, firstAllocationBlock
, startBlock
, blockCount
; 
1769     if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drSigWord
) != kHFSSigWord
) { 
1770         result 
= FSUR_UNRECOGNIZED
; 
1774     allocationBlockSize 
= OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr
->drAlBlkSiz
); 
1775     firstAllocationBlock 
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drAlBlSt
); 
1777     if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedSigWord
) != kHFSPlusSigWord
) { 
1778         result 
= FSUR_UNRECOGNIZED
; 
1782     startBlock 
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.startBlock
); 
1783     blockCount 
= OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr
->drEmbedExtent
.blockCount
); 
1785     if ( startOffsetPtr 
) 
1786         *startOffsetPtr 
= ((u_int64_t
)startBlock 
* (u_int64_t
)allocationBlockSize
) + 
1787                 ((u_int64_t
)firstAllocationBlock 
* (u_int64_t
)HFS_BLOCK_SIZE
); 
1797  --     GetNameFromHFSPlusVolumeStartingAt 
1799  --     Caller's responsibility to allocate and release memory for the converted string. 
1801  --     Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
1805 GetNameFromHFSPlusVolumeStartingAt(int fd
, off_t hfsPlusVolumeOffset
, unsigned char * name_o
) 
1807     int                                 result 
= FSUR_IO_SUCCESS
; 
1808     u_int32_t                           blockSize
; 
1809     char                        *       bufPtr 
= NULL
; 
1810     HFSPlusVolumeHeader         
*       volHdrPtr
; 
1811     BTNodeDescriptor            
*       bTreeNodeDescriptorPtr
; 
1812     u_int32_t                           catalogNodeSize
; 
1814         u_int32_t catalogExtCount
; 
1815         HFSPlusExtentDescriptor 
*catalogExtents 
= NULL
; 
1817     volHdrPtr 
= (HFSPlusVolumeHeader 
*)malloc(HFS_BLOCK_SIZE
); 
1818     if ( ! volHdrPtr 
) { 
1819         result 
= FSUR_IO_FAIL
; 
1824      * Read the Volume Header 
1825      * (This is a little redundant for a pure, unwrapped HFS+ volume) 
1827     result 
= readAt( fd
, volHdrPtr
, hfsPlusVolumeOffset 
+ (off_t
)(2*HFS_BLOCK_SIZE
), HFS_BLOCK_SIZE 
); 
1828     if (result 
== FSUR_IO_FAIL
) { 
1830         fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n"); 
1832         goto Return
; // return FSUR_IO_FAIL 
1835     /* Verify that it is an HFS+ volume. */ 
1837     if (OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSPlusSigWord 
&& 
1838         OSSwapBigToHostInt16(volHdrPtr
->signature
) != kHFSXSigWord
) { 
1839         result 
= FSUR_IO_FAIL
; 
1841         fprintf(stderr
, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n"); 
1846     blockSize 
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
); 
1847     catalogExtents 
= (HFSPlusExtentDescriptor 
*) malloc(sizeof(HFSPlusExtentRecord
)); 
1848     if ( ! catalogExtents 
) { 
1849         result 
= FSUR_IO_FAIL
; 
1852         bcopy(volHdrPtr
->catalogFile
.extents
, catalogExtents
, sizeof(HFSPlusExtentRecord
)); 
1853         catalogExtCount 
= kHFSPlusExtentDensity
; 
1855         /* if there are overflow catalog extents, then go get them */ 
1856         if (OSSwapBigToHostInt32(catalogExtents
[7].blockCount
) != 0) { 
1857                 result 
= GetCatalogOverflowExtents(fd
, hfsPlusVolumeOffset
, volHdrPtr
, &catalogExtents
, &catalogExtCount
); 
1858                 if (result 
!= FSUR_IO_SUCCESS
) 
1862         /* Read the header node of the catalog B-Tree */ 
1864         result 
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
, 
1865                                                         catalogExtCount
, catalogExtents
, 
1866                                                         &catalogNodeSize
, &leafNode
); 
1867         if (result 
!= FSUR_IO_SUCCESS
) 
1870         /* Read the first leaf node of the catalog b-tree */ 
1872     bufPtr 
= (char *)malloc(catalogNodeSize
); 
1874         result 
= FSUR_IO_FAIL
; 
1878     bTreeNodeDescriptorPtr 
= (BTNodeDescriptor 
*)bufPtr
; 
1880         result 
= ReadFile(fd
, bufPtr
, (off_t
) leafNode 
* (off_t
) catalogNodeSize
, catalogNodeSize
, 
1881                                                 hfsPlusVolumeOffset
, blockSize
, 
1882                                                 catalogExtCount
, catalogExtents
); 
1883     if (result 
== FSUR_IO_FAIL
) { 
1885         fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n"); 
1887         goto Return
; // return FSUR_IO_FAIL 
1893         HFSPlusCatalogKey       
*       k
; 
1896         if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
) < 1) { 
1897             result 
= FSUR_IO_FAIL
; 
1899                         fprintf(stderr
, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n"); 
1904         // Get the offset (in bytes) of the first record from the list of offsets at the end of the node. 
1906         p 
= bufPtr 
+ catalogNodeSize 
- sizeof(u_int16_t
); // pointer arithmetic in bytes 
1909         // Get a pointer to the first record. 
1911         p 
= bufPtr 
+ OSSwapBigToHostInt16(*v
); // pointer arithmetic in bytes 
1912         k 
= (HFSPlusCatalogKey 
*)p
; 
1914         // There should be only one record whose parent is the root parent.  It should be the first record. 
1916         if (OSSwapBigToHostInt32(k
->parentID
) != kHFSRootParentID
) { 
1917             result 
= FSUR_IO_FAIL
; 
1919             fprintf(stderr
, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n"); 
1924         if ((OSSwapBigToHostInt16(k
->nodeName
.length
) > 
1925                 (sizeof(k
->nodeName
.unicode
) / sizeof(k
->nodeName
.unicode
[0]))) || 
1926                 OSSwapBigToHostInt16(k
->nodeName
.length
) > 255) { 
1927                 result 
= FSUR_IO_FAIL
; 
1929                 fprintf(stderr
, "hfs.util: ERROR:  k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k
->nodeName
.length
)); 
1934         /* Extract the name of the root directory */ 
1937             HFSUniStr255 
*swapped
; 
1940             swapped 
= (HFSUniStr255 
*)malloc(sizeof(HFSUniStr255
)); 
1941             if (swapped 
== NULL
) { 
1942                 result 
= FSUR_IO_FAIL
; 
1945             swapped
->length 
= OSSwapBigToHostInt16(k
->nodeName
.length
); 
1947             for (i
=0; i
<swapped
->length
; i
++) { 
1948                 swapped
->unicode
[i
] = OSSwapBigToHostInt16(k
->nodeName
.unicode
[i
]); 
1950             swapped
->unicode
[i
] = 0; 
1951             cfstr 
= CFStringCreateWithCharacters(kCFAllocatorDefault
, swapped
->unicode
, swapped
->length
); 
1952             (void) CFStringGetCString(cfstr
, (char *)name_o
, NAME_MAX
, kCFStringEncodingUTF8
); 
1958     result 
= FSUR_IO_SUCCESS
; 
1962                 free((char*) volHdrPtr
); 
1965                 free((char*) catalogExtents
); 
1968                 free((char*)bufPtr
); 
1972 } /* GetNameFromHFSPlusVolumeStartingAt */ 
1976         BTNodeDescriptor        node
; 
1978 } __attribute__((aligned(2), packed
)) HeaderRec
, *HeaderPtr
; 
1983  --     Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
1987 GetBTreeNodeInfo(int fd
, off_t hfsPlusVolumeOffset
, u_int32_t blockSize
, 
1988                                 u_int32_t extentCount
, const HFSPlusExtentDescriptor 
*extentList
, 
1989                                 u_int32_t 
*nodeSize
, u_int32_t 
*firstLeafNode
) 
1992         HeaderRec 
* bTreeHeaderPtr 
= NULL
; 
1994         bTreeHeaderPtr 
= (HeaderRec 
*) malloc(HFS_BLOCK_SIZE
); 
1995         if (bTreeHeaderPtr 
== NULL
) 
1996                 return (FSUR_IO_FAIL
); 
1998         /* Read the b-tree header node */ 
2000         result 
= ReadFile(fd
, bTreeHeaderPtr
, 0, HFS_BLOCK_SIZE
, 
2001                                         hfsPlusVolumeOffset
, blockSize
, 
2002                                         extentCount
, extentList
); 
2003         if ( result 
== FSUR_IO_FAIL 
) { 
2005                 fprintf(stderr
, "hfs.util: ERROR: reading header node failed\n"); 
2010         if ( bTreeHeaderPtr
->node
.kind 
!= kBTHeaderNode 
) { 
2011                 result 
= FSUR_IO_FAIL
; 
2013                 fprintf(stderr
, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n"); 
2018         *nodeSize 
= OSSwapBigToHostInt16(bTreeHeaderPtr
->header
.nodeSize
); 
2020         if (OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.leafRecords
) == 0) 
2023                 *firstLeafNode 
= OSSwapBigToHostInt32(bTreeHeaderPtr
->header
.firstLeafNode
); 
2026         free((char*) bTreeHeaderPtr
); 
2030 } /* GetBTreeNodeInfo */ 
2036  --     Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
2040 GetCatalogOverflowExtents(int fd
, off_t hfsPlusVolumeOffset
, 
2041                 HFSPlusVolumeHeader 
*volHdrPtr
, 
2042                 HFSPlusExtentDescriptor 
**catalogExtents
, 
2043                 u_int32_t 
*catalogExtCount
) 
2046         u_int32_t numRecords
; 
2049         u_int32_t blockSize
; 
2050     BTNodeDescriptor 
* bTreeNodeDescriptorPtr
; 
2051         HFSPlusExtentDescriptor 
* extents
; 
2053     char *      bufPtr 
= NULL
; 
2057         blockSize 
= OSSwapBigToHostInt32(volHdrPtr
->blockSize
); 
2058         listsize 
= *catalogExtCount 
* sizeof(HFSPlusExtentDescriptor
); 
2059         extents 
= *catalogExtents
; 
2060         offset 
= (off_t
)OSSwapBigToHostInt32(volHdrPtr
->extentsFile
.extents
[0].startBlock
) * 
2063         /* Read the header node of the extents B-Tree */ 
2065         result 
= GetBTreeNodeInfo(fd
, hfsPlusVolumeOffset
, blockSize
, 
2066                         kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
, 
2067                     &nodeSize
, &leafNode
); 
2068         if (result 
!= FSUR_IO_SUCCESS 
|| leafNode 
== 0) 
2071         /* Calculate the logical position of the first leaf node */ 
2073         offset 
= (off_t
) leafNode 
* (off_t
) nodeSize
; 
2075         /* Read the first leaf node of the extents b-tree */ 
2077     bufPtr 
= (char *)malloc(nodeSize
); 
2079                 result 
= FSUR_IO_FAIL
; 
2083         bTreeNodeDescriptorPtr 
= (BTNodeDescriptor 
*)bufPtr
; 
2086         result 
= ReadFile(fd
, bufPtr
, offset
, nodeSize
, 
2087                                         hfsPlusVolumeOffset
, blockSize
, 
2088                                         kHFSPlusExtentDensity
, volHdrPtr
->extentsFile
.extents
); 
2089         if ( result 
== FSUR_IO_FAIL 
) { 
2091                 fprintf(stderr
, "hfs.util: ERROR: reading first leaf failed\n"); 
2096         if (bTreeNodeDescriptorPtr
->kind 
!= kBTLeafNode
) { 
2097                 result 
= FSUR_IO_FAIL
; 
2101         numRecords 
= OSSwapBigToHostInt16(bTreeNodeDescriptorPtr
->numRecords
); 
2102         for (i 
= 1; i 
<= numRecords
; ++i
) { 
2105                 HFSPlusExtentKey 
* k
; 
2108                  * Get the offset (in bytes) of the record from the 
2109                  * list of offsets at the end of the node 
2111                 p 
= bufPtr 
+ nodeSize 
- (sizeof(u_int16_t
) * i
); 
2114                 /* Get a pointer to the record */ 
2116                 p 
= bufPtr 
+ OSSwapBigToHostInt16(*v
); /* pointer arithmetic in bytes */ 
2117                 k 
= (HFSPlusExtentKey 
*)p
; 
2119                 if (OSSwapBigToHostInt32(k
->fileID
) != kHFSCatalogFileID
) 
2122                 /* grow list and copy additional extents */ 
2123                 listsize 
+= sizeof(HFSPlusExtentRecord
); 
2124                 extents 
= (HFSPlusExtentDescriptor 
*) realloc(extents
, listsize
); 
2125                 bcopy(p 
+ OSSwapBigToHostInt16(k
->keyLength
) + sizeof(u_int16_t
), 
2126                         &extents
[*catalogExtCount
], sizeof(HFSPlusExtentRecord
)); 
2128                 *catalogExtCount 
+= kHFSPlusExtentDensity
; 
2129                 *catalogExtents 
= extents
; 
2132         if ((leafNode 
= OSSwapBigToHostInt32(bTreeNodeDescriptorPtr
->fLink
)) != 0) { 
2134                 offset 
= (off_t
) leafNode 
* (off_t
) nodeSize
; 
2149  *      LogicalToPhysical - Map a logical file position and size to volume-relative physical 
2150  *      position and number of contiguous bytes at that position. 
2153  *              logicalOffset   Logical offset in bytes from start of file 
2154  *              length                  Maximum number of bytes to map 
2155  *              blockSize               Number of bytes per allocation block 
2156  *              extentCount             Number of extents in file 
2157  *              extentList              The file's extents 
2160  *              physicalOffset  Physical offset in bytes from start of volume 
2161  *              availableBytes  Number of bytes physically contiguous (up to length) 
2163  *      Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
2165 static int      LogicalToPhysical(off_t offset
, ssize_t length
, u_int32_t blockSize
, 
2166                                                         u_int32_t extentCount
, const HFSPlusExtentDescriptor 
*extentList
, 
2167                                                         off_t 
*physicalOffset
, ssize_t 
*availableBytes
) 
2170         u_int32_t       logicalBlock
; 
2172         u_int32_t       blockCount 
= 0; 
2174         /* Determine allocation block containing logicalOffset */ 
2175         logicalBlock 
= offset 
/ blockSize
;      /* This can't overflow for valid volumes */ 
2176         offset 
%= blockSize
;    /* Offset from start of allocation block */ 
2178         /* Find the extent containing logicalBlock */ 
2179         for (extent 
= 0; extent 
< extentCount
; ++extent
) 
2181                 blockCount 
= OSSwapBigToHostInt32(extentList
[extent
].blockCount
); 
2183                 if (blockCount 
== 0) 
2184                         return FSUR_IO_FAIL
;    /* Tried to map past physical end of file */ 
2186                 if (logicalBlock 
< blockCount
) 
2187                         break;                          /* Found it! */ 
2189                 logicalBlock 
-= blockCount
; 
2192         if (extent 
>= extentCount
) 
2193                 return FSUR_IO_FAIL
;            /* Tried to map past physical end of file */ 
2196          *      When we get here, extentList[extent] is the extent containing logicalOffset. 
2197          *      The desired allocation block is logicalBlock blocks into the extent. 
2200         /* Compute the physical starting position */ 
2201         temp 
= OSSwapBigToHostInt32(extentList
[extent
].startBlock
) + logicalBlock
;      /* First physical block */ 
2202         temp 
*= blockSize
;      /* Byte offset of first physical block */ 
2203         *physicalOffset 
= temp 
+ offset
; 
2205         /* Compute the available contiguous bytes. */ 
2206         temp 
= blockCount 
- logicalBlock
;       /* Number of blocks available in extent */ 
2208         temp 
-= offset
;                                         /* Number of bytes available */ 
2211                 *availableBytes 
= temp
; 
2213                 *availableBytes 
= length
; 
2215         return FSUR_IO_SUCCESS
; 
2221  *      ReadFile - Read bytes from a file.  Handles cases where the starting and/or 
2222  *      ending position are not allocation or device block aligned. 
2225  *              fd                      Descriptor for reading the volume 
2226  *              buffer          The bytes are read into here 
2227  *              offset          Offset in file to start reading 
2228  *              length          Number of bytes to read 
2229  *              volOffset       Byte offset from start of device to start of volume 
2230  *              blockSize       Number of bytes per allocation block 
2231  *              extentCount     Number of extents in file 
2232  *              extentList      The file's exents 
2234  *      Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
2236 static int      ReadFile(int fd
, void *buffer
, off_t offset
, ssize_t length
, 
2237                                         off_t volOffset
, u_int32_t blockSize
, 
2238                                         u_int32_t extentCount
, const HFSPlusExtentDescriptor 
*extentList
) 
2240         int             result 
= FSUR_IO_SUCCESS
; 
2246                 result 
= LogicalToPhysical(offset
, length
, blockSize
, extentCount
, extentList
, 
2247                                                                         &physOffset
, &physLength
); 
2248                 if (result 
!= FSUR_IO_SUCCESS
) 
2251                 result 
= readAt(fd
, buffer
, volOffset
+physOffset
, physLength
); 
2252                 if (result 
!= FSUR_IO_SUCCESS
) 
2255                 length 
-= physLength
; 
2256                 offset 
+= physLength
; 
2257                 buffer 
= (char *) buffer 
+ physLength
; 
2264  --     readAt = lseek() + read() 
2266  --     Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
2271 readAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length 
) 
2276     void *              rawData 
= NULL
; 
2279     ssize_t             dataOffset 
= 0; 
2280     int                 result 
= FSUR_IO_SUCCESS
; 
2282     if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) { 
2284         fprintf(stderr
, "hfs.util: readAt: couldn't determine block size of device.\n"); 
2286                 result 
= FSUR_IO_FAIL
; 
2289     /* put offset and length in terms of device blocksize */ 
2290     rawOffset 
= offset 
/ blocksize 
* blocksize
; 
2291     dataOffset 
= offset 
- rawOffset
; 
2292     rawLength 
= ((length 
+ dataOffset 
+ blocksize 
- 1) / blocksize
) * blocksize
; 
2293     rawData 
= malloc(rawLength
); 
2294     if (rawData 
== NULL
) { 
2295                 result 
= FSUR_IO_FAIL
; 
2299     lseekResult 
= lseek( fd
, rawOffset
, SEEK_SET 
); 
2300     if ( lseekResult 
!= rawOffset 
) { 
2301         result 
= FSUR_IO_FAIL
; 
2305     readResult 
= read(fd
, rawData
, rawLength
); 
2306     if ( readResult 
!= rawLength 
) { 
2308                 fprintf(stderr
, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno
); 
2310         result 
= FSUR_IO_FAIL
; 
2313     bcopy(rawData 
+ dataOffset
, bufPtr
, length
); 
2324  --     writeAt = lseek() + write() 
2326  --     Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL 
2331 writeAt( int fd
, void * bufPtr
, off_t offset
, ssize_t length 
) 
2335     ssize_t             bytestransferred
; 
2336     void *              rawData 
= NULL
; 
2339     ssize_t             dataOffset 
= 0; 
2340     int                 result 
= FSUR_IO_SUCCESS
; 
2342     if (ioctl(fd
, DKIOCGETBLOCKSIZE
, &blocksize
) < 0) { 
2344         fprintf(stderr
, "hfs.util: couldn't determine block size of device.\n"); 
2346                 result 
= FSUR_IO_FAIL
; 
2349     /* put offset and length in terms of device blocksize */ 
2350     rawOffset 
= offset 
/ blocksize 
* blocksize
; 
2351     dataOffset 
= offset 
- rawOffset
; 
2352     rawLength 
= ((length 
+ dataOffset 
+ blocksize 
- 1) / blocksize
) * blocksize
; 
2353     rawData 
= malloc(rawLength
); 
2354     if (rawData 
== NULL
) { 
2355                 result 
= FSUR_IO_FAIL
; 
2359     deviceoffset 
= lseek( fd
, rawOffset
, SEEK_SET 
); 
2360     if ( deviceoffset 
!= rawOffset 
) { 
2361         result 
= FSUR_IO_FAIL
; 
2365         /* If the write isn't block-aligned, read the existing data before writing the new data: */ 
2366         if (((rawOffset 
% blocksize
) != 0) || ((rawLength 
% blocksize
) != 0)) { 
2367                 bytestransferred 
= read(fd
, rawData
, rawLength
); 
2368             if ( bytestransferred 
!= rawLength 
) { 
2370                 fprintf(stderr
, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno
); 
2372                 result 
= FSUR_IO_FAIL
; 
2377     bcopy(bufPtr
, rawData 
+ dataOffset
, length
);        /* Copy in the new data */ 
2379     deviceoffset 
= lseek( fd
, rawOffset
, SEEK_SET 
); 
2380     if ( deviceoffset 
!= rawOffset 
) { 
2381         result 
= FSUR_IO_FAIL
; 
2385     bytestransferred 
= write(fd
, rawData
, rawLength
); 
2386     if ( bytestransferred 
!= rawLength 
) { 
2388                 fprintf(stderr
, "writeAt: attempt to write data to device failed?!"); 
2390         result 
= FSUR_IO_FAIL
; 
2395     if (rawData
) free(rawData
); 
2403  * Get kernel's encoding bias. 
2409         size_t buflen 
= sizeof(int); 
2413         if (getvfsbyname("hfs", &vfc
) < 0) 
2417         mib
[1] = vfc
.vfc_typenum
; 
2418         mib
[2] = HFS_ENCODINGBIAS
; 
2420         if (sysctl(mib
, 3, &hint
, &buflen
, NULL
, 0) < 0) 
2427 /****************************************************************************** 
2429  *  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 
2431  *****************************************************************************/ 
2433 #define DBHANDLESIGNATURE 0x75917737 
2435 /* Flag values for operation options: */ 
2436 #define DBMARKPOSITION 1 
2438 static char gVSDBPath
[] = "/var/db/volinfo.database"; 
2440 #define MAXIOMALLOC 16384 
2442 /* Database layout: */ 
2449         char statusFlags
[8]; 
2456         struct VSDBRecord record
; 
2460 #define DBKEYSEPARATOR ':' 
2461 #define DBBLANKSPACE ' ' 
2462 #define DBRECORDTERMINATOR '\n' 
2464 /* In-memory data structures: */ 
2467     unsigned long signature
; 
2470     off_t recordPosition
; 
2473 typedef struct VSDBState 
*VSDBStatePtr
; 
2477 /* Internal function prototypes: */ 
2478 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
); 
2479 static int UnlockDB(VSDBStatePtr dbstateptr
); 
2481 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID 
*volumeID
, struct VSDBEntry 
*dbentry
, unsigned long options
); 
2482 static int AddVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry 
*dbentry
); 
2483 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry 
*dbentry
); 
2484 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry 
*dbentry
); 
2485 static int CompareVSDBKeys(struct VSDBKey 
*key1
, struct VSDBKey 
*key2
); 
2487 static void FormatULong(unsigned long u
, char *s
); 
2488 static void FormatUUID(VolumeUUID 
*volumeID
, char *UUIDField
); 
2489 static void FormatDBKey(VolumeUUID 
*volumeID
, struct VSDBKey 
*dbkey
); 
2490 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord 
*dbrecord
); 
2491 static void FormatDBEntry(VolumeUUID 
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry 
*dbentry
); 
2492 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
); 
2496 /****************************************************************************** 
2498  *  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 
2500  *****************************************************************************/ 
2502 void GenerateVolumeUUID(VolumeUUID 
*newVolumeID
) { 
2504         char randomInputBuffer
[26]; 
2505         unsigned char digest
[20]; 
2510         char sysctlstring
[128]; 
2512         double sysloadavg
[3]; 
2513         struct vmtotal sysvmtotal
; 
2516                 /* Initialize the SHA-1 context for processing: */ 
2517                 SHA1_Init(&context
); 
2519                 /* Now process successive bits of "random" input to seed the process: */ 
2521                 /* The current system's uptime: */ 
2523                 SHA1_Update(&context
, &uptime
, sizeof(uptime
)); 
2525                 /* The kernel's boot time: */ 
2527                 mib
[1] = KERN_BOOTTIME
; 
2528                 datalen 
= sizeof(sysdata
); 
2529                 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0); 
2530                 SHA1_Update(&context
, &sysdata
, datalen
); 
2532                 /* The system's host id: */ 
2534                 mib
[1] = KERN_HOSTID
; 
2535                 datalen 
= sizeof(sysdata
); 
2536                 sysctl(mib
, 2, &sysdata
, &datalen
, NULL
, 0); 
2537                 SHA1_Update(&context
, &sysdata
, datalen
); 
2539                 /* The system's host name: */ 
2541                 mib
[1] = KERN_HOSTNAME
; 
2542                 datalen 
= sizeof(sysctlstring
); 
2543                 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0); 
2544                 SHA1_Update(&context
, sysctlstring
, datalen
); 
2546                 /* The running kernel's OS release string: */ 
2548                 mib
[1] = KERN_OSRELEASE
; 
2549                 datalen 
= sizeof(sysctlstring
); 
2550                 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0); 
2551                 SHA1_Update(&context
, sysctlstring
, datalen
); 
2553                 /* The running kernel's version string: */ 
2555                 mib
[1] = KERN_VERSION
; 
2556                 datalen 
= sizeof(sysctlstring
); 
2557                 sysctl(mib
, 2, sysctlstring
, &datalen
, NULL
, 0); 
2558                 SHA1_Update(&context
, sysctlstring
, datalen
); 
2560                 /* The system's load average: */ 
2561                 datalen 
= sizeof(sysloadavg
); 
2562                 getloadavg(sysloadavg
, 3); 
2563                 SHA1_Update(&context
, &sysloadavg
, datalen
); 
2565                 /* The system's VM statistics: */ 
2568                 datalen 
= sizeof(sysvmtotal
); 
2569                 sysctl(mib
, 2, &sysvmtotal
, &datalen
, NULL
, 0); 
2570                 SHA1_Update(&context
, &sysvmtotal
, datalen
); 
2572                 /* The current GMT (26 ASCII characters): */ 
2574                 strncpy(randomInputBuffer
, asctime(gmtime(&now
)), 26);  /* "Mon Mar 27 13:46:26 2000" */ 
2575                 SHA1_Update(&context
, randomInputBuffer
, 26); 
2577                 /* Pad the accumulated input and extract the final digest hash: */ 
2578                 SHA1_Final(digest
, &context
); 
2580                 memcpy(newVolumeID
, digest
, sizeof(*newVolumeID
)); 
2581         } while ((newVolumeID
->v
.high 
== 0) || (newVolumeID
->v
.low 
== 0)); 
2586 void ConvertVolumeUUIDStringToUUID(const char *UUIDString
, VolumeUUID 
*volumeID
) { 
2589         u_int32_t nextdigit
; 
2594         for (i 
= 0; (i 
< VOLUMEUUIDLENGTH
) && ((c 
= UUIDString
[i
]) != (char)0) ; ++i
) { 
2595                 if ((c 
>= '0') && (c 
<= '9')) { 
2596                         nextdigit 
= c 
- '0'; 
2597                 } else if ((c 
>= 'A') && (c 
<= 'F')) { 
2598                         nextdigit 
= c 
- 'A' + 10; 
2599                 } else if ((c 
>= 'a') && (c 
<= 'f')) { 
2600                         nextdigit 
= c 
- 'a' + 10; 
2604                 carry 
= ((low 
& 0xF0000000) >> 28) & 0x0000000F; 
2605                 high 
= (high 
<< 4) | carry
; 
2606                 low 
= (low 
<< 4) | nextdigit
; 
2609         volumeID
->v
.high 
= high
; 
2610         volumeID
->v
.low 
= low
; 
2615 void ConvertVolumeUUIDToString(VolumeUUID 
*volumeID
, char *UUIDString
) { 
2616         FormatUUID(volumeID
, UUIDString
); 
2617         *(UUIDString
+16) = (char)0;             /* Append a terminating null character */ 
2622 int OpenVolumeStatusDB(VolumeStatusDBHandle 
*DBHandlePtr
) { 
2623         VSDBStatePtr dbstateptr
; 
2625         *DBHandlePtr 
= NULL
; 
2627         dbstateptr 
= (VSDBStatePtr
)malloc(sizeof(*dbstateptr
)); 
2628         if (dbstateptr 
== NULL
) { 
2632         dbstateptr
->dbmode 
= O_RDWR
; 
2633         dbstateptr
->dbfile 
= open(gVSDBPath
, O_RDWR 
| O_CREAT
, S_IRUSR 
| S_IWUSR 
| S_IRGRP 
| S_IROTH
); 
2634         if (dbstateptr
->dbfile 
== -1) { 
2636                    The file couldn't be opened for read/write access: 
2637                    try read-only access before giving up altogether. 
2639                 dbstateptr
->dbmode 
= O_RDONLY
; 
2640                 dbstateptr
->dbfile 
= open(gVSDBPath
, O_RDONLY 
| O_CREAT
, S_IRUSR 
| S_IWUSR 
| S_IRGRP 
| S_IROTH
); 
2641                 if (dbstateptr
->dbfile 
== -1) { 
2646         dbstateptr
->signature 
= DBHANDLESIGNATURE
; 
2647         *DBHandlePtr 
= (VolumeStatusDBHandle
)dbstateptr
; 
2653 int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID 
*volumeID
, unsigned long *VolumeStatus
) { 
2654         VSDBStatePtr dbstateptr 
= (VSDBStatePtr
)DBHandle
; 
2655         struct VSDBEntry dbentry
; 
2658         if (dbstateptr
->signature 
!= DBHANDLESIGNATURE
) return EINVAL
; 
2660         if ((result 
= LockDB(dbstateptr
, LOCK_SH
)) != 0) return result
; 
2662         if ((result 
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, &dbentry
, 0)) != 0) { 
2665         *VolumeStatus 
= VOLUME_RECORDED 
| ConvertHexStringToULong(dbentry
.record
.statusFlags
, sizeof(dbentry
.record
.statusFlags
)); 
2670         UnlockDB(dbstateptr
); 
2676 int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID 
*volumeID
, unsigned long VolumeStatus
) { 
2677         VSDBStatePtr dbstateptr 
= (VSDBStatePtr
)DBHandle
; 
2678         struct VSDBEntry dbentry
; 
2681         if (dbstateptr
->signature 
!= DBHANDLESIGNATURE
) return EINVAL
; 
2682         if (VolumeStatus 
& ~VOLUME_VALIDSTATUSBITS
) return EINVAL
; 
2684         if ((result 
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
; 
2686         FormatDBEntry(volumeID
, VolumeStatus
, &dbentry
); 
2687         if ((result 
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) == 0) { 
2689                 fprintf(stderr
,"AddLocalVolumeUUID: found record in database; updating in place.\n"); 
2691                 result 
= UpdateVolumeRecord(dbstateptr
, &dbentry
); 
2692         } else if (result 
== -1) { 
2694                 fprintf(stderr
,"AddLocalVolumeUUID: record not found in database; appending at end.\n"); 
2696                 result 
= AddVolumeRecord(dbstateptr
, &dbentry
); 
2701         fsync(dbstateptr
->dbfile
); 
2706         UnlockDB(dbstateptr
); 
2712 int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle
, VolumeUUID 
*volumeID
) { 
2713         VSDBStatePtr dbstateptr 
= (VSDBStatePtr
)DBHandle
; 
2716         unsigned long iobuffersize
; 
2717         void *iobuffer 
= NULL
; 
2719         unsigned long iotransfersize
; 
2720         unsigned long bytestransferred
; 
2722         if (dbstateptr
->signature 
!= DBHANDLESIGNATURE
) return EINVAL
; 
2724         if ((result 
= LockDB(dbstateptr
, LOCK_EX
)) != 0) return result
; 
2726         if ((result 
= FindVolumeRecordByUUID(dbstateptr
, volumeID
, NULL
, DBMARKPOSITION
)) != 0) { 
2728                 fprintf(stderr
, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result
); 
2730                 if (result 
== -1) result 
= 0;   /* Entry wasn't in the database to begin with? */ 
2734                 fprintf(stderr
, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n"); 
2736                 if ((result 
= stat(gVSDBPath
, &dbinfo
)) != 0) goto ErrExit
; 
2737                 if ((dbinfo
.st_size 
- dbstateptr
->recordPosition 
- sizeof(struct VSDBEntry
)) <= MAXIOMALLOC
) { 
2738                         iobuffersize 
= dbinfo
.st_size 
- dbstateptr
->recordPosition 
- sizeof(struct VSDBEntry
); 
2740                         iobuffersize 
= MAXIOMALLOC
; 
2743                 fprintf(stderr
, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",  
2744                                                         (unsigned long)dbinfo
.st_size
, (unsigned long)dbstateptr
->recordPosition
); 
2745                 fprintf(stderr
, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize
); 
2747                 if (iobuffersize 
> 0) { 
2748                         iobuffer 
= malloc(iobuffersize
); 
2749                         if (iobuffer 
== NULL
) { 
2754                         dataoffset 
= dbstateptr
->recordPosition 
+ sizeof(struct VSDBEntry
); 
2756                                 iotransfersize 
= dbinfo
.st_size 
- dataoffset
; 
2757                                 if (iotransfersize 
> 0) { 
2758                                         if (iotransfersize 
> iobuffersize
) iotransfersize 
= iobuffersize
; 
2761                                         fprintf(stderr
, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)dataoffset
); 
2763                                         lseek(dbstateptr
->dbfile
, dataoffset
, SEEK_SET
); 
2764                                         bytestransferred 
= read(dbstateptr
->dbfile
, iobuffer
, iotransfersize
); 
2765                                         if (bytestransferred 
!= iotransfersize
) { 
2771                                         fprintf(stderr
, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize
, (unsigned long)(dataoffset 
- (off_t
)sizeof(struct VSDBEntry
))); 
2773                                         lseek(dbstateptr
->dbfile
, dataoffset 
- (off_t
)sizeof(struct VSDBEntry
), SEEK_SET
); 
2774                                         bytestransferred 
= write(dbstateptr
->dbfile
, iobuffer
, iotransfersize
); 
2775                                         if (bytestransferred 
!= iotransfersize
) { 
2780                                         dataoffset 
+= (off_t
)iotransfersize
; 
2782                         } while (iotransfersize 
> 0); 
2785                 fprintf(stderr
, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo
.st_size 
- (off_t
)(sizeof(struct VSDBEntry
)))); 
2787                 if ((result 
= ftruncate(dbstateptr
->dbfile
, dbinfo
.st_size 
- (off_t
)(sizeof(struct VSDBEntry
)))) != 0) { 
2791                 fsync(dbstateptr
->dbfile
); 
2797         if (iobuffer
) free(iobuffer
); 
2798         UnlockDB(dbstateptr
); 
2806 int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle
) { 
2807         VSDBStatePtr dbstateptr 
= (VSDBStatePtr
)DBHandle
; 
2809         if (dbstateptr
->signature 
!= DBHANDLESIGNATURE
) return EINVAL
; 
2811         dbstateptr
->signature 
= 0; 
2813         close(dbstateptr
->dbfile
);              /* Nothing we can do about any errors... */ 
2814         dbstateptr
->dbfile 
= 0; 
2823 /****************************************************************************** 
2825  *  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 
2827  *****************************************************************************/ 
2829 static int LockDB(VSDBStatePtr dbstateptr
, int lockmode
) { 
2831         fprintf(stderr
, "LockDB: Locking VSDB file...\n"); 
2833         return flock(dbstateptr
->dbfile
, lockmode
); 
2838 static int UnlockDB(VSDBStatePtr dbstateptr
) { 
2840         fprintf(stderr
, "UnlockDB: Unlocking VSDB file...\n"); 
2842         return flock(dbstateptr
->dbfile
, LOCK_UN
); 
2847 static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr
, VolumeUUID 
*volumeID
, struct VSDBEntry 
*targetEntry
, unsigned long options
) { 
2848         struct VSDBKey searchkey
; 
2849         struct VSDBEntry dbentry
; 
2852         FormatDBKey(volumeID
, &searchkey
); 
2853         lseek(dbstateptr
->dbfile
, 0, SEEK_SET
); 
2856                 result 
= GetVSDBEntry(dbstateptr
, &dbentry
); 
2857                 if ((result 
== 0) && (CompareVSDBKeys(&dbentry
.key
, &searchkey
) == 0)) { 
2858                         if (targetEntry 
!= NULL
) { 
2860                                 fprintf(stderr
, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry
), &dbentry
, targetEntry
); 
2862                                 memcpy(targetEntry
, &dbentry
, sizeof(*targetEntry
)); 
2866         } while (result 
== 0); 
2873 static int AddVolumeRecord(VSDBStatePtr dbstateptr 
, struct VSDBEntry 
*dbentry
) { 
2875         VolumeUUIDString id
; 
2879         strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
)); 
2880         id
[sizeof(dbentry
->key
.uuid
)] = (char)0; 
2881         fprintf(stderr
, "AddVolumeRecord: Adding record for UUID #%s...\n", id
); 
2883         lseek(dbstateptr
->dbfile
, 0, SEEK_END
); 
2884         return write(dbstateptr
->dbfile
, dbentry
, sizeof(struct VSDBEntry
)); 
2890 static int UpdateVolumeRecord(VSDBStatePtr dbstateptr
, struct VSDBEntry 
*dbentry
) { 
2892         VolumeUUIDString id
; 
2896         strncpy(id
, dbentry
->key
.uuid
, sizeof(dbentry
->key
.uuid
)); 
2897         id
[sizeof(dbentry
->key
.uuid
)] = (char)0; 
2898         fprintf(stderr
, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id
, (unsigned long)dbstateptr
->recordPosition
); 
2900         lseek(dbstateptr
->dbfile
, dbstateptr
->recordPosition
, SEEK_SET
); 
2902         fprintf(stderr
, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry
)); 
2904         return write(dbstateptr
->dbfile
, dbentry
, sizeof(*dbentry
)); 
2909 static int GetVSDBEntry(VSDBStatePtr dbstateptr
, struct VSDBEntry 
*dbentry
) { 
2910         struct VSDBEntry entry
; 
2913         VolumeUUIDString id
; 
2916         dbstateptr
->recordPosition 
= lseek(dbstateptr
->dbfile
, 0, SEEK_CUR
); 
2917 #if 0 // DEBUG_TRACE 
2918         fprintf(stderr
, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr
->recordPosition
); 
2920         result 
= read(dbstateptr
->dbfile
, &entry
, sizeof(entry
)); 
2921         if ((result 
!= sizeof(entry
)) || 
2922                 (entry
.keySeparator 
!= DBKEYSEPARATOR
) || 
2923                 (entry
.space 
!= DBBLANKSPACE
) || 
2924                 (entry
.terminator 
!= DBRECORDTERMINATOR
)) { 
2929         strncpy(id
, entry
.key
.uuid
, sizeof(entry
.key
.uuid
)); 
2930         id
[sizeof(entry
.key
.uuid
)] = (char)0; 
2931         fprintf(stderr
, "GetVSDBEntry: returning entry for UUID #%s...\n", id
); 
2933         memcpy(dbentry
, &entry
, sizeof(*dbentry
)); 
2939 static int CompareVSDBKeys(struct VSDBKey 
*key1
, struct VSDBKey 
*key2
) { 
2940 #if 0 // DEBUG_TRACE 
2941         VolumeUUIDString id
; 
2943         strncpy(id
, key1
->uuid
, sizeof(key1
->uuid
)); 
2944         id
[sizeof(key1
->uuid
)] = (char)0; 
2945         fprintf(stderr
, "CompareVSDBKeys: comparing #%s to ", id
); 
2946         strncpy(id
, key2
->uuid
, sizeof(key2
->uuid
)); 
2947         id
[sizeof(key2
->uuid
)] = (char)0; 
2948         fprintf(stderr
, "%s (%d.)...\n", id
, sizeof(key1
->uuid
)); 
2951         return memcmp(key1
->uuid
, key2
->uuid
, sizeof(key1
->uuid
)); 
2956 /****************************************************************************** 
2958  *  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 
2960  *****************************************************************************/ 
2962 static void FormatULong(unsigned long u
, char *s
) { 
2967         for (i 
= 0; i 
< 8; ++i
) { 
2968                 d 
= ((u 
& 0xF0000000) >> 28) & 0x0000000F; 
2970                         *digitptr
++ = (char)(d 
+ '0'); 
2972                         *digitptr
++ = (char)(d 
- 10 + 'A'); 
2980 static void FormatUUID(VolumeUUID 
*volumeID
, char *UUIDField
) { 
2981         FormatULong(volumeID
->v
.high
, UUIDField
); 
2982         FormatULong(volumeID
->v
.low
, UUIDField
+8); 
2988 static void FormatDBKey(VolumeUUID 
*volumeID
, struct VSDBKey 
*dbkey
) { 
2989         FormatUUID(volumeID
, dbkey
->uuid
); 
2994 static void FormatDBRecord(unsigned long volumeStatusFlags
, struct VSDBRecord 
*dbrecord
) { 
2995         FormatULong(volumeStatusFlags
, dbrecord
->statusFlags
); 
3000 static void FormatDBEntry(VolumeUUID 
*volumeID
, unsigned long volumeStatusFlags
, struct VSDBEntry 
*dbentry
) { 
3001         FormatDBKey(volumeID
, &dbentry
->key
); 
3002         dbentry
->keySeparator 
= DBKEYSEPARATOR
; 
3003         dbentry
->space 
= DBBLANKSPACE
; 
3004         FormatDBRecord(volumeStatusFlags
, &dbentry
->record
); 
3005 #if 0 // DEBUG_TRACE 
3006         dbentry
->terminator 
= (char)0; 
3007         fprintf(stderr
, "FormatDBEntry: '%s' (%d.)\n", dbentry
, sizeof(*dbentry
)); 
3009         dbentry
->terminator 
= DBRECORDTERMINATOR
; 
3014 static unsigned long ConvertHexStringToULong(const char *hs
, long maxdigits
) { 
3017         unsigned long nextdigit
; 
3021         for (i 
= 0; (i 
< 8) && ((c 
= hs
[i
]) != (char)0) ; ++i
) { 
3022                 if ((c 
>= '0') && (c 
<= '9')) { 
3023                         nextdigit 
= c 
- '0'; 
3024                 } else if ((c 
>= 'A') && (c 
<= 'F')) { 
3025                         nextdigit 
= c 
- 'A' + 10; 
3026                 } else if ((c 
>= 'a') && (c 
<= 'f')) { 
3027                         nextdigit 
= c 
- 'a' + 10; 
3031                 n 
= (n 
<< 4) + nextdigit
;