2008-02-06 11:39:01

by Matthias Koenig

[permalink] [raw]
Subject: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

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.

Patch 1 is the initial move with minor adjustments for the
build system.
Patch 2 adds support for the fsprobe library wrapper.
Patch 3 adds a new option which has been in Suse for some
time to ignore mounted filesystems.

Matthias


2008-02-06 11:39:03

by Matthias Koenig

[permalink] [raw]
Subject: [PATCH 1/3] fsck: add fsck from e2fsprogs to util-linux-ng tree

Signed-off-by: Matthias Koenig <[email protected]>
---

Makefile.am | 1
configure.ac | 2
fsck/Makefile.am | 7
fsck/base_device.c | 169 +++++++
fsck/fsck.8.in | 410 ++++++++++++++++
fsck/fsck.c | 1309 ++++++++++++++++++++++++++++++++++++++++++++++++++++
fsck/fsck.h | 70 +++
7 files changed, 1968 insertions(+), 0 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 245f90e..e2b5ed1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3,6 +3,7 @@ include $(top_srcdir)/config/include-Makefile.am
SUBDIRS = \
include \
disk-utils \
+ fsck \
fdisk \
getopt \
lib \
diff --git a/configure.ac b/configure.ac
index a148e28..cacf790 100644
--- a/configure.ac
+++ b/configure.ac
@@ -578,6 +578,8 @@ AC_CONFIG_HEADERS(config.h)
AC_CONFIG_FILES([
Makefile
disk-utils/Makefile
+fsck/Makefile
+fsck/fsck.8
fdisk/Makefile
getopt/Makefile
hwclock/Makefile
diff --git a/fsck/Makefile.am b/fsck/Makefile.am
new file mode 100644
index 0000000..59523fc
--- /dev/null
+++ b/fsck/Makefile.am
@@ -0,0 +1,7 @@
+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
+
diff --git a/fsck/base_device.c b/fsck/base_device.c
new file mode 100644
index 0000000..6237d0e
--- /dev/null
+++ b/fsck/base_device.c
@@ -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
diff --git a/fsck/fsck.8.in b/fsck/fsck.8.in
new file mode 100644
index 0000000..4a12677
--- /dev/null
+++ b/fsck/fsck.8.in
@@ -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 "Feb 2008" "@PACKAGE_NAME@ version @PACKAGE_VERSION@"
+.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).
diff --git a/fsck/fsck.c b/fsck/fsck.c
new file mode 100644
index 0000000..6b39b21
--- /dev/null
+++ b/fsck/fsck.c
@@ -0,0 +1,1309 @@
+/*
+ * 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 <stdlib.h>
+#include <errno.h>
+#include <paths.h>
+#include <unistd.h>
+#include <errno.h>
+#include <malloc.h>
+#include <signal.h>
+
+#include "../config.h"
+#include "nls.h"
+#include "fsck.h"
+#include "blkid/blkid.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);
+ cp = line;
+
+ device = parse_word(&cp);
+ if (!device || *device == '#')
+ return 0; /* Ignore blank lines and comments */
+ mntpnt = parse_word(&cp);
+ type = parse_word(&cp);
+ opts = parse_word(&cp);
+ freq = parse_word(&cp);
+ passno = parse_word(&cp);
+
+ 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;
+
+ /*
+ * If this is a bind mount, ignore it.
+ */
+ if (opt_in_list("bind", fs->opts)) {
+ fprintf(stderr,
+ _("%s: skipping bad line in /etc/fstab: bind mount with nonzero fsck pass number\n"),
+ fs->mountpt);
+ 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);
+}
+
+#ifdef HAVE_SIGNAL_H
+static void signal_cancel(int sig FSCK_ATTR((unused)))
+{
+ cancel_requested++;
+}
+#endif
+
+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;
+#ifdef HAVE_SIGNAL_H
+ 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);
+#endif
+
+ 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_MESSAGES, "");
+ setlocale(LC_CTYPE, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+ blkid_get_cache(&cache, NULL);
+ PRS(argc, argv);
+
+ if (!notitle)
+ printf("fsck %s\n", PACKAGE_VERSION);
+
+ 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;
+}
diff --git a/fsck/fsck.h b/fsck/fsck.h
new file mode 100644
index 0000000..55cb525
--- /dev/null
+++ b/fsck/fsck.h
@@ -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);

