btrfs-progs: fix check of running scrub
[platform/upstream/btrfs-progs.git] / cmds-scrub.c
index 4338a0b..06c2a30 100644 (file)
@@ -59,6 +59,7 @@ struct scrub_stats {
        u64 duration;
        u64 finished;
        u64 canceled;
+       int in_progress;
 };
 
 /* TBD: replace with #include "linux/ioprio.h" in some years */
@@ -251,7 +252,11 @@ static void _print_scrub_ss(struct scrub_stats *ss)
                printf(" and was aborted after %llu seconds\n",
                       ss->duration);
        } else {
-               printf(", running for %llu seconds\n", ss->duration);
+               if (ss->in_progress)
+                       printf(", running for %llu seconds\n", ss->duration);
+               else
+                       printf(", interrupted after %llu seconds, not running\n",
+                                       ss->duration);
        }
 }
 
@@ -474,9 +479,6 @@ static struct scrub_file_record **scrub_read_file(int fd, int report_errors)
        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);
@@ -553,7 +555,7 @@ again:
                                ;
                        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);
@@ -865,7 +867,7 @@ static void *progress_one_dev(void *ctx)
 /* 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;
@@ -1060,6 +1062,24 @@ static int is_scrub_running_on_fs(struct btrfs_ioctl_fs_info_args *fi_args,
        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[];
 
@@ -1172,7 +1192,13 @@ static int scrub_start(int argc, char **argv, int resume)
        fdmnt = open_path_or_dev_mnt(path, &dirstream);
 
        if (fdmnt < 0) {
-               ERR(!do_quiet, "ERROR: can't access '%s'\n", path);
+               if (errno == EINVAL)
+                       ERR(!do_quiet,
+                           "ERROR: '%s' is not a mounted btrfs device\n",
+                           path);
+               else
+                       ERR(!do_quiet, "ERROR: can't access '%s': %s\n",
+                           path, strerror(errno));
                return 1;
        }
 
@@ -1203,6 +1229,13 @@ static int scrub_start(int argc, char **argv, int resume)
        }
 
        /*
+        * 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
@@ -1508,29 +1541,32 @@ out:
        }
        close_file_or_dir(fdmnt, dirstream);
 
-       if (nothing_to_resume)
-               return 2;
        if (err)
                return 1;
-       if (e_correctable)
+       if (nothing_to_resume)
+               return 2;
+       if (e_uncorrectable) {
+               ERR(!do_quiet, "ERROR: There are uncorrectable errors.\n");
                return 3;
-       if (e_uncorrectable)
-               return 4;
+       }
+       if (e_correctable)
+               ERR(!do_quiet, "WARNING: errors detected during scrubbing, corrected.\n");
+
        return 0;
 }
 
 static const char * const cmd_scrub_start_usage[] = {
        "btrfs scrub start [-BdqrRf] [-c ioprio_class -n ioprio_classdata] <path>|<device>",
-       "Start a new scrub",
+       "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"
+       "-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 to skip checking whether scrub has started/resumed in userspace ",
+       "-f     force starting new scrub even if a scrub is already running",
        "       this is useful when scrub stats record file is damaged",
        NULL
 };
@@ -1560,8 +1596,13 @@ static int cmd_scrub_cancel(int argc, char **argv)
 
        fdmnt = open_path_or_dev_mnt(path, &dirstream);
        if (fdmnt < 0) {
-               fprintf(stderr, "ERROR: could not open %s: %s\n",
-                       path, strerror(errno));
+               if (errno == EINVAL)
+                       fprintf(stderr,
+                               "ERROR: '%s' is not a mounted btrfs device\n",
+                               path);
+               else
+                       fprintf(stderr, "ERROR: can't access '%s': %s\n",
+                               path, strerror(errno));
                ret = 1;
                goto out;
        }
@@ -1594,6 +1635,7 @@ static const char * const cmd_scrub_resume_usage[] = {
        "-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
@@ -1624,6 +1666,7 @@ static int cmd_scrub_status(int argc, char **argv)
        struct sockaddr_un addr = {
                .sun_family = AF_UNIX,
        };
+       int in_progress;
        int ret;
        int i;
        int fdmnt;
@@ -1658,7 +1701,13 @@ static int cmd_scrub_status(int argc, char **argv)
        fdmnt = open_path_or_dev_mnt(path, &dirstream);
 
        if (fdmnt < 0) {
-               fprintf(stderr, "ERROR: can't access '%s'\n", path);
+               if (errno == EINVAL)
+                       fprintf(stderr,
+                               "ERROR: '%s' is not a mounted btrfs device\n",
+                               path);
+               else
+                       fprintf(stderr, "ERROR: can't access '%s': %s\n",
+                               path, strerror(errno));
                return 1;
        }
 
@@ -1707,6 +1756,7 @@ static int cmd_scrub_status(int argc, char **argv)
                        fprintf(stderr, "WARNING: failed to read status: %s\n",
                                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);
 
@@ -1719,6 +1769,7 @@ static int cmd_scrub_status(int argc, char **argv)
                                                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",
@@ -1726,6 +1777,7 @@ static int cmd_scrub_status(int argc, char **argv)
                }
        } 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);