]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
e2fsck: add support for saving the problem code log
authorTheodore Ts'o <tytso@mit.edu>
Mon, 6 May 2019 05:10:53 +0000 (01:10 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 6 May 2019 14:15:41 +0000 (10:15 -0400)
Add the ability to save a log of problems found by e2fsck in a log
file that can be specified via /etc/e2fsck.conf.

Signed-off-by: Theodore Ts'o <tytso@mit.edu>
e2fsck/e2fsck.c
e2fsck/e2fsck.conf.5.in
e2fsck/e2fsck.h
e2fsck/logfile.c
e2fsck/problem.c
e2fsck/problemP.h
e2fsck/unix.c

index 88088a2578bb6542209dd56999dfe565c6143105..2af610529ba32a8442465c0eb9db9b60b7968790 100644 (file)
@@ -215,6 +215,12 @@ void e2fsck_free_context(e2fsck_t ctx)
        if (ctx->logf)
                fclose(ctx->logf);
 
+       if (ctx->problem_log_fn)
+               free(ctx->problem_log_fn);
+
+       if (ctx->problem_logf)
+               fclose(ctx->problem_logf);
+
        ext2fs_free_mem(&ctx);
 }
 
index 708e21346a3a947933fa7ef991836ebda8a6a477..48ad0fde1a6bcf7d5614c1d38e319ab7760379d7 100644 (file)
@@ -175,7 +175,9 @@ defaults to false.
 .I log_dir
 If the
 .I log_filename
-relation contains a relative pathname, then the log file will be placed
+or
+.I problem_log_filename
+relations contains a relative pathname, then the log file will be placed
 in the directory named by the
 .I log_dir
 relation.
@@ -224,6 +226,16 @@ end up delaying the boot process for a long time (potentially hours).
 If this boolean relation is true, do not offer to optimize the extent
 tree by reducing the tree's width or depth.  This setting defaults to false.
 .TP
+.I problem_log_filename
+This relation specifies the file name where a log of problem codes
+found by e2fsck be written.  The filename may contain various
+percent-expressions (%D, %T, %N,
+etc.) which will be expanded so that the file name for the log file can
+include things like date, time, device name, and other run-time
+parameters.  See the
+.B LOGGING
+section for more details.
+.TP
 .I readahead_mem_pct
 Use this percentage of memory to try to read in metadata blocks ahead of the
 main e2fsck thread.  This should reduce run times, depending on the speed of
index 1c7a67cba1cedd04e87f929d5fa0bba44d2fad39..2d359b384c1887acc7ff8e909e5ba11f44809ba3 100644 (file)
@@ -231,6 +231,8 @@ struct e2fsck_struct {
        char *io_options;
        FILE    *logf;
        char    *log_fn;
+       FILE    *problem_logf;
+       char    *problem_log_fn;
        int     flags;          /* E2fsck internal flags */
        int     options;
        int     blocksize;      /* blocksize */
index e004f31a9e386c13b68f9b8d931aeab6a0c0d34f..3eeefd1929434a3c996ebc715d8ab4c039337d37 100644 (file)
@@ -285,8 +285,9 @@ static FILE *save_output(const char *s0, const char *s1, const char *s2)
 }
 
 #ifndef TEST_PROGRAM
