2007-09-25 15:37:37

by Christoph Hellwig

[permalink] [raw]
Subject: [PATCH, RFC] add fsck to util-linux

This adds fsck from latest e2fsprogs git to util-linux. There are only
tiny changes to integrate it into the build system and nls setup of
util-linux and fixing up the trailing whitespaces quilt is complaining
about. I've not yet converted it to the fsprobe helpers as the discussion
on those libs is still ongoing and I haven't read up on the fsprobe library
either.


Signed-off-by: Christoph Hellwig <[email protected]>

Index: util-linux-ng/Makefile.am
===================================================================
--- util-linux-ng.orig/Makefile.am 2007-09-25 17:27:27.000000000 +0200
+++ util-linux-ng/Makefile.am 2007-09-25 17:27:30.000000000 +0200
@@ -4,6 +4,7 @@ SUBDIRS = \
include \
disk-utils \
fdisk \
+ fsck \
getopt \
hwclock \
login-utils \
Index: util-linux-ng/configure.ac
===================================================================
--- util-linux-ng.orig/configure.ac 2007-09-25 17:27:27.000000000 +0200
+++ util-linux-ng/configure.ac 2007-09-25 17:27:30.000000000 +0200
@@ -564,6 +564,7 @@ AC_CONFIG_FILES([
Makefile
disk-utils/Makefile
fdisk/Makefile
+fsck/Makefile
getopt/Makefile
hwclock/Makefile
include/Makefile
Index: util-linux-ng/fsck/Makefile.am
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ util-linux-ng/fsck/Makefile.am 2007-09-25 17:34:59.000000000 +0200
@@ -0,0 +1,6 @@
+include $(top_srcdir)/config/include-Makefile.am
+
+usrsbinexec_PROGRAMS = fsck
+fsck_SOURCES = fsck.c fsck.h base_device.c
+fsck_LDADD = -lblkid
+man_MANS = fsck.8
Index: util-linux-ng/fsck/base_device.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ util-linux-ng/fsck/base_device.c 2007-09-25 17:29:27.000000000 +0200
@@ -0,0 +1,169 @@
+/*
+ * 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, <[email protected]>
+ *
+ * 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: util-linux-ng/fsck/fsck.8
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ util-linux-ng/fsck/fsck.8 2007-09-25 17:28:44.000000000 +0200
@@ -0,0 +1,410 @@
+.\" -*- nroff -*-
+.\" Copyright 1993, 1994, 1995 by Theodore Ts'o. All Rights Reserved.
+.\" This file may be copied under the terms of the GNU Public License.
+.\"
+.TH FSCK 8 "Sep 2007" "Version 1.24"
+.SH NAME
+fsck \- check and repair a Linux file system
+.SH SYNOPSIS
+.B fsck
+[
+.B \-sAVRTNP
+]
+[
+.B \-C
+[
+.I fd
+]
+]
+[
+.B \-t
+.I fstype
+]
+.I [filesys ... ]
+[\-\-] [
+.B fs-specific-options
+]
+.SH DESCRIPTION
+.B fsck
+is used to check and optionally repair one or more Linux file systems.
+.I filesys
+can be a device name (e.g.
+.IR /dev/hdc1 ", " /dev/sdb2 ),
+a mount point (e.g.
+.IR / ", " /usr ", " /home ),
+or an ext2 label or UUID specifier (e.g.
+UUID=8868abf6-88c5-4a83-98b8-bfc24057f7bd or LABEL=root).
+Normally, the
+.B fsck
+program will try to handle filesystems on different physical disk drives
+in parallel to reduce the total amount of time needed to check all of the
+filesystems.
+.PP
+If no filesystems are specified on the command line, and the
+.B \-A
+option is not specified,
+.B fsck
+will default to checking filesystems in
+.B /etc/fstab
+serially. This is equivalent to the
+.B \-As
+options.
+.PP
+The exit code returned by
+.B fsck
+is the sum of the following conditions:
+.br
+\ 0\ \-\ No errors
+.br
+\ 1\ \-\ File system errors corrected
+.br
+\ 2\ \-\ System should be rebooted
+.br
+\ 4\ \-\ File system errors left uncorrected
+.br
+\ 8\ \-\ Operational error
+.br
+\ 16\ \-\ Usage or syntax error
+.br
+\ 32\ \-\ Fsck canceled by user request
+.br
+\ 128\ \-\ Shared library error
+.br
+The exit code returned when multiple file systems are checked
+is the bit-wise OR of the exit codes for each
+file system that is checked.
+.PP
+In actuality,
+.B fsck
+is simply a front-end for the various file system checkers
+(\fBfsck\fR.\fIfstype\fR) available under Linux. The file
+system-specific checker is searched for in
+.I /sbin
+first, then in
+.I /etc/fs
+and
+.IR /etc ,
+and finally in the directories listed in the PATH environment
+variable. Please see the file system-specific checker manual pages for
+further details.
+.SH OPTIONS
+.TP
+.B \-s
+Serialize
+.B fsck
+operations. This is a good idea if you are checking multiple
+filesystems and the checkers are in an interactive mode. (Note:
+.BR e2fsck (8)
+runs in an interactive mode by default. To make
+.BR e2fsck (8)
+run in a non-interactive mode, you must either specify the
+.B \-p
+or
+.B \-a
+option, if you wish for errors to be corrected automatically, or the
+.B \-n
+option if you do not.)
+.TP
+.BI \-t " fslist"
+Specifies the type(s) of file system to be checked. When the
+.B \-A
+flag is specified, only filesystems that match
+.I fslist
+are checked. The
+.I fslist
+parameter is a comma-separated list of filesystems and options
+specifiers. All of the filesystems in this comma-separated list may be
+prefixed by a negation operator
+.RB ' no '
+or
+.RB ' ! ',
+which requests that only those filesystems not listed in
+.I fslist
+will be checked. If all of the filesystems in
+.I fslist
+are not prefixed by a negation operator, then only those filesystems
+listed
+in
+.I fslist
+will be checked.
+.sp
+Options specifiers may be included in the comma-separated
+.IR fslist .
+They must have the format
+.BI opts= fs-option\fR.
+If an options specifier is present, then only filesystems which contain
+.I fs-option
+in their mount options field of
+.B /etc/fstab
+will be checked. If the options specifier is prefixed by a negation
+operator, then only
+those filesystems that do not have
+.I fs-option
+in their mount options field of
+.B /etc/fstab
+will be checked.
+.sp
+For example, if
+.B opts=ro
+appears in
+.IR fslist ,
+then only filesystems listed in
+.B /etc/fstab
+with the
+.B ro
+option will be checked.
+.sp
+For compatibility with Mandrake distributions whose boot scripts
+depend upon an unauthorized UI change to the
+.B fsck
+program, if a filesystem type of
+.B loop
+is found in
+.IR fslist ,
+it is treated as if
+.B opts=loop
+were specified as an argument to the
+.B \-t
+option.
+.sp
+Normally, the filesystem type is deduced by searching for
+.I filesys
+in the
+.I /etc/fstab
+file and using the corresponding entry.
+If the type can not be deduced, and there is only a single filesystem
+given as an argument to the
+.B \-t
+option,
+.B fsck
+will use the specified filesystem type. If this type is not
+available, then the default file system type (currently ext2) is used.
+.TP
+.B \-A
+Walk through the
+.I /etc/fstab
+file and try to check all file systems in one run. This option is
+typically used from the
+.I /etc/rc
+system initialization file, instead of multiple commands for checking
+a single file system.
+.sp
+The root filesystem will be checked first unless the
+.B \-P
+option is specified (see below). After that,
+filesystems will be checked in the order specified by the
+.I fs_passno
+(the sixth) field in the
+.I /etc/fstab
+file.
+Filesystems with a
+.I fs_passno
+value of 0 are skipped and are not checked at all. Filesystems with a
+.I fs_passno
+value of greater than zero will be checked in order,
+with filesystems with the lowest
+.I fs_passno
+number being checked first.
+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
+Hence, a very common configuration in
+.I /etc/fstab
+files is to set the root filesystem to have a
+.I fs_passno
+value of 1
+and to set all other filesystems to have a
+.I fs_passno
+value of 2. This will allow
+.B fsck
+to automatically run filesystem checkers in parallel if it is advantageous
+to do so. System administrators might choose
+not to use this configuration if they need to avoid multiple filesystem
+checks running in parallel for some reason --- for example, if the
+machine in question is short on memory so that
+excessive paging is a concern.
+.TP
+.B \-C\fR [ \fI "fd" \fR ]
+Display completion/progress bars for those filesystem checkers (currently
+only for ext2 and ext3) which support them. Fsck will manage the
+filesystem checkers so that only one of them will display
+a progress bar at a time. GUI front-ends may specify a file descriptor
+.IR fd ,
+in which case the progress bar information will be sent to that file descriptor.
+.TP
+.B \-N
+Don't execute, just show what would be done.
+.TP
+.B \-P
+When the
+.B \-A
+flag is set, check the root filesystem in parallel with the other filesystems.
+This is not the safest thing in the world to do,
+since if the root filesystem is in doubt things like the
+.BR e2fsck (8)
+executable might be corrupted! This option is mainly provided
+for those sysadmins who don't want to repartition the root
+filesystem to be small and compact (which is really the right solution).
+.TP
+.B \-R
+When checking all file systems with the
+.B \-A
+flag, skip the root file system (in case it's already mounted read-write).
+.TP
+.B \-T
+Don't show the title on startup.
+.TP
+.B \-V
+Produce verbose output, including all file system-specific commands
+that are executed.
+.TP
+.B fs-specific-options
+Options which are not understood by
+.B fsck
+are passed to the filesystem-specific checker. These arguments
+.B must
+not take arguments, as there is no
+way for
+.B fsck
+to be able to properly guess which arguments take options and which
+don't.
+.IP
+Options and arguments which follow the
+.B \-\-
+are treated as file system-specific options to be passed to the
+file system-specific checker.
+.IP
+Please note that fsck is not
+designed to pass arbitrarily complicated options to filesystem-specific
+checkers. If you're doing something complicated, please just
+execute the filesystem-specific checker directly. If you pass
+.B fsck
+some horribly complicated option and arguments, and it doesn't do
+what you expect,
+.B don't bother reporting it as a bug.
+You're almost certainly doing something that you shouldn't be doing
+with
+.BR fsck.
+.PP
+Options to different filesystem-specific fsck's are not standardized.
+If in doubt, please consult the man pages of the filesystem-specific
+checker. Although not guaranteed, the following options are supported
+by most file system checkers:
+.TP
+.B \-a
+Automatically repair the file system without any questions (use
+this option with caution). Note that
+.BR e2fsck (8)
+supports
+.B \-a
+for backwards compatibility only. This option is mapped to
+.BR e2fsck 's
+.B \-p
+option which is safe to use, unlike the
+.B \-a
+option that some file system checkers support.
+.TP
+.B \-n
+For some filesystem-specific checkers, the
+.B \-n
+option will cause the fs-specific fsck to avoid attempting to repair any
+problems, but simply report such problems to stdout. This is however
+not true for all filesystem-specific checkers. In particular,
+.BR fsck.reiserfs (8)
+will not report any corruption if given this option.
+.BR fsck.minix (8)
+does not support the
+.B \-n
+option at all.
+.TP
+.B \-r
+Interactively repair the filesystem (ask for confirmations). Note: It
+is generally a bad idea to use this option if multiple fsck's are being
+run in parallel. Also note that this is
+.BR e2fsck 's
+default behavior; it supports this option for backwards compatibility
+reasons only.
+.TP
+.B \-y
+For some filesystem-specific checkers, the
+.B \-y
+option will cause the fs-specific fsck to always attempt to fix any
+detected filesystem corruption automatically. Sometimes an expert may
+be able to do better driving the fsck manually. Note that
+.B not
+all filesystem-specific checkers implement this option. In particular
+.BR fsck.minix (8)
+and
+.BR fsck.cramfs (8)
+does not support the
+.B -y
+option as of this writing.
+.SH AUTHOR
+Theodore Ts'o ([email protected])
+.SH FILES
+.IR /etc/fstab .
+.SH ENVIRONMENT VARIABLES
+The
+.B fsck
+program's behavior is affected by the following environment variables:
+.TP
+.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.)
+.TP
+.B FSCK_MAX_INST
+This environment variable will limit the maximum number of file system
+checkers that can be running at one time. This allows configurations
+which have a large number of disks to avoid
+.B fsck
+starting too many file system checkers at once, which might overload
+CPU and memory resources available on the system. If this value is
+zero, then an unlimited number of processes can be spawned. This is
+currently the default, but future versions of
+.B fsck
+may attempt to automatically determine how many file system checks can
+be run based on gathering accounting data from the operating system.
+.TP
+.B PATH
+The
+.B PATH
+environment variable is used to find file system checkers. A set of
+system directories are searched first:
+.BR /sbin ,
+.BR /sbin/fs.d ,
+.BR /sbin/fs ,
+.BR /etc/fs ,
+and
+.BR /etc .
+Then the set of directories found in the
+.B PATH
+environment are searched.
+.TP
+.B FSTAB_FILE
+This environment variable allows the system administrator
+to override the standard location of the
+.B /etc/fstab
+file. It is also useful for developers who are testing
+.BR fsck .
+.SH SEE ALSO
+.BR fstab (5),
+.BR mkfs (8),
+.BR fsck.ext2 (8)
+or
+.BR fsck.ext3 (8)
+or
+.BR e2fsck (8),
+.BR cramfsck (8),
+.BR fsck.minix (8),
+.BR fsck.msdos (8),
+.BR fsck.jfs (8),
+.BR fsck.nfs (8),
+.BR fsck.vfat (8),
+.BR fsck.xfs (8),
+.BR fsck.xiafs (8),
+.BR reiserfsck (8).
Index: util-linux-ng/fsck/fsck.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ util-linux-ng/fsck/fsck.c 2007-09-25 17:36:19.000000000 +0200
@@ -0,0 +1,1295 @@
+/*
+ * pfsck --- A generic, parallelizing front-end for the fsck program.
+ * It will automatically try to run fsck programs in parallel if the
+ * devices are on separate spindles. It is based on the same ideas as
+ * the generic front end for fsck by David Engel and Fred van Kempen,
+ * but it has been completely rewritten from scratch to support
+ * parallel execution.
+ *
+ * Written by Theodore Ts'o, <[email protected]>
+ *
+ * Miquel van Smoorenburg ([email protected]) 20-Oct-1994:
+ * o Changed -t fstype to behave like with mount when -A (all file
+ * systems) or -M (like mount) is specified.
+ * o fsck looks if it can find the fsck.type program to decide
+ * if it should ignore the fs type. This way more fsck programs
+ * can be added without changing this front-end.
+ * o -R flag skip root file system.
+ *
+ * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+ * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
+ *
+ * %Begin-Header%
+ * This file may be redistributed under the terms of the GNU Public
+ * License.
+ * %End-Header%
+ */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <limits.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+#include <nls.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <blkid/blkid.h>
+
+#include "fsck.h"
+
+#ifndef _PATH_MNTTAB
+#define _PATH_MNTTAB "/etc/fstab"
+#endif
+
+static const char *ignored_types[] = {
+ "ignore",
+ "iso9660",
+ "nfs",
+ "proc",
+ "sw",
+ "swap",
+ "tmpfs",
+ "devpts",
+ NULL
+};
+
+static const char *really_wanted[] = {
+ "minix",
+ "ext2",
+ "ext3",
+ "jfs",
+ "reiserfs",
+ "xiafs",
+ "xfs",
+ NULL
+};
+
+#define BASE_MD "/dev/md"
+
+/*
+ * Global variables for options
+ */
+char *devices[MAX_DEVICES];
+char *args[MAX_ARGS];
+int num_devices, num_args;
+
+int verbose = 0;
+int doall = 0;
+int noexecute = 0;
+int serialize = 0;
+int skip_root = 0;
+int like_mount = 0;
+int notitle = 0;
+int parallel_root = 0;
+int progress = 0;
+int progress_fd = 0;
+int force_all_parallel = 0;
+int num_running = 0;
+int max_running = 0;
+volatile int cancel_requested = 0;
+int kill_sent = 0;
+char *progname;
+char *fstype = NULL;
+struct fs_info *filesys_info = NULL, *filesys_last = NULL;
+struct fsck_instance *instance_list;
+const char *fsck_prefix_path = "/sbin:/sbin/fs.d:/sbin/fs:/etc/fs:/etc";
+char *fsck_path = 0;
+blkid_cache cache = NULL;
+
+static char *string_copy(const char *s)
+{
+ char *ret;
+
+ if (!s)
+ return 0;
+ ret = malloc(strlen(s)+1);
+ if (ret)
+ strcpy(ret, s);
+ return ret;
+}
+
+static int string_to_int(const char *s)
+{
+ long l;
+ char *p;
+
+ l = strtol(s, &p, 0);
+ if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
+ return -1;
+ else
+ return (int) l;
+}
+
+static int ignore(struct fs_info *);
+
+static char *skip_over_blank(char *cp)
+{
+ while (*cp && isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static char *skip_over_word(char *cp)
+{
+ while (*cp && !isspace(*cp))
+ cp++;
+ return cp;
+}
+
+static void strip_line(char *line)
+{
+ char *p;
+
+ while (*line) {
+ p = line + strlen(line) - 1;
+ if ((*p == '\n') || (*p == '\r'))
+ *p = 0;
+ else
+ break;
+ }
+}
+
+static char *parse_word(char **buf)
+{
+ char *word, *next;
+
+ word = *buf;
+ if (*word == 0)
+ return 0;
+
+ word = skip_over_blank(word);
+ next = skip_over_word(word);
+ if (*next)
+ *next++ = 0;
+ *buf = next;
+ return word;
+}
+
+static void parse_escape(char *word)
+{
+ char *p, *q;
+ int ac, i;
+
+ if (!word)
+ return;
+
+ for (p = word, q = word; *p; p++, q++) {
+ *q = *p;
+ if (*p != '\\')
+ continue;
+ if (*++p == 0)
+ break;
+ if (*p == 't') {
+ *q = '\t';
+ continue;
+ }
+ if (*p == 'n') {
+ *q = '\n';
+ continue;
+ }
+ if (!isdigit(*p)) {
+ *q = *p;
+ continue;
+ }
+ ac = 0;
+ for (i = 0; i < 3; i++, p++) {
+ if (!isdigit(*p))
+ break;
+ ac = (ac * 8) + (*p - '0');
+ }
+ *q = ac;
+ p--;
+ }
+ *q = 0;
+}
+
+static void free_instance(struct fsck_instance *i)
+{
+ if (i->prog)
+ free(i->prog);
+ if (i->device)
+ free(i->device);
+ if (i->base_device)
+ free(i->base_device);
+ free(i);
+ return;
+}
+
+static struct fs_info *create_fs_device(const char *device, const char *mntpnt,
+ const char *type, const char *opts,
+ int freq, int passno)
+{
+ struct fs_info *fs;
+
+ if (!(fs = malloc(sizeof(struct fs_info))))
+ return NULL;
+
+ fs->device = string_copy(device);
+ fs->mountpt = string_copy(mntpnt);
+ fs->type = string_copy(type);
+ fs->opts = string_copy(opts ? opts : "");
+ fs->freq = freq;
+ fs->passno = passno;
+ fs->flags = 0;
+ fs->next = NULL;
+
+ if (!filesys_info)
+ filesys_info = fs;
+ else
+ filesys_last->next = fs;
+ filesys_last = fs;
+
+ return fs;
+}
+
+
+
+static int parse_fstab_line(char *line, struct fs_info **ret_fs)
+{
+ char *dev, *device, *mntpnt, *type, *opts, *freq, *passno, *cp;
+ struct fs_info *fs;
+
+ *ret_fs = 0;
+ strip_line(line);
+ if ((cp = strchr(line, '#')))
+ *cp = 0; /* Ignore everything after the comment char */
+ cp = line;
+
+ device = parse_word(&cp);
+ mntpnt = parse_word(&cp);
+ type = parse_word(&cp);
+ opts = parse_word(&cp);
+ freq = parse_word(&cp);
+ passno = parse_word(&cp);
+
+ if (!device)
+ return 0; /* Allow blank lines */
+
+ if (!mntpnt || !type)
+ return -1;
+
+ parse_escape(device);
+ parse_escape(mntpnt);
+ parse_escape(type);
+ parse_escape(opts);
+ parse_escape(freq);
+ parse_escape(passno);
+
+ dev = blkid_get_devname(cache, device, NULL);
+ if (dev)
+ device = dev;
+
+ if (strchr(type, ','))
+ type = 0;
+
+ fs = create_fs_device(device, mntpnt, type ? type : "auto", opts,
+ freq ? atoi(freq) : -1,
+ passno ? atoi(passno) : -1);
+ if (dev)
+ free(dev);
+
+ if (!fs)
+ return -1;
+ *ret_fs = fs;
+ return 0;
+}
+
+static void interpret_type(struct fs_info *fs)
+{
+ char *t;
+
+ if (strcmp(fs->type, "auto") != 0)
+ return;
+ t = blkid_get_tag_value(cache, "TYPE", fs->device);
+ if (t) {
+ free(fs->type);
+ fs->type = t;
+ }
+}
+
+/*
+ * Load the filesystem database from /etc/fstab
+ */
+static void load_fs_info(const char *filename)
+{
+ FILE *f;
+ char buf[1024];
+ int lineno = 0;
+ int old_fstab = 1;
+ struct fs_info *fs;
+
+ if ((f = fopen(filename, "r")) == NULL) {
+ fprintf(stderr, _("WARNING: couldn't open %s: %s\n"),
+ filename, strerror(errno));
+ return;
+ }
+ while (!feof(f)) {
+ lineno++;
+ if (!fgets(buf, sizeof(buf), f))
+ break;
+ buf[sizeof(buf)-1] = 0;
+ if (parse_fstab_line(buf, &fs) < 0) {
+ fprintf(stderr, _("WARNING: bad format "
+ "on line %d of %s\n"), lineno, filename);
+ continue;
+ }
+ if (!fs)
+ continue;
+ if (fs->passno < 0)
+ fs->passno = 0;
+ else
+ old_fstab = 0;
+ }
+
+ fclose(f);
+
+ if (old_fstab) {
+ fputs(_("\007\007\007"
+ "WARNING: Your /etc/fstab does not contain the fsck passno\n"
+ " field. I will kludge around things for you, but you\n"
+ " should fix your /etc/fstab file as soon as you can.\n\n"), stderr);
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ fs->passno = 1;
+ }
+ }
+}
+
+/* Lookup filesys in /etc/fstab and return the corresponding entry. */
+static struct fs_info *lookup(char *filesys)
+{
+ struct fs_info *fs;
+
+ /* No filesys name given. */
+ if (filesys == NULL)
+ return NULL;
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (!strcmp(filesys, fs->device) ||
+ (fs->mountpt && !strcmp(filesys, fs->mountpt)))
+ break;
+ }
+
+ return fs;
+}
+
+/* Find fsck program for a given fs type. */
+static char *find_fsck(char *type)
+{
+ char *s;
+ const char *tpl;
+ static char prog[256];
+ char *p = string_copy(fsck_path);
+ struct stat st;
+
+ /* Are we looking for a program or just a type? */
+ tpl = (strncmp(type, "fsck.", 5) ? "%s/fsck.%s" : "%s/%s");
+
+ for(s = strtok(p, ":"); s; s = strtok(NULL, ":")) {
+ sprintf(prog, tpl, s, type);
+ if (stat(prog, &st) == 0) break;
+ }
+ free(p);
+ return(s ? prog : NULL);
+}
+
+static int progress_active(NOARGS)
+{
+ struct fsck_instance *inst;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ if (inst->flags & FLAG_PROGRESS)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * 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)
+{
+ char *s, *argv[80], prog[80];
+ int argc, i;
+ struct fsck_instance *inst, *p;
+ pid_t pid;
+
+ inst = malloc(sizeof(struct fsck_instance));
+ if (!inst)
+ return ENOMEM;
+ memset(inst, 0, sizeof(struct fsck_instance));
+
+ sprintf(prog, "fsck.%s", type);
+ argv[0] = string_copy(prog);
+ argc = 1;
+
+ for (i=0; i <num_args; i++)
+ argv[argc++] = string_copy(args[i]);
+
+ if (progress && !progress_active()) {
+ if ((strcmp(type, "ext2") == 0) ||
+ (strcmp(type, "ext3") == 0)) {
+ char tmp[80];
+ snprintf(tmp, 80, "-C%d", progress_fd);
+ argv[argc++] = string_copy(tmp);
+ inst->flags |= FLAG_PROGRESS;
+ }
+ }
+
+ argv[argc++] = string_copy(device);
+ argv[argc] = 0;
+
+ s = find_fsck(prog);
+ if (s == NULL) {
+ fprintf(stderr, _("fsck: %s: not found\n"), prog);
+ free(inst);
+ return ENOENT;
+ }
+
+ if (verbose || noexecute) {
+ printf("[%s (%d) -- %s] ", s, num_running,
+ mntpt ? mntpt : device);
+ for (i=0; i < argc; i++)
+ printf("%s ", argv[i]);
+ printf("\n");
+ }
+
+ /* Fork and execute the correct program. */
+ if (noexecute)
+ pid = -1;
+ else if ((pid = fork()) < 0) {
+ perror("fork");
+ free(inst);
+ return errno;
+ } else if (pid == 0) {
+ if (!interactive)
+ close(0);
+ (void) execv(s, argv);
+ perror(argv[0]);
+ free(inst);
+ exit(EXIT_ERROR);
+ }
+
+ for (i=0; i < argc; i++)
+ free(argv[i]);
+
+ 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->next = NULL;
+
+ /*
+ * Find the end of the list, so we add the instance on at the end.
+ */
+ for (p = instance_list; p && p->next; p = p->next);
+
+ if (p)
+ p->next = inst;
+ else
+ instance_list = inst;
+
+ return 0;
+}
+
+/*
+ * Send a signal to all outstanding fsck child processes
+ */
+static int kill_all(int signum)
+{
+ struct fsck_instance *inst;
+ int n = 0;
+
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (inst->flags & FLAG_DONE)
+ continue;
+ kill(inst->pid, signum);
+ n++;
+ }
+ return n;
+}
+
+/*
+ * Wait for one child process to exit; when it does, unlink it from
+ * the list of executing child processes, and return it.
+ */
+static struct fsck_instance *wait_one(int flags)
+{
+ int status;
+ int sig;
+ struct fsck_instance *inst, *inst2, *prev;
+ pid_t pid;
+
+ if (!instance_list)
+ return NULL;
+
+ if (noexecute) {
+ inst = instance_list;
+ prev = 0;
+#ifdef RANDOM_DEBUG
+ while (inst->next && (random() & 1)) {
+ prev = inst;
+ inst = inst->next;
+ }
+#endif
+ inst->exit_status = 0;
+ goto ret_inst;
+ }
+
+ /*
+ * gcc -Wall fails saving throw against stupidity
+ * (inst and prev are thought to be uninitialized variables)
+ */
+ inst = prev = NULL;
+
+ do {
+ pid = waitpid(-1, &status, flags);
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ if ((pid == 0) && (flags & WNOHANG))
+ return NULL;
+ if (pid < 0) {
+ if ((errno == EINTR) || (errno == EAGAIN))
+ continue;
+ if (errno == ECHILD) {
+ fprintf(stderr,
+ _("%s: wait: No more child process?!?\n"),
+ progname);
+ return NULL;
+ }
+ perror("wait");
+ continue;
+ }
+ for (prev = 0, inst = instance_list;
+ inst;
+ prev = inst, inst = inst->next) {
+ if (inst->pid == pid)
+ break;
+ }
+ } while (!inst);
+
+ if (WIFEXITED(status))
+ status = WEXITSTATUS(status);
+ else if (WIFSIGNALED(status)) {
+ sig = WTERMSIG(status);
+ if (sig == SIGINT) {
+ status = EXIT_UNCORRECTED;
+ } else {
+ printf(_("Warning... %s for device %s exited "
+ "with signal %d.\n"),
+ inst->prog, inst->device, sig);
+ status = EXIT_ERROR;
+ }
+ } else {
+ printf(_("%s %s: status is %x, should never happen.\n"),
+ inst->prog, inst->device, status);
+ status = EXIT_ERROR;
+ }
+ inst->exit_status = status;
+ if (progress && (inst->flags & FLAG_PROGRESS) &&
+ !progress_active()) {
+ for (inst2 = instance_list; inst2; inst2 = inst2->next) {
+ if (inst2->flags & FLAG_DONE)
+ continue;
+ if (strcmp(inst2->type, "ext2") &&
+ strcmp(inst2->type, "ext3"))
+ continue;
+ /*
+ * If we've just started the fsck, wait a tiny
+ * bit before sending the kill, to give it
+ * time to set up the signal handler
+ */
+ if (inst2->start_time < time(0)+2) {
+ if (fork() == 0) {
+ sleep(1);
+ kill(inst2->pid, SIGUSR1);
+ exit(0);
+ }
+ } else
+ kill(inst2->pid, SIGUSR1);
+ inst2->flags |= FLAG_PROGRESS;
+ break;
+ }
+ }
+ret_inst:
+ if (prev)
+ prev->next = inst->next;
+ else
+ instance_list = inst->next;
+ if (verbose > 1)
+ printf(_("Finished with %s (exit status %d)\n"),
+ inst->device, inst->exit_status);
+ num_running--;
+ return inst;
+}
+
+#define FLAG_WAIT_ALL 0
+#define FLAG_WAIT_ATLEAST_ONE 1
+/*
+ * Wait until all executing child processes have exited; return the
+ * logical OR of all of their exit code values.
+ */
+static int wait_many(int flags)
+{
+ struct fsck_instance *inst;
+ int global_status = 0;
+ int wait_flags = 0;
+
+ while ((inst = wait_one(wait_flags))) {
+ global_status |= inst->exit_status;
+ free_instance(inst);
+#ifdef RANDOM_DEBUG
+ if (noexecute && (flags & WNOHANG) && !(random() % 3))
+ break;
+#endif
+ if (flags & FLAG_WAIT_ATLEAST_ONE)
+ wait_flags = WNOHANG;
+ }
+ return global_status;
+}
+
+/*
+ * Run the fsck program on a particular device
+ *
+ * If the type is specified using -t, and it isn't prefixed with "no"
+ * (as in "noext2") and only one filesystem type is specified, then
+ * use that type regardless of what is specified in /etc/fstab.
+ *
+ * If the type isn't specified by the user, then use either the type
+ * specified in /etc/fstab, or DEFAULT_FSTYPE.
+ */
+static void fsck_device(struct fs_info *fs, int interactive)
+{
+ const char *type;
+ int retval;
+
+ interpret_type(fs);
+
+ if (strcmp(fs->type, "auto") != 0)
+ type = fs->type;
+ else if (fstype && strncmp(fstype, "no", 2) &&
+ strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
+ !strchr(fstype, ','))
+ type = fstype;
+ else
+ type = DEFAULT_FSTYPE;
+
+ num_running++;
+ retval = execute(type, fs->device, fs->mountpt, interactive);
+ if (retval) {
+ fprintf(stderr, _("%s: Error %d while executing fsck.%s "
+ "for %s\n"), progname, retval, type, fs->device);
+ num_running--;
+ }
+}
+
+
+/*
+ * Deal with the fsck -t argument.
+ */
+struct fs_type_compile {
+ char **list;
+ int *type;
+ int negate;
+} fs_type_compiled;
+
+#define FS_TYPE_NORMAL 0
+#define FS_TYPE_OPT 1
+#define FS_TYPE_NEGOPT 2
+
+static const char *fs_type_syntax_error =
+N_("Either all or none of the filesystem types passed to -t must be prefixed\n"
+ "with 'no' or '!'.\n");
+
+static void compile_fs_type(char *fs_type, struct fs_type_compile *cmp)
+{
+ char *cp, *list, *s;
+ int num = 2;
+ int negate, first_negate = 1;
+
+ if (fs_type) {
+ for (cp=fs_type; *cp; cp++) {
+ if (*cp == ',')
+ num++;
+ }
+ }
+
+ cmp->list = malloc(num * sizeof(char *));
+ cmp->type = malloc(num * sizeof(int));
+ if (!cmp->list || !cmp->type) {
+ fputs(_("Couldn't allocate memory for filesystem types\n"),
+ stderr);
+ exit(EXIT_ERROR);
+ }
+ memset(cmp->list, 0, num * sizeof(char *));
+ memset(cmp->type, 0, num * sizeof(int));
+ cmp->negate = 0;
+
+ if (!fs_type)
+ return;
+
+ list = string_copy(fs_type);
+ num = 0;
+ s = strtok(list, ",");
+ while(s) {
+ negate = 0;
+ if (strncmp(s, "no", 2) == 0) {
+ s += 2;
+ negate = 1;
+ } else if (*s == '!') {
+ s++;
+ negate = 1;
+ }
+ if (strcmp(s, "loop") == 0)
+ /* loop is really short-hand for opts=loop */
+ goto loop_special_case;
+ else if (strncmp(s, "opts=", 5) == 0) {
+ s += 5;
+ loop_special_case:
+ cmp->type[num] = negate ? FS_TYPE_NEGOPT : FS_TYPE_OPT;
+ } else {
+ if (first_negate) {
+ cmp->negate = negate;
+ first_negate = 0;
+ }
+ if ((negate && !cmp->negate) ||
+ (!negate && cmp->negate)) {
+ fputs(_(fs_type_syntax_error), stderr);
+ exit(EXIT_USAGE);
+ }
+ }
+#if 0
+ printf("Adding %s to list (type %d).\n", s, cmp->type[num]);
+#endif
+ cmp->list[num++] = string_copy(s);
+ s = strtok(NULL, ",");
+ }
+ free(list);
+}
+
+/*
+ * This function returns true if a particular option appears in a
+ * comma-delimited options list
+ */
+static int opt_in_list(char *opt, char *optlist)
+{
+ char *list, *s;
+
+ if (!optlist)
+ return 0;
+ list = string_copy(optlist);
+
+ s = strtok(list, ",");
+ while(s) {
+ if (strcmp(s, opt) == 0) {
+ free(list);
+ return 1;
+ }
+ s = strtok(NULL, ",");
+ }
+ free(list);
+ return 0;
+}
+
+/* See if the filesystem matches the criteria given by the -t option */
+static int fs_match(struct fs_info *fs, struct fs_type_compile *cmp)
+{
+ int n, ret = 0, checked_type = 0;
+ char *cp;
+
+ if (cmp->list == 0 || cmp->list[0] == 0)
+ return 1;
+
+ for (n=0; (cp = cmp->list[n]); n++) {
+ switch (cmp->type[n]) {
+ case FS_TYPE_NORMAL:
+ checked_type++;
+ if (strcmp(cp, fs->type) == 0) {
+ ret = 1;
+ }
+ break;
+ case FS_TYPE_NEGOPT:
+ if (opt_in_list(cp, fs->opts))
+ return 0;
+ break;
+ case FS_TYPE_OPT:
+ if (!opt_in_list(cp, fs->opts))
+ return 0;
+ break;
+ }
+ }
+ if (checked_type == 0)
+ return 1;
+ return (cmp->negate ? !ret : ret);
+}
+
+/* Check if we should ignore this filesystem. */
+static int ignore(struct fs_info *fs)
+{
+ const char **ip;
+ int wanted = 0;
+
+ /*
+ * If the pass number is 0, ignore it.
+ */
+ if (fs->passno == 0)
+ return 1;
+
+ interpret_type(fs);
+
+ /*
+ * If a specific fstype is specified, and it doesn't match,
+ * ignore it.
+ */
+ if (!fs_match(fs, &fs_type_compiled)) return 1;
+
+ /* Are we ignoring this type? */
+ for(ip = ignored_types; *ip; ip++)
+ if (strcmp(fs->type, *ip) == 0) return 1;
+
+ /* Do we really really want to check this fs? */
+ for(ip = really_wanted; *ip; ip++)
+ if (strcmp(fs->type, *ip) == 0) {
+ wanted = 1;
+ break;
+ }
+
+ /* See if the <fsck.fs> program is available. */
+ if (find_fsck(fs->type) == NULL) {
+ if (wanted)
+ fprintf(stderr, _("fsck: cannot check %s: fsck.%s not found\n"),
+ fs->device, fs->type);
+ return 1;
+ }
+
+ /* We can and want to check this file system type. */
+ return 0;
+}
+
+/*
+ * Returns TRUE if a partition on the same disk is already being
+ * checked.
+ */
+static int device_already_active(char *device)
+{
+ 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)))
+ return 1;
+#endif
+
+ base = base_device(device);
+ /*
+ * If we don't know the base device, assume that the device is
+ * already active if there are any fsck instances running.
+ */
+ if (!base)
+ return (instance_list != 0);
+ for (inst = instance_list; inst; inst = inst->next) {
+ if (!inst->base_device || !strcmp(base, inst->base_device)) {
+ free(base);
+ return 1;
+ }
+ }
+ free(base);
+ return 0;
+}
+
+/* Check all file systems, using the /etc/fstab table. */
+static int check_all(NOARGS)
+{
+ struct fs_info *fs = NULL;
+ int status = EXIT_OK;
+ int not_done_yet = 1;
+ int passno = 1;
+ int pass_done;
+
+ if (verbose)
+ fputs(_("Checking all file systems.\n"), stdout);
+
+ /*
+ * Do an initial scan over the filesystem; mark filesystems
+ * which should be ignored as done, and resolve any "auto"
+ * filesystem types (done as a side-effect of calling ignore()).
+ */
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (ignore(fs))
+ fs->flags |= FLAG_DONE;
+ }
+
+ /*
+ * Find and check the root filesystem.
+ */
+ if (!parallel_root) {
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (!strcmp(fs->mountpt, "/"))
+ break;
+ }
+ if (fs) {
+ if (!skip_root && !ignore(fs)) {
+ fsck_device(fs, 1);
+ status |= wait_many(FLAG_WAIT_ALL);
+ if (status > EXIT_NONDESTRUCT)
+ return status;
+ }
+ fs->flags |= FLAG_DONE;
+ }
+ }
+ /*
+ * This is for the bone-headed user who enters the root
+ * filesystem twice. Skip root will skep all root entries.
+ */
+ if (skip_root)
+ for (fs = filesys_info; fs; fs = fs->next)
+ if (!strcmp(fs->mountpt, "/"))
+ fs->flags |= FLAG_DONE;
+
+ while (not_done_yet) {
+ not_done_yet = 0;
+ pass_done = 1;
+
+ for (fs = filesys_info; fs; fs = fs->next) {
+ if (cancel_requested)
+ break;
+ if (fs->flags & FLAG_DONE)
+ continue;
+ /*
+ * If the filesystem's pass number is higher
+ * than the current pass number, then we don't
+ * do it yet.
+ */
+ if (fs->passno > passno) {
+ not_done_yet++;
+ continue;
+ }
+ /*
+ * If a filesystem on a particular device has
+ * already been spawned, then we need to defer
+ * this to another pass.
+ */
+ if (device_already_active(fs->device)) {
+ pass_done = 0;
+ continue;
+ }
+ /*
+ * Spawn off the fsck process
+ */
+ fsck_device(fs, serialize);
+ fs->flags |= FLAG_DONE;
+
+ /*
+ * Only do one filesystem at a time, or if we
+ * have a limit on the number of fsck's extant
+ * at one time, apply that limit.
+ */
+ if (serialize ||
+ (max_running && (num_running >= max_running))) {
+ pass_done = 0;
+ break;
+ }
+ }
+ if (cancel_requested)
+ break;
+ if (verbose > 1)
+ printf(_("--waiting-- (pass %d)\n"), passno);
+ status |= wait_many(pass_done ? FLAG_WAIT_ALL :
+ FLAG_WAIT_ATLEAST_ONE);
+ if (pass_done) {
+ if (verbose > 1)
+ printf("----------------------------------\n");
+ passno++;
+ } else
+ not_done_yet++;
+ }
+ if (cancel_requested && !kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ status |= wait_many(FLAG_WAIT_ATLEAST_ONE);
+ return status;
+}
+
+static void usage(NOARGS)
+{
+ fputs(_("Usage: fsck [-ANPRTV] [ -C [ fd ] ] [-t fstype] [fs-options] [filesys ...]\n"), stderr);
+ exit(EXIT_USAGE);
+}
+
+static void signal_cancel(int sig FSCK_ATTR((unused)))
+{
+ cancel_requested++;
+}
+
+static void PRS(int argc, char *argv[])
+{
+ int i, j;
+ char *arg, *dev, *tmp = 0;
+ char options[128];
+ int opt = 0;
+ int opts_for_fsck = 0;
+ struct sigaction sa;
+
+ /*
+ * Set up signal action
+ */
+ memset(&sa, 0, sizeof(struct sigaction));
+ sa.sa_handler = signal_cancel;
+ sigaction(SIGINT, &sa, 0);
+ sigaction(SIGTERM, &sa, 0);
+
+ num_devices = 0;
+ num_args = 0;
+ instance_list = 0;
+
+ progname = argv[0];
+
+ for (i=1; i < argc; i++) {
+ arg = argv[i];
+ if (!arg)
+ continue;
+ if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
+ if (num_devices >= MAX_DEVICES) {
+ fprintf(stderr, _("%s: too many devices\n"),
+ progname);
+ exit(EXIT_ERROR);
+ }
+ dev = blkid_get_devname(cache, arg, NULL);
+ if (!dev && strchr(arg, '=')) {
+ /*
+ * Check to see if we failed because
+ * /proc/partitions isn't found.
+ */
+ if (access("/proc/partitions", R_OK) < 0) {
+ fprintf(stderr, "Couldn't open /proc/partitions: %s\n",
+ strerror(errno));
+ fprintf(stderr, "Is /proc mounted?\n");
+ exit(EXIT_ERROR);
+ }
+ /*
+ * Check to see if this is because
+ * we're not running as root
+ */
+ if (geteuid())
+ fprintf(stderr,
+ "Must be root to scan for matching filesystems: %s\n", arg);
+ else
+ fprintf(stderr,
+ "Couldn't find matching filesystem: %s\n", arg);
+ exit(EXIT_ERROR);
+ }
+ devices[num_devices++] = dev ? dev : string_copy(arg);
+ continue;
+ }
+ if (arg[0] != '-' || opts_for_fsck) {
+ if (num_args >= MAX_ARGS) {
+ fprintf(stderr, _("%s: too many arguments\n"),
+ progname);
+ exit(EXIT_ERROR);
+ }
+ args[num_args++] = string_copy(arg);
+ continue;
+ }
+ for (j=1; arg[j]; j++) {
+ if (opts_for_fsck) {
+ options[++opt] = arg[j];
+ continue;
+ }
+ switch (arg[j]) {
+ case 'A':
+ doall++;
+ break;
+ case 'C':
+ progress++;
+ if (arg[j+1]) {
+ progress_fd = string_to_int(arg+j+1);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else
+ goto next_arg;
+ } else if ((i+1) < argc &&
+ !strncmp(argv[i+1], "-", 1) == 0) {
+ progress_fd = string_to_int(argv[i]);
+ if (progress_fd < 0)
+ progress_fd = 0;
+ else {
+ goto next_arg;
+ i++;
+ }
+ }
+ break;
+ case 'V':
+ verbose++;
+ break;
+ case 'N':
+ noexecute++;
+ break;
+ case 'R':
+ skip_root++;
+ break;
+ case 'T':
+ notitle++;
+ break;
+ case 'M':
+ like_mount++;
+ break;
+ case 'P':
+ parallel_root++;
+ break;
+ case 's':
+ serialize++;
+ break;
+ case 't':
+ tmp = 0;
+ if (fstype)
+ usage();
+ if (arg[j+1])
+ tmp = arg+j+1;
+ else if ((i+1) < argc)
+ tmp = argv[++i];
+ else
+ usage();
+ fstype = string_copy(tmp);
+ compile_fs_type(fstype, &fs_type_compiled);
+ goto next_arg;
+ case '-':
+ opts_for_fsck++;
+ break;
+ case '?':
+ usage();
+ break;
+ default:
+ options[++opt] = arg[j];
+ break;
+ }
+ }
+ next_arg:
+ if (opt) {
+ options[0] = '-';
+ options[++opt] = '\0';
+ if (num_args >= MAX_ARGS) {
+ fprintf(stderr,
+ _("%s: too many arguments\n"),
+ progname);
+ exit(EXIT_ERROR);
+ }
+ args[num_args++] = string_copy(options);
+ opt = 0;
+ }
+ }
+ if (getenv("FSCK_FORCE_ALL_PARALLEL"))
+ force_all_parallel++;
+ if ((tmp = getenv("FSCK_MAX_INST")))
+ max_running = atoi(tmp);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, status = 0;
+ int interactive = 0;
+ char *oldpath = getenv("PATH");
+ const char *fstab;
+ struct fs_info *fs;
+
+ setvbuf(stdout, NULL, _IONBF, BUFSIZ);
+ setvbuf(stderr, NULL, _IONBF, BUFSIZ);
+
+ setlocale(LC_CTYPE, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ blkid_get_cache(&cache, NULL);
+ PRS(argc, argv);
+
+ if (!notitle)
+ printf("fsck %s\n", PACKAGE_STRING);
+
+ fstab = getenv("FSTAB_FILE");
+ if (!fstab)
+ fstab = _PATH_MNTTAB;
+ load_fs_info(fstab);
+
+ /* Update our search path to include uncommon directories. */
+ if (oldpath) {
+ fsck_path = malloc (strlen (fsck_prefix_path) + 1 +
+ strlen (oldpath) + 1);
+ if (!fsck_path) {
+ fprintf(stderr, "%s: Unable to allocate memory for fsck_path\n", progname);
+ exit(EXIT_ERROR);
+ }
+ strcpy (fsck_path, fsck_prefix_path);
+ strcat (fsck_path, ":");
+ strcat (fsck_path, oldpath);
+ } else {
+ fsck_path = string_copy(fsck_prefix_path);
+ }
+
+ if ((num_devices == 1) || (serialize))
+ interactive = 1;
+
+ /* If -A was specified ("check all"), do that! */
+ if (doall)
+ return check_all();
+
+ if (num_devices == 0) {
+ serialize++;
+ interactive++;
+ return check_all();
+ }
+ for (i = 0 ; i < num_devices; i++) {
+ if (cancel_requested) {
+ if (!kill_sent) {
+ kill_all(SIGTERM);
+ kill_sent++;
+ }
+ break;
+ }
+ fs = lookup(devices[i]);
+ if (!fs) {
+ fs = create_fs_device(devices[i], 0, "auto",
+ 0, -1, -1);
+ if (!fs)
+ continue;
+ }
+ fsck_device(fs, interactive);
+ if (serialize ||
+ (max_running && (num_running >= max_running))) {
+ struct fsck_instance *inst;
+
+ inst = wait_one(0);
+ if (inst) {
+ status |= inst->exit_status;
+ free_instance(inst);
+ }
+ if (verbose > 1)
+ printf("----------------------------------\n");
+ }
+ }
+ status |= wait_many(FLAG_WAIT_ALL);
+ free(fsck_path);
+ blkid_put_cache(cache);
+ return status;
+}
Index: util-linux-ng/fsck/fsck.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ util-linux-ng/fsck/fsck.h 2007-09-25 17:28:53.000000000 +0200
@@ -0,0 +1,70 @@
+/*
+ * fsck.h
+ */
+
+#include <time.h>
+
+#ifdef __STDC__
+#define NOARGS void
+#else
+#define NOARGS
+#define const
+#endif
+
+#ifdef __GNUC__
+#define FSCK_ATTR(x) __attribute__(x)
+#else
+#define FSCK_ATTR(x)
+#endif
+
+
+#ifndef DEFAULT_FSTYPE
+#define DEFAULT_FSTYPE "ext2"
+#endif
+
+#define MAX_DEVICES 32
+#define MAX_ARGS 32
+
+#define EXIT_OK 0
+#define EXIT_NONDESTRUCT 1
+#define EXIT_DESTRUCT 2
+#define EXIT_UNCORRECTED 4
+#define EXIT_ERROR 8
+#define EXIT_USAGE 16
+#define EXIT_LIBRARY 128
+
+/*
+ * Internal structure for mount tabel entries.
+ */
+
+struct fs_info {
+ char *device;
+ char *mountpt;
+ char *type;
+ char *opts;
+ int freq;
+ int passno;
+ int flags;
+ struct fs_info *next;
+};
+
+#define FLAG_DONE 1
+#define FLAG_PROGRESS 2
+
+/*
+ * Structure to allow exit codes to be stored
+ */
+struct fsck_instance {
+ int pid;
+ int flags;
+ int exit_status;
+ time_t start_time;
+ char * prog;
+ char * type;
+ char * device;
+ char * base_device;
+ struct fsck_instance *next;
+};
+
+extern char *base_device(const char *device);
+extern const char *identify_fs(const char *fs_name, const char *fs_types);


