]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/fsck.c
wipefs: add --lock and LOCK_BLOCK_DEVICE
[thirdparty/util-linux.git] / disk-utils / fsck.c
index 28a1d706b302870fd4c658dfd0d719ef4ca4abb7..fda80a6fe85be0c6fe3e8ec9f7ddb1cf8d5ca754 100644 (file)
@@ -19,7 +19,7 @@
  * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
  *              2001, 2002, 2003, 2004, 2005 by  Theodore Ts'o.
  *
- * Copyright (C) 2009, 2012 Karel Zak <kzak@redhat.com>
+ * Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com>
  *
  * This file may be redistributed under the terms of the GNU Public
  * License.
 #include "pathnames.h"
 #include "exitcodes.h"
 #include "c.h"
-#include "closestream.h"
+#include "fileutils.h"
+#include "monotonic.h"
+#include "strutils.h"
 
 #define XALLOC_EXIT_CODE       FSCK_EX_ERROR
 #include "xalloc.h"
 
+#define CLOSE_EXIT_CODE                FSCK_EX_ERROR
+#include "closestream.h"
+
 #ifndef DEFAULT_FSTYPE
 # define DEFAULT_FSTYPE        "ext2"
 #endif
@@ -64,6 +69,8 @@
 #define MAX_DEVICES 32
 #define MAX_ARGS 32
 
