tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

snapshot support for fsck_ffs



Hi,
the attached diff adds snapshot support for fsck_ffs (via -x or -X options,
like dump(8)). The point is to have fsck_ffs optionally take a snapshot
of a R/W mounted filesystem before checking it, to avoid errors related
to the filesystem being live. Obviously this will only work with -n, the
code enforce this.

The goal is to be able to use
fsck -fn -Tffs:-X 
in /etc/daily (via a daily.conf variable of course). The daily fsck check is
often highly noisy on busy filesystems (like, say, /var/spool/mqueue on
a busy sendmail server), making it hard to differenciate between trancient
errors, and errors related to real filesystem corruption. When a snapshot
is used, a filesystem should always show clean, unless it's really corrupted.

I've tested this by running 'fsck -fn -Tffs:-X' in a loop on a system,
while at the same time doing a tar xf of the NetBSD src tree followed
by rm -rf src; also in a loop. The fsck -fn -Tffs:-X always did come
clean; while any run of fsck -fn done in parallel did show lots of
errors.

comments ?

-- 
Manuel Bouyer <bouyer%antioche.eu.org@localhost>
     NetBSD: 26 ans d'experience feront toujours la difference
--
Index: sbin/fsck_ffs/Makefile
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/Makefile,v
retrieving revision 1.36
diff -u -r1.36 Makefile
--- sbin/fsck_ffs/Makefile      31 Jul 2008 05:38:04 -0000      1.36
+++ sbin/fsck_ffs/Makefile      24 Aug 2008 15:44:00 -0000
@@ -7,17 +7,18 @@
 MAN=   fsck_ffs.8
 SRCS=  dir.c inode.c main.c pass1.c pass1b.c pass2.c pass3.c pass4.c \
        pass5.c fsutil.c setup.c utilities.c ffs_bswap.c ffs_subr.c \
-       ffs_tables.c ffs_appleufs.c partutil.c
+       ffs_tables.c ffs_appleufs.c partutil.c snapshot.c
 
 FSCK=  ${NETBSDSRCDIR}/sbin/fsck
-CPPFLAGS+=-I${FSCK}
+DUMP=  ${NETBSDSRCDIR}/sbin/dump
+CPPFLAGS+=-I${FSCK} -I${DUMP}
 .ifndef  SMALLPROG
 CPPFLAGS+=-DPROGRESS
 .endif
 SRCS+= progress.c
 .PATH: ${FSCK}
 
-.PATH: ${NETBSDSRCDIR}/sys/ufs/ffs ${FSCK}
+.PATH: ${NETBSDSRCDIR}/sys/ufs/ffs ${FSCK} ${DUMP}
 
 SRCS+= vfs_wapbl.c wapbl.c
 .PATH: ${NETBSDSRCDIR}/sys/kern
Index: sbin/fsck_ffs/extern.h
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/extern.h,v
retrieving revision 1.23
diff -u -r1.23 extern.h
--- sbin/fsck_ffs/extern.h      31 Jul 2008 05:38:04 -0000      1.23
+++ sbin/fsck_ffs/extern.h      24 Aug 2008 15:44:00 -0000
@@ -79,7 +79,7 @@
 void           propagate(ino_t);
 int            reply(const char *);
 void           setinodebuf(ino_t);
-int            setup(const char *);
+int            setup(const char *, const char *);
 void           voidquit(int);
 
 void           replay_wapbl(void);
Index: sbin/fsck_ffs/fsck_ffs.8
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/fsck_ffs.8,v
retrieving revision 1.41
diff -u -r1.41 fsck_ffs.8
--- sbin/fsck_ffs/fsck_ffs.8    31 Jul 2008 05:38:04 -0000      1.41
+++ sbin/fsck_ffs/fsck_ffs.8    24 Aug 2008 15:44:01 -0000
@@ -29,7 +29,7 @@
 .\"
 .\"    @(#)fsck.8      8.3 (Berkeley) 11/29/94
 .\"
-.Dd January 13, 2004
+.Dd August 24, 2008
 .Dt FSCK_FFS 8
 .Os
 .Sh NAME
@@ -37,11 +37,12 @@
 .Nd Fast File System consistency check and interactive repair
 .Sh SYNOPSIS
 .Nm
-.Op Fl adFfPpq
+.Op Fl adFfPpqX
 .Op Fl B Ar byteorder
 .Op Fl b Ar block
 .Op Fl c Ar level
 .Op Fl m Ar mode
+.Op Fl x Ar snap-backup
 .Op Fl y | n
 .Ar filesystem ...
 .Sh DESCRIPTION
@@ -248,8 +249,18 @@
 Specify
 .Dq preen
 mode, described above.
