]> err.no Git - util-linux/commitdiff
fsck: improve whole-disk check, detect stacked devices
authorKarel Zak <kzak@redhat.com>
Thu, 19 Aug 2010 13:33:45 +0000 (15:33 +0200)
committerKarel Zak <kzak@redhat.com>
Thu, 19 Aug 2010 13:33:45 +0000 (15:33 +0200)
The current heuristic for conversion from partition to whole-disk
is based on device names. It's pretty poor. This patch replaces this
code with blkid_devno_to_wholedisk(). This solution is based on
/sys FS and it works for arbitrary partitioned devices.

The another problem is the way how fsck determines stacked devices.
The current code checks device name for "md" prefix only. It does not
care about DM, dm-ccypt, and so on. This patch uses
/sys/block/.../slaves/, but it does not fully resolves dependencies
between all devices. The method is simple -- fsck does not check
stacked devices in parallel with any other device.

Signed-off-by: Karel Zak <kzak@redhat.com>
Makefile.am
fsck/Makefile.am
fsck/base_device.c [deleted file]
fsck/fsck.8
fsck/fsck.c
fsck/fsck.h

index c489665f5b532c8ee1e77cba57897b76a48cfaed..9febbfd9b76e2df909cadc77916b4081a17ae579 100644 (file)
@@ -44,8 +44,10 @@ SUBDIRS += mount
 endif
 
 if BUILD_FSCK
+if BUILD_LIBBLKID
 SUBDIRS += fsck
 endif
+endif
 
 ACLOCAL_AMFLAGS = -I m4
 
index b0949e2549bbc8cba45eab59f6de9f55a749dd30..22566d19f602e255b5e471a66dc0e9ccfbd11399 100644 (file)
@@ -3,7 +3,7 @@ include $(top_srcdir)/config/include-Makefile.am
 sbin_PROGRAMS = fsck
 dist_man_MANS = fsck.8
 
-fsck_SOURCES = base_device.c fsck.c fsck.h $(top_srcdir)/lib/ismounted.c \
+fsck_SOURCES = fsck.c fsck.h $(top_srcdir)/lib/ismounted.c \
               $(top_srcdir)/lib/fsprobe.c $(top_srcdir)/lib/canonicalize.c
 fsck_LDADD =
 fsck_CFLAGS = $(AM_CFLAGS)