+#define FSCK_RUNTIME_DIRNAME   "/run/fsck"
+
 static const char *ignored_types[] = {
        "ignore",
        "iso9660",
@@ -78,14 +85,11 @@ static const char *really_wanted[] = {
        "ext4",
        "ext4dev",
        "jfs",
-       "reiserfs",
-       "xiafs",
-       "xfs",
-       NULL
+       "reiserfs"
 };
 
 /*
- * Internal structure for mount tabel entries.
+ * Internal structure for mount table entries.
  */
 struct fsck_fs_data
 {
@@ -102,7 +106,10 @@ struct fsck_fs_data
 struct fsck_instance {
        int     pid;
        int     flags;          /* FLAG_{DONE|PROGRESS} */
-       int     lock;           /* flock()ed whole disk file descriptor or -1 */
+
+       int     lock;           /* flock()ed lockpath file descriptor or -1 */
+       char    *lockpath;      /* /run/fsck/<diskname>.lock or NULL */
+
        int     exit_status;
        struct timeval start_time;
        struct timeval end_time;
@@ -137,6 +144,7 @@ static int progress;
 static int progress_fd;
 static int force_all_parallel;
 static int report_stats;
+static FILE *report_stats_file;
 
 static int num_running;
 static int max_running;
@@ -146,9 +154,10 @@ static int kill_sent;
 static char *fstype;
 static struct fsck_instance *instance_list;
 
-static const char fsck_prefix_path[] = FS_SEARCH_PATH;
+#define FSCK_DEFAULT_PATH "/sbin"
 static char *fsck_path;
 
+
 /* parsed fstab and mtab */
 static struct libmnt_table *fstab, *mtab;
 static struct libmnt_cache *mntcache;
@@ -163,8 +172,21 @@ static int string_to_int(const char *s)
        l = strtol(s, &p, 0);
        if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
                return -1;
-       else
-               return (int) l;
+
+       return (int) l;
+}
+
+/* Do we really really want to check this fs? */
+static int fs_check_required(const char *type)
+{
+       size_t i;
+
+       for(i = 0; i < ARRAY_SIZE(really_wanted); i++) {
+               if (strcmp(type, really_wanted[i]) == 0)
+                       return 1;
+       }
+
+       return 0;
 }
 
 static int is_mounted(struct libmnt_fs *fs)
@@ -294,7 +316,7 @@ static int is_irrotational_disk(dev_t disk)
                        "/sys/dev/block/%d:%d/queue/rotational",
                        major(disk), minor(disk));
 
-       if (rc < 0 || (unsigned int) (rc + 1) > sizeof(path))
+       if (rc < 0 || (unsigned int) rc >= sizeof(path))
                return 0;
 
        f = fopen(path, "r");
@@ -304,7 +326,7 @@ static int is_irrotational_disk(dev_t disk)
        rc = fscanf(f, "%d", &x);
        if (rc != 1) {
                if (ferror(f))
-                       warn(_("failed to read: %s"), path);
+                       warn(_("cannot read %s"), path);
                else
                        warnx(_("parse error: %s"), path);
        }
@@ -316,19 +338,40 @@ static int is_irrotational_disk(dev_t disk)
 static void lock_disk(struct fsck_instance *inst)
 {
        dev_t disk = fs_get_disk(inst->fs, 1);
-       char *diskname;
+       char *diskpath = NULL, *diskname;
+
+       inst->lock = -1;
 
        if (!disk || is_irrotational_disk(disk))
-               return;
+               goto done;
+
+       diskpath = blkid_devno_to_devname(disk);
+       if (!diskpath)
+               goto done;
+
+       if (access(FSCK_RUNTIME_DIRNAME, F_OK) != 0) {
+               int rc = mkdir(FSCK_RUNTIME_DIRNAME,
+                                   S_IWUSR|
+                                   S_IRUSR|S_IRGRP|S_IROTH|
+                                   S_IXUSR|S_IXGRP|S_IXOTH);
+               if (rc && errno != EEXIST) {
+                       warn(_("cannot create directory %s"),
+                                       FSCK_RUNTIME_DIRNAME);
+                       goto done;
+               }
+       }
 
-       diskname = blkid_devno_to_devname(disk);
+       diskname = stripoff_last_component(diskpath);
        if (!diskname)
-               return;
+               diskname = diskpath;
+
+       xasprintf(&inst->lockpath, FSCK_RUNTIME_DIRNAME "/%s.lock", diskname);
 
        if (verbose)
-               printf(_("Locking disk %s ... "), diskname);
+               printf(_("Locking disk by %s ... "), inst->lockpath);
 
-       inst->lock = open(diskname, O_CLOEXEC | O_RDONLY);
+       inst->lock = open(inst->lockpath, O_RDONLY|O_CREAT|O_CLOEXEC,
+                                   S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH);
        if (inst->lock >= 0) {
                int rc = -1;
 
@@ -348,21 +391,29 @@ static void lock_disk(struct fsck_instance *inst)
                /* TRANSLATORS: These are followups to "Locking disk...". */
                printf("%s.\n", inst->lock >= 0 ? _("succeeded") : _("failed"));
 
-       free(diskname);
-       return;
+
+done:
+       if (inst->lock < 0) {
+               free(inst->lockpath);
+               inst->lockpath = NULL;
+       }
+       free(diskpath);
 }
 
 static void unlock_disk(struct fsck_instance *inst)
 {
-       if (inst->lock >= 0) {
-               /* explicitly unlock, don't rely on close(), maybe some library
-                * (e.g. liblkid) has still open the device.
-                */
-               flock(inst->lock, LOCK_UN);
-               close(inst->lock);
+       if (inst->lock < 0)
+               return;
 
-               inst->lock = -1;
-       }
+       if (verbose)
+               printf(_("Unlocking %s.\n"), inst->lockpath);
+
+       close(inst->lock);                      /* unlock */
+
+       free(inst->lockpath);
+
+       inst->lock = -1;
+       inst->lockpath = NULL;
 }
 
 static void free_instance(struct fsck_instance *i)
@@ -370,8 +421,9 @@ static void free_instance(struct fsck_instance *i)
        if (lockdisk)
                unlock_disk(i);
        free(i->prog);
+       free(i->lockpath);
+       mnt_unref_fs(i->fs);
        free(i);
-       return;
 }
 
 static struct libmnt_fs *add_dummy_fs(const char *device)
@@ -379,10 +431,12 @@ static struct libmnt_fs *add_dummy_fs(const char *device)
        struct libmnt_fs *fs = mnt_new_fs();
 
        if (fs && mnt_fs_set_source(fs, device) == 0 &&
-                 mnt_table_add_fs(fstab, fs) == 0)
+                                 mnt_table_add_fs(fstab, fs) == 0) {
+               mnt_unref_fs(fs);
                return fs;
+       }
 
-       mnt_free_fs(fs);
+       mnt_unref_fs(fs);
        err(FSCK_EX_ERROR, _("failed to setup description for %s"), device);
 }
 