-void set_up_logging(e2fsck_t ctx)
+static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
 {
+       FILE *f = NULL;
        struct string s, s1, s2;
        char *s0 = 0, *log_dir = 0, *log_fn = 0;
        int log_dir_wait = 0;
@@ -295,10 +296,10 @@ void set_up_logging(e2fsck_t ctx)
 
        profile_get_boolean(ctx->profile, "options", "log_dir_wait", 0, 0,
                            &log_dir_wait);
-       if (ctx->log_fn)
-               log_fn = string_copy(ctx, ctx->log_fn, 0);
+       if (fn)
+               log_fn = string_copy(ctx, fn, 0);
        else
-               profile_get_string(ctx->profile, "options", "log_filename",
+               profile_get_string(ctx->profile, "options", key,
                                   0, 0, &log_fn);
        profile_get_string(ctx->profile, "options", "log_dir", 0, 0, &log_dir);
 
@@ -328,13 +329,13 @@ void set_up_logging(e2fsck_t ctx)
        }
 
        if (s0)
-               ctx->logf = fopen(s0, "w");
-       if (!ctx->logf && s1.s)
-               ctx->logf = fopen(s1.s, "w");
-       if (!ctx->logf && s2.s)
-               ctx->logf = fopen(s2.s, "w");
-       if (!ctx->logf && log_dir_wait)
-               ctx->logf = save_output(s0, s1.s, s2.s);
+               f = fopen(s0, "w");
+       if (!f && s1.s)
+               f = fopen(s1.s, "w");
+       if (!f && s2.s)
+               f = fopen(s2.s, "w");
+       if (!f && log_dir_wait)
+               f = save_output(s0, s1.s, s2.s);
 
 out:
        free(s.s);
@@ -342,7 +343,14 @@ out:
        free(s2.s);
        free(log_fn);
        free(log_dir);
-       return;
+       return f;
+}
+
+void set_up_logging(e2fsck_t ctx)
+{
+       ctx->logf = set_up_log_file(ctx, "log_filename", ctx->log_fn);
+       ctx->problem_logf = set_up_log_file(ctx, "problem_log_filename",
+                                           ctx->problem_log_fn);
 }
 #else
 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
