#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#include <sys/vmmeter.h>
#include <sys/mount.h>
#include <sys/wait.h>
#include <bsd/dev/disk.h>
#include <sys/loadable_fs.h>
#include <hfs/hfs_format.h>
#include <errno.h>
#include <fcntl.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <CoreFoundation/CFString.h>
#define READ_DEFAULT_ENCODING 1
#ifndef FSUR_MOUNT_HIDDEN
#define FSUR_MOUNT_HIDDEN (-9)
#endif
#ifndef FSUC_GETKEY
#define FSUC_GETKEY 'k'
#endif
#ifndef FSUC_ADOPT
#define FSUC_ADOPT 'a'
#endif
#ifndef FSUC_DISOWN
#define FSUC_DISOWN 'd'
#endif
#ifndef FSUC_NEWUUID
#define FSUC_NEWUUID 'n'
#endif
#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";
char gUnmountCommand[] = "/sbin/umount";
char gReadOnlyOption[] = "-r";
char gReadWriteOption[] = "-w";
char gUsePermissionsOption[] = "perm";
char gIgnorePermissionsOption[] = "noperm";
boolean_t gIsEjectable = 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;
#define VOLUMEUUIDLENGTH 16
typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1];
#define VOLUME_RECORDED 0x80000000
#define VOLUME_USEPERMISSIONS 0x00000001
#define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
typedef void *VolumeStatusDBHandle;
void GenerateVolumeUUID(VolumeUUID *newVolumeID);
void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID);
void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString);
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 CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle);
static void DoDisplayUsage( const char * argv[] );
static void DoFileSystemFile( char * theFileNameSuffixPtr, char * theContentsPtr );
static int DoMount( char * theDeviceNamePtr, const char * theMountPointPtr, boolean_t isLocked );
static int DoProbe( char * theDeviceNamePtr );
static int DoUnmount( const char * theMountPointPtr );
static int DoGetUUIDKey( const char * theDeviceNamePtr );
static int DoChangeUUIDKey( const char * theDeviceNamePtr );
static int DoAdopt( const char * theDeviceNamePtr );
static int DoDisown( const char * theDeviceNamePtr );
static int ParseArgs( int argc, const char * argv[], const char ** actionPtr, const char ** mountPointPtr, boolean_t * isEjectablePtr, boolean_t * isLockedPtr );
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 GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset, HFSPlusVolumeHeader *volHdrPtr,
HFSPlusExtentDescriptor **catalogExtents, u_int32_t *catalogExtCount);
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 );
#if READ_DEFAULT_ENCODING
#define __kCFUserEncodingFileName ("/.CFUserTextEncoding")
static unsigned int __CFStringGetDefaultEncodingForHFSUtil() {
struct passwd *passwdp;
if ((passwdp = getpwuid(0))) { char buffer[MAXPATHLEN + 1];
int fd;
strcpy(buffer, passwdp->pw_dir);
strcat(buffer, __kCFUserEncodingFileName);
if ((fd = open(buffer, O_RDONLY, 0)) > 0) {
size_t readSize;
readSize = read(fd, buffer, MAXPATHLEN);
buffer[(readSize < 0 ? 0 : readSize)] = '\0';
close(fd);
return strtol(buffer, NULL, 0);
}
}
return 0; }
#endif
int main (int argc, const char *argv[])
{
const char * actionPtr = NULL;
char rawDeviceName[MAXPATHLEN];
char blockDeviceName[MAXPATHLEN];
const char * mountPointPtr = NULL;
int result = FSUR_IO_SUCCESS;
boolean_t isLocked = 0;
if ( (result = ParseArgs( argc, argv, & actionPtr, & mountPointPtr, & gIsEjectable, & isLocked )) != 0 ) {
goto AllDone;
}
sprintf(rawDeviceName, "/dev/r%s", argv[2]);
sprintf(blockDeviceName, "/dev/%s", argv[2]);
result = seteuid( 0 );
if ( result ) {
result = FSUR_INVAL;
goto AllDone;
}
result = setegid( 0 );
switch( * actionPtr ) {
case FSUC_PROBE:
result = DoProbe(rawDeviceName);
break;
case FSUC_MOUNT:
case FSUC_MOUNT_FORCE:
result = DoMount(blockDeviceName, mountPointPtr, isLocked);
break;
case FSUC_UNMOUNT:
result = DoUnmount( mountPointPtr );
break;
case FSUC_GETKEY:
result = DoGetUUIDKey( blockDeviceName );
break;
case FSUC_NEWUUID:
result = DoChangeUUIDKey( blockDeviceName );
break;
case FSUC_ADOPT:
result = DoAdopt( blockDeviceName );
break;
case FSUC_DISOWN:
result = DoDisown( blockDeviceName );
break;
default:
DoDisplayUsage( argv );
result = FSUR_INVAL;
break;
}
AllDone:
exit(result);
return result;
}
static int
DoMount(char *deviceNamePtr, const char *mountPointPtr, boolean_t isLocked)
{
int pid;
char *isLockedstr;
char *permissionsOption;
int result = FSUR_IO_FAIL;
union wait status;
char encodeopt[16] = "";
CFStringEncoding encoding;
VolumeUUID targetVolumeUUID;
VolumeStatusDBHandle vsdbhandle = NULL;
unsigned long targetVolumeStatus;
if (mountPointPtr == NULL || *mountPointPtr == '\0')
return (FSUR_IO_FAIL);
targetVolumeStatus = 0;
if (((result = GetVolumeUUID(deviceNamePtr, &targetVolumeUUID, FALSE)) != FSUR_IO_SUCCESS) ||
(targetVolumeUUID.v.high ==0) ||
(targetVolumeUUID.v.low == 0)) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: GetVolumeUUID returned %d.\n", result);
#endif
#if AUTO_ADOPT_FIXED
if (gIsEjectable == 0) {
result = DoAdopt( deviceNamePtr );
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr, result);
#endif
targetVolumeStatus = VOLUME_USEPERMISSIONS;
} else {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr);
#endif
targetVolumeStatus = 0;
};
#endif
} else {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: UUID = %08lX%08lX.\n", targetVolumeUUID.v.high, targetVolumeUUID.v.low);
#endif
if ((result = OpenVolumeStatusDB(&vsdbhandle)) != 0) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: OpenVolumeStatusDB returned %d; ignoring permissions.\n", result);
#endif
targetVolumeStatus = VOLUME_USEPERMISSIONS;
} else {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: Looking up volume status...\n");
#endif
if ((result = GetVolumeStatusDBEntry(vsdbhandle, &targetVolumeUUID, &targetVolumeStatus)) != 0) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: GetVolumeStatusDBEntry returned %d.\n", result);
#endif
#if AUTO_ENTER_FIXED
if (gIsEjectable == 0) {
result = DoAdopt( deviceNamePtr );
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: Auto-adopting %s; result = %d.\n", deviceNamePtr, result);
#endif
targetVolumeStatus = VOLUME_USEPERMISSIONS;
} else {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: DoMount: Not adopting ejectable %s.\n", deviceNamePtr);
#endif
targetVolumeStatus = 0;
};
#else
targetVolumeStatus = 0;
#endif
};
(void)CloseVolumeStatusDB(vsdbhandle);
vsdbhandle = NULL;
};
};
pid = fork();
if (pid == 0) {
isLockedstr = isLocked ? gReadOnlyOption : gReadWriteOption;
permissionsOption =
(targetVolumeStatus & VOLUME_USEPERMISSIONS) ? gUsePermissionsOption : gIgnorePermissionsOption;
#if READ_DEFAULT_ENCODING
encoding = __CFStringGetDefaultEncodingForHFSUtil();
#else
encoding = CFStringGetSystemEncoding();
#endif
sprintf(encodeopt, "-e=%d", (int)encoding);
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: %s %s -o -x -o %s -o %s -o -u=unknown,-g=unknown,-m=0777 -t %s %s %s ...\n",
gMountCommand, isLockedstr, encodeopt, permissionsOption, gHFS_FS_NAME, deviceNamePtr, mountPointPtr);
#endif
(void) execl(gMountCommand, gMountCommand, isLockedstr,
"-o", encodeopt, "-o", permissionsOption,
"-o", "-u=unknown,-g=unknown,-m=0777",
"-t", gHFS_FS_NAME, deviceNamePtr, mountPointPtr, NULL);
return (FSUR_IO_FAIL);
}
if (pid == -1)
return (FSUR_IO_FAIL);
if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status)))
result = status.w_retcode;
else
result = -1;
return (result == 0) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
}
static int
DoUnmount(const char * theMountPointPtr)
{
int pid;
union wait status;
int result;
if (theMountPointPtr == NULL || *theMountPointPtr == '\0') return (FSUR_IO_FAIL);
pid = fork();
if (pid == 0) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: %s %s ...\n", gUnmountCommand, theMountPointPtr);
#endif
(void) execl(gUnmountCommand, gUnmountCommand, theMountPointPtr, NULL);
return (FSUR_IO_FAIL);
}
if (pid == -1)
return (FSUR_IO_FAIL);
if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status)))
result = status.w_retcode;
else
result = -1;
return (result == 0 ? FSUR_IO_SUCCESS : FSUR_IO_FAIL);
}
static int
DoProbe(char *deviceNamePtr)
{
int result = FSUR_UNRECOGNIZED;
int fd = 0;
char * bufPtr;
HFSMasterDirectoryBlock * mdbPtr;
HFSPlusVolumeHeader * volHdrPtr;
u_char volnameUTF8[NAME_MAX+1];
bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
if ( ! bufPtr ) {
result = FSUR_UNRECOGNIZED;
goto Return;
}
mdbPtr = (HFSMasterDirectoryBlock *) bufPtr;
volHdrPtr = (HFSPlusVolumeHeader *) bufPtr;
fd = open( deviceNamePtr, O_RDONLY, 0 );
if( fd <= 0 ) {
result = FSUR_IO_FAIL;
goto Return;
}
result = readAt(fd, bufPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
if (FSUR_IO_FAIL == result)
goto Return;
if (mdbPtr->drSigWord == kHFSSigWord &&
mdbPtr->drEmbedSigWord != kHFSPlusSigWord) {
Boolean cfOK;
CFStringRef cfstr;
CFStringEncoding encoding;
encoding = CFStringGetSystemEncoding();
cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
mdbPtr->drVN, encoding);
cfOK = CFStringGetCString(cfstr, volnameUTF8, NAME_MAX,
kCFStringEncodingUTF8);
CFRelease(cfstr);
if (!cfOK && encoding != kCFStringEncodingMacRoman) {
cfstr = CFStringCreateWithPascalString(kCFAllocatorDefault,
mdbPtr->drVN, kCFStringEncodingMacRoman);
CFStringGetCString(cfstr, volnameUTF8, NAME_MAX,
kCFStringEncodingUTF8);
CFRelease(cfstr);
}
} else if ((volHdrPtr->signature == kHFSPlusSigWord) ||
(mdbPtr->drSigWord == kHFSSigWord &&
mdbPtr->drEmbedSigWord == kHFSPlusSigWord)) {
off_t startOffset;
if (volHdrPtr->signature == kHFSPlusSigWord) {
startOffset = 0;
} else {
result = GetEmbeddedHFSPlusVol(mdbPtr, &startOffset);
if ( result != FSUR_IO_SUCCESS )
goto Return;
}
result = GetNameFromHFSPlusVolumeStartingAt(fd, startOffset,
volnameUTF8);
} else {
result = FSUR_UNRECOGNIZED;
}
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,
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;
}
Return:
if ( bufPtr )
free( bufPtr );
if (fd > 0)
close(fd);
return result;
}
static int
DoGetUUIDKey( const char * theDeviceNamePtr ) {
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:
return result;
}
static int
DoChangeUUIDKey( const char * theDeviceNamePtr ) {
int result;
VolumeUUID newVolumeUUID;
GenerateVolumeUUID(&newVolumeUUID);
result = SetVolumeUUID(theDeviceNamePtr, &newVolumeUUID);
return result;
}
static int
DoAdopt( const char * theDeviceNamePtr ) {
int result, closeresult;
VolumeUUID targetVolumeUUID;
VolumeStatusDBHandle vsdbhandle = NULL;
unsigned long targetVolumeStatus;
if ((result = GetVolumeUUID(theDeviceNamePtr, &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;
result = FSUR_IO_SUCCESS;
Err_Exit:
if (vsdbhandle) {
closeresult = CloseVolumeStatusDB(vsdbhandle);
vsdbhandle = NULL;
if (result == FSUR_IO_SUCCESS) result = closeresult;
};
if ((result != 0) && (result != FSUR_IO_SUCCESS)) result = FSUR_IO_FAIL;
Err_Return:
#if TRACE_HFS_UTIL
if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoAdopt: returning %d...\n", result);
#endif
return result;
}
static int
DoDisown( const char * theDeviceNamePtr ) {
int result, closeresult;
VolumeUUID targetVolumeUUID;
VolumeStatusDBHandle vsdbhandle = NULL;
unsigned long targetVolumeStatus;
if ((result = GetVolumeUUID(theDeviceNamePtr, &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;
result = FSUR_IO_SUCCESS;
Err_Exit:
if (vsdbhandle) {
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
if (result != FSUR_IO_SUCCESS) fprintf(stderr, "DoDisown: returning %d...\n", result);
#endif
return result;
}
static int
ParseArgs(int argc, const char *argv[], const char ** actionPtr,
const char ** mountPointPtr, boolean_t * isEjectablePtr,
boolean_t * isLockedPtr)
{
int result = FSUR_INVAL;
int deviceLength;
int index;
if ( (argc < 3) || (argv[1][0] != '-') ) {
DoDisplayUsage( argv );
goto Return;
}
* actionPtr = & argv[1][1];
switch ( argv[1][1] ) {
case FSUC_PROBE:
if ( argc < 5 ) {
DoDisplayUsage( argv );
goto Return;
} else {
index = 3;
}
break;
case FSUC_UNMOUNT:
* mountPointPtr = argv[3];
index = 0;
break;
case FSUC_MOUNT:
case FSUC_MOUNT_FORCE:
if ( argc < 6 ) {
DoDisplayUsage( argv );
goto Return;
} else {
* mountPointPtr = argv[3];
index = 4;
}
break;
case FSUC_GETKEY:
index = 0;
break;
case FSUC_NEWUUID:
index = 0;
break;
case FSUC_ADOPT:
index = 0;
break;
case FSUC_DISOWN:
index = 0;
break;
default:
DoDisplayUsage( argv );
goto Return;
break;
}
deviceLength = strlen( argv[2] );
if ( deviceLength < 3 || deviceLength > 10 ) {
DoDisplayUsage( argv );
goto Return;
}
if ( index ) {
if ( 0 == strcmp(argv[index],"removable") ) {
* isEjectablePtr = 1;
} else if ( 0 == strcmp(argv[index],"fixed") ) {
* isEjectablePtr = 0;
} else {
printf("hfs.util: ERROR: unrecognized flag (removable/fixed) argv[%d]='%s'\n",index,argv[index]);
}
if ( 0 == strcmp(argv[index+1],"readonly") ) {
* isLockedPtr = 1;
} else if ( 0 == strcmp(argv[index+1],"writable") ) {
* isLockedPtr = 0;
} else {
printf("hfs.util: ERROR: unrecognized flag (readonly/writable) argv[%d]='%s'\n",index,argv[index+1]);
}
}
result = 0;
Return:
return result;
}
static void
DoDisplayUsage(const char *argv[])
{
printf("usage: %s action_arg device_arg [mount_point_arg] [Flags] \n", argv[0]);
printf("action_arg:\n");
printf(" -%c (Probe for mounting)\n", FSUC_PROBE);
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);
printf(" -%c (Adopt permissions)\n", FSUC_ADOPT);
printf("device_arg:\n");
printf(" device we are acting upon (for example, 'disk0s2')\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("Examples:\n");
printf(" %s -p disk0s2 fixed writable\n", argv[0]);
printf(" %s -m disk0s2 /my/hfs removable readonly\n", argv[0]);
return;
}
static void
DoFileSystemFile(char *fileNameSuffixPtr, char *contentsPtr)
{
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);
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 );
}
}
return;
}
static int
GetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr, boolean_t generate) {
int fd = 0;
char * bufPtr;
HFSMasterDirectoryBlock * mdbPtr;
HFSPlusVolumeHeader * volHdrPtr;
VolumeUUID *finderInfoUUIDPtr;
int result;
bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
if ( ! bufPtr ) {
result = FSUR_UNRECOGNIZED;
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) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: GetVolumeUUID: device open failed (errno = %d).\n", errno);
#endif
result = FSUR_IO_FAIL;
goto Err_Exit;
};
};
result = readAt(fd, volHdrPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
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 {
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; }
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;
};
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);
#endif
return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
};
static int
SetVolumeUUID(const char *deviceNamePtr, VolumeUUID *volumeUUIDPtr) {
int fd = 0;
char * bufPtr;
HFSMasterDirectoryBlock * mdbPtr;
HFSPlusVolumeHeader * volHdrPtr;
VolumeUUID *finderInfoUUIDPtr;
int result;
bufPtr = (char *)malloc(HFS_BLOCK_SIZE);
if ( ! bufPtr ) {
result = FSUR_UNRECOGNIZED;
goto Err_Exit;
}
mdbPtr = (HFSMasterDirectoryBlock *) bufPtr;
volHdrPtr = (HFSPlusVolumeHeader *) bufPtr;
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);
#endif
result = FSUR_IO_FAIL;
goto Err_Exit;
};
result = readAt(fd, volHdrPtr, (off_t)(2 * HFS_BLOCK_SIZE), HFS_BLOCK_SIZE);
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 {
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; }
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;
};
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);
#endif
return (result == FSUR_IO_SUCCESS) ? FSUR_IO_SUCCESS : FSUR_IO_FAIL;
};
static int
GetEmbeddedHFSPlusVol (HFSMasterDirectoryBlock * hfsMasterDirectoryBlockPtr, off_t * startOffsetPtr)
{
int result = FSUR_IO_SUCCESS;
u_int32_t allocationBlockSize, firstAllocationBlock, startBlock, blockCount;
if (hfsMasterDirectoryBlockPtr->drSigWord != kHFSSigWord) {
result = FSUR_UNRECOGNIZED;
goto Return;
}
allocationBlockSize = hfsMasterDirectoryBlockPtr->drAlBlkSiz;
firstAllocationBlock = hfsMasterDirectoryBlockPtr->drAlBlSt;
if (hfsMasterDirectoryBlockPtr->drEmbedSigWord != kHFSPlusSigWord) {
result = FSUR_UNRECOGNIZED;
goto Return;
}
startBlock = hfsMasterDirectoryBlockPtr->drEmbedExtent.startBlock;
blockCount = hfsMasterDirectoryBlockPtr->drEmbedExtent.blockCount;
if ( startOffsetPtr )
*startOffsetPtr = ((u_int64_t)startBlock * (u_int64_t)allocationBlockSize) +
((u_int64_t)firstAllocationBlock * (u_int64_t)HFS_BLOCK_SIZE);
Return:
return result;
}
static int
GetNameFromHFSPlusVolumeStartingAt(int fd, off_t hfsPlusVolumeOffset, 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;
u_int32_t catalogExtCount;
HFSPlusExtentDescriptor *catalogExtents = NULL;
volHdrPtr = (HFSPlusVolumeHeader *)malloc(HFS_BLOCK_SIZE);
if ( ! volHdrPtr ) {
result = FSUR_IO_FAIL;
goto Return;
}
result = readAt( fd, volHdrPtr, hfsPlusVolumeOffset + (off_t)(2*HFS_BLOCK_SIZE), HFS_BLOCK_SIZE );
if (result == FSUR_IO_FAIL) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: readAt failed\n");
#endif
goto Return; }
if (volHdrPtr->signature != kHFSPlusSigWord) {
result = FSUR_IO_FAIL;
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: GetNameFromHFSPlusVolumeStartingAt: volHdrPtr->signature != kHFSPlusSigWord\n");
#endif
goto Return;
}
blockSize = volHdrPtr->blockSize;
catalogExtents = (HFSPlusExtentDescriptor *) malloc(sizeof(HFSPlusExtentRecord));
if ( ! catalogExtents ) {
result = FSUR_IO_FAIL;
goto Return;
}
bcopy(volHdrPtr->catalogFile.extents, catalogExtents, sizeof(HFSPlusExtentRecord));
catalogExtCount = kHFSPlusExtentDensity;
if (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;
result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset + offset, &catalogNodeSize, &leafNode);
if (result != FSUR_IO_SUCCESS)
goto Return;
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;
}
bufPtr = (char *)malloc(catalogNodeSize);
if ( ! bufPtr ) {
result = FSUR_IO_FAIL;
goto Return;
}
bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
result = readAt( fd, bufPtr, hfsPlusVolumeOffset + offset, catalogNodeSize );
if (result == FSUR_IO_FAIL) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: ERROR: readAt (first leaf) failed\n");
#endif
goto Return; }
{
u_int16_t * v;
char * p;
HFSPlusCatalogKey * k;
CFStringRef cfstr;
if ( bTreeNodeDescriptorPtr->numRecords < 1) {
result = FSUR_IO_FAIL;
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: ERROR: bTreeNodeDescriptorPtr->numRecords < 1\n");
#endif
goto Return;
}
p = bufPtr + catalogNodeSize - sizeof(u_int16_t); v = (u_int16_t *)p;
p = bufPtr + *v; k = (HFSPlusCatalogKey *)p;
if (k->parentID != kHFSRootParentID) {
result = FSUR_IO_FAIL;
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: ERROR: k->parentID != kHFSRootParentID\n");
#endif
goto Return;
}
cfstr = CFStringCreateWithCharacters(kCFAllocatorDefault, k->nodeName.unicode, k->nodeName.length);
(void) CFStringGetCString(cfstr, name_o, NAME_MAX, kCFStringEncodingUTF8);
CFRelease(cfstr);
}
result = FSUR_IO_SUCCESS;
Return:
if (volHdrPtr)
free((char*) volHdrPtr);
if (catalogExtents)
free((char*) catalogExtents);
if (bufPtr)
free((char*)bufPtr);
return result;
}
#pragma options align=mac68k
typedef struct {
BTNodeDescriptor node;
BTHeaderRec header;
} HeaderRec, *HeaderPtr;
#pragma options align=reset
static int
GetBTreeNodeInfo(int fd, off_t btreeOffset, u_int32_t *nodeSize, u_int32_t *firstLeafNode)
{
int result;
HeaderRec * bTreeHeaderPtr = NULL;
bTreeHeaderPtr = (HeaderRec *) malloc(HFS_BLOCK_SIZE);
if (bTreeHeaderPtr == NULL)
return (FSUR_IO_FAIL);
result = readAt( fd, bTreeHeaderPtr, btreeOffset, HFS_BLOCK_SIZE );
if ( result == FSUR_IO_FAIL ) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: ERROR: readAt (header node) failed\n");
#endif
goto free;
}
if ( bTreeHeaderPtr->node.kind != kBTHeaderNode ) {
result = FSUR_IO_FAIL;
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: ERROR: bTreeHeaderPtr->node.kind != kBTHeaderNode\n");
#endif
goto free;
}
*nodeSize = bTreeHeaderPtr->header.nodeSize;
if (bTreeHeaderPtr->header.leafRecords == 0)
*firstLeafNode = 0;
else
*firstLeafNode = bTreeHeaderPtr->header.firstLeafNode;
free:;
free((char*) bTreeHeaderPtr);
return result;
}
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;
leafblk = fileOffset / blockSize;
extblks = 0;
for (i = 0; i < extentCount; ++i) {
if (extentList[i].startBlock == 0 || extentList[i].blockCount == 0)
break;
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;
}
static int
GetCatalogOverflowExtents(int fd, off_t hfsPlusVolumeOffset,
HFSPlusVolumeHeader *volHdrPtr,
HFSPlusExtentDescriptor **catalogExtents,
u_int32_t *catalogExtCount)
{
off_t offset;
u_int32_t nodeSize;
u_int32_t leafNode;
BTNodeDescriptor * bTreeNodeDescriptorPtr;
HFSPlusExtentDescriptor * extents;
size_t listsize;
char * bufPtr = NULL;
int i;
int result;
listsize = *catalogExtCount * sizeof(HFSPlusExtentDescriptor);
extents = *catalogExtents;
offset = (off_t)volHdrPtr->extentsFile.extents[0].startBlock *
(off_t)volHdrPtr->blockSize;
result = GetBTreeNodeInfo(fd, hfsPlusVolumeOffset + offset,
&nodeSize, &leafNode);
if (result != FSUR_IO_SUCCESS || leafNode == 0)
goto Return;
offset = CalcLeafNodeOffset((leafNode * nodeSize), volHdrPtr->blockSize,
kHFSPlusExtentDensity, &volHdrPtr->extentsFile.extents[0]);
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;
}
bufPtr = (char *)malloc(nodeSize);
if (! bufPtr) {
result = FSUR_IO_FAIL;
goto Return;
}
bTreeNodeDescriptorPtr = (BTNodeDescriptor *)bufPtr;
again:
result = readAt(fd, bufPtr, hfsPlusVolumeOffset + offset, nodeSize);
if ( result == FSUR_IO_FAIL ) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: ERROR: readAt (first leaf) failed\n");
#endif
goto Return;
}
if (bTreeNodeDescriptorPtr->kind != kBTLeafNode) {
result = FSUR_IO_FAIL;
goto Return;
}
for (i = 1; i <= bTreeNodeDescriptorPtr->numRecords; ++i) {
u_int16_t * v;
char * p;
HFSPlusExtentKey * k;
p = bufPtr + nodeSize - (sizeof(u_int16_t) * i);
v = (u_int16_t *)p;
p = bufPtr + *v;
k = (HFSPlusExtentKey *)p;
if (k->fileID != kHFSCatalogFileID)
goto Return;
listsize += sizeof(HFSPlusExtentRecord);
extents = (HFSPlusExtentDescriptor *) realloc(extents, listsize);
bcopy(p + k->keyLength + sizeof(u_int16_t),
&extents[*catalogExtCount], sizeof(HFSPlusExtentRecord));
*catalogExtCount += kHFSPlusExtentDensity;
*catalogExtents = extents;
}
if ((leafNode = bTreeNodeDescriptorPtr->fLink) != 0) {
offset = CalcLeafNodeOffset((leafNode * nodeSize),
volHdrPtr->blockSize, kHFSPlusExtentDensity,
&volHdrPtr->extentsFile.extents[0]);
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;
}
Return:;
if (bufPtr)
free(bufPtr);
return (result);
}
static ssize_t
readAt( int fd, void * bufPtr, off_t offset, ssize_t length )
{
int blocksize;
off_t lseekResult;
ssize_t readResult;
void * rawData = NULL;
off_t rawOffset;
ssize_t rawLength;
int result = FSUR_IO_SUCCESS;
if (ioctl(fd, DKIOCBLKSIZE, &blocksize) < 0) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: readAt: couldn't determine block size of device.\n");
#endif
result = FSUR_IO_FAIL;
goto Return;
}
rawOffset = offset / blocksize * blocksize;
rawLength = ((length + blocksize - 1) / blocksize) * blocksize;
rawData = malloc(rawLength);
if (rawData == NULL) {
result = FSUR_IO_FAIL;
goto Return;
}
lseekResult = lseek( fd, rawOffset, SEEK_SET );
if ( lseekResult != rawOffset ) {
result = FSUR_IO_FAIL;
goto Return;
}
readResult = read(fd, rawData, rawLength);
if ( readResult != rawLength ) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: readAt: attempt to read data from device failed (errno = %d)?\n", errno);
#endif
result = FSUR_IO_FAIL;
goto Return;
}
bcopy(rawData + (offset - rawOffset), bufPtr, length);
Return:
if (rawData) {
free(rawData);
}
return result;
}
static ssize_t
writeAt( int fd, void * bufPtr, off_t offset, ssize_t length )
{
int blocksize;
off_t deviceoffset;
ssize_t bytestransferred;
void * rawData = NULL;
off_t rawOffset;
ssize_t rawLength;
int result = FSUR_IO_SUCCESS;
if (ioctl(fd, DKIOCBLKSIZE, &blocksize) < 0) {
#if TRACE_HFS_UTIL
fprintf(stderr, "hfs.util: couldn't determine block size of device.\n");
#endif
result = FSUR_IO_FAIL;
goto Return;
}
rawOffset = offset / blocksize * blocksize;
rawLength = ((length + blocksize - 1) / blocksize) * blocksize;
rawData = malloc(rawLength);
if (rawData == NULL) {
result = FSUR_IO_FAIL;
goto Return;
}
deviceoffset = lseek( fd, rawOffset, SEEK_SET );
if ( deviceoffset != rawOffset ) {
result = FSUR_IO_FAIL;
goto Return;
}
if (((rawOffset % blocksize) != 0) || ((rawLength % blocksize) != 0)) {
bytestransferred = read(fd, rawData, rawLength);
if ( bytestransferred != rawLength ) {
#if TRACE_HFS_UTIL
fprintf(stderr, "writeAt: attempt to pre-read data from device failed (errno = %d)\n", errno);
#endif
result = FSUR_IO_FAIL;
goto Return;
}
};
bcopy(bufPtr, rawData + (offset - rawOffset), length);
deviceoffset = lseek( fd, rawOffset, SEEK_SET );
if ( deviceoffset != rawOffset ) {
result = FSUR_IO_FAIL;
goto Return;
}
bytestransferred = write(fd, rawData, rawLength);
if ( bytestransferred != rawLength ) {
#if TRACE_HFS_UTIL
fprintf(stderr, "writeAt: attempt to write data to device failed?!");
#endif
result = FSUR_IO_FAIL;
goto Return;
}
Return:
if (rawData) free(rawData);
return result;
}
#define DBHANDLESIGNATURE 0x75917737
#define DBMARKPOSITION 1
static char gVSDBPath[] = "/var/db/volinfo.database";
#define MAXIOMALLOC 16384
struct VSDBKey {
char uuid[16];
};
struct VSDBRecord {
char statusFlags[8];
};
struct VSDBEntry {
struct VSDBKey key;
char keySeparator;
char space;
struct VSDBRecord record;
char terminator;
};
#define DBKEYSEPARATOR ':'
#define DBBLANKSPACE ' '
#define DBRECORDTERMINATOR '\n'
struct VSDBState {
unsigned long signature;
int dbfile;
int dbmode;
off_t recordPosition;
};
typedef struct VSDBState *VSDBStatePtr;
typedef struct {
unsigned long state[5];
unsigned long count[2];
unsigned char buffer[64];
} SHA1_CTX;
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 void FormatULong(unsigned long u, char *s);
static void FormatUUID(VolumeUUID *volumeID, char *UUIDField);
static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey);
static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord);
static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *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);
void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
SHA1_CTX context;
char randomInputBuffer[26];
unsigned char digest[20];
time_t now;
clock_t uptime;
int mib[2];
int sysdata;
char sysctlstring[128];
size_t datalen;
struct loadavg sysloadavg;
struct vmtotal sysvmtotal;
do {
SHA1Init(&context);
uptime = clock();
SHA1Update(&context, &uptime, sizeof(uptime));
mib[0] = CTL_KERN;
mib[1] = KERN_BOOTTIME;
datalen = sizeof(sysdata);
sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
SHA1Update(&context, &sysdata, datalen);
mib[0] = CTL_KERN;
mib[1] = KERN_HOSTID;
datalen = sizeof(sysdata);
sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
SHA1Update(&context, &sysdata, datalen);
mib[0] = CTL_KERN;
mib[1] = KERN_HOSTNAME;
datalen = sizeof(sysctlstring);
sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
SHA1Update(&context, sysctlstring, datalen);
mib[0] = CTL_KERN;
mib[1] = KERN_OSRELEASE;
datalen = sizeof(sysctlstring);
sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
SHA1Update(&context, sysctlstring, datalen);
mib[0] = CTL_KERN;
mib[1] = KERN_VERSION;
datalen = sizeof(sysctlstring);
sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
SHA1Update(&context, sysctlstring, datalen);
mib[0] = CTL_VM;
mib[1] = VM_LOADAVG;
datalen = sizeof(sysloadavg);
sysctl(mib, 2, &sysloadavg, &datalen, NULL, 0);
SHA1Update(&context, &sysloadavg, datalen);
mib[0] = CTL_VM;
mib[1] = VM_METER;
datalen = sizeof(sysvmtotal);
sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
SHA1Update(&context, &sysvmtotal, datalen);
time(&now);
strncpy(randomInputBuffer, asctime(gmtime(&now)), 26);
SHA1Update(&context, randomInputBuffer, 26);
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;
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;
}
void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) {
FormatUUID(volumeID, UUIDString);
*(UUIDString+16) = (char)0;
}
int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
VSDBStatePtr dbstateptr;
*DBHandlePtr = NULL;
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);
if (dbstateptr->dbfile == -1) {
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;
};
};
dbstateptr->signature = DBHANDLESIGNATURE;
*DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
return 0;
}
int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long *VolumeStatus) {
VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
struct VSDBEntry dbentry;
int result;
if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
goto ErrExit;
};
*VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
result = 0;
ErrExit:
UnlockDB(dbstateptr);
return result;
}
int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, unsigned long VolumeStatus) {
VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
struct VSDBEntry dbentry;
int result;
if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
FormatDBEntry(volumeID, VolumeStatus, &dbentry);
if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) == 0) {
#if DEBUG_TRACE
fprintf(stderr,"AddLocalVolumeUUID: found record in database; updating in place.\n");
#endif
result = UpdateVolumeRecord(dbstateptr, &dbentry);
} else if (result == -1) {
#if DEBUG_TRACE
fprintf(stderr,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
#endif
result = AddVolumeRecord(dbstateptr, &dbentry);
} else {
goto ErrExit;
};
fsync(dbstateptr->dbfile);
result = 0;
ErrExit:
UnlockDB(dbstateptr);
return result;
}
int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
struct stat dbinfo;
int result;
unsigned long iobuffersize;
void *iobuffer = NULL;
off_t dataoffset;
unsigned long iotransfersize;
unsigned long bytestransferred;
if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
#if DEBUG_TRACE
fprintf(stderr, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result);
#endif
if (result == -1) result = 0;
goto StdEdit;
} else {
#if DEBUG_TRACE
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);
} 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);
fprintf(stderr, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize);
#endif
if (iobuffersize > 0) {
iobuffer = malloc(iobuffersize);
if (iobuffer == NULL) {
result = ENOMEM;
goto ErrExit;
};
dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
do {
iotransfersize = dbinfo.st_size - dataoffset;
if (iotransfersize > 0) {
if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
#if DEBUG_TRACE
fprintf(stderr, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (unsigned long)dataoffset);
#endif
lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
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)));
#endif
lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), 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))));
#endif
if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
goto ErrExit;
};
fsync(dbstateptr->dbfile);
result = 0;
};
ErrExit:
if (iobuffer) free(iobuffer);
UnlockDB(dbstateptr);
StdEdit:
return result;
}
int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
dbstateptr->signature = 0;
close(dbstateptr->dbfile);
dbstateptr->dbfile = 0;
free(dbstateptr);
return 0;
}
static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
#if DEBUG_TRACE
fprintf(stderr, "LockDB: Locking VSDB file...\n");
#endif
return flock(dbstateptr->dbfile, lockmode);
}
static int UnlockDB(VSDBStatePtr dbstateptr) {
#if DEBUG_TRACE
fprintf(stderr, "UnlockDB: Unlocking VSDB file...\n");
#endif
return flock(dbstateptr->dbfile, LOCK_UN);
}
static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, unsigned long options) {
struct VSDBKey searchkey;
struct VSDBEntry dbentry;
int result;
FormatDBKey(volumeID, &searchkey);
lseek(dbstateptr->dbfile, 0, SEEK_SET);
do {
result = GetVSDBEntry(dbstateptr, &dbentry);
if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
if (targetEntry != NULL) {
#if DEBUG_TRACE
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;
}
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
lseek(dbstateptr->dbfile, 0, SEEK_END);
return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry));
}
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
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;
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
return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid));
}
static void FormatULong(unsigned long u, char *s) {
unsigned long d;
int i;
char *digitptr = s;
for (i = 0; i < 8; ++i) {
d = ((u & 0xF0000000) >> 28) & 0x0000000F;
if (d < 10) {
*digitptr++ = (char)(d + '0');
} else {
*digitptr++ = (char)(d - 10 + 'A');
};
u = u << 4;
};
}
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);
}
static void FormatDBRecord(unsigned long volumeStatusFlags, struct VSDBRecord *dbrecord) {
FormatULong(volumeStatusFlags, dbrecord->statusFlags);
}
static void FormatDBEntry(VolumeUUID *volumeID, unsigned long volumeStatusFlags, struct VSDBEntry *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;
}
static unsigned long ConvertHexStringToULong(const char *hs, long maxdigits) {
int i;
char c;
unsigned long nextdigit;
unsigned long n;
n = 0;
for (i = 0; (i < 8) && ((c = hs[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;
};
n = (n << 4) + nextdigit;
};
return n;
}
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
#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))
#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
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
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
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);
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
a = b = c = d = e = 0;
}
static void SHA1Init(SHA1_CTX* context)
{
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;
}
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);
}
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);
}
SHA1Update(context, (unsigned char *)"\200", 1);
while ((context->count[0] & 504) != 448) {
SHA1Update(context, (unsigned char *)"\0", 1);
}
SHA1Update(context, finalcount, 8);
for (i = 0; i < 20; i++) {
digest[i] = (unsigned char)
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
}
i = j = 0;
memset(context->buffer, 0, 64);
memset(context->state, 0, 20);
memset(context->count, 0, 8);
memset(&finalcount, 0, 8);
#ifdef SHA1HANDSOFF
SHA1Transform(context->state, context->buffer);
#endif
}