2008-02-06 12:56:55

by Karel Zak

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

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.

Thanks (also thanks to Christoph)!

I didn't forget about this task. It's still in my TODO list.

It seems that the fsck wrapper in e2fsprogs is optional
(--enable-fsck) so we needn't any extra change to e2fsprogs.

It would be nice to add a note

"fsck is deprecated in favor of fsck from util-linux-ng"

to RELEASE-NOTES in e2fsprogs.

> Patch 1 is the initial move with minor adjustments for the
> build system.
> Patch 2 adds support for the fsprobe library wrapper.

What about to move the fsprobe library wrapper to lib/ ?

> Patch 3 adds a new option which has been in Suse for some
> time to ignore mounted filesystems.

We also need a --disable-fsck configure option.

Karel

--
Karel Zak <[email protected]>

2008-02-06 14:06:44

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

On Wed, Feb 06, 2008 at 01:56:51PM +0100, Karel Zak wrote:
> It would be nice to add a note
>
> "fsck is deprecated in favor of fsck from util-linux-ng"
>
> to RELEASE-NOTES in e2fsprogs.

I don't want to deprecate fsck quite yet from e2fsprogs, because there
are some ext4 issues with fsck (or more accurately, fsck's use of
vol_id instead of blkid), where it is more convenient if at least for
distros that are being aggressive with ext4 testing, if we don't try
to move fsck just now.

I'm working with Eric Sandeen from Red Hat to make sure that we can
get usable ext4 support for FC9 in rawhide, and making sure that
vol_id is doing the right thing with ext4 support is one more moving
part that I'd rather not have to coordinate with. So it's going to be
much simpler for now if we keep fsck in e2fsprogs, and what I'm
telling people who want to play with ext4 is that life will be much
simpler if they use the latest e2fsprogs and build mount so that it
uses blkid, and NOT vol_id.

Kay and I have had philosophical differences about how hard to try to
make in-line detection of filesystems work, and when I've tried to
submit patches that fine-tune filesystem id detection, by rejiggering
the order in which various filesystems types are tried (based on how
thoroughly their mkfs commands scrub the disk to remove false
positives for previous filesystems on the disk), they've been rejected
by Kay.

So these days, while I'll take any patches to improve blkid's
filesystem detection, I've largely stopped trying to send patches in
the other direction. And there are some rather delicate tests that
have been encoded into blkid to coordinate with ext4 and ext4dev
(since at some point when the code is production ready we will be
changing the filesystem name from ext4dev to ext4), it's just much
simpler if I tell people that in order to support ext4, the only
approach I've validated uses fsck from e2fsprogs, and mount compiled
to use libblkid.

I'd love to try to work on merging the functionality between vol_id
and blkid, and at some point that's great, but I just don't have the
time right now to try to coordinate yet another moving part.

- Ted

2008-02-06 15:23:10

by Karel Zak

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

On Wed, Feb 06, 2008 at 09:05:59AM -0500, Theodore Tso wrote:
> On Wed, Feb 06, 2008 at 01:56:51PM +0100, Karel Zak wrote:
> > It would be nice to add a note
> >
> > "fsck is deprecated in favor of fsck from util-linux-ng"
> >
> > to RELEASE-NOTES in e2fsprogs.
>
> I don't want to deprecate fsck quite yet from e2fsprogs, because there
> are some ext4 issues with fsck (or more accurately, fsck's use of
> vol_id instead of blkid), where it is more convenient if at least for
> distros that are being aggressive with ext4 testing, if we don't try
> to move fsck just now.