2007-09-26 10:59:52

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On Tue, Sep 25, 2007 at 04:37:35PM +0100, Christoph Hellwig wrote:
> This adds fsck from latest e2fsprogs git to util-linux. There are only
> tiny changes to integrate it into the build system and nls setup of
> util-linux and fixing up the trailing whitespaces quilt is complaining
> about. I've not yet converted it to the fsprobe helpers as the discussion
> on those libs is still ongoing and I haven't read up on the fsprobe library
> either.

It looks like you pulled fsck from the master branch of e2fsprogs git;
there is one slight bug fix in the maint branch that hasn't been
merged into master yet, commit ed773a263829493e4e4bf612dbec2380cf09349f:

fsck: Ignore /etc/fstab entries for bind mounts

If a user specifies a bind mount with a non-zero fsck pass number, for
example:

/foo /bar ext3 bind,defaults 1 3

print a warning and ignore the fstab entry.

Addresses-Red-Hat-Bugzilla: #151533

Signed-off-by: Eric Sandeen <[email protected]>
Signed-off-by: "Theodore Ts'o" <[email protected]>

BTW, I don't like this syntax in the fstab file AT ALL, but it is in
use in the wild by at least some Fedora users, and it's not documented
in the fstab man page. I'd suggest using a filesystem type of bind,
rather than ext3, as the officially "blessed" way of specifying it in
fstab, but it badly needs to be documented in the fstab and/or mount
man pages. The above patch should probably get included, though, and
backwards compatibility for allowing "bind" to be specified in the
mount options, and with a warning message that the specifying "bind"
in the options field has been deprecated.