@@ -399,18 +453,22 @@ static void fs_interpret_type(struct libmnt_fs *fs)
        device = fs_get_device(fs);
        if (device) {
                int ambi = 0;
+               char *tp;
+               struct libmnt_cache *cache = mnt_table_get_cache(fstab);
 
-               type = mnt_get_fstype(device, &ambi, mnt_table_get_cache(fstab));
+               tp = mnt_get_fstype(device, &ambi, cache);
                if (!ambi)
-                       mnt_fs_set_fstype(fs, type);
+                       mnt_fs_set_fstype(fs, tp);
+               if (!cache)
+                       free(tp);
        }
 }
 
 static int parser_errcb(struct libmnt_table *tb __attribute__ ((__unused__)),
                        const char *filename, int line)
 {
-       warnx(_("%s: parse error at line %d -- ignore"), filename, line);
-       return 0;
+       warnx(_("%s: parse error at line %d -- ignored"), filename, line);
+       return 1;
 }
 
 /*
@@ -430,17 +488,21 @@ static void load_fs_info(void)
        errno = 0;
 
        /*
-        * Let's follow libmount defauls if $FSTAB_FILE is not specified
+        * Let's follow libmount defaults if $FSTAB_FILE is not specified
         */
        path = getenv("FSTAB_FILE");
 
        if (mnt_table_parse_fstab(fstab, path)) {
                if (!path)
                        path = mnt_get_fstab_path();
-               if (errno)
-                       warn(_("%s: failed to parse fstab"), path);
-               else
-                       warnx(_("%s: failed to parse fstab"), path);
+
+               /* don't print error when there is no fstab at all */
+               if (access(path, F_OK) == 0) {
+                       if (errno)
+                               warn(_("%s: failed to parse fstab"), path);
+                       else
+                               warnx(_("%s: failed to parse fstab"), path);
+               }
        }
 }
 
@@ -476,25 +538,34 @@ static struct libmnt_fs *lookup(char *path)
 }
 
 /* Find fsck program for a given fs type. */