-.It Fl q
-Quiet mode, do not output any messages for clean filesystems.
+.It Fl x Ar snap-backup
+Use a snapshot with
+.Ar snap-backup
+as backup to check a read-write mounted filesystem. Must be used with
+.Fl n .
+See
+.Xr fss 4
+for more details.
+.It Fl X
+Similar to
+.Fl x
+but uses a file system internal snapshot on the file system to be checked.
 .It Fl y
 Assume a yes response to all questions asked by
 .Nm ;
@@ -321,6 +332,7 @@
 .%T "Fsck \- The UNIX File System Check Program"
 .Re
 .Sh SEE ALSO
+.Xr fss 4 ,
 .Xr fs 5 ,
 .Xr fstab 5 ,
 .Xr fsck 8 ,
Index: sbin/fsck_ffs/main.c
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/main.c,v
retrieving revision 1.68
diff -u -r1.68 main.c
--- sbin/fsck_ffs/main.c        20 Jul 2008 01:20:22 -0000      1.68
+++ sbin/fsck_ffs/main.c        24 Aug 2008 15:44:01 -0000
@@ -67,13 +67,15 @@
 #include "extern.h"
 #include "fsutil.h"
 #include "exitvalues.h"
+#include "snapshot.h"
 
 int    progress = 0;
 int    returntosingle = 0;
 
 static int     argtoi(int, const char *, const char *, int);
-static int     checkfilesys(const char *, char *, long, int);
+static int     checkfilesys(const char *, const char *, int);
 static void    usage(void);
+static char    *get_snap_device(char *);
 
 int
 main(int argc, char *argv[])
@@ -81,6 +83,8 @@
        struct rlimit r;
        int ch;
        int ret = FSCK_EXIT_OK;