So with the above patch included,

Acked-by: "Theodore Ts'o" <[email protected]>

For future code movement, I don't mind fsck moving over, but I would
like to manage moving over blkid to util-linux-ng myself, as I have
some pretty strong feelings about the right way to do things. I am
quite willing to add some low-level interfaces so that fsid can use
the same fs probing logic, and I'm willing to add some code so that
the high-level interfaces of libblkid, if the /dev/disk/by-* links are
present and the user isn't asking for information which isn't in the
blkid cache, will use the symlinks instead. However, I really don't
want to encode a dependency on udev being there, and I think it should
be possible to make the fallback be transparent instead of being a
compile-time option.

Regards,

- Ted

2007-09-26 12:05:35

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On Wed, Sep 26, 2007 at 06:59:46AM -0400, Theodore Tso wrote:
> It looks like you pulled fsck from the master branch of e2fsprogs git;
> there is one slight bug fix in the maint branch that hasn't been
> merged into master yet, commit ed773a263829493e4e4bf612dbec2380cf09349f:

I'll pick that up.

> BTW, I don't like this syntax in the fstab file AT ALL, but it is in
> use in the wild by at least some Fedora users, and it's not documented
> in the fstab man page. I'd suggest using a filesystem type of bind,
> rather than ext3, as the officially "blessed" way of specifying it in
> fstab, but it badly needs to be documented in the fstab and/or mount
> man pages. The above patch should probably get included, though, and
> backwards compatibility for allowing "bind" to be specified in the
> mount options, and with a warning message that the specifying "bind"
> in the options field has been deprecated.

