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