+       char *snap_backup = NULL;
+       int snap_internal = 0;
 
        if (getrlimit(RLIMIT_DATA, &r) == 0) {
                r.rlim_cur = r.rlim_max;
@@ -92,7 +96,7 @@
        forceimage = 0;
        endian = 0;
        isappleufs = 0;
-       while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqy")) != -1) {
+       while ((ch = getopt(argc, argv, "aB:b:c:dFfm:npPqyx:X")) != -1) {
                switch (ch) {
                case 'a':
                        isappleufs = 1;
@@ -163,12 +167,27 @@
                        yflag++;
                        nflag = 0;
                        break;
+               case 'x':
+                       snap_backup = optarg;
+                       break;
+               case 'X':
+                       snap_internal = 1;
+                       break;
 
                default:
                        usage();
                }
        }
 
+       if (snap_backup || snap_internal) {
+               if (!nflag || yflag) {
+                       warnx("Cannot use -x or -X without -n\n");
+                       snap_backup = NULL;
+                       snap_internal = 0;
+               }
+       }
+                       
+
        argc -= optind;
        argv += optind;
 
@@ -191,15 +210,38 @@
        signal(SIGINFO, infohandler);
 
        while (argc-- > 0) {
-               const char *path = blockcheck(*argv);
+               int nret;
+               char *path = strdup(blockcheck(*argv));
 
                if (path == NULL)
                        pfatal("Can't check %s\n", *argv);
-               else {
-                       int nret = checkfilesys(blockcheck(*argv), 0, 0L, 0);
+               
+               if (snap_backup || snap_internal) {
+                       char *mpt;
+                       char *snap_dev;
+                       int snapfd;
+
+                       mpt = get_snap_device(*argv);
+                       if (mpt == NULL)
+                               goto next;
+                       snapfd = snap_open(mpt, snap_backup, NULL, &snap_dev);
+                       if (snapfd < 0) {
+                               warn("can't take snapshot of %s", mpt);
+                               free(mpt);
+                               goto next;
+                       }
+                       nret = checkfilesys(blockcheck(snap_dev), path, 0);
+                       if (ret < nret)
+                               ret = nret;
+                       free(mpt);
+                       close(snapfd);
+               } else {
+                       nret = checkfilesys(path, path, 0);
                        if (ret < nret)
                                ret = nret;
-               }                       
+               }
+next:
+               free(path);
                argv++;
        }
 
@@ -224,7 +266,7 @@
  */
 /* ARGSUSED */
 static int
-checkfilesys(const char *filesys, char *mntpt, long auxdata, int child)
+checkfilesys(const char *filesys, const char *origfs, int child)
 {
        daddr_t n_ffree, n_bfree;
        struct dups *dp;
@@ -255,7 +297,7 @@
        setcdevname(filesys, preen);
        if (debug && preen)
                pwarn("starting\n");
-       switch (setup(filesys)) {
+       switch (setup(filesys, origfs)) {
        case 0:
                if (preen)
                        pfatal("CAN'T CHECK FILE SYSTEM.");
@@ -458,9 +500,62 @@
 {
 
        (void) fprintf(stderr,
-           "usage: %s [-adFfnPpqy] [-B be|le] [-b block] [-c level] [-m mode]"
-           " filesystem ...\n",
+           "usage: %s [-adFfnPpqyX] [-B be|le] [-b block] [-c level] [-m mode]"
+           " [-x snap-backup] filesystem ...\n",
            getprogname());
        exit(FSCK_EXIT_USAGE);
 }
 
+static 
+char *get_snap_device(char *file)
+{
+       char *mountpoint = NULL;
+       struct statvfs *mntbuf, *fs, fsbuf;
+       struct stat sb;
+
+       /* find the mount point */
+       if (lstat(file, &sb) == -1) {
+               warn("can't stat %s", file);
+               return NULL;
+       }
+       if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) {
+               int mntbufc, i;
+               if ((mntbufc = getmntinfo(&mntbuf, MNT_NOWAIT)) == 0)
+                       pfatal("can't get mount list: %s\n", strerror(errno));
+               for (fs = mntbuf, i = 0;
+                    i < mntbufc; i++, fs++) {
+                       if (strcmp(fs->f_fstypename, "ufs") != 0 &&
+                           strcmp(fs->f_fstypename, "ffs") != 0)
+                               continue;
+                       if (fs->f_flag & ST_RDONLY) {
+                               warnx("Cannot use -x or -X "
+                                    "on read-only filesystem");
+                               free(mntbuf);
+                               return NULL;
+                       }
+                       if (strcmp(fs->f_mntfromname, unrawname(file)) == 0) {
+                               mountpoint = strdup(fs->f_mntonname);
+                               free(mntbuf);
+                               return mountpoint;
+                       }
+               }
+               warnx("Cannot use -x or -X on unmounted device");
+               free(mntbuf);
+               return NULL;
+       }
+       if (S_ISDIR(sb.st_mode)) {
+               if (statvfs(file, &fsbuf) == -1)
+                       pfatal("can't statvfs %s: %s\n", file, strerror(errno));
+               if (strcmp(fsbuf.f_mntonname, file))
+                       pfatal("%s is not a mount point\n", file);
+               if (fsbuf.f_flag & ST_RDONLY) {
+                       warnx("Cannot use -x or -X "
+                            "on read-only filesystem");
+                       return NULL;
+               }
+               mountpoint = strdup(file);
+               return mountpoint;
+       }
+       pfatal("%s is not a mount point\n", file);
+       return NULL;
+}
Index: sbin/fsck_ffs/setup.c
===================================================================
RCS file: /cvsroot/src/sbin/fsck_ffs/setup.c,v
retrieving revision 1.83
diff -u -r1.83 setup.c
--- sbin/fsck_ffs/setup.c       31 Jul 2008 05:38:04 -0000      1.83
+++ sbin/fsck_ffs/setup.c       24 Aug 2008 15:44:01 -0000
@@ -79,7 +79,7 @@
  * is already clean (preen mode only).
  */
 int
-setup(const char *dev)
+setup(const char *dev, const char *origdev)
 {
        long cg, size, asked, i, j;
        long bmapsize;
@@ -91,6 +91,7 @@
        int doskipclean;
        u_int64_t maxfilesize;
        struct csum *ccsp;
+       int fd;
 
        havesb = 0;
        fswritefd = -1;
@@ -128,7 +129,20 @@
        if (sblk.b_un.b_buf == NULL || asblk.b_un.b_buf == NULL ||
                sblock == NULL || altsblock == NULL)
                errexit("Cannot allocate space for superblock");
-       if (!forceimage && getdiskinfo(dev, fsreadfd, NULL, &geo, &dkw) != -1)
+       if (strcmp(dev, origdev) && !forceimage) {
+               /*
+                * dev isn't the original fs (for example it's a snapshot)
+                * do getdiskinfo on the original device
+                */
+                fd = open(origdev, O_RDONLY);
+                if (fd < 0) {
+                       warn("Can't open %s", origdev);
+                       return (0);
+               }
+       } else {
+               fd = fsreadfd;
+       }
+       if (!forceimage && getdiskinfo(origdev, fd, NULL, &geo, &dkw) != -1)
                dev_bsize = secsize = geo.dg_secsize;
        else
                dev_bsize = secsize = DEV_BSIZE;
Index: sbin/fsdb/fsdb.c
===================================================================
RCS file: /cvsroot/src/sbin/fsdb/fsdb.c,v
retrieving revision 1.37
diff -u -r1.37 fsdb.c
--- sbin/fsdb/fsdb.c    28 Apr 2008 20:23:08 -0000      1.37
+++ sbin/fsdb/fsdb.c    24 Aug 2008 15:44:01 -0000
@@ -130,7 +130,7 @@
        if (fsys == NULL)
                usage();
        endian = 0;
-       if (setup(fsys) <= 0)
+       if (setup(fsys, fsys) <= 0)
                errx(1, "cannot set up file system `%s'", fsys);
        printf("Editing file system `%s'\nLast Mounted on %s\n", fsys,
            sblock->fs_fsmnt);


Home | Main Index | Thread Index | Old Index