1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
25	About vsdbutil.c:
26		Contains code to manipulate the volume status DB (/var/db/volinfo.database).
27
28	Change History:
29	18-Apr-2000	Pat Dirks	New Today.
30
31 */
32
33
34/* ************************************** I N C L U D E S ***************************************** */
35
36#include <sys/types.h>
37#include <sys/param.h>
38#include <sys/mount.h>
39#include <sys/stat.h>
40#include <sys/sysctl.h>
41#include <sys/time.h>
42#include <sys/ucred.h>
43#include <sys/resource.h>
44#include <sys/vmmeter.h>
45#include <sys/wait.h>
46#include <stdio.h>
47#include <unistd.h>
48#include <stdlib.h>
49#include <fcntl.h>
50#include <err.h>
51#include <errno.h>
52#include <dirent.h>
53#include <strings.h>
54
55#include <sys/attr.h>
56
57/*
58 * CommonCrypto is meant to be a more stable API than OpenSSL.
59 * Defining COMMON_DIGEST_FOR_OPENSSL gives API-compatibility
60 * with OpenSSL, so we don't have to change the code.
61 */
62#define COMMON_DIGEST_FOR_OPENSSL
63#include <CommonCrypto/CommonDigest.h>
64#include <libkern/OSByteOrder.h>
65
66struct FinderAttrBuf {
67	u_int32_t info_length;
68	u_int32_t finderinfo[8];
69};
70
71static char usage[] = "Usage: %s [-a path] | [-c path ] [-d path] [-i]\n";
72
73static char gHFSTypeName[] = "hfs";
74
75/*****************************************************************************
76 *
77 * The following should really be in some system header file:
78 *
79 *****************************************************************************/
80
81#define VOLUMEUUIDVALUESIZE 2
82typedef union VolumeUUID {
83	u_int32_t value[VOLUMEUUIDVALUESIZE];
84	struct {
85		u_int32_t high;
86		u_int32_t low;
87	} v;
88} VolumeUUID;
89
90#define VOLUMEUUIDLENGTH 16
91typedef char VolumeUUIDString[VOLUMEUUIDLENGTH+1];
92
93#define VOLUME_RECORDED 0x80000000
94#define VOLUME_USEPERMISSIONS 0x00000001
95#define VOLUME_VALIDSTATUSBITS ( VOLUME_USEPERMISSIONS )
96
97typedef void *VolumeStatusDBHandle;
98
99void GenerateVolumeUUID(VolumeUUID *newVolumeID);
100void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID);
101void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString);
102
103int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr);
104int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, u_int32_t *VolumeStatus);
105int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, u_int32_t VolumeStatus);
106int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID);
107int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle);
108
109/*****************************************************************************
110 *
111 * Internal function prototypes:
112 *
113 *****************************************************************************/
114
115static void check_uid(void);
116static int GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr, boolean_t generate);
117static int SetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr);
118static int AdoptAllLocalVolumes(void);
119static int AdoptVolume(const char *path);
120static int DisownVolume(const char *path);
121static int ClearVolumeUUID(const char *path);
122static int DisplayVolumeStatus(const char *path);
123static int UpdateMountStatus(const char *path, u_int32_t volstatus);
124
125
126int main (int argc, const char *argv[])
127{
128	int arg;
129	char option;
130	int result = 0;
131
132	if (argc < 2) {
133		fprintf(stderr, usage, argv[0]);
134		exit(1);
135	};
136
137	for (arg = 1; arg < argc; ++arg) {
138		if ((argv[arg][0] == '-') &&
139			((option = argv[arg][1]) != (char)0) &&
140			(argv[arg][2] == (char)0)) {
141			switch (option) {
142				case 'a':
143				case 'A':
144					/* Pick out the pathname argument: */
145					if (++arg >= argc) {
146						fprintf(stderr, usage, argv[0]);
147						exit(1);
148					}
149
150					check_uid();
151					result = AdoptVolume(argv[arg]);
152					break;
153
154				case 'c':
155				case 'C':
156					/* Pick out the pathname argument: */
157					if (++arg >= argc) {
158						fprintf(stderr, usage, argv[0]);
159						exit(1);
160					};
161
162					result = DisplayVolumeStatus(argv[arg]);
163					break;
164
165				case 'd':
166				case 'D':
167					/* Pick out the pathname argument: */
168					if (++arg >= argc) {
169						fprintf(stderr, usage, argv[0]);
170						exit(1);
171					};
172
173					check_uid();
174					result = DisownVolume(argv[arg]);
175					break;
176
177				case 'h':
178				case 'H':
179					printf(usage, argv[0]);
180					printf("where\n");
181					printf("\t-a adopts (activates) on-disk permissions on the specified path,\n");
182					printf("\t-c checks the status of the permissions usage on the specified path\n");
183					printf("\t-d disowns (deactivates) the on-disk permissions on the specified path\n");
184					printf("\t-i initializes the permissions database to include all mounted HFS/HFS+ volumes\n");
185					break;
186
187				case 'i':
188				case 'I':
189					check_uid();
190					result = AdoptAllLocalVolumes();
191					break;
192
193				case 'x':
194				case 'X':
195					/* Pick out the pathname argument: */
196					if (++arg >= argc) {
197						fprintf(stderr, usage, argv[0]);
198						exit(1);
199					};
200
201					check_uid();
202					result = ClearVolumeUUID(argv[arg]);
203					break;
204
205				default:
206					fprintf(stderr, usage, argv[0]);
207					exit(1);
208			}
209		}
210	}
211
212	if (result < 0) result = 1;		// Make sure only positive exit status codes are generated
213
214	exit(result);	   				// ensure the process exit status is returned
215	return result;      			// ...and make main fit the ANSI spec.
216}
217
218
219
220static void check_uid(void) {
221	if (geteuid() != 0) {
222		fprintf(stderr, "###\n");
223		fprintf(stderr, "### You must be root to perform this operation.\n");
224		fprintf(stderr, "###\n");
225		exit(1);
226	};
227}
228
229
230
231/*
232 --	UpdateMountStatus
233 --
234 --	Returns: error code (0 if successful).
235 */
236static int
237UpdateMountStatus(const char *path, u_int32_t volstatus) {
238	struct statfs mntstat;
239	int result;
240	union wait status;
241	int pid;
242	int nosuid;
243	int nodev;
244	int noexec;
245	int readonly;
246	int protect;
247	char mountline[255];
248
249	result = statfs(path, &mntstat);
250	if (result != 0) {
251		warn("couldn't look up mount status for '%s'", path);
252		return errno;
253	};
254
255	nosuid = mntstat.f_flags & MNT_NOSUID;
256	nodev = mntstat.f_flags & MNT_NODEV;
257	noexec = mntstat.f_flags & MNT_NOEXEC;
258	readonly = mntstat.f_flags & MNT_RDONLY;
259	protect = mntstat.f_flags & MNT_CPROTECT;
260
261	snprintf(mountline, sizeof(mountline), "%s%s%s%s%s%s", (volstatus & VOLUME_USEPERMISSIONS) ? "perm" : "noperm",(nosuid)?",nosuid":"",(nodev)?",nodev":"",(noexec)?",noexec":"",(readonly)?",rdonly":"", (protect)?",protect":"");
262
263	pid = fork();
264	if (pid == 0) {
265		result = execl("/sbin/mount", "mount",
266						"-t", mntstat.f_fstypename,
267						"-u","-o", mountline,
268						mntstat.f_mntfromname, mntstat.f_mntonname, NULL);
269#if DEBUG_TRACE
270		if (result) {
271			fprintf(stderr, "Error from execl(/sbin/mount...): result = %d.\n", result);
272		};
273		fprintf(stderr, "UpdateMountStatus in child: returning 1...\n");
274#endif
275		/* IF WE ARE HERE, WE WERE UNSUCCESFULL */
276		return (1);
277	}
278
279	if (pid == -1) {
280		warn("couldn't fork to execute mount command");
281		return errno;
282	};
283
284	/* Success! */
285	if ((wait4(pid, (int *)&status, 0, NULL) == pid) && (WIFEXITED(status))) {
286		result = status.w_retcode;
287	} else {
288		result = -1;
289	};
290
291	return result;
292}
293
294
295
296/*
297 --	AdoptAllLocalVolumes
298 --
299 --	Returns: error code (0 if successful).
300 */
301static int
302AdoptAllLocalVolumes(void) {
303	struct statfs *mntstatptr;
304	int fscount;
305
306	fscount = getmntinfo(&mntstatptr, MNT_WAIT);
307	if (fscount == 0) {
308		warn("couldn't get information on mounted volumes");
309		return errno;
310	};
311
312	while (fscount > 0) {
313		if (strcmp(mntstatptr->f_fstypename, gHFSTypeName) == 0) {
314			(void)AdoptVolume(mntstatptr->f_mntonname);
315		};
316
317		++mntstatptr;
318		--fscount;
319	};
320
321	return 0;
322}
323
324
325
326/*
327 --	AdoptVolume
328 --
329 --	Returns: error code (0 if successful).
330 */
331static int
332AdoptVolume(const char *path) {
333	VolumeUUID targetuuid;
334	VolumeStatusDBHandle vsdb;
335	u_int32_t volstatus;
336	int result = 0;
337
338	/* Look up the target volume UUID, generating one if no valid one has been assigned yet: */
339	result = GetVolumeUUID(path, &targetuuid, TRUE);
340	if (result != 0) {
341		warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
342		return result;
343	};
344
345	if ((targetuuid.v.high == 0) || (targetuuid.v.low == 0)) {
346		warnx("internal error: incomplete UUID generated.");
347		return EINVAL;
348	};
349
350	/* Open the volume status DB to look up the entry for the volume in question: */
351	if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
352		warnx("couldn't access volume status database: %s", strerror(result));
353		return result;
354	};
355
356	/* Check to see if an entry exists.  If not, prepare a default initial status value: */
357	if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
358		volstatus = 0;
359	};
360
361	/* Enable permissions on the specified volume: */
362	volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) | VOLUME_USEPERMISSIONS;
363
364	/* Update the entry in the volume status database: */
365	if ((result = SetVolumeStatusDBEntry(vsdb, &targetuuid, volstatus)) != 0) {
366		warnx("couldn't update volume status database: %s", strerror(result));
367		return result;
368	};
369
370	(void)CloseVolumeStatusDB(vsdb);
371
372	if ((result = UpdateMountStatus(path, volstatus)) != 0) {
373		warnx("couldn't update mount status of '%s': %s", path, strerror(result));
374		return result;
375	};
376
377	return 0;
378}
379
380
381
382/*
383 --	DisownVolume
384 --
385 --	Returns: error code (0 if successful).
386 */
387static int
388DisownVolume(const char *path) {
389	VolumeUUID targetuuid;
390	VolumeStatusDBHandle vsdb;
391	u_int32_t volstatus;
392	int result = 0;
393
394	/* Look up the target volume UUID, generating one if no valid one has been assigned yet: */
395	result = GetVolumeUUID(path, &targetuuid, TRUE);
396	if (result != 0) {
397		warnx("no valid volume UUID found on '%s': %s", path, strerror(result));
398		return result;
399	};
400
401	volstatus = 0;
402	if ((targetuuid.v.high != 0) || (targetuuid.v.low != 0)) {
403		/* Open the volume status DB to look up the entry for the volume in question: */
404		if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
405			warnx("couldn't access volume status database: %s", strerror(result));
406			return result;
407		};
408
409		/* Check to see if an entry exists.  If not, prepare a default initial status value: */
410		if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
411			volstatus = 0;
412		};
413
414		/* Disable permissions on the specified volume: */
415		volstatus = (volstatus & VOLUME_VALIDSTATUSBITS) & ~VOLUME_USEPERMISSIONS;
416
417		/* Update the entry in the volume status database: */
418		if ((result = SetVolumeStatusDBEntry(vsdb, &targetuuid, volstatus)) != 0) {
419			warnx("couldn't update volume status database: %s", strerror(result));
420			return result;
421		};
422
423		(void)CloseVolumeStatusDB(vsdb);
424
425	};
426
427	if ((result = UpdateMountStatus(path, volstatus)) != 0) {
428		warnx("couldn't update mount status of '%s': %s", path, strerror(result));
429		return result;
430	};
431
432	return result;
433};
434
435
436
437/*
438 --	ClearVolumeUUID
439 --
440 --	Returns: error code (0 if successful).
441 */
442static int
443ClearVolumeUUID(const char *path) {
444	VolumeUUID targetuuid;
445	VolumeStatusDBHandle vsdb;
446	u_int32_t volstatus;
447	int result = 0;
448
449	/* Check to see whether the target volume has an assigned UUID: */
450	result = GetVolumeUUID(path, &targetuuid, FALSE);
451	if (result != 0) {
452		warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
453		return result;
454	};
455
456	if ((targetuuid.v.high != 0) || (targetuuid.v.low != 0)) {
457		/* Open the volume status DB to look up the entry for the volume in question: */
458		if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
459			warnx("couldn't access volume status database: %s", strerror(result));
460			return result;
461		};
462
463		/* Check to see if an entry exists: */
464		if (GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus) == 0) {
465			/* Remove the entry from the volume status database: */
466			if ((result = DeleteVolumeStatusDBEntry(vsdb, &targetuuid)) != 0) {
467				warnx("couldn't update volume status database: %s", strerror(result));
468				return result;
469			};
470		};
471
472		(void)CloseVolumeStatusDB(vsdb);
473
474		if ((result = UpdateMountStatus(path, 0)) != 0) {
475			warnx("couldn't update mount status of '%s': %s", path, strerror(result));
476			return result;
477		};
478
479		/* Erase the volume's UUID: */
480		targetuuid.v.high = 0;
481		targetuuid.v.low = 0;
482		result = SetVolumeUUID(path, &targetuuid);
483
484	};
485
486	return result;
487};
488
489
490
491/*
492 --	DisplayVolumeStatus
493 --
494 --	Returns: error code (0 if successful).
495 */
496static int
497DisplayVolumeStatus(const char *path) {
498	VolumeUUID targetuuid;
499	VolumeStatusDBHandle vsdb;
500	u_int32_t volstatus;
501	int result = 0;
502
503	/* Look up the target volume UUID, exactly as stored on disk: */
504	result = GetVolumeUUID(path, &targetuuid, FALSE);
505	if (result != 0) {
506		warnx("couldn't read volume UUID on '%s': %s", path, strerror(result));
507		return result;
508	};
509
510	if ((targetuuid.v.high == 0) || (targetuuid.v.low == 0)) {
511		warnx("no valid volume UUID found on '%s': permissions are disabled.", path);
512		return 0;
513	};
514
515	/* Open the volume status DB to look up the entry for the volume in question: */
516	if ((result = OpenVolumeStatusDB(&vsdb)) != 0) {
517		warnx("couldn't access volume status database: %s", strerror(result));
518		return result;
519	};
520
521	if ((result = GetVolumeStatusDBEntry(vsdb, &targetuuid, &volstatus)) != 0) {
522		printf("No entry found for '%s'.\n", path);
523		goto Std_Exit;
524	};
525
526	if (volstatus & VOLUME_USEPERMISSIONS) {
527		printf("Permissions on '%s' are enabled.\n", path);
528	} else {
529		printf("Permissions on '%s' are disabled.\n", path);
530	};
531
532Std_Exit:
533	(void)CloseVolumeStatusDB(vsdb);
534
535	return result;
536}
537
538
539
540/*
541 --	GetVolumeUUID
542 --
543 --	Returns: error code (0 if successful).
544 */
545
546static int
547GetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr, boolean_t generate) {
548	struct attrlist alist;
549	struct FinderAttrBuf volFinderInfo;
550	VolumeUUID *finderInfoUUIDPtr;
551	int result;
552
553	/* Set up the attrlist structure to get the volume's Finder Info: */
554	alist.bitmapcount = 5;
555	alist.reserved = 0;
556	alist.commonattr = ATTR_CMN_FNDRINFO;
557	alist.volattr = ATTR_VOL_INFO;
558	alist.dirattr = 0;
559	alist.fileattr = 0;
560	alist.forkattr = 0;
561
562	result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
563	if (result) {
564		warn("Couldn't get volume information for '%s'", path);
565		result = errno;
566		goto Err_Exit;
567	}
568
569	finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
570	if (generate && ((finderInfoUUIDPtr->v.high == 0) || (finderInfoUUIDPtr->v.low == 0))) {
571		GenerateVolumeUUID(volumeUUIDPtr);
572		finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
573		finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
574		result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
575		if (result) {
576			warn("Couldn't update volume information for '%s'", path);
577			result = errno;
578			goto Err_Exit;
579		};
580	};
581
582	volumeUUIDPtr->v.high = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.high);
583	volumeUUIDPtr->v.low = OSSwapBigToHostInt32(finderInfoUUIDPtr->v.low);
584	result = 0;
585
586Err_Exit:
587	return result;
588};
589
590
591
592/*
593 --	SetVolumeUUID (needed only to update existing volume UUIDs)
594 --
595 --	Returns: error code (0 if successful).
596 */
597
598static int
599SetVolumeUUID(const char *path, VolumeUUID *volumeUUIDPtr) {
600	struct attrlist alist;
601	struct FinderAttrBuf volFinderInfo;
602	VolumeUUID *finderInfoUUIDPtr;
603	int result;
604
605	/* Set up the attrlist structure to get the volume's Finder Info: */
606	alist.bitmapcount = 5;
607	alist.reserved = 0;
608	alist.commonattr = ATTR_CMN_FNDRINFO;
609	alist.volattr = ATTR_VOL_INFO;
610	alist.dirattr = 0;
611	alist.fileattr = 0;
612	alist.forkattr = 0;
613
614	result = getattrlist(path, &alist, &volFinderInfo, sizeof(volFinderInfo), 0);
615	if (result) {
616		warn("Couldn't get volume information for '%s'", path);
617		result = errno;
618		goto Err_Exit;
619	}
620
621	finderInfoUUIDPtr = (VolumeUUID *)(&volFinderInfo.finderinfo[6]);
622	finderInfoUUIDPtr->v.high = OSSwapHostToBigInt32(volumeUUIDPtr->v.high);
623	finderInfoUUIDPtr->v.low = OSSwapHostToBigInt32(volumeUUIDPtr->v.low);
624
625	result = setattrlist(path, &alist, &volFinderInfo.finderinfo, sizeof(volFinderInfo.finderinfo), 0);
626	if (result != 0) {
627		warn("Couldn't set volume information for '%s'", path);
628		result = errno;
629		goto Err_Exit;
630	};
631
632Err_Exit:
633	return result;
634};
635
636
637
638
639
640/******************************************************************************
641 *
642 *  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
643 *
644 *****************************************************************************/
645
646#define DBHANDLESIGNATURE 0x75917737
647
648/* Flag values for operation options: */
649#define DBMARKPOSITION 1
650
651static char gVSDBPath[] = "/var/db/volinfo.database";
652
653#define MAXIOMALLOC 16384
654
655/* Database layout: */
656
657struct VSDBKey {
658	char uuid[16];
659};
660
661struct VSDBRecord {
662	char statusFlags[8];
663};
664
665struct VSDBEntry {
666	struct VSDBKey key;
667	char keySeparator;
668	char space;
669	struct VSDBRecord record;
670	char terminator;
671};
672
673#define DBKEYSEPARATOR ':'
674#define DBBLANKSPACE ' '
675#define DBRECORDTERMINATOR '\n'
676
677/* In-memory data structures: */
678
679struct VSDBState {
680	u_int32_t signature;
681	int dbfile;
682	int dbmode;
683	off_t recordPosition;
684};
685
686typedef struct VSDBState *VSDBStatePtr;
687
688
689
690/* Internal function prototypes: */
691static int LockDB(VSDBStatePtr dbstateptr, int lockmode);
692static int UnlockDB(VSDBStatePtr dbstateptr);
693
694static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *dbentry, u_int32_t options);
695static int AddVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
696static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
697static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry);
698static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2);
699
700static void FormatULong(u_int32_t u, char *s);
701static void FormatUUID(VolumeUUID *volumeID, char *UUIDField);
702static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey);
703static void FormatDBRecord(u_int32_t volumeStatusFlags, struct VSDBRecord *dbrecord);
704static void FormatDBEntry(VolumeUUID *volumeID, u_int32_t volumeStatusFlags, struct VSDBEntry *dbentry);
705static u_int32_t ConvertHexStringToULong(const char *hs, long maxdigits);
706
707
708
709/******************************************************************************
710 *
711 *  P U B L I S H E D   I N T E R F A C E   R O U T I N E S
712 *
713 *****************************************************************************/
714
715void GenerateVolumeUUID(VolumeUUID *newVolumeID) {
716	SHA_CTX context;
717	char randomInputBuffer[26];
718	unsigned char digest[20];
719	time_t now;
720	clock_t uptime;
721	int mib[2];
722	int sysdata;
723	char sysctlstring[128];
724	size_t datalen;
725	double  sysloadavg[3];
726	struct vmtotal sysvmtotal;
727
728	do {
729		/* Initialize the SHA-1 context for processing: */
730		SHA1_Init(&context);
731
732		/* Now process successive bits of "random" input to seed the process: */
733
734		/* The current system's uptime: */
735		uptime = clock();
736		SHA1_Update(&context, &uptime, sizeof(uptime));
737
738		/* The kernel's boot time: */
739		mib[0] = CTL_KERN;
740		mib[1] = KERN_BOOTTIME;
741		datalen = sizeof(sysdata);
742		sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
743		SHA1_Update(&context, &sysdata, datalen);
744
745		/* The system's host id: */
746		mib[0] = CTL_KERN;
747		mib[1] = KERN_HOSTID;
748		datalen = sizeof(sysdata);
749		sysctl(mib, 2, &sysdata, &datalen, NULL, 0);
750		SHA1_Update(&context, &sysdata, datalen);
751
752		/* The system's host name: */
753		mib[0] = CTL_KERN;
754		mib[1] = KERN_HOSTNAME;
755		datalen = sizeof(sysctlstring);
756		sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
757		SHA1_Update(&context, sysctlstring, datalen);
758
759		/* The running kernel's OS release string: */
760		mib[0] = CTL_KERN;
761		mib[1] = KERN_OSRELEASE;
762		datalen = sizeof(sysctlstring);
763		sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
764		SHA1_Update(&context, sysctlstring, datalen);
765
766		/* The running kernel's version string: */
767		mib[0] = CTL_KERN;
768		mib[1] = KERN_VERSION;
769		datalen = sizeof(sysctlstring);
770		sysctl(mib, 2, sysctlstring, &datalen, NULL, 0);
771		SHA1_Update(&context, sysctlstring, datalen);
772
773		/* The system's load average: */
774		datalen = sizeof(sysloadavg);
775		getloadavg(sysloadavg, 3);
776		SHA1_Update(&context, &sysloadavg, datalen);
777
778		/* The system's VM statistics: */
779		mib[0] = CTL_VM;
780		mib[1] = VM_METER;
781		datalen = sizeof(sysvmtotal);
782		sysctl(mib, 2, &sysvmtotal, &datalen, NULL, 0);
783		SHA1_Update(&context, &sysvmtotal, datalen);
784
785		/* The current GMT (26 ASCII characters): */
786		time(&now);
787		strncpy(randomInputBuffer, asctime(gmtime(&now)), 26);	/* "Mon Mar 27 13:46:26 2000" */
788		SHA1_Update(&context, randomInputBuffer, 26);
789
790		/* Pad the accumulated input and extract the final digest hash: */
791		SHA1_Final(digest, &context);
792
793		memcpy(newVolumeID, digest, sizeof(*newVolumeID));
794	} while ((newVolumeID->v.high == 0) || (newVolumeID->v.low == 0));
795}
796
797
798
799void ConvertVolumeUUIDStringToUUID(const char *UUIDString, VolumeUUID *volumeID) {
800	int i;
801	char c;
802	u_int32_t nextdigit;
803	u_int32_t high = 0;
804	u_int32_t low = 0;
805	u_int32_t carry;
806
807	for (i = 0; (i < VOLUMEUUIDLENGTH) && ((c = UUIDString[i]) != (char)0) ; ++i) {
808		if ((c >= '0') && (c <= '9')) {
809			nextdigit = c - '0';
810		} else if ((c >= 'A') && (c <= 'F')) {
811			nextdigit = c - 'A' + 10;
812		} else if ((c >= 'a') && (c <= 'f')) {
813			nextdigit = c - 'a' + 10;
814		} else {
815			nextdigit = 0;
816		};
817		carry = ((low & 0xF0000000) >> 28) & 0x0000000F;
818		high = (high << 4) | carry;
819		low = (low << 4) | nextdigit;
820	};
821
822	volumeID->v.high = high;
823	volumeID->v.low = low;
824}
825
826
827
828void ConvertVolumeUUIDToString(VolumeUUID *volumeID, char *UUIDString) {
829	FormatUUID(volumeID, UUIDString);
830	*(UUIDString+16) = (char)0;		/* Append a terminating null character */
831}
832
833
834
835int OpenVolumeStatusDB(VolumeStatusDBHandle *DBHandlePtr) {
836	VSDBStatePtr dbstateptr;
837
838	*DBHandlePtr = NULL;
839
840	dbstateptr = (VSDBStatePtr)malloc(sizeof(*dbstateptr));
841	if (dbstateptr == NULL) {
842		return ENOMEM;
843	};
844
845	dbstateptr->dbmode = O_RDWR;
846	dbstateptr->dbfile = open(gVSDBPath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
847	if (dbstateptr->dbfile == -1) {
848		/*
849		   The file couldn't be opened for read/write access:
850		   try read-only access before giving up altogether.
851		 */
852		dbstateptr->dbmode = O_RDONLY;
853		dbstateptr->dbfile = open(gVSDBPath, O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
854		if (dbstateptr->dbfile == -1) {
855			return errno;
856		};
857	};
858
859	dbstateptr->signature = DBHANDLESIGNATURE;
860	*DBHandlePtr = (VolumeStatusDBHandle)dbstateptr;
861	return 0;
862}
863
864
865
866int GetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, u_int32_t *VolumeStatus) {
867	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
868	struct VSDBEntry dbentry;
869	int result;
870
871	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
872
873	if ((result = LockDB(dbstateptr, LOCK_SH)) != 0) return result;
874
875	if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, &dbentry, 0)) != 0) {
876		goto ErrExit;
877	};
878	*VolumeStatus = VOLUME_RECORDED | ConvertHexStringToULong(dbentry.record.statusFlags, sizeof(dbentry.record.statusFlags));
879
880	result = 0;
881
882ErrExit:
883	UnlockDB(dbstateptr);
884	return result;
885}
886
887
888
889int SetVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID, u_int32_t VolumeStatus) {
890	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
891	struct VSDBEntry dbentry;
892	int result;
893
894	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
895	if (VolumeStatus & ~VOLUME_VALIDSTATUSBITS) return EINVAL;
896
897	if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
898
899	FormatDBEntry(volumeID, VolumeStatus, &dbentry);
900	if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) == 0) {
901#if DEBUG_TRACE
902		fprintf(stderr,"AddLocalVolumeUUID: found record in database; updating in place.\n");
903#endif
904		result = UpdateVolumeRecord(dbstateptr, &dbentry);
905	} else if (result == -1) {
906#if DEBUG_TRACE
907		fprintf(stderr,"AddLocalVolumeUUID: record not found in database; appending at end.\n");
908#endif
909		result = AddVolumeRecord(dbstateptr, &dbentry);
910	} else {
911		goto ErrExit;
912	};
913
914	fsync(dbstateptr->dbfile);
915
916	result = 0;
917
918ErrExit:
919	UnlockDB(dbstateptr);
920	return result;
921}
922
923
924
925int DeleteVolumeStatusDBEntry(VolumeStatusDBHandle DBHandle, VolumeUUID *volumeID) {
926	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
927	struct stat dbinfo;
928	int result;
929	u_int32_t iobuffersize;
930	void *iobuffer = NULL;
931	off_t dataoffset;
932	u_int32_t iotransfersize;
933	u_int32_t bytestransferred;
934
935	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
936
937	if ((result = LockDB(dbstateptr, LOCK_EX)) != 0) return result;
938
939	if ((result = FindVolumeRecordByUUID(dbstateptr, volumeID, NULL, DBMARKPOSITION)) != 0) {
940#if DEBUG_TRACE
941		fprintf(stderr, "DeleteLocalVolumeUUID: No record with matching volume UUID in DB (result = %d).\n", result);
942#endif
943		if (result == -1) result = 0;	/* Entry wasn't in the database to begin with? */
944		goto StdEdit;
945	} else {
946#if DEBUG_TRACE
947		fprintf(stderr, "DeleteLocalVolumeUUID: Found record with matching volume UUID...\n");
948#endif
949		if ((result = stat(gVSDBPath, &dbinfo)) != 0) goto ErrExit;
950		if ((dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry)) <= MAXIOMALLOC) {
951			iobuffersize = dbinfo.st_size - dbstateptr->recordPosition - sizeof(struct VSDBEntry);
952		} else {
953			iobuffersize = MAXIOMALLOC;
954		};
955#if DEBUG_TRACE
956		fprintf(stderr, "DeleteLocalVolumeUUID: DB size = 0x%08lx; recordPosition = 0x%08lx;\n",
957							(u_int32_t)dbinfo.st_size, (u_int32_t)dbstateptr->recordPosition);
958		fprintf(stderr, "DeleteLocalVolumeUUID: I/O buffer size = 0x%lx\n", iobuffersize);
959#endif
960		if (iobuffersize > 0) {
961			iobuffer = malloc(iobuffersize);
962			if (iobuffer == NULL) {
963				result = ENOMEM;
964				goto ErrExit;
965			};
966
967			dataoffset = dbstateptr->recordPosition + sizeof(struct VSDBEntry);
968			do {
969				iotransfersize = dbinfo.st_size - dataoffset;
970				if (iotransfersize > 0) {
971					if (iotransfersize > iobuffersize) iotransfersize = iobuffersize;
972
973#if DEBUG_TRACE
974					fprintf(stderr, "DeleteLocalVolumeUUID: reading 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (u_int32_t)dataoffset);
975#endif
976					lseek(dbstateptr->dbfile, dataoffset, SEEK_SET);
977					bytestransferred = read(dbstateptr->dbfile, iobuffer, iotransfersize);
978					if (bytestransferred != iotransfersize) {
979						result = errno;
980						goto ErrExit;
981					};
982
983#if DEBUG_TRACE
984					fprintf(stderr, "DeleteLocalVolumeUUID: writing 0x%08lx bytes starting at 0x%08lx ...\n", iotransfersize, (u_int32_t)(dataoffset - (off_t)sizeof(struct VSDBEntry)));
985#endif
986					lseek(dbstateptr->dbfile, dataoffset - (off_t)sizeof(struct VSDBEntry), SEEK_SET);
987					bytestransferred = write(dbstateptr->dbfile, iobuffer, iotransfersize);
988					if (bytestransferred != iotransfersize) {
989						result = errno;
990						goto ErrExit;
991					};
992
993					dataoffset += (off_t)iotransfersize;
994				};
995			} while (iotransfersize > 0);
996		};
997#if DEBUG_TRACE
998		fprintf(stderr, "DeleteLocalVolumeUUID: truncating database file to 0x%08lx bytes.\n", (u_int32_t)(dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry))));
999#endif
1000		if ((result = ftruncate(dbstateptr->dbfile, dbinfo.st_size - (off_t)(sizeof(struct VSDBEntry)))) != 0) {
1001			goto ErrExit;
1002		};
1003
1004		fsync(dbstateptr->dbfile);
1005
1006		result = 0;
1007	};
1008
1009ErrExit:
1010	if (iobuffer) free(iobuffer);
1011	UnlockDB(dbstateptr);
1012
1013StdEdit:
1014	return result;
1015}
1016
1017
1018
1019int CloseVolumeStatusDB(VolumeStatusDBHandle DBHandle) {
1020	VSDBStatePtr dbstateptr = (VSDBStatePtr)DBHandle;
1021
1022	if (dbstateptr->signature != DBHANDLESIGNATURE) return EINVAL;
1023
1024	dbstateptr->signature = 0;
1025
1026	close(dbstateptr->dbfile);		/* Nothing we can do about any errors... */
1027	dbstateptr->dbfile = 0;
1028
1029	free(dbstateptr);
1030
1031	return 0;
1032}
1033
1034
1035
1036/******************************************************************************
1037 *
1038 *  I N T E R N A L   O N L Y   D A T A B A S E   R O U T I N E S
1039 *
1040 *****************************************************************************/
1041
1042static int LockDB(VSDBStatePtr dbstateptr, int lockmode) {
1043#if DEBUG_TRACE
1044	fprintf(stderr, "LockDB: Locking VSDB file...\n");
1045#endif
1046	return flock(dbstateptr->dbfile, lockmode);
1047}
1048
1049
1050
1051static int UnlockDB(VSDBStatePtr dbstateptr) {
1052#if DEBUG_TRACE
1053	fprintf(stderr, "UnlockDB: Unlocking VSDB file...\n");
1054#endif
1055	return flock(dbstateptr->dbfile, LOCK_UN);
1056}
1057
1058
1059
1060static int FindVolumeRecordByUUID(VSDBStatePtr dbstateptr, VolumeUUID *volumeID, struct VSDBEntry *targetEntry, u_int32_t options) {
1061	struct VSDBKey searchkey;
1062	struct VSDBEntry dbentry;
1063	int result;
1064
1065	FormatDBKey(volumeID, &searchkey);
1066	lseek(dbstateptr->dbfile, 0, SEEK_SET);
1067
1068	do {
1069		result = GetVSDBEntry(dbstateptr, &dbentry);
1070		if ((result == 0) && (CompareVSDBKeys(&dbentry.key, &searchkey) == 0)) {
1071			if (targetEntry != NULL) {
1072#if DEBUG_TRACE
1073				fprintf(stderr, "FindVolumeRecordByUUID: copying %d. bytes from %08xl to %08l...\n", sizeof(*targetEntry), &dbentry, targetEntry);
1074#endif
1075				memcpy(targetEntry, &dbentry, sizeof(*targetEntry));
1076			};
1077			return 0;
1078		};
1079	} while (result == 0);
1080
1081	return -1;
1082}
1083
1084
1085
1086static int AddVolumeRecord(VSDBStatePtr dbstateptr , struct VSDBEntry *dbentry) {
1087#if DEBUG_TRACE
1088	VolumeUUIDString id;
1089#endif
1090
1091#if DEBUG_TRACE
1092	strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
1093	id[sizeof(dbentry->key.uuid)] = (char)0;
1094	fprintf(stderr, "AddVolumeRecord: Adding record for UUID #%s...\n", id);
1095#endif
1096	lseek(dbstateptr->dbfile, 0, SEEK_END);
1097	return write(dbstateptr->dbfile, dbentry, sizeof(struct VSDBEntry));
1098}
1099
1100
1101
1102
1103static int UpdateVolumeRecord(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
1104#if DEBUG_TRACE
1105	VolumeUUIDString id;
1106#endif
1107
1108#if DEBUG_TRACE
1109	strncpy(id, dbentry->key.uuid, sizeof(dbentry->key.uuid));
1110	id[sizeof(dbentry->key.uuid)] = (char)0;
1111	fprintf(stderr, "UpdateVolumeRecord: Updating record for UUID #%s at offset 0x%08lx in database...\n", id, (u_int32_t)dbstateptr->recordPosition);
1112#endif
1113	lseek(dbstateptr->dbfile, dbstateptr->recordPosition, SEEK_SET);
1114#if DEBUG_TRACE
1115	fprintf(stderr, "UpdateVolumeRecord: Writing %d. bytes...\n", sizeof(*dbentry));
1116#endif
1117	return write(dbstateptr->dbfile, dbentry, sizeof(*dbentry));
1118}
1119
1120
1121
1122static int GetVSDBEntry(VSDBStatePtr dbstateptr, struct VSDBEntry *dbentry) {
1123	struct VSDBEntry entry;
1124	int result;
1125#if DEBUG_TRACE
1126	VolumeUUIDString id;
1127#endif
1128
1129	dbstateptr->recordPosition = lseek(dbstateptr->dbfile, 0, SEEK_CUR);
1130#if 0 // DEBUG_TRACE
1131	fprintf(stderr, "GetVSDBEntry: starting reading record at offset 0x%08lx...\n", (u_int32_t)dbstateptr->recordPosition);
1132#endif
1133	result = read(dbstateptr->dbfile, &entry, sizeof(entry));
1134	if ((result != sizeof(entry)) ||
1135		(entry.keySeparator != DBKEYSEPARATOR) ||
1136		(entry.space != DBBLANKSPACE) ||
1137		(entry.terminator != DBRECORDTERMINATOR)) {
1138		return -1;
1139	};
1140
1141#if DEBUG_TRACE
1142	strncpy(id, entry.key.uuid, sizeof(entry.key.uuid));
1143	id[sizeof(entry.key.uuid)] = (char)0;
1144	fprintf(stderr, "GetVSDBEntry: returning entry for UUID #%s...\n", id);
1145#endif
1146	memcpy(dbentry, &entry, sizeof(*dbentry));
1147	return 0;
1148};
1149
1150
1151
1152static int CompareVSDBKeys(struct VSDBKey *key1, struct VSDBKey *key2) {
1153#if 0 // DEBUG_TRACE
1154	VolumeUUIDString id;
1155
1156	strncpy(id, key1->uuid, sizeof(key1->uuid));
1157	id[sizeof(key1->uuid)] = (char)0;
1158	fprintf(stderr, "CompareVSDBKeys: comparing #%s to ", id);
1159	strncpy(id, key2->uuid, sizeof(key2->uuid));
1160	id[sizeof(key2->uuid)] = (char)0;
1161	fprintf(stderr, "%s (%d.)...\n", id, sizeof(key1->uuid));
1162#endif
1163
1164	return memcmp(key1->uuid, key2->uuid, sizeof(key1->uuid));
1165}
1166
1167
1168
1169/******************************************************************************
1170 *
1171 *  F O R M A T T I N G   A N D   C O N V E R S I O N   R O U T I N E S
1172 *
1173 *****************************************************************************/
1174
1175static void FormatULong(u_int32_t u, char *s) {
1176	u_int32_t d;
1177	int i;
1178	char *digitptr = s;
1179
1180	for (i = 0; i < 8; ++i) {
1181		d = ((u & 0xF0000000) >> 28) & 0x0000000F;
1182		if (d < 10) {
1183			*digitptr++ = (char)(d + '0');
1184		} else {
1185			*digitptr++ = (char)(d - 10 + 'A');
1186		};
1187		u = u << 4;
1188	};
1189}
1190
1191
1192
1193static void FormatUUID(VolumeUUID *volumeID, char *UUIDField) {
1194	FormatULong(volumeID->v.high, UUIDField);
1195	FormatULong(volumeID->v.low, UUIDField+8);
1196
1197};
1198
1199
1200
1201static void FormatDBKey(VolumeUUID *volumeID, struct VSDBKey *dbkey) {
1202	FormatUUID(volumeID, dbkey->uuid);
1203}
1204
1205
1206
1207static void FormatDBRecord(u_int32_t volumeStatusFlags, struct VSDBRecord *dbrecord) {
1208	FormatULong(volumeStatusFlags, dbrecord->statusFlags);
1209}
1210
1211
1212
1213static void FormatDBEntry(VolumeUUID *volumeID, u_int32_t volumeStatusFlags, struct VSDBEntry *dbentry) {
1214	FormatDBKey(volumeID, &dbentry->key);
1215	dbentry->keySeparator = DBKEYSEPARATOR;
1216	dbentry->space = DBBLANKSPACE;
1217	FormatDBRecord(volumeStatusFlags, &dbentry->record);
1218#if 0 // DEBUG_TRACE
1219	dbentry->terminator = (char)0;
1220	fprintf(stderr, "FormatDBEntry: '%s' (%d.)\n", dbentry, sizeof(*dbentry));
1221#endif
1222	dbentry->terminator = DBRECORDTERMINATOR;
1223}
1224
1225
1226
1227static u_int32_t ConvertHexStringToULong(const char *hs, long maxdigits) {
1228	int i;
1229	char c;
1230	u_int32_t nextdigit;
1231	u_int32_t n;
1232
1233	n = 0;
1234	for (i = 0; (i < 8) && ((c = hs[i]) != (char)0) ; ++i) {
1235		if ((c >= '0') && (c <= '9')) {
1236			nextdigit = c - '0';
1237		} else if ((c >= 'A') && (c <= 'F')) {
1238			nextdigit = c - 'A' + 10;
1239		} else if ((c >= 'a') && (c <= 'f')) {
1240			nextdigit = c - 'a' + 10;
1241		} else {
1242			nextdigit = 0;
1243		};
1244		n = (n << 4) + nextdigit;
1245	};
1246
1247	return n;
1248}
1249