index 0e6bacec21abe48bf2101ef5fb9542407a002a55..01f0867376adf4d2c07ec3980a721cf4d7dd1a03 100644 (file)
@@ -515,7 +515,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 1: Checking inodes, blocks, and sizes */
        { PR_1_PASS_HEADER,
          N_("Pass 1: Checking @is, @bs, and sizes\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* Root inode is not a directory */
        { PR_1_ROOT_NO_DIR, N_("@r is not a @d.  "),
@@ -1190,7 +1190,7 @@ static struct e2fsck_problem problem_table[] = {
        { PR_1B_PASS_HEADER,
          N_("\nRunning additional passes to resolve @bs claimed by more than one @i...\n"
          "Pass 1B: Rescanning for @m @bs\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* Duplicate/bad block(s) header */
        { PR_1B_DUP_BLOCK_HEADER,
@@ -1235,13 +1235,13 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 1C: Scan directories for inodes with multiply-claimed blocks. */
        { PR_1C_PASS_HEADER,
          N_("Pass 1C: Scanning directories for @is with @m @bs\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
 
        /* Pass 1D: Reconciling multiply-claimed blocks */
        { PR_1D_PASS_HEADER,
          N_("Pass 1D: Reconciling @m @bs\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* File has duplicate blocks */
        { PR_1D_DUP_FILE,
@@ -1286,7 +1286,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 1E: Optimizing extent trees */
        { PR_1E_PASS_HEADER,
          N_("Pass 1E: Optimizing @x trees\n"),
-         PROMPT_NONE, PR_PREEN_NOMSG, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER | PR_PREEN_NOMSG, 0, 0, 0 },
 
        /* Failed to optimize extent tree */
        { PR_1E_OPTIMIZE_EXT_ERR,
@@ -1328,7 +1328,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 2: Checking directory structure */
        { PR_2_PASS_HEADER,
          N_("Pass 2: Checking @d structure\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* Bad inode number for '.' */
        { PR_2_BAD_INODE_DOT,
@@ -1694,7 +1694,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 3: Checking directory connectivity */
        { PR_3_PASS_HEADER,
          N_("Pass 3: Checking @d connectivity\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* Root inode not allocated */
        { PR_3_NO_ROOT_INODE,
@@ -1836,7 +1836,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 3A: Optimizing directories */
        { PR_3A_PASS_HEADER,
          N_("Pass 3A: Optimizing directories\n"),
-         PROMPT_NONE, PR_PREEN_NOMSG, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER | PR_PREEN_NOMSG, 0, 0, 0 },
 
        /* Error iterating over directories */
        { PR_3A_OPTIMIZE_ITER,
@@ -1868,7 +1868,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 4: Checking reference counts */
        { PR_4_PASS_HEADER,
          N_("Pass 4: Checking reference counts\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* Unattached zero-length inode */
        { PR_4_ZERO_LEN_INODE,
@@ -1906,7 +1906,7 @@ static struct e2fsck_problem problem_table[] = {
        /* Pass 5: Checking group summary information */
        { PR_5_PASS_HEADER,
          N_("Pass 5: Checking @g summary information\n"),
-         PROMPT_NONE, 0, 0, 0, 0 },
+         PROMPT_NONE, PR_HEADER, 0, 0, 0 },
 
        /* Padding at end of inode bitmap is not set. */
        { PR_5_INODE_BMAP_PADDING,
@@ -2191,6 +2191,45 @@ static void reconfigure_bool(e2fsck_t ctx, struct e2fsck_problem *ptr,
                ptr->flags &= ~mask;
 }
 
+static void print_problem(FILE *f, problem_t code, int answer, int fixed,
+                         struct e2fsck_problem *ptr,
+                         struct problem_context *pctx)
+{
+       if (ptr->flags & PR_HEADER) {
+               fprintf(f, "<header code=\"0x%06x\">\n", code);
+               return;
+       }
+       fprintf(f, "<problem code=\"0x%06x\" answer=\"%d\"", code, answer);
+       if (pctx->errcode)
+               fprintf(f, " errcode=\"%lu\"", pctx->errcode);
+       if (fixed)
+               fputs(" fixed=\"1\"", f);
+       if (pctx->ino)
+               fprintf(f, " ino=\"%lu\"", pctx->ino);
+       if (pctx->ino2)
+               fprintf(f, " ino2=\"%lu\"", pctx->ino2);
+       if (pctx->dir)
+               fprintf(f, " dir=\"%lu\"", pctx->dir);
+       if (pctx->blk)
+               fprintf(f, " blk=\"%llu\"", pctx->blk);
+       if (pctx->blk2)
+               fprintf(f, " blk2=\"%llu\"", pctx->blk2);
+       if (pctx->blkcount != (e2_blkcnt_t) -1)
+               fprintf(f, " blkcount=\"%lld\"", pctx->blkcount);
+       if (pctx->group != (dgrp_t) -1)
+               fprintf(f, " group=\"%lu\"", pctx->group);
+       if (pctx->csum1)
+               fprintf(f, " csum1=\"%lu\"", pctx->csum1);
+       if (pctx->csum2)
+               fprintf(f, " csum2=\"%lu\"", pctx->csum2);
+       if (pctx->num)
+               fprintf(f, " num=\"%llu\"", pctx->num);
+       if (pctx->num2)
+               fprintf(f, " num2=\"%llu\"", pctx->num2);
+       if (pctx->str)
+               fprintf(f, " str=\"%s\"", pctx->str);
+       fputs("/>\n", f);
+}
 
 int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
 {
@@ -2201,6 +2240,7 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
        int             def_yn, answer, ans;
        int             print_answer = 0;
        int             suppress = 0;
+       int             fixed = 0;
 
        ptr = find_problem(code);
        if (!ptr) {
@@ -2275,6 +2315,9 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
                    (ldesc->flags & (PRL_YES | PRL_NO)))
                        suppress++;
                if (ptr->count == ptr->max_count + 1) {
+                       if (ctx->problem_logf)
+                               fprintf(ctx->problem_logf,
+                                       "<suppressed code=\"0x%06x\">\n", code);
                        printf("...problem 0x%06x suppressed\n",
                               ptr->e2p_code);
                        fflush(stdout);
@@ -2345,8 +2388,14 @@ int fix_problem(e2fsck_t ctx, problem_t code, struct problem_context *pctx)
                answer = fix_problem(ctx, ptr->second_code, pctx);
 
        if (answer && (ptr->prompt != PROMPT_NONE) &&
-           !(ptr->flags & PR_NOT_A_FIX))
+           !(ptr->flags & PR_NOT_A_FIX)) {
+               fixed = 1;
                ctx->flags |= E2F_FLAG_PROBLEMS_FIXED;
+       }
+
+       if (ctx->problem_logf)
+               print_problem(ctx->problem_logf, code, answer, fixed,
+                             ptr, pctx);
 
        return answer;
 }
index 63bb8df6c7bf70df78085baa21fabbc8746cae9b..95c1a704e2cfb02dc55ce4e38de46cd4c69430f8 100644 (file)
@@ -45,3 +45,4 @@ struct latch_descr {
                                    from the config file */
 #define PR_FORCE_NO    0x100000 /* Force the answer to be no */
 #define PR_NOT_A_FIX   0x200000 /* Yes doesn't mean a problem was fixed */
+#define PR_HEADER      0x400000 /* Problem is a header marker */
index 37fca0f187ba21b7101b956f1b895f5f8e990f3c..f8c4983d94be2cabe0b8c2506a1ff152ad852980 100644 (file)
@@ -50,6 +50,7 @@ extern int optind;
 #include "e2p/e2p.h"
 #include "et/com_err.h"
 #include "e2p/e2p.h"
+#include "uuid/uuid.h"
 #include "support/plausible.h"
 #include "e2fsck.h"
 #include "problem.h"
@@ -736,6 +737,12 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                        else
                                ctx->log_fn = string_copy(ctx, arg, 0);
                        continue;
+               } else if (strcmp(token, "problem_log") == 0) {
+                       if (!arg)
+                               extended_usage++;
+                       else
+                               ctx->problem_log_fn = string_copy(ctx, arg, 0);
+                       continue;
                } else if (strcmp(token, "bmap2extent") == 0) {
                        ctx->options |= E2F_OPT_CONVERT_BMAP;
                        continue;
@@ -1417,6 +1424,7 @@ int main (int argc, char *argv[])
        set_up_logging(ctx);
        if (ctx->logf) {
                int i;
+
                fputs("E2fsck run: ", ctx->logf);
                for (i = 0; i < argc; i++) {
                        if (i)
@@ -1425,6 +1433,17 @@ int main (int argc, char *argv[])
                }
                fputc('\n', ctx->logf);
        }
+       if (ctx->problem_logf) {
+               int i;
+
+               fputs("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n",
+                     ctx->problem_logf);
+               fprintf(ctx->problem_logf, "<invocation prog=\"%s\"",
+                       argv[0]);
+               for (i = 1; i < argc; i++)
+                       fprintf(ctx->problem_logf, " arg=\"%s\"", argv[i]);
+               fputs(">\n", ctx->problem_logf);
+       }
 
        init_resource_track(&ctx->global_rtrack, NULL);
        if (!(ctx->options & E2F_OPT_PREEN) || show_version_only)
@@ -1682,6 +1701,24 @@ failure:
                if (isspace(*cp) || *cp == ':')
                        *cp = '_';
 
+       if (ctx->problem_logf) {
+               char buf[48];
+
+               fprintf(ctx->problem_logf, "<filesystem dev=\"%s\"",
+                       ctx->filesystem_name);
+               if (!uuid_is_null(sb->s_uuid)) {
+                       uuid_unparse(sb->s_uuid, buf);
+                       fprintf(ctx->problem_logf, " uuid=\"%s\"", buf);
+               }
+               if (sb->s_volume_name[0]) {
+                       memset(buf, 0, sizeof(buf));
+                       strncpy(buf, sb->s_volume_name,
+                               sizeof(sb->s_volume_name));
+                       fprintf(ctx->problem_logf, " label=\"%s\"", buf);
+               }
+               fputs(">\n", ctx->problem_logf);
+       }
+
        ehandler_init(fs->io);
 
        if (ext2fs_has_feature_mmp(fs->super) &&