*/
#include "kerncompat.h"
+#include "androidcompat.h"
#include <sys/ioctl.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/syscall.h>
#include <poll.h>
#include <sys/file.h>
#include <uuid/uuid.h>
#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
+#include <limits.h>
#include "ctree.h"
#include "ioctl.h"
u64 duration;
u64 finished;
u64 canceled;
+ int in_progress;
};
+/* TBD: replace with #include "linux/ioprio.h" in some years */
+#if !defined (IOPRIO_H)
+#define IOPRIO_WHO_PROCESS 1
+#define IOPRIO_CLASS_SHIFT 13
+#define IOPRIO_PRIO_VALUE(class, data) \
+ (((class) << IOPRIO_CLASS_SHIFT) | (data))
+#define IOPRIO_CLASS_IDLE 3
+#endif
+
struct scrub_progress {
struct btrfs_ioctl_scrub_args scrub_args;
int fd;
struct scrub_file_record *resumed;
int ioctl_errno;
pthread_mutex_t progress_mutex;
+ int ioprio_class;
+ int ioprio_classdata;
};
struct scrub_file_record {
printf("\tlast_physical: %lld\n", sp->last_physical);
}
-#define ERR(test, ...) do { \
- if (test) \
- fprintf(stderr, __VA_ARGS__); \
-} while (0)
-
#define PRINT_SCRUB_ERROR(test, desc) do { \
if (test) \
printf(" %s=%llu", desc, test); \
{
u64 err_cnt;
u64 err_cnt2;
- char *bytes;
err_cnt = p->read_errors +
p->csum_errors +
if (p->malloc_errors)
printf("*** WARNING: memory allocation failed while scrubbing. "
"results may be inaccurate\n");
- bytes = pretty_sizes(p->data_bytes_scrubbed + p->tree_bytes_scrubbed);
- printf("\ttotal bytes scrubbed: %s with %llu errors\n", bytes,
+
+ printf("\ttotal bytes scrubbed: %s with %llu errors\n",
+ pretty_size(p->data_bytes_scrubbed + p->tree_bytes_scrubbed),
max(err_cnt, err_cnt2));
- free(bytes);
+
if (err_cnt || err_cnt2) {
printf("\terror details:");
PRINT_SCRUB_ERROR(p->read_errors, "read");
{
char t[4096];
struct tm tm;
+ time_t seconds;
+ unsigned hours;
if (!ss || !ss->t_start) {
printf("\tno stats available\n");
t[sizeof(t) - 1] = '\0';
printf("\tscrub started at %s", t);
}
- if (ss->finished && !ss->canceled) {
- printf(" and finished after %llu seconds\n",
- ss->duration);
- } else if (ss->canceled) {
- printf(" and was aborted after %llu seconds\n",
- ss->duration);
- } else {
- printf(", running for %llu seconds\n", ss->duration);
- }
+
+ seconds = ss->duration;
+ hours = ss->duration / (60 * 60);
+ gmtime_r(&seconds, &tm);
+ strftime(t, sizeof(t), "%M:%S", &tm);
+ if (ss->in_progress)
+ printf(", running for %02u:%s\n", hours, t);
+ else if (ss->canceled)
+ printf(" and was aborted after %02u:%s\n", hours, t);
+ else if (ss->finished)
+ printf(" and finished after %02u:%s\n", hours, t);
+ else
+ printf(", interrupted after %02u:%s, not running\n",
+ hours, t);
}
static void print_scrub_dev(struct btrfs_ioctl_dev_info_args *di,
static void free_history(struct scrub_file_record **last_scrubs)
{
struct scrub_file_record **l = last_scrubs;
- if (!l)
+ if (!l || IS_ERR(l))
return;
while (*l)
free(*l++);
static int scrub_open_file_r(const char *fn_base, const char *fn_local)
{
int ret;
- char datafile[BTRFS_PATH_NAME_MAX + 1];
+ char datafile[PATH_MAX];
ret = scrub_datafile(fn_base, fn_local, NULL,
datafile, sizeof(datafile));
if (ret < 0)
const char *tmp)
{
int ret;
- char datafile[BTRFS_PATH_NAME_MAX + 1];
+ char datafile[PATH_MAX];
ret = scrub_datafile(fn_base, fn_local, tmp,
datafile, sizeof(datafile));
if (ret < 0)
const char *tmp)
{
int ret;
- char datafile_old[BTRFS_PATH_NAME_MAX + 1];
- char datafile_new[BTRFS_PATH_NAME_MAX + 1];
+ char datafile_old[PATH_MAX];
+ char datafile_new[PATH_MAX];
ret = scrub_datafile(fn_base, fn_local, tmp,
datafile_old, sizeof(datafile_old));
if (ret < 0)
#define _SCRUB_INVALID do { \
if (report_errors) \
- fprintf(stderr, "WARNING: invalid data in line %d pos " \
- "%d state %d (near \"%.*s\") at %s:%d\n", \
+ warning("invalid data on line %d pos " \
+ "%d state %d (near \"%.*s\") at %s:%d", \
lineno, i, state, 20 > avail ? avail : 20, \
l + i, __FILE__, __LINE__); \
goto skip; \
char empty_uuid[BTRFS_FSID_SIZE] = {0};
struct scrub_file_record **p = NULL;
- if (fd < 0)
- return ERR_PTR(-EINVAL);
-
again:
old_avail = avail - i;
BUG_ON(old_avail < 0);
}
return p;
}
- if (avail == -1)
+ if (avail == -1) {
+ free_history(p);
return ERR_PTR(-errno);
+ }
avail += old_avail;
i = 0;
while (i < avail) {
+ void *tmp;
+
switch (state) {
case 0: /* start of file */
ret = scrub_kvread(&i,
continue;
}
++curr;
+ tmp = p;
p = realloc(p, (curr + 2) * sizeof(*p));
- if (p)
- p[curr] = malloc(sizeof(**p));
- if (!p || !p[curr])
+ if (!p) {
+ free_history(tmp);
+ return ERR_PTR(-errno);
+ }
+ p[curr] = malloc(sizeof(**p));
+ if (!p[curr]) {
+ free_history(p);
return ERR_PTR(-errno);
+ }
memset(p[curr], 0, sizeof(**p));
p[curr + 1] = NULL;
++state;
;
if (i + j + 1 >= avail)
_SCRUB_INVALID;
- if (j != 36)
+ if (j != BTRFS_UUID_UNPARSED_SIZE - 1)
_SCRUB_INVALID;
l[i + j] = '\0';
ret = uuid_parse(l + i, p[curr]->fsid);
int fd = -1;
int old;
- ret = pthread_mutex_lock(m);
+ ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
if (ret) {
err = -ret;
- goto out;
+ goto out3;
}
- ret = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old);
+ ret = pthread_mutex_lock(m);
if (ret) {
err = -ret;
- goto out;
+ goto out2;
}
fd = scrub_open_file_w(SCRUB_DATA_FILE, fsid, "tmp");
if (fd < 0) {
err = fd;
- goto out;
+ goto out1;
}
err = scrub_write_file(fd, fsid, data, n);
if (err)
- goto out;
+ goto out1;
err = scrub_rename_file(SCRUB_DATA_FILE, fsid, "tmp");
if (err)
- goto out;
+ goto out1;
-out:
+out1:
if (fd >= 0) {
ret = close(fd);
if (ret)
if (ret && !err)
err = -ret;
+out2:
ret = pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &old);
if (ret && !err)
err = -ret;
+out3:
return err;
}
sp->stats.duration = 0;
sp->stats.finished = 0;
+ ret = syscall(SYS_ioprio_set, IOPRIO_WHO_PROCESS, 0,
+ IOPRIO_PRIO_VALUE(sp->ioprio_class,
+ sp->ioprio_classdata));
+ if (ret)
+ warning("setting ioprio failed: %s (ignored)",
+ strerror(errno));
+
ret = ioctl(sp->fd, BTRFS_IOC_SCRUB, &sp->scrub_args);
gettimeofday(&tv, NULL);
sp->ret = ret;
/* nb: returns a negative errno via ERR_PTR */
static void *scrub_progress_cycle(void *ctx)
{
- int ret;
+ int ret = 0;
int perr = 0; /* positive / pthread error returns */
int old;
int i;
- char fsid[37];
+ char fsid[BTRFS_UUID_UNPARSED_SIZE];
struct scrub_progress *sp;
struct scrub_progress *sp_last;
struct scrub_progress *sp_shared;
* result we got for the current write and go
* on. flag should be set on next cycle, then.
*/
+ perr = pthread_setcancelstate(
+ PTHREAD_CANCEL_DISABLE, &old);
+ if (perr)
+ goto out;
perr = pthread_mutex_lock(&sp_shared->progress_mutex);
if (perr)
goto out;
&sp_shared->progress_mutex);
if (perr)
goto out;
+ perr = pthread_setcancelstate(
+ PTHREAD_CANCEL_ENABLE, &old);
+ if (perr)
+ goto out;
memcpy(sp, sp_last, sizeof(*sp));
continue;
}
perr = pthread_mutex_unlock(&sp_shared->progress_mutex);
if (perr)
goto out;
+ perr = pthread_setcancelstate(
+ PTHREAD_CANCEL_ENABLE, &old);
+ if (perr)
+ goto out;
memcpy(sp, sp_shared, sizeof(*sp));
memcpy(sp_last, sp_shared, sizeof(*sp));
}
return NULL;
}
-int mkdir_p(char *path)
+static int mkdir_p(char *path)
{
int i;
int ret;
path[i] = '\0';
ret = mkdir(path, 0777);
if (ret && errno != EEXIST)
- return 1;
+ return -errno;
path[i] = '/';
}
return 0;
}
+static int is_scrub_running_on_fs(struct btrfs_ioctl_fs_info_args *fi_args,
+ struct btrfs_ioctl_dev_info_args *di_args,
+ struct scrub_file_record **past_scrubs)
+{
+ int i;
+
+ if (!fi_args || !di_args || !past_scrubs)
+ return 0;
+
+ for (i = 0; i < fi_args->num_devices; i++) {
+ struct scrub_file_record *sfr =
+ last_dev_scrub(past_scrubs, di_args[i].devid);
+
+ if (!sfr)
+ continue;
+ if (!(sfr->stats.finished || sfr->stats.canceled))
+ return 1;
+ }
+ return 0;
+}
+
+static int is_scrub_running_in_kernel(int fd,
+ struct btrfs_ioctl_dev_info_args *di_args, u64 max_devices)
+{
+ struct scrub_progress sp;
+ int i;
+ int ret;
+
+ for (i = 0; i < max_devices; i++) {
+ memset(&sp, 0, sizeof(sp));
+ sp.scrub_args.devid = di_args[i].devid;
+ ret = ioctl(fd, BTRFS_IOC_SCRUB_PROGRESS, &sp.scrub_args);
+ if (!ret)
+ return 1;
+ }
+
+ return 0;
+}
+
static const char * const cmd_scrub_start_usage[];
static const char * const cmd_scrub_resume_usage[];
int do_record = 1;
int readonly = 0;
int do_stats_per_dev = 0;
+ int ioprio_class = IOPRIO_CLASS_IDLE;
+ int ioprio_classdata = 0;
int n_start = 0;
int n_skip = 0;
int n_resume = 0;
};
pthread_t *t_devs = NULL;
pthread_t t_prog;
- pthread_attr_t t_attr;
struct scrub_file_record **past_scrubs = NULL;
struct scrub_file_record *last_scrub = NULL;
char *datafile = strdup(SCRUB_DATA_FILE);
- char fsid[37];
- char sock_path[BTRFS_PATH_NAME_MAX + 1] = "";
+ char fsid[BTRFS_UUID_UNPARSED_SIZE];
+ char sock_path[PATH_MAX] = "";
struct scrub_progress_cycle spc;
pthread_mutex_t spc_write_mutex = PTHREAD_MUTEX_INITIALIZER;
void *terr;
u64 devid;
+ DIR *dirstream = NULL;
+ int force = 0;
+ int nothing_to_resume = 0;
optind = 1;
- while ((c = getopt(argc, argv, "BdqrR")) != -1) {
+ while ((c = getopt(argc, argv, "BdqrRc:n:f")) != -1) {
switch (c) {
case 'B':
do_background = 0;
case 'R':
print_raw = 1;
break;
+ case 'c':
+ ioprio_class = (int)strtol(optarg, NULL, 10);
+ break;
+ case 'n':
+ ioprio_classdata = (int)strtol(optarg, NULL, 10);
+ break;
+ case 'f':
+ force = 1;
+ break;
case '?':
default:
usage(resume ? cmd_scrub_resume_usage :
do_print = 0;
if (mkdir_p(datafile)) {
- ERR(!do_quiet, "WARNING: cannot create scrub data "
- "file, mkdir %s failed: %s. Status recording "
- "disabled\n", datafile, strerror(errno));
+ warning_on(!do_quiet,
+ "cannot create scrub data file, mkdir %s failed: %s. Status recording disabled",
+ datafile, strerror(errno));
do_record = 0;
}
free(datafile);
path = argv[optind];
- fdmnt = open_file_or_dir(path);
- if (fdmnt < 0) {
- ERR(!do_quiet, "ERROR: can't access '%s'\n", path);
- return 12;
- }
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, !do_quiet);
+ if (fdmnt < 0)
+ return 1;
- ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
+ ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
- ERR(!do_quiet, "ERROR: getting dev info for scrub failed: "
- "%s\n", strerror(-ret));
+ error_on(!do_quiet,
+ "getting dev info for scrub failed: %s",
+ strerror(-ret));
err = 1;
goto out;
}
if (!fi_args.num_devices) {
- ERR(!do_quiet, "ERROR: no devices found\n");
+ error_on(!do_quiet, "no devices found");
err = 1;
goto out;
}
uuid_unparse(fi_args.fsid, fsid);
fdres = scrub_open_file_r(SCRUB_DATA_FILE, fsid);
if (fdres < 0 && fdres != -ENOENT) {
- ERR(!do_quiet, "WARNING: failed to open status file: "
- "%s\n", strerror(-fdres));
+ warning_on(!do_quiet, "failed to open status file: %s",
+ strerror(-fdres));
} else if (fdres >= 0) {
past_scrubs = scrub_read_file(fdres, !do_quiet);
if (IS_ERR(past_scrubs))
- ERR(!do_quiet, "WARNING: failed to read status file: "
- "%s\n", strerror(-PTR_ERR(past_scrubs)));
+ warning_on(!do_quiet, "failed to read status file: %s",
+ strerror(-PTR_ERR(past_scrubs)));
close(fdres);
}
+ /*
+ * Check for stale information in the status file, ie. if it's
+ * canceled=0, finished=0 but no scrub is running.
+ */
+ if (!is_scrub_running_in_kernel(fdmnt, di_args, fi_args.num_devices))
+ force = 1;
+
+ /*
+ * check whether any involved device is already busy running a
+ * scrub. This would cause damaged status messages and the state
+ * "aborted" without the explanation that a scrub was already
+ * running. Therefore check it first, prevent it and give some
+ * feedback to the user if scrub is already running.
+ * Note that if scrub is started with a block device as the
+ * parameter, only that particular block device is checked. It
+ * is a normal mode of operation to start scrub on multiple
+ * single devices, there is no reason to prevent this.
+ */
+ if (!force && is_scrub_running_on_fs(&fi_args, di_args, past_scrubs)) {
+ error_on(!do_quiet,
+ "Scrub is already running.\n"
+ "To cancel use 'btrfs scrub cancel %s'.\n"
+ "To see the status use 'btrfs scrub status [-d] %s'",
+ path, path);
+ err = 1;
+ goto out;
+ }
+
t_devs = malloc(fi_args.num_devices * sizeof(*t_devs));
sp = calloc(fi_args.num_devices, sizeof(*sp));
spc.progress = calloc(fi_args.num_devices * 2, sizeof(*spc.progress));
if (!t_devs || !sp || !spc.progress) {
- ERR(!do_quiet, "ERROR: scrub failed: %s", strerror(errno));
- err = 1;
- goto out;
- }
-
- ret = pthread_attr_init(&t_attr);
- if (ret) {
- ERR(!do_quiet, "ERROR: pthread_attr_init failed: %s\n",
- strerror(ret));
+ error_on(!do_quiet, "scrub failed: %s", strerror(errno));
err = 1;
goto out;
}
devid = di_args[i].devid;
ret = pthread_mutex_init(&sp[i].progress_mutex, NULL);
if (ret) {
- ERR(!do_quiet, "ERROR: pthread_mutex_init failed: "
- "%s\n", strerror(ret));
+ error_on(!do_quiet, "pthread_mutex_init failed: %s",
+ strerror(ret));
err = 1;
goto out;
}
sp[i].skip = 0;
sp[i].scrub_args.end = (u64)-1ll;
sp[i].scrub_args.flags = readonly ? BTRFS_SCRUB_READONLY : 0;
+ sp[i].ioprio_class = ioprio_class;
+ sp[i].ioprio_classdata = ioprio_classdata;
}
if (!n_start && !n_resume) {
if (!do_quiet)
printf("scrub: nothing to resume for %s, fsid %s\n",
path, fsid);
- err = 0;
+ nothing_to_resume = 1;
goto out;
}
ret = connect(prg_fd, (struct sockaddr *)&addr, sizeof(addr));
if (!ret || errno != ECONNREFUSED) {
/* ... yes, so scrub must be running. error out */
- fprintf(stderr, "ERROR: scrub already running\n");
+ error("scrub already running");
close(prg_fd);
prg_fd = -1;
goto out;
if (ret != -1)
ret = listen(prg_fd, 100);
if (ret == -1) {
- ERR(!do_quiet, "WARNING: failed to open the progress status "
- "socket at %s: %s. Progress cannot be queried\n",
- sock_path[0] ? sock_path : SCRUB_PROGRESS_SOCKET_PATH,
- strerror(errno));
+ warning_on(!do_quiet,
+ "failed to open the progress status socket at %s: %s. Progress cannot be queried",
+ sock_path[0] ? sock_path :
+ SCRUB_PROGRESS_SOCKET_PATH, strerror(errno));
if (prg_fd != -1) {
close(prg_fd);
prg_fd = -1;
ret = scrub_write_progress(&spc_write_mutex, fsid, sp,
fi_args.num_devices);
if (ret) {
- ERR(!do_quiet, "WARNING: failed to write the progress "
- "status file: %s. Status recording disabled\n",
- strerror(-ret));
+ warning_on(!do_quiet,
+ "failed to write the progress status file: %s. Status recording disabled",
+ strerror(-ret));
do_record = 0;
}
}
if (do_background) {
pid = fork();
if (pid == -1) {
- ERR(!do_quiet, "ERROR: cannot scrub, fork failed: "
- "%s\n", strerror(errno));
+ error_on(!do_quiet, "cannot scrub, fork failed: %s",
+ strerror(errno));
err = 1;
goto out;
}
}
ret = wait(&stat);
if (ret != pid) {
- ERR(!do_quiet, "ERROR: wait failed: (ret=%d) "
- "%s\n", ret, strerror(errno));
+ error_on(!do_quiet, "wait failed (ret=%d): %s",
+ ret, strerror(errno));
err = 1;
goto out;
}
if (!WIFEXITED(stat) || WEXITSTATUS(stat)) {
- ERR(!do_quiet, "ERROR: scrub process failed\n");
+ error_on(!do_quiet, "scrub process failed");
err = WIFEXITED(stat) ? WEXITSTATUS(stat) : -1;
goto out;
}
devid = di_args[i].devid;
gettimeofday(&tv, NULL);
sp[i].stats.t_start = tv.tv_sec;
- ret = pthread_create(&t_devs[i], &t_attr,
+ ret = pthread_create(&t_devs[i], NULL,
scrub_one_dev, &sp[i]);
if (ret) {
if (do_print)
- fprintf(stderr, "ERROR: creating "
- "scrub_one_dev[%llu] thread failed: "
- "%s\n", devid, strerror(ret));
+ error("creating scrub_one_dev[%llu] thread failed: %s",
+ devid, strerror(ret));
err = 1;
goto out;
}
spc.write_mutex = &spc_write_mutex;
spc.shared_progress = sp;
spc.fi = &fi_args;
- ret = pthread_create(&t_prog, &t_attr, scrub_progress_cycle, &spc);
+ ret = pthread_create(&t_prog, NULL, scrub_progress_cycle, &spc);
if (ret) {
if (do_print)
- fprintf(stderr, "ERROR: creating progress thread "
- "failed: %s\n", strerror(ret));
+ error("creating progress thread failed: %s",
+ strerror(ret));
err = 1;
goto out;
}
ret = pthread_join(t_devs[i], NULL);
if (ret) {
if (do_print)
- fprintf(stderr, "ERROR: pthread_join failed "
- "for scrub_one_dev[%llu]: %s\n", devid,
- strerror(ret));
+ error("pthread_join failed for scrub_one_dev[%llu]: %s",
+ devid, strerror(ret));
++err;
continue;
}
- if (sp[i].ret && sp[i].ioctl_errno == ENODEV) {
- if (do_print)
- fprintf(stderr, "WARNING: device %lld not "
- "present\n", devid);
- continue;
- }
- if (sp[i].ret && sp[i].ioctl_errno == ECANCELED) {
- ++err;
- } else if (sp[i].ret) {
- if (do_print)
- fprintf(stderr, "ERROR: scrubbing %s failed "
- "for device id %lld (%s)\n", path,
- devid, strerror(sp[i].ioctl_errno));
- ++err;
- continue;
+ if (sp[i].ret) {
+ switch (sp[i].ioctl_errno) {
+ case ENODEV:
+ if (do_print)
+ warning("device %lld not present",
+ devid);
+ continue;
+ case ECANCELED:
+ ++err;
+ break;
+ default:
+ if (do_print)
+ error("scrubbing %s failed for device id %lld: ret=%d, errno=%d (%s)",
+ path, devid,
+ sp[i].ret, sp[i].ioctl_errno,
+ strerror(sp[i].ioctl_errno));
+ ++err;
+ continue;
+ }
}
if (sp[i].scrub_args.progress.uncorrectable_errors > 0)
e_uncorrectable++;
/* check for errors from the handling of the progress thread */
if (do_print && ret) {
- fprintf(stderr, "ERROR: progress thread handling failed: %s\n",
+ error("progress thread handling failed: %s",
strerror(ret));
}
/* check for errors returned from the progress thread itself */
- if (do_print && terr && terr != PTHREAD_CANCELED) {
- fprintf(stderr, "ERROR: recording progress "
- "failed: %s\n", strerror(-PTR_ERR(terr)));
- }
+ if (do_print && terr && terr != PTHREAD_CANCELED)
+ error("recording progress failed: %s",
+ strerror(-PTR_ERR(terr)));
if (do_record) {
ret = scrub_write_progress(&spc_write_mutex, fsid, sp,
fi_args.num_devices);
- if (ret && do_print) {
- fprintf(stderr, "ERROR: failed to record the result: "
- "%s\n", strerror(-ret));
- }
+ if (ret && do_print)
+ error("failed to record the result: %s",
+ strerror(-ret));
}
scrub_handle_sigint_child(-1);
if (sock_path[0])
unlink(sock_path);
}
- close(fdmnt);
+ close_file_or_dir(fdmnt, dirstream);
if (err)
return 1;
+ if (nothing_to_resume)
+ return 2;
+ if (e_uncorrectable) {
+ error_on(!do_quiet, "there are uncorrectable errors");
+ return 3;
+ }
if (e_correctable)
- return 7;
- if (e_uncorrectable)
- return 8;
+ warning_on(!do_quiet,
+ "errors detected during scrubbing, corrected");
+
return 0;
}
static const char * const cmd_scrub_start_usage[] = {
- "btrfs scrub start [-Bdqr] <path>|<device>",
- "Start a new scrub",
+ "btrfs scrub start [-BdqrRf] [-c ioprio_class -n ioprio_classdata] <path>|<device>",
+ "Start a new scrub. If a scrub is already running, the new one fails.",
"",
"-B do not background",
"-d stats per device (-B only)",
"-q be quiet",
"-r read only mode",
+ "-R raw print mode, print full data instead of summary",
+ "-c set ioprio class (see ionice(1) manpage)",
+ "-n set ioprio classdata (see ionice(1) manpage)",
+ "-f force starting new scrub even if a scrub is already running",
+ " this is useful when scrub stats record file is damaged",
NULL
};
char *path;
int ret;
int fdmnt = -1;
+ DIR *dirstream = NULL;
+
+ clean_args_no_options(argc, argv, cmd_scrub_cancel_usage);
- if (check_argc_exact(argc, 2))
+ if (check_argc_exact(argc - optind, 1))
usage(cmd_scrub_cancel_usage);
- path = argv[1];
+ path = argv[optind];
- fdmnt = open_path_or_dev_mnt(path);
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
if (fdmnt < 0) {
- fprintf(stderr, "ERROR: could not open %s: %s\n",
- path, strerror(errno));
ret = 1;
goto out;
}
ret = ioctl(fdmnt, BTRFS_IOC_SCRUB_CANCEL, NULL);
if (ret < 0) {
- fprintf(stderr, "ERROR: scrub cancel failed on %s: %s\n", path,
+ error("scrub cancel failed on %s: %s", path,
errno == ENOTCONN ? "not running" : strerror(errno));
- ret = 1;
+ if (errno == ENOTCONN)
+ ret = 2;
+ else
+ ret = 1;
goto out;
}
printf("scrub cancelled\n");
out:
- if (fdmnt != -1)
- close(fdmnt);
+ close_file_or_dir(fdmnt, dirstream);
return ret;
}
static const char * const cmd_scrub_resume_usage[] = {
- "btrfs scrub resume [-Bdqr] <path>|<device>",
+ "btrfs scrub resume [-BdqrR] [-c ioprio_class -n ioprio_classdata] <path>|<device>",
"Resume previously canceled or interrupted scrub",
"",
"-B do not background",
"-d stats per device (-B only)",
"-q be quiet",
"-r read only mode",
+ "-R raw print mode, print full data instead of summary",
+ "-c set ioprio class (see ionice(1) manpage)",
+ "-n set ioprio classdata (see ionice(1) manpage)",
NULL
};
struct sockaddr_un addr = {
.sun_family = AF_UNIX,
};
+ int in_progress;
int ret;
int i;
int fdmnt;
int print_raw = 0;
int do_stats_per_dev = 0;
int c;
- char fsid[37];
+ char fsid[BTRFS_UUID_UNPARSED_SIZE];
int fdres = -1;
int err = 0;
+ DIR *dirstream = NULL;
optind = 1;
while ((c = getopt(argc, argv, "dR")) != -1) {
path = argv[optind];
- fdmnt = open_file_or_dir(path);
- if (fdmnt < 0) {
- fprintf(stderr, "ERROR: can't access to '%s'\n", path);
- return 12;
- }
+ fdmnt = open_path_or_dev_mnt(path, &dirstream, 1);
+ if (fdmnt < 0)
+ return 1;
- ret = get_fs_info(fdmnt, path, &fi_args, &di_args);
+ ret = get_fs_info(path, &fi_args, &di_args);
if (ret) {
- fprintf(stderr, "ERROR: getting dev info for scrub failed: "
- "%s\n", strerror(-ret));
+ error("getting dev info for scrub failed: %s",
+ strerror(-ret));
err = 1;
goto out;
}
if (!fi_args.num_devices) {
- fprintf(stderr, "ERROR: no devices found\n");
+ error("no devices found");
err = 1;
goto out;
}
fdres = socket(AF_UNIX, SOCK_STREAM, 0);
if (fdres == -1) {
- fprintf(stderr, "ERROR: failed to create socket to "
- "receive progress information: %s\n",
+ error("failed to create socket to receive progress information: %s",
strerror(errno));
err = 1;
goto out;
close(fdres);
fdres = scrub_open_file_r(SCRUB_DATA_FILE, fsid);
if (fdres < 0 && fdres != -ENOENT) {
- fprintf(stderr, "WARNING: failed to open status file: "
- "%s\n", strerror(-fdres));
+ warning("failed to open status file: %s",
+ strerror(-fdres));
err = 1;
goto out;
}
if (fdres >= 0) {
past_scrubs = scrub_read_file(fdres, 1);
if (IS_ERR(past_scrubs))
- fprintf(stderr, "WARNING: failed to read status: %s\n",
+ warning("failed to read status: %s",
strerror(-PTR_ERR(past_scrubs)));
}
+ in_progress = is_scrub_running_in_kernel(fdmnt, di_args, fi_args.num_devices);
printf("scrub status for %s\n", fsid);
NULL, NULL);
continue;
}
+ last_scrub->stats.in_progress = in_progress;
print_scrub_dev(&di_args[i], &last_scrub->p, print_raw,
last_scrub->stats.finished ?
"history" : "status",
}
} else {
init_fs_stat(&fs_stat);
+ fs_stat.s.in_progress = in_progress;
for (i = 0; i < fi_args.num_devices; ++i) {
last_scrub = last_dev_scrub(past_scrubs,
di_args[i].devid);
free(di_args);
if (fdres > -1)
close(fdres);
+ close_file_or_dir(fdmnt, dirstream);
- return err;
+ return !!err;
}
+static const char scrub_cmd_group_info[] =
+"verify checksums of data and metadata";
+
const struct cmd_group scrub_cmd_group = {
- scrub_cmd_group_usage, NULL, {
+ scrub_cmd_group_usage, scrub_cmd_group_info, {
{ "start", cmd_scrub_start, cmd_scrub_start_usage, NULL, 0 },
{ "cancel", cmd_scrub_cancel, cmd_scrub_cancel_usage, NULL, 0 },
{ "resume", cmd_scrub_resume, cmd_scrub_resume_usage, NULL, 0 },
{ "status", cmd_scrub_status, cmd_scrub_status_usage, NULL, 0 },
- { 0, 0, 0, 0, 0 }
+ NULL_CMD_STRUCT
}
};