]> git.saurik.com Git - apple/hfs.git/blobdiff - fsck_hfs/utilities.c
hfs-226.1.1.tar.gz
[apple/hfs.git] / fsck_hfs / utilities.c
diff --git a/fsck_hfs/utilities.c b/fsck_hfs/utilities.c
new file mode 100644 (file)
index 0000000..683a506
--- /dev/null
@@ -0,0 +1,1024 @@
+/*
+ * Copyright (c) 1999-2000, 2002, 2004, 2007-2008 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+/*
+ * Copyright (c) 1980, 1986, 1993
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/syslimits.h>
+#include <pwd.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h> 
+#include <stdlib.h>
+#include <sys/sysctl.h>
+
+#include "fsck_hfs.h"
+
+char *rawname __P((char *name));
+char *unrawname __P((char *name));
+
+
+int
+reply(char *question)
+{
+       int persevere;
+       char c;
+
+       if (preen)
+               pfatal("INTERNAL ERROR: GOT TO reply()");
+       persevere = !strcmp(question, "CONTINUE");
+       plog("\n");
+       if (!persevere && (nflag || fswritefd < 0)) {
+               plog("%s? no\n\n", question);
+               return (0);
+       }
+       if (yflag || (persevere && nflag)) {
+               plog("%s? yes\n\n", question);
+               return (1);
+       }
+       do      {
+               plog("%s? [yn] ", question);
+               (void) fflush(stdout);
+               c = getc(stdin);
+               while (c != '\n' && getc(stdin) != '\n')
+                       if (feof(stdin))
+                               return (0);
+       } while (c != 'y' && c != 'Y' && c != 'n' && c != 'N');
+       plog("\n");
+       if (c == 'y' || c == 'Y')
+               return (1);
+       return (0);
+}
+
+
+void
+ckfini(markclean)
+       int markclean;
+{
+//     register struct bufarea *bp, *nbp;
+//     int ofsmodified, cnt = 0;
+
+       (void) CacheDestroy(&fscache);
+
+       if (fswritefd < 0) {
+               (void)close(fsreadfd);
+               return;
+       }
+#if 0
+       flush(fswritefd, &sblk);
+       if (havesb && sblk.b_bno != SBOFF / dev_bsize &&
+           !preen && reply("UPDATE STANDARD SUPERBLOCK")) {
+               sblk.b_bno = SBOFF / dev_bsize;
+               sbdirty();
+               flush(fswritefd, &sblk);
+       }
+       flush(fswritefd, &cgblk);
+       free(cgblk.b_un.b_buf);
+       for (bp = bufhead.b_prev; bp && bp != &bufhead; bp = nbp) {
+               cnt++;
+               flush(fswritefd, bp);
+               nbp = bp->b_prev;
+               free(bp->b_un.b_buf);
+               free((char *)bp);
+       }
+       if (bufhead.b_size != cnt)
+               errx(EEXIT, "Panic: lost %d buffers", bufhead.b_size - cnt);
+       pbp = pdirbp = (struct bufarea *)0;
+       if (markclean && sblock.fs_clean == 0) {
+               sblock.fs_clean = 1;
+               sbdirty();
+               ofsmodified = fsmodified;
+               flush(fswritefd, &sblk);
+               fsmodified = ofsmodified;
+               if (!preen)
+                       plog("\n***** FILE SYSTEM MARKED CLEAN *****\n");
+       }
+       if (debug)
+               plog("cache missed %ld of %ld (%d%%)\n", diskreads,
+                   totalreads, (int)(diskreads * 100 / totalreads));
+#endif
+       (void)close(fsreadfd);
+       (void)close(fswritefd);
+}
+
+
+char *
+blockcheck(char *origname)
+{
+       struct stat stslash, stblock, stchar;
+       char *newname, *raw;
+       int retried = 0;
+
+       hotroot = 0;
+       if (stat("/", &stslash) < 0) {
+               perror("/");
+               plog("Can't stat root\n");
+               return (origname);
+       }
+       newname = origname;
+retry:
+       if (stat(newname, &stblock) < 0) {
+               perror(newname);
+               plog("Can't stat %s\n", newname);
+               return (origname);
+       }
+       if ((stblock.st_mode & S_IFMT) == S_IFBLK) {
+               if (stslash.st_dev == stblock.st_rdev)
+                       hotroot++;
+               raw = rawname(newname);
+               if (stat(raw, &stchar) < 0) {
+                       perror(raw);
+                       plog("Can't stat %s\n", raw);
+                       return (origname);
+               }
+               if ((stchar.st_mode & S_IFMT) == S_IFCHR) {
+                       return (raw);
+               } else {
+                       plog("%s is not a character device\n", raw);
+                       return (origname);
+               }
+       } else if ((stblock.st_mode & S_IFMT) == S_IFCHR && !retried) {
+               newname = unrawname(newname);
+               retried++;
+               goto retry;
+       }
+       /*
+        * Not a block or character device, just return name and
+        * let the caller decide whether to use it.
+        */
+       return (origname);
+}
+
+
+char *
+rawname(char *name)
+
+{
+       static char rawbuf[32];
+       char *dp;
+
+       if ((dp = strrchr(name, '/')) == 0)
+               return (0);
+       *dp = 0;
+       (void)strlcpy(rawbuf, name, sizeof(rawbuf));
+       *dp = '/';
+       (void)strlcat(rawbuf, "/r", sizeof(rawbuf));
+       (void)strlcat(rawbuf, &dp[1], sizeof(rawbuf));
+
+       return (rawbuf);
+}
+
+
+char *
+unrawname(char *name)
+{
+       char *dp;
+       struct stat stb;
+
+       if ((dp = strrchr(name, '/')) == 0)
+               return (name);
+       if (stat(name, &stb) < 0)
+               return (name);
+       if ((stb.st_mode & S_IFMT) != S_IFCHR)
+               return (name);
+       if (dp[1] != 'r')
+               return (name);
+       memmove(&dp[1], &dp[2], strlen(&dp[2]) + 1);
+
+       return (name);
+}
+
+
+void
+catch(sig)
+       int sig;
+{
+       if (!upgrading)
+               ckfini(0);
+       exit(12);
+}
+
+
+//
+// Logging stuff...
+//
+//
+#include <stdarg.h>
+#include <pthread.h>
+#include <time.h>
+
+#define FSCK_LOG_FILE "/var/log/fsck_hfs.log"
+
+extern char lflag;           // indicates if we're doing a live fsck (defined in fsck_hfs.c)
+extern char guiControl;      // indicates if we're outputting for the gui (defined in fsck_hfs.c)
+
+FILE   *log_file = NULL;
+
+/* Variables for in-memory log for strings that will be written to log file */
+char   *in_mem_log = NULL;
+char   *cur_in_mem_log = NULL;
+size_t  in_mem_log_size = 0;
+
+/* Variables for in-memory log for strings that will be printed on standard out */
+char   *in_mem_out = NULL;
+char   *cur_in_mem_out = NULL;
+size_t  in_mem_out_size = 0;
+
+int     live_fsck = 0;
+
+#define DEFAULT_IN_MEM_SIZE  4096
+
+static pthread_mutex_t mem_buf_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t  mem_buf_cond;
+
+static pthread_t       printing_thread;
+static pthread_t       logging_thread;
+static volatile int    keep_going = 1;
+
+#undef fprintf
+#undef printf
+
+// prototype
+void print_to_mem(int type, int mem_type, const char *fmt, const char *str, va_list ap);
+
+#define  DO_VPRINT   1    // types for print_to_mem
+#define  DO_STR      2
+
+/* Types for mem_type */
+#define IN_MEM_LOG   1  // in-memory log strings
+#define IN_MEM_OUT   2  // in-memory stdout strings
+
+static void *
+fsck_logging_thread(void *arg)
+{ 
+    int  copy_amt;
+    char buff[1024], *ptr;
+    
+    /* Handle writing to the log file */
+    while(keep_going || cur_in_mem_log != in_mem_log) {
+
+       pthread_mutex_lock(&mem_buf_lock);
+       while (keep_going != 0 && cur_in_mem_log == in_mem_log) {
+           int err;
+
+           err = pthread_cond_wait(&mem_buf_cond, &mem_buf_lock);
+           if (err != 0) {
+               fprintf(stderr, "error %d from cond wait\n", err);
+               break;
+           }
+       }           
+
+       copy_amt = (cur_in_mem_log - in_mem_log);
+       if (copy_amt == 0) {
+           pthread_mutex_unlock(&mem_buf_lock);
+           continue;
+       }
+       
+       if (copy_amt >= sizeof(buff)) {
+           copy_amt = sizeof(buff) - 1;
+           memcpy(buff, in_mem_log, copy_amt);
+
+           memmove(in_mem_log, &in_mem_log[copy_amt], (cur_in_mem_log - in_mem_log) - copy_amt);
+           cur_in_mem_log -= copy_amt;
+       } else {
+           memcpy(buff, in_mem_log, copy_amt);
+           cur_in_mem_log = in_mem_log;
+       }
+
+       buff[copy_amt] = '\0';
+       
+       pthread_mutex_unlock(&mem_buf_lock);
+
+       for(ptr=buff; *ptr; ) {
+           char *start;
+           
+           start = ptr;
+           while(*ptr && *ptr != '\n') {
+               ptr++;
+           }
+           if (*ptr == '\n') {
+               *ptr++ = '\0';
+               if (log_file) {
+                   fprintf(log_file, "%s: %s\n", cdevname ? cdevname : "UNKNOWN-DEV", start);
+               }
+           } else {
+               if (log_file) {
+                   fprintf(log_file, "%s", start);
+               }
+           }
+           
+       }
+
+       fflush(stdout);
+    }
+
+    return NULL;
+}
+
+static void *
+fsck_printing_thread(void *arg)
+{ 
+    int  copy_amt;
+    char buff[1024], *ptr;
+    
+    /* Handle writing to the out file */
+    while(keep_going || cur_in_mem_out != in_mem_out) {
+
+       pthread_mutex_lock(&mem_buf_lock);
+       while (keep_going != 0 && cur_in_mem_out == in_mem_out) {
+           int err;
+
+           err = pthread_cond_wait(&mem_buf_cond, &mem_buf_lock);
+           if (err != 0) {
+               fprintf(stderr, "error %d from cond wait\n", err);
+               break;
+           }
+       }           
+
+       copy_amt = (cur_in_mem_out - in_mem_out);
+       if (copy_amt == 0) {
+           pthread_mutex_unlock(&mem_buf_lock);
+           continue;
+       }
+       
+       if (copy_amt >= sizeof(buff)) {
+           copy_amt = sizeof(buff) - 1;
+           memcpy(buff, in_mem_out, copy_amt);
+
+           memmove(in_mem_out, &in_mem_out[copy_amt], (cur_in_mem_out - in_mem_out) - copy_amt);
+           cur_in_mem_out -= copy_amt;
+       } else {
+           memcpy(buff, in_mem_out, copy_amt);
+           cur_in_mem_out = in_mem_out;
+       }
+
+       buff[copy_amt] = '\0';
+       
+       pthread_mutex_unlock(&mem_buf_lock);
+
+       for(ptr=buff; *ptr; ) {
+           char *start;
+           
+           start = ptr;
+           while(*ptr && *ptr != '\n') {
+               ptr++;
+           }
+           if (*ptr == '\n') {
+               *ptr++ = '\0';
+               printf("%s\n", start);
+           } else {
+               printf("%s", start);
+           }
+           
+       }
+
+       fflush(stdout);
+    }
+
+    return NULL;
+}
+    
+
+int was_signaled = 0;
+
+void
+shutdown_logging(void)
+{
+    keep_going = 0;
+    time_t t;
+    
+    /* Log fsck_hfs check completion time */
+    t = time(NULL);
+    if (in_mem_log) {
+       print_to_mem(DO_STR, IN_MEM_LOG, "fsck_hfs completed at %s\n", ctime(&t), NULL);
+    } else {
+       fprintf(log_file, "%s: fsck_hfs completed at %s\n", cdevname ? cdevname : "UNKNOWN-DEV", ctime(&t));
+    }
+
+    if (was_signaled) {
+       // if we were signaled, we can't really call any of these
+       // functions from the context of a signal handler (which
+       // is how we're called if we don't have a signal handler).
+       // so we have our own signal handler which sets this var
+       // which tells us to just bail out.
+       return;
+    }
+
+    if (log_file && !live_fsck) {
+       fflush(log_file);
+       fclose(log_file);
+       log_file = NULL;
+    } else if ((in_mem_out || in_mem_log) && live_fsck && log_file) {
+       // make sure the printing and logging threads are woken up...
+       pthread_mutex_lock(&mem_buf_lock);
+       pthread_cond_broadcast(&mem_buf_cond);
+       pthread_mutex_unlock(&mem_buf_lock);
+       
+       // then wait for them 
+       pthread_join(printing_thread, NULL);
+       pthread_join(logging_thread, NULL);
+
+       free(in_mem_out);
+       in_mem_out = cur_in_mem_out = NULL;
+       in_mem_out_size = 0;
+
+       free(in_mem_log);
+       in_mem_log = cur_in_mem_log = NULL;
+       in_mem_log_size = 0;
+
+       if (log_file) {
+           fflush(log_file);
+           fclose(log_file);
+           log_file = NULL;
+       }
+    } else if (in_mem_log) {
+       int ret;
+       
+       if (getuid() == 0) {
+           // just in case, flush any pending output
+           fflush(stdout);
+           fflush(stderr);
+           
+           //
+           // fork so that the child can wait around until the
+           // root volume is mounted read-write and we can add
+           // our output to the log
+           //
+           ret = fork();
+       } else {
+           // if we're not root we don't need to fork
+           ret = 0;
+       }
+       if (ret == 0) {
+           int i;
+           char *fname = FSCK_LOG_FILE, path[PATH_MAX];
+
+               // Disk Management waits for fsck_hfs' stdout to close rather
+               // than the process death to understand if fsck_hfs has exited 
+               // or not.  Since we do not use stdout any further, close all 
+               // the file descriptors so that Disk Management does not wait 
+               // for 60 seconds unnecessarily on read-only boot volumes.
+               fclose(stdout);
+               fclose(stdin);
+               fclose(stderr);
+
+           // non-root will never be able to write to /var/log
+           // so point the file somewhere else.
+           if (getuid() != 0) {
+               struct passwd *pwd;
+               fname = NULL;
+               // each user will get their own log as ~/Library/Logs/fsck_hfs.log
+               pwd = getpwuid(getuid());
+               if (pwd) {
+                       snprintf(path, sizeof(path), "%s/Library/Logs/fsck_hfs.log", pwd->pw_dir);
+                       fname = &path[0];
+               } 
+           }
+
+           for(i=0; i < 60; i++) {
+               log_file = fopen(fname, "a");
+               if (log_file) {
+                   fwrite(in_mem_log, cur_in_mem_log - in_mem_log, 1, log_file);
+
+                   fflush(log_file);
+                   fclose(log_file);
+                   log_file = NULL;
+
+                   free(in_mem_log);
+                   in_mem_log = cur_in_mem_log = NULL;
+                   in_mem_log_size = 0;
+
+                   break;
+               } else {
+                       // hmmm, failed to open the output file so wait
+                       // a while only if the fs is read-only and then 
+                       // try again
+                       if (errno == EROFS) {
+                               sleep(1);
+                       } else {
+                               break;
+                       } 
+               }
+           }
+       }
+    }
+}
+
+static void
+my_sighandler(int sig)
+{
+    was_signaled = 1;
+    cleanup_fs_fd();
+    exit(sig);
+}
+
+
+void
+setup_logging(void)
+{
+    static int at_exit_setup = 0;
+    time_t t;
+    
+    // if this is set, we don't have to do anything
+    if (at_exit_setup) {
+       return;
+    }
+
+    if (guiControl) {
+           setlinebuf(stdout);
+           setlinebuf(stderr);
+    }
+
+    // our copy of this variable since we may
+    // need to change it to make the right thing
+    // happen for fsck on the root volume.
+    live_fsck = (int)lflag;
+
+    if (log_file == NULL) {
+       log_file = fopen(FSCK_LOG_FILE, "a");
+       if (log_file) {
+           setlinebuf(log_file);
+       } else {
+           //
+           // if we can't open the output file it's either because
+           // we're being run on the root volume during early boot
+           // or we were not run as the root user and so we can't
+           // write to /var/log/fsck_hfs.log.  in either case we
+           // turn off "live_fsck" so that the right thing happens
+           // in here with respect to where output goes.
+           //
+           live_fsck = 0;
+       }
+
+       if (!live_fsck && log_file) {
+           t = time(NULL);
+           fprintf(log_file, "\n%s: fsck_hfs started at %s", cdevname ? cdevname : "UNKNOWN-DEV", ctime(&t));
+           fflush(log_file);
+
+       } else if (live_fsck || in_mem_log == NULL || in_mem_out == NULL) {
+           //
+           // hmm, we couldn't open the log file (or it's a
+           // live fsck).  let's just squirrel away a copy 
+           // of the data in memory and then deal with it
+           // later (or print it out from a separate thread
+           // if we're doing a live fsck).
+           //
+           in_mem_log = (char *)malloc(DEFAULT_IN_MEM_SIZE);
+           in_mem_out = (char *)malloc(DEFAULT_IN_MEM_SIZE);
+           if ((in_mem_log != NULL) && (in_mem_out != NULL)) {
+               in_mem_log_size = DEFAULT_IN_MEM_SIZE;
+               in_mem_log[0] = '\0';
+               cur_in_mem_log = in_mem_log;
+
+               in_mem_out_size = DEFAULT_IN_MEM_SIZE;
+               in_mem_out[0] = '\0';
+               cur_in_mem_out = in_mem_out;
+
+               t = time(NULL);
+               print_to_mem(DO_STR, IN_MEM_LOG, "\nfsck_hfs started at %s", ctime(&t), NULL);
+
+               if (live_fsck && log_file) {
+                   pthread_cond_init(&mem_buf_cond, NULL);
+                   
+                   signal(SIGINT,  my_sighandler);
+                   signal(SIGHUP,  my_sighandler);
+                   signal(SIGTERM, my_sighandler);
+                   signal(SIGQUIT, my_sighandler);                 
+                   signal(SIGBUS,  my_sighandler);                 
+                   signal(SIGSEGV, my_sighandler);                 
+                   signal(SIGILL,  my_sighandler);                 
+                   
+                   pthread_create(&printing_thread, NULL, fsck_printing_thread, NULL);
+                   pthread_create(&logging_thread, NULL, fsck_logging_thread, NULL);
+
+               }
+           }
+       }
+
+       if (at_exit_setup == 0 && (log_file || in_mem_log || in_mem_out)) {
+           atexit(shutdown_logging);
+           at_exit_setup = 1;
+       }
+    }
+}
+
+
+void
+print_to_mem(int type, int mem_type, const char *fmt, const char *str, va_list ap)
+{
+    int ret;
+    size_t size_remaining;
+    va_list ap_copy;
+    char *cur_in_mem;
+    char *in_mem_data;
+    size_t in_mem_data_size;
+    
+    if (type == DO_VPRINT) {
+       va_copy(ap_copy, ap);
+    }
+    
+    if (mem_type == IN_MEM_LOG) {
+           cur_in_mem = cur_in_mem_log;
+           in_mem_data = in_mem_log;
+           in_mem_data_size = in_mem_log_size;
+    } else {
+           cur_in_mem = cur_in_mem_out;
+           in_mem_data = in_mem_out;
+           in_mem_data_size = in_mem_out_size;
+    }
+
+    /* Grab the lock only when adding output strings to the in-memory data */
+    if (live_fsck && (mem_type == IN_MEM_OUT)) {
+       pthread_mutex_lock(&mem_buf_lock);
+    }
+       
+    size_remaining = in_mem_data_size - (ptrdiff_t)(cur_in_mem - in_mem_data);
+    if (type == DO_VPRINT) {
+       ret = vsnprintf(cur_in_mem, size_remaining, fmt, ap);
+    } else {
+       ret = snprintf(cur_in_mem, size_remaining, fmt, str);
+    }
+    if (ret > size_remaining) {
+       char *new_log;
+       size_t amt;
+
+       if (ret >= DEFAULT_IN_MEM_SIZE) {
+           amt = (ret + 4095) & (~4095);   // round up to a 4k boundary
+       } else {
+           amt = DEFAULT_IN_MEM_SIZE;
+       }
+           
+       new_log = realloc(in_mem_data, in_mem_data_size + amt);
+       if (new_log == NULL) {
+           if (live_fsck && (mem_type == IN_MEM_OUT)) {
+               pthread_cond_signal(&mem_buf_cond);
+               pthread_mutex_unlock(&mem_buf_lock);
+           }
+           goto done;
+       }
+
+       in_mem_data_size += amt;
+       cur_in_mem = new_log + (cur_in_mem - in_mem_data);
+       in_mem_data = new_log;
+       size_remaining = in_mem_data_size - (ptrdiff_t)(cur_in_mem - new_log);
+       if (type == DO_VPRINT) {
+           ret = vsnprintf(cur_in_mem, size_remaining, fmt, ap_copy);
+       } else {
+           ret = snprintf(cur_in_mem, size_remaining, fmt, str);
+       }
+       if (ret <= size_remaining) {
+           cur_in_mem += ret;
+       }
+    } else {
+       cur_in_mem += ret;
+    }
+
+    if (live_fsck && (mem_type == IN_MEM_OUT)) {
+       pthread_cond_signal(&mem_buf_cond);
+       pthread_mutex_unlock(&mem_buf_lock);
+    }
+
+done:
+
+    if (mem_type == IN_MEM_LOG) {
+           cur_in_mem_log = cur_in_mem;
+           in_mem_log = in_mem_data;
+           in_mem_log_size = in_mem_data_size;
+    } else {
+           cur_in_mem_out = cur_in_mem;
+           in_mem_out = in_mem_data;
+           in_mem_out_size = in_mem_data_size;
+    }
+
+    if (type == DO_VPRINT) {
+       va_end(ap_copy);
+    }
+}
+
+
+static int need_prefix=1;
+
+#define LOG_PREFIX   \
+       if (need_prefix) { \
+            fprintf(log_file, "%s: ", cdevname); \
+           if (strchr(fmt, '\n')) { \
+               need_prefix = 1; \
+           } else { \
+               need_prefix = 0; \
+           } \
+       } else if (strchr(fmt, '\n')) { \
+           need_prefix = 1; \
+       }
+
+/* Print output string on given stream or store it into in-memory buffer */
+#define VOUT(stream, fmt, ap) \
+    if (!live_fsck) { \
+       vfprintf(stream, fmt, ap);              \
+    } else { \
+       print_to_mem(DO_VPRINT, IN_MEM_OUT, fmt, NULL, ap);     \
+    }
+
+#define FOUT(fmt, str) \
+    print_to_mem(DO_STR, IN_MEM_OUT, fmt, str, NULL);  
+
+/* Store output string written to fsck_hfs.log into file or in-memory buffer */
+#define VLOG(fmt, ap) \
+    va_start(ap, fmt); \
+    VLOG_INTERNAL(fmt, ap);
+
+#define VLOG_INTERNAL(fmt, ap) \
+    if (log_file && !live_fsck) { \
+       LOG_PREFIX \
+       vfprintf(log_file, fmt, ap); \
+    } else { \
+       print_to_mem(DO_VPRINT, IN_MEM_LOG, fmt, NULL, ap);     \
+    }
+
+#define FLOG(fmt, str) \
+    if (log_file && !live_fsck) { \
+       LOG_PREFIX;                             \
+       fprintf(log_file, fmt, str);            \
+    } else { \
+       print_to_mem(DO_STR, IN_MEM_LOG, fmt, str, NULL);       \
+    }
+
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+/*
+ * An unexpected inconsistency occurred.
+ * Die if preening, otherwise just print message and continue.
+ */
+void
+#if __STDC__
+pfatal(const char *fmt, ...)
+#else
+pfatal(fmt, va_alist)
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+
+       setup_logging();
+       
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       if (!preen) {
+               (void)vfprintf(stderr, fmt, ap);
+               VLOG(fmt, ap);
+               va_end(ap);
+               return;
+       }
+       if (!live_fsck) 
+           (void)fprintf(stderr, "%s: ", cdevname);
+       FLOG("%s: ", cdevname);
+       
+       if (!live_fsck) 
+           (void)vfprintf(stderr, fmt, ap);
+       VLOG(fmt, ap);
+       
+       if (!live_fsck) 
+           (void)fprintf(stderr,
+               "\n%s: UNEXPECTED INCONSISTENCY; RUN fsck_hfs MANUALLY.\n",
+               cdevname);
+       FLOG("\n%s: UNEXPECTED INCONSISTENCY; RUN fsck_hfs MANUALLY.\n", cdevname);
+
+       exit(EEXIT);
+}
+
+/*
+ * Pwarn just prints a message when not preening,
+ * or a warning (preceded by filename) when preening.
+ */
+void
+#if __STDC__
+pwarn(const char *fmt, ...)
+#else
+pwarn(fmt, va_alist)
+       char *fmt;
+       va_dcl
+#endif
+{
+       va_list ap;
+
+       setup_logging();
+
+#if __STDC__
+       va_start(ap, fmt);
+#else
+       va_start(ap);
+#endif
+       if (preen) {
+               (void)fprintf(stderr, "%s: ", cdevname);
+               FLOG("%s: ", cdevname);
+       }
+       if (!live_fsck) 
+           (void)vfprintf(stderr, fmt, ap);
+       VLOG(fmt, ap);
+       
+       va_end(ap);
+}
+
+/* Write a string and parameters, if any, directly to the log file.
+ * These strings will not be printed to standard out/error.
+ */
+void 
+logstring(void *c, const char *str)
+{
+       llog("%s", str);
+}
+
+/* Write a string and parameters, if any, directly to standard out/error.
+ * These strings will not be printed to log file.
+ */
+void 
+outstring(void *c, const char *str) 
+{
+       olog("%s", str);
+}
+
+/* Write to both standard out and log file */
+void
+plog(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    vplog(fmt, ap);
+    va_end(ap);
+}
+
+/* Write to only standard out */
+void
+olog(const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    
+    setup_logging();
+
+    /* For live fsck_hfs, add output strings to in-memory log, 
+     * and for non-live fsck_hfs, print output to stdout. 
+     */
+    VOUT(stdout, fmt, ap);
+
+    va_end(ap);
+}
+
+/* Write to only log file */
+void 
+llog(const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+
+       setup_logging();
+       need_prefix = 1;
+       VLOG(fmt, ap);
+
+       va_end(ap);
+}
+
+/* Write to both standard out and log file */
+void
+vplog(const char *fmt, va_list ap)
+{
+    va_list copy_ap;
+
+    va_copy(copy_ap, ap);
+    
+    setup_logging();
+
+    /* Always print prefix to strings written to log files */
+    need_prefix = 1;
+
+    /* Handle output strings, print to stdout or store in-memory */
+    VOUT(stdout, fmt, ap);
+       
+    /* Add log strings to the log file.  VLOG() handles live case internally */
+    VLOG_INTERNAL(fmt, copy_ap);
+}
+
+/* Write to both standard out and log file */
+void
+fplog(FILE *stream, const char *fmt, ...)
+{
+    va_list ap, copy_ap;
+    va_start(ap, fmt);
+    va_copy(copy_ap, ap);
+    
+    setup_logging();
+    need_prefix = 1;
+
+    /* Handle output strings, print to given stream or store in-memory */
+    VOUT(stream, fmt, ap);
+       
+    /* Add log strings to the log file.  VLOG() handles live case internally */
+    VLOG(fmt, copy_ap);
+
+    va_end(ap);
+}
+
+#define kProgressToggle        "kern.progressmeterenable"
+#define        kProgress       "kern.progressmeter"
+
+void
+start_progress(void)
+{
+       int rv;
+       int enable = 1;
+       if (hotroot == 0)
+               return;
+       rv = sysctlbyname(kProgressToggle, NULL, NULL, &enable, sizeof(enable));
+       if (debug && rv == -1 && errno != ENOENT) {
+               warn("sysctl(%s) failed", kProgressToggle);
+       }
+}
+
+void
+draw_progress(int pct)
+{
+       int rv;
+       if (hotroot == 0)
+               return;
+       rv = sysctlbyname(kProgress, NULL, NULL, &pct, sizeof(pct));
+       if (debug && rv == -1 && errno != ENOENT) {
+               warn("sysctl(%s) failed", kProgress);
+       }
+}
+
+void
+end_progress(void)
+{
+       int rv;
+       int enable = 0;
+       if (hotroot == 0)
+               return;
+       rv = sysctlbyname(kProgressToggle, NULL, NULL, &enable, sizeof(enable));
+       if (debug && rv == -1 && errno != ENOENT) {
+               warn("sysctl(%s) failed", kProgressToggle);
+       }
+}
+