btrfs-progs: fix check of running scrub
[platform/upstream/btrfs-progs.git] / cmds-scrub.c
index f9e2b40..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);
@@ -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[];
 
@@ -1209,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
@@ -1530,16 +1557,16 @@ out:
 
 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
 };
@@ -1608,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
@@ -1638,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;
@@ -1727,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);
 
@@ -1739,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",
@@ -1746,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);