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