1/* 2 * Copyright (c) 2006-2012 Apple Inc. All rights reserved. 3 * 4 * 5 * @APPLE_LICENSE_HEADER_START@ 6 * 7 * This file contains Original Code and/or Modifications of Original Code 8 * as defined in and that are subject to the Apple Public Source License 9 * Version 2.0 (the 'License'). You may not use this file except in 10 * compliance with the License. Please obtain a copy of the License at 11 * http://www.opensource.apple.com/apsl/ and read it before using this 12 * file. 13 * 14 * The Original Code and all software distributed under the License are 15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 19 * Please see the License for the specific language governing rights and 20 * limitations under the License. 21 * 22 * @APPLE_LICENSE_HEADER_END@ 23 */ 24/* 25 * FILE: bootcaches.c 26 * AUTH: Soren Spies (sspies) 27 * DATE: "spring" 2006 28 * DESC: routines for bootcache data 29 * 30 */ 31 32#include <bless.h> 33#include <bootfiles.h> 34#include <IOKit/IOKitLib.h> 35 36#include <fcntl.h> 37#include <libgen.h> 38#include <notify.h> 39#include <paths.h> 40#include <mach/mach.h> 41#include <mach/kmod.h> 42#include <sys/attr.h> 43#include <sys/stat.h> 44#include <sys/sysctl.h> 45#include <sys/time.h> 46#include <sys/types.h> 47#include <unistd.h> 48 49#include <EFILogin/EFILogin.h> 50#include <System/libkern/mkext.h> 51#include <System/libkern/OSKextLibPrivate.h> 52#include <DiskArbitration/DiskArbitration.h> // for UUID fetching 53#include <IOKit/kext/fat_util.h> 54#include <IOKit/kext/macho_util.h> 55#include <IOKit/storage/CoreStorage/CoreStorageUserLib.h> 56#include <IOKit/storage/CoreStorage/CoreStorageCryptoIDs.h> 57#include <IOKit/storage/CoreStorage/CSFullDiskEncryption.h> 58 59// Kext Management pieces from IOKitUser 60#include <IOKit/kext/OSKext.h> 61#include <IOKit/kext/OSKextPrivate.h> 62 63#include "bootcaches.h" // includes CF 64#include "bootroot_internal.h" // kBRUpdateOpts_t 65#include "fork_program.h" 66#include "kext_tools_util.h" 67#include "safecalls.h" 68 69// only used here 70#define kBRDiskArbMaxRetries (10) 71 72static void removeTrailingSlashes(char * path); 73 74// XX These are used often together, rely on 'finish', and should be all-caps. 75// 'dst must be char[PATH_MAX]' 76// COMPILE_TIME_ASSERT fails for get_locres_info() due to char[size] ~ char* 77#define pathcpy(dst, src) do { \ 78 /* COMPILE_TIME_ASSERT(sizeof(dst) == PATH_MAX); */ \ 79 if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \ 80 } while(0) 81#define pathcat(dst, src) do { \ 82 /* COMPILE_TIME_ASSERT(sizeof(dst) == PATH_MAX); */ \ 83 if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \ 84 } while(0) 85 86 87// http://lists.freebsd.org/pipermail/freebsd-hackers/2004-February/005627.html 88#define LOGERRxlate(ctx1, ctx2, errval) do { \ 89 char *c2cpy = ctx2, ctx[256]; \ 90 if (ctx2 != NULL) { \ 91 snprintf(ctx, sizeof(ctx), "%s: %s", ctx1, c2cpy); \ 92 } else { \ 93 snprintf(ctx, sizeof(ctx), "%s", ctx1); \ 94 } \ 95 /* if necessary, modify passed-in argument so errno is returned */ \ 96 if (errval == -1) errval = errno; \ 97 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, \ 98 "%s: %s", ctx, strerror(errval)); \ 99 } while(0) 100 101/****************************************************************************** 102* destroyCaches cleans up a bootCaches structure 103******************************************************************************/ 104void destroyCaches(struct bootCaches *caches) 105{ 106 if (caches) { 107 if (caches->cachefd != -1) close(caches->cachefd); 108 if (caches->cacheinfo) CFRelease(caches->cacheinfo); 109 if (caches->miscpaths) free(caches->miscpaths); // free strings 110 if (caches->rpspaths) free(caches->rpspaths); 111 if (caches->exts) free(caches->exts); 112 if (caches->csfde_uuid) CFRelease(caches->csfde_uuid); 113 free(caches); 114 } 115} 116 117/****************************************************************************** 118* readCaches checks for and reads bootcaches.plist 119* it will also create directories for the caches in question if needed 120******************************************************************************/ 121// used for turning /foo/bar into :foo:bar for kTSCacheDir entries (see awk(1)) 122static void gsub(char old, char new, char *s) 123{ 124 char *p; 125 126 while((p = s++) && *p) 127 if (*p == old) 128 *p = new; 129} 130 131static int 132MAKE_CACHEDPATH(cachedPath *cpath, struct bootCaches *caches, 133 CFStringRef relstr) 134{ 135 int rval; 136 size_t fullLen; 137 char tsname[NAME_MAX]; 138 139 // check params 140 rval = EINVAL; 141 if (!(relstr)) goto finish; 142 if (CFGetTypeID(relstr) != CFStringGetTypeID()) goto finish; 143 144 // extract rpath 145 rval = EOVERFLOW; 146 if (!CFStringGetFileSystemRepresentation(relstr, cpath->rpath, 147 sizeof(cpath->rpath))) { 148 goto finish; 149 } 150 151 // tspath: rpath with '/' -> ':' 152 if (strlcpy(tsname, cpath->rpath, sizeof(tsname)) >= sizeof(tsname)) 153 goto finish; 154 gsub('/', ':', tsname); 155 fullLen = snprintf(cpath->tspath, sizeof(cpath->tspath), "%s/%s/%s", 156 kTSCacheDir, caches->fsys_uuid, tsname); 157 if (fullLen >= sizeof(cpath->tspath)) 158 goto finish; 159 160 rval = 0; 161 162finish: 163 return rval; 164} 165 166// validate bootcaches.plist dict; data -> struct 167// caller properly frees the structure if we fail 168static int 169extractProps(struct bootCaches *caches, CFDictionaryRef bcDict) 170{ 171 int rval = ENODEV; 172 CFDictionaryRef dict; // don't release 173 CFIndex keyCount; // track whether we've handled all keys 174 CFIndex rpsindex = 0; // index into rps; compared to caches->nrps @ end 175 CFStringRef str; // used to point to objects owned by others 176 CFStringRef createdStr = NULL; 177 178 rval = EFTYPE; 179 keyCount = CFDictionaryGetCount(bcDict); // start with the top 180 caches->exts = NULL; 181 caches->nexts = 0; 182 183 // process keys for paths read "before the booter" 184 dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPreBootKey); 185 if (dict) { 186 CFArrayRef apaths; 187 CFIndex miscindex = 0; 188 189 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish; 190 // only "Additional Paths" can contain > 1 path 191 caches->nmisc = (int)CFDictionaryGetCount(dict); // init w/1 path/key 192 keyCount += CFDictionaryGetCount(dict); 193 194 // look at variable-sized member first -> right size for miscpaths 195 apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey); 196 if (apaths) { 197 CFIndex acount; 198 199 if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish; 200 acount = CFArrayGetCount(apaths); 201 // total "misc" paths = # of keyed paths + # additional paths 202 caches->nmisc += acount - 1; // kBCAdditionalPathsKey not a path 203 204 if ((unsigned int)caches->nmisc > INT_MAX/sizeof(*caches->miscpaths)) goto finish; 205 caches->miscpaths = (cachedPath*)calloc(caches->nmisc, 206 sizeof(*caches->miscpaths)); 207 if (!caches->miscpaths) goto finish; 208 209 for (/*miscindex = 0 (above)*/; miscindex < acount; miscindex++) { 210 str = CFArrayGetValueAtIndex(apaths, miscindex); 211 // MAKE_CACHEDPATH checks str != NULL && str's type 212 MAKE_CACHEDPATH(&caches->miscpaths[miscindex], caches, str); 213 } 214 keyCount--; // AdditionalPaths sub-key 215 } else { 216 // allocate enough for the top-level keys (nothing variable-sized) 217 if ((unsigned int)caches->nmisc > INT_MAX/sizeof(*caches->miscpaths)) goto finish; 218 caches->miscpaths = calloc(caches->nmisc, sizeof(cachedPath)); 219 if (!caches->miscpaths) goto finish; 220 } 221 222 str = (CFStringRef)CFDictionaryGetValue(dict, kBCLabelKey); 223 if (str) { 224 MAKE_CACHEDPATH(&caches->miscpaths[miscindex], caches, str); 225 caches->label = &caches->miscpaths[miscindex]; 226 227 miscindex++; // get ready for the next guy 228#pragma unused(miscindex) 229 keyCount--; // DiskLabel is dealt with 230 } 231 232 // add new keys here 233 keyCount--; // preboot dict 234 } 235 236 237 // process booter keys 238 dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCBootersKey); 239 if (dict) { 240 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish; 241 keyCount += CFDictionaryGetCount(dict); 242 243 str = (CFStringRef)CFDictionaryGetValue(dict, kBCEFIBooterKey); 244 if (str) { 245 MAKE_CACHEDPATH(&caches->efibooter, caches, str); 246 247 keyCount--; // EFIBooter is dealt with 248 } 249 250 str = (CFStringRef)CFDictionaryGetValue(dict, kBCOFBooterKey); 251 if (str) { 252 MAKE_CACHEDPATH(&caches->ofbooter, caches, str); 253 254 keyCount--; // BootX, check 255 } 256 257 // add new booters here 258 keyCount--; // booters dict 259 } 260 261 // process keys for paths read "after the booter [is loaded]" 262 // these are read by the booter proper, which determines which 263 // of the Rock, Paper, Scissors directories is most current 264 dict = (CFDictionaryRef)CFDictionaryGetValue(bcDict, kBCPostBootKey); 265 if (dict) { 266 CFDictionaryRef mkDict, erDict; 267 CFArrayRef apaths; 268 CFIndex acount; 269 Boolean isKernelcache = false; 270 int kcacheKeys = 0; 271 272 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) goto finish; 273 274 // we must deal with all sub-keys 275 keyCount += CFDictionaryGetCount(dict); 276 277 // Figure out how many files will be watched/copied via rpspaths 278 // start by assuming each key provides one path to watch/copy 279 caches->nrps = (int)CFDictionaryGetCount(dict); 280 281 // AdditionalPaths: 1 key -> extra RPS entries 282 apaths = (CFArrayRef)CFDictionaryGetValue(dict, kBCAdditionalPathsKey); 283 if (apaths) { 284 if (CFArrayGetTypeID() != CFGetTypeID(apaths)) goto finish; 285 acount = CFArrayGetCount(apaths); 286 287 // add in extra keys 288 // "additional paths" array is not itself a path -> add one less 289 caches->nrps += (acount - 1); 290 } 291 292 293 // EncryptedRoot has 5 subkeys 294 erDict=(CFDictionaryRef)CFDictionaryGetValue(dict,kBCEncryptedRootKey); 295 if (erDict) { 296 if (CFGetTypeID(erDict)!=CFDictionaryGetTypeID()) goto finish; 297 // erDict has one slot, but two required & copied sub-properties 298 caches->nrps++; 299 300 // the other three keys lead to a single localized resources cache 301 if (CFDictionaryGetValue(erDict,kBCCSFDELocalizationSrcKey)) { 302 caches->nrps++; 303 } 304 } 305 306 // finally allocate correctly-sized rpspaths 307 if ((unsigned int)caches->nrps > INT_MAX/sizeof(*caches->rpspaths)) 308 goto finish; 309 caches->rpspaths = (cachedPath*)calloc(caches->nrps, 310 sizeof(*caches->rpspaths)); 311 if (!caches->rpspaths) goto finish; 312 313 314 // Load up rpspaths 315 316 // populate rpspaths with AdditionalPaths; leave rpsindex -> avail 317 // (above: apaths type-checked, rpsindex initialized to zero) 318 if (apaths) { 319 for (; rpsindex < acount; rpsindex++) { 320 str = CFArrayGetValueAtIndex(apaths, rpsindex); 321 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 322 } 323 keyCount--; // handled AdditionalPaths 324 } 325 326 // com.apple.Boot.plist 327 str = (CFStringRef)CFDictionaryGetValue(dict, kBCBootConfigKey); 328 if (str) { 329 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 330 caches->bootconfig = &caches->rpspaths[rpsindex++]; 331 keyCount--; // handled BootConfig 332 } 333 334 // EncryptedRoot items 335 // two sub-keys required; one optional; one set of three optional 336 if (erDict) { 337 CFNumberRef boolRef; 338 keyCount += CFDictionaryGetCount(erDict); 339 340 str = CFDictionaryGetValue(erDict, kBCCSFDEPropertyCacheKey); 341 if (str) { 342 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 343 caches->erpropcache = &caches->rpspaths[rpsindex++]; 344 keyCount--; 345 } 346 347 // !RootVolumePropertyCache => enable "timestamp only" optimization 348 boolRef = CFDictionaryGetValue(erDict,kBCCSFDERootVolPropCacheKey); 349 if (boolRef) { 350 if (CFGetTypeID(boolRef) == CFBooleanGetTypeID()) { 351 caches->erpropTSOnly = CFEqual(boolRef, kCFBooleanFalse); 352 keyCount--; 353 } else { 354 goto finish; 355 } 356 } 357 358 // 8163405: non-localized resources 359 str = CFDictionaryGetValue(erDict, kBCCSFDEDefResourcesDirKey); 360 // XX check old key name for now 361 if (!str) str=CFDictionaryGetValue(erDict,CFSTR("ResourcesDir")); 362 if (str) { 363 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 364 caches->efidefrsrcs = &caches->rpspaths[rpsindex++]; 365 keyCount--; 366 } 367 368 // localized resource cache 369 str = CFDictionaryGetValue(erDict,kBCCSFDELocRsrcsCacheKey); 370 if (str) { 371 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); 372 caches->efiloccache = &caches->rpspaths[rpsindex++]; 373 keyCount--; 374 375 // localization source material (required) 376 str = CFDictionaryGetValue(erDict, kBCCSFDELocalizationSrcKey); 377 if (str && CFGetTypeID(str) == CFStringGetTypeID() && 378 CFStringGetFileSystemRepresentation(str, 379 caches->locSource, sizeof(caches->locSource))) { 380 keyCount--; 381 } else { 382 goto finish; 383 } 384 385 // localization prefs file (required) 386 str = CFDictionaryGetValue(erDict, kBCCSFDELanguagesPrefKey); 387 if (str && CFGetTypeID(str) == CFStringGetTypeID() && 388 CFStringGetFileSystemRepresentation(str, caches->locPref, 389 sizeof(caches->locPref))) { 390 keyCount--; 391 } else { 392 goto finish; 393 } 394 } 395 396 keyCount--; // handled EncryptedRoot 397 } 398 399 // we support any one of three kext archival methods 400 kcacheKeys = 0; 401 if (CFDictionaryContainsKey(dict, kBCMKextKey)) kcacheKeys++; 402 if (CFDictionaryContainsKey(dict, kBCMKext2Key)) kcacheKeys++; 403 if (CFDictionaryContainsKey(dict, kBCKernelcacheV1Key)) kcacheKeys++; 404 if (CFDictionaryContainsKey(dict, kBCKernelcacheV2Key)) kcacheKeys++; 405 if (CFDictionaryContainsKey(dict, kBCKernelcacheV3Key)) kcacheKeys++; 406 407 if (kcacheKeys > 1) { 408 // don't support multiple types of kernel caching ... 409 goto finish; 410 } 411 412 /* Handle the "Kernelcache" key for prelinked kernels for Lion and 413 * later, the "MKext2 key" for format-2 mkext on Snow Leopard, and the 414 * original "MKext" key for format-1 mkexts prior to SnowLeopard. 415 */ 416 do { 417 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV1Key); 418 if (!mkDict) { 419 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV2Key); 420 } 421 if (!mkDict) { 422 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCKernelcacheV3Key); 423 } 424 425 if (mkDict) { 426 isKernelcache = true; 427 break; 428 } 429 430 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKext2Key); 431 if (mkDict) break; 432 433 mkDict = (CFDictionaryRef)CFDictionaryGetValue(dict, kBCMKextKey); 434 if (mkDict) break; 435 } while (0); 436 437 if (mkDict) { 438 if (CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) { 439 goto finish; 440 } 441 442 // path to the cache itself 443 // currently /System/Library/Caches/com.apple.kext.caches/Startup/kernelcache 444 str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCPathKey); 445 MAKE_CACHEDPATH(&caches->rpspaths[rpsindex], caches, str); // M 446 caches->kext_boot_cache_file = &caches->rpspaths[rpsindex++]; 447#pragma unused(rpsindex) 448 449 // Starting with Kernelcache v1.3 kBCExtensionsDirKey is a key for 450 // an array of paths to extensions directory. Pre v1.3 it is just 451 // a string equal to "/System/Library/Extensions" 452 size_t bufsize = 0; 453 apaths = (CFArrayRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey); 454 if (apaths && CFArrayGetTypeID() == CFGetTypeID(apaths)) { 455 int i; 456 char *bufptr; 457 char tempbuf[PATH_MAX]; 458 459 caches->nexts = (int) CFArrayGetCount(apaths); 460 if (caches->nexts == 0) goto finish; 461 462 caches->exts = malloc(caches->nexts * PATH_MAX); 463 if (caches->exts == NULL) { 464 OSKextLogMemError(); 465 goto finish; 466 } 467 bufptr = caches->exts; 468 469 for (i = 0; i < caches->nexts; i++) { 470 str = CFArrayGetValueAtIndex(apaths, i); 471 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) { 472 goto finish; 473 } 474 if (!CFStringGetFileSystemRepresentation(str, tempbuf, 475 sizeof(tempbuf))) { 476 goto finish; 477 } 478 pathcpy(bufptr, tempbuf); 479 bufsize += (strlen(tempbuf) + 1); 480 bufptr += (strlen(tempbuf) + 1); 481 } 482 } 483 else { 484 // Pre v1.3 so we're dealing with just 1 path 485 caches->exts = malloc(PATH_MAX); 486 if (caches->exts == NULL) { 487 OSKextLogMemError(); 488 goto finish; 489 } 490 caches->nexts = 1; 491 str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCExtensionsDirKey); 492 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) { 493 goto finish; 494 } 495 if (!CFStringGetFileSystemRepresentation(str, caches->exts, 496 PATH_MAX)) { 497 goto finish; 498 } 499 bufsize = (strlen(caches->exts) + 1); 500 } 501 // trim if possible 502 if (bufsize) { 503 caches->exts = reallocf(caches->exts, bufsize); 504 if (caches->exts == NULL) { 505 OSKextLogMemError(); 506 goto finish; 507 } 508 } 509 510 // kernelcaches have a kernel path key, which we set up by hand 511 if (isKernelcache) { 512 513 str = (CFStringRef)CFDictionaryGetValue(mkDict, kBCKernelPathKey); 514 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) goto finish; 515 516 if (!CFStringGetFileSystemRepresentation(str, caches->kernel, 517 sizeof(caches->kernel))) { 518 goto finish; 519 } 520 521 } 522 523 // Archs are fetched from the cacheinfo dictionary when needed 524 keyCount--; // mkext, mkext2, or kernelcache key handled 525 } 526 527 keyCount--; // postBootPaths handled 528 } 529 530 if (keyCount == 0 && (unsigned)rpsindex == caches->nrps) { 531 rval = 0; 532 caches->cacheinfo = CFRetain(bcDict); // for archs, etc 533 } 534 535finish: 536 if (createdStr) CFRelease(createdStr); 537 if (rval != 0 && caches->exts != NULL) { 538 free(caches->exts); 539 caches->exts = NULL; 540 caches->nexts = 0; 541 } 542 543 return rval; 544} 545 546// helper to create cache dirs; updateStamps() calls and accepts errors 547static int 548createCacheDirs(struct bootCaches *caches) 549{ 550 int errnum, result = ELAST + 1; 551 struct statfs sfs; 552 char *errname; 553 struct stat sb; 554 char cachedir[PATH_MAX], uuiddir[PATH_MAX]; // bootstamps, csfde 555 556 // don't create new cache directories if owners are disabled 557 errname = caches->root; 558 if (statfs(caches->root, &sfs) == 0) { 559 if (sfs.f_flags & MNT_IGNORE_OWNERSHIP) { 560 result = ENOTSUP; goto finish; 561 } 562 } else { 563 result = errno; goto finish; 564 } 565 566 // bootstamps directory 567 // (always made because it's used by libbless on non-BootRoot for ESP) 568 errname = kTSCacheDir; 569 pathcpy(cachedir, caches->root); 570 pathcat(cachedir, kTSCacheDir); 571 pathcpy(uuiddir, cachedir); 572 pathcat(uuiddir, "/"); 573 pathcat(uuiddir, caches->fsys_uuid); 574 if ((errnum = stat(uuiddir, &sb))) { 575 if (errno == ENOENT) { 576 // attempt to clean up cache dir to eliminate stale UUID dirs 577 if (stat(cachedir, &sb) == 0) { 578 (void)sdeepunlink(caches->cachefd, cachedir); 579 } 580 // s..mkdir ensures the cache directory is on the same volume 581 if ((errnum = sdeepmkdir(caches->cachefd,uuiddir,kCacheDirMode))){ 582 result = errnum; goto finish; 583 } 584 } else { 585 result = errnum; goto finish; 586 } 587 } 588 589 // create /S/L/Caches/com.apple.corestorage as necessary 590 if (caches->erpropcache) { 591 errname = caches->erpropcache->rpath; 592 pathcpy(cachedir, caches->root); 593 pathcat(cachedir, dirname(caches->erpropcache->rpath)); 594 errname = cachedir; 595 if ((-1 == stat(cachedir, &sb))) { 596 if (errno == ENOENT) { 597 // s..mkdir ensures cachedir is on the same volume 598 errnum=sdeepmkdir(caches->cachefd,cachedir,kCacheDirMode); 599 if (errnum) { 600 result = errnum; goto finish; 601 } 602 } else { 603 result = errno; goto finish; 604 } 605 } 606 } 607 608 // success 609 errname = NULL; 610 result = 0; 611 612// XX need to centralize this sort of error decoding (w/9217695?) 613finish: 614 if (result) { 615 LOGERRxlate(errname, NULL, result); 616 617 // so kextcache -u doesn't claim bootcaches.plist didn't exist, etc 618 errno = 0; 619 } 620 621 return result; 622} 623 624static CFDictionaryRef 625copy_dict_from_fd(int fd, struct stat *sb) 626{ 627 CFDictionaryRef rval = NULL; 628 void *buf = NULL; 629 CFDataRef data = NULL; 630 CFDictionaryRef dict = NULL; 631 632 // read the plist 633 if (sb->st_size > UINT_MAX || sb->st_size > LONG_MAX) goto finish; 634 if (!(buf = malloc((size_t)sb->st_size))) goto finish; 635 if (read(fd, buf, (size_t)sb->st_size) != sb->st_size) 636 goto finish; 637 if (!(data = CFDataCreate(nil, buf, (long)sb->st_size))) 638 goto finish; 639 640 // Sec: see 4623105 & related for an assessment of our XML parsers 641 dict = (CFDictionaryRef)CFPropertyListCreateFromXMLData(nil, 642 data, kCFPropertyListImmutable, NULL); 643 if (!dict || CFGetTypeID(dict)!=CFDictionaryGetTypeID()) { 644 goto finish; 645 } 646 647 rval = CFRetain(dict); 648 649finish: 650 if (dict) CFRelease(dict); // CFRetain()'d on success 651 if (data) CFRelease(data); 652 if (buf) free(buf); 653 654 return rval; 655} 656 657/* 658 * readBootCaches() reads a volumes bootcaches.plist file and returns 659 * the contents in a new struct bootCaches. Because it returns a pointer, 660 * it stores a more precise error code in errno. 661 */ 662struct bootCaches* 663readBootCaches(char *volRoot, BRUpdateOpts_t opts) 664{ 665 struct bootCaches *rval = NULL, *caches = NULL; 666 int errnum = ELAST + 1; 667 char *errmsg; 668 struct statfs rootsfs; 669 struct stat sb; 670 char bcpath[PATH_MAX]; 671 CFDictionaryRef bcDict = NULL; 672 uuid_t vol_uuid; 673 674 errmsg = "allocation failure"; 675 caches = calloc(1, sizeof(*caches)); 676 if (!caches) goto finish; 677 caches->cachefd = -1; // set cardinal (fd 0 valid) 678 pathcpy(caches->root, volRoot); 679 680 errmsg = "error opening " kBootCachesPath; 681 pathcpy(bcpath, caches->root); 682 pathcat(bcpath, kBootCachesPath); 683 // Sec: cachefd lets us validate data, operations 684 caches->cachefd = (errnum = open(bcpath, O_RDONLY|O_EVTONLY)); 685 if (errnum == -1) { 686 if (errno == ENOENT) { 687 // let kextcache -u log this special case 688 errmsg = NULL; 689 } 690 goto finish; 691 } 692 693 // check the owner and mode (fstat() to insure it's the same file) 694 // w/Leopard, root can see all the way to the disk; 99 -> truly unknown 695 // note: 'sudo cp mach_kernel /Volumes/disrespected/' should -> error 696 if (fstatfs(caches->cachefd, &rootsfs)) { 697 goto finish; 698 } 699 if (fstat(caches->cachefd, &sb)) { 700 goto finish; 701 } 702 // stash the timestamps for later reference (detect bc.plist changes) 703 caches->bcTime = sb.st_mtimespec; // stash so we can detect changes 704 if (rootsfs.f_flags & MNT_QUARANTINE) { 705 errmsg = kBootCachesPath " quarantined"; 706 goto finish; 707 } 708 if (sb.st_uid != 0) { 709 errmsg = kBootCachesPath " not owned by root; no rebuilds"; 710 goto finish; 711 } 712 if (sb.st_mode & S_IWGRP || sb.st_mode & S_IWOTH) { 713 errmsg = kBootCachesPath " writable by non-root"; 714 goto finish; 715 } 716 717 // get UUIDs & other info 718 errmsg = "error obtaining storage information"; 719 if ((errnum = copyVolumeInfo(volRoot, &vol_uuid, &caches->csfde_uuid, 720 caches->bsdname, caches->defLabel))){ 721 errno = errnum; goto finish; 722 } 723 if ((opts & kBRAnyBootStamps) == 0) { 724 uuid_unparse_upper(vol_uuid, caches->fsys_uuid); 725 } 726 727 728 // plist -> dictionary 729 errmsg = "error reading " kBootCachesPath; 730 bcDict = copy_dict_from_fd(caches->cachefd, &sb); 731 if (!bcDict) goto finish; 732 733 734 // error returned via errno now all that matters 735 errmsg = NULL; 736 737 // extractProps returns EFTYPE if the contents were whack 738 // this function returns NULL on failure -> sends err# via errno :P 739 if ((errnum = extractProps(caches, bcDict))) { 740 errno = errnum; goto finish; 741 } 742 743 744 // success! 745 rval = caches; 746 747finish: 748 // report any error message 749 if (errmsg) { 750 if (errnum == -1) { 751 OSKextLog(/* kext */ NULL, 752 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 753 "%s: %s: %s.", caches->root, errmsg, strerror(errno)); 754 } else { 755 OSKextLog(/* kext */ NULL, 756 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 757 "%s: %s.", caches->root, errmsg); 758 } 759 } 760 761 // clean up (unwind in reverse order of allocation) 762 if (bcDict) CFRelease(bcDict); // extractProps() retains for struct 763 764 // if things went awry, free anything associated with 'caches' 765 if (!rval) { 766 destroyCaches(caches); // closes cachefd if needed 767 } 768 769 return rval; 770} 771 772struct bootCaches* 773readBootCachesForDADisk(DADiskRef dadisk) 774{ 775 struct bootCaches *rval = NULL; 776 CFDictionaryRef ddesc = NULL; 777 CFURLRef volURL; // owned by dict; don't release 778 char volRoot[PATH_MAX]; 779 int ntries = 0; 780 781 // 'kextcache -U /' needs this retry to work around 5454260 782 // kexd's vol_appeared filters volumes w/o mount points 783 do { 784 ddesc = DADiskCopyDescription(dadisk); 785 if (!ddesc) goto finish; 786 volURL = CFDictionaryGetValue(ddesc,kDADiskDescriptionVolumePathKey); 787 if (volURL) { 788 break; 789 } else { 790 sleep(1); 791 CFRelease(ddesc); 792 ddesc = NULL; 793 } 794 } while (++ntries < kBRDiskArbMaxRetries); 795 796 if (!volURL) { 797 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 798 "Disk description missing mount point for %d tries", ntries); 799 goto finish; 800 } 801 802 if (ntries) { 803 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 804 "Warning: readCaches got mount point after %d tries.", ntries); 805 } 806 807 if (!CFURLGetFileSystemRepresentation(volURL, /* resolveToBase */ true, 808 (UInt8 *)volRoot, sizeof(volRoot))){ 809 OSKextLogStringError(NULL); 810 goto finish; 811 } 812 813 rval = readBootCaches(volRoot, kBROptsNone); 814 815finish: 816 if (ddesc) CFRelease(ddesc); 817 818 return rval; 819} 820 821/******************************************************************************* 822* needsUpdate checks a single path and timestamp; populates path->tstamp 823* compares/stores *ctime* of the source file vs. the *mtime* of the bootstamp. 824* returns false on error: if we can't tell, we probably can't update 825*******************************************************************************/ 826Boolean needsUpdate(char *root, cachedPath* cpath) 827{ 828 Boolean outofdate = false; 829 Boolean rfpresent, tsvalid; 830 struct stat rsb, tsb; 831 char fullrp[PATH_MAX], fulltsp[PATH_MAX]; 832 833 // create full paths 834 pathcpy(fullrp, root); 835 pathcat(fullrp, cpath->rpath); 836 pathcpy(fulltsp, root); 837 pathcat(fulltsp, cpath->tspath); 838 839 // check the source file in the root volume 840 if (stat(fullrp, &rsb) == 0) { 841 rfpresent = true; 842 } else if (errno == ENOENT) { 843 rfpresent = false; 844 } else { 845 // non-ENOENT errars => fail with log message 846 OSKextLog(/* kext */ NULL, 847 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 848 "Cached file %s: %s.", fullrp, strerror(errno)); 849 goto finish; 850 } 851 852 // The timestamp file's mtime tracks the source file's ctime. 853 // If present, store the root path's timestamps to apply later. 854 if (rfpresent) { 855 TIMESPEC_TO_TIMEVAL(&cpath->tstamps[0], &rsb.st_atimespec); 856 TIMESPEC_TO_TIMEVAL(&cpath->tstamps[1], &rsb.st_ctimespec); 857 } else { 858 // "no [corresponding] root file" is represented by a timestamp 859 // file ("bootstamp") with a/mtime == 0. 860 bzero(cpath->tstamps, sizeof(cpath->tstamps)); 861 } 862 863 // check on the timestamp file itself 864 // it's invalid if it tracks a non-existant root file 865 if (stat(fulltsp, &tsb) == 0) { 866 if (tsb.st_mtimespec.tv_sec != 0) { 867 tsvalid = true; 868 } else { 869 tsvalid = false; 870 } 871 } else if (errno == ENOENT) { 872 tsvalid = false; 873 } else { 874 // non-ENOENT errors => fail w/log message 875 OSKextLog(/* kext */ NULL, 876 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 877 "timestamp cache %s: %s!", fulltsp, strerror(errno)); 878 goto finish; 879 } 880 881 882 // Depnding on root file vs. timestamp data, figure out what, if 883 // anything, needs to be done. 884 if (rfpresent && tsvalid) { 885 outofdate = (tsb.st_mtimespec.tv_sec != rsb.st_ctimespec.tv_sec || 886 tsb.st_mtimespec.tv_nsec != rsb.st_ctimespec.tv_nsec); 887 } else if (!rfpresent && tsvalid) { 888 // need to propagate the fact that the file no longer exists 889 outofdate = true; 890 } else if (rfpresent && !tsvalid) { 891 // need to make the timestamp valid 892 outofdate = true; 893 } else { 894 // !rfpresent && !tsvalid 895 outofdate = false; 896 } 897 898finish: 899 return outofdate; 900} 901 902/******************************************************************************* 903* needUpdates checks all paths and returns details if you want them 904* It only to be called on volumes that will have timestamp paths 905* (i.e. BootRoot volumes! ;) 906* 907* In theory, all we have to do is find one "problem" (out of date file) 908* but in practice, there could be real problems (like missing sources) 909* which would prevent a complete update (at a minimum, all updates copy 910* all RPS paths to a new RPS dir). needsUpdate() also populates the 911* tstamps used by updateStamps (for all files, regardless of whether 912* they were updated). 913*******************************************************************************/ 914#define OODMSG "not cached." 915Boolean needUpdates(struct bootCaches *caches, Boolean *rps, Boolean *booters, 916 Boolean *misc, OSKextLogSpec oodLogSpec) 917{ 918 Boolean rpsOOD, bootersOOD, miscOOD, anyOOD; 919 cachedPath *cp; 920 921 // assume nothing needs updating (caller may interpret error -> needsUpdate) 922 rpsOOD = bootersOOD = miscOOD = anyOOD = false; 923 924 for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) { 925 if (needsUpdate(caches->root, cp)) { 926 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 927 anyOOD = rpsOOD = true; 928 } 929 } 930 if ((cp = &(caches->efibooter)), cp->rpath[0]) { 931 if (needsUpdate(caches->root, cp)) { 932 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 933 anyOOD = bootersOOD = true; 934 } 935 } 936 if ((cp = &(caches->ofbooter)), cp->rpath[0]) { 937 if (needsUpdate(caches->root, cp)) { 938 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 939 anyOOD = bootersOOD = true; 940 } 941 } 942 for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++) { 943 if (needsUpdate(caches->root, cp)) { 944 OSKextLog(NULL, oodLogSpec, "%s " OODMSG, cp->rpath); 945 anyOOD = miscOOD = true; 946 } 947 } 948 949 // This function only checks bootstamp timestamps as compared to 950 // the source file. kernel/kext caches, property caches files, 951 // and other files are checked explicitly before this function 952 // is called. 953 954 if (rps) *rps = rpsOOD; 955 if (booters) *booters = bootersOOD; 956 if (misc) *misc = miscOOD; 957 958 return anyOOD; 959} 960 961/******************************************************************************* 962* updateStamps runs through all of the cached paths in a struct bootCaches 963* and applies the timestamps captured before the update 964* not going to bother with a re-stat() of the sources for now 965*******************************************************************************/ 966// could/should use schdirparent, move to safecalls.[ch] 967static int 968_sutimes(int fdvol, char *path, int oflags, struct timeval times[2]) 969{ 970 int bsderr; 971 int fd = -1; 972 973 // X O_RDONLY is the only way to open directories ... and the 974 // descriptor allows timestamp updates 975 if (-1 == (fd = sopen(fdvol, path, oflags, kCacheFileMode))) { 976 bsderr = fd; 977 // XX sopen() should log on its own after we get errors correct 978 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 979 "%s: %s", path, strerror(errno)); 980 goto finish; 981 } 982 if ((bsderr = futimes(fd, times))) { 983 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 984 "futimes(<%s>): %s", path, strerror(errno)); 985 } 986 987finish: 988 if (fd != -1) close(fd); 989 990 return bsderr; 991} 992 993// Sec review: no need to drop privs thanks to safecalls.[ch] 994static int 995updateStamp(char *root, cachedPath *cpath, int fdvol, int command) 996{ 997 int bsderr = -1; 998 char fulltspath[PATH_MAX]; 999 1000 pathcpy(fulltspath, root); 1001 pathcat(fulltspath, cpath->tspath); 1002 1003 // we must unlink even for ApplyTimes b/c sopen() passes O_EXCL 1004 bsderr = sunlink(fdvol, fulltspath); 1005 if (bsderr == -1 && errno == ENOENT) { 1006 bsderr = 0; 1007 } 1008 1009 if (command == kBCStampsApplyTimes) { 1010 bsderr = _sutimes(fdvol, fulltspath, O_CREAT, cpath->tstamps); 1011 } 1012 1013finish: 1014 return bsderr; 1015} 1016 1017#define BRDBG_DISABLE_EXTSYNC_F "/var/db/.BRDisableExtraSync" 1018int 1019updateStamps(struct bootCaches *caches, int command) 1020{ 1021 int anyErr = 0; // accumulates errors 1022 struct statfs sfs; 1023 cachedPath *cp; 1024 struct stat sb; 1025 1026 // don't try to apply bootstamps to a read-only volume 1027 if (statfs(caches->root, &sfs) == 0) { 1028 if ((sfs.f_flags & MNT_RDONLY)) { 1029 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1030 "Warning: %s read-only: no bootstamp updates", 1031 caches->root); 1032 return 0; // success 1033 } 1034 } 1035 1036 // allow known commands through 1037 switch (command) { 1038 case kBCStampsApplyTimes: 1039 case kBCStampsUnlinkOnly: 1040 break; 1041 1042 default: 1043 return EINVAL; 1044 } 1045 1046 // if writing stamps, make sure cache directory exists 1047 if (command == kBCStampsApplyTimes && 1048 (anyErr = createCacheDirs(caches))) { 1049 return anyErr; 1050 } 1051 1052 // run through all of the cached paths apply bootstamp 1053 for (cp = caches->rpspaths; cp < &caches->rpspaths[caches->nrps]; cp++) { 1054 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1055 } 1056 if ((cp = &(caches->efibooter)), cp->rpath[0]) { 1057 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1058 } 1059 if ((cp = &(caches->ofbooter)), cp->rpath[0]) { 1060 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1061 } 1062 for (cp = caches->miscpaths; cp < &caches->miscpaths[caches->nmisc]; cp++){ 1063 anyErr |= updateStamp(caches->root, cp, caches->cachefd, command); 1064 } 1065 1066 // Clean shutdown should make sure these stamps are on disk; this 1067 // code worked around 8603195/6848376 which were fixed by Lion GM. 1068 if (stat(BRDBG_DISABLE_EXTSYNC_F, &sb) == -1) { 1069 anyErr |= fcntl(caches->cachefd, F_FULLFSYNC); 1070 } 1071 1072 return anyErr; 1073} 1074 1075/******************************************************************************* 1076* rebuild_kext_boot_cache_file fires off kextcache on the given volume 1077* XX there is a bug here that can mask a stale mkext in the Apple_Boot (4764605) 1078*******************************************************************************/ 1079int rebuild_kext_boot_cache_file( 1080 struct bootCaches *caches, 1081 Boolean wait, 1082 const char * kext_boot_cache_file, 1083 const char * kernel_file) 1084{ 1085 int rval = ELAST + 1; 1086 int pid = -1; 1087 CFIndex i, argi = 0, argc = 0, narchs = 0; 1088 CFDictionaryRef pbDict, mkDict; 1089 CFArrayRef archArray; 1090 char **kcargs = NULL, **archstrs = NULL; // no [ARCH_MAX] anywhere? 1091 char * lastslash = NULL; 1092 char rcpath[PATH_MAX] = ""; 1093 struct stat sb; 1094 char full_cache_file_path[PATH_MAX] = ""; 1095 char full_cache_file_dir_path[PATH_MAX] = ""; 1096 char * fullextsp = NULL; 1097 char fullkernelp[PATH_MAX] = ""; 1098 Boolean generateKernelcache = false; 1099 int mkextVersion = 0; 1100 1101 // bootcaches.plist might not request mkext/kernelcache rebuilds 1102 if (!caches->kext_boot_cache_file 1103 ) { 1104 goto finish; 1105 } 1106 1107 fullextsp = malloc(caches->nexts * PATH_MAX); 1108 if (!fullextsp) goto finish; 1109 *fullextsp = 0x00; 1110 1111 pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey); 1112 if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish; 1113 1114 /* Try for a Kernelcache key, and if there isn't one, look for an "MKext" key. 1115 */ 1116 do { 1117 mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key); 1118 if (!mkDict) 1119 mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV2Key); 1120 if (!mkDict) { 1121 mkDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV3Key); 1122 } 1123 1124 if (mkDict) { 1125 generateKernelcache = true; 1126 break; 1127 } 1128 1129 mkDict = CFDictionaryGetValue(pbDict, kBCMKext2Key); 1130 if (mkDict) { 1131 mkextVersion = 2; 1132 break; 1133 } 1134 1135 mkDict = CFDictionaryGetValue(pbDict, kBCMKextKey); 1136 if (mkDict) { 1137 mkextVersion = 1; 1138 break; 1139 } 1140 1141 } while (0); 1142 1143 if (!mkDict || CFGetTypeID(mkDict) != CFDictionaryGetTypeID()) goto finish; 1144 1145 archArray = CFDictionaryGetValue(mkDict, kBCArchsKey); 1146 if (archArray) { 1147 narchs = CFArrayGetCount(archArray); 1148 archstrs = calloc(narchs, sizeof(char*)); 1149 if (!archstrs) goto finish; 1150 } 1151 1152 // argv[0] -a x -a y -l [-n] [-r] [-K <kernel>] -c <kcache> -volume-root <vol> <exts> NULL 1153 argc = 1 + (narchs*2) + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + caches->nexts + 1; 1154 kcargs = malloc(argc * sizeof(char*)); 1155 if (!kcargs) goto finish; 1156 kcargs[argi++] = "kextcache"; 1157 1158 // convert each -arch argument into a char* and add to the vector 1159 for(i = 0; i < narchs; i++) { 1160 CFStringRef archStr; 1161 size_t archSize; 1162 1163 // get arch 1164 archStr = CFArrayGetValueAtIndex(archArray, i); 1165 if (!archStr || CFGetTypeID(archStr)!=CFStringGetTypeID()) goto finish; 1166 // XX an arch is not a pathname; EncodingASCII might be more appropriate 1167 archSize = CFStringGetMaximumSizeOfFileSystemRepresentation(archStr); 1168 if (!archSize) goto finish; 1169 // X marks the spot: over 800 lines written before I realized that 1170 // there were some serious security implications 1171 archstrs[i] = malloc(archSize); 1172 if (!archstrs[i]) goto finish; 1173 if (!CFStringGetFileSystemRepresentation(archStr,archstrs[i],archSize)) 1174 goto finish; 1175 1176 kcargs[argi++] = "-arch"; 1177 kcargs[argi++] = archstrs[i]; 1178 } 1179 1180 // BootRoot always includes local kexts 1181 kcargs[argi++] = "-local-root"; 1182 1183 // 6413843 check if it's installation media (-> add -n) 1184 pathcpy(rcpath, caches->root); 1185 removeTrailingSlashes(rcpath); // X caches->root trailing '/'? 1186 pathcat(rcpath, "/etc/rc.cdrom"); 1187 if (stat(rcpath, &sb) == 0) { 1188 kcargs[argi++] = "-network-root"; 1189 } 1190 1191 // determine proper argument to precede kext_boot_cache_file 1192 if (generateKernelcache) { 1193 // for '/' only, include all kexts loaded since boot (9130863) 1194 // TO DO: can we optimize for the install->first boot case? 1195 if (0 == strcmp(caches->root, "/")) { 1196 kcargs[argi++] = "-all-loaded"; 1197 } 1198 pathcpy(fullkernelp, caches->root); 1199 removeTrailingSlashes(fullkernelp); 1200 pathcat(fullkernelp, kernel_file); 1201 kcargs[argi++] = "-kernel"; 1202 kcargs[argi++] = fullkernelp; 1203 // prelinked kernel path below 1204 kcargs[argi++] = "-prelinked-kernel"; 1205 } else if (mkextVersion == 2) { 1206 kcargs[argi++] = "-mkext2"; 1207 } else if (mkextVersion == 1) { 1208 kcargs[argi++] = "-mkext1"; 1209 } else { 1210 // internal error! 1211 goto finish; 1212 } 1213 1214 pathcpy(full_cache_file_path, caches->root); 1215 removeTrailingSlashes(full_cache_file_path); 1216 pathcat(full_cache_file_path, kext_boot_cache_file); 1217 kcargs[argi++] = full_cache_file_path; 1218 1219 kcargs[argi++] = "-volume-root"; 1220 kcargs[argi++] = caches->root; 1221 1222 // we now support multiple extensions directories 1223 char *extsDirPtr = caches->exts; 1224 char *tempExtsDirPtr = fullextsp; 1225 1226 for (i = 0; i < caches->nexts; i++) { 1227 pathcpy(tempExtsDirPtr, caches->root); 1228 removeTrailingSlashes(tempExtsDirPtr); 1229 pathcat(tempExtsDirPtr, extsDirPtr); 1230 1231 kcargs[argi++] = tempExtsDirPtr; 1232 1233 extsDirPtr += (strlen(extsDirPtr) + 1); 1234 tempExtsDirPtr += (strlen(tempExtsDirPtr) + 1); 1235 } 1236 kcargs[argi] = NULL; 1237 1238 pathcpy(full_cache_file_dir_path, full_cache_file_path); 1239 lastslash = rindex(full_cache_file_dir_path, '/'); 1240 if (lastslash) { 1241 *lastslash = '\0'; 1242 1243 /* Make sure we have a destination directory to write the new mkext 1244 * file into (people occasionally delete the caches folder). 1245 */ 1246 if ((rval = sdeepmkdir(caches->cachefd, full_cache_file_dir_path, 1247 kCacheDirMode))) { 1248 OSKextLog(/* kext */ NULL, 1249 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1250 "failed to create cache folder %s.", full_cache_file_dir_path); 1251 // can't make dest directory, kextcache will fail, so don't bother 1252 goto finish; 1253 } 1254 1255 } 1256 rval = 0; 1257 1258#if 0 1259 OSKextLog(NULL, 1260 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1261 "%s: kextcache args %ld ", 1262 __FUNCTION__, argi); 1263 for (i = 0; i < argi; i++) { 1264 OSKextLog(NULL, 1265 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1266 "%s ", 1267 kcargs[i]); 1268 } 1269#endif 1270 1271 /* wait:false means the return value is <0 for fork/exec failures and 1272 * the pid of the forked process if >0. 1273 * 1274 * wait:true means the return value is <0 for fork/exec failures and 1275 * the exit status of the forked process (>=0) otherwise. 1276 */ 1277 pid = fork_program("/usr/sbin/kextcache", kcargs, wait); // logs errors 1278 1279finish: 1280 if (rval) { 1281 OSKextLog(/* kext */ NULL, 1282 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1283 "Data error before mkext rebuild."); 1284 } 1285 if (wait || pid < 0) { 1286 rval = pid; 1287 } 1288 1289 if (archstrs) { 1290 for (i = 0; i < narchs; i++) { 1291 if (archstrs[i]) free(archstrs[i]); 1292 } 1293 free(archstrs); 1294 } 1295 if (fullextsp) free(fullextsp); 1296 if (kcargs) free(kcargs); 1297 1298 return rval; 1299} 1300 1301/******************************************************************************* 1302* Check our various plist caches, for the current kernel arch only, to see if 1303* they need to be rebuilt: 1304* 1305* id -> url index (per directory) 1306* logindwindow prop/value cache for OSBundleHelper (global) 1307* 1308* This should only be called for the root volume! 1309*******************************************************************************/ 1310Boolean plistCachesNeedRebuild(const NXArchInfo * kernelArchInfo) 1311{ 1312 Boolean result = true; 1313 CFArrayRef systemExtensionsFolderURLs = NULL; // need not release 1314 CFStringRef cacheBasename = NULL; // must release 1315 CFIndex count, i; 1316 1317 systemExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs(); 1318 if (!systemExtensionsFolderURLs || 1319 !CFArrayGetCount(systemExtensionsFolderURLs)) { 1320 1321 result = false; 1322 goto finish; 1323 } 1324 1325 count = CFArrayGetCount(systemExtensionsFolderURLs); 1326 for (i = 0; i < count; i++) { 1327 CFURLRef directoryURL = CFArrayGetValueAtIndex( 1328 systemExtensionsFolderURLs, i); 1329 1330 /* Check the KextIdentifiers index. 1331 */ 1332 if (!_OSKextReadCache(directoryURL, CFSTR(_kOSKextIdentifierCacheBasename), 1333 /* arch */ NULL, _kOSKextCacheFormatCFBinary, /* parseXML? */ false, 1334 /* valuesOut*/ NULL)) { 1335 1336 goto finish; 1337 } 1338 } 1339 1340 /* Check the KextPropertyValues_OSBundleHelper cache for the current kernel arch. 1341 */ 1342 cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault, 1343 /* formatOptions */ NULL, CFSTR("%s%s"), 1344 _kKextPropertyValuesCacheBasename, 1345 "OSBundleHelper"); 1346 if (!cacheBasename) { 1347 OSKextLogMemError(); 1348 result = false; // cause we don't be able to update 1349 goto finish; 1350 } 1351 1352 if (!_OSKextReadCache(systemExtensionsFolderURLs, cacheBasename, 1353 kernelArchInfo, _kOSKextCacheFormatCFXML, /* parseXML? */ false, 1354 /* valuesOut*/ NULL)) { 1355 1356 goto finish; 1357 } 1358 1359 result = false; 1360 1361finish: 1362 SAFE_RELEASE(cacheBasename); 1363 return result; 1364} 1365 1366Boolean check_kext_boot_cache_file( 1367 struct bootCaches * caches, 1368 const char * cache_path, 1369 const char * kernel_path) 1370{ 1371 Boolean needsrebuild = false; 1372 char full_cache_file_path[PATH_MAX] = ""; 1373 char fullextsp[PATH_MAX] = ""; 1374 char fullkernelp[PATH_MAX] = ""; 1375 struct stat extsb; 1376 struct stat kernelsb; 1377 struct stat sb; 1378 time_t validModtime = 0; 1379 1380 /* Do we have a cache file (mkext or kernelcache)? 1381 * Note: cache_path is a pointer field, not a static array. 1382 */ 1383 if (cache_path == NULL) 1384 goto finish; 1385 1386 /* If so, check the mod time of the cache file vs. the extensions folder. 1387 */ 1388 // struct bootCaches paths are all *relative* 1389 pathcpy(full_cache_file_path, caches->root); 1390 removeTrailingSlashes(full_cache_file_path); 1391 pathcat(full_cache_file_path, cache_path); 1392 1393 // we support multiple extensions directories, use latest mod time 1394 char *bufptr; 1395 int i; 1396 bufptr = caches->exts; 1397 1398 for (i = 0; i < caches->nexts; i++) { 1399 pathcpy(fullextsp, caches->root); 1400 removeTrailingSlashes(fullextsp); 1401 pathcat(fullextsp, bufptr); 1402 1403 if (stat(fullextsp, &extsb) == 0) { 1404 if (extsb.st_mtime + 1 > validModtime) { 1405 validModtime = extsb.st_mtime + 1; 1406 } 1407 } 1408 else { 1409 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1410 "Warning: %s: %s", fullextsp, strerror(errno)); 1411 } 1412 bufptr += (strlen(bufptr) + 1); 1413 fullextsp[0] = 0x00; 1414 } 1415 1416 /* Check the mod time of the appropriate kernel too, if applicable. 1417 */ 1418 1419 /* A kernel path in bootcaches.plist means we should have a kernelcache. 1420 * Note: kernel_path is a static array, not a pointer field. 1421 */ 1422 if (kernel_path[0]) { 1423 pathcpy(fullkernelp, caches->root); 1424 removeTrailingSlashes(fullkernelp); 1425 pathcat(fullkernelp, kernel_path); 1426 1427 if (stat(fullkernelp, &kernelsb) == -1) { 1428 OSKextLog(/* kext */ NULL, 1429 kOSKextLogBasicLevel | kOSKextLogFileAccessFlag, 1430 "Note: %s: %s", fullkernelp, strerror(errno)); 1431 // assert(needsrebuild == false); // we can't build w/o kernel 1432 goto finish; 1433 } 1434 1435 /* The cache file should be 1 second newer than the newer of the 1436 * Extensions folder(s) or the kernel. 1437 */ 1438 if (kernelsb.st_mtime > validModtime) { 1439 validModtime = kernelsb.st_mtime + 1; 1440 } 1441 } 1442 1443 // The cache file itself 1444 needsrebuild = true; // since this stat() will fail if cache file is gone 1445 if (stat(full_cache_file_path, &sb) == -1) { 1446 goto finish; 1447 } 1448 needsrebuild = (sb.st_mtime != validModtime); 1449 1450finish: 1451 return needsrebuild; 1452} 1453 1454/******************************************************************************* 1455* createDiskForMount creates a DADisk object given a mount point 1456* session is optional; one is created and released if the caller can't supply 1457*******************************************************************************/ 1458DADiskRef createDiskForMount(DASessionRef session, const char *mount) 1459{ 1460 DADiskRef rval = NULL; 1461 DASessionRef dasession = NULL; 1462 CFURLRef volURL = NULL; 1463 1464 if (session) { 1465 dasession = session; 1466 } else { 1467 dasession = DASessionCreate(nil); 1468 if (!dasession) goto finish; 1469 } 1470 1471 volURL = CFURLCreateFromFileSystemRepresentation(nil, (UInt8*)mount, 1472 strlen(mount), 1 /*isDirectory*/); 1473 if (!volURL) goto finish; 1474 1475 rval = DADiskCreateFromVolumePath(nil, dasession, volURL); 1476 1477finish: 1478 if (volURL) CFRelease(volURL); 1479 if (!session && dasession) 1480 CFRelease(dasession); 1481 1482 return rval; 1483} 1484 1485 1486/***************************************************************************** 1487* CoreStorage FDE check & update routines 1488* kextd calls check_csfde; kextcache calls check_, rebuild_csfde_cache() 1489*****************************************************************************/ 1490// on success, caller is responsible for releasing econtext 1491int 1492copyCSFDEInfo(CFStringRef uuidStr, CFDictionaryRef *econtext, 1493 time_t *timeStamp) 1494{ 1495 int rval = ELAST+1; 1496 CFDictionaryRef lvfprops = NULL; 1497 CFDictionaryRef ectx; 1498 CFNumberRef psRef; 1499 CFArrayRef eusers; // owned by lvfprops 1500 Boolean encrypted; 1501 1502 if (!uuidStr) { 1503 rval = EINVAL; goto finish; 1504 } 1505 1506 // 04/25/11 - gab: <rdar://problem/9168337> 1507 // can't operate without libCoreStorage func 1508 if (CoreStorageCopyFamilyProperties == NULL) { 1509 rval = ESHLIBVERS; goto finish; 1510 } 1511 1512 lvfprops = CoreStorageCopyFamilyProperties(uuidStr); 1513 if (!lvfprops) { 1514 rval = EFTYPE; goto finish; 1515 } 1516 1517 ectx = (CFMutableDictionaryRef)CFDictionaryGetValue(lvfprops, 1518 CFSTR(kCoreStorageFamilyEncryptionContextKey)); 1519 if (!ectx || CFGetTypeID(ectx) != CFDictionaryGetTypeID()) { 1520 rval = EFTYPE; goto finish; 1521 } 1522 1523 // does it have encrypted users? 1524 eusers = (CFArrayRef)CFDictionaryGetValue(ectx, CFSTR(kCSFDECryptoUsersID)); 1525 encrypted = (eusers && CFArrayGetCount(eusers)); 1526 1527 if (encrypted) { 1528 if (econtext) { 1529 *econtext = CFRetain(ectx); 1530 } 1531 if (timeStamp) { 1532 psRef = CFDictionaryGetValue(ectx, CFSTR(kCSFDELastUpdateTime)); 1533 if (psRef) { 1534 if (CFGetTypeID(psRef) != CFNumberGetTypeID() || 1535 !CFNumberGetValue(psRef,kCFNumberSInt64Type,timeStamp)){ 1536 rval = EFTYPE; goto finish; 1537 } 1538 } else { // no timestamp (odd, but maybe okay) 1539 *timeStamp = 0LL; 1540 } 1541 } 1542 } else { // not encrypted 1543 if (econtext) *econtext = NULL; 1544 if (timeStamp) *timeStamp = 0LL; 1545 } 1546 1547 rval = 0; 1548 1549finish: 1550 if (lvfprops) CFRelease(lvfprops); 1551 1552 if (rval) { 1553 OSKextLogCFString(NULL, kOSKextLogErrorLevel|kOSKextLogFileAccessFlag, 1554 CFSTR("could not copy LVF props for %@: %s"), 1555 uuidStr, strerror(rval)); 1556 } 1557 1558 return rval; 1559} 1560 1561Boolean 1562check_csfde(struct bootCaches *caches) 1563{ 1564 Boolean needsupdate = false; 1565 time_t propStamp, erStamp; 1566 char erpath[PATH_MAX]; 1567 struct stat ersb; 1568 1569 if (!caches->csfde_uuid || !caches->erpropcache) 1570 goto finish; 1571 1572 if (copyCSFDEInfo(caches->csfde_uuid, NULL, &propStamp)) 1573 goto finish; 1574 1575 // get property cache file's timestamp 1576 pathcpy(erpath, caches->root); 1577 pathcat(erpath, caches->erpropcache->rpath); 1578 if (stat(erpath, &ersb) == 0) { 1579 erStamp = ersb.st_mtimespec.tv_sec; 1580 } else { 1581 if (errno == ENOENT) { 1582 erStamp = 0LL; 1583 } else { 1584 goto finish; 1585 } 1586 } 1587 1588 // generally the timestamp advances, but != means out of date 1589 needsupdate = erStamp != propStamp; 1590 1591finish: 1592 return needsupdate; 1593} 1594 1595/* 1596 * _writeCSFDENoFD() 1597 * If possible, use CSFDEInitPropertyCache() to write 1598 * EncryptedRoot.plist.wipekey to the requested path. 1599 * 1600 * CSFDEInitPropertyCache() writes to <path>/S/L/Caches/com.apple.corestorage 1601 * so the basic algorithm is 1602 * 0) provided dstpath = /path/to/S/L/Caches/com.apple.corestorage 1603 * 1) find substring S/L/Caches/c.a.corestorage/EncryptedRoot.plist.wipekey 1604 * 2) create terminated parentpath = path/to/\0ystem/L/Caches... 1605 * 3) create /path/to/.../SystemVersion.plist if it doesn't exist 1606 * 4) call CSFDEInitPropertyCache(/path/to)! 1607 */ 1608// CSFDEInitPropertyCache() uses /S/L/E in 10.7.2+, but _writeCSFDENoFD() 1609// is only for 10.7.[01] where InitPropertyCache() uses SystemVersion.plist. 1610#define kOrigInitCookieDir "/System/Library/CoreServices" 1611#define kOrigInitCookieFile "/SystemVersion.plist" 1612#define kFDECacheFile kCSFDEPropertyCacheDir"/"kCSFDEPropertyCacheFileEncrypted 1613static int 1614_writeCSFDENoFD(int scopefd, CFDictionaryRef ectx, 1615 CFStringRef wipeKeyUUID, char *dstpath) 1616{ 1617 int bsderr, rval = ELAST + 1; // all but path*() should set 1618 int fd = -1; 1619 Boolean createdCookie = false; 1620 char parentpath[PATH_MAX], cookiepath[PATH_MAX]; 1621 char *relpath; 1622 struct stat sb; 1623 1624 // detect expected relative path to EncryptedRoot.plist.wipekey 1625 // and create terminated parentpath 1626 pathcpy(parentpath, dstpath); 1627 if (!(relpath = strstr(parentpath, kFDECacheFile))) { 1628 // path doesn't contain expected substring 1629 rval = EINVAL; LOGERRxlate(dstpath, "missing" kFDECacheFile, rval); 1630 goto finish; 1631 } 1632 relpath[0] = '\0'; // terminate parentpath[] at common parent 1633 1634 // if necessary, create sibling SystemVersion.plist 1635 pathcpy(cookiepath, parentpath); 1636 pathcat(cookiepath, kOrigInitCookieDir); 1637 if ((bsderr = sdeepmkdir(scopefd, cookiepath, kCacheDirMode))) { 1638 rval = bsderr; LOGERRxlate(cookiepath, NULL, rval); goto finish; 1639 } 1640 pathcat(cookiepath, kOrigInitCookieFile); 1641 if (0 != stat(cookiepath, &sb)) { 1642 if ((fd = sopen(scopefd, cookiepath, O_CREAT, kCacheFileMode)) < 0) { 1643 rval = errno; LOGERRxlate(cookiepath, NULL, rval); goto finish; 1644 } 1645 close(fd); 1646 createdCookie = true; 1647 } 1648 1649 // write via the 10.7.[01] function (scopefd ignored!) 1650 errno = 0; 1651 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1652 "WARNING: no CSFDEWritePropertyCacheToFD(); " 1653 "trying CSFDEInitPropertyCache()"); 1654 if (false == CSFDEInitPropertyCache(ectx, parentpath, wipeKeyUUID)) { 1655 rval = ELAST + 1; // "internal error" :P 1656 LOGERRxlate("CSFDEInitPropertyCache", parentpath, rval); 1657 goto finish; 1658 } 1659 // make sure it did the deed 1660 if (-1 == stat(dstpath, &sb)) { 1661 rval = errno; LOGERRxlate(dstpath, NULL, rval); goto finish; 1662 } 1663 1664 // success! 1665 rval = 0; 1666 1667finish: 1668 if (createdCookie) { 1669 (void)sunlink(scopefd, cookiepath); // empty boot.?/S/L/CS okay 1670 } 1671 1672 return rval; 1673} 1674 1675// NOTE: weak-linking depends on -weak-l/-weak_framemwork *and* the 1676// function declaration being marked correctly in the header file! 1677int 1678writeCSFDEProps(int scopefd, CFDictionaryRef ectx, 1679 char *cspvbsd, char *dstpath) 1680{ 1681 int errnum, rval = ELAST + 1; 1682 CFStringRef wipeKeyUUID = NULL; 1683 char dstparent[PATH_MAX]; 1684 int erfd = -1; 1685 1686 // 9168337 didn't quite do it, see 10831618 1687 // check for required weak-linked symbol 1688 if (CoreStorageCopyPVWipeKeyUUID==NULL) { 1689 rval = ESHLIBVERS; 1690 LOGERRxlate("no CoreStorageCopyPVWipeKeyUUID()", NULL, rval); 1691 goto finish; 1692 } 1693 wipeKeyUUID = CoreStorageCopyPVWipeKeyUUID(cspvbsd); 1694 if (!wipeKeyUUID) { 1695 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1696 "CoreStorageCopyPVWipeKeyUUID(%s) failed", cspvbsd); 1697 rval = ENODEV; goto finish; 1698 } 1699 1700 // prep (ENOENT ignored by szerofile()) 1701 if ((errnum = szerofile(scopefd, dstpath)) || 1702 ((errnum = sunlink(scopefd, dstpath)) && errno != ENOENT)) { 1703 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 1704 "WARNING: %s: %s", dstpath, strerror(errno)); 1705 } 1706 1707 // recursively create the parent directory 1708 if (strlcpy(dstparent,dirname(dstpath),PATH_MAX) >= PATH_MAX) { 1709 rval = EOVERFLOW; goto finish; 1710 } 1711 if ((errnum = sdeepmkdir(scopefd, dstparent, kCacheDirMode))) { 1712 rval = errnum; LOGERRxlate(dstparent, NULL, rval); goto finish; 1713 } 1714 1715 // use modern function if available 1716 if (CSFDEWritePropertyCacheToFD!=NULL) { 1717 // open and write to FD 1718 erfd = sopen(scopefd, dstpath, O_CREAT|O_RDWR, kCacheFileMode); 1719 if (-1 == erfd) { 1720 rval = errno; LOGERRxlate(dstpath, NULL, rval); goto finish; 1721 } 1722 if (!CSFDEWritePropertyCacheToFD(ectx, erfd, wipeKeyUUID)) { 1723 rval = ELAST + 1; // "internal error" :P 1724 LOGERRxlate("CSFDEWritePropertyCacheToFD", dstpath, rval); 1725 goto finish; 1726 } 1727 } else { 1728 // try to trick the old function into writing the cache 1729 if ((errnum = _writeCSFDENoFD(scopefd,ectx,wipeKeyUUID,dstpath))) { 1730 rval = errnum; goto finish; // error logged by function 1731 } 1732 } 1733 1734 // success 1735 rval = 0; 1736 1737finish: 1738 if (wipeKeyUUID) CFRelease(wipeKeyUUID); 1739 if (erfd != -1) close(erfd); 1740 1741 return rval; 1742} 1743 1744// write out a populated EncryptedRoot.plist.wipekey to the root volume 1745static int 1746_writeLegacyCSFDECache(struct bootCaches *caches) 1747{ 1748 int errnum, rval = ELAST + 1; 1749 CFArrayRef dataVolumes = NULL; 1750 CFStringRef bsdStr; // belongs to dataVolumes 1751 char bsdname[DEVMAXPATHSIZE]; 1752 CFDictionaryRef ectx = NULL; 1753 char *errmsg; 1754 char erpath[PATH_MAX]; 1755 int erfd = -1; 1756 1757 errmsg = "invalid argument"; 1758 if (!caches->csfde_uuid || !caches->erpropcache) { 1759 rval = EINVAL; goto finish; 1760 } 1761 1762 // hasBRBs() cares about Apple_Boot's; FDE, data partitions 1763 (void)hasBootRootBoots(caches, NULL, &dataVolumes, NULL); 1764 if (!dataVolumes || CFArrayGetCount(dataVolumes) == 0) { 1765 errmsg = "no data partition! (for wipe key)"; 1766 rval = ENODEV; goto finish; 1767 } 1768 1769 // legacy => encrypt with the first Apple_CoreStorage wipe key 1770 errmsg = "error getting volume wipe key"; 1771 bsdStr = CFArrayGetValueAtIndex(dataVolumes, 0); 1772 if (!bsdStr) { 1773 rval = ENODEV; goto finish; 1774 } 1775 if (!CFStringGetFileSystemRepresentation(bsdStr,bsdname,sizeof(bsdname))){ 1776 rval = EINVAL; goto finish; 1777 } 1778 1779 errmsg = "error getting encryption context data"; 1780 if ((errnum = copyCSFDEInfo(caches->csfde_uuid, &ectx, NULL))) { 1781 rval = errnum; goto finish; 1782 } 1783 1784 // build /<vol>/S/L/Caches/..corestorage/EncryptedRoot.plist.wipekey 1785 errmsg = "error building encryption context cache file path"; 1786 pathcpy(erpath, caches->root); 1787 pathcat(erpath, caches->erpropcache->rpath); 1788 errmsg = NULL; 1789 1790 // if not encrypted, just nuke :) 1791 if (!ectx) { 1792 (void)sunlink(caches->cachefd, erpath); 1793 rval = 0; goto finish; 1794 } 1795 1796 errmsg = NULL; // writeCSFDEProps() logs errors 1797 if ((errnum = writeCSFDEProps(caches->cachefd, ectx, bsdname, erpath))) { 1798 rval = errnum; goto finish; 1799 } 1800 1801 // success 1802 rval = 0; 1803 1804finish: 1805 if (erfd != -1) close (erfd); 1806 if (dataVolumes) CFRelease(dataVolumes); 1807 if (ectx) CFRelease(ectx); 1808 1809 if (rval && errmsg) { 1810 LOGERRxlate(caches->root, errmsg, rval); 1811 } 1812 1813 return rval; 1814} 1815 1816int 1817rebuild_csfde_cache(struct bootCaches *caches) 1818{ 1819 int errnum, rval = ELAST + 1; 1820 time_t timeStamp; 1821 char erpath[PATH_MAX] = "<unknown>"; 1822 struct timeval times[2] = {{ 0, 0 }, { 0, 0 }}; 1823 1824 if (!caches->csfde_uuid || !caches->erpropcache) { 1825 rval = EINVAL; goto finish; 1826 } 1827 1828 if ((errnum = createCacheDirs(caches))) { 1829 rval = errnum; goto finish; 1830 } 1831 1832 // OSes that only support single-PV CSFDE need content in erpropcache 1833 if (caches->erpropTSOnly == false) { 1834 return _writeLegacyCSFDECache(caches); // takes care of everything 1835 } 1836 1837 // otherwise, just grab the timestamp so update_boot.c knows to re-fetch 1838 if ((errnum = copyCSFDEInfo(caches->csfde_uuid, NULL, &timeStamp))) { 1839 rval = errnum; goto finish; 1840 } 1841 times[0].tv_sec = (__darwin_time_t)timeStamp; 1842 times[1].tv_sec = (__darwin_time_t)timeStamp; // mdworker -> atime 1843 1844 // build path and recreate proper timestamp 1845 pathcpy(erpath, caches->root); 1846 pathcat(erpath, caches->erpropcache->rpath); 1847 (void)sunlink(caches->cachefd, erpath); 1848 1849 if (timeStamp != 0LL) { 1850 if ((errnum = _sutimes(caches->cachefd, erpath, O_CREAT, times))) { 1851 rval = errnum; goto finish; 1852 } 1853 } 1854 1855 // success 1856 rval = 0; 1857 1858finish: 1859 // no logging above 1860 if (rval) LOGERRxlate(erpath, NULL, rval); 1861 1862 return rval; 1863} 1864 1865 1866/******************************************************************************* 1867* check_loccache() checks caches that depend on the system localization 1868* XX could use getFilePathModTimePlusOne() -- currently in kextcache_main.c 1869*******************************************************************************/ 1870// [PATH_MAX] is essentially a comment; char[] are char* after being passed 1871static int 1872get_locres_info(struct bootCaches *caches, char locRsrcDir[PATH_MAX], 1873 char prefPath[PATH_MAX], struct stat *prefsb, 1874 char locCacheDir[PATH_MAX], time_t *validModTime) 1875{ 1876 int rval = EOVERFLOW; // all other paths set rval 1877 time_t newestTime; 1878 struct stat sb; 1879 1880 if (!validModTime) { 1881 rval = EINVAL; LOGERRxlate("get_locres_info", NULL, rval); goto finish; 1882 } 1883 1884 // build localization sources directory path 1885 pathcpy(locRsrcDir, caches->root); 1886 pathcat(locRsrcDir, caches->locSource); 1887 // get localization sources directory timestamp 1888 if (stat(locRsrcDir, &sb)) { 1889 rval = errno; LOGERRxlate(locRsrcDir, NULL, rval); goto finish; 1890 } 1891 newestTime = sb.st_mtime; 1892 1893 // prefs file path & timestamp (if it exists) 1894 pathcpy(prefPath, caches->root); 1895 pathcat(prefPath, caches->locPref); 1896 if (stat(prefPath, prefsb) == 0) { 1897 if (prefsb->st_mtime > newestTime) { 1898 newestTime = prefsb->st_mtime; 1899 } 1900 } else { 1901 if (errno != ENOENT) { 1902 rval = errno; LOGERRxlate(prefPath, NULL, rval); goto finish; 1903 } 1904 } 1905 1906 // the cache directory must be one second newer than the 1907 // later of the prefs file and the source directory. 1908 *validModTime = newestTime + 1; 1909 1910 // build localized resources cache directory path 1911 pathcpy(locCacheDir, caches->root); 1912 pathcat(locCacheDir, caches->efiloccache->rpath); 1913 1914 rval = 0; 1915 1916finish: 1917 return rval; 1918} 1919 1920Boolean 1921check_loccache(struct bootCaches *caches) 1922{ 1923 Boolean needsupdate = false; // needsupdate defaults to "nope" 1924 struct stat prefsb, cachesb; 1925 char erpath[PATH_MAX]; 1926 char locRsrcDir[PATH_MAX], prefPath[PATH_MAX]; 1927 char locCacheDir[PATH_MAX]; 1928 time_t validModTime = 0; 1929 1930 if (!caches->efiloccache) goto finish; 1931 1932 // 9516786: loccache only needed if EFI Login plist is active 1933 pathcpy(erpath, caches->root); 1934 pathcat(erpath, caches->erpropcache->rpath); 1935 if (stat(erpath, &cachesb) == -1 && errno == ENOENT) { 1936 // not an error, there is no cache file on non-encrypted volumes 1937 goto finish; 1938 } 1939 1940 if (get_locres_info(caches, locRsrcDir, prefPath, &prefsb, 1941 locCacheDir, &validModTime)) { 1942 goto finish; // error logged by function 1943 } 1944 1945 if (stat(locCacheDir, &cachesb) == 0) { 1946 needsupdate = (cachesb.st_mtime != validModTime); 1947 } else if (errno == ENOENT) { 1948 needsupdate = true; 1949 } 1950 1951finish: 1952 return needsupdate; 1953} 1954 1955/***************************************************************************** 1956* rebuild_loccache() rebuilds the localized resources for EFI Login 1957*****************************************************************************/ 1958struct writeRsrcCtx { 1959 struct bootCaches *caches; 1960 char *locCacheDir; 1961 int *result; 1962}; 1963void 1964_writeResource(const void *value, void *ctxp) 1965{ 1966 int bsderr; 1967 CFDictionaryRef rsrc = (CFDictionaryRef)value; 1968 struct writeRsrcCtx *ctx = (struct writeRsrcCtx*)ctxp; 1969 struct bootCaches *caches = ctx->caches; 1970 1971 CFDataRef data; 1972 void *buf; 1973 ssize_t bufsz; 1974 CFStringRef nameStr; 1975 int fflags, fd = -1; 1976 char fname[PATH_MAX], fullp[PATH_MAX]; 1977 1978 1979 // extract data, filename & prepare for BSD syscalls 1980 bsderr = EFTYPE; 1981 if (!(data = CFDictionaryGetValue(rsrc, kEFILoginDataKey))) 1982 goto finish; 1983 if (!(buf = (void*)CFDataGetBytePtr(data))) 1984 goto finish; 1985 bufsz = (ssize_t)CFDataGetLength(data); 1986 if (bufsz < 0) goto finish; 1987 1988 if (!(nameStr = CFDictionaryGetValue(rsrc, kEFILoginFileNameKey))) 1989 goto finish; 1990 if(!CFStringGetFileSystemRepresentation(nameStr, fname, PATH_MAX)) 1991 goto finish; 1992 bsderr = EOVERFLOW; 1993 pathcpy(fullp, ctx->locCacheDir); 1994 pathcat(fullp, "/"); 1995 pathcat(fullp, fname); 1996 1997 // open & write! 1998 fflags = O_WRONLY | O_CREAT | O_TRUNC; // sopen() adds EXCL/NOFOL 1999 if (-1 == (fd = sopen(caches->cachefd, fullp, fflags, kCacheFileMode))) { 2000 bsderr = -1; 2001 goto finish; 2002 } 2003 if (write(fd, buf, bufsz) != bufsz) { 2004 bsderr = -1; 2005 goto finish; 2006 } 2007 2008 // success 2009 bsderr = 0; 2010 2011finish: 2012 if (bsderr) { 2013 *(ctx->result) = bsderr; 2014 } 2015 2016 if (fd != -1) close(fd); 2017 2018 return; 2019} 2020 2021// ahh, ye olde SysLang.h :] 2022// #define GLOBALPREFSFILE "/Library/Preferences/.GlobalPreferences.plist" 2023#define LANGSKEY CFSTR("AppleLanguages") // key in .GlobalPreferences 2024#define ENGLISHKEY CFSTR("en") 2025static int 2026_writeEFILoginResources(struct bootCaches *caches, 2027 char prefPath[PATH_MAX], struct stat *prefsb, 2028 char locCacheDir[PATH_MAX]) 2029{ 2030 int result; // all paths set an explicit result 2031 int gpfd = -1; 2032 CFDictionaryRef gprefs = NULL; 2033 CFMutableArrayRef locsList = NULL; // retained & released 2034 CFStringRef volStr = NULL; 2035 CFArrayRef blobList = NULL; 2036 2037 CFRange allEntries; 2038 struct writeRsrcCtx applyCtx = { caches, locCacheDir, &result }; 2039 2040 // can't operate without EFILogin.framework function 2041 // (XX as of Zin12A190, this function is not properly decorated ...) 2042 if (EFILoginCopyInterfaceGraphics == NULL) { 2043 result = ESHLIBVERS; 2044 goto finish; 2045 } 2046 2047 // attempt to get AppleLanguages out of .GlobalPreferences 2048 if ((gpfd = sopen(caches->cachefd, prefPath, O_RDONLY, 0)) >= 0 && 2049 (gprefs = copy_dict_from_fd(gpfd, prefsb)) && 2050 (locsList=(CFMutableArrayRef)CFDictionaryGetValue(gprefs,LANGSKEY)) && 2051 CFGetTypeID(locsList) == CFArrayGetTypeID()) { 2052 CFRetain(locsList); 2053 } else { 2054 // create a new array containing the default "en" (locsList !retained) 2055 CFRange range = { 0, 1 }; 2056 locsList = CFArrayCreateMutable(nil, 1, &kCFTypeArrayCallBacks); 2057 if (!locsList) { 2058 result = ENOMEM; 2059 goto finish; 2060 } 2061 CFArrayAppendValue(locsList, ENGLISHKEY); 2062 if (!CFArrayContainsValue(locsList, range, ENGLISHKEY)) { 2063 result = ENOMEM; // ECFFAILED :P 2064 goto finish; 2065 } 2066 } 2067 2068 // generate all resources 2069 volStr = CFStringCreateWithFileSystemRepresentation(nil, caches->root); 2070 if (!volStr || 2071 !(blobList = EFILoginCopyInterfaceGraphics(locsList, volStr))) { 2072 result = ENOMEM; 2073 goto finish; 2074 } 2075 2076 // write everything out 2077 result = 0; // applier only modifies on error 2078 allEntries = CFRangeMake(0, CFArrayGetCount(blobList)); 2079 CFArrayApplyFunction(blobList, allEntries, _writeResource, &applyCtx); 2080 if (result) goto finish; 2081 2082 // success! 2083 result = 0; 2084 2085finish: 2086 if (blobList) CFRelease(blobList); 2087 if (volStr) CFRelease(volStr); 2088 if (locsList) CFRelease(locsList); 2089 if (gprefs) CFRelease(gprefs); 2090 if (gpfd != -1) close(gpfd); 2091 2092 return result; 2093} 2094 2095int 2096rebuild_loccache(struct bootCaches *caches) 2097{ 2098 int errnum, result = ELAST + 1; 2099 struct stat cachesb, prefsb; 2100 char locRsrcDir[PATH_MAX], prefPath[PATH_MAX]; 2101 char locCacheDir[PATH_MAX]; 2102 time_t validModTime = 0; 2103 int fd = -1; 2104 struct timeval times[2]; 2105 2106 // prefsb.st_size = 0; // Analyzer doesn't check get_locres_info(&prefsb) 2107 bzero(&prefsb, sizeof(prefsb)); // and doesn't know bzero sets st_size = 0 2108 if ((errnum = get_locres_info(caches, locRsrcDir, prefPath, &prefsb, 2109 locCacheDir, &validModTime))) { 2110 result = errnum; goto finish; // error logged by function 2111 } 2112 2113 // empty out locCacheDir ... 2114 /* This cache is an optional part of RPS, thus it is okay to 2115 destroy on failure (leaving it empty risks "right" timestamps). */ 2116 if (sdeepunlink(caches->cachefd, locCacheDir) == -1 && errno == EROFS) { 2117 result = errno; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2118 } 2119 if ((errnum = sdeepmkdir(caches->cachefd,locCacheDir,kCacheDirMode))) { 2120 result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2121 } 2122 2123 // actually write resources! 2124 errnum = _writeEFILoginResources(caches, prefPath, &prefsb, locCacheDir); 2125 if (errnum) { 2126 (void)sdeepunlink(caches->cachefd, locCacheDir); 2127 result = errnum; 2128 LOGERRxlate("_writeEFILoginResources", NULL, result); 2129 goto finish; 2130 } 2131 2132 // get current times (keeping access, overwriting mod) 2133 if ((errnum = stat(locCacheDir, &cachesb))) { 2134 result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2135 } 2136 cachesb.st_mtime = validModTime; 2137 TIMESPEC_TO_TIMEVAL(×[0], &cachesb.st_atimespec); 2138 TIMESPEC_TO_TIMEVAL(×[1], &cachesb.st_mtimespec); 2139 if ((errnum = _sutimes(caches->cachefd, locCacheDir, O_RDONLY, times))) { 2140 result = errnum; LOGERRxlate(locCacheDir, NULL, result); goto finish; 2141 } 2142 2143 // success 2144 result = 0; 2145 2146finish: 2147 if (fd != -1) close(fd); 2148 2149 return result; 2150} 2151 2152 2153 2154/***************************************************************************** 2155* hasBRBoots lets you know if a volume has boot partitions and if it's on GPT 2156* no error reporting except residual errno 2157*****************************************************************************/ 2158Boolean 2159hasBootRootBoots(struct bootCaches *caches, CFArrayRef *auxPartsCopy, 2160 CFArrayRef *dataPartsCopy, Boolean *isAPM) 2161{ 2162 CFDictionaryRef binfo = NULL; 2163 Boolean rval = false, apm = false; 2164 CFArrayRef dparts = NULL, bparts = NULL; 2165 char stack_bsdname[DEVMAXPATHSIZE]; 2166 char * lookup_bsdname = caches->bsdname; 2167 CFArrayRef dataPartitions = NULL; // do not release; 2168 size_t fullLen; 2169 char fulldev[DEVMAXPATHSIZE]; 2170#if DEBUG_REGISTRY 2171 char parentdevname[DEVMAXPATHSIZE]; 2172 uint32_t partitionNum; 2173 BLPartitionType partitionType; 2174#endif 2175 2176 /* Get the BL info about partitions & such. 2177 */ 2178 if (BLCreateBooterInformationDictionary(NULL, lookup_bsdname, &binfo)) 2179 goto finish; 2180 bparts = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey); 2181 dparts = CFDictionaryGetValue(binfo, kBLDataPartitionsKey); 2182 if (!bparts || !dparts) goto finish; 2183 2184 /***** 2185 * Now, for a GPT check, use one of the data partitions given by the above 2186 * call to BLCreateBooterInformationDictionary(). 2187 */ 2188 dataPartitions = CFDictionaryGetValue(binfo, kBLDataPartitionsKey); 2189 if (dataPartitions && CFArrayGetCount(dataPartitions)) { 2190 CFStringRef dpBsdName = CFArrayGetValueAtIndex(dataPartitions, 0); 2191 2192 if (dpBsdName) { 2193 if (!CFStringGetFileSystemRepresentation(dpBsdName, stack_bsdname, 2194 sizeof(stack_bsdname))) 2195 goto finish; 2196 lookup_bsdname = stack_bsdname; 2197 } 2198 } 2199 2200 /* Get the BL info about the partition type (that's all we use, but 2201 * we have to pass in valid buffer pointers for all the rest). 2202 */ 2203 fullLen = snprintf(fulldev, sizeof(fulldev), "/dev/%s", lookup_bsdname); 2204 if (fullLen >= sizeof(fulldev)) { 2205 goto finish; 2206 } 2207 2208#if DEBUG_REGISTRY 2209 // doesn't work on watson w/USB disk?? 2210 if (BLGetParentDeviceAndPartitionType(NULL /* context */, 2211 fulldev, parentdevname, &partitionNum, &partitionType)) 2212 { 2213 goto finish; 2214 } 2215 if (partitionType == kBLPartitionType_APM) { 2216 apm = true; 2217 } 2218#endif 2219 2220 // 5158091 / 6413843: 10.4.x APM Apple_Boot's aren't BootRoot 2221 // Boot!=Root was introduced in 10.4.7 for *Intel only*. 2222 // BootX didn't learn about Boot!=Root until 10.5 (mkext2 era). 2223 // XX 10740646 tracks reviewing / dropping ppc support 2224 // The check is APM-only because ppc only booted APM. 2225 if (apm) { 2226 CFDictionaryRef pbDict, mk2Dict, kcDict; 2227 2228 // i.e. Leopard had BootX; SnowLeopard has mkext2 2229 pbDict = CFDictionaryGetValue(caches->cacheinfo, kBCPostBootKey); 2230 if (!pbDict || CFGetTypeID(pbDict) != CFDictionaryGetTypeID()) goto finish; 2231 2232 kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV1Key); 2233 if (!kcDict) 2234 kcDict = CFDictionaryGetValue(pbDict, kBCKernelcacheV2Key); 2235 mk2Dict = CFDictionaryGetValue(pbDict, kBCMKext2Key); 2236 2237 // if none of these indicates a more modern OS, we skip 2238 // XX should the ofbooter path check be != '\0' ? 2239 // (then we could drop the kcDict check?) 2240 if (!kcDict && !mk2Dict && caches->ofbooter.rpath[0] == '\0') 2241 goto finish; 2242 } 2243 2244 // check for helper partitions 2245 rval = (CFArrayGetCount(bparts) > 0); 2246 2247finish: 2248 // out parameters set if provided 2249 if (auxPartsCopy) { 2250 if (bparts) CFRetain(bparts); 2251 *auxPartsCopy = bparts; 2252 } 2253 if (dataPartsCopy) { 2254 if (dparts) CFRetain(dparts); 2255 *dataPartsCopy = dparts; 2256 } 2257 if (isAPM) *isAPM = apm; 2258 2259 // cleanup 2260 if (binfo) CFRelease(binfo); 2261 2262 return rval; 2263} 2264 2265CFArrayRef 2266BRCopyActiveBootPartitions(CFURLRef volRoot) 2267{ 2268 CFArrayRef bparts, rval = NULL; 2269 char path[PATH_MAX], *bsdname; 2270 struct statfs sfs; 2271 CFDictionaryRef binfo = NULL; 2272 2273 if (!volRoot) goto finish; 2274 2275 // get BSD Name of volRoot 2276 if (!CFURLGetFileSystemRepresentation( 2277 volRoot, false, (UInt8*)path, sizeof(path))) { 2278 goto finish; 2279 } 2280 if (-1 == statfs(path, &sfs)) goto finish; 2281 if (strlen(sfs.f_mntfromname) < sizeof(_PATH_DEV)) { 2282 goto finish; 2283 } 2284 bsdname = sfs.f_mntfromname + (sizeof(_PATH_DEV)-1); 2285 2286 // have libbless provide the helper partitions 2287 // (doesn't vet them as much as hasBootRootBoots()) 2288 if (BLCreateBooterInformationDictionary(NULL, bsdname, &binfo)) 2289 goto finish; 2290 bparts = CFDictionaryGetValue(binfo, kBLAuxiliaryPartitionsKey); 2291 2292 // success -> retain sub-dictionary for caller 2293 if (bparts && CFArrayGetCount(bparts)) { 2294 rval = CFRetain(bparts); 2295 } 2296 2297finish: 2298 if (binfo) CFRelease(binfo); 2299 2300 return rval; 2301} 2302 2303/******************************************************************************* 2304*******************************************************************************/ 2305void _daDone(DADiskRef disk __unused, DADissenterRef dissenter, void *ctx) 2306{ 2307 if (dissenter) 2308 CFRetain(dissenter); 2309 *(DADissenterRef*)ctx = dissenter; 2310 CFRunLoopStop(CFRunLoopGetCurrent()); // assumed okay even if not running 2311} 2312 2313/******************************************************************************* 2314* We don't want to wind up invoking kextcache using assembled paths that have 2315* repeating slashes. Note that paths in bootcaches.plist are absolute so 2316* appending them should always put a slash in as expected. 2317*******************************************************************************/ 2318static void removeTrailingSlashes(char * path) 2319{ 2320 size_t pathLength = strlen(path); 2321 size_t scanIndex = pathLength - 1; 2322 2323 if (!pathLength) return; 2324 2325 while (path[scanIndex] == '/') { 2326 path[scanIndex] = '\0'; 2327 if (scanIndex == 0) break; 2328 scanIndex--; 2329 } 2330 2331 return; 2332} 2333 2334 2335 2336/****************************************************************************** 2337 * updateMount() remounts the volume with the requested flags! 2338 *****************************************************************************/ 2339int 2340updateMount(mountpoint_t mount, uint32_t mntgoal) 2341{ 2342 int result = ELAST + 1; // 3/22/12: all paths set result 2343 DASessionRef session = NULL; 2344 CFStringRef toggleMode = CFSTR("updateMountMode"); 2345 CFURLRef volURL = NULL; 2346 DADiskRef disk = NULL; 2347 DADissenterRef dis = (void*)kCFNull; 2348 CFStringRef mountargs[] = { 2349 CFSTR("update"), 2350 ( mntgoal & MNT_NODEV ) ? CFSTR("nodev") : CFSTR("dev"), 2351 ( mntgoal & MNT_NOEXEC ) ? CFSTR("noexec") : CFSTR("exec"), 2352 ( mntgoal & MNT_NOSUID ) ? CFSTR("nosuid") : CFSTR("suid"), 2353 ( mntgoal & MNT_RDONLY ) ? CFSTR("rdonly") : CFSTR("rw"), 2354 ( mntgoal & MNT_DONTBROWSE ) ? CFSTR("nobrowse") : CFSTR("browse"), 2355 (mntgoal & MNT_IGNORE_OWNERSHIP) ? CFSTR("noowners") : CFSTR("owners"), 2356 NULL }; 2357 2358 // same 'dis' logic as mountBoot in update_boot.c 2359 if (!(session = DASessionCreate(nil))) { 2360 result = ENOMEM; goto finish; 2361 } 2362 DASessionScheduleWithRunLoop(session, CFRunLoopGetCurrent(), toggleMode); 2363 if (!(volURL=CFURLCreateFromFileSystemRepresentation(nil, (void*)mount, 2364 strlen(mount), true))) { 2365 result = ENOMEM; goto finish; 2366 } 2367 if (!(disk = DADiskCreateFromVolumePath(nil, session, volURL))) { 2368 result = ENOMEM; goto finish; 2369 } 2370 DADiskMountWithArguments(disk, NULL, kDADiskMountOptionDefault, _daDone, 2371 &dis, mountargs); 2372 2373 while (dis == (void*)kCFNull) { 2374 CFRunLoopRunInMode(toggleMode, 0, true); // _daDone updates 'dis' 2375 } 2376 if (dis) { 2377 result = DADissenterGetStatus(dis); // XX errno |= unix_err() 2378 if (result == 0) result = ELAST + 1; 2379 goto finish; 2380 } 2381 2382 result = 0; 2383 2384finish: 2385 if (dis && dis != (void*)kCFNull) CFRelease(dis); 2386 if (disk) CFRelease(disk); 2387 if (session) CFRelease(session); 2388 if (volURL) CFRelease(volURL); 2389 2390 if (result) { 2391 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 2392 "Warning: couldn't update %s->f_flags to %#x: error %#x", mount, 2393 mntgoal, result); 2394 } 2395 2396 return result; 2397} 2398 2399/****************************************************************************** 2400 * returns the result of fork/exec (negative on error; pid on success) 2401 * a (waited-for) helper exit status will also be returned (see fork_program.c) 2402 * - 'force' -> -f to ignore bootstamps (13784516 removed only use) 2403 *****************************************************************************/ 2404// kextcache -u helper sets up argv 2405pid_t launch_rebuild_all(char * rootPath, Boolean force, Boolean wait) 2406{ 2407 pid_t rval = -1; 2408 int argc, argi = 0; 2409 char **kcargs = NULL; 2410 2411 // argv[0] '-F' '-u' root -f ? NULL 2412 argc = 1 + 1 + 1 + 1 + (force == true) + 1; 2413 kcargs = malloc(argc * sizeof(char*)); 2414 if (!kcargs) goto finish; 2415 2416 kcargs[argi++] = "/usr/sbin/kextcache"; 2417 // fork_program(wait=false) also sets IOPOL_THROTTLE while spawning 2418 kcargs[argi++] = "-F"; // lower priority within kextcache 2419 if (force) { 2420 kcargs[argi++] = "-f"; 2421 } 2422 kcargs[argi++] = "-u"; 2423 kcargs[argi++] = rootPath; 2424 // kextcache reads bc.plist so nothing more needed 2425 2426 kcargs[argi] = NULL; // terminate the list 2427 2428 /* wait:false means the return value is <0 for fork/exec failures and 2429 * the pid of the forked process if >0. 2430 */ 2431 rval = fork_program(kcargs[0], kcargs, wait); 2432 2433finish: 2434 if (kcargs) free(kcargs); 2435 2436 if (rval < 0) 2437 OSKextLog(/* kext */ NULL, 2438 kOSKextLogErrorLevel | kOSKextLogIPCFlag, 2439 "Error launching kextcache -u."); 2440 2441 return rval; 2442} 2443 2444/******************************************************************************* 2445*******************************************************************************/ 2446struct nameAndUUID { 2447 uint32_t nbytes; 2448 struct attrreference nameref; 2449 uuid_t uuid; 2450 char namedata[NAME_MAX+1]; 2451}; 2452int 2453copyVolumeInfo(const char *vol_path, uuid_t *vol_uuid, CFStringRef *cslvf_uuid, 2454 char vol_bsd[DEVMAXPATHSIZE], char vol_name[NAME_MAX]) 2455{ 2456 int bsderr, rval = ENODEV; 2457 struct nameAndUUID attrs; 2458 struct attrlist attrdesc = { ATTR_BIT_MAP_COUNT, 0, 0, ATTR_VOL_INFO | 2459 ATTR_VOL_NAME | ATTR_VOL_UUID, 0, 0, 0 }; 2460 struct statfs sfs; 2461 char *bsdname; 2462 io_object_t ioObj = IO_OBJECT_NULL; 2463 CFTypeRef regEntry = NULL; 2464 2465 // get basic data 2466 // (don't worry about FSOPT_REPORT_FULLSIZE; NAME_MAX+1 is plenty :] 2467 if ((bsderr=getattrlist(vol_path, &attrdesc, &attrs, sizeof(attrs), 0)) 2468 || attrs.nbytes >= sizeof(attrs)) { 2469 rval = errno; goto finish; 2470 } 2471 if (vol_bsd || cslvf_uuid) { 2472 if ((bsderr = statfs(vol_path, &sfs))) { 2473 rval = errno; goto finish; 2474 } 2475 bsdname = sfs.f_mntfromname; 2476 if (strncmp(bsdname, _PATH_DEV, strlen(_PATH_DEV)) == 0) { 2477 bsdname += strlen(_PATH_DEV); 2478 } 2479 } 2480 2481 2482 // handle UUID if requested 2483 if (vol_uuid) { 2484 memcpy(*vol_uuid, attrs.uuid, sizeof(uuid_t)); 2485 } 2486 2487 // CoreStorage UUID if requested 2488 if (cslvf_uuid) { 2489 CFDictionaryRef matching; // IOServiceGetMatchingServices() releases 2490 matching = IOBSDNameMatching(kIOMasterPortDefault, 0, bsdname); 2491 if (!matching) { 2492 rval = ENOMEM; goto finish; 2493 } 2494 ioObj = IOServiceGetMatchingService(kIOMasterPortDefault, matching); 2495 matching = NULL; // IOServiceGetMatchingService() released 2496 if (ioObj == IO_OBJECT_NULL) { 2497 rval = ENODEV; goto finish; 2498 } 2499 regEntry = IORegistryEntryCreateCFProperty(ioObj, 2500 CFSTR(kCoreStorageLVFUUIDKey), nil, 0); 2501 if (regEntry && CFGetTypeID(regEntry) == CFStringGetTypeID()) { 2502 // retain the result (regEntry released below) 2503 *cslvf_uuid = (CFStringRef)CFRetain(regEntry); 2504 } else { 2505 *cslvf_uuid = NULL; 2506 } 2507 } 2508 2509 // BSD Name 2510 if (vol_bsd) { 2511 if (strlcpy(vol_bsd, bsdname, DEVMAXPATHSIZE) >= DEVMAXPATHSIZE) { 2512 rval = EOVERFLOW; goto finish; 2513 } 2514 } 2515 2516 // volume name 2517 if (vol_name) { 2518 char *volname = (char*)&attrs.nameref + attrs.nameref.attr_dataoffset; 2519 (void)strlcpy(vol_name, volname, NAME_MAX); 2520 } 2521 2522 rval = 0; 2523 2524finish: 2525 if (rval) { 2526 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 2527 "%s: %s", vol_path, strerror(rval)); 2528 } 2529 2530 if (regEntry) CFRelease(regEntry); 2531 if (ioObj != IO_OBJECT_NULL) IOObjectRelease(ioObj); 2532 // matching consumed by IOServiceGetMatchingService() 2533 2534 return rval; 2535} 2536