]> git.ipfire.org Git - thirdparty/util-linux.git/blobdiff - disk-utils/fsck.c
fsck: use PATH or fallback to /sbin
[thirdparty/util-linux.git] / disk-utils / fsck.c
index 0d3fe5f595b79b0b3d15f4647ce92cd799f5d65f..299a775254d15ee855fe79b0bedf5bbba2561375 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.
@@ -53,6 +53,8 @@
 #include "exitcodes.h"
 #include "c.h"
 #include "closestream.h"
+#include "fileutils.h"
+#include "monotonic.h"
 
 #define XALLOC_EXIT_CODE       FSCK_EX_ERROR
 #include "xalloc.h"
@@ -64,6 +66,8 @@
 #define MAX_DEVICES 32
 #define MAX_ARGS 32
 
+#define FSCK_RUNTIME_DIRNAME   "/run/fsck"
+
 static const char *ignored_types[] = {
        "ignore",
        "iso9660",
@@ -78,10 +82,7 @@ static const char *really_wanted[] = {
        "ext4",
        "ext4dev",
        "jfs",
-       "reiserfs",
-       "xiafs",
-       "xfs",
-       NULL
+       "reiserfs"
 };
 
 /*
@@ -102,7 +103,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;
@@ -146,9 +150,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;
@@ -167,6 +172,19 @@ static int string_to_int(const char *s)
                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)
 {
        int rc;
@@ -304,7 +322,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 +334,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 +387,31 @@ 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);
+
+done:
+       if (inst->lock < 0) {
+               free(inst->lockpath);
+               inst->lockpath = NULL;
+       }
+       free(diskpath);
        return;
 }
 
 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 */
+       unlink(inst->lockpath);
+
+       free(inst->lockpath);
+
+       inst->lock = -1;
+       inst->lockpath = NULL;
 }
 
 static void free_instance(struct fsck_instance *i)
@@ -370,6 +419,8 @@ 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;
 }
@@ -379,10 +430,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,10 +452,14 @@ 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);
        }
 }
 
@@ -437,10 +494,14 @@ static void load_fs_info(void)
        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);
+               }
        }
 }
 
@@ -518,20 +579,19 @@ 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;
+       timersub(&inst->end_time, &inst->start_time, &delta);
 
        fprintf(stdout, "%s: status %d, rss %ld, "
-                       "real %f, user %d.%06d, sys %d.%06d\n",
+                       "real %ld.%06ld, user %d.%06d, sys %d.%06d\n",
                fs_get_device(inst->fs),
                inst->exit_status,
                inst->rusage.ru_maxrss,
-               time_diff,
+               delta.tv_sec, delta.tv_usec,
                (int)inst->rusage.ru_utime.tv_sec,
                (int)inst->rusage.ru_utime.tv_usec,
                (int)inst->rusage.ru_stime.tv_sec,
@@ -542,17 +602,17 @@ static void print_stats(struct fsck_instance *inst)
  * 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++)
@@ -579,24 +639,18 @@ static int execute(const char *type, struct libmnt_fs *fs, int interactive)
        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;
-       }
-
        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 +662,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 +716,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;
@@ -705,7 +759,7 @@ 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;
@@ -736,7 +790,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) &&
@@ -818,6 +872,7 @@ static int wait_many(int flags)
  */
 static int fsck_device(struct libmnt_fs *fs, int interactive)
 {
+       char progname[80], *progpath;
        const char *type;
        int retval;
 
@@ -834,15 +889,27 @@ static int fsck_device(struct libmnt_fs *fs, int interactive)
        else
                type = DEFAULT_FSTYPE;
 
+       sprintf(progname, "fsck.%s", type);
+       progpath = find_fsck(progname);
+       if (progpath == NULL) {
+               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);
        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;
 }
 
 
@@ -1010,8 +1077,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 +1132,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 (fs_check_required(type))
                        warnx(_("cannot check %s: fsck.%s not found"),
                                fs_get_device(fs), type);
                return 1;
@@ -1293,30 +1354,36 @@ static int check_all(void)
        return status;
 }
 
-static void __attribute__((__noreturn__)) usage(void)
+static void __attribute__((__noreturn__)) usage(FILE *out)
 {
-       printf(_("\nUsage:\n"));
-       printf(_(" %s [options] [fs-options] [<filesystem>...]\n"),
-                program_invocation_short_name);
-
-       puts(_( "\nOptions:\n"));
-       puts(_( " -A         check all filesystems\n"));
-       puts(_( " -C [<fd>]  display progress bar; file descriptor is for GUIs\n"));
-       puts(_( " -l         lock the device to guarantee exclusive access\n"));
-       puts(_( " -M         do not check mounted filesystems\n"));
-       puts(_( " -N         do not execute, just show what would be done\n"));
-       puts(_( " -P         check filesystems in parallel, including root\n"));
-       puts(_( " -R         skip root filesystem; useful only with '-A'\n"));
-       puts(_( " -r         report statistics for each device checked\n"));
-       puts(_( " -s         serialize the checking operations\n"));
-       puts(_( " -T         do not show the title on startup\n"));
-       puts(_( " -t <type>  specify filesystem types to be checked;\n"
-               "              <type> is allowed to be a comma-separated list\n"));
-       puts(_( " -V         explain what is being done\n"));
-       puts(_( " -?         display this help and exit\n\n"));
-       puts(_( "See the specific fsck.* commands for available fs-options."));
-
-       exit(FSCK_EX_USAGE);
+       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         report statistics for each device checked\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(_(" -?         display this help and exit\n"), out);
+
+       fputs(USAGE_SEPARATOR, out);
+       fputs(_("See the specific fsck.* commands for available fs-options."), out);
+       fprintf(out, USAGE_MAN_TAIL("fsck(8)"));
+
+       exit(out == stderr ? FSCK_EX_USAGE : FSCK_EX_OK);
 }
 
 static void signal_cancel(int sig __attribute__((__unused__)))
@@ -1398,15 +1465,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 {
@@ -1445,13 +1511,13 @@ static void parse_argv(int argc, char *argv[])
                        case 't':
                                tmp = 0;
                                if (fstype)
-                                       usage();
+                                       usage(stderr);
                                if (arg[j+1])
                                        tmp = arg+j+1;
                                else if ((i+1) < argc)
                                        tmp = argv[++i];
                                else
-                                       usage();
+                                       usage(stderr);
                                fstype = xstrdup(tmp);
                                compile_fs_type(fstype, &fs_type_compiled);
                                goto next_arg;
@@ -1459,7 +1525,7 @@ static void parse_argv(int argc, char *argv[])
                                opts_for_fsck++;
                                break;
                        case '?':
-                               usage();
+                               usage(stdout);
                                break;
                        default:
                                options[++opt] = arg[j];
@@ -1486,8 +1552,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);
@@ -1508,16 +1574,7 @@ int main(int argc, char *argv[])
 
        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 +1607,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 +1625,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;
 }