X-Git-Url: https://git.saurik.com/apple/hfs.git/blobdiff_plain/c008b8640c766dc2c747ee6c23cd99952ce58d7d..558d2836cb4329cfdb20acd2c5c4cd1945ad0a66:/hfs_util/hfsutil_main.c diff --git a/hfs_util/hfsutil_main.c b/hfs_util/hfsutil_main.c index 738c475..8d86934 100644 --- a/hfs_util/hfsutil_main.c +++ b/hfs_util/hfsutil_main.c @@ -1,10 +1,10 @@ /* - * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2016 Apple Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the + * are subject to the Apple Public Source License Version 1.2 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. @@ -47,11 +47,16 @@ #include #include #include - -#include +#include +#include +#include #include +#include #include +#include +#include +#include #include #include #include @@ -59,18 +64,24 @@ #include #include #include +#include -#include +/* + * CommonCrypto provides a more stable API than OpenSSL guarantees; + * the #define causes it to use the same API for MD5 and SHA1, so the rest of + * the code need not change. + */ +#define COMMON_DIGEST_FOR_OPENSSL +#include -#define READ_DEFAULT_ENCODING 1 +#include -#ifndef FSUR_MOUNT_HIDDEN -#define FSUR_MOUNT_HIDDEN (-9) -#endif +#include -#ifndef FSUC_GETKEY -#define FSUC_GETKEY 'k' -#endif +#include +#include + +#define READ_DEFAULT_ENCODING 1 #ifndef FSUC_ADOPT #define FSUC_ADOPT 'a' @@ -80,20 +91,48 @@ #define FSUC_DISOWN 'd' #endif -#ifndef FSUC_NEWUUID -#define FSUC_NEWUUID 'n' +#ifndef FSUC_GETUUID +#define FSUC_GETUUID 'k' #endif +#ifndef FSUC_SETUUID +#define FSUC_SETUUID 's' +#endif +#ifndef FSUC_MKJNL +#define FSUC_MKJNL 'J' +#endif + +#ifndef FSUC_UNJNL +#define FSUC_UNJNL 'U' +#endif + +#ifndef FSUC_UNJNL_RAW +#define FSUC_UNJNL_RAW 'N' +#endif + +#ifndef FSUC_JNLINFS_RAW +#define FSUC_JNLINFS_RAW 'e' +#endif + +#ifndef FSUC_EXTJNL_RAW +#define FSUC_EXTJNL_RAW 'E' +#endif + +#ifndef FSUC_JNLINFO +#define FSUC_JNLINFO 'I' +#endif + /* **************************************** L O C A L S ******************************************* */ +#define kHFSPlusMaxFileNameBytes (3 * 255 + 1) /* 255 unicode characters, plus 1 NUL byte */ + #define HFS_BLOCK_SIZE 512 char gHFS_FS_NAME[] = "hfs"; char gHFS_FS_NAME_NAME[] = "HFS"; -char gFS_UUID_SUFFIX[] = ".uuid"; char gNewlineString[] = "\n"; char gMountCommand[] = "/sbin/mount"; @@ -114,21 +153,37 @@ char gIgnorePermissionsOption[] = "noperm"; boolean_t gIsEjectable = 0; +int gJournalSize = 0; + #define AUTO_ADOPT_FIXED 1 #define AUTO_ENTER_FIXED 0 -#define VOLUMEUUIDVALUESIZE 2 -typedef union VolumeUUID { - unsigned long value[VOLUMEUUIDVALUESIZE]; - struct { - unsigned long high; - unsigned long low; - } v; -} VolumeUUID; +typedef struct FinderAttrBuf { + u_int32_t info_length; + u_int32_t finderinfo[8]; +} FinderAttrBuf_t; + + +/* For requesting the UUID from the FS */ +typedef struct UUIDAttrBuf { + uint32_t info_length; + uuid_t uu; +} UUIDAttrBuf_t; + +/* HFS+ internal representation of UUID */ +typedef struct hfs_UUID { + uint32_t high; + uint32_t low; +} hfs_UUID_t; + +/* an actual UUID */ +typedef struct volUUID { + uuid_t uuid; +} volUUID_t; -#define VOLUMEUUIDLENGTH 16 -typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1]; + +#define HFSUUIDLENGTH 16 #define VOLUME_RECORDED 0x80000000 #define VOLUME_USEPERMISSIONS 0x00000001 @@ -136,41 +191,74 @@ typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1]; typedef void *VolumeStatusDBHandle; -void GenerateVolumeUUID(VolumeUUID *newVolumeID); -void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID); -void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString); +/* UUID generation and conversion functions */ +void GenerateHFSVolumeUUID(hfs_UUID_t *hfsuu); +void ConvertHFSUUIDStringToUUID (const char* UUIDString, volUUID_t *volid); +void ConvertHFSUUIDToUUID (hfs_UUID_t *hfsuuid, volUUID_t *uu); +/* + * Volume Database manipulation routines + * These functions MUST manipulate the VSDB in the same way that vsdbutil does + */ +int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle); int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr); -int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus); -int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus); -int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID); +int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, volUUID_t *volumeID, unsigned long *VolumeStatus); +int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, volUUID_t *volumeID, unsigned long VolumeStatus); +int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, volUUID_t *volumeID); int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle); /* ************************************ P R O T O T Y P E S *************************************** */ - static void DoDisplayUsage( const char * argv[] ); -static void DoFileSystemFile( char * theFileNameSuffixPtr, char * theContentsPtr ); -static int DoMount( char * theDeviceNamePtr, const char * theMountPointPtr, boolean_t isLocked, boolean_t isSetuid, boolean_t isDev ); -static int DoProbe( char * theDeviceNamePtr ); +static int DoMount( char * theDeviceNamePtr, const char *rawName, const char * theMountPointPtr, + boolean_t isLocked, boolean_t isSetuid, boolean_t isDev ); +static int DoProbe( char * rawDeviceNamePtr, char * blockDeviceNamePtr ); static int DoUnmount( const char * theMountPointPtr ); -static int DoGetUUIDKey( const char * theDeviceNamePtr ); +static int DoGetUUIDKey( const char * theDeviceNamePtr, const char *rawName ); static int DoChangeUUIDKey( const char * theDeviceNamePtr ); -static int DoAdopt( const char * theDeviceNamePtr ); -static int DoDisown( const char * theDeviceNamePtr ); +static int DoAdopt( const char * theDeviceNamePtr, const char *rawName); +static int DoDisown( const char * theDeviceNamePtr, const char *rawName); + +extern int DoMakeJournaled( const char * volNamePtr, int journalSize ); // XXXdbg +extern int DoUnJournal( const char * volNamePtr ); // XXXdbg +extern int DoGetJournalInfo( const char * volNamePtr ); +extern int RawDisableJournaling( const char *devname ); +extern int SetJournalInFSState( const char *devname, int journal_in_fs); + 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 ); +static int GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr); + +/* Helper functions for manipulating HFS and full UUIDs */ +static int ReadHeaderBlock(int fd, void *bufptr, off_t *startOffset, hfs_UUID_t **finderInfoUUIDPtr); +static int GetVolumeUUIDRaw(const char *deviceNamePtr, const char *rawName, volUUID_t *volumeUUIDPtr); +static int GetVolumeUUIDAttr(const char *path, volUUID_t *volumeUUIDPtr); +static int GetVolumeUUID(const char *deviceNamePtr, const char *rawName, volUUID_t *volumeUUIDPtr, boolean_t generate); +static int SetVolumeUUIDRaw(const char *deviceNamePtr, hfs_UUID_t *hfsuu); +static int SetVolumeUUIDAttr(const char *path, hfs_UUID_t *hfsuu); +static int SetVolumeUUID(const char *deviceNamePtr, hfs_UUID_t *hfsuu); + -static int GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t generate); -static int SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr); static int GetEmbeddedHFSPlusVol(HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr); -static int GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * name_o); -static int GetBTreeNodeInfo(int fd, off_t btreeOffset, u_int32_t *nodeSize, u_int32_t *firstLeafNode); -static off_t CalcLeafNodeOffset(off_t fileOffset, u_int32_t blockSize, u_int32_t extentCount, - HFSPlusExtentDescriptor *extentList); +static int GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o); +static int GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize, + u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList, + u_int32_t *nodeSize, u_int32_t *firstLeafNode); static int GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset, HFSPlusVolumeHeader *volHdrPtr, HFSPlusExtentDescriptor **catalogExtents, u_int32_t *catalogExtCount); +static int LogicalToPhysical(off_t logicalOffset, ssize_t length, u_int32_t blockSize, + u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList, + off_t *physicalOffset, ssize_t *availableBytes); +static int ReadFile(int fd, void *buffer, off_t offset, ssize_t length, + off_t volOffset, u_int32_t blockSize, + u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList); static ssize_t readAt( int fd, void * buf, off_t offset, ssize_t length ); static ssize_t writeAt( int fd, void * buf, off_t offset, ssize_t length ); +static int GetEncodingBias(void); + + +CF_EXPORT Boolean _CFStringGetFileSystemRepresentation(CFStringRef string, UInt8 *buffer, CFIndex maxBufLen); + +static void uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen); /* * The fuction CFStringGetSystemEncoding does not work correctly in @@ -187,11 +275,11 @@ static unsigned int __CFStringGetDefaultEncodingForHFSUtil() { char buffer[MAXPATHLEN + 1]; int fd; - strcpy(buffer, passwdp->pw_dir); - strcat(buffer, __kCFUserEncodingFileName); + strlcpy(buffer, passwdp->pw_dir, sizeof(buffer)); + strlcat(buffer, __kCFUserEncodingFileName, sizeof(buffer)); if ((fd = open(buffer, O_RDONLY, 0)) > 0) { - size_t readSize; + ssize_t readSize; readSize = read(fd, buffer, MAXPATHLEN); buffer[(readSize < 0 ? 0 : readSize)] = '\0'; @@ -203,6 +291,119 @@ static unsigned int __CFStringGetDefaultEncodingForHFSUtil() { } #endif + +#define MXENCDNAMELEN 16 /* Maximun length of encoding name string */ + +struct hfs_mnt_encoding { + char encoding_name[MXENCDNAMELEN]; /* encoding type name */ + CFStringEncoding encoding_id; /* encoding type number */ +}; + +static struct hfs_mnt_encoding hfs_mnt_encodinglist[] = { + { "Arabic", 4 }, + { "Armenian", 24 }, + { "Bengali", 13 }, + { "Burmese", 19 }, + { "Celtic", 39 }, + { "CentralEurRoman", 29 }, + { "ChineseSimp", 25 }, + { "ChineseTrad", 2 }, + { "Croatian", 36 }, + { "Cyrillic", 7 }, + { "Devanagari", 9 }, + { "Ethiopic", 28 }, + { "Farsi", 140 }, + { "Gaelic", 40 }, + { "Georgian", 23 }, + { "Greek", 6 }, + { "Gujarati", 11 }, + { "Gurmukhi", 10 }, + { "Hebrew", 5 }, + { "Icelandic", 37 }, + { "Japanese", 1 }, + { "Kannada", 16 }, + { "Khmer", 20 }, + { "Korean", 3 }, + { "Laotian", 22 }, + { "Malayalam", 17 }, + { "Mongolian", 27 }, + { "Oriya", 12 }, + { "Roman", 0 }, /* default */ + { "Romanian", 38 }, + { "Sinhalese", 18 }, + { "Tamil", 14 }, + { "Telugu", 15 }, + { "Thai", 21 }, + { "Tibetan", 26 }, + { "Turkish", 35 }, + { "Ukrainian", 152 }, + { "Vietnamese", 30 }, +}; + +#define KEXT_LOAD_COMMAND "/sbin/kextload" +#define ENCODING_MODULE_PATH "/System/Library/Filesystems/hfs.fs/Contents/Resources/Encodings/" + +static int load_encoding(CFStringEncoding encoding) +{ + int i; + int numEncodings; + int pid; + char *encodingName; + struct stat sb; + union wait status; + char kmodfile[MAXPATHLEN]; + + /* Find the encoding that matches the one passed in */ + numEncodings = sizeof(hfs_mnt_encodinglist) / sizeof(struct hfs_mnt_encoding); + encodingName = NULL; + for (i=0; idrSigWord == kHFSSigWord && - mdbPtr->drEmbedSigWord != kHFSPlusSigWord) { + if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord && + OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) != kHFSPlusSigWord) { Boolean cfOK; CFStringRef cfstr; CFStringEncoding encoding; - encoding = CFStringGetSystemEncoding(); + /* Some poorly mastered HFS CDs have an empty MDB name field! */ + if (mdbPtr->drVN[0] == '\0') { + strcpy((char *)&mdbPtr->drVN[1], gHFS_FS_NAME_NAME); + mdbPtr->drVN[0] = strlen(gHFS_FS_NAME_NAME); + } + + /* Check for an encoding hint in the Finder Info (field 4). */ + encoding = GET_HFS_TEXT_ENCODING(OSSwapBigToHostInt32(mdbPtr->drFndrInfo[4])); + if (encoding == kCFStringEncodingInvalidId) { + /* Next try the encoding bias in the kernel. */ + encoding = GetEncodingBias(); + if (encoding == 0 || encoding == kCFStringEncodingInvalidId) + encoding = __CFStringGetDefaultEncodingForHFSUtil(); + } + cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault, mdbPtr->drVN, encoding); - cfOK = CFStringGetCString(cfstr, volnameUTF8, NAME_MAX, - kCFStringEncodingUTF8); + if (cfstr == NULL) { + result = FSUR_INVAL; + goto Return; + } + cfOK = _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX); CFRelease(cfstr); if (!cfOK && encoding != kCFStringEncodingMacRoman) { @@ -531,23 +843,35 @@ DoProbe(char *deviceNamePtr) /* default to MacRoman on conversion errors */ cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault, mdbPtr->drVN, kCFStringEncodingMacRoman); - CFStringGetCString(cfstr, volnameUTF8, NAME_MAX, - kCFStringEncodingUTF8); + _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX); CFRelease(cfstr); + encoding = kCFStringEncodingMacRoman; } + /* Preload the encoding converter so mount_hfs can run as an ordinary user. */ + if (encoding != kCFStringEncodingMacRoman) { + if (load_encoding(encoding) != FSUR_IO_SUCCESS) { + encoding = kCFStringEncodingMacRoman; + cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault, mdbPtr->drVN, encoding); + _CFStringGetFileSystemRepresentation(cfstr, volnameUTF8, NAME_MAX); + CFRelease(cfstr); + } + } + /* get HFS Plus volume name (from Catalog) */ - } else if ((volHdrPtr->signature == kHFSPlusSigWord) || - (mdbPtr->drSigWord == kHFSSigWord && - mdbPtr->drEmbedSigWord == kHFSPlusSigWord)) { + } else if ((OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord) || + (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) || + (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord && + OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord)) { off_t startOffset; - if (volHdrPtr->signature == kHFSPlusSigWord) { - startOffset = 0; - } else {/* embedded volume, first find offset */ + if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSSigWord) { + /* embedded volume, first find offset */ result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset); if ( result != FSUR_IO_SUCCESS ) goto Return; + } else { + startOffset = 0; } result = GetNameFromHFSPlusVolumeStartingAt(fd, startOffset, @@ -557,43 +881,9 @@ DoProbe(char *deviceNamePtr) } if (FSUR_IO_SUCCESS == result) { - CFStringRef slash; - CFStringRef colon; - CFMutableStringRef volumeName; - CFIndex volumeNameLength; - CFRange foundSubString; - - slash = CFStringCreateWithCString(kCFAllocatorDefault, "/", kCFStringEncodingUTF8); - if (slash == NULL) { - result = FSUR_IO_FAIL; - goto Return; - }; - - colon = CFStringCreateWithCString(kCFAllocatorDefault, ":", kCFStringEncodingUTF8); - if (colon == NULL) { - result = FSUR_IO_FAIL; - goto Return; - }; - - volumeName = CFStringCreateMutableCopy( - kCFAllocatorDefault, - 0, /* maxLength */ - CFStringCreateWithCString(kCFAllocatorDefault, volnameUTF8, kCFStringEncodingUTF8)); - if (volumeName == NULL) { - result = FSUR_IO_FAIL; - goto Return; - }; - volumeNameLength = CFStringGetLength(volumeName); - - while (CFStringFindWithOptions(volumeName, slash, CFRangeMake(0, volumeNameLength-1), 0, &foundSubString)) { - CFStringReplace(volumeName, foundSubString, colon); - }; - - CFStringGetCString(volumeName, volnameUTF8, NAME_MAX, kCFStringEncodingUTF8); - - DoFileSystemFile( FS_NAME_SUFFIX, gHFS_FS_NAME_NAME ); - DoFileSystemFile( FS_LABEL_SUFFIX, volnameUTF8 ); - result = FSUR_MOUNT_HIDDEN; + /* Print the volume name to standard output */ + write(1, volnameUTF8, strlen((char *)volnameUTF8)); + result = FSUR_RECOGNIZED; } Return: @@ -603,37 +893,60 @@ Return: if (fd > 0) close(fd); - +Err_Exit: return result; } /* DoProbe */ +/* + * Create a version 3 UUID from a unique "name" in the given "name space". + * Version 3 UUID are derived using "name" via MD5 checksum. + * + * Parameters: + * result_uuid - resulting UUID. + * namespace - namespace in which given name exists and UUID should be created. + * name - unique string used to create version 3 UUID. + * namelen - length of the name string. + */ +static void +uuid_create_md5_from_name(uuid_t result_uuid, const uuid_t namespace, const void *name, int namelen) +{ + MD5_CTX c; + + MD5_Init(&c); + MD5_Update(&c, namespace, sizeof(uuid_t)); + MD5_Update(&c, name, namelen); + MD5_Final(result_uuid, &c); + + result_uuid[6] = (result_uuid[6] & 0x0F) | 0x30; + result_uuid[8] = (result_uuid[8] & 0x3F) | 0x80; +} /* **************************************** DoGetUUIDKey ******************************************* Purpose - - This routine will open the given block device and return the volume UUID in text form. + This routine will open the given block device and return the 128-bit volume UUID in text form written to stdout. Input - theDeviceNamePtr - pointer to the device name (full path, like /dev/disk0s2). Output - returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. *************************************************************************************************** */ static int -DoGetUUIDKey( const char * theDeviceNamePtr ) { +DoGetUUIDKey( const char * theDeviceNamePtr, const char *rawName) { int result; - VolumeUUID targetVolumeUUID; - VolumeUUIDString UUIDString; - char uuidLine[VOLUMEUUIDLENGTH+2]; - - if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) goto Err_Exit; - - ConvertVolumeUUIDToString( &targetVolumeUUID, UUIDString); - strncpy(uuidLine, UUIDString, VOLUMEUUIDLENGTH+1); - strcat(uuidLine, gNewlineString); - DoFileSystemFile( gFS_UUID_SUFFIX, uuidLine ); - result = FSUR_IO_SUCCESS; - -Err_Exit: + volUUID_t targetVolumeUUID; + uuid_string_t uustr; + + result = GetVolumeUUID(theDeviceNamePtr, rawName, &targetVolumeUUID, FALSE); + if (result == FSUR_IO_SUCCESS) { + uuid_unparse (targetVolumeUUID.uuid, uustr); + /* for compatibility, must write out the string to stdout, with NO newline */ + write(STDOUT_FILENO, uustr, strlen(uustr)); + + /* for debugging */ + // fprintf(stderr, "device %s UUID : %s\n", rawName, uustr); + } + return result; } @@ -650,11 +963,17 @@ Output - static int DoChangeUUIDKey( const char * theDeviceNamePtr ) { int result; - VolumeUUID newVolumeUUID; + hfs_UUID_t newVolumeUUID; - GenerateVolumeUUID(&newVolumeUUID); + GenerateHFSVolumeUUID(&newVolumeUUID); +#if 0 + // for testing purposes, may want to set a NULL UUID from command line. + memset (&newVolumeUUID, 0, sizeof(newVolumeUUID)); +#endif result = SetVolumeUUID(theDeviceNamePtr, &newVolumeUUID); + //fprintf(stderr, "device %s has new UUID \n", theDeviceNamePtr); + return result; } @@ -669,18 +988,18 @@ Output - returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. *************************************************************************************************** */ static int -DoAdopt( const char * theDeviceNamePtr ) { +DoAdopt( const char * theDeviceNamePtr, const char *rawName) { int result, closeresult; - VolumeUUID targetVolumeUUID; + volUUID_t targetVolumeUUID; VolumeStatusDBHandle vsdbhandle = NULL; unsigned long targetVolumeStatus; - if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return; + if ((result = GetVolumeUUID(theDeviceNamePtr, rawName, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return; if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit; if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) { targetVolumeStatus = 0; - }; + } targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS; if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit; @@ -691,7 +1010,7 @@ Err_Exit: closeresult = CloseVolumeStatusDB(vsdbhandle); vsdbhandle = NULL; if (result == FSUR_IO_SUCCESS) result = closeresult; - }; + } if ((result != 0) && (result != FSUR_IO_SUCCESS)) result = FSUR_IO_FAIL; @@ -713,18 +1032,18 @@ Output - returns FSUR_IO_SUCCESS or else one of the FSUR_xyz error codes. *************************************************************************************************** */ static int -DoDisown( const char * theDeviceNamePtr ) { +DoDisown( const char * theDeviceNamePtr, const char *rawName) { int result, closeresult; - VolumeUUID targetVolumeUUID; + volUUID_t targetVolumeUUID; VolumeStatusDBHandle vsdbhandle = NULL; unsigned long targetVolumeStatus; - if ((result = GetVolumeUUID(theDeviceNamePtr, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return; + if ((result = GetVolumeUUID(theDeviceNamePtr, rawName, &targetVolumeUUID, TRUE)) != FSUR_IO_SUCCESS) goto Err_Return; if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) goto Err_Exit; if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) { targetVolumeStatus = 0; - }; + } targetVolumeStatus = (targetVolumeStatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS; if ((result = SetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, targetVolumeStatus)) != 0) goto Err_Exit; @@ -735,14 +1054,14 @@ Err_Exit: closeresult = CloseVolumeStatusDB(vsdbhandle); vsdbhandle = NULL; if (result == FSUR_IO_SUCCESS) result = closeresult; - }; + } if ((result != 0) && (result != FSUR_IO_SUCCESS)) { #if TRACE_HFS_UTIL if (result != 0) fprintf(stderr, "DoDisown: result = %d; changing to %d...\n", result, FSUR_IO_FAIL); #endif result = FSUR_IO_FAIL; - }; + } Err_Return: #if TRACE_HFS_UTIL @@ -752,6 +1071,19 @@ Err_Return: } +static int +get_multiplier(char c) +{ + if (tolower(c) == 'k') { + return 1024; + } else if (tolower(c) == 'm') { + return 1024 * 1024; + } else if (tolower(c) == 'g') { + return 1024 * 1024 * 1024; + } + + return 1; +} /* **************************************** ParseArgs ******************************************** Purpose - @@ -779,6 +1111,7 @@ flagsArg: either "readonly" OR "writable" either "removable" OR "fixed" either "nosuid" or "suid" + either "nodev" or "dev" examples: hfs.util -p disk0s2 removable writable @@ -797,7 +1130,7 @@ ParseArgs(int argc, const char *argv[], const char ** actionPtr, boolean_t * isLockedPtr, boolean_t * isSetuidPtr, boolean_t * isDevPtr) { int result = FSUR_INVAL; - int deviceLength; + int deviceLength, doLengthCheck = 1; int index; int mounting = 0; @@ -830,8 +1163,8 @@ ParseArgs(int argc, const char *argv[], const char ** actionPtr, case FSUC_MOUNT: case FSUC_MOUNT_FORCE: - /* action Mount and ForceMount require 7 arguments (need the mountpoint and the flags) */ - if ( argc < 7 ) { + /* action Mount and ForceMount require 8 arguments (need the mountpoint and the flags) */ + if ( argc < 8 ) { DoDisplayUsage( argv ); goto Return; } else { @@ -841,11 +1174,11 @@ ParseArgs(int argc, const char *argv[], const char ** actionPtr, } break; - case FSUC_GETKEY: + case FSUC_GETUUID: index = 0; break; - case FSUC_NEWUUID: + case FSUC_SETUUID: index = 0; break; @@ -857,6 +1190,46 @@ ParseArgs(int argc, const char *argv[], const char ** actionPtr, index = 0; break; + // XXXdbg + case FSUC_MKJNL: + index = 0; + doLengthCheck = 0; + if (isdigit(argv[2][0])) { + char *ptr; + gJournalSize = strtoul(argv[2], &ptr, 0); + if (ptr) { + gJournalSize *= get_multiplier(*ptr); + } + return 0; + } + break; + + case FSUC_UNJNL: + index = 0; + doLengthCheck = 0; + break; + + case FSUC_UNJNL_RAW: + index = 0; + doLengthCheck = 0; + break; + + case FSUC_JNLINFS_RAW: + index = 0; + doLengthCheck = 0; + break; + + case FSUC_EXTJNL_RAW: + index = 0; + doLengthCheck = 0; + break; + + case FSUC_JNLINFO: + index = 0; + doLengthCheck = 0; + break; + // XXXdbg + default: DoDisplayUsage( argv ); goto Return; @@ -865,7 +1238,7 @@ ParseArgs(int argc, const char *argv[], const char ** actionPtr, /* Make sure device (argv[2]) is something reasonable */ deviceLength = strlen( argv[2] ); - if ( deviceLength < 3 || deviceLength > 10 ) { + if ( doLengthCheck && (deviceLength < 3 || deviceLength > NAME_MAX) ) { DoDisplayUsage( argv ); goto Return; } @@ -937,84 +1310,163 @@ DoDisplayUsage(const char *argv[]) printf(" -%c (Mount)\n", FSUC_MOUNT); printf(" -%c (Unmount)\n", FSUC_UNMOUNT); printf(" -%c (Force Mount)\n", FSUC_MOUNT_FORCE); - printf(" -%c (Get UUID Key)\n", FSUC_GETKEY); - printf(" -%c (New UUID Key)\n", FSUC_NEWUUID); +#ifdef HFS_UUID_SUPPORT + printf(" -%c (Get UUID Key)\n", FSUC_GETUUID); + printf(" -%c (Set UUID Key)\n", FSUC_SETUUID); +#endif //HFS_UUID_SUPPORT printf(" -%c (Adopt permissions)\n", FSUC_ADOPT); + printf(" -%c (Make a file system journaled)\n", FSUC_MKJNL); + printf(" -%c (Turn off journaling on a file system)\n", FSUC_UNJNL); + printf(" -%c (Turn off journaling on a raw device)\n", FSUC_UNJNL_RAW); + printf(" -%c (Disable use of an external journal on a raw device)\n", FSUC_JNLINFS_RAW); + printf(" -%c (Enable the use of an external journal on a raw device)\n", FSUC_EXTJNL_RAW); + printf(" -%c (Get size & location of journaling on a file system)\n", FSUC_JNLINFO); printf("device_arg:\n"); printf(" device we are acting upon (for example, 'disk0s2')\n"); + printf(" if '-%c' or '-%c' is specified, this should be the\n", FSUC_MKJNL, FSUC_UNJNL); + printf(" name of the file system we're to act on (for example, '/Volumes/foo' or '/')\n"); printf("mount_point_arg:\n"); printf(" required for Mount and Force Mount \n"); printf("Flags:\n"); printf(" required for Mount, Force Mount and Probe\n"); printf(" indicates removable or fixed (for example 'fixed')\n"); printf(" indicates readonly or writable (for example 'readonly')\n"); + printf(" indicates suid or nosuid (for example 'suid')\n"); + printf(" indicates dev or nodev (for example 'dev')\n"); printf("Examples:\n"); printf(" %s -p disk0s2 fixed writable\n", argv[0]); - printf(" %s -m disk0s2 /my/hfs removable readonly\n", argv[0]); + printf(" %s -m disk0s2 /my/hfs removable readonly nosuid nodev\n", argv[0]); return; } /* DoDisplayUsage */ -/* ************************************** DoFileSystemFile ******************************************* -Purpose - - This routine will create a file system info file that is used by WorkSpace. After creating the - file it will write whatever theContentsPtr points to the new file. - We end up with a file something like: - /System/Library/Filesystems/hfs.fs/hfs.name - when our file system name is "hfs" and theFileNameSuffixPtr points to ".name" -Input - - theFileNameSuffixPtr - pointer to a suffix we add to the file name we're creating. - theContentsPtr - pointer to C string to write into the file. -Output - - NA. -*************************************************************************************************** */ -static void -DoFileSystemFile(char *fileNameSuffixPtr, char *contentsPtr) +/* + GetHFSMountPoint + + Given a path to a device, determine if a volume is mounted on that + device. If there is an HFS volume, return its path and FSUR_IO_SUCCESS. + If there is a non-HFS volume, return FSUR_UNRECOGNIZED. If there is + no volume mounted on the device, set *pathPtr to NULL and return + FSUR_IO_SUCCESS. + + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED +*/ +static int +GetHFSMountPoint(const char *deviceNamePtr, char **pathPtr) { - int fd; - char fileName[MAXPATHLEN]; - - sprintf(fileName, "%s/%s%s/%s", FS_DIR_LOCATION, gHFS_FS_NAME, - FS_DIR_SUFFIX, gHFS_FS_NAME ); - strcat(fileName, fileNameSuffixPtr ); - unlink(fileName); /* delete existing string */ - - if ( strlen( fileNameSuffixPtr ) ) { - int oldMask = umask(0); - - fd = open( & fileName[0], O_CREAT | O_TRUNC | O_WRONLY, 0644 ); - umask( oldMask ); - if ( fd > 0 ) { - write( fd, contentsPtr, strlen( contentsPtr ) ); - close( fd ); - } else { - perror( fileName ); - } - } + int result; + int i, numMounts; + struct statfs *buf; + + /* Assume no mounted volume found */ + *pathPtr = NULL; + result = FSUR_IO_SUCCESS; + + numMounts = getmntinfo(&buf, MNT_NOWAIT); + if (numMounts == 0) + return FSUR_IO_FAIL; + + for (i=0; idrSigWord) == kHFSSigWord && + OSSwapBigToHostInt16(mdbPtr->drEmbedSigWord) == kHFSPlusSigWord) { + result = GetEmbeddedHFSPlusVol(mdbPtr, startOffset); + if (result != FSUR_IO_SUCCESS) + goto Err_Exit; + result = readAt(fd, bufPtr, *startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE); + if (result != FSUR_IO_SUCCESS) + goto Err_Exit; + } + + /* + * At this point, we have the MDB for plain HFS, or VHB for HFS Plus and HFSX + * volumes (including wrapped HFS Plus). Verify the signature and grab the + * UUID from the Finder Info. + */ + if (OSSwapBigToHostInt16(mdbPtr->drSigWord) == kHFSSigWord) { + *finderInfoUUIDPtr = (hfs_UUID_t *)(&mdbPtr->drFndrInfo[6]); + } else if (OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSPlusSigWord || + OSSwapBigToHostInt16(volHdrPtr->signature) == kHFSXSigWord) { + *finderInfoUUIDPtr = (hfs_UUID_t *)&volHdrPtr->finderInfo[24]; + } else { + result = FSUR_UNRECOGNIZED; + } + +Err_Exit: + return result; +} /* - -- GetVolumeUUID - -- - -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL - */ + GetVolumeUUIDRaw + + Read the UUID from an unmounted volume, by doing direct access to the device. + Assumes the caller has already determined that a volume is not mounted + on the device. Once we have the HFS UUID from the finderinfo, convert it to a + full UUID and then write it into the output argument provided (volUUIDPtr) + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED +*/ static int -GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t generate) { +GetVolumeUUIDRaw(const char *deviceNamePtr, const char *rawName, volUUID_t *volUUIDPtr) +{ int fd = 0; char * bufPtr; - HFSMasterDirectoryBlock * mdbPtr; - HFSPlusVolumeHeader * volHdrPtr; - VolumeUUID *finderInfoUUIDPtr; + off_t startOffset; + hfs_UUID_t *finderInfoUUIDPtr; + hfs_UUID_t hfs_uuid; + volUUID_t fullUUID; int result; + int error; bufPtr = (char *)malloc(HFS_BLOCK_SIZE); if ( ! bufPtr ) { @@ -1022,106 +1474,143 @@ GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t ge goto Err_Exit; } - mdbPtr = (HFSMasterDirectoryBlock *) bufPtr; - volHdrPtr = (HFSPlusVolumeHeader *) bufPtr; - - fd = open( deviceNamePtr, O_RDWR, 0 ); - if( fd <= 0 ) { - fd = open( deviceNamePtr, O_RDONLY, 0); - if (fd <= 0) { + fd = open( deviceNamePtr, O_RDONLY, 0); + if (fd <= 0) { + error = errno; +#if TRACE_HFS_UTIL + fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", deviceNamePtr, errno); +#endif + if (error == EBUSY) { + /* If it was busy, then retry, this time using the raw device */ + fd = open (rawName, O_RDONLY, 0); + if (fd <= 0) { #if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: GetVolumeUUID: device open failed (errno = %d).\n", errno); + fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: device (%s) open failed (errno = %d).\n", rawName, errno); #endif + result = FSUR_IO_FAIL; + goto Err_Exit; + } + } + else { result = FSUR_IO_FAIL; goto Err_Exit; - }; - }; + } + } /* - * Read the HFS Master Directory Block from sector 2 - */ - result = readAt(fd, volHdrPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE); - if (result != FSUR_IO_SUCCESS) { + * Get the pointer to the volume UUID in the Finder Info*/ + result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr); + if (result != FSUR_IO_SUCCESS) goto Err_Exit; - }; - - if (mdbPtr->drSigWord == kHFSSigWord && - mdbPtr->drEmbedSigWord != kHFSPlusSigWord) { - finderInfoUUIDPtr = (VolumeUUID *)(&mdbPtr->drFndrInfo[6]); - if (generate && ((finderInfoUUIDPtr->v.high == 0) || (finderInfoUUIDPtr->v.low == 0))) { - GenerateVolumeUUID(volumeUUIDPtr); - bcopy(volumeUUIDPtr, finderInfoUUIDPtr, sizeof(*finderInfoUUIDPtr)); - result = writeAt(fd, volHdrPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE); - if (result != FSUR_IO_SUCCESS) goto Err_Exit; - }; - bcopy(finderInfoUUIDPtr, volumeUUIDPtr, sizeof(*volumeUUIDPtr)); - result = FSUR_IO_SUCCESS; - } else if ((volHdrPtr->signature == kHFSPlusSigWord) || - ((mdbPtr->drSigWord == kHFSSigWord) && - (mdbPtr->drEmbedSigWord == kHFSPlusSigWord))) { - off_t startOffset; - if (volHdrPtr->signature == kHFSPlusSigWord) { - startOffset = 0; - } else {/* embedded volume, first find offset */ - result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset); - if ( result != FSUR_IO_SUCCESS ) { - goto Err_Exit; - }; - } - - result = readAt( fd, volHdrPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE ); - if (result != FSUR_IO_SUCCESS) { - goto Err_Exit; // return FSUR_IO_FAIL - } - - /* Verify that it is an HFS+ volume. */ - - if (volHdrPtr->signature != kHFSPlusSigWord) { - result = FSUR_IO_FAIL; - goto Err_Exit; - } - - finderInfoUUIDPtr = (VolumeUUID *)&volHdrPtr->finderInfo[24]; - if (generate && ((finderInfoUUIDPtr->v.high == 0) || (finderInfoUUIDPtr->v.low == 0))) { - GenerateVolumeUUID(volumeUUIDPtr); - bcopy(volumeUUIDPtr, finderInfoUUIDPtr, sizeof(*finderInfoUUIDPtr)); - result = writeAt( fd, volHdrPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE ); - if (result != FSUR_IO_SUCCESS) { - goto Err_Exit; - }; - }; - bcopy(finderInfoUUIDPtr, volumeUUIDPtr, sizeof(*volumeUUIDPtr)); - result = FSUR_IO_SUCCESS; - } else { - result = FSUR_UNRECOGNIZED; - }; + /* + * Copy the volume UUID out of the Finder Info. Note that the FinderInfo + * stores the UUID in big-endian so we have to convert to native + * endianness. + */ + hfs_uuid.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->high); + hfs_uuid.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->low); + + /* + * Now convert to a full UUID using the same algorithm as HFS+ + * This makes sure to construct a full NULL-UUID if necessary. + */ + ConvertHFSUUIDToUUID (&hfs_uuid, &fullUUID); + + /* Copy it out into the caller's buffer */ + uuid_copy(volUUIDPtr->uuid, fullUUID.uuid); Err_Exit: if (fd > 0) close(fd); if (bufPtr) free(bufPtr); #if TRACE_HFS_UTIL - if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: GetVolumeUUID: result = %d...\n", result); + if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: GetVolumeUUIDRaw: result = %d...\n", result); #endif return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL; -}; +} + + + +void ConvertHFSUUIDStringToUUID(const char *UUIDString, volUUID_t *volumeID) { + int i; + char c; + u_int32_t nextdigit; + u_int32_t high = 0; + u_int32_t low = 0; + u_int32_t carry; + hfs_UUID_t hfsuu; + + for (i = 0; (i < HFSUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) { + if ((c >= '0') && (c <= '9')) { + nextdigit = c - '0'; + } else if ((c >= 'A') && (c <= 'F')) { + nextdigit = c - 'A' + 10; + } else if ((c >= 'a') && (c <= 'f')) { + nextdigit = c - 'a' + 10; + } else { + nextdigit = 0; + } + carry = ((low & 0xF0000000) >> 28) & 0x0000000F; + high = (high << 4) | carry; + low = (low << 4) | nextdigit; + } + + hfsuu.high = high; + hfsuu.low = low; + + /* now convert to a full UUID */ + ConvertHFSUUIDToUUID(&hfsuu, volumeID); + + return; +} /* - -- SetVolumeUUID - -- - -- Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL + * Convert an HFS+ UUID in binary form to a full UUID + * + * Assumes that the HFS UUID argument is stored in native endianness + * If the input UUID is zeroes, then it will emit a NULL'd out UUID. */ +void ConvertHFSUUIDToUUID (hfs_UUID_t *hfsuuid, volUUID_t *uu) +{ + uint8_t rawUUID[8]; + /* if either high or low is 0, then return the NULL uuid */ + if ((hfsuuid->high == 0) || (hfsuuid->low == 0)) { + uuid_clear (uu->uuid); + return; + } + /* + * If the input UUID was not zeroes, then run it through the normal md5 + * + * NOTE: When using MD5 to compute the "full" UUID, we must pass in the + * big-endian values of the two 32-bit fields. In the kernel, HFS uses the + * raw 4-byte fields of the finderinfo directly, without running them through + * an endian-swap. As a result, we must endian-swap back to big endian here. + */ + ((uint32_t*)rawUUID)[0] = OSSwapHostToBigInt32(hfsuuid->high); + ((uint32_t*)rawUUID)[1] = OSSwapHostToBigInt32(hfsuuid->low); + uuid_create_md5_from_name(uu->uuid, kFSUUIDNamespaceSHA1, rawUUID, sizeof(rawUUID)); +} + +/* + SetVolumeUUIDRaw + + Write a previously generated UUID to an unmounted volume, by doing direct + access to the device. Assumes the caller has already determined that a + volume is not mounted on the device. + + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED +*/ static int -SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr) { +SetVolumeUUIDRaw(const char *deviceNamePtr, hfs_UUID_t *volumeUUIDPtr) +{ int fd = 0; char * bufPtr; - HFSMasterDirectoryBlock * mdbPtr; - HFSPlusVolumeHeader * volHdrPtr; - VolumeUUID *finderInfoUUIDPtr; + off_t startOffset; + hfs_UUID_t *finderInfoUUIDPtr; int result; bufPtr = (char *)malloc(HFS_BLOCK_SIZE); @@ -1130,77 +1619,287 @@ SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr) { goto Err_Exit; } - mdbPtr = (HFSMasterDirectoryBlock *) bufPtr; - volHdrPtr = (HFSPlusVolumeHeader *) bufPtr; - - fd = open( deviceNamePtr, O_RDWR, 0 ); - if( fd <= 0 ) { + fd = open( deviceNamePtr, O_RDWR, 0); + if (fd <= 0) { #if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: SetVolumeUUID: device open failed (errno = %d).\n", errno); + fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: device open failed (errno = %d).\n", errno); #endif result = FSUR_IO_FAIL; goto Err_Exit; - }; + } /* - * Read the HFS Master Directory Block from sector 2 + * Get the pointer to the volume UUID in the Finder Info */ - result = readAt(fd, volHdrPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE); - if (result != FSUR_IO_SUCCESS) { + result = ReadHeaderBlock(fd, bufPtr, &startOffset, &finderInfoUUIDPtr); + if (result != FSUR_IO_SUCCESS) goto Err_Exit; - }; - - if (mdbPtr->drSigWord == kHFSSigWord && - mdbPtr->drEmbedSigWord != kHFSPlusSigWord) { - finderInfoUUIDPtr = (VolumeUUID *)(&mdbPtr->drFndrInfo[6]); - bcopy(volumeUUIDPtr, finderInfoUUIDPtr, sizeof(*finderInfoUUIDPtr)); - result = writeAt(fd, volHdrPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE); - } else if ((volHdrPtr->signature == kHFSPlusSigWord) || - ((mdbPtr->drSigWord == kHFSSigWord) && - (mdbPtr->drEmbedSigWord == kHFSPlusSigWord))) { - off_t startOffset; - if (volHdrPtr->signature == kHFSPlusSigWord) { - startOffset = 0; - } else {/* embedded volume, first find offset */ - result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset); - if ( result != FSUR_IO_SUCCESS ) { - goto Err_Exit; - }; - } - - result = readAt( fd, volHdrPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE ); - if (result != FSUR_IO_SUCCESS) { - goto Err_Exit; // return FSUR_IO_FAIL - } - - /* Verify that it is an HFS+ volume. */ - - if (volHdrPtr->signature != kHFSPlusSigWord) { - result = FSUR_IO_FAIL; - goto Err_Exit; - } - - finderInfoUUIDPtr = (VolumeUUID *)&volHdrPtr->finderInfo[24]; - bcopy(volumeUUIDPtr, finderInfoUUIDPtr, sizeof(*finderInfoUUIDPtr)); - result = writeAt( fd, volHdrPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE ); - if (result != FSUR_IO_SUCCESS) { - goto Err_Exit; - }; - result = FSUR_IO_SUCCESS; - } else { - result = FSUR_UNRECOGNIZED; - }; + /* + * Update the UUID in the Finder Info. Make sure to write out big endian. + */ + finderInfoUUIDPtr->high = OSSwapHostToBigInt32(volumeUUIDPtr->high); + finderInfoUUIDPtr->low = OSSwapHostToBigInt32(volumeUUIDPtr->low); + + /* + * Write the modified MDB or VHB back to disk + */ + result = writeAt(fd, bufPtr, startOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE); Err_Exit: if (fd > 0) close(fd); if (bufPtr) free(bufPtr); #if TRACE_HFS_UTIL - if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: SetVolumeUUID: result = %d...\n", result); + if (result != FSUR_IO_SUCCESS) fprintf(stderr, "hfs.util: SetVolumeUUIDRaw: result = %d...\n", result); #endif return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL; -}; +} + + +/* + GetVolumeUUIDAttr + + Read the UUID from a mounted volume, by calling getattrlist(). + Assumes the path is the mount point of an HFS volume. Note that this will + return the full-length UUID to the caller, as emitted by the underlying + filesystem. On HFS+ this means that we use the hfs_vfsops.c implementation + to construct the UUID + + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL +*/ +static int +GetVolumeUUIDAttr(const char *path, volUUID_t *volUUIDPtr) +{ + struct attrlist alist; + UUIDAttrBuf_t uuidattr; + FinderAttrBuf_t finderinfo; + int result; + + /* + * This is a little bit dodgy. In order to detect whether or not the + * volume has a valid UUID, we need to call getattrlist() and examine + * the FinderInfo, which is what this function has historically done, even if + * we ultimately want the full UUID, which is what is returned if one requests + * ATTR_VOL_UUID. + * + * The reason is that if the UUID does not exist, it will be stored + * as 8 bytes of zeroes in the UUID portion of the finder info. However, if + * you request ATTR_VOL_UUID, it will run the 8 bytes of zeroes through + * the MD5 function, where they will be manipulated into a full UUID. It + * doesn't look like that guarantees the resulting UUID will also be a + * NULL-uuid (i.e. all zeroes). + * + * All of this to say we need to check the finder info first, then check + * ATTR_VOL_UUID as needed afterwards. + */ + + /* First set up for a call to getattrlist for the finderinfo */ + memset (&alist, 0, sizeof(alist)); + alist.bitmapcount = ATTR_BIT_MAP_COUNT; + alist.reserved = 0; + alist.commonattr = ATTR_CMN_FNDRINFO; + alist.volattr = ATTR_VOL_INFO; + alist.dirattr = 0; + alist.fileattr = 0; + alist.forkattr = 0; + + /* Get the finderinfo */ + result = getattrlist(path, &alist, &finderinfo, sizeof(finderinfo), 0); + if (result) { + return FSUR_IO_FAIL; + } + + /* Now we need to check if the finderinfo UUID is NULL */ + hfs_UUID_t* hfs_finderinfo = (hfs_UUID_t*)(&finderinfo.finderinfo[6]); + + /* + * We should really endian-swap these, but if a uint32_t is 0, + * the endianness doesn't matter + */ + if ((hfs_finderinfo->high == 0) || (hfs_finderinfo->low == 0)) { + /* Then it is an uninitialized/NULL UUID. Zap the caller buffer and bail out */ + uuid_clear (volUUIDPtr->uuid); + return FSUR_IO_SUCCESS; + } + + /* OK, now set up the attrlist structure to get the volume's UUID */ + memset (&alist, 0, sizeof(alist)); + alist.bitmapcount = ATTR_BIT_MAP_COUNT; + alist.reserved = 0; + alist.commonattr = 0; + alist.volattr = (ATTR_VOL_INFO | ATTR_VOL_UUID); + alist.dirattr = 0; + alist.fileattr = 0; + alist.forkattr = 0; + + /* Get the full UUID from the kernel */ + result = getattrlist(path, &alist, &uuidattr, sizeof(uuidattr), 0); + if (result) { + return FSUR_IO_FAIL; + } + + /* Copy the UUID from the buf to caller's buffer */ + uuid_copy (volUUIDPtr->uuid, uuidattr.uu); + result = FSUR_IO_SUCCESS; + + return result; +} + + +/* + SetVolumeUUIDAttr + + Write a UUID to a mounted volume, by calling setattrlist(). + Assumes the path is the mount point of an HFS volume. + + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL +*/ +static int +SetVolumeUUIDAttr(const char *path, hfs_UUID_t *volumeUUIDPtr) +{ + struct attrlist alist; + struct FinderAttrBuf volFinderInfo; + hfs_UUID_t *finderInfoUUIDPtr; + int result; + + /* Set up the attrlist structure to get the volume's Finder Info */ + memset (&alist, 0, sizeof(alist)); + alist.bitmapcount = ATTR_BIT_MAP_COUNT; + alist.reserved = 0; + alist.commonattr = ATTR_CMN_FNDRINFO; + alist.volattr = ATTR_VOL_INFO; + alist.dirattr = 0; + alist.fileattr = 0; + alist.forkattr = 0; + + /* Get the Finder Info */ + result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0); + if (result) { + result = FSUR_IO_FAIL; + goto Err_Exit; + } + + /* Update the UUID in the Finder Info. Make sure to swap back to big endian */ + finderInfoUUIDPtr = (hfs_UUID_t *)(&volFinderInfo.finderinfo[6]); + finderInfoUUIDPtr->high = OSSwapHostToBigInt32(volumeUUIDPtr->high); + finderInfoUUIDPtr->low = OSSwapHostToBigInt32(volumeUUIDPtr->low); + + /* Write the Finder Info back to the volume */ + result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0); + if (result) { + result = FSUR_IO_FAIL; + goto Err_Exit; + } + + result = FSUR_IO_SUCCESS; + +Err_Exit: + return result; +} + + +/* + GetVolumeUUID + + Return the UUID of an HFS, HFS Plus or HFSX volume. If there is no UUID and + we were asked to generate one, then generate a new UUID and write it to the + volume. + + Determine whether an HFS volume is mounted on the given device. If so, we + need to use GetVolumeUUIDAttr and SetVolumeUUIDAttr to access the UUID through + the filesystem. If there is no mounted volume, then do direct device access + with GetVolumeUUIDRaw and SetVolumeUUIDRaw. + + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED + */ + +static int +GetVolumeUUID(const char *deviceNamePtr, const char *rawName, volUUID_t *voluu, boolean_t generate) +{ + int result; + char *path = NULL; + + /* + * Determine whether a volume is mounted on this device. If it is HFS, then + * get the mount point's path. If it is non-HFS, then we can exit immediately + * with FSUR_UNRECOGNIZED. + */ + result = GetHFSMountPoint(deviceNamePtr, &path); + if (result != FSUR_IO_SUCCESS) { + return result; + } + + /* + * Get any existing UUID. + */ + if (path) { + result = GetVolumeUUIDAttr(path, voluu); + } + else { + result = GetVolumeUUIDRaw(deviceNamePtr, rawName, voluu); + } + + if (result != FSUR_IO_SUCCESS) { + return result; + } + + /* + * If there was no valid UUID, and we were asked to generate one, then + * generate it and write it back to disk. + */ + if (generate && (uuid_is_null(voluu->uuid))) { + hfs_UUID_t hfsuu; + + GenerateHFSVolumeUUID(&hfsuu); + if (path) { + result = SetVolumeUUIDAttr(path, &hfsuu); + } + else { + result = SetVolumeUUIDRaw(deviceNamePtr, &hfsuu); + } + } + return result; +} + + + +/* + SetVolumeUUID + + Write a UUID to an HFS, HFS Plus or HFSX volume. + + Determine whether an HFS volume is mounted on the given device. If so, we + need to use SetVolumeUUIDAttr to access the UUID through the filesystem. + If there is no mounted volume, then do direct device access SetVolumeUUIDRaw. + + Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL, FSUR_UNRECOGNIZED + */ +static int +SetVolumeUUID(const char *deviceNamePtr, hfs_UUID_t *volumeUUIDPtr) { + int result; + char *path = NULL; + + /* + * Determine whether a volume is mounted on this device. If it is HFS, then + * get the mount point's path. If it is non-HFS, then we can exit immediately + * with FSUR_UNRECOGNIZED. + */ + result = GetHFSMountPoint(deviceNamePtr, &path); + if (result != FSUR_IO_SUCCESS) + goto Err_Exit; + + /* + * Update the UUID. + */ + if (path) + result = SetVolumeUUIDAttr(path, volumeUUIDPtr); + else + result = SetVolumeUUIDRaw(deviceNamePtr, volumeUUIDPtr); + +Err_Exit: + return result; +} @@ -1219,21 +1918,21 @@ GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off int result = FSUR_IO_SUCCESS; u_int32_t allocationBlockSize, firstAllocationBlock, startBlock, blockCount; - if (hfsMasterDirectoryBlockPtr->drSigWord != kHFSSigWord) { + if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drSigWord) != kHFSSigWord) { result = FSUR_UNRECOGNIZED; goto Return; } - allocationBlockSize = hfsMasterDirectoryBlockPtr->drAlBlkSiz; - firstAllocationBlock = hfsMasterDirectoryBlockPtr->drAlBlSt; + allocationBlockSize = OSSwapBigToHostInt32(hfsMasterDirectoryBlockPtr->drAlBlkSiz); + firstAllocationBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drAlBlSt); - if (hfsMasterDirectoryBlockPtr->drEmbedSigWord != kHFSPlusSigWord) { + if (OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedSigWord) != kHFSPlusSigWord) { result = FSUR_UNRECOGNIZED; goto Return; } - startBlock = hfsMasterDirectoryBlockPtr->drEmbedExtent.startBlock; - blockCount = hfsMasterDirectoryBlockPtr->drEmbedExtent.blockCount; + startBlock = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.startBlock); + blockCount = OSSwapBigToHostInt16(hfsMasterDirectoryBlockPtr->drEmbedExtent.blockCount); if ( startOffsetPtr ) *startOffsetPtr = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) + @@ -1255,13 +1954,12 @@ Return: */ static int -GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * name_o) +GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, unsigned char * name_o) { int result = FSUR_IO_SUCCESS; u_int32_t blockSize; char * bufPtr = NULL; HFSPlusVolumeHeader * volHdrPtr; - off_t offset; BTNodeDescriptor * bTreeNodeDescriptorPtr; u_int32_t catalogNodeSize; u_int32_t leafNode; @@ -1288,7 +1986,8 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam /* Verify that it is an HFS+ volume. */ - if (volHdrPtr->signature != kHFSPlusSigWord) { + if (OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSPlusSigWord && + OSSwapBigToHostInt16(volHdrPtr->signature) != kHFSXSigWord) { result = FSUR_IO_FAIL; #if TRACE_HFS_UTIL fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n"); @@ -1296,7 +1995,7 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam goto Return; } - blockSize = volHdrPtr->blockSize; + blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize); catalogExtents = (HFSPlusExtentDescriptor *) malloc(sizeof(HFSPlusExtentRecord)); if ( ! catalogExtents ) { result = FSUR_IO_FAIL; @@ -1306,32 +2005,20 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam catalogExtCount = kHFSPlusExtentDensity; /* if there are overflow catalog extents, then go get them */ - if (catalogExtents[7].blockCount != 0) { + if (OSSwapBigToHostInt32(catalogExtents[7].blockCount) != 0) { result = GetCatalogOverflowExtents(fd, hfsPlusVolumeOffset, volHdrPtr, &catalogExtents, &catalogExtCount); if (result != FSUR_IO_SUCCESS) goto Return; } - offset = (off_t)catalogExtents[0].startBlock * (off_t)blockSize; - /* Read the header node of the catalog B-Tree */ - result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset + offset, &catalogNodeSize, &leafNode); + result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize, + catalogExtCount, catalogExtents, + &catalogNodeSize, &leafNode); if (result != FSUR_IO_SUCCESS) goto Return; - /* Calculate the starting block of the first leaf node */ - - offset = CalcLeafNodeOffset((leafNode * catalogNodeSize), blockSize, catalogExtCount, catalogExtents); - - if ( offset == 0 ) { - result = FSUR_IO_FAIL; -#if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: ERROR: can't find leaf block\n"); -#endif - goto Return; - } - /* Read the first leaf node of the catalog b-tree */ bufPtr = (char *)malloc(catalogNodeSize); @@ -1342,10 +2029,12 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr; - result = readAt( fd, bufPtr, hfsPlusVolumeOffset + offset, catalogNodeSize ); + result = ReadFile(fd, bufPtr, (off_t) leafNode * (off_t) catalogNodeSize, catalogNodeSize, + hfsPlusVolumeOffset, blockSize, + catalogExtCount, catalogExtents); if (result == FSUR_IO_FAIL) { #if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: ERROR: readAt (first leaf) failed\n"); + fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n"); #endif goto Return; // return FSUR_IO_FAIL } @@ -1356,7 +2045,7 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam HFSPlusCatalogKey * k; CFStringRef cfstr; - if ( bTreeNodeDescriptorPtr->numRecords < 1) { + if ( OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords) < 1) { result = FSUR_IO_FAIL; #if TRACE_HFS_UTIL fprintf(stderr, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n"); @@ -1371,12 +2060,12 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam // Get a pointer to the first record. - p = bufPtr + *v; // pointer arithmetic in bytes + p = bufPtr + OSSwapBigToHostInt16(*v); // pointer arithmetic in bytes k = (HFSPlusCatalogKey *)p; // There should be only one record whose parent is the root parent. It should be the first record. - if (k->parentID != kHFSRootParentID) { + if (OSSwapBigToHostInt32(k->parentID) != kHFSRootParentID) { result = FSUR_IO_FAIL; #if TRACE_HFS_UTIL fprintf(stderr, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n"); @@ -1384,11 +2073,38 @@ GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, char * nam goto Return; } + if ((OSSwapBigToHostInt16(k->nodeName.length) > + (sizeof(k->nodeName.unicode) / sizeof(k->nodeName.unicode[0]))) || + OSSwapBigToHostInt16(k->nodeName.length) > 255) { + result = FSUR_IO_FAIL; +#if TRACE_HFS_UTIL + fprintf(stderr, "hfs.util: ERROR: k->nodeName.length is a bad size (%d)\n", OSSwapBigToHostInt16(k->nodeName.length)); +#endif + goto Return; + } + /* Extract the name of the root directory */ - cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, k->nodeName.unicode, k->nodeName.length); - (void) CFStringGetCString(cfstr, name_o, NAME_MAX, kCFStringEncodingUTF8); - CFRelease(cfstr); + { + HFSUniStr255 *swapped; + int i; + + swapped = (HFSUniStr255 *)malloc(sizeof(HFSUniStr255)); + if (swapped == NULL) { + result = FSUR_IO_FAIL; + goto Return; + } + swapped->length = OSSwapBigToHostInt16(k->nodeName.length); + + for (i=0; ilength; i++) { + swapped->unicode[i] = OSSwapBigToHostInt16(k->nodeName.unicode[i]); + } + swapped->unicode[i] = 0; + cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, swapped->unicode, swapped->length); + (void) CFStringGetCString(cfstr, (char *)name_o, NAME_MAX * 3 + 1, kCFStringEncodingUTF8); + CFRelease(cfstr); + free(swapped); + } } result = FSUR_IO_SUCCESS; @@ -1408,12 +2124,10 @@ Return: } /* GetNameFromHFSPlusVolumeStartingAt */ -#pragma options align=mac68k typedef struct { BTNodeDescriptor node; BTHeaderRec header; -} HeaderRec, *HeaderPtr; -#pragma options align=reset +} __attribute__((aligned(2), packed)) HeaderRec, *HeaderPtr; /* -- @@ -1422,7 +2136,9 @@ typedef struct { -- */ static int -GetBTreeNodeInfo(int fd, off_t btreeOffset, u_int32_t *nodeSize, u_int32_t *firstLeafNode) +GetBTreeNodeInfo(int fd, off_t hfsPlusVolumeOffset, u_int32_t blockSize, + u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList, + u_int32_t *nodeSize, u_int32_t *firstLeafNode) { int result; HeaderRec * bTreeHeaderPtr = NULL; @@ -1433,10 +2149,12 @@ GetBTreeNodeInfo(int fd, off_t btreeOffset, u_int32_t *nodeSize, u_int32_t *firs /* Read the b-tree header node */ - result = readAt( fd, bTreeHeaderPtr, btreeOffset, HFS_BLOCK_SIZE ); + result = ReadFile(fd, bTreeHeaderPtr, 0, HFS_BLOCK_SIZE, + hfsPlusVolumeOffset, blockSize, + extentCount, extentList); if ( result == FSUR_IO_FAIL ) { #if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: ERROR: readAt (header node) failed\n"); + fprintf(stderr, "hfs.util: ERROR: reading header node failed\n"); #endif goto free; } @@ -1449,12 +2167,12 @@ GetBTreeNodeInfo(int fd, off_t btreeOffset, u_int32_t *nodeSize, u_int32_t *firs goto free; } - *nodeSize = bTreeHeaderPtr->header.nodeSize; + *nodeSize = OSSwapBigToHostInt16(bTreeHeaderPtr->header.nodeSize); - if (bTreeHeaderPtr->header.leafRecords == 0) + if (OSSwapBigToHostInt32(bTreeHeaderPtr->header.leafRecords) == 0) *firstLeafNode = 0; else - *firstLeafNode = bTreeHeaderPtr->header.firstLeafNode; + *firstLeafNode = OSSwapBigToHostInt32(bTreeHeaderPtr->header.firstLeafNode); free:; free((char*) bTreeHeaderPtr); @@ -1464,44 +2182,6 @@ free:; } /* GetBTreeNodeInfo */ -/* - -- - -- - -- Returns: byte offset to first leaf node - -- - */ -static off_t -CalcLeafNodeOffset(off_t fileOffset, u_int32_t blockSize, u_int32_t extentCount, - HFSPlusExtentDescriptor *extentList) -{ - off_t offset = 0; - int i; - u_long extblks; - u_long leafblk; - - /* Find this block in the list of extents */ - - leafblk = fileOffset / blockSize; - extblks = 0; - - for (i = 0; i < extentCount; ++i) { - if (extentList[i].startBlock == 0 || extentList[i].blockCount == 0) - break; /* done when we reach empty extents */ - - extblks += extentList [i].blockCount; - - if (extblks > leafblk) { - offset = (off_t) extentList[i].startBlock * (off_t) blockSize; - offset += fileOffset - (off_t) ((extblks - extentList[i].blockCount) * blockSize); - break; - } - } - - return offset; - -} /* CalcLeafNodeOffset */ - - /* -- -- @@ -1515,39 +2195,34 @@ GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset, u_int32_t *catalogExtCount) { off_t offset; + u_int32_t numRecords; u_int32_t nodeSize; u_int32_t leafNode; + u_int32_t blockSize; BTNodeDescriptor * bTreeNodeDescriptorPtr; HFSPlusExtentDescriptor * extents; size_t listsize; char * bufPtr = NULL; - int i; + uint32_t i; int result; + blockSize = OSSwapBigToHostInt32(volHdrPtr->blockSize); listsize = *catalogExtCount * sizeof(HFSPlusExtentDescriptor); extents = *catalogExtents; - offset = (off_t)volHdrPtr->extentsFile.extents[0].startBlock * - (off_t)volHdrPtr->blockSize; + offset = (off_t)OSSwapBigToHostInt32(volHdrPtr->extentsFile.extents[0].startBlock) * + (off_t)blockSize; /* Read the header node of the extents B-Tree */ - result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset + offset, + result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset, blockSize, + kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents, &nodeSize, &leafNode); if (result != FSUR_IO_SUCCESS || leafNode == 0) goto Return; - /* Calculate the starting block of the first leaf node */ - - offset = CalcLeafNodeOffset((leafNode * nodeSize), volHdrPtr->blockSize, - kHFSPlusExtentDensity, &volHdrPtr->extentsFile.extents[0]); + /* Calculate the logical position of the first leaf node */ - if (offset == 0) { - result = FSUR_IO_FAIL; -#if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: ERROR: can't find extents b-tree leaf block\n"); -#endif - goto Return; - } + offset = (off_t) leafNode * (off_t) nodeSize; /* Read the first leaf node of the extents b-tree */ @@ -1560,20 +2235,23 @@ GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset, bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr; again: - result = readAt(fd, bufPtr, hfsPlusVolumeOffset + offset, nodeSize); - if ( result == FSUR_IO_FAIL ) { + result = ReadFile(fd, bufPtr, offset, nodeSize, + hfsPlusVolumeOffset, blockSize, + kHFSPlusExtentDensity, volHdrPtr->extentsFile.extents); + if ( result == FSUR_IO_FAIL ) { #if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: ERROR: readAt (first leaf) failed\n"); + fprintf(stderr, "hfs.util: ERROR: reading first leaf failed\n"); #endif - goto Return; + goto Return; } - if (bTreeNodeDescriptorPtr->kind != kBTLeafNode) { + if (bTreeNodeDescriptorPtr->kind != kBTLeafNode) { result = FSUR_IO_FAIL; goto Return; } - for (i = 1; i <= bTreeNodeDescriptorPtr->numRecords; ++i) { + numRecords = OSSwapBigToHostInt16(bTreeNodeDescriptorPtr->numRecords); + for (i = 1; i <= numRecords; ++i) { u_int16_t * v; char * p; HFSPlusExtentKey * k; @@ -1587,35 +2265,26 @@ again: /* Get a pointer to the record */ - p = bufPtr + *v; /* pointer arithmetic in bytes */ + p = bufPtr + OSSwapBigToHostInt16(*v); /* pointer arithmetic in bytes */ k = (HFSPlusExtentKey *)p; - if (k->fileID != kHFSCatalogFileID) + if (OSSwapBigToHostInt32(k->fileID) != kHFSCatalogFileID) goto Return; /* grow list and copy additional extents */ listsize += sizeof(HFSPlusExtentRecord); extents = (HFSPlusExtentDescriptor *) realloc(extents, listsize); - bcopy(p + k->keyLength + sizeof(u_int16_t), + bcopy(p + OSSwapBigToHostInt16(k->keyLength) + sizeof(u_int16_t), &extents[*catalogExtCount], sizeof(HFSPlusExtentRecord)); *catalogExtCount += kHFSPlusExtentDensity; *catalogExtents = extents; } - if ((leafNode = bTreeNodeDescriptorPtr->fLink) != 0) { + if ((leafNode = OSSwapBigToHostInt32(bTreeNodeDescriptorPtr->fLink)) != 0) { - offset = CalcLeafNodeOffset((leafNode * nodeSize), - volHdrPtr->blockSize, kHFSPlusExtentDensity, - &volHdrPtr->extentsFile.extents[0]); + offset = (off_t) leafNode * (off_t) nodeSize; - if (offset == 0) { - result = FSUR_IO_FAIL; -#if TRACE_HFS_UTIL - fprintf(stderr, "hfs.util: ERROR: can't find extents b-tree leaf block\n"); -#endif - goto Return; - } goto again; } @@ -1627,6 +2296,122 @@ Return:; } + +/* + * LogicalToPhysical - Map a logical file position and size to volume-relative physical + * position and number of contiguous bytes at that position. + * + * Inputs: + * logicalOffset Logical offset in bytes from start of file + * length Maximum number of bytes to map + * blockSize Number of bytes per allocation block + * extentCount Number of extents in file + * extentList The file's extents + * + * Outputs: + * physicalOffset Physical offset in bytes from start of volume + * availableBytes Number of bytes physically contiguous (up to length) + * + * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL + */ +static int LogicalToPhysical(off_t offset, ssize_t length, u_int32_t blockSize, + u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList, + off_t *physicalOffset, ssize_t *availableBytes) +{ + off_t temp; + u_int32_t logicalBlock; + u_int32_t extent; + u_int32_t blockCount = 0; + + /* Determine allocation block containing logicalOffset */ + logicalBlock = offset / blockSize; /* This can't overflow for valid volumes */ + offset %= blockSize; /* Offset from start of allocation block */ + + /* Find the extent containing logicalBlock */ + for (extent = 0; extent < extentCount; ++extent) + { + blockCount = OSSwapBigToHostInt32(extentList[extent].blockCount); + + if (blockCount == 0) + return FSUR_IO_FAIL; /* Tried to map past physical end of file */ + + if (logicalBlock < blockCount) + break; /* Found it! */ + + logicalBlock -= blockCount; + } + + if (extent >= extentCount) + return FSUR_IO_FAIL; /* Tried to map past physical end of file */ + + /* + * When we get here, extentList[extent] is the extent containing logicalOffset. + * The desired allocation block is logicalBlock blocks into the extent. + */ + + /* Compute the physical starting position */ + temp = OSSwapBigToHostInt32(extentList[extent].startBlock) + logicalBlock; /* First physical block */ + temp *= blockSize; /* Byte offset of first physical block */ + *physicalOffset = temp + offset; + + /* Compute the available contiguous bytes. */ + temp = blockCount - logicalBlock; /* Number of blocks available in extent */ + temp *= blockSize; + temp -= offset; /* Number of bytes available */ + + if (temp < length) + *availableBytes = temp; + else + *availableBytes = length; + + return FSUR_IO_SUCCESS; +} + + + +/* + * ReadFile - Read bytes from a file. Handles cases where the starting and/or + * ending position are not allocation or device block aligned. + * + * Inputs: + * fd Descriptor for reading the volume + * buffer The bytes are read into here + * offset Offset in file to start reading + * length Number of bytes to read + * volOffset Byte offset from start of device to start of volume + * blockSize Number of bytes per allocation block + * extentCount Number of extents in file + * extentList The file's exents + * + * Returns: FSUR_IO_SUCCESS, FSUR_IO_FAIL + */ +static int ReadFile(int fd, void *buffer, off_t offset, ssize_t length, + off_t volOffset, u_int32_t blockSize, + u_int32_t extentCount, const HFSPlusExtentDescriptor *extentList) +{ + int result = FSUR_IO_SUCCESS; + off_t physOffset; + ssize_t physLength; + + while (length > 0) + { + result = LogicalToPhysical(offset, length, blockSize, extentCount, extentList, + &physOffset, &physLength); + if (result != FSUR_IO_SUCCESS) + break; + + result = readAt(fd, buffer, volOffset+physOffset, physLength); + if (result != FSUR_IO_SUCCESS) + break; + + length -= physLength; + offset += physLength; + buffer = (char *) buffer + physLength; + } + + return result; +} + /* -- readAt = lseek() + read() -- @@ -1646,7 +2431,7 @@ readAt( int fd, void * bufPtr, off_t offset, ssize_t length ) ssize_t dataOffset = 0; int result = FSUR_IO_SUCCESS; - if (ioctl(fd, DKIOCBLKSIZE, &blocksize) < 0) { + if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) { #if TRACE_HFS_UTIL fprintf(stderr, "hfs.util: readAt: couldn't determine block size of device.\n"); #endif @@ -1706,7 +2491,7 @@ writeAt( int fd, void * bufPtr, off_t offset, ssize_t length ) ssize_t dataOffset = 0; int result = FSUR_IO_SUCCESS; - if (ioctl(fd, DKIOCBLKSIZE, &blocksize) < 0) { + if (ioctl(fd, DKIOCGETBLOCKSIZE, &blocksize) < 0) { #if TRACE_HFS_UTIL fprintf(stderr, "hfs.util: couldn't determine block size of device.\n"); #endif @@ -1739,7 +2524,7 @@ writeAt( int fd, void * bufPtr, off_t offset, ssize_t length ) result = FSUR_IO_FAIL; goto Return; } - }; + } bcopy(bufPtr, rawData + dataOffset, length); /* Copy in the new data */ @@ -1766,6 +2551,31 @@ Return: } /* writeAt */ +/* + * Get kernel's encoding bias. + */ +static int +GetEncodingBias() +{ + int mib[3]; + size_t buflen = sizeof(int); + struct vfsconf vfc; + int hint = 0; + + if (getvfsbyname("hfs", &vfc) < 0) + goto error; + + mib[0] = CTL_VFS; + mib[1] = vfc.vfc_typenum; + mib[2] = HFS_ENCODINGBIAS; + + if (sysctl(mib, 3, &hint, &buflen, NULL, 0) < 0) + goto error; + return (hint); +error: + return (-1); +} + /****************************************************************************** * * 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 @@ -1783,21 +2593,35 @@ static char gVSDBPath[] = "/var/db/volinfo.database"; /* Database layout: */ -struct VSDBKey { +typedef struct VSDBKey { char uuid[16]; -}; +} VSDBKey_t; + +typedef struct VSDBKeyUUID { + uuid_string_t uuid_string; +} VSDBKeyUUID_t; struct VSDBRecord { char statusFlags[8]; }; -struct VSDBEntry { - struct VSDBKey key; +/* A VSDB Entry using a uuid_str (36 byte) instead of HFS UUID string (8 byte) */ +typedef struct VSDBEntryUUID { + VSDBKeyUUID_t key; char keySeparator; char space; struct VSDBRecord record; char terminator; -}; +} VSDBEntryUUID_t; + +/* a VSDB entry using the HFS UUID */ +typedef struct VSDBEntryHFS { + VSDBKey_t key; + char keySeparator; + char space; + struct VSDBRecord record; + char terminator; +} VSDBEntryHFS_t; #define DBKEYSEPARATOR ':' #define DBBLANKSPACE ' ' @@ -1814,36 +2638,25 @@ struct VSDBState { typedef struct VSDBState *VSDBStatePtr; -typedef struct { - unsigned long state[5]; - unsigned long count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - /* Internal function prototypes: */ static int LockDB(VSDBStatePtr dbstateptr, int lockmode); static int UnlockDB(VSDBStatePtr dbstateptr); -static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *dbentry, unsigned long options); -static int AddVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry); -static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry); -static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry); -static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2); +static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, volUUID_t *volumeID, VSDBEntryUUID_t *dbentry, unsigned long options); +static int AddVolumeRecord(VSDBStatePtr dbstateptr, VSDBEntryUUID_t *dbentry); +static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, VSDBEntryUUID_t *dbentry); +static int GetVSDBEntry(VSDBStatePtr dbstateptr, VSDBEntryUUID_t *dbentry); +static int CompareVSDBKeys(VSDBKeyUUID_t *key1, VSDBKeyUUID_t *key2); + static void FormatULong(unsigned long u, char *s); -static void FormatUUID(VolumeUUID *volumeID, char *UUIDField); -static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey); +static void FormatDBKey(volUUID_t *volumeID, VSDBKeyUUID_t *dbkey); static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord); -static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry); +static void FormatDBEntry(volUUID_t *volumeID, unsigned long volumeStatusFlags, VSDBEntryUUID_t *dbentry); static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits); -static void SHA1Transform(unsigned long state[5], unsigned char buffer[64]); -static void SHA1Init(SHA1_CTX* context); -static void SHA1Update(SHA1_CTX* context, void* data, size_t len); -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); - /****************************************************************************** @@ -1852,8 +2665,8 @@ static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); * *****************************************************************************/ -void GenerateVolumeUUID(VolumeUUID *newVolumeID) { - SHA1_CTX context; +void GenerateHFSVolumeUUID(hfs_UUID_t *newuuid) { + SHA_CTX context; char randomInputBuffer[26]; unsigned char digest[20]; time_t now; @@ -1862,118 +2675,86 @@ void GenerateVolumeUUID(VolumeUUID *newVolumeID) { int sysdata; char sysctlstring[128]; size_t datalen; - struct loadavg sysloadavg; + double sysloadavg[3]; struct vmtotal sysvmtotal; - + hfs_UUID_t hfsuuid; + + memset (&hfsuuid, 0, sizeof(hfsuuid)); + do { /* Initialize the SHA-1 context for processing: */ - SHA1Init(&context); + SHA1_Init(&context); /* Now process successive bits of "random" input to seed the process: */ /* The current system's uptime: */ uptime = clock(); - SHA1Update(&context, &uptime, sizeof(uptime)); + SHA1_Update(&context, &uptime, sizeof(uptime)); /* The kernel's boot time: */ mib[0] = CTL_KERN; mib[1] = KERN_BOOTTIME; datalen = sizeof(sysdata); sysctl(mib, 2, &sysdata, &datalen, NULL, 0); - SHA1Update(&context, &sysdata, datalen); + SHA1_Update(&context, &sysdata, datalen); /* The system's host id: */ mib[0] = CTL_KERN; mib[1] = KERN_HOSTID; datalen = sizeof(sysdata); sysctl(mib, 2, &sysdata, &datalen, NULL, 0); - SHA1Update(&context, &sysdata, datalen); + SHA1_Update(&context, &sysdata, datalen); /* The system's host name: */ mib[0] = CTL_KERN; mib[1] = KERN_HOSTNAME; datalen = sizeof(sysctlstring); sysctl(mib, 2, sysctlstring, &datalen, NULL, 0); - SHA1Update(&context, sysctlstring, datalen); + SHA1_Update(&context, sysctlstring, datalen); /* The running kernel's OS release string: */ mib[0] = CTL_KERN; mib[1] = KERN_OSRELEASE; datalen = sizeof(sysctlstring); sysctl(mib, 2, sysctlstring, &datalen, NULL, 0); - SHA1Update(&context, sysctlstring, datalen); + SHA1_Update(&context, sysctlstring, datalen); /* The running kernel's version string: */ mib[0] = CTL_KERN; mib[1] = KERN_VERSION; datalen = sizeof(sysctlstring); sysctl(mib, 2, sysctlstring, &datalen, NULL, 0); - SHA1Update(&context, sysctlstring, datalen); + SHA1_Update(&context, sysctlstring, datalen); /* The system's load average: */ - mib[0] = CTL_VM; - mib[1] = VM_LOADAVG; datalen = sizeof(sysloadavg); - sysctl(mib, 2, &sysloadavg, &datalen, NULL, 0); - SHA1Update(&context, &sysloadavg, datalen); + getloadavg(sysloadavg, 3); + SHA1_Update(&context, &sysloadavg, datalen); /* The system's VM statistics: */ mib[0] = CTL_VM; mib[1] = VM_METER; datalen = sizeof(sysvmtotal); sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0); - SHA1Update(&context, &sysvmtotal, datalen); + SHA1_Update(&context, &sysvmtotal, datalen); /* The current GMT (26 ASCII characters): */ time(&now); strncpy(randomInputBuffer, asctime(gmtime(&now)), 26); /* "Mon Mar 27 13:46:26 2000" */ - SHA1Update(&context, randomInputBuffer, 26); + SHA1_Update(&context, randomInputBuffer, 26); /* Pad the accumulated input and extract the final digest hash: */ - SHA1Final(digest, &context); - - memcpy(newVolumeID, digest, sizeof(*newVolumeID)); - } while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0)); -} - - - -void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID) { - int i; - char c; - unsigned long nextdigit; - unsigned long high = 0; - unsigned long low = 0; - unsigned long carry; + SHA1_Final(digest, &context); - for (i = 0; (i < VOLUMEUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) { - if ((c >= '0') && (c <= '9')) { - nextdigit = c - '0'; - } else if ((c >= 'A') && (c <= 'F')) { - nextdigit = c - 'A' + 10; - } else if ((c >= 'a') && (c <= 'f')) { - nextdigit = c - 'a' + 10; - } else { - nextdigit = 0; - }; - carry = ((low & 0xF0000000) >> 28) & 0x0000000F; - high = (high << 4) | carry; - low = (low << 4) | nextdigit; - }; - - volumeID->v.high = high; - volumeID->v.low = low; -} + memcpy(&hfsuuid, digest, sizeof(hfsuuid)); + } while ((hfsuuid.high == 0) || (hfsuuid.low == 0)); + /* now copy out the hfs uuid */ + memcpy (newuuid, &hfsuuid, sizeof (hfsuuid)); - -void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) { - FormatUUID(volumeID, UUIDString); - *(UUIDString+16) = (char)0; /* Append a terminating null character */ + return; } - - int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) { VSDBStatePtr dbstateptr; @@ -1982,7 +2763,7 @@ int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) { dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr)); if (dbstateptr == NULL) { return ENOMEM; - }; + } dbstateptr->dbmode = O_RDWR; dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); @@ -1994,20 +2775,117 @@ int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) { dbstateptr->dbmode = O_RDONLY; dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (dbstateptr->dbfile == -1) { - return errno; - }; - }; + errno_t local_errno = errno; + free(dbstateptr); + return local_errno; + } + } dbstateptr->signature = DBHANDLESIGNATURE; *DBHandlePtr = (VolumeStatusDBHandle)dbstateptr; + + /* VSDBUtil converts the status DB, so we do it here, too */ + ConvertVolumeStatusDB(*DBHandlePtr); + return 0; } +/* Convert the volume status DB from 64-bit (HFS-style) entries into full UUIDs */ +int ConvertVolumeStatusDB(VolumeStatusDBHandle DBHandle) { + VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle; + struct VSDBEntryHFS entry64; + struct stat dbinfo; + int result; + u_int32_t iobuffersize; + void *iobuffer = NULL; + int i; + + if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL; + + if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result; + + + /* + * This function locks the database file then tries to read in + * the size of a old-style HFS UUID database entry. If what it finds + * is a well-formatted HFS entry, then it will convert the entire database. + * If it finds that the file isn't long enough or isn't actually the + * format for the 64-bit UUID struct on-disk, then it will bail out and not + * touch it. Otherwise it will read the whole file and convert it + * + * In practice this means that if the file is empty or if we have already + * converted it then do nothing. + */ + lseek(dbstateptr->dbfile, 0, SEEK_SET); + result = read(dbstateptr->dbfile, &entry64, sizeof(entry64)); + if ((result != sizeof(entry64)) || + (entry64.keySeparator != DBKEYSEPARATOR) || + (entry64.space != DBBLANKSPACE) || + (entry64.terminator != DBRECORDTERMINATOR)) { + result = 0; + goto ErrExit; + } else { + /* Read in a giant buffer */ + if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit; + iobuffersize = dbinfo.st_size; + iobuffer = malloc(iobuffersize); + if (iobuffer == NULL) { + result = ENOMEM; + goto ErrExit; + }; + + lseek(dbstateptr->dbfile, 0, SEEK_SET); + result = read(dbstateptr->dbfile, iobuffer, iobuffersize); + if (result != iobuffersize) { + result = errno; + goto ErrExit; + }; + if ((result = ftruncate(dbstateptr->dbfile, 0)) != 0) { + goto ErrExit; + }; + for (i = 0; i < iobuffersize / sizeof(entry64); i++) { + volUUID_t volumeID; + u_int32_t VolumeStatus; + struct VSDBEntryUUID dbentry; + + /* + * now iterate through the contents of the 64-bit entries in RAM + * and write them on top of the existing database file + */ + entry64 = *(((struct VSDBEntryHFS *)iobuffer) + i); + if ((entry64.keySeparator != DBKEYSEPARATOR) || + (entry64.space != DBBLANKSPACE) || + (entry64.terminator != DBRECORDTERMINATOR)) { + continue; + } + + ConvertHFSUUIDToUUID(entry64.key.uuid, &volumeID); + VolumeStatus = ConvertHexStringToULong(entry64.record.statusFlags, sizeof(entry64.record.statusFlags)); + + FormatDBEntry(&volumeID, VolumeStatus, &dbentry); + if ((result = AddVolumeRecord(dbstateptr, &dbentry)) != sizeof(dbentry)) { + warnx("couldn't convert volume status database: %s", strerror(result)); + goto ErrExit; + }; + }; + + fsync(dbstateptr->dbfile); + + result = 0; + }; + +ErrExit: + if (iobuffer) free(iobuffer); + UnlockDB(dbstateptr); + return result; +} + + -int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus) { +int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, volUUID_t *volumeID, unsigned long *VolumeStatus) { VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle; - struct VSDBEntry dbentry; + struct VSDBEntryUUID dbentry; int result; if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL; @@ -2016,7 +2894,7 @@ int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) { goto ErrExit; - }; + } *VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags)); result = 0; @@ -2028,9 +2906,9 @@ ErrExit: -int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus) { +int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, volUUID_t *volumeID, unsigned long VolumeStatus) { VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle; - struct VSDBEntry dbentry; + struct VSDBEntryUUID dbentry; int result; if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL; @@ -2051,7 +2929,7 @@ int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, result = AddVolumeRecord(dbstateptr, &dbentry); } else { goto ErrExit; - }; + } fsync(dbstateptr->dbfile); @@ -2064,7 +2942,7 @@ ErrExit: -int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) { +int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, volUUID_t *volumeID) { VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle; struct stat dbinfo; int result; @@ -2089,11 +2967,11 @@ int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeI fprintf(stderr, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n"); #endif if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit; - if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry)) <= MAXIOMALLOC) { - iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry); + if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntryUUID)) <= MAXIOMALLOC) { + iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntryUUID); } else { iobuffersize = MAXIOMALLOC; - }; + } #if DEBUG_TRACE fprintf(stderr, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n", (unsigned long)dbinfo.st_size, (unsigned long)dbstateptr->recordPosition); @@ -2104,9 +2982,9 @@ int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeI if (iobuffer == NULL) { result = ENOMEM; goto ErrExit; - }; + } - dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry); + dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntryUUID); do { iotransfersize = dbinfo.st_size - dataoffset; if (iotransfersize > 0) { @@ -2120,33 +2998,33 @@ int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeI if (bytestransferred != iotransfersize) { result = errno; goto ErrExit; - }; + } #if DEBUG_TRACE - fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)(dataoffset - (off_t)sizeof(struct VSDBEntry))); + fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)(dataoffset - (off_t)sizeof(struct VSDBEntryUUID))); #endif - lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET); + lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntryUUID), SEEK_SET); bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize); if (bytestransferred != iotransfersize) { result = errno; goto ErrExit; - }; + } dataoffset += (off_t)iotransfersize; - }; + } } while (iotransfersize > 0); - }; + } #if DEBUG_TRACE - fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))); + fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (unsigned long)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntryUUID)))); #endif - if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) { + if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntryUUID)))) != 0) { goto ErrExit; - }; + } fsync(dbstateptr->dbfile); result = 0; - }; + } ErrExit: if (iobuffer) free(iobuffer); @@ -2199,9 +3077,10 @@ static int UnlockDB(VSDBStatePtr dbstateptr) { -static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, unsigned long options) { - struct VSDBKey searchkey; - struct VSDBEntry dbentry; +static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, volUUID_t *volumeID, + VSDBEntryUUID_t *targetEntry, unsigned long options) { + VSDBKeyUUID_t searchkey; + struct VSDBEntryUUID dbentry; int result; FormatDBKey(volumeID, &searchkey); @@ -2215,9 +3094,9 @@ static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, fprintf(stderr, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry), &dbentry, targetEntry); #endif memcpy(targetEntry, &dbentry, sizeof(*targetEntry)); - }; + } return 0; - }; + } } while (result == 0); return -1; @@ -2225,85 +3104,39 @@ static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, -static int AddVolumeRecord(VSDBStatePtr dbstateptr , struct VSDBEntry *dbentry) { -#if DEBUG_TRACE - VolumeUUIDString id; -#endif - -#if DEBUG_TRACE - strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid)); - id[sizeof(dbentry->key.uuid)] = (char)0; - fprintf(stderr, "AddVolumeRecord: Adding record for UUID #%s...\n", id); -#endif +static int AddVolumeRecord(VSDBStatePtr dbstateptr, VSDBEntryUUID_t *dbentry) { lseek(dbstateptr->dbfile, 0, SEEK_END); - return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry)); + return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntryUUID)); } - - -static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) { -#if DEBUG_TRACE - VolumeUUIDString id; -#endif - -#if DEBUG_TRACE - strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid)); - id[sizeof(dbentry->key.uuid)] = (char)0; - fprintf(stderr, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id, (unsigned long)dbstateptr->recordPosition); -#endif +static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, VSDBEntryUUID_t *dbentry) { lseek(dbstateptr->dbfile, dbstateptr->recordPosition, SEEK_SET); -#if DEBUG_TRACE - fprintf(stderr, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry)); -#endif return write(dbstateptr->dbfile, dbentry, sizeof(*dbentry)); } - - -static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) { - struct VSDBEntry entry; +static int GetVSDBEntry(VSDBStatePtr dbstateptr, VSDBEntryUUID_t *dbentry) { + struct VSDBEntryUUID entry; int result; -#if DEBUG_TRACE - VolumeUUIDString id; -#endif dbstateptr->recordPosition = lseek(dbstateptr->dbfile, 0, SEEK_CUR); -#if 0 // DEBUG_TRACE - fprintf(stderr, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (unsigned long)dbstateptr->recordPosition); -#endif result = read(dbstateptr->dbfile, &entry, sizeof(entry)); if ((result != sizeof(entry)) || (entry.keySeparator != DBKEYSEPARATOR) || (entry.space != DBBLANKSPACE) || (entry.terminator != DBRECORDTERMINATOR)) { return -1; - }; + } -#if DEBUG_TRACE - strncpy(id, entry.key.uuid, sizeof(entry.key.uuid)); - id[sizeof(entry.key.uuid)] = (char)0; - fprintf(stderr, "GetVSDBEntry: returning entry for UUID #%s...\n", id); -#endif memcpy(dbentry, &entry, sizeof(*dbentry)); return 0; -}; - +} -static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) { -#if 0 // DEBUG_TRACE - VolumeUUIDString id; - strncpy(id, key1->uuid, sizeof(key1->uuid)); - id[sizeof(key1->uuid)] = (char)0; - fprintf(stderr, "CompareVSDBKeys: comparing #%s to ", id); - strncpy(id, key2->uuid, sizeof(key2->uuid)); - id[sizeof(key2->uuid)] = (char)0; - fprintf(stderr, "%s (%d.)...\n", id, sizeof(key1->uuid)); -#endif +static int CompareVSDBKeys(VSDBKeyUUID_t *key1, VSDBKeyUUID_t *key2) { - return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid)); + return strcmp(key1->uuid_string, key2->uuid_string); } @@ -2325,23 +3158,17 @@ static void FormatULong(unsigned long u, char *s) { *digitptr++ = (char)(d + '0'); } else { *digitptr++ = (char)(d - 10 + 'A'); - }; + } u = u << 4; - }; + } } +static void FormatDBKey(volUUID_t *volumeID, VSDBKeyUUID_t *dbkey) { + uuid_string_t uuid_str; -static void FormatUUID(VolumeUUID *volumeID, char *UUIDField) { - FormatULong(volumeID->v.high, UUIDField); - FormatULong(volumeID->v.low, UUIDField+8); - -}; - - - -static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) { - FormatUUID(volumeID, dbkey->uuid); + uuid_unparse (volumeID->uuid, uuid_str); + memcpy (dbkey->uuid_string, uuid_str, sizeof (uuid_str)); } @@ -2351,16 +3178,12 @@ static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *d } - -static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *dbentry) { +static void FormatDBEntry(volUUID_t *volumeID, unsigned long volumeStatusFlags, struct VSDBEntryUUID *dbentry) { FormatDBKey(volumeID, &dbentry->key); dbentry->keySeparator = DBKEYSEPARATOR; dbentry->space = DBBLANKSPACE; FormatDBRecord(volumeStatusFlags, &dbentry->record); -#if 0 // DEBUG_TRACE - dbentry->terminator = (char)0; - fprintf(stderr, "FormatDBEntry: '%s' (%d.)\n", dbentry, sizeof(*dbentry)); -#endif + dbentry->terminator = DBRECORDTERMINATOR; } @@ -2382,191 +3205,9 @@ static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits) { nextdigit = c - 'a' + 10; } else { nextdigit = 0; - }; + } n = (n << 4) + nextdigit; - }; + } return n; } - - - -/****************************************************************************** - * - * S H A - 1 I M P L E M E N T A T I O N R O U T I N E S - * - *****************************************************************************/ - -/* - Derived from SHA-1 in C - By Steve Reid - 100% Public Domain - - Test Vectors (from FIPS PUB 180-1) - "abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D - "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 - A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -/* #define LITTLE_ENDIAN * This should be #define'd if true. */ -/* #define SHA1HANDSOFF * Copies data before messing with it. */ - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -#ifdef LITTLE_ENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ - |(rol(block->l[i],8)&0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#if TRACE_HASH -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);printf("t = %2d: %08lX %08lX %08lX %08lX %08lX\n", i, a, b, c, d, e); -#else -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); -#endif - - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -static void SHA1Transform(unsigned long state[5], unsigned char buffer[64]) -{ -unsigned long a, b, c, d, e; -typedef union { - unsigned char c[64]; - unsigned long l[16]; -} CHAR64LONG16; -CHAR64LONG16* block; -#ifdef SHA1HANDSOFF -static unsigned char workspace[64]; - block = (CHAR64LONG16*)workspace; - memcpy(block, buffer, 64); -#else - block = (CHAR64LONG16*)buffer; -#endif - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; -#if TRACE_HASH - printf(" A B C D E\n"); - printf(" -------- -------- -------- -------- --------\n"); - printf(" %08lX %08lX %08lX %08lX %08lX\n", a, b, c, d, e); -#endif - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -} - - -/* SHA1Init - Initialize new context */ - -static void SHA1Init(SHA1_CTX* context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ - -static void SHA1Update(SHA1_CTX* context, void* data, size_t len) -{ - unsigned char *dataptr = (char *)data; - unsigned int i, j; - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], dataptr, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &dataptr[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &dataptr[i], len - i); -} - - -/* Add padding and return the message digest. */ - -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) -{ -unsigned long i, j; -unsigned char finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - SHA1Update(context, (unsigned char *)"\200", 1); - while ((context->count[0] & 504) != 448) { - SHA1Update(context, (unsigned char *)"\0", 1); - } - SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); - } - /* Wipe variables */ - i = j = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); -#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite it's own static vars */ - SHA1Transform(context->state, context->buffer); -#endif -}