#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/ucred.h>
#include <sys/resource.h>
#include <sys/vmmeter.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <err.h>
#include <errno.h>
#include <dirent.h>
#include <strings.h>
#include <sys/attr.h>
struct FinderAttrBuf {
unsigned long info_length;
unsigned long finderinfo[8];
};
static char usage[] = "Usage: %s [-a path] | [-c path ] [-d path] [-i]\n";
static char gHFSTypeName[] = "hfs";
#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 check_uid(void);
static int GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr, boolean_t generate);
static int SetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr);
static int AdoptAllLocalVolumes(void);
static int AdoptVolume(const char *path);
static int DisownVolume(const char *path);
static int ClearVolumeUUID(const char *path);
static int DisplayVolumeStatus(const char *path);
static int UpdateMountStatus(const char *path, unsigned long volstatus);
int main (int argc, const char *argv[])
{
int arg;
char option;
int result = 0;
if (argc < 2) {
fprintf(stderr, usage, argv[0]);
exit(1);
};
for (arg = 1; arg < argc; ++arg) {
if ((argv[arg][0] == '-') &&
((option = argv[arg][1]) != (char)0) &&
(argv[arg][2] == (char)0)) {
switch (option) {
case 'a':
case 'A':
if (++arg >= argc) {
fprintf(stderr, usage, argv[0]);
exit(1);
}
check_uid();
result = AdoptVolume(argv[arg]);
break;
case 'c':
case 'C':
if (++arg >= argc) {
fprintf(stderr, usage, argv[0]);
exit(1);
};
result = DisplayVolumeStatus(argv[arg]);
break;
case 'd':
case 'D':
if (++arg >= argc) {
fprintf(stderr, usage, argv[0]);
exit(1);
};
check_uid();
result = DisownVolume(argv[arg]);
break;
case 'h':
case 'H':
printf(usage, argv[0]);
printf("where\n");
printf("\t-a adopts (activates) on-disk permissions on the specified path,\n");
printf("\t-c checks the status of the permissions usage on the specified path\n");
printf("\t-d disowns (deactivates) the on-disk permissions on the specified path\n");
printf("\t-i initializes the permissions database to include all mounted HFS/HFS+ volumes\n");
break;
case 'i':
case 'I':
check_uid();
result = AdoptAllLocalVolumes();
break;
case 'x':
case 'X':
if (++arg >= argc) {
fprintf(stderr, usage, argv[0]);
exit(1);
};
check_uid();
result = ClearVolumeUUID(argv[arg]);
break;
default:
fprintf(stderr, usage, argv[0]);
exit(1);
}
}
}
if (result < 0) result = 1;
exit(result); return result; }
static void check_uid(void) {
if (getuid() != 0) {
fprintf(stderr, "###\n");
fprintf(stderr, "### You must be root to perform this operation.\n");
fprintf(stderr, "###\n");
exit(1);
};
}
static int
UpdateMountStatus(const char *path, unsigned long volstatus) {
struct statfs mntstat;
int result;
union wait status;
int pid;
result = statfs(path, &mntstat);
if (result != 0) {
warn("couldn't look up mount status for '%s'", path);
return errno;
};
pid = fork();
if (pid == 0) {
result = execl("/sbin/mount", "mount",
"-t", mntstat.f_fstypename,
"-u","-o", (volstatus & VOLUME_USEPERMISSIONS) ? "perm" : "noperm",
mntstat.f_mntfromname, mntstat.f_mntonname, NULL);
#if DEBUG_TRACE
if (result) {
fprintf(stderr, "Error from execl(/sbin/mount...): result = %d.\n", result);
};
fprintf(stderr, "UpdateMountStatus in child: returning 1...\n");
#endif
return (1);
}
if (pid == -1) {
warn("couldn't fork to execute mount command");
return errno;
};
if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status))) {
result = status.w_retcode;
} else {
result = -1;
};
return result;
}
static int
AdoptAllLocalVolumes(void) {
struct statfs *mntstatptr;
int fscount;
fscount = getmntinfo(&mntstatptr, MNT_WAIT);
if (fscount == 0) {
warn("couldn't get information on mounted volumes");
return errno;
};
while (fscount > 0) {
if (strcmp(mntstatptr->f_fstypename, gHFSTypeName) == 0) {
(void)AdoptVolume(mntstatptr->f_mntonname);
};
++mntstatptr;
--fscount;
};
return 0;
}
static int
AdoptVolume(const char *path) {
VolumeUUID targetuuid;
VolumeStatusDBHandle vsdb;
unsigned long volstatus;
int result = 0;
result = GetVolumeUUID(path, &targetuuid, TRUE);
if (result != 0) {
warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
return result;
};
if ((targetuuid.v.high == 0) || (targetuuid.v.low == 0)) {
warnx("internal error: incomplete UUID generated.");
return EINVAL;
};
if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
warnx("couldn't access volume status database: %s", strerror(result));
return result;
};
if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
volstatus = 0;
};
volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;
if ((result = SetVolumeStatusDBEntry(vsdb, &targetuuid, volstatus)) != 0) {
warnx("couldn't update volume status database: %s", strerror(result));
return result;
};
(void)CloseVolumeStatusDB(CloseVolumeStatusDB);
sync();
if ((result = UpdateMountStatus(path, volstatus)) != 0) {
warnx("couldn't update mount status of '%s': %s", path, strerror(result));
return result;
};
return 0;
}
static int
DisownVolume(const char *path) {
VolumeUUID targetuuid;
VolumeStatusDBHandle vsdb;
unsigned long volstatus;
int result = 0;
result = GetVolumeUUID(path, &targetuuid, TRUE);
if (result != 0) {
warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
return result;
};
volstatus = 0;
if ((targetuuid.v.high != 0) || (targetuuid.v.low != 0)) {
if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
warnx("couldn't access volume status database: %s", strerror(result));
return result;
};
if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
volstatus = 0;
};
volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;
if ((result = SetVolumeStatusDBEntry(vsdb, &targetuuid, volstatus)) != 0) {
warnx("couldn't update volume status database: %s", strerror(result));
return result;
};
(void)CloseVolumeStatusDB(CloseVolumeStatusDB);
sync();
};
if ((result = UpdateMountStatus(path, volstatus)) != 0) {
warnx("couldn't update mount status of '%s': %s", path, strerror(result));
return result;
};
return result;
};
static int
ClearVolumeUUID(const char *path) {
VolumeUUID targetuuid;
VolumeStatusDBHandle vsdb;
unsigned long volstatus;
int result = 0;
result = GetVolumeUUID(path, &targetuuid, FALSE);
if (result != 0) {
warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
return result;
};
if ((targetuuid.v.high != 0) || (targetuuid.v.low != 0)) {
if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
warnx("couldn't access volume status database: %s", strerror(result));
return result;
};
if (GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus) == 0) {
if ((result = DeleteVolumeStatusDBEntry(vsdb, &targetuuid)) != 0) {
warnx("couldn't update volume status database: %s", strerror(result));
return result;
};
};
(void)CloseVolumeStatusDB(CloseVolumeStatusDB);
sync();
if ((result = UpdateMountStatus(path, 0)) != 0) {
warnx("couldn't update mount status of '%s': %s", path, strerror(result));
return result;
};
targetuuid.v.high = 0;
targetuuid.v.low = 0;
result = SetVolumeUUID(path, &targetuuid);
};
return result;
};
static int
DisplayVolumeStatus(const char *path) {
VolumeUUID targetuuid;
VolumeStatusDBHandle vsdb;
unsigned long volstatus;
int result = 0;
result = GetVolumeUUID(path, &targetuuid, FALSE);
if (result != 0) {
warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
return result;
};
if ((targetuuid.v.high == 0) || (targetuuid.v.low == 0)) {
warnx("no valid volume UUID found on '%s': permissions are disabled.", path);
return 0;
};
if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
warnx("couldn't access volume status database: %s", strerror(result));
return result;
};
if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
printf("No entry found for '%s'.\n", path);
goto Std_Exit;
};
if (volstatus & VOLUME_USEPERMISSIONS) {
printf("Permissions on '%s' are enabled.\n", path);
} else {
printf("Permissions on '%s' are disabled.\n", path);
};
Std_Exit:
(void)CloseVolumeStatusDB(CloseVolumeStatusDB);
return result;
}
static int
GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr, boolean_t generate) {
struct attrlist alist;
struct FinderAttrBuf volFinderInfo;
VolumeUUID *finderInfoUUIDPtr;
int result;
alist.bitmapcount = 5;
alist.reserved = 0;
alist.commonattr = ATTR_CMN_FNDRINFO;
alist.volattr = ATTR_VOL_INFO;
alist.dirattr = 0;
alist.fileattr = 0;
alist.forkattr = 0;
result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
if (result) {
warn("Couldn't get volume information for '%s'", path);
result = errno;
goto Err_Exit;
}
finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
if (generate && ((finderInfoUUIDPtr->v.high == 0) || (finderInfoUUIDPtr->v.low == 0))) {
GenerateVolumeUUID(volumeUUIDPtr);
bcopy(volumeUUIDPtr, finderInfoUUIDPtr, sizeof(*finderInfoUUIDPtr));
result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
if (result) {
warn("Couldn't update volume information for '%s'", path);
result = errno;
goto Err_Exit;
};
};
bcopy(finderInfoUUIDPtr, volumeUUIDPtr, sizeof(*volumeUUIDPtr));
result = 0;
Err_Exit:
return result;
};
static int
SetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr) {
struct attrlist alist;
struct FinderAttrBuf volFinderInfo;
VolumeUUID *finderInfoUUIDPtr;
int result;
alist.bitmapcount = 5;
alist.reserved = 0;
alist.commonattr = ATTR_CMN_FNDRINFO;
alist.volattr = ATTR_VOL_INFO;
alist.dirattr = 0;
alist.fileattr = 0;
alist.forkattr = 0;
result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
if (result) {
warn("Couldn't get volume information for '%s'", path);
result = errno;
goto Err_Exit;
}
finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
bcopy(volumeUUIDPtr, finderInfoUUIDPtr, sizeof(*finderInfoUUIDPtr));
result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
if (result != 0) {
warn("Couldn't set volume information for '%s'", path);
result = errno;
goto Err_Exit;
};
Err_Exit:
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);
if (dbstateptr->dbfile == -1) {
dbstateptr->dbmode = O_RDONLY;
dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
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
}