The syntax is indeed horrible. Is it supported by upstream util-linux?

> For future code movement, I don't mind fsck moving over, but I would
> like to manage moving over blkid to util-linux-ng myself, as I have
> some pretty strong feelings about the right way to do things. I am
> quite willing to add some low-level interfaces so that fsid can use
> the same fs probing logic, and I'm willing to add some code so that
> the high-level interfaces of libblkid, if the /dev/disk/by-* links are
> present and the user isn't asking for information which isn't in the
> blkid cache, will use the symlinks instead. However, I really don't
> want to encode a dependency on udev being there, and I think it should
> be possible to make the fallback be transparent instead of being a
> compile-time option.

I've started looking into this, and I think at least for the detect
which filesystem we have part libblkid is complete overkill. libvolume_id
has a really nice lowlevel API for that that is much more suitable.

So if it was up to me I'd do the following:

- move libvolume_id out of udev
- make mount/fsck use libvolume_id unconditionally for detecting the
filesystem type. There's absolute no reason to use anything in
libblkid for this, and caching the result doesn't help us at all
as we're going to touch the disk anyway as part of the mount/fsck.
- make libblkid use libvolume_id internally for filesystem detection

note that the latter might aswell be a static inclusion of the code,
I haven't looked at the details yet.

Another note on moving the libraries into util-linux vs a standalone package:
At least in xfs land people do upgrade xfsprogs frequently and sometimes
independent os the distro because new features get added quite a bit, including
new filesystem features that require support. Having to upgrade util-linux
for that is not very helpful. So I'm not so sure about moving this to
util-linux

