* 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 "exitcodes.h"
#include "c.h"
#include "closestream.h"
+#include "fileutils.h"
+#include "monotonic.h"
#define XALLOC_EXIT_CODE FSCK_EX_ERROR
#include "xalloc.h"
#define MAX_DEVICES 32
#define MAX_ARGS 32
+#define FSCK_RUNTIME_DIRNAME "/run/fsck"
+
static const char *ignored_types[] = {
"ignore",
"iso9660",
"ext4",
"ext4dev",
"jfs",
- "reiserfs",
- "xiafs",
- "xfs",
- NULL
+ "reiserfs"
};
/*
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;
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;
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;
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);
}
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;
/* 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)
if (lockdisk)
unlock_disk(i);
free(i->prog);
+ free(i->lockpath);
+ mnt_unref_fs(i->fs);
free(i);
return;
}
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);
}
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);
}
}
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);
+ }
}
}
*/
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,
* 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(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;
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;
/*
*/
static struct fsck_instance *wait_one(int flags)
{
- int status;
+ int status = 0;
int sig;
struct fsck_instance *inst, *inst2, *prev;
pid_t pid;
warnx(_("wait: no more child process?!?"));
return NULL;
}
- warn(_("waidpid failed"));
+ warn(_("waitpid failed"));
continue;
}
for (prev = 0, inst = instance_list;
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) &&
*/
static int fsck_device(struct libmnt_fs *fs, int interactive)
{
+ char progname[80], *progpath;
const char *type;
int retval;
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;
}
/* 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.
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;
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__)))
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 {
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;
opts_for_fsck++;
break;
case '?':
- usage();
+ usage(stdout);
break;
default:
options[++opt] = arg[j];
{
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);
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;
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);
}
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;
}