That's nothing urgent for me.

No problem to postpone this change until util-linux-ng 2.15 (at end
of this year). I believe ext4 will be pretty stable at this time :-)

> I'd love to try to work on merging the functionality between vol_id
> and blkid, and at some point that's great, but I just don't have the
> time right now to try to coordinate yet another moving part.

I'm already working on vol_id + blkid merge

http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=shortlog;h=topic/fsprobe

My plan is follow ideas and code from blkid and add a new low-level API
for udev. That's all.

--
Karel Zak <[email protected]>

2008-02-06 16:28:30

by maximilian attems

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

On Wed, Feb 06, 2008 at 04:23:10PM +0100, Karel Zak wrote:
> I'm already working on vol_id + blkid merge
>
> http://git.kernel.org/?p=utils/util-linux-ng/util-linux-ng.git;a=shortlog;h=topic/fsprobe
>
> My plan is follow ideas and code from blkid and add a new low-level API
> for udev. That's all.

could you also integrate the requirements for fstype klibc,
so that we can kill the third place of needed fs recognition patches.

hpa formulated them a while ago, searching,
http://www.zytor.com/pipermail/klibc/2006-February/001345.html

concerning initramfs volume_id added --probe-all, before it
used to guess wrongly, so currently we prefer klibc fstype.


2008-02-07 00:37:43

by Kay Sievers

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

On Feb 6, 2008 3:05 PM, Theodore Tso <[email protected]> wrote:
> On Wed, Feb 06, 2008 at 01:56:51PM +0100, Karel Zak wrote:
> > It would be nice to add a note
> >
> > "fsck is deprecated in favor of fsck from util-linux-ng"
> >
> > to RELEASE-NOTES in e2fsprogs.
>
> I don't want to deprecate fsck quite yet from e2fsprogs, because there
> are some ext4 issues with fsck (or more accurately, fsck's use of
> vol_id instead of blkid), where it is more convenient if at least for
> distros that are being aggressive with ext4 testing, if we don't try
> to move fsck just now.

Care to explain what ext4 development has to do with the generic fsck program?
I don''t see any convincing reason not to move that now.

Kay

2008-02-07 13:41:59

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

On Thu, Feb 07, 2008 at 01:37:43AM +0100, Kay Sievers wrote:
>
> Care to explain what ext4 development has to do with the generic
> fsck program? I don''t see any convincing reason not to move that
> now.

Fsck and mount (and especially mount) needs to be linked against the
very latest blkid library in order to make sure things work, and when
we spread out packages across the various different packages, it means
updating things to assure correct functionality is that much harder.

It also means getting the right ext4 detection routines into vol_id,
and in the past I've had real problems getting patches that fix real
bugs in vol_id past you, and I'm just too busy right now to deal with
that right now.

- Ted

2008-02-08 11:16:39

by Kay Sievers

[permalink] [raw]
Subject: Re: [PATCH 0/3] Move fsck from e2fsprogs to util-linux-ng

On Feb 7, 2008 2:41 PM, Theodore Tso <[email protected]> wrote:
> On Thu, Feb 07, 2008 at 01:37:43AM +0100, Kay Sievers wrote:
> >
> > Care to explain what ext4 development has to do with the generic
> > fsck program? I don''t see any convincing reason not to move that
> > now.
>
> Fsck and mount (and especially mount) needs to be linked against the
> very latest blkid library in order to make sure things work, and when
> we spread out packages across the various different packages, it means
> updating things to assure correct functionality is that much harder.
>
> It also means getting the right ext4 detection routines into vol_id,
> and in the past I've had real problems getting patches that fix real
> bugs in vol_id past you, and I'm just too busy right now to deal with
> that right now.

Sorry, some major distrost don't use blkid at all, so this does not apply
at all to the fact that fsck should not be part of util-linux now. Again provide
facts and stop complaining about the useless probe-reorder patches that
didn't make it into libvolume_id for good reason.

Kay