/*
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
* fsck --- 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
* 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o.
*
* Copyright (C) 2009-2014 Karel Zak <kzak@redhat.com>
- *
- * This file may be redistributed under the terms of the GNU Public
- * License.
*/
-
#define _XOPEN_SOURCE 600 /* for inclusion of sa_handler in Solaris */
#include <sys/types.h>
#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
};
/*
- * Internal structure for mount tabel entries.
+ * Internal structure for mount table entries.
*/
struct fsck_fs_data
{
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;
-static volatile int cancel_requested;
+static volatile sig_atomic_t cancel_requested;
static int kill_sent;
static char *fstype;
static struct fsck_instance *instance_list;
long l;
char *p;
+ errno = 0;
l = strtol(s, &p, 0);
- if (*p || l == LONG_MIN || l == LONG_MAX || l < 0 || l > INT_MAX)
+ if (errno || *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? */
"/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");
inst->lockpath = NULL;
}
free(diskpath);
- return;
}
static void unlock_disk(struct fsck_instance *inst)
printf(_("Unlocking %s.\n"), inst->lockpath);
close(inst->lock); /* unlock */
- unlink(inst->lockpath);
free(inst->lockpath);
free(i->lockpath);
mnt_unref_fs(i->fs);
free(i);
- return;
}
static struct libmnt_fs *add_dummy_fs(const char *device)
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;
}
/*
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");
}
/* 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)
*/
static void print_stats(struct fsck_instance *inst)
{
- struct timeval delta;
+ struct timeval delta = { 0 };
if (!inst || !report_stats || noexecute)
return;
timersub(&inst->end_time, &inst->start_time, &delta);
- fprintf(stdout, "%s: status %d, rss %ld, "
- "real %ld.%06ld, user %d.%06d, sys %d.%06d\n",
- fs_get_device(inst->fs),
- inst->exit_status,
- inst->rusage.ru_maxrss,
- 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,
- (int)inst->rusage.ru_stime.tv_usec);
+ if (report_stats_file)
+ fprintf(report_stats_file, "%s %d %ld"
+ " %"PRId64".%06"PRId64
+ " %"PRId64".%06"PRId64
+ " %"PRId64".%06"PRId64"\n",
+ fs_get_device(inst->fs),
+ inst->exit_status,
+ inst->rusage.ru_maxrss,
+ (int64_t)delta.tv_sec, (int64_t)delta.tv_usec,
+ (int64_t)inst->rusage.ru_utime.tv_sec,
+ (int64_t)inst->rusage.ru_utime.tv_usec,
+ (int64_t)inst->rusage.ru_stime.tv_sec,
+ (int64_t)inst->rusage.ru_stime.tv_usec);
+ else
+ fprintf(stdout, "%s: status %d, rss %ld, "
+ "real %"PRId64".%06"PRId64", "
+ "user %"PRId64".%06"PRId64", "
+ "sys %"PRId64".%06"PRId64"\n",
+ fs_get_device(inst->fs),
+ inst->exit_status,
+ inst->rusage.ru_maxrss,
+ (int64_t)delta.tv_sec, (int64_t)delta.tv_usec,
+ (int64_t)inst->rusage.ru_utime.tv_sec,
+ (int64_t)inst->rusage.ru_utime.tv_usec,
+ (int64_t)inst->rusage.ru_stime.tv_sec,
+ (int64_t)inst->rusage.ru_stime.tv_usec);
}
/*
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;
+ argv[argc] = NULL;
if (verbose || noexecute) {
const char *tgt = mnt_fs_get_target(fs);
for (inst = instance_list; inst; inst = inst->next) {
if (inst->flags & FLAG_DONE)
continue;
+ if (inst->pid <= 0)
+ continue;
kill(inst->pid, signum);
n++;
}
if (noexecute) {
inst = instance_list;
- prev = 0;
+ prev = NULL;
#ifdef RANDOM_DEBUG
while (inst->next && (random() & 1)) {
prev = inst;
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)
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);
* If the type isn't specified by the user, then use either the type
* specified in /etc/fstab, or DEFAULT_FSTYPE.
*/
-static int fsck_device(struct libmnt_fs *fs, int interactive)
+static int fsck_device(struct libmnt_fs *fs, int interactive, int warn_notfound)
{
- char progname[80], *progpath;
+ char *progname, *progpath;
const char *type;
int retval;
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;
- sprintf(progname, "fsck.%s", type);
- progpath = find_fsck(progname);
- if (progpath == NULL) {
+ xasprintf(&progname, "fsck.%s", type);
+
+ if (!find_fsck(progname, &progpath)) {
+ free(progname);
if (fs_check_required(type)) {
retval = ENOENT;
goto err;
}
+ if (warn_notfound)
+ warnx(_("fsck.%s not found; ignore %s"), type,
+ fs_get_device(fs));
return 0;
}
num_running++;
retval = execute(progname, progpath, type, fs, interactive);
+ free(progname);
+ free(progpath);
if (retval) {
num_running--;
goto err;
}
return 0;
err:
- warnx(_("error %d (%m) while executing fsck.%s for %s"),
- retval, type, fs_get_device(fs));
+ warnx(_("error %d (%s) while executing fsck.%s for %s"),
+ retval, strerror(errno), 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;
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++) {
{
const char **ip, *type;
- if (mnt_fs_is_netfs(fs) || mnt_fs_is_pseudofs(fs) || mnt_fs_is_swaparea(fs))
+ if (!mnt_fs_is_regularfs(fs))
return 1;
type = mnt_fs_get_fstype(fs);
/* See if the <fsck.fs> program is available. */
- if (find_fsck(type) == NULL) {
+ if (!find_fsck(type, NULL)) {
if (fs_check_required(type))
warnx(_("cannot check %s: fsck.%s not found"),
fs_get_device(fs), type);
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;
* 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);
if (!skip_root &&
!fs_is_done(fs) &&
!(ignore_mounted && is_mounted(fs))) {
- status |= fsck_device(fs, 1);
+ status |= fsck_device(fs, 1, 0);
status |= wait_many(FLAG_WAIT_ALL);
if (status > FSCK_EX_NONDESTRUCT) {
mnt_free_iter(itr);
/*
* 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);
/*
* Spawn off the fsck process
*/
- status |= fsck_device(fs, serialize);
+ status |= fsck_device(fs, serialize, 0);
fs_set_done(fs);
/*
return status;
}
-static void __attribute__((__noreturn__)) usage(FILE *out)
+static void __attribute__((__noreturn__)) usage(void)
{
+ FILE *out = stdout;
fputs(USAGE_HEADER, out);
fprintf(out, _(" %s [options] -- [fs-options] [<filesystem> ...]\n"),
program_invocation_short_name);
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(_(" -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);
+ " <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);
+ fprintf(out, " -?, --help %s\n", USAGE_OPTSTR_HELP);
+ fprintf(out, " --version %s\n", USAGE_OPTSTR_VERSION);
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);
+ exit(FSCK_EX_OK);
}
static void signal_cancel(int sig __attribute__((__unused__)))
{
- cancel_requested++;
+ cancel_requested = 1;
}
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"));
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(stderr);
+ 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(stderr);
+ errx(FSCK_EX_USAGE,
+ _("option '%s' requires an argument"), "-t");
fstype = xstrdup(tmp);
compile_fs_type(fstype, &fs_type_compiled);
goto next_arg;
opts_for_fsck++;
break;
case '?':
- usage(stdout);
+ usage();
break;
default:
options[++opt] = arg[j];
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")))
- max_running = atoi(tmp);
+ if (ul_strtos32(getenv("FSCK_MAX_INST"), &max_running, 10) != 0)
+ max_running = 0;
}
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 */
+ if (mntcache)
+ /* Force libblkid to accept also filesystems with bad
+ * checksums. This feature is helpful for "fsck /dev/foo," but
+ * if it evaluates LABEL/UUIDs from fstab, then libmount may
+ * use cached data from udevd and udev accepts only properly
+ * detected filesystems.
+ */
+ mnt_cache_set_sbprobe(mntcache, BLKID_SUBLKS_BADCSUM);
+
+
parse_argv(argc, argv);
if (!notitle)
printf(UTIL_LINUX_VERSION);
+ signal(SIGCHLD, SIG_DFL); /* clear any inherited settings */
+
load_fs_info();
fsck_path = xstrdup(path && *path ? path : FSCK_DEFAULT_PATH);
continue;
if (ignore_mounted && is_mounted(fs))
continue;
- status |= fsck_device(fs, interactive);
+ status |= fsck_device(fs, interactive, interactive);
if (serialize ||
(max_running && (num_running >= max_running))) {
struct fsck_instance *inst;