2007-09-26 12:29:30

by Kay Sievers

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On 9/26/07, Theodore Tso <[email protected]> wrote:
> On Tue, Sep 25, 2007 at 04:37:35PM +0100, Christoph Hellwig wrote:
> > This adds fsck from latest e2fsprogs git to util-linux. There are only
> > tiny changes to integrate it into the build system and nls setup of
> > util-linux and fixing up the trailing whitespaces quilt is complaining
> > about. I've not yet converted it to the fsprobe helpers as the discussion
> > on those libs is still ongoing and I haven't read up on the fsprobe library
> > either.
>
> It looks like you pulled fsck from the master branch of e2fsprogs git;
> there is one slight bug fix in the maint branch that hasn't been
> merged into master yet, commit ed773a263829493e4e4bf612dbec2380cf09349f:
>
> fsck: Ignore /etc/fstab entries for bind mounts
>
> If a user specifies a bind mount with a non-zero fsck pass number, for
> example:
>
> /foo /bar ext3 bind,defaults 1 3
>
> print a warning and ignore the fstab entry.
>
> Addresses-Red-Hat-Bugzilla: #151533
>
> Signed-off-by: Eric Sandeen <[email protected]>
> Signed-off-by: "Theodore Ts'o" <[email protected]>
>
> BTW, I don't like this syntax in the fstab file AT ALL, but it is in
> use in the wild by at least some Fedora users, and it's not documented
> in the fstab man page. I'd suggest using a filesystem type of bind,
> rather than ext3, as the officially "blessed" way of specifying it in
> fstab, but it badly needs to be documented in the fstab and/or mount
> man pages. The above patch should probably get included, though, and
> backwards compatibility for allowing "bind" to be specified in the
> mount options, and with a warning message that the specifying "bind"
> in the options field has been deprecated.
>
> So with the above patch included,
>
> Acked-by: "Theodore Ts'o" <[email protected]>
>
> For future code movement, I don't mind fsck moving over, but I would
> like to manage moving over blkid to util-linux-ng myself, as I have
> some pretty strong feelings about the right way to do things. I am
> quite willing to add some low-level interfaces so that fsid can use
> the same fs probing logic, and I'm willing to add some code so that
> the high-level interfaces of libblkid, if the /dev/disk/by-* links are
> present and the user isn't asking for information which isn't in the
> blkid cache, will use the symlinks instead. However, I really don't
> want to encode a dependency on udev being there, and I think it should
> be possible to make the fallback be transparent instead of being a
> compile-time option.

