1/* 2 * Copyright (c) 1999-2000, 2002, 2004, 2007-2008 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 * Copyright (c) 1980, 1986, 1993 25 * The Regents of the University of California. All rights reserved. 26 * 27 * Redistribution and use in source and binary forms, with or without 28 * modification, are permitted provided that the following conditions 29 * are met: 30 * 1. Redistributions of source code must retain the above copyright 31 * notice, this list of conditions and the following disclaimer. 32 * 2. Redistributions in binary form must reproduce the above copyright 33 * notice, this list of conditions and the following disclaimer in the 34 * documentation and/or other materials provided with the distribution. 35 * 3. All advertising materials mentioning features or use of this software 36 * must display the following acknowledgement: 37 * This product includes software developed by the University of 38 * California, Berkeley and its contributors. 39 * 4. Neither the name of the University nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53 * SUCH DAMAGE. 54 */ 55#include <stddef.h> 56#include <sys/types.h> 57#include <sys/stat.h> 58#include <sys/errno.h> 59#include <sys/syslimits.h> 60#include <pwd.h> 61 62#include <ctype.h> 63#include <err.h> 64#include <stdio.h> 65#include <string.h> 66#include <unistd.h> 67#include <stdlib.h> 68#include <sys/sysctl.h> 69 70#include "fsck_hfs.h" 71 72char *rawname __P((char *name)); 73char *unrawname __P((char *name)); 74 75 76int 77reply(char *question) 78{ 79 int persevere; 80 char c; 81 82 if (preen) 83 pfatal("INTERNAL ERROR: GOT TO reply()"); 84 persevere = !strcmp(question, "CONTINUE"); 85 plog("\n"); 86 if (!persevere && (nflag || fswritefd < 0)) { 87 plog("%s? no\n\n", question); 88 return (0); 89 } 90 if (yflag || (persevere && nflag)) { 91 plog("%s? yes\n\n", question); 92 return (1); 93 } 94 do { 95 plog("%s? [yn] ", question); 96 (void) fflush(stdout); 97 c = getc(stdin); 98 while (c != '\n' && getc(stdin) != '\n') 99 if (feof(stdin)) 100 return (0); 101 } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N'); 102 plog("\n"); 103 if (c == 'y' || c == 'Y') 104 return (1); 105 return (0); 106} 107 108 109void 110ckfini(markclean) 111 int markclean; 112{ 113// register struct bufarea *bp, *nbp; 114// int ofsmodified, cnt = 0; 115 116 (void) CacheDestroy(&fscache); 117 118 if (fswritefd < 0) { 119 (void)close(fsreadfd); 120 return; 121 } 122#if 0 123 flush(fswritefd, &sblk); 124 if (havesb && sblk.b_bno != SBOFF / dev_bsize && 125 !preen && reply("UPDATE STANDARD SUPERBLOCK")) { 126 sblk.b_bno = SBOFF / dev_bsize; 127 sbdirty(); 128 flush(fswritefd, &sblk); 129 } 130 flush(fswritefd, &cgblk); 131 free(cgblk.b_un.b_buf); 132 for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) { 133 cnt++; 134 flush(fswritefd, bp); 135 nbp = bp->b_prev; 136 free(bp->b_un.b_buf); 137 free((char *)bp); 138 } 139 if (bufhead.b_size != cnt) 140 errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt); 141 pbp = pdirbp = (struct bufarea *)0; 142 if (markclean && sblock.fs_clean == 0) { 143 sblock.fs_clean = 1; 144 sbdirty(); 145 ofsmodified = fsmodified; 146 flush(fswritefd, &sblk); 147 fsmodified = ofsmodified; 148 if (!preen) 149 plog("\n***** FILE SYSTEM MARKED CLEAN *****\n"); 150 } 151 if (debug) 152 plog("cache missed %ld of %ld (%d%%)\n", diskreads, 153 totalreads, (int)(diskreads * 100 / totalreads)); 154#endif 155 (void)close(fsreadfd); 156 (void)close(fswritefd); 157} 158 159 160char * 161blockcheck(char *origname) 162{ 163 struct stat stslash, stblock, stchar; 164 char *newname, *raw; 165 int retried = 0; 166 167 hotroot = 0; 168 if (stat("/", &stslash) < 0) { 169 perror("/"); 170 plog("Can't stat root\n"); 171 return (origname); 172 } 173 newname = origname; 174retry: 175 if (stat(newname, &stblock) < 0) { 176 perror(newname); 177 plog("Can't stat %s\n", newname); 178 return (origname); 179 } 180 if ((stblock.st_mode & S_IFMT) == S_IFBLK) { 181 if (stslash.st_dev == stblock.st_rdev) 182 hotroot++; 183 raw = rawname(newname); 184 if (stat(raw, &stchar) < 0) { 185 perror(raw); 186 plog("Can't stat %s\n", raw); 187 return (origname); 188 } 189 if ((stchar.st_mode & S_IFMT) == S_IFCHR) { 190 return (raw); 191 } else { 192 plog("%s is not a character device\n", raw); 193 return (origname); 194 } 195 } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) { 196 newname = unrawname(newname); 197 retried++; 198 goto retry; 199 } 200 /* 201 * Not a block or character device, just return name and 202 * let the caller decide whether to use it. 203 */ 204 return (origname); 205} 206 207 208char * 209rawname(char *name) 210 211{ 212 static char rawbuf[32]; 213 char *dp; 214 215 if ((dp = strrchr(name, '/')) == 0) 216 return (0); 217 *dp = 0; 218 (void)strlcpy(rawbuf, name, sizeof(rawbuf)); 219 *dp = '/'; 220 (void)strlcat(rawbuf, "/r", sizeof(rawbuf)); 221 (void)strlcat(rawbuf, &dp[1], sizeof(rawbuf)); 222 223 return (rawbuf); 224} 225 226 227char * 228unrawname(char *name) 229{ 230 char *dp; 231 struct stat stb; 232 233 if ((dp = strrchr(name, '/')) == 0) 234 return (name); 235 if (stat(name, &stb) < 0) 236 return (name); 237 if ((stb.st_mode & S_IFMT) != S_IFCHR) 238 return (name); 239 if (dp[1] != 'r') 240 return (name); 241 memmove(&dp[1], &dp[2], strlen(&dp[2]) + 1); 242 243 return (name); 244} 245 246 247void 248catch(sig) 249 int sig; 250{ 251 if (!upgrading) 252 ckfini(0); 253 exit(12); 254} 255 256 257// 258// Logging stuff... 259// 260// 261#include <stdarg.h> 262#include <pthread.h> 263#include <time.h> 264 265#define FSCK_LOG_FILE "/var/log/fsck_hfs.log" 266 267extern char lflag; // indicates if we're doing a live fsck (defined in fsck_hfs.c) 268extern char guiControl; // indicates if we're outputting for the gui (defined in fsck_hfs.c) 269 270FILE *log_file = NULL; 271 272/* Variables for in-memory log for strings that will be written to log file */ 273char *in_mem_log = NULL; 274char *cur_in_mem_log = NULL; 275size_t in_mem_log_size = 0; 276 277/* Variables for in-memory log for strings that will be printed on standard out */ 278char *in_mem_out = NULL; 279char *cur_in_mem_out = NULL; 280size_t in_mem_out_size = 0; 281 282int live_fsck = 0; 283 284#define DEFAULT_IN_MEM_SIZE 4096 285 286static pthread_mutex_t mem_buf_lock = PTHREAD_MUTEX_INITIALIZER; 287static pthread_cond_t mem_buf_cond; 288 289static pthread_t printing_thread; 290static pthread_t logging_thread; 291static volatile int keep_going = 1; 292 293#undef fprintf 294#undef printf 295 296// prototype 297void print_to_mem(int type, int mem_type, const char *fmt, const char *str, va_list ap); 298 299#define DO_VPRINT 1 // types for print_to_mem 300#define DO_STR 2 301 302/* Types for mem_type */ 303#define IN_MEM_LOG 1 // in-memory log strings 304#define IN_MEM_OUT 2 // in-memory stdout strings 305 306static void * 307fsck_logging_thread(void *arg) 308{ 309 int copy_amt; 310 char buff[1024], *ptr; 311 312 /* Handle writing to the log file */ 313 while(keep_going || cur_in_mem_log != in_mem_log) { 314 315 pthread_mutex_lock(&mem_buf_lock); 316 while (keep_going != 0 && cur_in_mem_log == in_mem_log) { 317 int err; 318 319 err = pthread_cond_wait(&mem_buf_cond, &mem_buf_lock); 320 if (err != 0) { 321 fprintf(stderr, "error %d from cond wait\n", err); 322 break; 323 } 324 } 325 326 copy_amt = (cur_in_mem_log - in_mem_log); 327 if (copy_amt == 0) { 328 pthread_mutex_unlock(&mem_buf_lock); 329 continue; 330 } 331 332 if (copy_amt >= sizeof(buff)) { 333 copy_amt = sizeof(buff) - 1; 334 memcpy(buff, in_mem_log, copy_amt); 335 336 memmove(in_mem_log, &in_mem_log[copy_amt], (cur_in_mem_log - in_mem_log) - copy_amt); 337 cur_in_mem_log -= copy_amt; 338 } else { 339 memcpy(buff, in_mem_log, copy_amt); 340 cur_in_mem_log = in_mem_log; 341 } 342 343 buff[copy_amt] = '\0'; 344 345 pthread_mutex_unlock(&mem_buf_lock); 346 347 for(ptr=buff; *ptr; ) { 348 char *start; 349 350 start = ptr; 351 while(*ptr && *ptr != '\n') { 352 ptr++; 353 } 354 if (*ptr == '\n') { 355 *ptr++ = '\0'; 356 if (log_file) { 357 fprintf(log_file, "%s: %s\n", cdevname ? cdevname : "UNKNOWN-DEV", start); 358 } 359 } else { 360 if (log_file) { 361 fprintf(log_file, "%s", start); 362 } 363 } 364 365 } 366 367 fflush(stdout); 368 } 369 370 return NULL; 371} 372 373static void * 374fsck_printing_thread(void *arg) 375{ 376 int copy_amt; 377 char buff[1024], *ptr; 378 379 /* Handle writing to the out file */ 380 while(keep_going || cur_in_mem_out != in_mem_out) { 381 382 pthread_mutex_lock(&mem_buf_lock); 383 while (keep_going != 0 && cur_in_mem_out == in_mem_out) { 384 int err; 385 386 err = pthread_cond_wait(&mem_buf_cond, &mem_buf_lock); 387 if (err != 0) { 388 fprintf(stderr, "error %d from cond wait\n", err); 389 break; 390 } 391 } 392 393 copy_amt = (cur_in_mem_out - in_mem_out); 394 if (copy_amt == 0) { 395 pthread_mutex_unlock(&mem_buf_lock); 396 continue; 397 } 398 399 if (copy_amt >= sizeof(buff)) { 400 copy_amt = sizeof(buff) - 1; 401 memcpy(buff, in_mem_out, copy_amt); 402 403 memmove(in_mem_out, &in_mem_out[copy_amt], (cur_in_mem_out - in_mem_out) - copy_amt); 404 cur_in_mem_out -= copy_amt; 405 } else { 406 memcpy(buff, in_mem_out, copy_amt); 407 cur_in_mem_out = in_mem_out; 408 } 409 410 buff[copy_amt] = '\0'; 411 412 pthread_mutex_unlock(&mem_buf_lock); 413 414 for(ptr=buff; *ptr; ) { 415 char *start; 416 417 start = ptr; 418 while(*ptr && *ptr != '\n') { 419 ptr++; 420 } 421 if (*ptr == '\n') { 422 *ptr++ = '\0'; 423 printf("%s\n", start); 424 } else { 425 printf("%s", start); 426 } 427 428 } 429 430 fflush(stdout); 431 } 432 433 return NULL; 434} 435 436 437int was_signaled = 0; 438 439void 440shutdown_logging(void) 441{ 442 keep_going = 0; 443 time_t t; 444 445 /* Log fsck_hfs check completion time */ 446 t = time(NULL); 447 if (in_mem_log) { 448 va_list empty_list = {0}; 449 print_to_mem(DO_STR, IN_MEM_LOG, "fsck_hfs completed at %s\n", ctime(&t), empty_list); 450 } else { 451 fprintf(log_file, "%s: fsck_hfs completed at %s\n", cdevname ? cdevname : "UNKNOWN-DEV", ctime(&t)); 452 } 453 454 if (was_signaled) { 455 // if we were signaled, we can't really call any of these 456 // functions from the context of a signal handler (which 457 // is how we're called if we don't have a signal handler). 458 // so we have our own signal handler which sets this var 459 // which tells us to just bail out. 460 return; 461 } 462 463 if (log_file && !live_fsck) { 464 fflush(log_file); 465 fclose(log_file); 466 log_file = NULL; 467 } else if ((in_mem_out || in_mem_log) && live_fsck && log_file) { 468 // make sure the printing and logging threads are woken up... 469 pthread_mutex_lock(&mem_buf_lock); 470 pthread_cond_broadcast(&mem_buf_cond); 471 pthread_mutex_unlock(&mem_buf_lock); 472 473 // then wait for them 474 pthread_join(printing_thread, NULL); 475 pthread_join(logging_thread, NULL); 476 477 free(in_mem_out); 478 in_mem_out = cur_in_mem_out = NULL; 479 in_mem_out_size = 0; 480 481 free(in_mem_log); 482 in_mem_log = cur_in_mem_log = NULL; 483 in_mem_log_size = 0; 484 485 if (log_file) { 486 fflush(log_file); 487 fclose(log_file); 488 log_file = NULL; 489 } 490 } else if (in_mem_log) { 491 int ret; 492 493 if (getuid() == 0) { 494 // just in case, flush any pending output 495 fflush(stdout); 496 fflush(stderr); 497 498 // 499 // fork so that the child can wait around until the 500 // root volume is mounted read-write and we can add 501 // our output to the log 502 // 503 ret = fork(); 504 } else { 505 // if we're not root we don't need to fork 506 ret = 0; 507 } 508 if (ret == 0) { 509 int i; 510 char *fname = FSCK_LOG_FILE, path[PATH_MAX]; 511 512 // Disk Management waits for fsck_hfs' stdout to close rather 513 // than the process death to understand if fsck_hfs has exited 514 // or not. Since we do not use stdout any further, close all 515 // the file descriptors so that Disk Management does not wait 516 // for 60 seconds unnecessarily on read-only boot volumes. 517 fclose(stdout); 518 fclose(stdin); 519 fclose(stderr); 520 521 // non-root will never be able to write to /var/log 522 // so point the file somewhere else. 523 if (getuid() != 0) { 524 struct passwd *pwd; 525 fname = NULL; 526 // each user will get their own log as ~/Library/Logs/fsck_hfs.log 527 pwd = getpwuid(getuid()); 528 if (pwd) { 529 snprintf(path, sizeof(path), "%s/Library/Logs/fsck_hfs.log", pwd->pw_dir); 530 fname = &path[0]; 531 } 532 } 533 534 for(i=0; i < 60; i++) { 535 log_file = fopen(fname, "a"); 536 if (log_file) { 537 fwrite(in_mem_log, cur_in_mem_log - in_mem_log, 1, log_file); 538 539 fflush(log_file); 540 fclose(log_file); 541 log_file = NULL; 542 543 free(in_mem_log); 544 in_mem_log = cur_in_mem_log = NULL; 545 in_mem_log_size = 0; 546 547 break; 548 } else { 549 // hmmm, failed to open the output file so wait 550 // a while only if the fs is read-only and then 551 // try again 552 if (errno == EROFS) { 553 sleep(1); 554 } else { 555 break; 556 } 557 } 558 } 559 } 560 } 561} 562 563static void 564my_sighandler(int sig) 565{ 566 was_signaled = 1; 567 cleanup_fs_fd(); 568 exit(sig); 569} 570 571 572void 573setup_logging(void) 574{ 575 static int at_exit_setup = 0; 576 time_t t; 577 578 // if this is set, we don't have to do anything 579 if (at_exit_setup) { 580 return; 581 } 582 583 if (guiControl) { 584 setlinebuf(stdout); 585 setlinebuf(stderr); 586 } 587 588 // our copy of this variable since we may 589 // need to change it to make the right thing 590 // happen for fsck on the root volume. 591 live_fsck = (int)lflag; 592 593 if (log_file == NULL) { 594 log_file = fopen(FSCK_LOG_FILE, "a"); 595 if (log_file) { 596 setlinebuf(log_file); 597 } else { 598 // 599 // if we can't open the output file it's either because 600 // we're being run on the root volume during early boot 601 // or we were not run as the root user and so we can't 602 // write to /var/log/fsck_hfs.log. in either case we 603 // turn off "live_fsck" so that the right thing happens 604 // in here with respect to where output goes. 605 // 606 live_fsck = 0; 607 } 608 609 if (!live_fsck && log_file) { 610 t = time(NULL); 611 fprintf(log_file, "\n%s: fsck_hfs started at %s", cdevname ? cdevname : "UNKNOWN-DEV", ctime(&t)); 612 fflush(log_file); 613 614 } else if (live_fsck || in_mem_log == NULL || in_mem_out == NULL) { 615 // 616 // hmm, we couldn't open the log file (or it's a 617 // live fsck). let's just squirrel away a copy 618 // of the data in memory and then deal with it 619 // later (or print it out from a separate thread 620 // if we're doing a live fsck). 621 // 622 in_mem_log = (char *)malloc(DEFAULT_IN_MEM_SIZE); 623 in_mem_out = (char *)malloc(DEFAULT_IN_MEM_SIZE); 624 if ((in_mem_log != NULL) && (in_mem_out != NULL)) { 625 in_mem_log_size = DEFAULT_IN_MEM_SIZE; 626 in_mem_log[0] = '\0'; 627 cur_in_mem_log = in_mem_log; 628 629 in_mem_out_size = DEFAULT_IN_MEM_SIZE; 630 in_mem_out[0] = '\0'; 631 cur_in_mem_out = in_mem_out; 632 633 t = time(NULL); 634 va_list empty_list = {0}; 635 print_to_mem(DO_STR, IN_MEM_LOG, "\nfsck_hfs started at %s", ctime(&t), empty_list); 636 637 if (live_fsck && log_file) { 638 pthread_cond_init(&mem_buf_cond, NULL); 639 640 signal(SIGINT, my_sighandler); 641 signal(SIGHUP, my_sighandler); 642 signal(SIGTERM, my_sighandler); 643 signal(SIGQUIT, my_sighandler); 644 signal(SIGBUS, my_sighandler); 645 signal(SIGSEGV, my_sighandler); 646 signal(SIGILL, my_sighandler); 647 648 pthread_create(&printing_thread, NULL, fsck_printing_thread, NULL); 649 pthread_create(&logging_thread, NULL, fsck_logging_thread, NULL); 650 651 } 652 } 653 } 654 655 if (at_exit_setup == 0 && (log_file || in_mem_log || in_mem_out)) { 656 atexit(shutdown_logging); 657 at_exit_setup = 1; 658 } 659 } 660} 661 662 663void 664print_to_mem(int type, int mem_type, const char *fmt, const char *str, va_list ap) 665{ 666 int ret; 667 size_t size_remaining; 668 va_list ap_copy; 669 char *cur_in_mem; 670 char *in_mem_data; 671 size_t in_mem_data_size; 672 673 if (type == DO_VPRINT) { 674 va_copy(ap_copy, ap); 675 } 676 677 if (mem_type == IN_MEM_LOG) { 678 cur_in_mem = cur_in_mem_log; 679 in_mem_data = in_mem_log; 680 in_mem_data_size = in_mem_log_size; 681 } else { 682 cur_in_mem = cur_in_mem_out; 683 in_mem_data = in_mem_out; 684 in_mem_data_size = in_mem_out_size; 685 } 686 687 /* Grab the lock only when adding output strings to the in-memory data */ 688 if (live_fsck && (mem_type == IN_MEM_OUT)) { 689 pthread_mutex_lock(&mem_buf_lock); 690 } 691 692 size_remaining = in_mem_data_size - (ptrdiff_t)(cur_in_mem - in_mem_data); 693 if (type == DO_VPRINT) { 694 ret = vsnprintf(cur_in_mem, size_remaining, fmt, ap); 695 } else { 696 ret = snprintf(cur_in_mem, size_remaining, fmt, str); 697 } 698 if (ret > size_remaining) { 699 char *new_log; 700 size_t amt; 701 702 if (ret >= DEFAULT_IN_MEM_SIZE) { 703 amt = (ret + 4095) & (~4095); // round up to a 4k boundary 704 } else { 705 amt = DEFAULT_IN_MEM_SIZE; 706 } 707 708 new_log = realloc(in_mem_data, in_mem_data_size + amt); 709 if (new_log == NULL) { 710 if (live_fsck && (mem_type == IN_MEM_OUT)) { 711 pthread_cond_signal(&mem_buf_cond); 712 pthread_mutex_unlock(&mem_buf_lock); 713 } 714 goto done; 715 } 716 717 in_mem_data_size += amt; 718 cur_in_mem = new_log + (cur_in_mem - in_mem_data); 719 in_mem_data = new_log; 720 size_remaining = in_mem_data_size - (ptrdiff_t)(cur_in_mem - new_log); 721 if (type == DO_VPRINT) { 722 ret = vsnprintf(cur_in_mem, size_remaining, fmt, ap_copy); 723 } else { 724 ret = snprintf(cur_in_mem, size_remaining, fmt, str); 725 } 726 if (ret <= size_remaining) { 727 cur_in_mem += ret; 728 } 729 } else { 730 cur_in_mem += ret; 731 } 732 733 if (live_fsck && (mem_type == IN_MEM_OUT)) { 734 pthread_cond_signal(&mem_buf_cond); 735 pthread_mutex_unlock(&mem_buf_lock); 736 } 737 738done: 739 740 if (mem_type == IN_MEM_LOG) { 741 cur_in_mem_log = cur_in_mem; 742 in_mem_log = in_mem_data; 743 in_mem_log_size = in_mem_data_size; 744 } else { 745 cur_in_mem_out = cur_in_mem; 746 in_mem_out = in_mem_data; 747 in_mem_out_size = in_mem_data_size; 748 } 749 750 if (type == DO_VPRINT) { 751 va_end(ap_copy); 752 } 753} 754 755 756static int need_prefix=1; 757 758#define LOG_PREFIX \ 759 if (need_prefix) { \ 760 fprintf(log_file, "%s: ", cdevname); \ 761 if (strchr(fmt, '\n')) { \ 762 need_prefix = 1; \ 763 } else { \ 764 need_prefix = 0; \ 765 } \ 766 } else if (strchr(fmt, '\n')) { \ 767 need_prefix = 1; \ 768 } 769 770/* Print output string on given stream or store it into in-memory buffer */ 771#define VOUT(stream, fmt, ap) \ 772 if (!live_fsck) { \ 773 vfprintf(stream, fmt, ap); \ 774 } else { \ 775 print_to_mem(DO_VPRINT, IN_MEM_OUT, fmt, NULL, ap); \ 776 } 777 778#define FOUT(fmt, str) \ 779 print_to_mem(DO_STR, IN_MEM_OUT, fmt, str, NULL); 780 781/* Store output string written to fsck_hfs.log into file or in-memory buffer */ 782#define VLOG(fmt, ap) \ 783 va_start(ap, fmt); \ 784 VLOG_INTERNAL(fmt, ap); 785 786#define VLOG_INTERNAL(fmt, ap) \ 787 if (log_file && !live_fsck) { \ 788 LOG_PREFIX \ 789 vfprintf(log_file, fmt, ap); \ 790 } else { \ 791 print_to_mem(DO_VPRINT, IN_MEM_LOG, fmt, NULL, ap); \ 792 } 793 794#define FLOG(fmt, str) \ 795 if (log_file && !live_fsck) { \ 796 LOG_PREFIX; \ 797 fprintf(log_file, fmt, str); \ 798 } else { \ 799 va_list empty_list = {0}; \ 800 print_to_mem(DO_STR, IN_MEM_LOG, fmt, str, empty_list); \ 801 } 802 803 804#if __STDC__ 805#include <stdarg.h> 806#else 807#include <varargs.h> 808#endif 809 810/* 811 * An unexpected inconsistency occurred. 812 * Die if preening, otherwise just print message and continue. 813 */ 814void 815#if __STDC__ 816pfatal(const char *fmt, ...) 817#else 818pfatal(fmt, va_alist) 819 char *fmt; 820 va_dcl 821#endif 822{ 823 va_list ap; 824 825 setup_logging(); 826 827#if __STDC__ 828 va_start(ap, fmt); 829#else 830 va_start(ap); 831#endif 832 if (!preen) { 833 (void)vfprintf(stderr, fmt, ap); 834 VLOG(fmt, ap); 835 va_end(ap); 836 return; 837 } 838 if (!live_fsck) 839 (void)fprintf(stderr, "%s: ", cdevname); 840 FLOG("%s: ", cdevname); 841 842 if (!live_fsck) 843 (void)vfprintf(stderr, fmt, ap); 844 VLOG(fmt, ap); 845 846 if (!live_fsck) 847 (void)fprintf(stderr, 848 "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck_hfs MANUALLY.\n", 849 cdevname); 850 FLOG("\n%s: UNEXPECTED INCONSISTENCY; RUN fsck_hfs MANUALLY.\n", cdevname); 851 852 exit(EEXIT); 853} 854 855/* 856 * Pwarn just prints a message when not preening, 857 * or a warning (preceded by filename) when preening. 858 */ 859void 860#if __STDC__ 861pwarn(const char *fmt, ...) 862#else 863pwarn(fmt, va_alist) 864 char *fmt; 865 va_dcl 866#endif 867{ 868 va_list ap; 869 870 setup_logging(); 871 872#if __STDC__ 873 va_start(ap, fmt); 874#else 875 va_start(ap); 876#endif 877 if (preen) { 878 (void)fprintf(stderr, "%s: ", cdevname); 879 FLOG("%s: ", cdevname); 880 } 881 if (!live_fsck) 882 (void)vfprintf(stderr, fmt, ap); 883 VLOG(fmt, ap); 884 885 va_end(ap); 886} 887 888/* Write a string and parameters, if any, directly to the log file. 889 * These strings will not be printed to standard out/error. 890 */ 891void 892logstring(void *c, const char *str) 893{ 894 llog("%s", str); 895} 896 897/* Write a string and parameters, if any, directly to standard out/error. 898 * These strings will not be printed to log file. 899 */ 900void 901outstring(void *c, const char *str) 902{ 903 olog("%s", str); 904} 905 906/* Write to both standard out and log file */ 907void 908plog(const char *fmt, ...) 909{ 910 va_list ap; 911 va_start(ap, fmt); 912 vplog(fmt, ap); 913 va_end(ap); 914} 915 916/* Write to only standard out */ 917void 918olog(const char *fmt, ...) 919{ 920 va_list ap; 921 va_start(ap, fmt); 922 923 setup_logging(); 924 925 /* For live fsck_hfs, add output strings to in-memory log, 926 * and for non-live fsck_hfs, print output to stdout. 927 */ 928 VOUT(stdout, fmt, ap); 929 930 va_end(ap); 931} 932 933/* Write to only log file */ 934void 935llog(const char *fmt, ...) 936{ 937 va_list ap; 938 va_start(ap, fmt); 939 940 setup_logging(); 941 need_prefix = 1; 942 VLOG(fmt, ap); 943 944 va_end(ap); 945} 946 947/* Write to both standard out and log file */ 948void 949vplog(const char *fmt, va_list ap) 950{ 951 va_list copy_ap; 952 953 va_copy(copy_ap, ap); 954 955 setup_logging(); 956 957 /* Always print prefix to strings written to log files */ 958 need_prefix = 1; 959 960 /* Handle output strings, print to stdout or store in-memory */ 961 VOUT(stdout, fmt, ap); 962 963 /* Add log strings to the log file. VLOG() handles live case internally */ 964 VLOG_INTERNAL(fmt, copy_ap); 965} 966 967/* Write to both standard out and log file */ 968void 969fplog(FILE *stream, const char *fmt, ...) 970{ 971 va_list ap, copy_ap; 972 va_start(ap, fmt); 973 va_copy(copy_ap, ap); 974 975 setup_logging(); 976 need_prefix = 1; 977 978 /* Handle output strings, print to given stream or store in-memory */ 979 VOUT(stream, fmt, ap); 980 981 /* Add log strings to the log file. VLOG() handles live case internally */ 982 VLOG(fmt, copy_ap); 983 984 va_end(ap); 985} 986 987#define kProgressToggle "kern.progressmeterenable" 988#define kProgress "kern.progressmeter" 989 990void 991start_progress(void) 992{ 993 int rv; 994 int enable = 1; 995 if (hotroot == 0) 996 return; 997 rv = sysctlbyname(kProgressToggle, NULL, NULL, &enable, sizeof(enable)); 998 if (debug && rv == -1 && errno != ENOENT) { 999 warn("sysctl(%s) failed", kProgressToggle); 1000 } 1001} 1002 1003void 1004draw_progress(int pct) 1005{ 1006 int rv; 1007 if (hotroot == 0) 1008 return; 1009 rv = sysctlbyname(kProgress, NULL, NULL, &pct, sizeof(pct)); 1010 if (debug && rv == -1 && errno != ENOENT) { 1011 warn("sysctl(%s) failed", kProgress); 1012 } 1013} 1014 1015void 1016end_progress(void) 1017{ 1018 int rv; 1019 int enable = 0; 1020 if (hotroot == 0) 1021 return; 1022 rv = sysctlbyname(kProgressToggle, NULL, NULL, &enable, sizeof(enable)); 1023 if (debug && rv == -1 && errno != ENOENT) { 1024 warn("sysctl(%s) failed", kProgressToggle); 1025 } 1026} 1027 1028