summaryrefslogtreecommitdiff
path: root/ufs-fsck/utilities.c
diff options
context:
space:
mode:
Diffstat (limited to 'ufs-fsck/utilities.c')
-rw-r--r--ufs-fsck/utilities.c277
1 files changed, 206 insertions, 71 deletions
diff --git a/ufs-fsck/utilities.c b/ufs-fsck/utilities.c
index e83cb2f5..d886e780 100644
--- a/ufs-fsck/utilities.c
+++ b/ufs-fsck/utilities.c
@@ -24,6 +24,9 @@
#include <unistd.h>
#include <stdarg.h>
#include <pwd.h>
+#include <error.h>
+
+static void retch (char *reason);
/* Read disk block ADDR into BUF of SIZE bytes. */
void
@@ -126,7 +129,7 @@ allocblk (int nfrags)
/* Mark the frags allocated in our map */
for (k = 0; k < nfrags; k++)
setbmap (i + j + k);
-
+
return (i + j);
}
}
@@ -157,33 +160,93 @@ check_range (daddr_t blk, int cnt)
return 0;
}
+
+struct problem {
+ char *desc;
+ struct problem *prev;
+};
+
+/* A queue of problems found by fsck that are waiting resolution. The front
+ of the list is the most recent problem found (and presumably since
+ previous problems haven't been resolved yet, they depend on this one being
+ solved for their resolution). */
+static struct problem *problems = 0;
+
+static struct problem *free_problems = 0;
+
+static void
+push_problem (char *fmt, va_list args)
+{
+ struct problem *prob = free_problems;
-/* Like printf, but exit if we are preening. */
-int
-pfatal (char *fmt, ...)
+ if (! prob)
+ prob = malloc (sizeof (struct problem));
+ else
+ problems = prob->prev;
+ if (! prob)
+ retch ("malloc failed");
+
+ if (vasprintf (&prob->desc, fmt, args) < 0)
+ retch ("vasprintf failed");
+
+ prob->prev = problems;
+ problems = prob;
+}
+
+/* Print the most recent problem, and perhaps how it was resolved. */
+static void
+resolve_problem (char *fix)
{
- va_list args;
- int ret;
+ struct problem *prob = problems;
+
+ if (! prob)
+ retch ("no more problems");
+
+ problems = prob->prev;
+ prob->prev = free_problems;
if (preen && device_name)
- printf ("%s: ", device_name);
-
- va_start (args, fmt);
- ret = vprintf (fmt, args);
- va_end (args);
- putchar ('\n');
- if (preen)
- exit (1);
-
- return ret;
+ printf ("%s: %s", device_name, prob->desc);
+ else
+ printf ("%s", prob->desc);
+ if (fix)
+ printf (" (%s)\n", fix);
+ else
+ putchar ('\n');
+ free (prob->desc);
}
-
+
+/* Retire all problems as if they failed. We print them in chronological
+ order rather than lifo order, as this is a bit clearer, and we can do it
+ when we know they're all going to fail. */
+static void
+flush_problems ()
+{
+ struct problem *fail (struct problem *prob)
+ {
+ struct problem *last = prob->prev ? fail (prob->prev) : prob;
+ if (preen && device_name)
+ printf ("%s: %s\n", device_name, prob->desc);
+ else
+ puts (prob->desc);
+ free (prob->desc);
+ return last;
+ }
+ if (problems)
+ {
+ fail (problems)->prev = free_problems;
+ free_problems = problems;
+ }
+}
+
/* Like printf, but exit after printing. */
void
errexit (char *fmt, ...)
{
va_list args;
+ flush_problems ();
+
if (preen && device_name)
printf ("%s: ", device_name);
@@ -191,37 +254,146 @@ errexit (char *fmt, ...)
vprintf (fmt, args);
va_end (args);
putchar ('\n');
+
exit (1);
}
-/* Like printf, but give more information (when we fully support it)
- when preening. */
-int
-pwarn (char *fmt, ...)
+static void
+retch (char *reason)
+{
+ flush_problems ();
+ error (99, 0, "(internal error) %s!", reason);
+}
+
+static void
+punt ()
+{
+ problem (0, "PLEASE RUN fsck MANUALLY");
+ flush_problems ();
+ exit (1);
+}
+
+/* Store away the given message about a problem found. A call to problem must
+ be matched later with a call to pfix, pfail, or reply; to print more
+ in the same message, intervening calls to pextend can be used. If SEVERE is
+ true, and we're in preen mode, then the program is terminated. */
+void
+problem (int severe, char *fmt, ...)
{
va_list args;
- int ret;
- if (preen && device_name)
- printf ("%s: ", device_name);
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+
+ if (severe && preen)
+ punt ();
+}
+
+/* Following a call to problem (with perhaps intervening calls to
+ pmore), appends the given message to that message. */
+void
+pextend (char *fmt, ...)
+{
+ va_list args;
+ char *more, *concat;
+ struct problem *prob = problems;
+
+ if (! prob)
+ retch ("No pending problem to add to");
va_start (args, fmt);
- ret = vprintf (fmt, args);
+ if (vasprintf (&more, fmt, args) < 0)
+ retch ("vasprintf failed");
va_end (args);
- return ret;
+ concat = realloc (prob->desc, strlen (prob->desc) + 1 + strlen (more) + 1);
+ if (! concat)
+ retch ("realloc failed");
+
+ strcpy (concat + strlen (concat), more);
+ prob->desc = concat;
}
+
+/* Like problem, but as if immediately followed by pfail. */
+void
+warning (int severe, char *fmt, ...)
+{
+ va_list args;
-/* Print how a problem was fixed in preen mode. */
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+
+ flush_problems ();
+
+ if (severe && preen)
+ punt ();
+}
+
+/* Like problem, but appends a helpful description of the given inode number to
+ the message. */
+void
+pinode (int severe, ino_t ino, char *fmt, ...)
+{
+ if (fmt)
+ {
+ va_list args;
+ va_start (args, fmt);
+ push_problem (fmt, args);
+ va_end (args);
+ }
+
+ if (ino < ROOTINO || ino > maxino)
+ pextend ("(BOGUS INODE) I=%d", ino);
+ else
+ {
+ char *p;
+ struct dinode dino;
+ struct passwd *pw;
+
+ getinode (ino, &dino);
+
+ pextend (" %s I=%d", (DI_MODE (&dino) & IFMT) == IFDIR ? "DIR" : "FILE",
+ ino);
+
+ pw = getpwuid (dino.di_uid);
+ if (pw)
+ pextend (" O=%s", pw->pw_name);
+ else
+ pextend (" O=%lu", dino.di_uid);
+
+ pextend (" M=0%o", DI_MODE (&dino));
+ pextend (" SZ=%llu", dino.di_size);
+ p = ctime (&dino.di_mtime.ts_sec);
+ pextend (" MT=%12.12s %4.4s", &p[4], &p[20]);
+ }
+
+ if (severe && preen)
+ punt ();
+}
+
+/* Print a successful resolution to a pending problem. Must follow a call to
+ problem or pextend. */
void
pfix (char *fix)
{
if (preen)
- printf (" (%s)\n", fix);
+ resolve_problem (fix ?: "FIXED");
}
+/* Print an unsuccessful resolution to a pending problem. Must follow a call
+ to problem or pextend. */
+void
+pfail (char *failure)
+{
+ if (preen)
+ resolve_problem (failure);
+}
+
/* Ask the user a question; return 1 if the user says yes, and 0
- if the user says no. */
+ if the user says no. This call must follow a call to problem or pextend,
+ which it completes. */
int
reply (char *question)
{
@@ -229,10 +401,13 @@ reply (char *question)
char c;
if (preen)
- pfatal ("INTERNAL ERROR: GOT TO reply ()");
+ retch ("Got to reply() in preen mode");
+
+ /* Emit the problem to which the question pertains. */
+ resolve_problem (0);
persevere = !strcmp (question, "CONTINUE");
- putchar ('\n');
+
if (!persevere && (nowrite || writefd < 0))
{
fix_denied = 1;
@@ -269,43 +444,3 @@ reply (char *question)
}
}
}
-
-/* Print a helpful description of the given inode number. */
-void
-pinode (ino_t ino, char *fmt, ...)
-{
- if (fmt)
- {
- va_list args;
- va_start (args, fmt);
- vprintf (fmt, args);
- va_end (args);
- putchar (' ');
- }
-
- if (ino < ROOTINO || ino > maxino)
- printf (" NODE I=%d", ino);
- else
- {
- char *p;
- struct dinode dino;
- struct passwd *pw;
-
- getinode (ino, &dino);
-
- printf ("%s I=%d", (DI_MODE (&dino) & IFMT) == IFDIR ? "DIR" : "FILE",
- ino);
-
- pw = getpwuid (dino.di_uid);
- if (pw)
- printf (" OWNER=%s", pw->pw_name);
- else
- printf (" OWNER=%lu", dino.di_uid);
-
- printf (" MODE=%o", DI_MODE (&dino));
- printf (" SIZE=%llu ", dino.di_size);
- p = ctime (&dino.di_mtime.ts_sec);
- printf (" MTIME=%12.12s %4.4s", &p[4], &p[20]);
- }
-}
-