diff --git a/fsck/base_device.c b/fsck/base_device.c
deleted file mode 100644 (file)
index aad2ba0..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * base_device.c
- *
- * Return the "base device" given a particular device; this is used to
- * assure that we only fsck one partition on a particular drive at any
- * one time.  Otherwise, the disk heads will be seeking all over the
- * place.  If the base device can not be determined, return NULL.
- *
- * The base_device() function returns an allocated string which must
- * be freed.
- *
- * Written by Theodore Ts'o, <tytso@mit.edu>
- *
- * Copyright (C) 2000 Theodore Ts'o.
- *
- * %Begin-Header%
- * This file may be redistributed under the terms of the GNU Public
- * License.
- * %End-Header%
- */
-#include <stdio.h>
-#if HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#if HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#include <ctype.h>
-#include <string.h>
-
-#include "fsck.h"
-
-/*
- * Required for the uber-silly devfs /dev/ide/host1/bus2/target3/lun3
- * pathames.
- */
-static const char *devfs_hier[] = {
-       "host", "bus", "target", "lun", 0
-};
-
-char *base_device(const char *device)
-{
-       char *str, *cp;
-       const char **hier, *disk;
-       int len;
-
-       str = malloc(strlen(device)+1);
-       if (!str)
-               return NULL;
-       strcpy(str, device);
-       cp = str;
-
-       /* Skip over /dev/; if it's not present, give up. */
-       if (strncmp(cp, "/dev/", 5) != 0)
-               goto errout;
-       cp += 5;
-
-       /* Skip over /dev/dsk/... */
-       if (strncmp(cp, "dsk/", 4) == 0)
-               cp += 4;
-
-       /*
-        * For md devices, we treat them all as if they were all
-        * on one disk, since we don't know how to parallelize them.
-        */
-       if (cp[0] == 'm' && cp[1] == 'd') {
-               *(cp+2) = 0;
-               return str;
-       }
-
-       /* Handle DAC 960 devices */
-       if (strncmp(cp, "rd/", 3) == 0) {
-               cp += 3;
-               if (cp[0] != 'c' || cp[2] != 'd' ||
-                   !isdigit(cp[1]) || !isdigit(cp[3]))
-                       goto errout;
-               *(cp+4) = 0;
-               return str;
-       }
-
-       /* Now let's handle /dev/hd* and /dev/sd* devices.... */
-       if ((cp[0] == 'h' || cp[0] == 's') && (cp[1] == 'd')) {
-               cp += 2;
-               /* If there's a single number after /dev/hd, skip it */
-               if (isdigit(*cp))
-                       cp++;
-               /* What follows must be an alpha char, or give up */
-               if (!isalpha(*cp))
-                       goto errout;
-               *(cp + 1) = 0;
-               return str;
-       }
-
-       /* Now let's handle devfs (ugh) names */
-       len = 0;
-       if (strncmp(cp, "ide/", 4) == 0)
-               len = 4;
-       if (strncmp(cp, "scsi/", 5) == 0)
-               len = 5;
-       if (len) {
-               cp += len;
-               /*
-                * Now we proceed down the expected devfs hierarchy.
-                * i.e., .../host1/bus2/target3/lun4/...
-                * If we don't find the expected token, followed by
-                * some number of digits at each level, abort.
-                */
-               for (hier = devfs_hier; *hier; hier++) {
-                       len = strlen(*hier);
-                       if (strncmp(cp, *hier, len) != 0)
-                               goto errout;
-                       cp += len;
-                       while (*cp != '/' && *cp != 0) {
-                               if (!isdigit(*cp))
-                                       goto errout;
-                               cp++;
-                       }
-                       cp++;
-               }
-               *(cp - 1) = 0;
-               return str;
-       }
-
-       /* Now handle devfs /dev/disc or /dev/disk names */
-       disk = 0;
-       if (strncmp(cp, "discs/", 6) == 0)
-               disk = "disc";
-       else if (strncmp(cp, "disks/", 6) == 0)
-               disk = "disk";
-       if (disk) {
-               cp += 6;
-               if (strncmp(cp, disk, 4) != 0)
-                       goto errout;
-               cp += 4;
-               while (*cp != '/' && *cp != 0) {
-                       if (!isdigit(*cp))
-                               goto errout;
-                       cp++;
-               }
-               *cp = 0;
-               return str;
-       }
-
-errout:
-       free(str);
-       return NULL;
-}
-
-#ifdef DEBUG
-int main(int argc, char** argv)
-{
-       const char *base;
-       char  buf[256], *cp;
-
-       while (1) {
-               if (fgets(buf, sizeof(buf), stdin) == NULL)
-                       break;
-               cp = strchr(buf, '\n');
-               if (cp)
-                       *cp = 0;
-               cp = strchr(buf, '\t');
-               if (cp)
-                       *cp = 0;
-               base = base_device(buf);
-               printf("%s\t%s\n", buf, base ? base : "NONE");
-       }
-       exit(0);
-}
-#endif
index 027fb3fed8d859b0fcc9244f3eaac8f1d9906cee..9cf80785df97fb0188bf0fab42028431fa4ee3e7 100644 (file)
@@ -200,6 +200,11 @@ If there are multiple filesystems with the same pass number,
 fsck will attempt to check them in parallel, although it will avoid running
 multiple filesystem checks on the same physical disk.
 .sp
+.B fsck
+does not check stacked devices (RAIDs, dm-crypt, ...) in parallel with any other
+device. See below for FSCK_FORCE_ALL_PARALLEL setting. The /sys filesystem is
+used to detemine dependencies between devices.
+.sp
 Hence, a very common configuration in
 .I /etc/fstab
 files is to set the root filesystem to have a