-static char *find_fsck(const char *type)
+static int find_fsck(const char *type, char **progpath)
 {
        char *s;
        const char *tpl;
-       static char prog[256];
+       char *prog = NULL;
        char *p = xstrdup(fsck_path);
-       struct stat st;
+       int rc;
 
        /* 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)
+               xasprintf(&prog, tpl, s, type);
+               if (access(prog, X_OK) == 0)
                        break;
+               free(prog);
+               prog = NULL;
        }
+
        free(p);
+       rc = prog ? 1 : 0;
+
+       if (progpath)
+               *progpath = prog;
+       else
+               free(prog);
 
-       return(s ? prog : NULL);
+       return rc;
 }
 
 static int progress_active(void)
@@ -518,85 +589,89 @@ static int progress_active(void)
  */
 static void print_stats(struct fsck_instance *inst)
 {
-       double time_diff;
+       struct timeval delta;
 
        if (!inst || !report_stats || noexecute)
                return;
 
-       time_diff = (inst->end_time.tv_sec  - inst->start_time.tv_sec)
-                 + (inst->end_time.tv_usec - inst->start_time.tv_usec) / 1E6;
-
-       fprintf(stdout, "%s: status %d, rss %ld, "
-                       "real %f, user %d.%06d, sys %d.%06d\n",
-               fs_get_device(inst->fs),
-               inst->exit_status,
-               inst->rusage.ru_maxrss,
-               time_diff,
-               (int)inst->rusage.ru_utime.tv_sec,
-               (int)inst->rusage.ru_utime.tv_usec,
-               (int)inst->rusage.ru_stime.tv_sec,
-               (int)inst->rusage.ru_stime.tv_usec);
+       timersub(&inst->end_time, &inst->start_time, &delta);
+
+       if (report_stats_file)
+               fprintf(report_stats_file, "%s %d %ld "
+                                          "%ld.%06ld %ld.%06ld %ld.%06ld\n",
+                       fs_get_device(inst->fs),
+                       inst->exit_status,
+                       inst->rusage.ru_maxrss,
+                       (long)delta.tv_sec, (long)delta.tv_usec,
+                       (long)inst->rusage.ru_utime.tv_sec,
+                       (long)inst->rusage.ru_utime.tv_usec,
+                       (long)inst->rusage.ru_stime.tv_sec,
+                       (long)inst->rusage.ru_stime.tv_usec);
+       else
+               fprintf(stdout, "%s: status %d, rss %ld, "
+                               "real %ld.%06ld, user %ld.%06ld, sys %ld.%06ld\n",
+                       fs_get_device(inst->fs),
+                       inst->exit_status,
+                       inst->rusage.ru_maxrss,
+                       (long)delta.tv_sec, (long)delta.tv_usec,
+                       (long)inst->rusage.ru_utime.tv_sec,
+                       (long)inst->rusage.ru_utime.tv_usec,
+                       (long)inst->rusage.ru_stime.tv_sec,
+                       (long)inst->rusage.ru_stime.tv_usec);
 }
 
 /*
  * Execute a particular fsck program, and link it into the list of
  * child processes we are waiting for.
  */
-static int execute(const char *type, struct libmnt_fs *fs, int interactive)
+static int execute(const char *progname, const char *progpath,
+                  const char *type, struct libmnt_fs *fs, int interactive)
 {
-       char *s, *argv[80], prog[80];
+       char *argv[80];
        int  argc, i;
        struct fsck_instance *inst, *p;
        pid_t   pid;
 
        inst = xcalloc(1, sizeof(*inst));
 
-       sprintf(prog, "fsck.%s", type);
-       argv[0] = xstrdup(prog);
+       argv[0] = xstrdup(progname);
        argc = 1;
 
        for (i=0; i <num_args; i++)
                argv[argc++] = xstrdup(args[i]);
 
-       if (progress) {
-               if ((strcmp(type, "ext2") == 0) ||
-                   (strcmp(type, "ext3") == 0) ||
-                   (strcmp(type, "ext4") == 0) ||
-                   (strcmp(type, "ext4dev") == 0)) {
-                       char tmp[80];
-
-                       tmp[0] = 0;
-                       if (!progress_active()) {
-                               snprintf(tmp, 80, "-C%d", progress_fd);
-                               inst->flags |= FLAG_PROGRESS;
-                       } else if (progress_fd)
-                               snprintf(tmp, 80, "-C%d", progress_fd * -1);
-                       if (tmp[0])
-                               argv[argc++] = xstrdup(tmp);
-               }
+       if (progress &&
+              ((strcmp(type, "ext2") == 0) ||
+               (strcmp(type, "ext3") == 0) ||
+               (strcmp(type, "ext4") == 0) ||
+               (strcmp(type, "ext4dev") == 0))) {
+
+               char tmp[80];
+               tmp[0] = 0;
+               if (!progress_active()) {
+                       snprintf(tmp, 80, "-C%d", progress_fd);
+                       inst->flags |= FLAG_PROGRESS;
+               } else if (progress_fd)
+                       snprintf(tmp, 80, "-C%d", progress_fd * -1);
+               if (tmp[0])
+                       argv[argc++] = xstrdup(tmp);
        }
 
        argv[argc++] = xstrdup(fs_get_device(fs));
-       argv[argc] = 0;
-
-       s = find_fsck(prog);
-       if (s == NULL) {
-               warnx(_("%s: not found"), prog);
-               free(inst);
-               return ENOENT;
-       }
+       argv[argc] = NULL;
 
        if (verbose || noexecute) {
                const char *tgt = mnt_fs_get_target(fs);
 
                if (!tgt)
                        tgt = fs_get_device(fs);
-               printf("[%s (%d) -- %s] ", s, num_running, tgt);
+               printf("[%s (%d) -- %s] ", progpath, num_running, tgt);
                for (i=0; i < argc; i++)
                        printf("%s ", argv[i]);
                printf("\n");
        }
 
+       mnt_ref_fs(fs);
        inst->fs = fs;
        inst->lock = -1;
 
@@ -608,22 +683,22 @@ static int execute(const char *type, struct libmnt_fs *fs, int interactive)
                pid = -1;
        else if ((pid = fork()) < 0) {
                warn(_("fork failed"));
-               free(inst);
+               free_instance(inst);
                return errno;
        } else if (pid == 0) {
                if (!interactive)
                        close(0);
-               execv(s, argv);
-               err(FSCK_EX_ERROR, _("%s: execute failed"), s);
+               execv(progpath, argv);
+               err(FSCK_EX_ERROR, _("%s: execute failed"), progpath);
        }
 
        for (i=0; i < argc; i++)
                free(argv[i]);
 
        inst->pid = pid;
-       inst->prog = xstrdup(prog);
+       inst->prog = xstrdup(progname);
        inst->type = xstrdup(type);
-       gettimeofday(&inst->start_time, NULL);
+       gettime_monotonic(&inst->start_time);
        inst->next = NULL;
 
        /*
@@ -662,7 +737,7 @@ static int kill_all(int signum)
  */
 static struct fsck_instance *wait_one(int flags)
 {
-       int     status;
+       int     status = 0;
        int     sig;
        struct fsck_instance *inst, *inst2, *prev;
        pid_t   pid;
@@ -673,7 +748,7 @@ static struct fsck_instance *wait_one(int flags)
 
        if (noexecute) {
                inst = instance_list;
-               prev = 0;
+               prev = NULL;
 #ifdef RANDOM_DEBUG
                while (inst->next && (random() & 1)) {
                        prev = inst;
@@ -705,10 +780,10 @@ static struct fsck_instance *wait_one(int flags)
                                warnx(_("wait: no more child process?!?"));
                                return NULL;
                        }
-                       warn(_("waidpid failed"));
+                       warn(_("waitpid failed"));
                        continue;
                }
-               for (prev = 0, inst = instance_list;
+               for (prev = NULL, inst = instance_list;
                     inst;
                     prev = inst, inst = inst->next) {
                        if (inst->pid == pid)
@@ -736,7 +811,7 @@ static struct fsck_instance *wait_one(int flags)
 
        inst->exit_status = status;
        inst->flags |= FLAG_DONE;
-       gettimeofday(&inst->end_time, NULL);
+       gettime_monotonic(&inst->end_time);
        memcpy(&inst->rusage, &rusage, sizeof(struct rusage));
 
        if (progress && (inst->flags & FLAG_PROGRESS) &&
@@ -744,17 +819,17 @@ static struct fsck_instance *wait_one(int flags)
                for (inst2 = instance_list; inst2; inst2 = inst2->next) {
                        if (inst2->flags & FLAG_DONE)
                                continue;
-                       if (strcmp(inst2->type, "ext2") &&
+                       if (strcmp(inst2->type, "ext2") != 0 &&
                            strcmp(inst2->type, "ext3") &&
-                           strcmp(inst2->type, "ext4") &&
-                           strcmp(inst2->type, "ext4dev"))
+                           strcmp(inst2->type, "ext4") != 0 &&
+                           strcmp(inst2->type, "ext4dev") != 0)
                                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.tv_sec < time(0) + 2) {
+                       if (inst2->start_time.tv_sec < time(NULL) + 2) {
                                if (fork() == 0) {
                                        sleep(1);
                                        kill(inst2->pid, SIGUSR1);
@@ -818,6 +893,7 @@ static int wait_many(int flags)
  */
 static int fsck_device(struct libmnt_fs *fs, int interactive)
 {
+       char *progname, *progpath;
        const char *type;
        int retval;
 
@@ -827,29 +903,44 @@ static int fsck_device(struct libmnt_fs *fs, int interactive)
 
        if (type && strcmp(type, "auto") != 0)
                ;
-       else if (fstype && strncmp(fstype, "no", 2) &&
-           strncmp(fstype, "opts=", 5) && strncmp(fstype, "loop", 4) &&
+       else if (fstype && strncmp(fstype, "no", 2) != 0 &&
+           strncmp(fstype, "opts=", 5) != 0 && strncmp(fstype, "loop", 4) != 0 &&
            !strchr(fstype, ','))
                type = fstype;
        else
                type = DEFAULT_FSTYPE;
 
+       xasprintf(&progname, "fsck.%s", type);
+
+       if (!find_fsck(progname, &progpath)) {
+               free(progname);
+               if (fs_check_required(type)) {
+                       retval = ENOENT;
+                       goto err;
+               }
+               return 0;
+       }
+
        num_running++;
-       retval = execute(type, fs, interactive);
+       retval = execute(progname, progpath, type, fs, interactive);
+       free(progname);
+       free(progpath);
        if (retval) {
-               warnx(_("error %d while executing fsck.%s for %s"),
-                       retval, type, fs_get_device(fs));
                num_running--;
-               return FSCK_EX_ERROR;
+               goto err;
        }
        return 0;
+err:
+       warnx(_("error %d (%m) while executing fsck.%s for %s"),
+                       retval, type, fs_get_device(fs));
+       return FSCK_EX_ERROR;
 }
 
 
 /*
  * Deal with the fsck -t argument.
  */
-struct fs_type_compile {
+static struct fs_type_compile {
        char **list;
        int *type;
        int  negate;
@@ -947,7 +1038,7 @@ static int fs_match(struct libmnt_fs *fs, struct fs_type_compile *cmp)
        int n, ret = 0, checked_type = 0;
        char *cp;
 
-       if (cmp->list == 0 || cmp->list[0] == 0)
+       if (cmp->list == NULL || cmp->list[0] == NULL)
                return 1;
 
        for (n=0; (cp = cmp->list[n]); n++) {
@@ -1010,8 +1101,7 @@ static int fs_ignored_type(struct libmnt_fs *fs)
 /* Check if we should ignore this filesystem. */
 static int ignore(struct libmnt_fs *fs)
 {
-       const char **ip, *type;
-       int wanted = 0;
+       const char *type;
 
        /*
         * If the pass number is 0, ignore it.
@@ -1066,16 +1156,11 @@ static int ignore(struct libmnt_fs *fs)
        if (fs_ignored_type(fs))
                return 1;
 
-       /* Do we really really want to check this fs? */
-       for(ip = really_wanted; *ip; ip++)
-               if (strcmp(type, *ip) == 0) {
-                       wanted = 1;
-                       break;
-               }
+
 
        /* See if the <fsck.fs> program is available. */
-       if (find_fsck(type) == NULL) {
-               if (wanted)
+       if (!find_fsck(type, NULL)) {
+               if (fs_check_required(type))
                        warnx(_("cannot check %s: fsck.%s not found"),
                                fs_get_device(fs), type);
                return 1;
@@ -1099,7 +1184,7 @@ static int count_slaves(dev_t disk)
        if (!(dir = opendir(dirname)))
                return -1;
 
-       while ((dp = readdir(dir)) != 0) {
+       while ((dp = readdir(dir)) != NULL) {
 #ifdef _DIRENT_HAVE_D_TYPE
                if (dp->d_type != DT_UNKNOWN && dp->d_type != DT_LNK)
                        continue;
@@ -1141,7 +1226,7 @@ static int disk_already_active(struct libmnt_fs *fs)
         * Don't check a stacked device with any other disk too.
         */
        if (!disk || fs_is_stacked(fs))
-               return (instance_list != 0);
+               return (instance_list != NULL);
 
        for (inst = instance_list; inst; inst = inst->next) {
                dev_t idisk = fs_get_disk(inst->fs, 0);
@@ -1204,7 +1289,7 @@ static int check_all(void)
 
        /*
         * This is for the bone-headed user who enters the root
-        * filesystem twice.  Skip root will skep all root entries.
+        * filesystem twice.  Skip root will skip all root entries.
         */
        if (skip_root) {
                mnt_reset_iter(itr, MNT_ITER_FORWARD);
@@ -1295,28 +1380,37 @@ static int check_all(void)
 
 static void __attribute__((__noreturn__)) usage(void)
 {
-       printf(_("\nUsage:\n"
-                " %s [fsck-options] [fs-options] [filesys ...]\n"),
-               program_invocation_short_name);
-
-       puts(_( "\nOptions:\n"
-               " -A         check all filesystems\n"
-               " -R         skip root filesystem; useful only with `-A'\n"
-               " -M         do not check mounted filesystems\n"
-               " -t <type>  specify filesystem types to be checked;\n"
-               "              type is allowed to be comma-separated list\n"
-               " -P         check filesystems in parallel, including root\n"
-               " -r         report statistics for each device fsck\n"
-               " -s         serialize fsck operations\n"
-               " -l         lock the device using flock()\n"
-               " -N         do not execute, just show what would be done\n"
-               " -T         do not show the title on startup\n"
-               " -C <fd>    display progress bar; file descriptor is for GUIs\n"
-               " -V         explain what is being done\n"
-               " -?         display this help and exit\n\n"
-               "See fsck.* commands for fs-options."));
-
-       exit(FSCK_EX_USAGE);
+       FILE *out = stdout;
+       fputs(USAGE_HEADER, out);
+       fprintf(out, _(" %s [options] -- [fs-options] [<filesystem> ...]\n"),
+                        program_invocation_short_name);
+
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("Check and repair a Linux filesystem.\n"), out);
+
+       fputs(USAGE_OPTIONS, out);
+       fputs(_(" -A         check all filesystems\n"), out);
+       fputs(_(" -C [<fd>]  display progress bar; file descriptor is for GUIs\n"), out);
+       fputs(_(" -l         lock the device to guarantee exclusive access\n"), out);
+       fputs(_(" -M         do not check mounted filesystems\n"), out);
+       fputs(_(" -N         do not execute, just show what would be done\n"), out);
+       fputs(_(" -P         check filesystems in parallel, including root\n"), out);
+       fputs(_(" -R         skip root filesystem; useful only with '-A'\n"), out);
+       fputs(_(" -r [<fd>]  report statistics for each device checked;\n"
+               "            file descriptor is for GUIs\n"), out);
+       fputs(_(" -s         serialize the checking operations\n"), out);
+       fputs(_(" -T         do not show the title on startup\n"), out);
+       fputs(_(" -t <type>  specify filesystem types to be checked;\n"
+               "            <type> is allowed to be a comma-separated list\n"), out);
+       fputs(_(" -V         explain what is being done\n"), out);
+
+       fputs(USAGE_SEPARATOR, out);
+       printf( " -?, --help     %s\n", USAGE_OPTSTR_HELP);
+       printf( "     --version  %s\n", USAGE_OPTSTR_VERSION);
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("See the specific fsck.* commands for available fs-options."), out);
+       printf(USAGE_MAN_TAIL("fsck(8)"));
+       exit(FSCK_EX_OK);
 }
 
 static void signal_cancel(int sig __attribute__((__unused__)))
@@ -1327,28 +1421,36 @@ static void signal_cancel(int sig __attribute__((__unused__)))
 static void parse_argv(int argc, char *argv[])
 {
        int     i, j;
-       char    *arg, *dev, *tmp = 0;
+       char    *arg, *dev, *tmp = NULL;
        char    options[128];
        int     opt = 0;
        int     opts_for_fsck = 0;
        struct sigaction        sa;
+       int     report_stats_fd = -1;
 
        /*
         * Set up signal action
         */
        memset(&sa, 0, sizeof(struct sigaction));
        sa.sa_handler = signal_cancel;
-       sigaction(SIGINT, &sa, 0);
-       sigaction(SIGTERM, &sa, 0);
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
 
        num_devices = 0;
        num_args = 0;
-       instance_list = 0;
+       instance_list = NULL;
 
        for (i=1; i < argc; i++) {
                arg = argv[i];
                if (!arg)
                        continue;
+
+               /* the only two longopts to satisfy UL standards */
+               if (!opts_for_fsck && !strcmp(arg, "--help"))
+                       usage();
+               if (!opts_for_fsck && !strcmp(arg, "--version"))
+                       print_version(FSCK_EX_OK);
+
                if ((arg[0] == '/' && !opts_for_fsck) || strchr(arg, '=')) {
                        if (num_devices >= MAX_DEVICES)
                                errx(FSCK_EX_ERROR, _("too many devices"));
@@ -1398,15 +1500,14 @@ static void parse_argv(int argc, char *argv[])
                                break;
                        case 'C':
                                progress = 1;
-                               if (arg[j+1]) {
+                               if (arg[j+1]) {                                 /* -C<fd> */
                                        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]);
+                               } else if (i+1 < argc && *argv[i+1] != '-') {   /* -C <fd> */
+                                       progress_fd = string_to_int(argv[i+1]);
                                        if (progress_fd < 0)
                                                progress_fd = 0;
                                        else {
@@ -1438,20 +1539,30 @@ static void parse_argv(int argc, char *argv[])
                                break;
                        case 'r':
                                report_stats = 1;
+                               if (arg[j+1]) {                                 /* -r<fd> */
+                                       report_stats_fd = strtou32_or_err(arg+j+1, _("invalid argument of -r"));
+                                       goto next_arg;
+                               } else if (i+1 < argc && *argv[i+1] >= '0' && *argv[i+1] <= '9') {      /* -r <fd> */
+                                       report_stats_fd = strtou32_or_err(argv[i+1], _("invalid argument of -r"));
+                                       ++i;
+                                       goto next_arg;
+                               }
                                break;
                        case 's':
                                serialize = 1;
                                break;
                        case 't':
-                               tmp = 0;
+                               tmp = NULL;
                                if (fstype)
-                                       usage();
+                                       errx(FSCK_EX_USAGE,
+                                               _("option '%s' may be specified only once"), "-t");
                                if (arg[j+1])
                                        tmp = arg+j+1;
                                else if ((i+1) < argc)
                                        tmp = argv[++i];
                                else
-                                       usage();
+                                       errx(FSCK_EX_USAGE,
+                                               _("option '%s' requires an argument"), "-t");
                                fstype = xstrdup(tmp);
                                compile_fs_type(fstype, &fs_type_compiled);
                                goto next_arg;
@@ -1476,6 +1587,16 @@ static void parse_argv(int argc, char *argv[])
                        opt = 0;
                }
        }
+
+       /* Validate the report stats file descriptor to avoid disasters */
+       if (report_stats_fd >= 0) {
+               report_stats_file = fdopen(report_stats_fd, "w");
+               if (!report_stats_file)
+                       err(FSCK_EX_ERROR,
+                               _("invalid argument of -r: %d"),
+                               report_stats_fd);
+       }
+
        if (getenv("FSCK_FORCE_ALL_PARALLEL"))
                force_all_parallel++;
        if ((tmp = getenv("FSCK_MAX_INST")))
@@ -1486,8 +1607,8 @@ int main(int argc, char *argv[])
 {
        int i, status = 0;
        int interactive = 0;
-       char *oldpath = getenv("PATH");
        struct libmnt_fs *fs;
+       const char *path = getenv("PATH");
 
        setvbuf(stdout, NULL, _IONBF, BUFSIZ);
        setvbuf(stderr, NULL, _IONBF, BUFSIZ);
@@ -1496,28 +1617,20 @@ int main(int argc, char *argv[])
        setlocale(LC_CTYPE, "");
        bindtextdomain(PACKAGE, LOCALEDIR);
        textdomain(PACKAGE);
-       atexit(close_stdout);
+       close_stdout_atexit();
 
+       strutils_set_exitcode(FSCK_EX_USAGE);
        mnt_init_debug(0);              /* init libmount debug mask */
        mntcache = mnt_new_cache();     /* no fatal error if failed */
 
        parse_argv(argc, argv);
 
        if (!notitle)
-               printf(_("%s from %s\n"), program_invocation_short_name, PACKAGE_STRING);
+               printf(UTIL_LINUX_VERSION);
 
        load_fs_info();
 
-       /* Update our search path to include uncommon directories. */
-       if (oldpath) {
-               fsck_path = xmalloc (strlen (fsck_prefix_path) + 1 +
-                                   strlen (oldpath) + 1);
-               strcpy (fsck_path, fsck_prefix_path);
-               strcat (fsck_path, ":");
-               strcat (fsck_path, oldpath);
-       } else {
-               fsck_path = xstrdup(fsck_prefix_path);
-       }
+       fsck_path = xstrdup(path && *path ? path : FSCK_DEFAULT_PATH);
 
        if ((num_devices == 1) || (serialize))
                interactive = 1;
@@ -1550,7 +1663,6 @@ int main(int argc, char *argv[])
                        fs = add_dummy_fs(devices[i]);
                else if (fs_ignored_type(fs))
                        continue;
-
                if (ignore_mounted && is_mounted(fs))
                        continue;
                status |= fsck_device(fs, interactive);
@@ -1569,8 +1681,8 @@ int main(int argc, char *argv[])
        }
        status |= wait_many(FLAG_WAIT_ALL);
        free(fsck_path);
-       mnt_free_cache(mntcache);
-       mnt_free_table(fstab);
-       mnt_free_table(mtab);
+       mnt_unref_cache(mntcache);
+       mnt_unref_table(fstab);
+       mnt_unref_table(mtab);
        return status;
 }