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.
This commit is contained in:
bouyer 2009-09-13 14:25:28 +00:00
parent 32992733fa
commit cab6cd67bc
3 changed files with 156 additions and 74 deletions

View File

@ -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);

View File

@ -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,25 +173,36 @@ setup(const char *dev, const char *origdev)
doskipclean = 0;
pwarn("USING ALTERNATE SUPERBLOCK AT %d\n", bflag);
}
/* 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 && skipclean) {
if (preen) {
if (!quiet)
pwarn("file system is journaled; not checking\n");
pwarn("file system is journaled; "
"not checking\n");
return (-1);
}
if (!quiet)
pwarn("** File system is journaled; replaying journal\n");
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
/* 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);
if (doswap)

View File

@ -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 <sys/cdefs.h>
__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 <sys/stat.h>
#include <sys/time.h>
@ -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;
}