From cab6cd67bca29592b19f792d44cbb639c486087e Mon Sep 17 00:00:00 2001 From: bouyer Date: Sun, 13 Sep 2009 14:25:28 +0000 Subject: [PATCH] Do some basic checks of the WAPBL journal, to abort the boot before the kernel refuse to mount a filesystem read-write (booting a system multiuser with critical filesystems read-only is bad): Add a check_wapbl() which will check some WAPBL values in the superblock, and try to read the journal via wapbl_replay_start() if there is one. pfatal() if one of these fail (abort boot if in preen mode, as "CONTINUE" otherwise). In non-preen mode the bogus journal will be cleared. check_wapbl() is always called if the superblock supports WAPBL. Even if FS_DOWAPBL is not there, there could be flags asking the kernel to clear or create a log with bogus values which would cause the kernel refuse to mount the filesystem. Discussed in http://mail-index.netbsd.org/tech-kern/2009/08/17/msg005896.html and followups. --- sbin/fsck_ffs/extern.h | 3 +- sbin/fsck_ffs/setup.c | 49 +++++++----- sbin/fsck_ffs/wapbl.c | 178 ++++++++++++++++++++++++++++------------- 3 files changed, 156 insertions(+), 74 deletions(-) diff --git a/sbin/fsck_ffs/extern.h b/sbin/fsck_ffs/extern.h index 099a8d22deea..3e48ce9e47d9 100644 --- a/sbin/fsck_ffs/extern.h +++ b/sbin/fsck_ffs/extern.h @@ -1,4 +1,4 @@ -/* $NetBSD: extern.h,v 1.24 2008/08/30 10:46:16 bouyer Exp $ */ +/* $NetBSD: extern.h,v 1.25 2009/09/13 14:25:28 bouyer Exp $ */ /* * Copyright (c) 1994 James A. Jegers @@ -82,6 +82,7 @@ void setinodebuf(ino_t); int setup(const char *, const char *); void voidquit(int); +int check_wapbl(void); void replay_wapbl(void); void cleanup_wapbl(void); int read_wapbl(char *, long, daddr_t); diff --git a/sbin/fsck_ffs/setup.c b/sbin/fsck_ffs/setup.c index b50eb9782cd5..ef13c4d8e5be 100644 --- a/sbin/fsck_ffs/setup.c +++ b/sbin/fsck_ffs/setup.c @@ -1,4 +1,4 @@ -/* $NetBSD: setup.c,v 1.87 2009/04/07 05:50:11 mrg Exp $ */ +/* $NetBSD: setup.c,v 1.88 2009/09/13 14:25:28 bouyer Exp $ */ /* * Copyright (c) 1980, 1986, 1993 @@ -34,7 +34,7 @@ #if 0 static char sccsid[] = "@(#)setup.c 8.10 (Berkeley) 5/9/95"; #else -__RCSID("$NetBSD: setup.c,v 1.87 2009/04/07 05:50:11 mrg Exp $"); +__RCSID("$NetBSD: setup.c,v 1.88 2009/09/13 14:25:28 bouyer Exp $"); #endif #endif /* not lint */ @@ -173,24 +173,35 @@ setup(const char *dev, const char *origdev) doskipclean = 0; pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag); } - if (sblock->fs_flags & FS_DOWAPBL) { - if (preen && skipclean) { - if (!quiet) - pwarn("file system is journaled; not checking\n"); - return (-1); + /* ffs_superblock_layout() == 2 */ + if (sblock->fs_magic != FS_UFS1_MAGIC || + (sblock->fs_old_flags & FS_FLAGS_UPDATED) != 0) { + /* can have WAPBL */ + if (check_wapbl() != 0) { + doskipclean = 0; + } + if (sblock->fs_flags & FS_DOWAPBL) { + if (preen) { + if (!quiet) + pwarn("file system is journaled; " + "not checking\n"); + return (-1); + } + if (!quiet) + pwarn("** File system is journaled; " + "replaying journal\n"); + replay_wapbl(); + doskipclean = 0; + sblock->fs_flags &= ~FS_DOWAPBL; + sbdirty(); + /* Although we may have updated the superblock from + * the journal, we are still going to do a full check, + * so we don't bother to re-read the superblock from + * the journal. + * XXX, instead we could re-read the superblock and + * then not force doskipclean = 0 + */ } - if (!quiet) - pwarn("** File system is journaled; replaying journal\n"); - replay_wapbl(); - doskipclean = 0; - sblock->fs_flags &= ~FS_DOWAPBL; - sbdirty(); - /* Although we may have updated the superblock from the - * journal, we are still going to do a full check, so we - * don't bother to re-read the superblock from the journal. - * XXX, instead we could re-read the superblock and then not - * force doskipclean = 0 - */ } if (debug) printf("clean = %d\n", sblock->fs_clean); diff --git a/sbin/fsck_ffs/wapbl.c b/sbin/fsck_ffs/wapbl.c index 1ee0b0b7a18a..95558f0e5ded 100644 --- a/sbin/fsck_ffs/wapbl.c +++ b/sbin/fsck_ffs/wapbl.c @@ -1,4 +1,4 @@ -/* $NetBSD: wapbl.c,v 1.3 2008/11/10 20:12:13 joerg Exp $ */ +/* $NetBSD: wapbl.c,v 1.4 2009/09/13 14:25:28 bouyer Exp $ */ /*- * Copyright (c) 2005,2008 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ #define WAPBL_INTERNAL #include -__KERNEL_RCSID(0, "$NetBSD: wapbl.c,v 1.3 2008/11/10 20:12:13 joerg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: wapbl.c,v 1.4 2009/09/13 14:25:28 bouyer Exp $"); #include #include @@ -90,60 +90,8 @@ struct wapbl_replay *wapbl_replay; void replay_wapbl(void) { - uint64_t addr, count, blksize; int error; - if (debug) - wapbl_debug_print = WAPBL_PRINT_ERROR | WAPBL_PRINT_REPLAY; - if (debug > 1) - wapbl_debug_print |= WAPBL_PRINT_IO; - - if (sblock->fs_journal_version != UFS_WAPBL_VERSION) { - pfatal("INVALID JOURNAL VERSION %d", - sblock->fs_journal_version); - if (reply("CONTINUE") == 0) { - exit(FSCK_EXIT_CHECK_FAILED); - } - return; - } - - switch (sblock->fs_journal_location) { - case UFS_WAPBL_JOURNALLOC_NONE: - pfatal("INVALID JOURNAL LOCATION 'NONE'"); - if (reply("CONTINUE") == 0) { - exit(FSCK_EXIT_CHECK_FAILED); - } - return; - - case UFS_WAPBL_JOURNALLOC_END_PARTITION: - addr = sblock->fs_journallocs[UFS_WAPBL_EPART_ADDR]; - count = sblock->fs_journallocs[UFS_WAPBL_EPART_COUNT]; - blksize = sblock->fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; - break; - - case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: - addr = sblock->fs_journallocs[UFS_WAPBL_INFS_ADDR]; - count = sblock->fs_journallocs[UFS_WAPBL_INFS_COUNT]; - blksize = sblock->fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; - break; - - default: - pfatal("INVALID JOURNAL LOCATION %d", - sblock->fs_journal_location); - if (reply("CONTINUE") == 0) { - exit(FSCK_EXIT_CHECK_FAILED); - } - return; - } - - error = wapbl_replay_start(&wapbl_replay, 0, addr, count, blksize); - if (error) { - pfatal("UNABLE TO READ JOURNAL FOR REPLAY"); - if (reply("CONTINUE") == 0) { - exit(FSCK_EXIT_CHECK_FAILED); - } - return; - } if (!nflag) { error = wapbl_replay_write(wapbl_replay, 0); if (error) { @@ -201,3 +149,125 @@ is_journal_inode(ino_t ino) return 0; } + +int +check_wapbl(void) +{ + uint64_t addr = 0, count = 0, blksize = 0; + int error; + int ret = 0; + if (debug) + wapbl_debug_print = WAPBL_PRINT_ERROR | WAPBL_PRINT_REPLAY; + if (debug > 1) + wapbl_debug_print |= WAPBL_PRINT_IO; + + if (sblock->fs_flags & FS_DOWAPBL) { + if (sblock->fs_journal_version != UFS_WAPBL_VERSION) { + pfatal("INVALID JOURNAL VERSION %d", + sblock->fs_journal_version); + if (reply("CONTINUE") == 0) { + exit(FSCK_EXIT_CHECK_FAILED); + } + pwarn("CLEARING EXISTING JOURNAL\n"); + sblock->fs_flags &= ~FS_DOWAPBL; + sbdirty(); + ret = FSCK_EXIT_CHECK_FAILED; + } else { + switch(sblock->fs_journal_location) { + case UFS_WAPBL_JOURNALLOC_END_PARTITION: + addr = + sblock->fs_journallocs[UFS_WAPBL_EPART_ADDR]; + count = + sblock->fs_journallocs[UFS_WAPBL_EPART_COUNT]; + blksize = + sblock->fs_journallocs[UFS_WAPBL_EPART_BLKSZ]; + break; + case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: + addr = + sblock->fs_journallocs[UFS_WAPBL_INFS_ADDR]; + count = + sblock->fs_journallocs[UFS_WAPBL_INFS_COUNT]; + blksize = + sblock->fs_journallocs[UFS_WAPBL_INFS_BLKSZ]; + break; + default: + pfatal("INVALID JOURNAL LOCATION %d", + sblock->fs_journal_location); + if (reply("CONTINUE") == 0) { + exit(FSCK_EXIT_CHECK_FAILED); + } + pwarn("CLEARING EXISTING JOURNAL\n"); + sblock->fs_flags &= ~FS_DOWAPBL; + sblock->fs_journal_location = + UFS_WAPBL_JOURNALLOC_NONE; + sbdirty(); + ret = FSCK_EXIT_CHECK_FAILED; + break; + } + if (sblock->fs_flags & FS_DOWAPBL) { + error = wapbl_replay_start( + &wapbl_replay, 0, addr, count, blksize); + if (error) { + pfatal( + "UNABLE TO READ JOURNAL FOR REPLAY"); + if (reply("CONTINUE") == 0) { + exit(FSCK_EXIT_CHECK_FAILED); + } + pwarn("CLEARING EXISTING JOURNAL\n"); + sblock->fs_flags &= ~FS_DOWAPBL; + sbdirty(); + ret = FSCK_EXIT_CHECK_FAILED; + } + } + } + } + /* + * at this time fs_journal_flags can only be 0, + * UFS_WAPBL_FLAGS_CREATE_LOG or UFS_WAPBL_FLAGS_CLEAR_LOG. + * We can't have both flags at the same time. + */ + switch (sblock->fs_journal_flags) { + case 0: + break; + case UFS_WAPBL_FLAGS_CREATE_LOG: + case UFS_WAPBL_FLAGS_CLEAR_LOG: + switch(sblock->fs_journal_location) { + case UFS_WAPBL_JOURNALLOC_END_PARTITION: + case UFS_WAPBL_JOURNALLOC_IN_FILESYSTEM: + break; + case UFS_WAPBL_JOURNALLOC_NONE: + if (sblock->fs_journal_flags == + UFS_WAPBL_FLAGS_CLEAR_LOG) + break; + /* FALLTHROUGH */ + default: + pfatal("INVALID JOURNAL LOCATION %d", + sblock->fs_journal_location); + if (reply("CONTINUE") == 0) { + exit(FSCK_EXIT_CHECK_FAILED); + } + pwarn("CLEARING JOURNAL FLAGS\n"); + sblock->fs_journal_flags = 0; + sblock->fs_journal_location = + UFS_WAPBL_JOURNALLOC_NONE; + sbdirty(); + ret = FSCK_EXIT_CHECK_FAILED; + break; + } + break; + default: + pfatal("INVALID JOURNAL FLAGS %d", + sblock->fs_journal_flags); + if (reply("CONTINUE") == 0) { + exit(FSCK_EXIT_CHECK_FAILED); + } + pwarn("CLEARING JOURNAL FLAGS\n"); + sblock->fs_journal_flags = 0; + sblock->fs_journal_location = + UFS_WAPBL_JOURNALLOC_NONE; + sbdirty(); + ret = FSCK_EXIT_CHECK_FAILED; + break; + } + return ret; +}