1/* 2 * Copyright (c) 2006-2012 Apple 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 * FILE: update_boot.c 25 * AUTH: Soren Spies (sspies) 26 * DATE: 8 June 2006 27 * DESC: implement 'kextcache -u' (copying to Apple_Boot partitions) 28 * 29 */ 30 31#include <bless.h> 32#include <miscfs/devfs/devfs.h> // UID_ROOT, GID_WHEEL 33#include <fcntl.h> 34#include <hfs/hfs_mount.h> // hfs_mount_args 35#include <libgen.h> 36#include <mach/mach_error.h> 37#include <mach/mach_port.h> // mach_port_allocate() 38#include <servers/bootstrap.h> 39#include <sysexits.h> 40#include <sys/errno.h> 41#include <sys/param.h> 42#include <sys/mount.h> 43#include <sys/xattr.h> 44#include <unistd.h> 45 46#include <IOKit/kext/kextmanager_types.h> 47#include <IOKit/kext/OSKextPrivate.h> 48#include <IOKit/kext/kextmanager_types.h> 49#include <IOKit/kext/kextmanager_mig.h> 50#include <IOKit/IOKitLib.h> 51#include <IOKit/IOBSD.h> 52#include <IOKit/storage/IOMedia.h> 53#include <IOKit/storage/IOPartitionScheme.h> 54#include <MediaKit/GPTTypes.h> 55#include <bootfiles.h> 56#include <CoreFoundation/CoreFoundation.h> 57#include <DiskArbitration/DiskArbitration.h> 58#include <DiskArbitration/DiskArbitrationPrivate.h> 59 60#include "bootcaches.h" 61#include "bootroot_internal.h" // includes bootroot.h 62#include "fork_program.h" 63#include "safecalls.h" 64#include "kext_tools_util.h" 65 66 67/****************************************************************************** 68* File-Globals 69******************************************************************************/ 70static mach_port_t sBRUptLock = MACH_PORT_NULL; 71static uuid_t s_vol_uuid; // XX not threadsafe (10561671) 72static mach_port_t sKextdPort = MACH_PORT_NULL; 73 74 75/****************************************************************************** 76* Types 77******************************************************************************/ 78enum bootReversions { 79 nothingSerious = 0, 80 noLabel, // 1 81 copyingOFBooter, // 2 82 copyingEFIBooter, // 3 83 copiedBooters, // 4 84 activatingOFBooter, // 5 85 activatingEFIBooter, // 6 86 activatedBooters, // 7 87}; 88 89enum blessIndices { 90 kSystemFolderIdx = 0, 91 kEFIBooterIdx = 1 92 // sBLSetBootFinderInfo() preserves other values 93}; 94 95const char * bootReversionsStrings[] = { 96 NULL, // unused 97 "Label deleted", 98 "Unlinking and copying BootX booter", 99 "Unlinking and copying EFI booter", 100 "Booters copied", 101 "Activating BootX", 102 "Activating EFI booter", 103 "Booters activated" 104}; 105 106 107// for Apple_Boot update 108struct updatingVol { 109 struct bootCaches *caches; // parsed bootcaches.plist data 110 char srcRoot[PATH_MAX]; // src for boot caches as char[] 111 uuid_string_t host_uuid; // initialRoot's UUID 112 CFDictionaryRef bpoverrides; // provided Boot.plist overrides 113 CFDictionaryRef csfdeprops; // CSFDE property cache data (!encr) 114 char flatTarget[PATH_MAX]; // indy <helper>/<target>, min-RPS 115 OSKextLogSpec warnLogSpec; // flags for file access warnings 116 OSKextLogSpec errLogSpec; // flags for file access errors 117 CFArrayRef boots; // BSD Names of Apple_Boot partitions 118 DASessionRef dasession; // diskarb handle 119 BRBlessStyle blessSpec; // support non-default BR..ToDir() 120 BRUpdateOpts_t opts; // "how hard to try" & other flags 121 122 // default to false for the common kextcache -u case 123 Boolean doRPS, doMisc, doBooters; // what needs updating 124 Boolean doSanitize, cleanOnceDir; // how to cleanse each helper 125 Boolean customSource, customDest; // vs. default B!=R setup 126 Boolean useOnceDir; // copy to com.apple.boot.once 127 128 // updated as each Apple_Boot is updated 129 int bootIdx; // which helper are we updating 130 enum bootReversions changestate; // track changes to roll back 131 char bsdname[DEVMAXPATHSIZE]; // bsdname of Apple_Boot 132 DADiskRef curBoot; // and matching diskarb ref 133 char curMount[MNAMELEN]; // path to current boot mountpt 134 int curbootfd; // Sec: handle to curMount 135 char dstdir[PATH_MAX]; // full path to main dest. 136 char efidst[PATH_MAX], ofdst[PATH_MAX]; 137 Boolean onAPM; // tweak support based on pmap 138 Boolean detectedRecovery; // seen com.apple.recovery.boot? 139}; 140 141 142/****************************************************************************** 143* Definitions 144******************************************************************************/ 145#define COMPILE_TIME_ASSERT(pred) switch(0){case 0:case pred:;} 146 147// for non-RPS content, including booters 148#define OLDEXT ".old" 149#define NEWEXT ".new" 150#define SCALE_2xEXT "_2x" 151#define CONTENTEXT ".contentDetails" 152#define kBRRootUUIDFile ".root_uuid" 153#define kBRBootOnceDir "/com.apple.boot.once" 154 155// NOTE: These strings must be the same length, or code in ucopyRPS will break! 156// There is a compile time assert in the function to this effect. 157#define BOOTPLIST_NAME "com.apple.Boot.plist" 158#define BOOTPLIST_APM_NAME "com.apple.boot.plist" 159 160 161/****************************************************************************** 162* Helpers 163******************************************************************************/ 164 165// diskarb 166static int mountBoot(struct updatingVol *up); 167static void unmountBoot(struct updatingVol *up); 168 169// ucopy = unlink & copy 170// no race for RPS, so install it first 171static int ucopyRPS(struct updatingVol *s); // nuke/copy to inactive 172// the label files (for example) have no fallback, .new is harmless 173// XX ucopy"Preboot/Firmware" 174static int ucopyMisc(struct updatingVol *s); // use/overwrite .new names 175// booters have fallback paths, but originals might be broken 176static int ucopyBooters(struct updatingVol *s); // nuke/copy booters (inact) 177// no label -> hint of indeterminate state (label key in plist?) 178static int moveLabels(struct updatingVol *s); // move aside 179static int nukeBRLabels(struct updatingVol *s); // byebye (all?) 180// booters have worst critical:fragile ratio (point of departure) 181static int activateBooters(struct updatingVol *s); // bless new names 182// and the RPS data needed for booting 183static int activateRPS(struct updatingVol *s); // leap-frog w/rename() 184// finally, the label (indicating a working system via this helper partition) 185// XX activate"FirmwarePaths/postboot" 186static int activateMisc(struct updatingVol *s); // rename .new / label 187// and now that we're safe 188static int nukeFallbacks(struct updatingVol *s); 189static int eraseRPS(struct updatingVol *up, char *toErase); 190static int addHostVolInfo(struct updatingVol *up, CFURLRef hostVol, 191 CFDictionaryRef bootPrefOverrides, CFURLRef targetStr, 192 CFStringRef pickerLabel); 193static CFStringRef copy_kcsuffix(void); 194 195// cleanup routines (RPS is the last step; activateMisc handles label) 196static int revertState(struct updatingVol *up); 197 198/* Chain of Trust 199 * Our goal is to do anything the bootcaches.plist says, but only to that vol. 200 * #1 we only pay attention to root-owned bootcaches.plist files 201 * #2 we get an fd to the bootcaches.plist [trust is here] 202// * #3 we validate the bc.plist fd after getting an fd to the volume's root 203 * #4 we use stored bsdname for libbless 204 * #5 we validate cachefd after the call to bless [trust -> bsdname] 205 * #6 we get curbootfd after each apple_boot mount 206 * #7 we validate cachefd after the call [trust -> curfd] 207 * #8 operations take an fd limiting their scope to the mount 208 */ 209 210// XX should probably rename to all-caps 211// seed errno since strlxxx routines do not set it. This will make 212// downstream error messages more meaningful (since we're often logging the 213// errno value and message). 214#define pathcpy(dst, src) do { \ 215 Boolean useErrno = (errno == 0); \ 216 if (useErrno) errno = ENAMETOOLONG; \ 217 if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \ 218 if (useErrno) errno = 0; \ 219 } while(0) 220#define pathcat(dst, src) do { \ 221 Boolean useErrno = (errno == 0); \ 222 if (useErrno) errno = ENAMETOOLONG; \ 223 if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) goto finish; \ 224 if (useErrno) errno = 0; \ 225 } while(0) 226#define makebootpath(path, rpath) do { \ 227 pathcpy(path, up->curMount); \ 228 if (up->useOnceDir) { \ 229 pathcat(path, kBRBootOnceDir); \ 230 } \ 231 if (up->useOnceDir || up->flatTarget[0]) { \ 232 pathcat(path, up->flatTarget); \ 233 /* XX 10561671: basename unsafe */ \ 234 pathcat(path, "/"); \ 235 pathcat(path, basename(rpath)); \ 236 } else { \ 237 pathcat(path, rpath); \ 238 } \ 239 } while(0) 240 241// continue versions 242#define PATHCPYcont(dst, src) do { \ 243 if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) continue; \ 244 } while(0) 245#define PATHCATcont(dst, src) do { \ 246 if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) continue; \ 247 } while(0) 248 249// break versions 250#define PATHCPYbreak(dst, src) do { \ 251 if (strlcpy(dst, src, PATH_MAX) >= PATH_MAX) break; \ 252 } while(0) 253#define PATHCATbreak(dst, src) do { \ 254 if (strlcat(dst, src, PATH_MAX) >= PATH_MAX) break; \ 255 } while(0) 256 257#define LOGERRxlate(up, ctx1, ctx2, errval) do { \ 258 char *c2cpy = ctx2, ctx[256]; \ 259 if (ctx2 != NULL) { \ 260 snprintf(ctx, sizeof(ctx), "%s: %s", ctx1, c2cpy); \ 261 } else { \ 262 snprintf(ctx, sizeof(ctx), "%s", ctx1); \ 263 } \ 264 /* if necessary, modify passed-in argument so errno is returned */ \ 265 if (errval == -1) errval = errno; \ 266 OSKextLog(/* kext */ NULL, up->errLogSpec, \ 267 "%s: %s", ctx, strerror(errval)); \ 268 } while(0) 269 270 271// XX there is overlap between errno values and sysexits 272static int 273getExitValueFor(errval) 274{ 275 int rval; 276 277 switch (errval) { 278 case ELAST + 1: 279 rval = EX_SOFTWARE; 280 break; 281 case EPERM: 282 rval = EX_NOPERM; 283 break; 284 case EAGAIN: 285 case ENOLCK: 286 rval = EX_OSERR; 287 break; 288 case -1: 289 switch (errno) { 290 case EIO: 291 rval = EX_IOERR; 292 break; 293 default: 294 rval = EX_OSERR; 295 break; 296 } 297 break; 298 default: 299 rval = errval; 300 } 301 302 return rval; 303} 304 305// TM should no longer add to Apple_Boot partitions (8992773) 306#define MOBILEBACKUPS_DIR "/.MobileBackups" 307#define MDS_BULWARK "/.metadata_never_index" 308#define MDS_DIR "/.Spotlight-V100" 309#define FSEVENTS_BULWARK "/.fseventsd/no_log" 310#define FSEVENTS_DIR "/.fseventsd" 311#define NETBOOT_SHADOW "/.com.apple.NetBootX/shadowfile" 312static int 313sanitizeBoot(struct updatingVol *up) 314{ 315 int lastErrno = 0; // best effort 316 int fd; 317 struct statfs sfs; 318 char bloatp[PATH_MAX], blockp[PATH_MAX]; 319 Boolean blockMissing = true; 320 struct stat sb; 321 322 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 323 "Removing unnecessary bloat."); 324 325 // X if the size similar to a user's data volume, don't scrub 326 if ((fstatfs(up->curbootfd, &sfs) == 0) && 327 (sfs.f_blocks * sfs.f_bsize > 1ULL<<32)) { 328 goto finish; 329 } 330 331 // ensure root ownership of the helper root (opened in mountBoot()) 332 if ((fstat(up->curbootfd, &sb) == 0) && 333 (sb.st_uid != UID_ROOT || sb.st_gid != GID_WHEEL)) { 334 if (fchown(up->curbootfd, UID_ROOT, GID_WHEEL) == -1) { 335 lastErrno = errno; 336 } 337 } 338 339 // Time Machine 340 makebootpath(bloatp, MOBILEBACKUPS_DIR); 341 if (0 == (stat(bloatp, &sb))) { 342 if (sdeepunlink(up->curbootfd, bloatp) == -1) { 343 lastErrno = errno; 344 } 345 } 346 347 // NetBoot shadow file (see 11535905) 348 makebootpath(bloatp, NETBOOT_SHADOW); 349 if (0 == (stat(bloatp, &sb))) { 350 if (sdeepunlink(up->curbootfd, bloatp) == -1) { 351 lastErrno = errno; 352 } 353 } 354 355 // Spotlight 356 makebootpath(blockp, MDS_BULWARK); 357 if (-1 == stat(blockp, &sb) && errno == ENOENT) { 358 fd = sopen(up->curbootfd, blockp, O_CREAT, kCacheFileMode); 359 if (fd == -1) { 360 lastErrno = errno; 361 } else { 362 close(fd); 363 } 364 } 365 makebootpath(bloatp, MDS_DIR); 366 if (0 == (stat(bloatp, &sb))) { 367 if (sdeepunlink(up->curbootfd, bloatp) == -1) { 368 lastErrno = errno; 369 } 370 } 371 372 // FSEvents has its antithesis inside its directory :P 373 // we'll assume if no_log is present, that there's no cruft 374 makebootpath(bloatp, FSEVENTS_DIR); 375 makebootpath(blockp, FSEVENTS_BULWARK); 376 if (0 == (stat(bloatp, &sb))) { 377 if (-1 == stat(blockp, &sb) && errno == ENOENT) { 378 // no bulwark, so nuke the whole thing 379 if (sdeepunlink(up->curbootfd, bloatp) == -1) { 380 lastErrno = errno; 381 } 382 } else { 383 blockMissing = false; 384 } 385 } 386 387 if (blockMissing) { 388 // then recreate the directory and the "stay away" file 389 if (sdeepmkdir(up->curbootfd, bloatp, kCacheDirMode) == -1) { 390 lastErrno = errno; 391 } 392 fd = sopen(up->curbootfd, blockp, O_CREAT, kCacheFileMode); 393 if (fd == -1) { 394 lastErrno = errno; 395 } else { 396 close(fd); 397 } 398 } 399 400 // no accumulated errors -> success 401 402finish: 403 if (lastErrno) { 404 OSKextLog(NULL, up->warnLogSpec, "sanitizeBoot(): Warning: %s", 405 strerror(lastErrno)); 406 } 407 408 return lastErrno; 409} 410 411 412/****************************************************************************** 413 * checkBootContents 414 * Look for missing files in the current Apple_boot (helper) partition. If 415 * anything seems amiss, force an appropriate update. 416******************************************************************************/ 417static void 418checkBootContents(struct updatingVol *up) 419{ 420 unsigned i; 421 char srcpath[PATH_MAX], dstpath[PATH_MAX]; 422 struct stat sb; 423 424 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 425 "Looking for missing files."); 426 427 // check for non-standard boot bits in this helper partition 428 if (notBRDefault(up->curMount, NULL)) { 429 up->doRPS = up->doBooters = up->doMisc = true; 430 goto finish; 431 } 432 433 /* looking for missing .VolumeIcon.icns, SystemVersion.plist, 434 * PlatformSupport.plist, .disk_label, etc 435 */ 436 if (!up->doMisc) { 437 for (i = 0; i < up->caches->nmisc; i++) { 438 pathcpy(srcpath, up->caches->root); 439 pathcat(srcpath, up->caches->miscpaths[i].rpath); 440 makebootpath(dstpath, up->caches->miscpaths[i].rpath); 441 442 // If in the root volume but not the helper, force update 443 if (stat(srcpath, &sb) == 0) { 444 // source file exists, now check on Apple_Boot 445 if (stat(dstpath, &sb) != 0 && errno == ENOENT) { 446 // missing file, force an update 447 up->doMisc = true; 448 OSKextLog(nil,kOSKextLogFileAccessFlag|kOSKextLogBasicLevel, 449 "Helper partition missing misc files, forcing update"); 450 break; 451 } 452 } 453 } 454 } 455 456 // now look for boot.efi 457 if (!up->doBooters) { 458 if (up->caches->efibooter.rpath[0]) { 459 makebootpath(dstpath, up->caches->efibooter.rpath); 460 if (stat(dstpath, &sb) != 0 && errno == ENOENT) { 461 // missing file, force an update 462 up->doBooters = true; 463 OSKextLog(NULL, kOSKextLogFileAccessFlag|kOSKextLogBasicLevel, 464 "Helper partition missing EFI booter, forcing update"); 465 goto finish; 466 } 467 } 468 // OF booter deserves love too :) 469 if (up->caches->ofbooter.rpath[0]) { 470 makebootpath(dstpath, up->caches->ofbooter.rpath); 471 if (stat(dstpath, &sb) != 0 && errno == ENOENT) { 472 // missing file, force an update 473 up->doBooters = true; 474 OSKextLog(NULL, kOSKextLogFileAccessFlag|kOSKextLogBasicLevel, 475 "Helper partition missing OF booter, forcing update"); 476 goto finish; 477 } 478 } 479 } 480 481 // RPS content not checked 482 483finish: 484 return; 485} 486 487/******************************************************************************* 488* updateBootHelpers() updates per the passed-in struct updatingVol. 489* Sec: must ensure each target is one of the source's Apple_Boot partitions 490* Logically, callers provide up->boots,caches but initContext() also 491* fills in up->dasession. Callers must also releaseContext() afterwards. 492* 493* "expect up to date" -> just move the labels aside 494******************************************************************************/ 495static int 496updateBootHelpers(struct updatingVol *up) 497{ 498 int errnum, result = 0; 499 struct stat sb; 500 CFIndex bootcount, bootupdates = 0; 501 502 if (up->curbootfd != -1) { 503 close(up->curbootfd); 504 up->curbootfd = -1; 505 } 506 507 // if the plist has gone stale, punt 508 if ((result = fstat(up->caches->cachefd, &sb))) { 509 OSKextLog(NULL, up->errLogSpec, "fstat(cachefd): %s", strerror(errno)); 510 goto finish; 511 } 512 513 bootcount = CFArrayGetCount(up->boots); 514 for (up->bootIdx = 0; up->bootIdx < bootcount; up->bootIdx++) { 515 char path[PATH_MAX]; 516 517 up->changestate = nothingSerious; // init state 518 if ((errnum = mountBoot(up))) { // sets curMount 519 result = errnum; goto bootfail; 520 } 521 522 // if directed, do our best to nuke anything that doesn't belong 523 if (up->doSanitize) { 524 (void)sanitizeBoot(up); 525 } 526 if (up->cleanOnceDir && 527 strlcpy(path, up->curMount, PATH_MAX) < PATH_MAX && 528 strlcat(path, kBRBootOnceDir, PATH_MAX) < PATH_MAX && 529 0 == stat(path, &sb)) { 530 (void)sdeepunlink(up->curbootfd, path); 531 } 532 533 // If files are missing, update up.do* to ensure we copy them 534 // (implicitly forcing their update in subsequent helpers). 535 checkBootContents(up); 536 537 // If breaking default config, mark helper as tainted 538 if (up->customSource && !up->customDest) { 539 markNotBRDefault(up->curbootfd, up->curMount, NULL, true); 540 } 541 542 if (up->doRPS && (result = ucopyRPS(up))) { 543 goto bootfail; // new RPS content inactive 544 } 545 if (up->doMisc) { 546 (void)ucopyMisc(up); // -> .new files 547 } 548 549 // get the label out of the way (should be optional?) 550 // expectUpToDate => early boot -> harder to generate label? 551 if (up->opts & kBRUExpectUpToDate) { 552 if ((result = moveLabels(up))) { 553 goto bootfail; 554 } 555 } else { 556 if ((result = nukeBRLabels(up))) { 557 goto bootfail; 558 } 559 } 560 561 if (up->doBooters && (result = ucopyBooters(up))) { 562 goto bootfail; // .old still active 563 } 564 // If Recovery OS was available, we could swap these two and leave 565 // the Recovery OS blessed until RPS and new booters were activated. 566 if (up->doBooters && (result = activateBooters(up))) { // committed 567 goto bootfail; 568 } 569 // 10.x.n+1 booters remain compatible 10.x.n kernels?? (power outage!) 570 if (up->doRPS && (result = activateRPS(up))) { // complete 571 goto bootfail; 572 } 573 if ((result = activateMisc(up))) { 574 goto bootfail; // reverts label 575 } 576 577 // if restoring the default configuration, remove any taint 578 if (!up->customSource && !up->customDest) { 579 markNotBRDefault(up->curbootfd, up->curMount, NULL, false); 580 } 581 582 up->changestate = nothingSerious; 583 bootupdates++; // loop success 584 // -U -> updates are a warning 585 OSKextLog(NULL,kOSKextLogFileAccessFlag|((up->opts & kBRUExpectUpToDate) 586 ? kOSKextLogWarningLevel : kOSKextLogBasicLevel), 587 "Successfully updated %s%s.", up->bsdname, up->flatTarget); 588 589bootfail: 590 // clean up this helper only, no hard failures in the loop 591 if (up->changestate!=nothingSerious && !(up->opts&kBRUHelpersOptional)){ 592 OSKextLog(NULL, up->errLogSpec, 593 "Error updating helper partition %s, state %d: %s.", 594 up->bsdname, up->changestate, 595 bootReversionsStrings[up->changestate]); 596 } 597 // unroll any changes we may have made 598 (void)revertState(up); // smart enough to do nothing 599 600 // clean up and unmount (flatTarget -> might not be a helper) 601 // X could check for MNT_DONTBROWSE as a hint it's okay to unmount 602 if (nukeFallbacks(up)) { 603 OSKextLog(NULL, up->errLogSpec, "Warning: %s%s may be untidy.", 604 up->bsdname, up->flatTarget); 605 } 606 unmountBoot(up); // smart, handles "when to unmount" policy 607 } 608 609 if (bootupdates != bootcount && !(up->opts&kBRUHelpersOptional)) { 610 OSKextLog(NULL, up->errLogSpec, "Failed to update helper partition%s.", 611 bootcount - bootupdates == 1 ? "" : "s"); 612 // bullet-proofing: make sure there is a generic error 613 if (result == 0) { 614 // should always be a non-zero result at this point 615 result = ELAST + 1; 616 } 617 goto finish; 618 } 619 620finish: 621 return result; 622} 623 624/****************************************************************************** 625* entry points to update caches and copy files to helper partitions 626* these culminate in BRUpdateBootFiles() and BRCopyBootFiles(). 627******************************************************************************/ 628// XX move to bootcaches.[ch]? 629/* sBRUptLock is accessible here and could be used to conditionalize 630 the setting of ...skiplocks. This function might be useful to 631 kextd, though it would be a significant change in that kextd 632 would now be calling the 'rebuild' functions as well as the 633 'check' functions (instead of calling kextcache -u). For example, 634 it would preclude multiple stacked kextcache -u processes (good) 635 but change the nature of canceling in-progress updates (unknown). 636 kextd's memory footprint would likely grow (one way or another). 637 638 invalidateKextCache - if TRUE then we mimic 639 "sudo touch /System/Library/Extensions" 640*/ 641int 642checkRebuildAllCaches(struct bootCaches *caches, 643 int oodLogSpec, 644 Boolean invalidateKextCache, 645 Boolean *anyUpdates) 646{ 647 int opres, result = ELAST + 1; // no pathc() [yet] 648 struct stat sb; 649 Boolean didUpdate = false; 650#if DEV_KERNEL_SUPPORT 651 char * suffixPtr = NULL; // must free 652 char * tmpKernelPath = NULL; // must free 653#endif 654 655 if (caches == NULL) goto finish; 656 // if the caches data is no longer valid, abort immediately 657 if ((opres = fstat(caches->cachefd, &sb))) { 658 result = opres; goto finish; 659 } 660 661 OSKextLog(NULL, kOSKextLogProgressLevel | kOSKextLogArchiveFlag, 662 "Ensuring %s's caches are up to date.", caches->root); 663 664 /* XX Sec (re-review?): can't let an external volume insert a cache 665 * - mktmp/mkstmp used to create temp file at destination 666 * - final rename must be on whatever volume provided the kexts 667 * - if volume is /, then kexts owned by root can be trusted (4623559 fstat) 668 * - otherwise, rename from wrong volume will fail 669 */ 670 671 // We have to rely on the system's kextcache + IOKit.framework to 672 // rebuild these caches. If called on an older system via 673 // libBootRoot against newer cache files, the launched kextcache 674 // processes are unlikely to know how to update the caches. Errors 675 // should be returned. 676 677 // Avoid deadlock with the kextcache processes which might launch below. 678 // This environment variable tells it *not* to take a lock since we 679 // should be holding it (caller should have called initContext() XX?). 680 setenv("_com_apple_kextd_skiplocks", "1", 1); 681 682 // update the various kernel caches 683 if (invalidateKextCache || 684 check_kext_boot_cache_file(caches, 685 caches->kext_boot_cache_file->rpath, 686 caches->kernelpath)) { 687 // rebuild the mkext under our lock / lack thereof 688 // (-v forwarded via environment variable by kextcache & kextd) 689 OSKextLog(nil, oodLogSpec, "rebuilding %s%s", 690 caches->root ? caches->root : "/", 691 caches->kext_boot_cache_file->rpath); 692 if ((opres = rebuild_kext_boot_cache_file( 693 caches, true /*wait*/, 694 caches->kext_boot_cache_file->rpath, 695 caches->kernelpath))) { 696 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 697 "Error %d rebuilding %s", result, 698 caches->kext_boot_cache_file->rpath); 699 result = opres; goto finish; 700 } else { 701 didUpdate = true; 702 } 703 } else { 704 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogArchiveFlag, 705 "Primary kext cache does not need update."); 706 } 707#if DEV_KERNEL_SUPPORT 708 if (caches->extraKernelCachePaths) { 709 int i; 710 cachedPath * cp; 711 712 tmpKernelPath = malloc(PATH_MAX); 713 if (tmpKernelPath) { 714 for (i = 0; i < caches->nekcp; i++) { 715 cp = &caches->extraKernelCachePaths[i]; 716 SAFE_FREE_NULL(suffixPtr); 717 718 suffixPtr = getPathExtension(cp->rpath); 719 if (suffixPtr == NULL) 720 continue; 721 if (strlcpy(tmpKernelPath, caches->kernelpath, PATH_MAX) >= PATH_MAX) 722 continue; 723 if (strlcat(tmpKernelPath, ".", PATH_MAX) >= PATH_MAX) 724 continue; 725 if (strlcat(tmpKernelPath, suffixPtr, PATH_MAX) >= PATH_MAX) 726 continue; 727 if (invalidateKextCache || 728 check_kext_boot_cache_file(caches, cp->rpath, tmpKernelPath)) { 729 if ((opres = rebuild_kext_boot_cache_file(caches, 730 true /*wait*/, 731 cp->rpath, 732 tmpKernelPath))) { 733 result = opres; goto finish; 734 } 735 } 736 } // for loop... 737 } 738 } 739 740#endif 741 742 743 744 // Check/rebuild the CSFDE property cache which goes into the Apple_Boot. 745 // It's less critical for booting, but more critical for security. 746 if (check_csfde(caches)) { 747 OSKextLog(NULL,oodLogSpec,"rebuilding %s",caches->erpropcache->rpath); 748 if ((opres = rebuild_csfde_cache(caches))) { 749 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogArchiveFlag, 750 "Error %d rebuilding %s", result, 751 caches->erpropcache->rpath); 752 result = opres; goto finish; 753 } else { 754 didUpdate = true; 755 } 756 } else { 757 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogArchiveFlag, 758 "CSFDE property cache does not need update."); 759 } 760 761 // check on the (optional) localized resources used by EFI Login 762 if (check_loccache(caches)) { 763 OSKextLog(NULL,oodLogSpec,"rebuilding %s",caches->efiloccache->rpath); 764 if ((result = rebuild_loccache(caches))) { 765 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogArchiveFlag, 766 "Warning: Error %d rebuilding %s", result == -1 767 ? errno : result, caches->efiloccache->rpath); 768 } else { 769 didUpdate = true; 770 } 771 // efiloccache is not required when copying rpspaths 772 // so we can ignore failures to rebuild the cache. 773 } else { 774 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogArchiveFlag, 775 "Localized EFI Login resources do not need update."); 776 } 777 778 // success! 779 result = 0; 780 781 // report back if we did any updates 782 if (anyUpdates) *anyUpdates = didUpdate; 783 784finish: 785#if DEV_KERNEL_SUPPORT 786 SAFE_FREE(tmpKernelPath); 787 SAFE_FREE(suffixPtr); 788#endif 789 790 return result; 791} 792 793 794/***************************************************************************** 795* initContext() sets up a struct updatingVol for use by other functions 796* - volRoot must contain a supported bootcaches.plist 797* - volRoot will be locked with kextd 798* - if available, diskarb will be configured up->dasession 799* - specifiying helperBSDName -> up->boots = [ helperBSDName ] 800* releaseContext() should be called when the context is no longer needed. 801*****************************************************************************/ 802#define BOOTCOUNT 1 803static int 804initContext(struct updatingVol *up, CFURLRef srcVol, CFStringRef helperBSDName, 805 BRUpdateOpts_t opts) 806{ 807 int opres, result = ELAST + 1; // all paths should reset 808 const void *values[BOOTCOUNT] = { helperBSDName }; 809 810 // start fresh (all booleans to default false values) 811 bzero(up, sizeof(struct updatingVol)); 812 up->curbootfd = -1; // should be -1 or valid descriptor 813 814 // establish default options 815 up->warnLogSpec = kOSKextLogArchiveFlag | kOSKextLogWarningLevel; 816 up->errLogSpec = kOSKextLogArchiveFlag | kOSKextLogErrorLevel; 817 up->blessSpec = kBRBlessFSDefault; 818 819 // stash opts for subroutines 820 up->opts = opts; 821 822 // takeVolumeForPath() wants a char* ... comes before up->caches = ... 823 if (!CFURLGetFileSystemRepresentation(srcVol, /* resolveToBase */ true, 824 (UInt8 *)up->srcRoot,sizeof(up->srcRoot))){ 825 OSKextLogStringError(NULL); 826 result = ENOMEM; goto finish; 827 } 828 829 // Theoretically we don't need to lock to read bootcaches.plist, but 830 // read[SIC]BootCaches() will create S/L/Caches/bootstamps if missing. 831 // -Boot -> don't sync w/kextd 832 if ((opts & kBRUEarlyBoot) == 0) { 833 if ((opres = takeVolumeForPath(up->srcRoot))) { // lock (logs errors) 834 result = opres; goto finish; 835 } 836 } 837 838 // initializing the context fails if there's no bootcaches.plist 839 if (!(up->caches = readBootCaches(up->srcRoot, opts))) { 840 result = errno ? errno : ELAST + 1; 841 goto finish; 842 } 843 844 // attempt to configure a disk arb session 845 if ((up->dasession = DASessionCreate(nil))) { 846 // mountBoot and unmountBoot will spin the runloop for this DA session 847 DASessionScheduleWithRunLoop(up->dasession, CFRunLoopGetCurrent(), 848 kCFRunLoopDefaultMode); 849 } else { 850 OSKextLog(NULL, up->warnLogSpec, "Warning: proceeding w/o DiskArb"); 851 } 852 853 // if specified, this partition is the one to update 854 if (helperBSDName) { 855 up->boots = CFArrayCreate(nil,values,BOOTCOUNT,&kCFTypeArrayCallBacks); 856 } 857 858 result = 0; 859 860finish: 861 return result; 862} 863 864static void 865releaseContext(struct updatingVol *up, int status) 866{ 867 // unmountBoot() not always called 868 if (up->curBoot) CFRelease(up->curBoot); 869 if (up->curbootfd != -1) { 870 close(up->curbootfd); 871 up->curbootfd = -1; 872 } 873 874 if (up->dasession) { 875 DASessionUnscheduleFromRunLoop(up->dasession, CFRunLoopGetCurrent(), 876 kCFRunLoopDefaultMode); 877 CFRelease(up->dasession); 878 up->dasession = NULL; 879 } 880 881 if (up->boots) CFRelease(up->boots); 882 if (up->csfdeprops) CFRelease(up->csfdeprops); 883 if (up->bpoverrides) CFRelease(up->bpoverrides); 884 if (up->caches) destroyCaches(up->caches); 885 886 // unlock 887 putVolumeForPath(up->srcRoot, status); 888} 889 890static void 891addDictOverride(const void *key, const void *value, void *ctx) 892{ 893 CFMutableDictionaryRef tgtDict = (CFMutableDictionaryRef)ctx; 894 895 // AddValue is "add if absent;" we implement override by removing 896 if (CFDictionaryContainsKey(tgtDict, key)) 897 CFDictionaryRemoveValue(tgtDict, key); 898 899 CFDictionaryAddValue(tgtDict, key, value); 900} 901 902static CFDataRef 903createBootPrefData(struct updatingVol *up, uuid_string_t root_uuid, 904 CFDictionaryRef bootPrefOverrides) 905{ 906 CFDataRef rval = NULL; 907 char srcpath[PATH_MAX]; 908 int fd = -1; 909 void *buf = NULL; 910 CFDataRef data = NULL; 911 CFMutableDictionaryRef pldict = NULL; 912 CFStringRef UUIDStr = NULL; 913 CFStringRef kernPathStr = NULL; 914 915 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 916 "creating com.apple.Boot.plist data with UUID %s.", 917 root_uuid); 918 919 // suck in any existing plist 920 do { 921 struct stat sb; 922 PATHCPYcont(srcpath, up->caches->root); 923 PATHCATcont(srcpath, up->caches->bootconfig->rpath); 924 if (-1 == (fd=sopen(up->caches->cachefd,srcpath,O_RDONLY,0))) 925 break; 926 if (fstat(fd, &sb)) break; 927 if (sb.st_size > UINT_MAX || sb.st_size > LONG_MAX) break; 928 if (!(buf = malloc((size_t)sb.st_size))) break; 929 if (read(fd, buf, (size_t)sb.st_size) != sb.st_size)break; 930 if (!(data = CFDataCreate(nil, buf, (long)sb.st_size))) 931 break; 932 933 // make mutable dictionary from file data 934 pldict =(CFMutableDictionaryRef) 935 CFPropertyListCreateWithData(nil, 936 data, 937 kCFPropertyListMutableContainers, 938 NULL, 939 NULL/*err*/); 940 } while(0); 941 942 errno = 0; 943 944 // if we got a dictionary, just grab the file mode 945 if (!pldict || CFGetTypeID(pldict)!=CFDictionaryGetTypeID()) { 946 // otherwise, create a dictionary 947 if (pldict) CFRelease(pldict); // e.g. if it was a non-dict 948 pldict = CFDictionaryCreateMutable(nil, 1, 949 &kCFTypeDictionaryKeyCallBacks, 950 &kCFTypeDictionaryValueCallBacks); 951 if (!pldict) goto finish; 952 } 953 954 // make a CFStr out of the UUID and insert 955 errno = 0; 956 UUIDStr = CFStringCreateWithCString(nil,root_uuid,kCFStringEncodingASCII); 957 if (!UUIDStr) goto finish; 958 CFDictionarySetValue(pldict, CFSTR(kRootUUIDKey), UUIDStr); 959 if (!CFEqual(CFDictionaryGetValue(pldict,CFSTR(kRootUUIDKey)), UUIDStr)) 960 goto finish; 961 962 // if necessary, tell the booter to load <flatTarget>/kernelcache 963 if (up->flatTarget[0] || up->useOnceDir) { 964 char kpath[PATH_MAX] = ""; 965 /* XX 10561671: basename() unsafe */ 966 if (up->useOnceDir) { 967 pathcat(kpath, kBRBootOnceDir); 968 } 969 pathcat(kpath, up->flatTarget); 970 pathcat(kpath, "/"); 971 pathcat(kpath, basename(up->caches->kext_boot_cache_file->rpath)); 972 kernPathStr = CFStringCreateWithFileSystemRepresentation(nil, kpath); 973 if (!kernPathStr) goto finish; 974 CFDictionarySetValue(pldict, CFSTR(kKernelCacheKey), kernPathStr); 975 } 976 977 // add any additional override values 978 if (bootPrefOverrides) { 979 CFDictionaryApplyFunction(bootPrefOverrides,addDictOverride,pldict); 980 } 981 982 rval = CFPropertyListCreateData(nil, pldict, kCFPropertyListXMLFormat_v1_0, 983 0 /* !opts */, NULL); 984 985finish: 986 if (kernPathStr) CFRelease(kernPathStr); 987 if (UUIDStr) CFRelease(UUIDStr); 988 if (pldict) CFRelease(pldict); 989 if (data) CFRelease(data); 990 991 if (buf) free(buf); 992 if (fd != -1) close(fd); 993 994 return rval; 995} 996 997 998/* 999 * needUpdatesNoUUID() checks the top-level bootstamps directory. 1000 * 12369781: allow asr to change the fsys UUID w/o first boot rebooting 1001 * 1002 */ 1003static int 1004needUpdatesNoUUID(CFURLRef volURL, Boolean *anyCritical) 1005{ 1006 int rval = ELAST + 1; // all paths should reset 1007 Boolean doAnyNoUUID = false; 1008 char volRoot[PATH_MAX]; 1009 struct bootCaches *caches = NULL; 1010 1011 if (!CFURLGetFileSystemRepresentation(volURL, /* resolve */ true, 1012 (UInt8*)volRoot, sizeof(volRoot))) { 1013 OSKextLogStringError(NULL); 1014 rval = ENOMEM; goto finish; 1015 } 1016 1017 // "any stamps" option changes the embedded bootstamp paths 1018 caches = readBootCaches(volRoot, kBRAnyBootStamps); 1019 if (!caches) { 1020 rval = errno ? errno : ELAST + 1; 1021 goto finish; 1022 } 1023 1024 // needUpdates() has already been called once with higher verbosity 1025 doAnyNoUUID = needUpdates(caches, kBROptsNone, NULL, NULL, NULL, 1026 kOSKextLogGeneralFlag | kOSKextLogDetailLevel); 1027 1028 if (anyCritical) { 1029 *anyCritical = doAnyNoUUID; 1030 } 1031 1032finish: 1033 if (caches) destroyCaches(caches); 1034 1035 return rval; 1036} 1037 1038/****************************************************************************** 1039* checkUpdateCachesAndBoots() returns 1040* - success (EX_OK / 0) if nothing needs updating 1041* - success if updates were successfully made (and expectUTD = false) 1042* - EX_OSFILE if updates were unexpectedly needed and successfully made 1043******************************************************************************/ 1044// keeping these active for reliability testing of live builds 1045#define BRDBG_OOD_HANG_BOOT_F "/var/db/.BRHangBootOnOODCaches" 1046#define BRDBG_HANG_MSG PRODUCT_NAME ": " BRDBG_OOD_HANG_BOOT_F \ 1047 " -> hanging on out of date caches" 1048#define BRDBG_CONS_MSG "[via /dev/console] " BRDBG_HANG_MSG "\n" 1049int 1050checkUpdateCachesAndBoots(CFURLRef volumeURL, BRUpdateOpts_t opts) 1051{ 1052 int opres, result = ELAST + 1; // try to always set on error 1053 OSKextLogSpec oodLogSpec = kOSKextLogGeneralFlag | kOSKextLogBasicLevel; 1054 Boolean expectUpToDate = (opts & kBRUExpectUpToDate); // used a lot 1055 Boolean anyCacheUpdates = false; 1056 Boolean doAny = false, cachesUpToDate = false, *doMiscp; 1057 Boolean loggedOOD = false; 1058 struct updatingVol up = { /*NULL...*/ }; 1059 up.curbootfd = -1; 1060 1061 // try to configure 'up'; treat missing data per opts 1062 if ((opres = initContext(&up, volumeURL, NULL, opts))) { 1063 char *bcmsg = NULL; 1064 CFArrayRef helpers; 1065 switch (opres) { // describe known problems 1066 case ENOENT: bcmsg = "no " kBootCachesPath; break; 1067 case EFTYPE: bcmsg = "unrecognized " kBootCachesPath; break; 1068 default: break; 1069 } 1070 if ((opts & kBRUForceUpdateHelpers) && 1071 (helpers = BRCopyActiveBootPartitions(volumeURL))) { 1072 // helper partitions + -f => we require bootcaches.plist 1073 OSKextLog(NULL,up.errLogSpec,"%s: %s; aborting",up.srcRoot,bcmsg); 1074 CFRelease(helpers); 1075 result = opres; goto finish; 1076 } else if (bcmsg) { 1077 // politely pass on known limitations 1078 OSKextLog(NULL, oodLogSpec, "%s: %s; skipping",up.srcRoot,bcmsg); 1079 result = 0; goto finish; 1080 } else { 1081 // unknown error; fail 1082 OSKextLog(NULL, up.errLogSpec, "%s: error %d reading " 1083 kBootCachesPath, up.srcRoot, opres); 1084 result = opres; goto finish; 1085 } 1086 } 1087 1088 // -U logs what is out of date at a a more urgent level than -u 1089 if (expectUpToDate) { 1090 oodLogSpec = up.errLogSpec; 1091 } 1092 1093 // do some real work updating caches *in* the source volume 1094 if ((opres = checkRebuildAllCaches(up.caches, oodLogSpec, 1095 (opts & kBRUInvalidateKextcache), 1096 &anyCacheUpdates))) { 1097 result = opres; goto finish; // error logged by function 1098 } 1099 1100 // record partial success 1101 cachesUpToDate = true; 1102 1103 // 9455881: If requested, only update the caches 1104 if (opts & kBRUCachesOnly) { 1105 goto doneUpdatingHelpers; 1106 } 1107 1108 if (!hasBootRootBoots(up.caches, &up.boots, NULL, &up.onAPM)) { 1109 OSKextLog(NULL, kOSKextLogBasicLevel | kOSKextLogFileAccessFlag, 1110 "%s: no supported helper partitions to update.", up.srcRoot); 1111 goto doneUpdatingHelpers; // no boots -> nothing more to do 1112 } 1113 1114 /* --- updating a Boot!=Root volume --- */ 1115 1116 // these are helper (not OS) partitions & should be clean 1117 up.doSanitize = true; 1118 1119 // -U -Boot means we don't care about misc files 1120 if (expectUpToDate && (opts & kBRUEarlyBoot)) { 1121 doMiscp = NULL; 1122 } else { 1123 doMiscp = &up.doMisc; 1124 } 1125 1126 // figure out what needs updating 1127 // needUpdates() also populates the timestamp values used by updateStamps() 1128 doAny = needUpdates(up.caches, opts, &up.doRPS, &up.doBooters, doMiscp, 1129 oodLogSpec); 1130 1131 // for -U, give the non-UUID paths a chance (possibly resetting doAny) 1132 if (doAny && expectUpToDate) { 1133 loggedOOD = true; 1134 (void)needUpdatesNoUUID(volumeURL, &doAny); 1135 } 1136 1137#ifdef BRDBG_OOD_HANG_BOOT_F 1138 // check to see if out of date at early boot should cause a hang 1139 if (doAny && expectUpToDate && (opts & kBRUEarlyBoot)) { 1140 struct stat sb; 1141 int consfd = open(_PATH_CONSOLE, O_WRONLY|O_APPEND); 1142 while (stat(BRDBG_OOD_HANG_BOOT_F, &sb) == 0) { 1143 OSKextLog(NULL, up.errLogSpec, BRDBG_HANG_MSG); 1144 if (consfd > -1) 1145 write(consfd, BRDBG_CONS_MSG, sizeof(BRDBG_CONS_MSG)-1); 1146 sleep(30); 1147 } 1148 } 1149#endif // BRDBG_OOD_HANG_BOOT_F 1150 1151 // force ignores needUpdates() and does extra helper cleanup 1152 // (note: -Installer -> force, non-default cleanOnce preserves ANI) 1153 if (opts & kBRUForceUpdateHelpers) { 1154 up.doRPS = up.doBooters = up.doMisc = true; 1155 up.cleanOnceDir = true; 1156 } else if (!doAny) { 1157 // LogLevelBasic is only emitted with -v and above 1158 // 'Warning' level clarifies previous "not cached" messages 1159 OSKextLogSpec utdlogSpec = kOSKextLogFileAccessFlag; 1160 if (loggedOOD) { 1161 utdlogSpec |= kOSKextLogWarningLevel; 1162 } else { 1163 utdlogSpec |= kOSKextLogBasicLevel; 1164 } 1165 OSKextLog(NULL, utdlogSpec, "%s: helper partitions appear up to date.", 1166 up.srcRoot); 1167 goto doneUpdatingHelpers; 1168 } 1169 1170 // configure hostVol-based UUIDs, etc 1171 if ((opres = addHostVolInfo(&up, volumeURL, NULL, NULL, NULL))) { 1172 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1173 "%s: error %d extracting volume info.", up.srcRoot, opres); 1174 result = opres; goto finish; 1175 } 1176 1177 // Update = root from volume containing caches; fill in csfdeprops 1178 strlcpy(up.host_uuid, up.caches->fsys_uuid, sizeof(up.host_uuid)); 1179 if (up.caches->csfde_uuid) { 1180 opres = copyCSFDEInfo(up.caches->csfde_uuid, &up.csfdeprops, NULL); 1181 if (opres) { 1182 result = opres; goto finish; // error logged by function 1183 } 1184 } 1185 1186 // request actual helper updates 1187 if ((opres = updateBootHelpers(&up))) { 1188 result = opres; goto finish; // error logged by function 1189 } 1190 1191 if ((opres = updateStamps(up.caches, kBCStampsApplyTimes))) { 1192 OSKextLog(NULL, kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 1193 "%s: could not update bootstamps.", up.srcRoot); 1194 result = opres; goto finish; 1195 } 1196 1197doneUpdatingHelpers: 1198 // success 1199 result = 0; 1200 1201 // kBRUExpectUpToDate is used to differentiate "success: everything clean" 1202 // from "successfully updated:" the latter exits with EX_OSFILE. During 1203 // early boot, this informs launchd to force a reboot off fresh caches. 1204 if (expectUpToDate && (anyCacheUpdates || doAny)) { 1205 result = EX_OSFILE; 1206 } 1207 1208finish: 1209 if ((up.opts & kBRUHelpersOptional) && cachesUpToDate) { 1210 // partial success okay 1211 result = 0; 1212 } 1213 1214 // since updateBoots() -> exit(), convert common errors to sysexits(3) 1215 if (result && result != EX_OSFILE) { 1216 result = getExitValueFor(result); 1217 } 1218 1219 // handles unlock / reporting to kextd 1220 releaseContext(&up, result); 1221 1222 // all error paths should log if the functions they call don't 1223 1224 return result; 1225} 1226 1227#define kBRCheckLogSpec (kOSKextLogArchiveFlag | kOSKextLogProgressLevel) 1228OSStatus 1229BRUpdateBootFiles(CFURLRef volURL, Boolean force) 1230{ 1231 if (!volURL) 1232 return EINVAL; 1233 1234 return checkUpdateCachesAndBoots(volURL, force?kBRUForceUpdateHelpers:0); 1235} 1236 1237 1238/* error handling style in this function is an experiment to find a 1239 1. correct (doesn't miss errors) 1240 2. robust (doesn't fall over on correctness if not followed) 1241 3. accurate (returns detailed error values) 1242 4. readable (can figure out what's going on) 1243 5. concise (can we achieve #1-3 w/o using both 'errnum' and 'result'?) 1244 style for handling errors. 1245*/ 1246static int 1247addHostVolInfo(struct updatingVol *up, CFURLRef hostVol, 1248 CFDictionaryRef bootPrefOverrides, CFURLRef targetStr, 1249 CFStringRef pickerLabel) 1250{ 1251 OSStatus result = EOVERFLOW; // ! only set AFTER error detected ! 1252 OSStatus errnum; // temp var for collecting error values 1253 uuid_t host_uuidbytes; 1254 CFStringRef csUUIDStr = NULL; 1255 char hostroot[PATH_MAX]; 1256 1257 up->flatTarget[0] = '\0'; 1258 1259 // extract any caller-specified target directory 1260 if (targetStr) { 1261 char targetdir[PATH_MAX] = "", *slash; 1262 if (!CFURLGetFileSystemRepresentation(targetStr, true /*resolve*/, 1263 (UInt8*)targetdir, PATH_MAX)) { 1264 result = EINVAL; goto finish; 1265 } 1266 // target dir must not be '/' 1267 slash = targetdir; 1268 while (*slash == '/') slash++; 1269 if (*slash == '\0') { 1270 result = EINVAL; goto finish; 1271 } 1272 if (targetdir[0] != '/') { // did caller provide a '/'? 1273 pathcat(up->flatTarget, "/"); 1274 } 1275 pathcat(up->flatTarget, targetdir); 1276 } 1277 1278 // get UUIDs 1279 if (!CFURLGetFileSystemRepresentation(hostVol, true /*resolve base*/, 1280 (UInt8*)hostroot, PATH_MAX)) { 1281 result = ENOMEM; goto finish; 1282 } 1283 if ((errnum=copyVolumeInfo(hostroot,&host_uuidbytes,&csUUIDStr,NULL,NULL))){ 1284 result = errnum; goto finish; 1285 } 1286 uuid_unparse_upper(host_uuidbytes, up->host_uuid); 1287 1288 // stash overrides for writeBootPrefData(), set up any CSFDE cache 1289 up->bpoverrides = bootPrefOverrides; 1290 if (up->bpoverrides) { 1291 CFRetain(up->bpoverrides); // balances releaseContext() 1292 } 1293 if (csUUIDStr) { 1294 if ((errnum = copyCSFDEInfo(csUUIDStr, &up->csfdeprops, NULL))) { 1295 result = errnum; goto finish; 1296 } 1297 } 1298 1299 if (pickerLabel) { 1300 if (!CFStringGetFileSystemRepresentation(pickerLabel, 1301 up->caches->defLabel, PATH_MAX)) { 1302 result = EINVAL; goto finish; 1303 } 1304 } 1305 1306 result = 0; 1307 1308finish: 1309 if (csUUIDStr) CFRelease(csUUIDStr); 1310 1311 return result; 1312} 1313 1314 1315/****************************************************************************** 1316* copy boot files from source volume to destination partition 1317* - makes sure caches are up to date 1318* - copies regardless of bootstamps 1319* - updates bootstamps if default content -> likely default location 1320* - w/custom content->default location, marks root and helpers as such 1321* see bootroot.h for more details 1322******************************************************************************/ 1323/* 1324 BRCopyBootFilesToDir() uses circa OS X 10.10 development 1325 0) force an update like kextcache -u -f (which doesn't use this function) 1326 B!=R already set up: default content -> default location 1327 1a) switch a volume to Boot!=Root: 'diskutil cs convert', etc 1328 1b) brtest copyfiles -anyboot to populate a disk image's Apple_Boot 1329 setting up B!=R: future default content -> future default location 1330 2) InstallAssistant (Install OS X.app) [boots until complete, canceled] 1331 B!=R set up: BaseSystem.dmg (custom) content -> default location 1332 3) AppleNetInstall [just boot it once] 1333 B!=R set up: custom content -> custom location 1334 1335 While not in production use today, brtest allows 1336 - copy one B!=R's content to another volume's helper (no CSFDE support) 1337 manual override: non-default content to the default location 1338 1339 A libBootRoot client could also specify 1340 - copy to the default location, FS-bless *and* boot once 1341 potentially custom content -> default location 1342*/ 1343 1344OSStatus 1345BRCopyBootFilesToDir(CFURLRef srcVol, 1346 CFURLRef initialRoot, 1347 CFDictionaryRef bootPrefOverrides, 1348 CFStringRef targetBSDName, 1349 CFURLRef targetDir, 1350 BRBlessStyle blessSpec, 1351 CFStringRef pickerLabel, 1352 BRUpdateOpts_t opts) 1353{ 1354 OSStatus result = ELAST + 1; // generic = safest 1355 OSStatus errnum; 1356 CFArrayRef helpers; 1357 CFStringRef firstHelper; 1358 Boolean doUpdateStamps = false; 1359 struct updatingVol up = { /* NULL, ... */ }; 1360 up.curbootfd = -1; 1361 1362 // defend libBootRoot entry point 1363 if (!srcVol || !initialRoot || !targetBSDName) { 1364 result = EINVAL; goto finish; 1365 } 1366 1367 // configure a single-helper context 1368 errnum = initContext(&up, srcVol, targetBSDName, opts); 1369 if (errnum) { 1370 result = errnum; goto finish; 1371 } 1372 1373 // Detect non-default destination or source 1374 up.blessSpec = blessSpec; 1375 up.useOnceDir = ((blessSpec & kBRBlessOnce) && 1376 (blessSpec & kBRBlessFSDefault) == 0); 1377 if (up.useOnceDir || targetDir) { 1378 // A) a custom destination won't interact w/default 1379 up.customDest = true; 1380 } else if (CFEqual(srcVol, initialRoot) == false) { 1381 // B) targetBSD can't belong to both, so the content won't be default 1382 up.customSource = true; 1383 } else if ((helpers = BRCopyActiveBootPartitions(initialRoot))) { 1384 if ((CFArrayGetCount(helpers)) != 1 || 1385 (firstHelper = CFArrayGetValueAtIndex(helpers, 0)) == NULL || 1386 CFEqual(targetBSDName, firstHelper) == false) { 1387 // C) the only volume is B!=R, but targetBSD is not its only helper 1388 up.customSource = true; 1389 } else { 1390 // looks like default, already-configured Boot!=Root 1391 doUpdateStamps = true; 1392 } 1393 CFRelease(helpers); 1394 } else { 1395 // assume default Boot!=Root to be 1396 doUpdateStamps = true; 1397 } 1398 1399 // kBRAnyBootStamps forces a bootstamps update 1400 if (opts & kBRAnyBootStamps) { 1401 doUpdateStamps = true; 1402 } 1403 up.doSanitize = doUpdateStamps; 1404 1405 // Make sure all caches are up to date on the source 1406 // (undefined if OOD & system's kext management/EFILogin can't rebuild) 1407 errnum = checkRebuildAllCaches(up.caches, kBRCheckLogSpec, 1408 (opts & kBRUInvalidateKextcache), NULL); 1409 if (errnum) { 1410 result = errnum; goto finish; 1411 } 1412 1413 // if writing bootstamps, gather timestamp data to apply on success 1414 if (doUpdateStamps) { 1415 (void)needUpdates(up.caches, kBROptsNone, NULL, NULL, NULL, 1416 kOSKextLogGeneralFlag | kOSKextLogProgressLevel); 1417 } 1418 1419 // configure options shared with checkUpdate...() 1420 errnum = addHostVolInfo(&up, initialRoot, bootPrefOverrides, 1421 targetDir, pickerLabel); 1422 if (errnum) { 1423 result = errnum; goto finish; 1424 } 1425 1426 // BRCopyBootFiles() always copies everything fresh 1427 up.doRPS = up.doBooters = up.doMisc = true; 1428 up.cleanOnceDir = true; 1429 1430 1431 // And finally, update! 1432 if ((errnum = updateBootHelpers(&up))) { 1433 result = errnum; goto finish; 1434 } 1435 1436 // update bootstamps if establishing a default configuration 1437 if (doUpdateStamps) { 1438 if (opts & kBRAnyBootStamps) { 1439 // if writing top-level bootstamps, attempt start fresh 1440 char cachedir[PATH_MAX]; 1441 pathcpy(cachedir, up.caches->root); 1442 pathcat(cachedir, kTSCacheDir); 1443 (void)sdeepunlink(up.caches->cachefd, cachedir); 1444 } 1445 errnum = updateStamps(up.caches, kBCStampsApplyTimes); 1446 if (errnum) { 1447 result = errnum; goto finish; 1448 } 1449 } 1450 1451 // Note any pollution of a default destination from a non-default source. 1452 if (up.customSource && !up.customDest) { 1453 (void)taintDefaultStamps(targetBSDName); // logs warnings 1454 // (if we failed, it would be up to the client to un-do?) 1455 } 1456 1457 // success 1458 result = 0; 1459 1460finish: 1461 releaseContext(&up, result); 1462 1463 return result; 1464} 1465 1466OSStatus 1467BRCopyBootFiles(CFURLRef srcVol, 1468 CFURLRef initialRoot, 1469 CFStringRef helperBSDName, 1470 CFDictionaryRef bootPrefOverrides) 1471{ 1472 return BRCopyBootFilesToDir(srcVol, initialRoot, bootPrefOverrides, 1473 helperBSDName, NULL /*helperDir*/, 1474 kBRBlessFSDefault, NULL /*pickerLabel*/, 1475 kBROptsNone); 1476} 1477 1478/****************************************************************************** 1479* FindRPSDir plays rock, paper scissors to identify the location of 1480* the latest complete copy of the files the booter needs. 1481******************************************************************************/ 1482static int 1483FindRPSDir(struct updatingVol *up, char prev[PATH_MAX], char current[PATH_MAX], 1484 char next[PATH_MAX]) 1485{ 1486 char rpath[PATH_MAX], ppath[PATH_MAX], spath[PATH_MAX]; 1487/* 1488 * FindRPSDir looks for a "rock," "paper," or "scissors" directory 1489 * - handle all permutations: 3 dirs, any 2 dirs, any 1 dir 1490 */ 1491// static EFI_STATUS 1492// FindRPSDir(EFI_FILE_HANDLE BootDir, EFI_FILE_HANDLE *newBoot) 1493// 1494 int rval = ELAST + 1, status; 1495 struct stat r, p, s; 1496 Boolean haveR = false, haveP = false, haveS = false; 1497 char *prevp = NULL, *curp = NULL, *nextp = NULL; 1498 1499 // set up full paths with intervening slash 1500 pathcpy(rpath, up->curMount); 1501 pathcat(rpath, "/"); 1502 pathcpy(ppath, rpath); 1503 pathcpy(spath, rpath); 1504 1505 pathcat(rpath, kBootDirR); 1506 pathcat(ppath, kBootDirP); 1507 pathcat(spath, kBootDirS); 1508 1509 status = stat(rpath, &r); // easier to let this fail 1510 haveR = (status == 0); 1511 status = stat(ppath, &p); 1512 haveP = (status == 0); 1513 status = stat(spath, &s); 1514 haveS = (status == 0); 1515 1516 if (haveR && haveP && haveS) { // NComb(3,3) = 1 1517 OSKextLog(NULL, up->warnLogSpec, 1518 "Warning: all of R,P,S exist: picking 'R'; destroying 'P'."); 1519 curp = rpath; nextp = ppath; prevp = spath; 1520 if ((rval = eraseRPS(up, nextp))) 1521 goto finish; 1522 } else if (haveR && haveP) { // NComb(3,2) = 3 1523 // p wins 1524 curp = ppath; nextp = spath; prevp = rpath; 1525 } else if (haveR && haveS) { 1526 // r wins 1527 curp = rpath; nextp = ppath; prevp = spath; 1528 } else if (haveP && haveS) { 1529 // s wins 1530 curp = spath; nextp = rpath; prevp = ppath; 1531 } else if (haveR) { // NComb(3,1) = 3 1532 // r wins by default 1533 curp = rpath; nextp = ppath; prevp = spath; 1534 } else if (haveP) { 1535 // p wins by default 1536 curp = ppath; nextp = spath; prevp = rpath; 1537 } else if (haveS) { 1538 // s wins by default 1539 curp = spath; nextp = rpath; prevp = ppath; 1540 } else { // NComb(3,0) = 0 1541 // we'll start with rock 1542 curp = rpath; nextp = ppath; prevp = spath; 1543 } 1544 1545 if (strlcpy(prev, prevp, PATH_MAX) >= PATH_MAX) goto finish; 1546 if (strlcpy(current, curp, PATH_MAX) >= PATH_MAX) goto finish; 1547 if (strlcpy(next, nextp, PATH_MAX) >= PATH_MAX) goto finish; 1548 1549 rval = 0; 1550 1551finish: 1552 if (rval) { 1553 /* can't use errno here since strlcpy and strlcat don't set it */ 1554 OSKextLog(NULL, up->errLogSpec, 1555 "%s - strlcpy or cat failed - >= PATH_MAX", __FUNCTION__); 1556 } 1557 1558 return rval; 1559} 1560 1561/****************************************************************************** 1562* BREraseBootFiles() un-does BRCopyBootFiles() 1563******************************************************************************/ 1564// helper does wraps BLSetFinderVolumeInfo with schdir() 1565static int 1566sBLSetBootFinderInfo(struct updatingVol *up, uint32_t newvinfo[8]) 1567{ 1568 int result, fd = -1; 1569 uint32_t vinfo[8]; 1570 1571 result = schdir(up->curbootfd, up->curMount, &fd); 1572 if (result) goto finish; 1573 result = BLGetVolumeFinderInfo(NULL, ".", vinfo); 1574 if (result) goto finish; 1575 vinfo[kSystemFolderIdx] = newvinfo[kSystemFolderIdx]; 1576 vinfo[kEFIBooterIdx] = newvinfo[kEFIBooterIdx]; 1577 result = BLSetVolumeFinderInfo(NULL, ".", vinfo); 1578 1579finish: 1580 if (fd != -1) 1581 (void)restoredir(fd); 1582 return result; 1583} 1584 1585// helper attempts to bless the Recovery OS if present 1586static int 1587blessRecovery(struct updatingVol *up) 1588{ 1589 int result; 1590 char path[PATH_MAX]; 1591 struct stat sb; 1592 uint32_t vinfo[8] = { 0, }; 1593 1594 // look up pathnames & file IDs 1595 result = ENAMETOOLONG; 1596 1597 makebootpath(path, "/" kRecoveryBootDir); 1598 if (stat(path, &sb) == -1) { 1599 result = errno; 1600 goto finish; 1601 } 1602 vinfo[kSystemFolderIdx] = (uint32_t)sb.st_ino; 1603 1604 // append boot.efi 1605 pathcat(path, "/"); 1606 pathcat(path, basename(up->caches->efibooter.rpath)); 1607 if (stat(path, &sb) == -1) { 1608 result = errno; 1609 goto finish; 1610 } 1611 vinfo[kEFIBooterIdx] = (uint32_t)sb.st_ino; 1612 1613 if ((result = sBLSetBootFinderInfo(up, vinfo))) { 1614 OSKextLog(NULL, up->warnLogSpec, 1615 "Warning: found recovery booter but couldn't bless it."); 1616 } 1617 1618finish: 1619 return result; 1620} 1621 1622// 17769081 tracks using this more, but ENOENT can't be right for all 1623#define RECERR(up, opres, warnmsg) do { \ 1624 if (opres == -1 && errno == ENOENT) { \ 1625 opres = 0; \ 1626 } \ 1627 if (opres) { \ 1628 if (warnmsg) { \ 1629 OSKextLog(NULL, up->warnLogSpec, warnmsg); \ 1630 } \ 1631 if (firstErr == 0) { \ 1632 OSKextLog(NULL, up->warnLogSpec, "capturing err %d / %d", \ 1633 opres, errno); \ 1634 firstErr = opres; \ 1635 if (firstErr == -1) firstErrno = errno; \ 1636 } \ 1637 } \ 1638 } while(0) 1639 1640OSStatus 1641BREraseBootFiles(CFURLRef srcVolRoot, CFStringRef helperBSDName) 1642{ 1643 OSStatus result = ELAST + 1; 1644 int opres, firstErrno, firstErr = 0; 1645 char path[PATH_MAX], prevRPS[PATH_MAX], nextRPS[PATH_MAX]; 1646 struct stat sb; 1647 uint32_t zerowords[8] = { 0, }; 1648 unsigned i; 1649 struct updatingVol up = { /* NULL, ... */ }, *upp = &up; 1650 up.curbootfd = -1; 1651 1652 // defend libBootRoot entry point 1653 if (!srcVolRoot || !helperBSDName) { 1654 result = EINVAL; goto finish; 1655 } 1656 1657 opres = initContext(&up, srcVolRoot, helperBSDName, kBROptsNone); 1658 if (opres) { 1659 result = opres; goto finish; 1660 } 1661 1662 if ((opres = mountBoot(&up))) { // sets curMount 1663 result = opres; goto finish; 1664 } 1665 1666 // generally best effort 1667 1668 // bless recovery booter if present; else unbless volume 1669 if ((blessRecovery(&up))) { 1670 if ((opres = sBLSetBootFinderInfo(&up, zerowords))) { 1671 firstErr = opres; 1672 OSKextLog(NULL, up.warnLogSpec, 1673 "Warning: couldn't unbless %s", up.curMount); 1674 } 1675 } 1676 1677 // kill label 1678 opres = nukeBRLabels(&up); 1679 RECERR(upp, opres,"Warning: trouble nuking (inactive?) Boot!=Root label."); 1680 1681 // unlink booters 1682 if (up.caches->ofbooter.rpath[0]) { 1683 pathcpy(path, up.curMount); 1684 pathcat(path, up.caches->ofbooter.rpath); 1685 opres = sunlink(up.curbootfd, path); 1686 RECERR(upp, opres, "couldn't unlink OF booter" /* remove w/9217695 */); 1687 } 1688 if (up.caches->efibooter.rpath[0]) { 1689 pathcpy(path, up.curMount); 1690 pathcat(path, up.caches->efibooter.rpath); 1691 opres = sunlink(up.curbootfd, path); 1692 RECERR(upp, opres, "couldn't unlink EFI booter" /* NULL w/9217695 */); 1693 } 1694 1695 // find & nuke all RPS directories 1696 opres = FindRPSDir(&up, prevRPS, up.dstdir, nextRPS); 1697 if (opres == 0) { 1698 opres = eraseRPS(&up, prevRPS); 1699 RECERR(upp, opres, "Warning: trouble erasing R."); 1700 opres = eraseRPS(&up, up.dstdir); 1701 RECERR(upp, opres, "Warning: trouble erasing P."); 1702 opres = eraseRPS(&up, nextRPS); 1703 RECERR(upp, opres, "Warning: trouble erasing S."); 1704 } else { 1705 RECERR(upp, opres, "Warning: couldn't find RPS directories."); 1706 } 1707 1708 for (i=0; i < up.caches->nmisc; i++) { 1709 char *rpath = up.caches->miscpaths[i].rpath; 1710 1711 if (strlcpy(path, up.curMount, PATH_MAX) > PATH_MAX) continue; 1712 if (strlcat(path, rpath, PATH_MAX) > PATH_MAX) continue; 1713 opres = sdeepunlink(up.curbootfd, path); 1714 RECERR(upp, opres, "error unlinking miscpath" /* NULL w/9217695 */); 1715 } 1716 1717 // clean up com.apple.boot.once if it exists 1718 pathcpy(path, up.curMount); 1719 pathcat(path, kBRBootOnceDir); 1720 if (0 == stat(path, &sb)) { 1721 opres = sdeepunlink(up.curbootfd, path); 1722 RECERR(upp, opres, "error unlinking" kBRBootOnceDir); 1723 } 1724 1725 // no errors above, so firstErr == 0 -> success 1726 if (firstErr == -1) { 1727 firstErr = firstErrno; // recorded by RECERR 1728 } 1729 result = firstErr; 1730 1731finish: 1732 unmountBoot(&up); 1733 releaseContext(&up, result); 1734 1735 return result; 1736} 1737 1738 1739/****************************************************************************** 1740* revertState() rolls back incomplete changes 1741******************************************************************************/ 1742static int 1743revertState(struct updatingVol *up) 1744{ 1745 int rval = 0; // optimism to accumulate errors with |= 1746 char path[PATH_MAX], oldpath[PATH_MAX]; 1747 struct bootCaches *caches = up->caches; 1748 Boolean doMisc; 1749 struct stat sb; 1750 1751 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 1752 "Rolling back any incomplete updates."); 1753 1754 switch (up->changestate) { 1755 // inactive booters are still good 1756 case activatedBooters: 1757 // we've blessed the new booters; so let's bless the old ones 1758 pathcat(up->ofdst, OLDEXT); 1759 pathcat(up->efidst, OLDEXT); 1760 // reactivates the old *if* present 1761 rval |= activateBooters(up); 1762 case activatingEFIBooter: 1763 case activatingOFBooter: // unneeded since 'bless' is one op 1764 case copiedBooters: 1765 case copyingEFIBooter: 1766 if (caches->efibooter.rpath[0]) { 1767 makebootpath(path, caches->efibooter.rpath); 1768 pathcpy(oldpath, path); // old ones are blessed; rename 1769 pathcat(oldpath, OLDEXT); 1770 // only unlink current booter if old one present 1771 if (stat(oldpath, &sb) == 0) { 1772 (void)sunlink(up->curbootfd, path); 1773 rval |= srename(up->curbootfd, oldpath, path); 1774 } 1775 } 1776 1777 case copyingOFBooter: 1778 if (caches->ofbooter.rpath[0]) { 1779 makebootpath(path, caches->ofbooter.rpath); 1780 pathcpy(oldpath, path); 1781 pathcat(oldpath, OLDEXT); 1782 // only unlink current booter if old one present 1783 if (stat(oldpath, &sb) == 0) { 1784 (void)sunlink(up->curbootfd, path); 1785 rval |= srename(up->curbootfd, oldpath, path); 1786 } 1787 } 1788 1789 // XX 1790 // case copyingMisc: 1791 // would clean up the .new turds 1792 1793 case noLabel: 1794 // XX hacky (c.f. nukeFallbacks which nukes .disabled label) 1795 doMisc = up->doMisc; 1796 up->doMisc = false; 1797 rval |= activateMisc(up); // writes new label if !doMisc 1798 up->doMisc = doMisc; 1799 1800 case nothingSerious: 1801 // everything is good 1802 break; 1803 } 1804 1805finish: 1806 if (rval) { 1807 OSKextLog(NULL, kOSKextLogErrorLevel, 1808 "error rolling back incomplete updates."); 1809 } 1810 1811 return rval; 1812}; 1813 1814/****************************************************************************** 1815* mountBoot digs in for the root, and mounts up the Apple_Boots 1816* mountpoint -> up->curMount 1817******************************************************************************/ 1818static int 1819_mountBootDA(struct updatingVol *up) 1820{ 1821 int rval = ELAST + 1; 1822 CFStringRef mountargs[] = { CFSTR("perm"), CFSTR("nobrowse"), NULL }; 1823 DADissenterRef dis = (void*)kCFNull; 1824 CFDictionaryRef ddesc = NULL; 1825 CFURLRef volURL; 1826 1827 if (!(up->curBoot=DADiskCreateFromBSDName(nil,up->dasession,up->bsdname))){ 1828 goto finish; 1829 } 1830 1831 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 1832 "Mounting %s...", up->bsdname); 1833 1834 // DADiskMountWithArgument might call _daDone before it returns (e.g. if it 1835 // knows your request is impossible ...) 1836 // _daDone updates our 'dis[senter]' 1837 DADiskMountWithArguments(up->curBoot, NULL/*mnt*/,kDADiskMountOptionDefault, 1838 _daDone, &dis, mountargs); 1839 1840 // ... so we use kCFNull and check the value before CFRunLoopRun() 1841 if (dis == (void*)kCFNull) { 1842 CFRunLoopRun(); // stopped by _daDone (which updates 'dis') 1843 } 1844 if (dis) { 1845 rval = DADissenterGetStatus(dis); 1846 // only an error if it's not already mounted 1847 if (rval != kDAReturnBusy) { 1848 goto finish; 1849 } 1850 } 1851 1852 // get and stash the mountpoint of the boot partition 1853 if (!(ddesc = DADiskCopyDescription(up->curBoot))) goto finish; 1854 volURL = CFDictionaryGetValue(ddesc, kDADiskDescriptionVolumePathKey); 1855 if (!volURL || CFGetTypeID(volURL) != CFURLGetTypeID()) goto finish; 1856 if (!CFURLGetFileSystemRepresentation(volURL, true /*resolve base*/, 1857 (UInt8*)up->curMount, PATH_MAX)) goto finish; 1858 1859 // success 1860 rval = 0; 1861 1862finish: 1863 if (rval) { 1864 if (rval != ELAST + 1) { 1865 if (rval == -1) rval = errno; 1866 OSKextLog(NULL, up->errLogSpec, 1867 "Failed to mount helper (%d/%#x): %s", rval, 1868 rval & ~(err_local|err_local_diskarbitration), strerror(rval)); 1869 } else { 1870 OSKextLog(NULL, up->errLogSpec,"Failed to mount helper partition."); 1871 } 1872 } 1873 1874 if (ddesc) CFRelease(ddesc); 1875 if (dis && dis != (void*)kCFNull) { // for spurious CFRunLoopRun() return 1876 CFRelease(dis); 1877 } 1878 1879 return rval; 1880} 1881 1882/* _mountBootBuiltIn() will mount with mount(2) in /var/run. Use 1883 * _findMountedhelper() first to see if it's already mounted. */ 1884// Creating BRMNT_PARENT instead of using _PATH_VARRUN because the latter 1885// contains a trailing '/' and lacks the /private that mount(2) expects. 1886#define BRMNT_PARENT "/private/var/run" 1887#define BRMNT BRMNT_PARENT "/brmnt" 1888static int 1889_mountBootBuiltIn(struct updatingVol *up) 1890{ 1891 int bsderr, rval = ELAST + 1; // all paths should set rval 1892 int vrfd = -1; 1893 int fd = -1; 1894 struct stat sb; 1895 char devpath[DEVMAXPATHSIZE]; 1896 struct hfs_mount_args hfsargs; 1897 1898 // establish parent fd (assume /var/run safe) 1899 if (((vrfd = open(_PATH_VARRUN, O_RDONLY))) == -1) { 1900 rval = vrfd; LOGERRxlate(up, _PATH_VARRUN, NULL, rval); goto finish; 1901 } 1902 1903 // examine any existing filesystem object at intended mount point 1904 // [Can't use sopen() since BRMNT already hosts a mount.] 1905 fd = open(BRMNT, O_RDONLY); 1906 1907 // if it exists but isn't a directory, nuke it 1908 if (fd != -1 && fstat(fd, &sb)==0 && S_ISDIR(sb.st_mode)==false) { 1909 if ((bsderr = sunlink(vrfd, BRMNT))) { 1910 rval = bsderr; LOGERRxlate(up, BRMNT, NULL, rval); goto finish; 1911 } 1912 // it should be gone now: reset fd, etc 1913 close(fd); 1914 fd = open(BRMNT, O_RDONLY); 1915 if (fd != -1) { 1916 rval = EEXIST; LOGERRxlate(up, BRMNT, NULL, rval); goto finish; 1917 } 1918 } 1919 1920 // If BRMNT exists, it is a directory; if not, create it. 1921 if (fd == -1 && errno == ENOENT) { 1922 if ((bsderr = smkdir(vrfd, BRMNT, kCacheDirMode))) { 1923 rval = bsderr; LOGERRxlate(up, "mkdir", BRMNT, rval); goto finish; 1924 } 1925 } 1926 1927 // set up args & mount 1928 bzero(&hfsargs, sizeof(hfsargs)); 1929 // _PATH_DEV contains a trailing '/' 1930 (void)snprintf(devpath, sizeof(devpath), _PATH_DEV "%s", up->bsdname); 1931 hfsargs.fspec = devpath; 1932 if ((bsderr = mount("hfs", BRMNT, MNT_DONTBROWSE, &hfsargs))) { 1933 rval = bsderr; LOGERRxlate(up, "mount", BRMNT, rval); goto finish; 1934 } 1935 1936 // record result in context 1937 if (strlcpy(up->curMount, BRMNT, MNAMELEN) >= MNAMELEN) { 1938 rval = EOVERFLOW; LOGERRxlate(up,up->curMount,NULL,rval); goto finish; 1939 } 1940 1941 // success 1942 rval = 0; 1943 1944finish: 1945 if (fd != -1) close(fd); 1946 if (vrfd != -1) close(vrfd); 1947 1948 return rval; 1949} 1950 1951// loop copied from kextd_watchvol.c:reconsiderVolumes() 1952static int 1953_findMountedHelper(struct updatingVol *up) 1954{ 1955 int rval = ELAST + 1; 1956 int nfsys, i; 1957 int bufsz; 1958 struct statfs *mounts = NULL; 1959 1960 // get mount list 1961 if (-1 == (nfsys = getfsstat(NULL, 0, MNT_NOWAIT))) { 1962 rval = errno; goto finish; 1963 } 1964 bufsz = nfsys * sizeof(struct statfs); 1965 if (!(mounts = malloc(bufsz))) { 1966 rval = errno; goto finish; 1967 } 1968 if (-1 == getfsstat(mounts, bufsz, MNT_NOWAIT)) { 1969 rval = errno; goto finish; 1970 } 1971 1972 // see whether the filesystem is already mounted somewhere 1973 for (i = 0; i < nfsys; i++) { 1974 struct statfs *sfs = &mounts[i]; 1975 if (strlen(sfs->f_mntfromname) < sizeof(_PATH_DEV) || 1976 0 != strcmp(sfs->f_fstypename, "hfs")) { 1977 continue; 1978 } 1979 if (0 == strcmp(sfs->f_mntfromname+strlen(_PATH_DEV), up->bsdname)){ 1980 if (strlcpy(up->curMount, sfs->f_mntonname, MNAMELEN)>=MNAMELEN) { 1981 rval = EOVERFLOW; goto finish; 1982 } 1983 // we found it! 1984 rval = 0; 1985 goto finish; 1986 } 1987 } 1988 1989 // default = not found (success in loop) 1990 rval = ENOENT; 1991 1992finish: 1993 if (mounts) free(mounts); 1994 1995 return rval; 1996} 1997 1998static int 1999mountBoot(struct updatingVol *up) 2000{ 2001 int errnum, rval = ELAST + 1; 2002 CFStringRef str; 2003 struct statfs bsfs; 2004 uint32_t mntgoal; 2005 struct stat sb; 2006 2007 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2008 "Mounting helper partition..."); 2009 2010 // request the Apple_Boot mount 2011 str = (CFStringRef)CFArrayGetValueAtIndex(up->boots, up->bootIdx); 2012 if (!str || CFGetTypeID(str) != CFStringGetTypeID()) { 2013 goto finish; 2014 } 2015 if (!CFStringGetFileSystemRepresentation(str,up->bsdname,DEVMAXPATHSIZE)){ 2016 goto finish; 2017 } 2018 if (up->dasession) { 2019 if ((errnum = _mountBootDA(up))) { 2020 rval = errnum; goto finish; // error logged by function 2021 } 2022 } else if (_findMountedHelper(up) == ENOENT && 2023 (errnum = _mountBootBuiltIn(up))) { 2024 rval = errnum; goto finish; // error logged by function 2025 } 2026 2027 // Sec: get a non-spoofable handle to the current helper (extend trust) 2028 if (-1 == (up->curbootfd = open(up->curMount, O_RDONLY, 0))) { 2029 rval = errno; LOGERRxlate(up, up->curMount, NULL, rval); goto finish; 2030 } 2031 // if the source volume still exists, we now have fd's for source & dest 2032 if (fstat(up->caches->cachefd, &sb)) { 2033 rval = errno; LOGERRxlate(up, "cachefd MIA?", NULL, rval); goto finish; 2034 } 2035 2036 // Make sure the mount is read/write and has owners enabled. 2037 // Because helper partitions should always have owners enabled 2038 // and because we soft-unmount afterwards, we don't attempt to 2039 // restore this state. 2040 if (fstatfs(up->curbootfd, &bsfs)) { 2041 rval = errno; LOGERRxlate(up, "curboot MIA?", NULL, rval); goto finish; 2042 } 2043 mntgoal = bsfs.f_flags; 2044 mntgoal &= ~(MNT_RDONLY|MNT_IGNORE_OWNERSHIP); 2045 if ((bsfs.f_flags != mntgoal) && updateMount(up->curMount, mntgoal)) { 2046 OSKextLog(NULL, up->warnLogSpec, 2047 "Warning: couldn't update mount to read/write + owners"); 2048 } 2049 2050 // we only support 128+ MB Apple_Boot partitions 2051 if (bsfs.f_blocks * bsfs.f_bsize < (128 * 1<<20)) { 2052 rval = EFTYPE; 2053 OSKextLog(NULL, up->errLogSpec, "skipping Apple_Boot helper < 128 MB."); 2054 goto finish; 2055 } 2056 2057 // If not using the default directories, confirm targetDir exists. 2058 if (!up->useOnceDir && up->flatTarget[0]) { 2059 char path[PATH_MAX]; 2060 pathcpy(path, up->curMount); 2061 pathcat(path, up->flatTarget); 2062 if (stat(path, &sb) != 0) { 2063 if (errno == ENOENT) { 2064 rval = ENOENT; 2065 LOGERRxlate(up, "target directory must exist", path, rval); 2066 goto finish; 2067 } 2068 } else if (!S_ISDIR(sb.st_mode)) { 2069 rval = ENOTDIR; LOGERRxlate(up, path, NULL, rval); goto finish; 2070 } 2071 } 2072 2073 // success 2074 rval = 0; 2075 2076finish: 2077 if (rval != 0 && (up->curBoot || up->curMount[0])) { 2078 (void)unmountBoot(up); // undo anything significant 2079 } 2080 2081 return rval; 2082} 2083 2084/****************************************************************************** 2085* unmountBoot 2086* attempt to unmount; no worries on failure 2087******************************************************************************/ 2088static void 2089unmountBoot(struct updatingVol *up) 2090{ 2091 int errnum = 0; 2092 DADissenterRef dis = (void*)kCFNull; 2093 2094 // clean up curbootfd 2095 if (up->curbootfd != -1) { 2096 close(up->curbootfd); 2097 up->curbootfd = -1; 2098 } 2099 2100 // specifying a target directory => might not be a helper volume! 2101 if (up->flatTarget[0]) return; 2102 2103 if (up->curMount[0]) { 2104 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2105 "Unmounting helper partition %s.", up->bsdname); 2106 } 2107 2108 // clean up any DiskArb-mounted filesystem 2109 if (up->curBoot) { 2110 // _daDone populates 'dis'[senter] 2111 DADiskUnmount(up->curBoot,kDADiskMountOptionDefault,_daDone,&dis); 2112 if (dis == (void*)kCFNull) { // DA.Unmount can call _daDone 2113 CFRunLoopRun(); 2114 } 2115 2116 // if that didn't work, just log 2117 if (dis) { 2118 OSKextLog(NULL, up->warnLogSpec, 2119 "%s didn't unmount, leaving mounted", up->bsdname); 2120 if (dis != (void*)kCFNull) { 2121 CFRelease(dis); 2122 } 2123 } 2124 up->curMount[0] = '\0'; // only try to unmount once 2125 CFRelease(up->curBoot); 2126 up->curBoot = NULL; 2127 } 2128 2129 // unmount anything mounted by _mountBuiltIn() 2130 if (up->dasession == NULL && up->curMount[0] != '\0') { 2131 if (unmount(up->curMount, 0)) { 2132 errnum = errno; 2133 } 2134 up->curMount[0] = '\0'; // only try to unmount once 2135 } 2136 2137 if (errnum) { 2138 OSKextLog(NULL, up->errLogSpec, 2139 "Failed to unmount helper (%d/%#x): %s", errnum, 2140 errnum & ~(err_local|err_local_diskarbitration), strerror(errnum)); 2141 } 2142} 2143 2144 2145/****************************************************************************** 2146* ucopyRPS unlinks old/copies new RPS content w/o activating 2147* RPS files are considered important -- non-zero file sizes only! 2148* XX could validate the kernel with Mach-o header 2149* several intervening helpers including eraseRPS() 2150******************************************************************************/ 2151static int 2152writeBootPrefs(struct updatingVol *up, char *dstpath) 2153{ 2154 int opres, rval = ELAST + 1; 2155 CFDataRef bpdata = NULL; 2156 char dstparent[PATH_MAX]; 2157 ssize_t len; 2158 int fd = -1; 2159 2160 // create data to be written (uses up->useOnceDir from mountBoot) 2161 bpdata = createBootPrefData(up, up->host_uuid, up->bpoverrides); 2162 if (!bpdata) { rval = ENOMEM; goto finish; } 2163 2164 // recursively create the parent directory 2165 if (strlcpy(dstparent,dirname(dstpath),PATH_MAX) >= PATH_MAX) { 2166 rval = EOVERFLOW; goto finish; 2167 } 2168 opres = sdeepmkdir(up->curbootfd, dstparent, kCacheDirMode); 2169 if (opres) { 2170 rval = opres; goto finish; 2171 } 2172 2173 // sopen adds O_EXCL to O_CREAT 2174 (void)sunlink(up->curbootfd, dstpath); 2175 fd = sopen(up->curbootfd, dstpath, O_WRONLY|O_CREAT, kCacheFileMode); 2176 if (fd == -1) { 2177 rval = errno; goto finish; 2178 } 2179 2180 len = CFDataGetLength(bpdata); 2181 if (write(fd,CFDataGetBytePtr(bpdata),len) != len) { 2182 rval = errno; goto finish; 2183 } 2184 2185 rval = 0; 2186 2187finish: 2188 if (rval) { 2189 LOGERRxlate(up, dstpath, NULL, rval); 2190 } 2191 2192 if (fd != -1) close(fd); 2193 if (bpdata) CFRelease(bpdata); 2194 2195 return rval; 2196} 2197 2198// correctly erase (hopefully old :) items in the Apple_Boot 2199static int 2200eraseRPS(struct updatingVol *up, char *toErase) 2201{ 2202 int rval = ELAST+1; 2203 char path[PATH_MAX]; 2204 struct stat sb; 2205 2206 // if nothing to erase, return cleanly 2207 if (stat(toErase, &sb) == -1 && errno == ENOENT) { 2208 rval = 0; 2209 goto finish; 2210 } 2211 2212 if (up->caches->erpropcache->rpath) { 2213 // pathc*() seed errno 2214 pathcpy(path, toErase); 2215 pathcat(path, up->caches->erpropcache->rpath); 2216 // szerofile() won't complain if it is missing 2217 if (szerofile(up->curbootfd, path)) 2218 goto finish; 2219 } 2220 2221 rval = sdeepunlink(up->curbootfd, toErase); 2222 2223finish: 2224 if (rval) { 2225 OSKextLog(NULL, up->errLogSpec | kOSKextLogFileAccessFlag, 2226 "%s - %s. errno %d %s", 2227 __FUNCTION__, toErase, errno, strerror(errno)); 2228 } 2229 2230 return rval; 2231} 2232 2233static int 2234_writeFDEPropsToHelper(struct updatingVol *up, char *dstpath) 2235{ 2236 int errnum, rval = ELAST + 1; // everyone sets it? 2237 char *stage; 2238 CFDictionaryRef matching; // IOServiceGetMatchingServices() releases 2239 io_service_t helper = IO_OBJECT_NULL; 2240 CFNumberRef unitNum = NULL; 2241 CFNumberRef partNum = NULL; 2242 int partnum; 2243 const void *keys[2], *vals[2]; 2244 CFDictionaryRef props = NULL; 2245 io_service_t bearer = IO_OBJECT_NULL; 2246 CFStringRef partType = NULL; 2247 CFStringRef partBSD = NULL; 2248 char csbsd[DEVMAXPATHSIZE]; 2249 2250 stage = "check argument"; 2251 if (up->onAPM) { 2252 rval = EINVAL; goto finish; 2253 } 2254 2255 stage = "find current helper partition"; 2256 if (!(matching = IOBSDNameMatching(kIOMasterPortDefault, 0, up->bsdname))){ 2257 rval = ENOMEM; goto finish; 2258 } 2259 helper = IOServiceGetMatchingService(kIOMasterPortDefault, matching); 2260 matching = NULL; // IOServiceGetMatchingService() released 2261 if (!helper) { 2262 rval = ENOENT; goto finish; 2263 } 2264 unitNum = (CFNumberRef)IORegistryEntryCreateCFProperty(helper, 2265 CFSTR(kIOBSDUnitKey), nil, 0); 2266 if (!unitNum || CFGetTypeID(unitNum) != CFNumberGetTypeID()) { 2267 rval = ENODEV; goto finish; 2268 } 2269 partNum = (CFNumberRef)IORegistryEntryCreateCFProperty(helper, 2270 CFSTR(kIOMediaPartitionIDKey), nil, 0); 2271 if (!partNum || CFGetTypeID(partNum) != CFNumberGetTypeID()) { 2272 rval = ENODEV; goto finish; 2273 } 2274 2275 stage = "create description of corresponding data partition"; 2276 CFNumberGetValue(partNum, kCFNumberIntType, &partnum); 2277 CFRelease(partNum); 2278 partNum = NULL; 2279 // in GPT, data the partition comes before the Apple_Boot 2280 if (--partnum <= 0) { 2281 rval = ENODEV; goto finish; 2282 } 2283 partNum = CFNumberCreate(nil, kCFNumberIntType, &partnum); 2284 if (!partNum) { 2285 rval = ENOMEM; goto finish; 2286 } 2287 // create property and matching dictionaries 2288 keys[0] = CFSTR(kIOMediaPartitionIDKey); 2289 vals[0] = partNum; 2290 keys[1] = CFSTR(kIOBSDUnitKey); 2291 vals[1] = unitNum; 2292 if (!(props = CFDictionaryCreate(nil, keys, vals, 2, 2293 &kCFTypeDictionaryKeyCallBacks, 2294 &kCFTypeDictionaryValueCallBacks))) { 2295 rval = ENOMEM; goto finish; 2296 } 2297 keys[0] = CFSTR(kIOProviderClassKey); 2298 vals[0] = CFSTR(kIOMediaClass); 2299 keys[1] = CFSTR(kIOPropertyMatchKey); 2300 vals[1] = props; 2301 if (!(matching = CFDictionaryCreate(nil, keys, vals, 2, 2302 &kCFTypeDictionaryKeyCallBacks, 2303 &kCFTypeDictionaryValueCallBacks))) { 2304 rval = ENOMEM; goto finish; 2305 } 2306 2307 stage = "find & validate data partition"; 2308 bearer = IOServiceGetMatchingService(kIOMasterPortDefault, matching); 2309 matching = NULL; // IOServiceGetMatchingService() released 2310 if (!bearer) { 2311 rval = ENOENT; goto finish; 2312 } 2313 // extract BSD Name 2314 partBSD = (CFStringRef)IORegistryEntryCreateCFProperty(bearer, 2315 CFSTR(kIOBSDNameKey), nil, 0); 2316 if (!partBSD || CFGetTypeID(partBSD) != CFStringGetTypeID()) { 2317 rval = ENODEV; goto finish; 2318 } 2319 if (!CFStringGetFileSystemRepresentation(partBSD, csbsd, sizeof(csbsd))){ 2320 rval = EOVERFLOW; goto finish; 2321 } 2322 // the data partition's type must be Apple_CoreStorage 2323 partType = (CFStringRef)IORegistryEntryCreateCFProperty(bearer, 2324 CFSTR(kIOMediaContentKey), nil, 0); 2325 if (!partType || CFGetTypeID(partType) != CFStringGetTypeID()) { 2326 rval = ENODEV; goto finish; 2327 } 2328 if (!CFEqual(partType, CFSTR(APPLE_CORESTORAGE_UUID))) { 2329 rval = ENODEV; 2330 LOGERRxlate(up, csbsd, "must be of type Apple_CoreStorage", rval); 2331 stage = NULL; // logged our own error 2332 goto finish; 2333 } 2334 2335 stage = NULL; // writeCSFDEProps logs its own errors 2336 // writeCSFDEProps() uses csbsd's wipe key to encrypt the context data. 2337 if ((errnum=writeCSFDEProps(up->curbootfd,up->csfdeprops,csbsd,dstpath))){ 2338 rval = errnum; goto finish; 2339 } 2340 2341 // success! 2342 rval = 0; 2343 2344finish: 2345 if (rval && stage) { 2346 OSKextLog(NULL, up->errLogSpec | kOSKextLogFileAccessFlag, 2347 "%s() failed trying to %s", __func__, stage); 2348 } 2349 2350 if (partBSD) CFRelease(partBSD); 2351 if (partType) CFRelease(partType); 2352 if (bearer != IO_OBJECT_NULL) IOObjectRelease(bearer); 2353 if (props) CFRelease(props); 2354 if (partNum) CFRelease(partNum); 2355 if (unitNum) CFRelease(unitNum); 2356 if (helper != IO_OBJECT_NULL) IOObjectRelease(helper); 2357 2358 return rval; 2359} 2360 2361/* 2362 * ucopyRPS - copy new RPS directory to "inactive" location 2363 * bails on any error because only a whole RPS dir makes sense 2364 */ 2365static int 2366ucopyRPS(struct updatingVol *up) 2367{ 2368 int bsderr, rval = ELAST + 1; // generic safest 2369 char prevRPS[PATH_MAX], curRPS[PATH_MAX], discard[PATH_MAX]; 2370 char *erdir; 2371 unsigned i; 2372 char srcpath[PATH_MAX], dstpath[PATH_MAX]; 2373#if DEV_KERNEL_SUPPORT 2374 CFStringRef my_kcsuffix = NULL; // must release 2375 Boolean copiedPrefKernel = false; 2376#endif 2377 COMPILE_TIME_ASSERT(sizeof(BOOTPLIST_NAME)==sizeof(BOOTPLIST_APM_NAME)); 2378 2379 2380 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2381 "Copying files used by the booter."); 2382 2383 if ((bsderr = FindRPSDir(up, prevRPS, curRPS, discard))) { 2384 rval = bsderr; goto finish; // error logged by function 2385 } 2386 2387 if (up->flatTarget[0] || up->useOnceDir) { 2388 // copy desired target into dstdir 2389 pathcpy(up->dstdir, up->curMount); 2390 if (up->useOnceDir) { 2391 pathcat(up->dstdir, kBRBootOnceDir); 2392 } 2393 pathcat(up->dstdir, up->flatTarget); 2394 erdir = curRPS; 2395 } else { 2396 // we're going to copy into the currently-inactive directory 2397 pathcpy(up->dstdir, prevRPS); 2398 erdir = prevRPS; 2399 } 2400 2401 // we expect to have removed it and eraseRPS() doesn't mind it missing 2402 if ((bsderr = eraseRPS(up, up->dstdir))) { 2403 rval = bsderr; goto finish; // error logged by function 2404 } 2405 2406 // create the directory (RPS should not exist?) 2407 if ((bsderr = sdeepmkdir(up->curbootfd, up->dstdir, kCacheDirMode))) { 2408 rval = bsderr; LOGERRxlate(up, up->dstdir, NULL, rval); goto finish; 2409 } 2410 2411#if DEV_KERNEL_SUPPORT 2412 // NOTE - copy_kcsuffix will return ".release" suffix when kcsuffix is 2413 // "kcsuffix=" or "kcsuffix=release". Since the "release" kernel and 2414 // kernelcache file names do NOT have a suffix the for loop for 2415 // extraKernelCachePaths will not match via the CFStringHasSuffix() call and 2416 // we will drop out of the for loop with copiedPrefKernel == false. This is 2417 // by design. The copy of the release kernelcache will happen lower down. 2418 my_kcsuffix = copy_kcsuffix(); 2419 if (up->caches->extraKernelCachePaths && my_kcsuffix) { 2420 int i; 2421 for (i = 0; i < up->caches->nekcp; i++) { 2422 cachedPath *curItem = &up->caches->extraKernelCachePaths[i]; 2423 2424 // until 16140679 gets fixed we can only copy 1 kernelcache to 2425 // Apple_Boot partitions. We use boot-arg kcsuffix to give us a 2426 // hint about which kernelcache to copy - 16929470 2427 CFStringRef tempString; 2428 Boolean hasSuffix; 2429 tempString = CFStringCreateWithCString(NULL, 2430 curItem->rpath, 2431 kCFStringEncodingUTF8); 2432 if (tempString == NULL) { 2433 continue; 2434 } 2435 hasSuffix = CFStringHasSuffix(tempString, my_kcsuffix); 2436 SAFE_RELEASE_NULL(tempString); 2437 if (hasSuffix == false) { 2438 continue; 2439 } 2440 pathcpy(srcpath, up->caches->root); 2441 pathcat(srcpath, curItem->rpath); 2442 pathcpy(dstpath, up->dstdir); 2443 pathcat(dstpath, curItem->rpath); 2444 OSKextLog(NULL, kOSKextLogGeneralFlag|kOSKextLogDetailLevel, 2445 "copying %s to %s", srcpath, up->dstdir); 2446 bsderr = scopyitem(up->caches->cachefd, 2447 srcpath, 2448 up->curbootfd, 2449 dstpath); 2450 if (bsderr) { 2451 rval = bsderr == -1 ? errno : bsderr; 2452 OSKextLog(NULL,up->errLogSpec,"Error %d copying %s to %s: %s", 2453 rval, srcpath, dstpath, strerror(rval)); 2454 goto finish; 2455 } 2456 copiedPrefKernel = true; 2457 } // for loop... 2458 } 2459#endif 2460 2461 // and loop 2462 for (i = 0; i < up->caches->nrps; i++) { 2463 cachedPath *curItem = &up->caches->rpspaths[i]; 2464 2465 // 15860955: skip release kernel if preferred has already been copied 2466 if (copiedPrefKernel && curItem == up->caches->kext_boot_cache_file) { 2467 continue; 2468 } 2469 2470 pathcpy(srcpath, up->caches->root); 2471 pathcat(srcpath, curItem->rpath); 2472 pathcpy(dstpath, up->dstdir); 2473 // EfiLoginUI.a still digs down to its cache dirs 2474 if ((up->flatTarget[0] || up->useOnceDir) 2475 && curItem != up->caches->efidefrsrcs 2476 && curItem != up->caches->efiloccache) { 2477 /* XX 10561671: basename unsafe */ 2478 pathcat(dstpath, "/"); 2479 pathcat(dstpath, basename(curItem->rpath)); 2480 } else { 2481 pathcat(dstpath, curItem->rpath); 2482 } 2483 2484 // check for special files; first Boot.plist 2485 if (curItem == up->caches->bootconfig) { 2486 // PR-5115900 - call it com.apple.boot.plist on APM since Tiger 2487 // (since Tiger bless scribbles on com.apple.Boot.plist) 2488 if (up->onAPM) { 2489 char * plistNamePtr; 2490 // see assert above 2491 plistNamePtr = strstr(dstpath, BOOTPLIST_NAME); 2492 if (plistNamePtr) { 2493 strncpy(plistNamePtr, BOOTPLIST_APM_NAME, strlen(BOOTPLIST_NAME)); 2494 } 2495 } 2496 // write customized com.apple.Boot.plist data 2497 if ((bsderr = writeBootPrefs(up, dstpath))) { 2498 rval = bsderr; goto finish; // error logged by function 2499 } 2500 } else { 2501 // could deny zero-size cookies, busted Mach-O, etc here 2502 // scopyitem creates any intermediate directories 2503 OSKextLog(NULL, kOSKextLogGeneralFlag|kOSKextLogDetailLevel, 2504 "copying %s to %s", srcpath, up->dstdir); 2505 bsderr=scopyitem(up->caches->cachefd,srcpath,up->curbootfd,dstpath); 2506 if (bsderr) { 2507 // erpropcache, efiloccache are optional 2508 if ((curItem == up->caches->erpropcache || 2509 curItem == up->caches->efiloccache) 2510 && bsderr == -1 && errno == ENOENT) { 2511 ; // no-op to allow real CSFDE data to be written 2512 } else { 2513 rval = bsderr == -1 ? errno : bsderr; 2514 OSKextLog(0,up->errLogSpec,"Error %d copying %s to %s: %s", 2515 rval, srcpath, dstpath, strerror(rval)); 2516 goto finish; 2517 } 2518 } 2519 2520 // having copied any existing file (for HFS conversions), 2521 // we now prefer the real data 2522 if (up->csfdeprops && curItem == up->caches->erpropcache && 2523 up->onAPM == false) { 2524 if ((bsderr = _writeFDEPropsToHelper(up, dstpath))) { 2525 rval = bsderr; goto finish; // error logged by function 2526 } 2527 } 2528 } 2529 } 2530 2531 // XX EFI is happier if there is a SystemVersion.plist it can find 2532 2533 // 10561691 wasn't fixed until 10.8 so we implement "mostly flat" 2534 // for 10.7-era systems when flatTarget is set. 2535 // re-write correctly-encrypted context to secondary location 2536 if ((up->flatTarget[0] || up->useOnceDir) 2537 && up->caches->erpropTSOnly == false && up->onAPM == false 2538 && up->caches->erpropcache && up->csfdeprops) { 2539 pathcpy(dstpath, erdir); 2540 pathcat(dstpath, up->caches->erpropcache->rpath); 2541 if ((bsderr = _writeFDEPropsToHelper(up, dstpath))) { 2542 rval = bsderr; goto finish; // error logged by function 2543 } 2544 2545 if (up->caches->efidefrsrcs) { 2546 pathcpy(srcpath, up->caches->root); 2547 pathcat(srcpath, up->caches->efidefrsrcs->rpath); 2548 pathcpy(dstpath, erdir); 2549 pathcat(dstpath, up->caches->efidefrsrcs->rpath); 2550 bsderr=scopyitem(up->caches->cachefd,srcpath,up->curbootfd,dstpath); 2551 if (bsderr) { 2552 rval = bsderr == -1 ? errno : bsderr; 2553 OSKextLog(NULL,up->errLogSpec,"Error %d copying %s to %s: %s", 2554 rval, srcpath, dstpath, strerror(rval)); 2555 goto finish; 2556 } 2557 } 2558 } 2559 2560 // success 2561 rval = 0; 2562 2563finish: 2564#if DEV_KERNEL_SUPPORT 2565 SAFE_RELEASE(my_kcsuffix); 2566#endif 2567 return rval; 2568} 2569 2570/****************************************************************************** 2571* ucopyMisc writes misc files to .new (inactive) name 2572******************************************************************************/ 2573static int 2574ucopyMisc(struct updatingVol *up) 2575{ 2576 int bsderr, rval = -1; 2577 unsigned i, nprocessed = 0; 2578 char srcpath[PATH_MAX], dstpath[PATH_MAX]; 2579 struct stat sb; 2580 2581 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2582 "Copying files read before the booter runs."); 2583 2584 for (i = 0; i < up->caches->nmisc; i++) { 2585 pathcpy(srcpath, up->caches->root); 2586 pathcat(srcpath, up->caches->miscpaths[i].rpath); 2587 makebootpath(dstpath, up->caches->miscpaths[i].rpath); 2588 pathcat(dstpath, ".new"); 2589 2590 if (stat(srcpath, &sb) == 0) { 2591 // file exists and is accessible 2592 if ((bsderr = scopyitem(up->caches->cachefd, srcpath, 2593 up->curbootfd, dstpath))) { 2594 if (bsderr == -1) bsderr = errno; 2595 OSKextLog(NULL, up->errLogSpec, "Error %d copying %s to %s: %s", 2596 bsderr, srcpath, dstpath, strerror(bsderr)); 2597 continue; 2598 } 2599 } else if (errno != ENOENT) { 2600 continue; 2601 } 2602 2603 nprocessed++; 2604 } 2605 2606 if (nprocessed == i) { 2607 rval = 0; 2608 } else { 2609 rval = errno; 2610 } 2611 2612finish: 2613 if (rval) { 2614 LOGERRxlate(up, __func__, "failure copying pre-booter files", rval); 2615 } 2616 2617 return rval; 2618} 2619 2620/****************************************************************************** 2621* moveLabels() deactivates the but preserves it for later 2622* activateMisc() will move these back if needed 2623* no label -> hint of indeterminate state (label key in plist/other file??) 2624* XX put/switch in some sort of "(updating!)" label (see BL[ess] routines) 2625******************************************************************************/ 2626static int 2627moveLabels(struct updatingVol *up) 2628{ 2629 int rval = -1; 2630 char path[PATH_MAX]; 2631 struct stat sb; 2632 int fd = -1; 2633 2634 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2635 "Moving aside old label."); 2636 2637 // pathc*() seed errno 2638 makebootpath(path, up->caches->label->rpath); 2639 if (0 == (stat(path, &sb))) { 2640 char newpath[PATH_MAX]; 2641 unsigned char nulltype[32] = {'\0', }; 2642 2643 // rename 2644 pathcpy(newpath, path); 2645 pathcat(newpath, NEWEXT); 2646 rval = srename(up->curbootfd, path, newpath); 2647 if (rval) goto finish; 2648 2649 // remove magic type/creator 2650 if (-1 == (fd=sopen(up->curbootfd, newpath, O_RDWR, 0))) goto finish; 2651 if(fsetxattr(fd,XATTR_FINDERINFO_NAME,&nulltype,sizeof(nulltype),0,0)) { 2652 goto finish; 2653 } 2654 } 2655 2656 up->changestate = noLabel; 2657 rval = 0; 2658 2659finish: 2660 if (fd != -1) close(fd); 2661 2662 if (rval) { 2663 OSKextLog(NULL, up->errLogSpec, 2664 "%s - Error moving aside old label. errno %d %s.", 2665 __FUNCTION__, errno, strerror(errno)); 2666 } 2667 2668 return rval; 2669} 2670 2671/****************************************************************************** 2672* nukeBRLabels gets rid of the label and .contentDetails files 2673* no label -> hint of indeterminate state (label key in plist/other file?) 2674* someday: some sort of "(updating!)" label? 2675******************************************************************************/ 2676static int 2677nukeBRLabels(struct updatingVol *up) 2678{ 2679 int rval = EOVERFLOW; // path*() 2680 int opres, firstErrno, firstErr = 0; 2681 char labelp[PATH_MAX], dstparent[PATH_MAX]; 2682 struct stat sb; 2683 2684 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2685 "Removing current disk label."); 2686 2687 // .disk_label 2688 makebootpath(labelp, up->caches->label->rpath); 2689 if (0 == (stat(labelp, &sb))) { 2690 opres = sunlink(up->curbootfd, labelp); 2691 RECERR(up, opres, "error removing label" /*NULL w/9217695*/); 2692 } else { 2693 errno = 0; 2694 } 2695 2696 // .disk_label_2x 2697 pathcpy(labelp, up->curMount); 2698 if (up->useOnceDir) { 2699 pathcat(labelp, kBRBootOnceDir); 2700 } 2701 pathcat(labelp, up->caches->label->rpath); 2702 pathcat(labelp, SCALE_2xEXT); // append extension 2703 if (0 == (stat(labelp, &sb))) { 2704 opres = sunlink(up->curbootfd, labelp); 2705 RECERR(up, opres, "error removing .contentDetails" /*NULL w/9217695*/); 2706 } else { 2707 errno = 0; 2708 } 2709 2710 // .disk_label.contentsDetail 2711 pathcpy(labelp, up->curMount); 2712 if (up->useOnceDir) { 2713 pathcat(labelp, kBRBootOnceDir); 2714 } 2715 pathcat(labelp, up->caches->label->rpath); 2716 pathcat(labelp, CONTENTEXT); // append extension 2717 if (0 == (stat(labelp, &sb))) { 2718 opres = sunlink(up->curbootfd, labelp); 2719 RECERR(up, opres, "error removing " CONTENTEXT /*NULL w/9217695*/); 2720 } else { 2721 errno = 0; 2722 } 2723 2724 // and possible .root_uuid 2725 makebootpath(labelp, up->caches->label->rpath); 2726 pathcpy(dstparent, dirname(labelp)); 2727 pathcpy(labelp, dstparent); 2728 pathcat(labelp, "/" kBRRootUUIDFile); 2729 if (0 == (stat(labelp, &sb))) { 2730 opres = sunlink(up->curbootfd, labelp); 2731 RECERR(up, opres, "error removing " kBRRootUUIDFile /*NULL w/9217695*/); 2732 } else { 2733 errno = 0; 2734 } 2735 2736 up->changestate = noLabel; 2737 2738 if (firstErr == -1) errno = firstErrno; 2739 rval = firstErr; 2740 2741finish: 2742 if (rval) 2743 OSKextLog(NULL, kOSKextLogErrorLevel, "Error removing disk label."); 2744 2745 return rval; 2746} 2747 2748/****************************************************************************** 2749* ucopyBooters unlink/copies down booters but doesn't bless them 2750******************************************************************************/ 2751static int 2752ucopyBooters(struct updatingVol *up) 2753{ 2754 int rval = ELAST + 1; 2755 int bsderr; 2756 char srcpath[PATH_MAX], oldpath[PATH_MAX]; 2757 int nbooters = 0; 2758 2759 if (up->caches->ofbooter.rpath[0]) nbooters++; 2760 if (up->caches->efibooter.rpath[0]) nbooters++; 2761 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2762 "Copying new booter%s.", nbooters == 1 ? "" : "s"); 2763 2764 // copy BootX, boot.efi 2765 up->changestate = copyingOFBooter; 2766 if (up->caches->ofbooter.rpath[0]) { 2767 // pathc*() seed errno 2768 pathcpy(srcpath, up->caches->root); 2769 pathcat(srcpath, up->caches->ofbooter.rpath); // <root>/S/L/CS/BootX 2770 makebootpath(up->ofdst, up->caches->ofbooter.rpath); // <boot>/../BootX 2771 pathcpy(oldpath, up->ofdst); 2772 pathcat(oldpath, OLDEXT); // <boot>/S/L/CS/BootX.old 2773 2774 (void)sunlink(up->curbootfd, oldpath); 2775 bsderr = srename(up->curbootfd, up->ofdst, oldpath); 2776 if (bsderr && errno !=ENOENT) { 2777 OSKextLog(NULL, up->errLogSpec, 2778 "%s - Error rename old %s new %s", 2779 __FUNCTION__, up->ofdst, oldpath); 2780 goto finish; 2781 } 2782 if ((bsderr = scopyitem(up->caches->cachefd, srcpath, 2783 up->curbootfd, up->ofdst))) { 2784 rval = bsderr == -1 ? errno : bsderr; 2785 if (!(up->opts & kBRUHelpersOptional)) { 2786 OSKextLog(NULL,up->errLogSpec,"Error %d copying %s to %s: %s", 2787 rval, srcpath, up->ofdst, strerror(rval)); 2788 } 2789 goto finish; 2790 } 2791 } 2792 2793 up->changestate = copyingEFIBooter; 2794 if (up->caches->efibooter.rpath[0]) { 2795 // pathc*() seed errno 2796 pathcpy(srcpath, up->caches->root); 2797 pathcat(srcpath, up->caches->efibooter.rpath); // ... boot.efi 2798 makebootpath(up->efidst, up->caches->efibooter.rpath); 2799 pathcpy(oldpath, up->efidst); 2800 pathcat(oldpath, OLDEXT); 2801 2802 (void)sunlink(up->curbootfd, oldpath); 2803 bsderr = srename(up->curbootfd, up->efidst, oldpath); 2804 if (bsderr && errno != ENOENT) { 2805 OSKextLog(NULL, up->errLogSpec, 2806 "%s - Error rename old %s new %s", 2807 __FUNCTION__, up->efidst, oldpath); 2808 goto finish; 2809 } 2810 if ((bsderr = scopyitem(up->caches->cachefd, srcpath, 2811 up->curbootfd, up->efidst))) { 2812 if (!(up->opts & kBRUHelpersOptional)) { 2813 rval = bsderr == -1 ? errno : bsderr; 2814 OSKextLog(NULL,up->errLogSpec,"Error %d copying %s to %s: %s", 2815 rval, srcpath, up->efidst, strerror(rval)); 2816 } 2817 goto finish; 2818 } 2819 } 2820 2821 up->changestate = copiedBooters; 2822 rval = 0; 2823 2824finish: 2825 // all goto paths log 2826 2827 return rval; 2828} 2829 2830 2831// booters have worst critical:fragile ratio (basically point of no return) 2832/****************************************************************************** 2833* bless recently-copied booters 2834* operatens entirely on up->??dst which allows revertState to use it ..? 2835******************************************************************************/ 2836#define CLOSE(fd) do { (void)close(fd); fd = -1; } while(0) 2837static int 2838activateBooters(struct updatingVol *up) 2839{ 2840 int errnum, rval = ELAST + 1; 2841 int fd = -1; 2842 uint32_t vinfo[8] = { 0, }; 2843 struct stat sb; 2844 char parent[PATH_MAX]; 2845 int nbooters = 0; 2846 BLContext blctx = { 0, BRBLLogFunc, NULL }; 2847 2848 if (up->caches->ofbooter.rpath[0]) nbooters++; 2849 if (up->caches->efibooter.rpath[0]) nbooters++; 2850 2851 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2852 "Activating new booter%s.", nbooters == 1 ? "" : "s"); 2853 2854 // flush everything in this helper partition to disk 2855 if ((errnum = fcntl(up->curbootfd, F_FULLFSYNC))) { 2856 rval = errnum; goto finish; 2857 } 2858 2859 // activate BootX, boot.efi 2860 up->changestate = activatingOFBooter; 2861 if (up->caches->ofbooter.rpath[0]) { 2862 unsigned char tbxichrp[32] = {'t','b','x','i','c','h','r','p','\0',}; 2863 2864 // apply type/creator (assuming same folder as previous, now active) 2865 if (-1==(fd=sopen(up->curbootfd, up->ofdst, O_RDONLY, 0))) { 2866 rval = errno; goto finish; 2867 } 2868 if(fsetxattr(fd,XATTR_FINDERINFO_NAME,&tbxichrp,sizeof(tbxichrp),0,0)){ 2869 rval = errno; goto finish; 2870 } 2871 CLOSE(fd); 2872 2873 // get fileID of booter's enclosing folder 2874 pathcpy(parent, dirname(up->ofdst)); 2875 if (-1 == (fd=sopen(up->curbootfd, parent, O_RDONLY, 0)) 2876 || fstat(fd, &sb)) { 2877 rval = errno; goto finish; 2878 } 2879 CLOSE(fd); 2880 if (sb.st_ino < (__darwin_ino64_t)2<<31) { 2881 vinfo[kSystemFolderIdx] = (uint32_t)sb.st_ino; 2882 } else { 2883 rval = EOVERFLOW; goto finish; 2884 } 2885 } 2886 2887 up->changestate = activatingEFIBooter; 2888 if (up->caches->efibooter.rpath[0]) { 2889 // get file ID 2890 if (-1==(fd=sopen(up->curbootfd, up->efidst, O_RDONLY, 0)) 2891 || fstat(fd, &sb)) { 2892 rval = errno; goto finish; 2893 } 2894 CLOSE(fd); 2895 if (sb.st_ino < (__darwin_ino64_t)2<<31) { 2896 vinfo[kEFIBooterIdx] = (uint32_t)sb.st_ino; 2897 } else { 2898 rval = EOVERFLOW; goto finish; 2899 } 2900 2901 // get folder ID of enclosing folder if not provided by ofbooter 2902 if (!vinfo[0]) { 2903 pathcpy(parent, dirname(up->efidst)); 2904 if (-1 == (fd=sopen(up->curbootfd, parent, O_RDONLY, 0)) 2905 || fstat(fd, &sb)) { 2906 rval = errno; goto finish; 2907 } 2908 CLOSE(fd); 2909 if (sb.st_ino < (__darwin_ino64_t)2<<31) { 2910 vinfo[kSystemFolderIdx] = (uint32_t)sb.st_ino; 2911 } else { 2912 rval = EOVERFLOW; goto finish; 2913 } 2914 } 2915 } 2916 2917 // configure blessing as requested 2918 // FSDefault is a single unique bit. 2919 if (up->blessSpec & kBRBlessFSDefault) { 2920 if ((errnum = sBLSetBootFinderInfo(up, vinfo))) { 2921 rval = errnum; goto finish; 2922 } 2923 } 2924 // BlessFull = (FSDefault | setNVRAM) 2925 if (up->blessSpec == kBRBlessFull) { 2926 if (BLSetEFIBootDevice(&blctx, up->bsdname)) { 2927 rval = ENODEV; goto finish; 2928 } 2929 } 2930 // BlessOnce is a unique bit. Use BLSetEFIBootDeviceOnce() if we 2931 // just made the target the default for the filesystem. 2932 if (up->blessSpec & kBRBlessOnce) { 2933 if (up->blessSpec & kBRBlessFSDefault) { 2934 if (BLSetEFIBootDeviceOnce(&blctx, up->bsdname)) { 2935 rval = ENODEV; goto finish; 2936 } 2937 } else { 2938 if (BLSetEFIBootFileOnce(&blctx, up->efidst)) { 2939 rval = ENODEV; goto finish; 2940 } 2941 } 2942 } 2943 2944 up->changestate = activatedBooters; 2945 2946 // success 2947 rval = 0; 2948 2949finish: 2950 if (fd != -1) close(fd); 2951 2952 if (rval) 2953 OSKextLog(NULL, kOSKextLogErrorLevel, "Error activating booter."); 2954 2955 return rval; 2956} 2957 2958/****************************************************************************** 2959* leap-frog w/rename() 2960******************************************************************************/ 2961static int 2962activateRPS(struct updatingVol *up) 2963{ 2964 int rval = ELAST + 1; 2965 char prevRPS[PATH_MAX], curRPS[PATH_MAX], nextRPS[PATH_MAX]; 2966 2967 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 2968 "Activating files used by the booter."); 2969 2970 // if using default RPS dirs, make fresh one current 2971 if (up->flatTarget[0] == '\0' && up->useOnceDir == false) { 2972 if (FindRPSDir(up, prevRPS, curRPS, nextRPS)) goto finish; 2973 2974 // if current != the one we just populated 2975 if (strncmp(curRPS, up->dstdir, PATH_MAX) != 0) { 2976 // rename prev -> next ... done!? 2977 if (srename(up->curbootfd, prevRPS, nextRPS)) goto finish; 2978 } 2979 } 2980 2981 // thwunk everything to disk (now that essential boot files are in place) 2982 if (fcntl(up->curbootfd, F_FULLFSYNC)) goto finish; 2983 2984 rval = 0; 2985 2986finish: 2987 if (rval) { 2988 OSKextLog(NULL, kOSKextLogErrorLevel, 2989 "Error activating files used by the booter."); 2990 } 2991 2992 return rval; 2993} 2994 2995 2996/****************************************************************************** 2997* activateMisc renames .new files to final names and relabels the volumes 2998* active label indicates an updated helper partition 2999* - construct new label with a trailing number as appropriate 3000* - use BLGenerateLabelData() and overwrite any copied-down label 3001* X need to be consistent throughout regarding missing misc files (esp. label?) 3002******************************************************************************/ 3003/* 3004 * writeLabels() writes correctly-formatted label and related files. 3005 * These files should be removed first via nukeLabels(). 3006 * 3007 * Since com.apple.recovery.boot is generally only present in CoreStorage 3008 * helpers, the net effect of writeLabel()'s policy of 3009 * if (up->bootIdx == 0 || up->detectedRecovery) { 3010 * is that CoreStorage will get .root_uuid files (and matching label data) 3011 * in all Apple_Boot helpers while non-CS (AppleRAID, third party) will get 3012 * 'Mac HD', 'Mac HD 2', ... 'Mac HD <n>' in their helpers. The absence of 3013 * .root_uuid in subsequent helpers should prevent EFI from merging any of 3014 * these non-CS helpers. See 11129639 and related for more details. 3015 */ 3016 3017// see makebootpath() at top of file 3018#define MAKEBOOTPATHcont(path, rpath) do { \ 3019 PATHCPYcont(path, up->curMount); \ 3020 if (up->useOnceDir) { \ 3021 PATHCATcont(path, kBRBootOnceDir); \ 3022 } \ 3023 if (up->flatTarget[0] || up->useOnceDir) { \ 3024 PATHCATcont(path, up->flatTarget); \ 3025 /* XXX 10561671: basename unsafe */ \ 3026 PATHCATcont(path, "/"); \ 3027 PATHCATcont(path, basename(rpath)); \ 3028 } else { \ 3029 PATHCATcont(path, rpath); \ 3030 } \ 3031 } while(0) 3032static int 3033activateMisc(struct updatingVol *up) // rename the .new 3034{ 3035 int rval = ELAST + 1; 3036 char path[PATH_MAX], opath[PATH_MAX]; 3037 unsigned i = 0, nprocessed = 0; 3038 int fd = -1; 3039 struct stat sb; 3040 unsigned char tbxjchrp[32] = { 't','b','x','j','c','h','r','p','\0', }; 3041 3042 if (up->doMisc) { 3043 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 3044 "Activating files used before the booter runs."); 3045 3046 // do them all 3047 for (i = 0; i < up->caches->nmisc; i++) { 3048 MAKEBOOTPATHcont(path, up->caches->miscpaths[i].rpath); 3049 if (strlcpy(opath, path, PATH_MAX) >= PATH_MAX) continue; 3050 if (strlcat(opath, NEWEXT, PATH_MAX) >= PATH_MAX) continue; 3051 3052 if (stat(opath, &sb) == 0) { 3053 if (srename(up->curbootfd, opath, path)) continue; 3054 } 3055 3056 nprocessed++; 3057 } 3058 } 3059 3060 makebootpath(path, up->caches->label->rpath); 3061 // move label back 3062 char newpath[PATH_MAX]; 3063 3064 pathcpy(newpath, path); // just rename 3065 pathcat(newpath, NEWEXT); 3066 (void)srename(up->curbootfd, newpath, path); 3067 } 3068 3069 // assign type/creator to the label 3070 if (0 == (stat(path, &sb))) { 3071 if (-1 == (fd = sopen(up->curbootfd, path, O_RDWR, 0))) goto finish; 3072 3073 if (fsetxattr(fd,XATTR_FINDERINFO_NAME,&tbxjchrp,sizeof(tbxjchrp),0,0)) 3074 goto finish; 3075 close(fd); fd = -1; 3076 } 3077 3078 rval = (i != nprocessed); 3079 3080finish: 3081 if (fd != -1) close(fd); 3082 3083 if (rval) { 3084 OSKextLog(NULL, kOSKextLogErrorLevel, 3085 "Error activating files used before the booter runs."); 3086 } 3087 3088 return rval; 3089} 3090 3091/****************************************************************************** 3092* get rid of everything "extra" 3093******************************************************************************/ 3094static int 3095nukeFallbacks(struct updatingVol *up) 3096{ 3097 int rval = 0; // OR-ative return value 3098 int bsderr; 3099 char delpath[PATH_MAX]; 3100 struct bootCaches *caches = up->caches; 3101 3102 OSKextLog(NULL, kOSKextLogDetailLevel | kOSKextLogGeneralFlag, 3103 "Cleaning up fallbacks."); 3104 3105 // using pathcpy b/c if that's failing, it's worth bailing 3106 // XX should probably only try to unlink if present 3107 3108 // maybe mount failed (in which case there aren't any fallbacks) 3109 if (up->curMount[0] == '\0') goto finish; 3110 3111 // if needed, unlink .old booters 3112 if (up->doBooters) { 3113 if (caches->ofbooter.rpath[0]) { 3114 makebootpath(delpath, caches->ofbooter.rpath); 3115 pathcat(delpath, OLDEXT); 3116 if ((bsderr = sunlink(up->curbootfd, delpath)) && errno != ENOENT) { 3117 rval |= bsderr; 3118 } 3119 } 3120 if (caches->efibooter.rpath[0]) { 3121 makebootpath(delpath, caches->efibooter.rpath); 3122 pathcat(delpath, OLDEXT); 3123 if ((bsderr = sunlink(up->curbootfd, delpath)) && errno != ENOENT) { 3124 rval |= bsderr; 3125 } 3126 } 3127 } 3128 3129 // if needed, erase prevRPS 3130 // which, conveniently, will be right regardless of whether we succeeded 3131 if (up->doRPS) { 3132 char ignore[PATH_MAX]; 3133 3134 if (0 == FindRPSDir(up, delpath, ignore, ignore)) { 3135 // eraseRPS ignores if missing (and logs other errors) 3136 rval |= eraseRPS(up, delpath); 3137 } 3138 } 3139 3140finish: 3141 if (rval) 3142 OSKextLog(NULL, kOSKextLogErrorLevel, "Error cleaning up fallbacks."); 3143 3144 return rval; 3145} 3146 3147#if 0 3148/********************************************************************* 3149// XXX not yet used / tested 3150*********************************************************************/ 3151static int 3152kill_kextd(void) 3153{ 3154 int result = -1; 3155 kern_return_t kern_result = kOSReturnError; 3156 mach_port_t bootstrap_port = MACH_PORT_NULL; 3157 mach_port_t kextd_port = MACH_PORT_NULL; 3158 int kextd_pid = -1; 3159 3160 kern_result = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); 3161 if (kern_result != kOSReturnSuccess) { 3162 goto finish; 3163 } 3164 3165 kern_result = bootstrap_look_up(bootstrap_port, 3166 (char *)KEXTD_SERVER_NAME, &kextd_port); 3167 if (kern_result != kOSReturnSuccess) { 3168 goto finish; 3169 } 3170 3171 kern_result = pid_for_task(kextd_port, &kextd_pid); 3172 if (kern_result != kOSReturnSuccess) { 3173 goto finish; 3174 } 3175 3176 result = kill(kextd_pid, SIGKILL); 3177 if (-1 == result) { 3178 OSKextLog(/* kext */ NULL, 3179 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 3180 "kill kextd failed - %s.", strerror(errno)); 3181 } 3182 3183finish: 3184 if (kern_result != kOSReturnSuccess) { 3185 OSKextLog(/* kext */ NULL, 3186 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 3187 "kill kextd failed - %s.", safe_mach_error_string(kern_result)); 3188 } 3189 return result; 3190} 3191 3192/****************************************************************************** 3193// XXX not yet used / tested 3194******************************************************************************/ 3195int 3196renameBootcachesPlist( 3197 char * hostVolume, 3198 char * oldPlistPath, 3199 char * newPlistPath) 3200{ 3201 int result = -1; 3202 int bootcachesPlistFd = -1; 3203 char * errorMessage = NULL; 3204 char * errorPath = NULL; 3205 char oldname[PATH_MAX]; 3206 char newname[PATH_MAX]; 3207 char * kextcacheArgs[] = { 3208 "/usr/sbin/kextcache", 3209 "-f", 3210 "-u", 3211 NULL, // replace with hostVolume 3212 NULL }; 3213 3214 errorMessage = "path concatenation error"; 3215 errorPath = hostVolume; 3216 3217 if (strlcpy(oldname, hostVolume, PATH_MAX) >= PATH_MAX) { 3218 goto finish; 3219 } 3220 if (strlcpy(newname, hostVolume, PATH_MAX) >= PATH_MAX) { 3221 goto finish; 3222 } 3223 3224 errorPath = oldPlistPath; 3225 if (strlcpy(oldname, oldPlistPath, PATH_MAX) >= PATH_MAX) { 3226 goto finish; 3227 } 3228 3229 errorPath = newPlistPath; 3230 if (strlcpy(newname, newPlistPath, PATH_MAX) >= PATH_MAX) { 3231 goto finish; 3232 } 3233 3234 errorPath = oldname; 3235 bootcachesPlistFd = open(oldname, O_RDONLY); 3236 if (-1 == bootcachesPlistFd) { 3237 errorMessage = strerror(errno); 3238 goto finish; 3239 } 3240 3241 if (-1 == srename(bootcachesPlistFd, oldname, newname)) { 3242 errorMessage = "rename failed."; 3243 goto finish; 3244 } 3245 3246 errorMessage = "couldn't kill kextd"; 3247 if (-1 == kill_kextd()) { 3248 goto finish; 3249 } 3250 3251 /* Do we want to check fork_program's return value? 3252 */ 3253 kextcacheArgs[3] = hostVolume; 3254 result = fork_program(kextcacheArgs[0], kextcacheArgs, true /* wait */); 3255 3256finish: 3257 if (errorMessage) { 3258 OSKextLog(/* kext */ NULL, 3259 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 3260 "%s - %s.", errorPath, errorMessage); 3261 } 3262 if (bootcachesPlistFd >= 0) { 3263 close(bootcachesPlistFd); 3264 } 3265 return result; 3266} 3267#endif // UNUSED 3268 3269/****************************************************************************** 3270* takeVolumeForPath turns the path into a volume UUID and locks with kextd 3271******************************************************************************/ 3272// upstat() stat()s "up" the path if a file doesn't exist 3273static int 3274upstat(const char *path, struct stat *sb, struct statfs *sfs) 3275{ 3276 int rval = ELAST+1; 3277 char buf[PATH_MAX], *tpath = buf; 3278 struct stat defaultsb; 3279 3280 if (strlcpy(buf, path, PATH_MAX) > PATH_MAX) goto finish; 3281 3282 if (!sb) sb = &defaultsb; 3283 while ((rval = stat(tpath, sb)) == -1 && errno == ENOENT) { 3284 // "." and "/" should always exist, but you never know 3285 if (tpath[0] == '.' && tpath[1] == '\0') goto finish; 3286 if (tpath[0] == '/' && tpath[1] == '\0') goto finish; 3287 tpath = dirname(tpath); // Tiger's dirname() took const char* 3288 } 3289 3290 // call statfs if the caller needed it 3291 if (sfs) 3292 rval = statfs(tpath, sfs); 3293 3294finish: 3295 if (rval) { 3296 OSKextLog(/* kext */ NULL, 3297 kOSKextLogWarningLevel | kOSKextLogFileAccessFlag, 3298 "Couldn't find volume for %s.", path); 3299 } 3300 3301 return rval; 3302} 3303 3304 3305/****************************************************************************** 3306* theoretically, takeVolumeForPaths() ensured all paths are on the given 3307* volume, then locked 3308******************************************************************************/ 3309// int takeVolumeForPaths(char *volPath) 3310 3311/****************************************************************************** 3312* takeVolumeForPath() is all we ended up needing ... 3313* can return success if a lock isn't needed 3314* can return failure if sBRUptLock is already in use 3315******************************************************************************/ 3316#define WAITFORLOCK 1 3317int 3318takeVolumeForPath(const char *path) 3319{ 3320 int rval = ELAST + 1; 3321 kern_return_t macherr = KERN_SUCCESS; 3322 int lckres = 0; 3323 struct statfs sfs; 3324 const char *volPath = "<unknown>"; // llvm can't track lckres/macherr 3325 mach_port_t taskport = MACH_PORT_NULL; 3326 3327 if (sBRUptLock) { 3328 return EALREADY; // only support one lock at a time 3329 } 3330 3331 if (geteuid() != 0) { 3332 // kextd shouldn't be watching anything you can touch 3333 // and ignores locking requests from non-root anyway 3334 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogGeneralFlag, 3335 "Warning: non-root can't lock the volume for %s", path); 3336 rval = 0; 3337 goto finish; 3338 } 3339 3340 // look up kextd port if not cached 3341 // XX if there's a way to know kextd isn't already running, we could skip 3342 // unnecessarily bringing it up in the boot-time case (see 5108882). 3343 if (!sKextdPort) { 3344 macherr=bootstrap_look_up(bootstrap_port,KEXTD_SERVER_NAME,&sKextdPort); 3345 if (macherr) goto finish; 3346 } 3347 3348 // get the volume's UUID 3349 if ((rval = upstat(path, NULL, &sfs))) goto finish; 3350 volPath = sfs.f_mntonname; 3351 if ((rval = copyVolumeInfo(volPath,&s_vol_uuid,NULL,NULL,NULL))) { 3352 goto finish; 3353 } 3354 3355 // allocate a port to pass (in case we die -- kernel cleans up on exit()) 3356 taskport = mach_task_self(); 3357 if (taskport == MACH_PORT_NULL) goto finish; 3358 macherr = mach_port_allocate(taskport,MACH_PORT_RIGHT_RECEIVE,&sBRUptLock); 3359 if (macherr) goto finish; 3360 3361 // try to take the lock; warn if it's busy and then wait for it 3362 macherr = kextmanager_lock_volume(sKextdPort, sBRUptLock, s_vol_uuid, 3363 !WAITFORLOCK, &lckres); 3364 if (macherr) goto finish; 3365 3366 // 5519500: sleep until kextd is up and running (w/diskarb, etc) 3367 while (lckres == EAGAIN) { 3368 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3369 "kextd wasn't ready; waiting 10 seconds and trying again."); 3370 sleep(10); 3371 macherr = kextmanager_lock_volume(sKextdPort, sBRUptLock, s_vol_uuid, 3372 !WAITFORLOCK, &lckres); 3373 if (macherr) goto finish; 3374 } 3375 3376 // With kextd set up, we sleep until the volume is free. 3377 // WAITFORLOCK should cause the function not to return w/o the lock 3378 // but 8679674 suggested something was going awry so we'll retry. 3379 while (lckres == EBUSY) { 3380 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3381 "%s locked; waiting for lock.", volPath); 3382 macherr = kextmanager_lock_volume(sKextdPort, sBRUptLock, s_vol_uuid, 3383 WAITFORLOCK, &lckres); 3384 if (macherr) goto finish; 3385 if (lckres == 0) { 3386 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3387 "Lock acquired; proceeding."); 3388 } 3389 } 3390 3391 3392 // kextd might not be watching this volume (isn't currently competing) 3393 // so we set our success to the existance of the volume's root 3394 if (lckres == ENOENT) { 3395 struct stat sb; 3396 rval = stat(volPath, &sb); 3397 if (rval == 0) { 3398 OSKextLog(NULL, kOSKextLogProgressLevel | kOSKextLogIPCFlag, 3399 "Note: kextd not watching %s; proceeding w/o lock", volPath); 3400 } 3401 } else { 3402 rval = lckres; 3403 } 3404 3405finish: 3406 if (sBRUptLock != MACH_PORT_NULL && (lckres != 0 || macherr)) { 3407 mach_port_mod_refs(taskport, sBRUptLock, MACH_PORT_RIGHT_RECEIVE, -1); 3408 sBRUptLock = MACH_PORT_NULL; 3409 } 3410 3411 /* XX needs unraveling XX */ 3412 // if kextd isn't competing with us, then we didn't need the lock 3413 if (macherr == BOOTSTRAP_UNKNOWN_SERVICE || 3414 macherr == MACH_SEND_INVALID_DEST) { 3415 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3416 "Warning: kextd unavailable; proceeding w/o lock for %s", volPath); 3417 rval = 0; 3418 } else if (macherr) { 3419 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3420 "Couldn't lock %s: %s (%x).", path, 3421 safe_mach_error_string(macherr), macherr); 3422 rval = macherr; 3423 } else if (rval) { 3424 // dump rval 3425 if (rval == -1) rval = errno; 3426 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3427 "Couldn't lock %s: %s", path, strerror(rval)); 3428 } 3429 3430 return rval; 3431} 3432 3433/****************************************************************************** 3434* putVolumeForPath will unlock the relevant volume, passing 'status' to 3435* inform kextd whether we succeded, failed, or just need more time 3436******************************************************************************/ 3437int 3438putVolumeForPath(const char *path, int status) 3439{ 3440 int rval = KERN_SUCCESS; 3441 3442 // if not locked, don't sweat it 3443 if (sBRUptLock == MACH_PORT_NULL) 3444 goto finish; 3445 3446 rval = kextmanager_unlock_volume(sKextdPort,sBRUptLock,s_vol_uuid,status); 3447 3448 // tidy up; the server will clean up its stuff if we die prematurely 3449 mach_port_mod_refs(mach_task_self(),sBRUptLock,MACH_PORT_RIGHT_RECEIVE,-1); 3450 sBRUptLock = MACH_PORT_NULL; 3451 3452finish: 3453 if (rval) { 3454 OSKextLog(NULL, kOSKextLogWarningLevel | kOSKextLogIPCFlag, 3455 "Couldn't unlock volume for %s: %s (%d).", 3456 path, safe_mach_error_string(rval), rval); 3457 } 3458 3459 return rval; 3460} 3461 3462/******************************************************************************* 3463 * copy_kcsuffix() - return the current value of the kcsuffix boot-arg. 3464 * Caller must release the returned CFStringRef. 3465 * 3466 * The kcsuffix value tells us which kernelcache file the booter should use. 3467 * "kcsuffix=" is the same as "kcsuffix=release" which means the booter should 3468 * use "kernelcache" file. Any other suffix means use "kernelcache" plus the 3469 * given suffix with a '.' before the suffix. 3470 * 3471 * For example: 3472 * "kcsuffix=development" means the booter should pick the 3473 * "kernelcache.development" file. 3474 *******************************************************************************/ 3475CFStringRef copy_kcsuffix(void) 3476{ 3477 CFStringRef result = NULL; 3478 CFStringRef myTempStr; 3479 io_registry_entry_t optionsNode = MACH_PORT_NULL; // must release 3480 CFStringRef bootargsEntry = NULL; // must release 3481 CFArrayRef myStringArray = NULL; // must release 3482 CFRange findRange, myRange; 3483 CFIndex count, i; 3484 3485 optionsNode = IORegistryEntryFromPath(kIOMasterPortDefault, 3486 "IODeviceTree:/options"); 3487 while (optionsNode) { 3488 bootargsEntry = (CFStringRef) 3489 IORegistryEntryCreateCFProperty(optionsNode, 3490 CFSTR("boot-args"), 3491 kCFAllocatorDefault, 0); 3492 if (bootargsEntry == NULL || 3493 (CFGetTypeID(bootargsEntry) != CFStringGetTypeID())) { 3494 break; 3495 } 3496 3497 // a blank space is used to delimit the various values of boot-args 3498 // We are looking for something like: 3499 // boot-args="-v debug=0x146 kext-dev-mode=0 kcsuffix=release" 3500 myStringArray = CFStringCreateArrayBySeparatingStrings( 3501 kCFAllocatorDefault, 3502 bootargsEntry, 3503 CFSTR(" ") ); 3504 if (myStringArray == NULL) { 3505 break; 3506 } 3507 3508 count = CFArrayGetCount(myStringArray); 3509 for (i = 0; i < count; i++) { 3510 CFStringRef myString = NULL; 3511 CFIndex myStringLen; 3512 3513 myString = CFArrayGetValueAtIndex(myStringArray, i); 3514 if (myString == NULL) continue; 3515 findRange = CFStringFind(myString, CFSTR("kcsuffix="), 0); 3516 if (findRange.length == 0) { 3517 continue; 3518 } 3519 myStringLen = CFStringGetLength(myString); 3520 if (findRange.length == 9 && myStringLen == 9) { 3521 // "kcsuffix=" with no value means "release" 3522 result = CFRetain(CFSTR(".release")); 3523 break; 3524 } 3525 3526 // grab suffix and return it 3527 myRange.length = myStringLen - 9; 3528 myRange.location = findRange.location + 9; 3529 3530 myTempStr = CFStringCreateWithSubstring(kCFAllocatorDefault, 3531 myString, 3532 myRange); 3533 if (myTempStr) { 3534 result = CFStringCreateWithFormat( 3535 kCFAllocatorDefault, 3536 /* formatOptions */ NULL, 3537 CFSTR("%s%@"), 3538 ".", 3539 myTempStr ); 3540 CFRelease(myTempStr); 3541 } 3542 3543 break; 3544 } // for ... 3545 break; 3546 } // while ... 3547 3548 if (optionsNode) IOObjectRelease(optionsNode); 3549 SAFE_RELEASE(bootargsEntry); 3550 SAFE_RELEASE(myStringArray); 3551 3552 if (result == NULL) { 3553 // nothing set then let's default to development kernelcache 3554 result = CFRetain(CFSTR(".development")); 3555 } 3556 3557 return(result); 3558} 3559 3560