@@ -366,10 +371,10 @@ program's behavior is affected by the following environment variables:
 .B FSCK_FORCE_ALL_PARALLEL
 If this environment variable is set,
 .B fsck
-will attempt to run all of the specified filesystems in parallel,
-regardless of whether the filesystems appear to be on the same
-device.  (This is useful for RAID systems or high-end storage systems
-such as those sold by companies such as IBM or EMC.)
+will attempt to run all of the specified filesystems in parallel, regardless of
+whether the filesystems appear to be on the same device.  (This is useful for
+RAID systems or high-end storage systems such as those sold by companies such
+as IBM or EMC.) Note that the fs_passno value is still used.
 .TP
 .B FSCK_MAX_INST
 This environment variable will limit the maximum number of file system
index 7577eff13978fe2c99023801c0476bae77b8c2d4..e7526f9e6d5855af27ecb5df5cfde4824a8ed4db 100644 (file)
@@ -42,6 +42,8 @@
 #include <errno.h>
 #include <malloc.h>
 #include <signal.h>
+#include <dirent.h>
+#include <blkid.h>
 
 #include "fsprobe.h"
 
@@ -217,8 +219,6 @@ static void parse_escape(char *word)
 static void free_instance(struct fsck_instance *i)
 {
        free(i->prog);
-       free(i->device);
-       free(i->base_device);
        free(i);
        return;
 }
@@ -240,6 +240,8 @@ static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
        fs->passno = passno;
        fs->flags = 0;
        fs->next = NULL;
+       fs->disk = 0;
+       fs->stacked = 0;
 
        if (!filesys_info)
                filesys_info = fs;
@@ -414,8 +416,7 @@ static int progress_active(NOARGS)
  * Execute a particular fsck program, and link it into the list of
  * child processes we are waiting for.
  */
-static int execute(const char *type, const char *device, const char *mntpt,
-                  int interactive)
+static int execute(const char *type, struct fs_info *fs, int interactive)
 {
        char *s, *argv[80], prog[80];
        int  argc, i;
@@ -452,7 +453,7 @@ static int execute(const char *type, const char *device, const char *mntpt,
                }
        }
 
-       argv[argc++] = string_copy(device);
+       argv[argc++] = string_copy(fs->device);
        argv[argc] = 0;
 
        s = find_fsck(prog);
