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