I really don't want to see something like: "the user isn't asking for
information which isn't in the blkid cache" thing near the code udev
has to call. The cache must be part of the "high-level" blkid
functions, and nothing like this should come near the plain probing
functions.
The compile-time udev switch and the udev symlink logic is already in
the util-linux fsprobe wrapper, and there is no need to move that
into blkid now, especially not by trying to make anything
"transparent".

The low-level functions need to handle 64bit probing offset and size
values, so we can probe partitioned devices.

We also need all filesytem types, all raid metadata probing, and
filesystem version number and classification information in the final
library, so that udev is able to switch over.

A low-level probe must be able to return:
the classification (filesystem, other, raid, crypto)
the filesystem name string (ext3, vfat, xfs, ...)
the version of the filesystem (1.3, FAT32, 0.9, ...)
a label string (unicode validated and translated to utf8)
the raw bytes and the length of the label
a UUID string (some raid sets have free-text UUID's today)
the raw UUID string and the length

And we need to be able to call the sets of raid probing and filesystem
probing functions independent from each other, so that we are able to
skip all raid probing.

I like to see the ~40 volume_id filesystem/raid probing files merged,
and the content of probe.c replaced with calls to these files, while
preserving the current probing order of blkid.

Thanks,
Kay

2007-09-26 19:07:51

by Karel Zak

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On Wed, Sep 26, 2007 at 01:05:33PM +0100, Christoph Hellwig wrote:
> > BTW, I don't like this syntax in the fstab file AT ALL, but it is in
> > use in the wild by at least some Fedora users, and it's not documented
> > in the fstab man page. I'd suggest using a filesystem type of bind,
> > rather than ext3, as the officially "blessed" way of specifying it in
> > fstab, but it badly needs to be documented in the fstab and/or mount
> > man pages. The above patch should probably get included, though, and
> > backwards compatibility for allowing "bind" to be specified in the
> > mount options, and with a warning message that the specifying "bind"
> > in the options field has been deprecated.
>
> The syntax is indeed horrible. Is it supported by upstream util-linux?

Sure, it's supported.

mount(8) supports the "bind" as standard mount option (since
util-linux 2.10). There is not difference between options from fstab
and command line.

> I've started looking into this, and I think at least for the detect
> which filesystem we have part libblkid is complete overkill. libvolume_id
> has a really nice lowlevel API for that that is much more suitable.

It depends.. for example I think that call blindly all probing
functions is overkill. The libblkid is firstly trying to recognize FS
type by magic string.

> So if it was up to me I'd do the following:
>
> - move libvolume_id out of udev
> - make mount/fsck use libvolume_id unconditionally for detecting the
> filesystem type. There's absolute no reason to use anything in

unconditionally ... absolute no reason...

Too strong words, especially when there are distributions that depend
on some features from libblkid (for example device-mapper support).

> libblkid for this, and caching the result doesn't help us at all
> as we're going to touch the disk anyway as part of the mount/fsck.

I agree that the cache should be optional, rather than mandatory. For
example for FS type detection is it overkill, but for LABEL/UUID
translation is it good thing (especially on systems without
/dev/disk/by-*).

> Another note on moving the libraries into util-linux vs a standalone package:
> At least in xfs land people do upgrade xfsprogs frequently and sometimes
> independent os the distro because new features get added quite a bit, including
> new filesystem features that require support. Having to upgrade util-linux
> for that is not very helpful. So I'm not so sure about moving this to
> util-linux

I think we can always change this concept in dependence on feedback
from distributors/developers.

Karel

--
Karel Zak <[email protected]>

2007-09-26 22:54:31

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On Sep 26, 2007 13:05 +0100, Christoph Hellwig wrote:
> On Wed, Sep 26, 2007 at 06:59:46AM -0400, Theodore Tso wrote:
> > BTW, I don't like this syntax in the fstab file AT ALL, but it is in
> > use in the wild by at least some Fedora users, and it's not documented
> > in the fstab man page. I'd suggest using a filesystem type of bind,
> > rather than ext3, as the officially "blessed" way of specifying it in
> > fstab, but it badly needs to be documented in the fstab and/or mount
> > man pages. The above patch should probably get included, though, and
> > backwards compatibility for allowing "bind" to be specified in the
> > mount options, and with a warning message that the specifying "bind"
> > in the options field has been deprecated.
>
> The syntax is indeed horrible. Is it supported by upstream util-linux?

I think the issue is that if a filesystem is put into /etc/fstab with
type = "bind" then it will show up in /etc/mtab as "bind" and if the
original filesystem is unmounted the "mount" output will not show the
correct type anymore (I tested this with "bind" and "reiserfs" on an
ext3 filesystem).

Cheers, Andreas
--
Andreas Dilger
Principal Software Engineer
Cluster File Systems, Inc.

2007-09-27 00:16:27

by Theodore Y. Ts'o

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On Wed, Sep 26, 2007 at 01:05:33PM +0100, Christoph Hellwig wrote:
> - make mount/fsck use libvolume_id unconditionally for detecting the
> filesystem type. There's absolute no reason to use anything in
> libblkid for this, and caching the result doesn't help us at all
> as we're going to touch the disk anyway as part of the mount/fsck.
> - make libblkid use libvolume_id internally for filesystem detection

No, that's not true, since on a non-udev system (and there are those,
including a number of kernel hackers, who don't use udev for a number
of reasons --- like not trusting the sysfs/udev breakages, and who
want a static /dev) you need an efficient way to do lookup by UUID or
by LABEL without needing to search every single device. If you have a
some large storage box with 30,000 LUN's, you don't want to search
them all at boot time, whether you are using udev, especially if you
are only mounting a small subset of the images on demand. So there
will be circumstances where you really will want to use the cache, and
*not* want to use /dev/disk/by-* as the lookup mechanism.

- Ted

2009-02-18 16:42:54

by Karel Zak

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux


The fsck from the latest e2fsprogs git is util-linux-ng git now. It's
possible to link it against libblkid (from e2fsprogs or
util-linux-ng) and libvolume_id.

Karel

On Tue, Sep 25, 2007 at 04:37:35PM +0100, Christoph Hellwig wrote:
> This adds fsck from latest e2fsprogs git to util-linux. There are only

On Wed, Feb 06, 2008 at 12:38:59PM +0100, Matthias Koenig wrote:
> Here is another attempt to move fsck over to the util-linux-ng
> tree. A similar attempt has been done previously by Christoph
> Hellwig some time ago.

--
Karel Zak <[email protected]>

2009-02-19 18:42:22

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH, RFC] add fsck to util-linux

On Wed, Feb 18, 2009 at 05:42:54PM +0100, Karel Zak wrote:
>
> The fsck from the latest e2fsprogs git is util-linux-ng git now. It's

Did you speak to Ted to make sure it's from the right branch? I
remember that the one on the master branch wasn't the latests one when
I did the first patch.