@@ -464,7 +465,7 @@ static int execute(const char *type, const char *device, const char *mntpt,
 
        if (verbose || noexecute) {
                printf("[%s (%d) -- %s] ", s, num_running,
-                      mntpt ? mntpt : device);
+                      fs->mountpt ? fs->mountpt : fs->device);
                for (i=0; i < argc; i++)
                        printf("%s ", argv[i]);
                printf("\n");
@@ -492,9 +493,8 @@ static int execute(const char *type, const char *device, const char *mntpt,
        inst->pid = pid;
        inst->prog = string_copy(prog);
        inst->type = string_copy(type);
-       inst->device = string_copy(device);
-       inst->base_device = base_device(device);
        inst->start_time = time(0);
+       inst->fs = fs;
        inst->next = NULL;
 
        /*
@@ -597,12 +597,12 @@ static struct fsck_instance *wait_one(int flags)
                } else {
                        printf(_("Warning... %s for device %s exited "
                               "with signal %d.\n"),
-                              inst->prog, inst->device, sig);
+                              inst->prog, inst->fs->device, sig);
                        status = EXIT_ERROR;
                }
        } else {
                printf(_("%s %s: status is %x, should never happen.\n"),
-                      inst->prog, inst->device, status);
+                      inst->prog, inst->fs->device, status);
                status = EXIT_ERROR;
        }
        inst->exit_status = status;
@@ -641,7 +641,7 @@ ret_inst:
                instance_list = inst->next;
        if (verbose > 1)
                printf(_("Finished with %s (exit status %d)\n"),
-                      inst->device, inst->exit_status);
+                      inst->fs->device, inst->exit_status);
        num_running--;
        return inst;
 }
@@ -698,7 +698,7 @@ static void fsck_device(struct fs_info *fs, int interactive)
                type = DEFAULT_FSTYPE;
 
        num_running++;
-       retval = execute(type, fs->device, fs->mountpt, interactive);
+       retval = execute(type, fs, interactive);
        if (retval) {
                fprintf(stderr, _("%s: Error %d while executing fsck.%s "
                        "for %s\n"), progname, retval, type, fs->device);
@@ -924,40 +924,75 @@ static int ignore(struct fs_info *fs)
        return 0;
 }
 
+static int count_slaves(dev_t disk)
+{
+       DIR *dir;
+       struct dirent *dp;
+       char dirname[PATH_MAX];
+       int count = 0;
+
+       snprintf(dirname, sizeof(dirname),
+                       "/sys/dev/block/%u:%u/slaves/",
+                       major(disk), minor(disk));
+
+       if (!(dir = opendir(dirname)))
+               return -1;
+
+       while ((dp = readdir(dir)) != 0) {
+#ifdef _DIRENT_HAVE_D_TYPE
+               if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK)
+                       continue;
+#endif
+               if (dp->d_name[0] == '.' &&
+                   ((dp->d_name[1] == 0) ||
+                    ((dp->d_name[1] == '.') && (dp->d_name[2] == 0))))
+                       continue;
+
+               count++;
+       }
+       closedir(dir);
+       return count;
+}
+
 /*
  * Returns TRUE if a partition on the same disk is already being
  * checked.
  */
-static int device_already_active(char *device)
+static int disk_already_active(struct fs_info *fs)
 {
        struct fsck_instance *inst;
-       char *base;
 
        if (force_all_parallel)
                return 0;
 
-#ifdef BASE_MD
-       /* Don't check a soft raid disk with any other disk */
-       if (instance_list &&
-           (!strncmp(instance_list->device, BASE_MD, sizeof(BASE_MD)-1) ||
-            !strncmp(device, BASE_MD, sizeof(BASE_MD)-1)))
+       if (instance_list && instance_list->fs->stacked)
+               /* any instance for a stacked device is already running */
                return 1;
-#endif
 
-       base = base_device(device);
+       if (!fs->disk) {
+               struct stat st;
+               dev_t disk;
+
+               if (!stat(fs->device, &st) &&
+                   !blkid_devno_to_wholedisk(st.st_rdev, NULL, 0, &disk)) {
+                       fs->disk = disk;
+                       fs->stacked = count_slaves(disk);
+               }
+       }
+
        /*
         * If we don't know the base device, assume that the device is
         * already active if there are any fsck instances running.
+        *
+        * Don't check a stacked device with any other disk too.
         */
-       if (!base)
+       if (!fs->disk || fs->stacked)
                return (instance_list != 0);
+
        for (inst = instance_list; inst; inst = inst->next) {
-               if (!inst->base_device || !strcmp(base, inst->base_device)) {
-                       free(base);
+               if (!inst->fs->disk || fs->disk == inst->fs->disk)
                        return 1;
-               }
        }
-       free(base);
        return 0;
 }
 
@@ -1038,7 +1073,7 @@ static int check_all(NOARGS)
                         * already been spawned, then we need to defer
                         * this to another pass.
                         */
-                       if (device_already_active(fs->device)) {
+                       if (disk_already_active(fs)) {
                                pass_done = 0;
                                continue;
                        }
index 45b78446c867a4ffcf0c34f5de18945f72460e66..d212622f1dbd47cfc77bbde168814c763748fe6e 100644 (file)
@@ -44,6 +44,8 @@ struct fs_info {
        int   freq;
        int   passno;
        int   flags;
+       dev_t disk;
+       int   stacked;
        struct fs_info *next;
 };
 
@@ -60,8 +62,7 @@ struct fsck_instance {
        time_t  start_time;
        char *  prog;
        char *  type;
-       char *  device;
-       char *  base_device;
+       struct fs_info *fs;
        struct fsck_instance *next;
 };