btrfs-progs: extend parse_range API to accept a relaxed range
[platform/upstream/btrfs-progs.git] / cmds-balance.c
index c555249..053cd9b 100644 (file)
 #include "volumes.h"
 
 #include "commands.h"
+#include "utils.h"
 
 static const char * const balance_cmd_group_usage[] = {
-       "btrfs [filesystem] balance <command> [options] <path>",
-       "btrfs [filesystem] balance <path>",
+       "btrfs balance <command> [options] <path>",
+       "btrfs balance <path>",
        NULL
 };
 
-static const char balance_cmd_group_info[] =
-       "'btrfs filesystem balance' command is deprecated, please use\n"
-       "'btrfs balance start' command instead.";
-
 static int parse_one_profile(const char *profile, u64 *flags)
 {
        if (!strcmp(profile, "raid0")) {
@@ -66,7 +63,7 @@ static int parse_one_profile(const char *profile, u64 *flags)
 static int parse_profiles(char *profiles, u64 *flags)
 {
        char *this_char;
-       char *save_ptr;
+       char *save_ptr = NULL; /* Satisfy static checkers */
 
        for (this_char = strtok_r(profiles, "|", &save_ptr);
             this_char != NULL;
@@ -91,41 +88,74 @@ static int parse_u64(const char *str, u64 *result)
        return 0;
 }
 
+/*
+ * Parse range that's missing some part that can be implicit:
+ * a..b        - exact range, a can be equal to b
+ * a.. - implicitly unbounded maximum (end == (u64)-1)
+ * ..b - implicitly starting at 0
+ * a   - invalid; unclear semantics, use parse_u64 instead
+ *
+ * Returned values are u64, value validation and interpretation should be done
+ * by the caller.
+ */
 static int parse_range(const char *range, u64 *start, u64 *end)
 {
        char *dots;
+       char *endptr;
+       const char *rest;
+       int skipped = 0;
 
        dots = strstr(range, "..");
-       if (dots) {
-               const char *rest = dots + 2;
-               int skipped = 0;
+       if (!dots)
+               return 1;
 
-               *dots = 0;
+       rest = dots + 2;
 
-               if (!*rest) {
-                       *end = (u64)-1;
-                       skipped++;
-               } else {
-                       if (parse_u64(rest, end))
-                               return 1;
-               }
-               if (dots == range) {
-                       *start = 0;
-                       skipped++;
-               } else {
-                       if (parse_u64(range, start))
-                               return 1;
-               }
+       if (!*rest) {
+               *end = (u64)-1;
+               skipped++;
+       } else {
+               *end = strtoull(rest, &endptr, 10);
+               if (*endptr)
+                       return 1;
+       }
+       if (dots == range) {
+               *start = 0;
+               skipped++;
+       } else {
+               *end = strtoull(range, &endptr, 10);
+               if (*endptr != 0 && *endptr != '.')
+                       return 1;
+       }
+
+       if (*start > *end) {
+               fprintf(stderr,
+                       "ERROR: range %llu..%llu doesn't make sense\n",
+                       (unsigned long long)*start,
+                       (unsigned long long)*end);
+               return 1;
+       }
+
+       if (skipped <= 1)
+               return 0;
 
+       return 1;
+}
+
+/*
+ * Parse range and check if start < end
+ */
+static int parse_range_strict(const char *range, u64 *start, u64 *end)
+{
+       if (parse_range(range, start, end) == 0) {
                if (*start >= *end) {
-                       fprintf(stderr, "Range %llu..%llu doesn't make "
-                               "sense\n", (unsigned long long)*start,
+                       fprintf(stderr,
+                               "ERROR: range %llu..%llu not allowed\n",
+                               (unsigned long long)*start,
                                (unsigned long long)*end);
                        return 1;
                }
-
-               if (skipped <= 1)
-                       return 0;
+               return 0;
        }
 
        return 1;
@@ -135,7 +165,7 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
 {
        char *this_char;
        char *value;
-       char *save_ptr;
+       char *save_ptr = NULL; /* Satisfy static checkers */
 
        if (!filters)
                return 0;
@@ -163,7 +193,7 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
                                return 1;
                        }
                        if (parse_u64(value, &args->usage) ||
-                           args->usage < 1 || args->usage > 100) {
+                           args->usage > 100) {
                                fprintf(stderr, "Invalid usage argument: %s\n",
                                       value);
                                return 1;
@@ -188,7 +218,7 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
                                       "an argument\n");
                                return 1;
                        }
-                       if (parse_range(value, &args->pstart, &args->pend)) {
+                       if (parse_range_strict(value, &args->pstart, &args->pend)) {
                                fprintf(stderr, "Invalid drange argument\n");
                                return 1;
                        }
@@ -199,7 +229,7 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
                                       "an argument\n");
                                return 1;
                        }
-                       if (parse_range(value, &args->vstart, &args->vend)) {
+                       if (parse_range_strict(value, &args->vstart, &args->vend)) {
                                fprintf(stderr, "Invalid vrange argument\n");
                                return 1;
                        }
@@ -217,6 +247,18 @@ static int parse_filters(char *filters, struct btrfs_balance_args *args)
                        args->flags |= BTRFS_BALANCE_ARGS_CONVERT;
                } else if (!strcmp(this_char, "soft")) {
                        args->flags |= BTRFS_BALANCE_ARGS_SOFT;
+               } else if (!strcmp(this_char, "limit")) {
+                       if (!value || !*value) {
+                               fprintf(stderr,
+                                       "the limit filter requires an argument\n");
+                               return 1;
+                       }
+                       if (parse_u64(value, &args->limit)) {
+                               fprintf(stderr, "Invalid limit argument: %s\n",
+                                      value);
+                               return 1;
+                       }
+                       args->flags |= BTRFS_BALANCE_ARGS_LIMIT;
                } else {
                        fprintf(stderr, "Unrecognized balance option '%s'\n",
                                this_char);
@@ -251,6 +293,8 @@ static void dump_balance_args(struct btrfs_balance_args *args)
                printf(", vrange=%llu..%llu",
                       (unsigned long long)args->vstart,
                       (unsigned long long)args->vend);
+       if (args->flags & BTRFS_BALANCE_ARGS_LIMIT)
+               printf(", limit=%llu", (unsigned long long)args->limit);
 
        printf("\n");
 }
@@ -293,12 +337,11 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
        int fd;
        int ret;
        int e;
+       DIR *dirstream = NULL;
 
-       fd = open_file_or_dir(path);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-               return 12;
-       }
+       fd = btrfs_open_dir(path, &dirstream, 1);
+       if (fd < 0)
+               return 1;
 
        ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, args);
        e = errno;
@@ -328,7 +371,7 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
                        if (e != EINPROGRESS)
                                fprintf(stderr, "There may be more info in "
                                        "syslog - try dmesg | tail\n");
-                       ret = 19;
+                       ret = 1;
                }
        } else {
                printf("Done, had to relocate %llu out of %llu chunks\n",
@@ -338,12 +381,12 @@ static int do_balance(const char *path, struct btrfs_ioctl_balance_args *args,
        }
 
 out:
-       close(fd);
+       close_file_or_dir(fd, dirstream);
        return ret;
 }
 
 static const char * const cmd_balance_start_usage[] = {
-       "btrfs [filesystem] balance start [options] <path>",
+       "btrfs balance start [options] <path>",
        "Balance chunks across the devices",
        "Balance and/or convert (change allocation profile of) chunks that",
        "passed all filters in a comma-separated list of filters for a",
@@ -353,7 +396,7 @@ static const char * const cmd_balance_start_usage[] = {
        "",
        "-d[filters]    act on data chunks",
        "-m[filters]    act on metadata chunks",
-       "-s[filetrs]    act on system chunks (only under -f)",
+       "-s[filters]    act on system chunks (only under -f)",
        "-v             be verbose",
        "-f             force reducing of metadata integrity",
        NULL
@@ -373,18 +416,16 @@ static int cmd_balance_start(int argc, char **argv)
 
        optind = 1;
        while (1) {
-               int longindex;
-               static struct option longopts[] = {
+               static const struct option longopts[] = {
                        { "data", optional_argument, NULL, 'd'},
                        { "metadata", optional_argument, NULL, 'm' },
                        { "system", optional_argument, NULL, 's' },
                        { "force", no_argument, NULL, 'f' },
                        { "verbose", no_argument, NULL, 'v' },
-                       { 0, 0, 0, 0 }
+                       { NULL, 0, NULL, 0 }
                };
 
-               int opt = getopt_long(argc, argv, "d::s::m::fv", longopts,
-                                     &longindex);
+               int opt = getopt_long(argc, argv, "d::s::m::fv", longopts, NULL);
                if (opt < 0)
                        break;
 
@@ -475,7 +516,7 @@ static int cmd_balance_start(int argc, char **argv)
 }
 
 static const char * const cmd_balance_pause_usage[] = {
-       "btrfs [filesystem] balance pause <path>",
+       "btrfs balance pause <path>",
        "Pause running balance",
        NULL
 };
@@ -486,33 +527,35 @@ static int cmd_balance_pause(int argc, char **argv)
        int fd;
        int ret;
        int e;
+       DIR *dirstream = NULL;
 
        if (check_argc_exact(argc, 2))
                usage(cmd_balance_pause_usage);
 
        path = argv[1];
 
-       fd = open_file_or_dir(path);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-               return 12;
-       }
+       fd = btrfs_open_dir(path, &dirstream, 1);
+       if (fd < 0)
+               return 1;
 
        ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_PAUSE);
        e = errno;
-       close(fd);
+       close_file_or_dir(fd, dirstream);
 
        if (ret < 0) {
                fprintf(stderr, "ERROR: balance pause on '%s' failed - %s\n",
                        path, (e == ENOTCONN) ? "Not running" : strerror(e));
-               return 19;
+               if (e == ENOTCONN)
+                       return 2;
+               else
+                       return 1;
        }
 
        return 0;
 }
 
 static const char * const cmd_balance_cancel_usage[] = {
-       "btrfs [filesystem] balance cancel <path>",
+       "btrfs balance cancel <path>",
        "Cancel running or paused balance",
        NULL
 };
@@ -523,33 +566,35 @@ static int cmd_balance_cancel(int argc, char **argv)
        int fd;
        int ret;
        int e;
+       DIR *dirstream = NULL;
 
        if (check_argc_exact(argc, 2))
                usage(cmd_balance_cancel_usage);
 
        path = argv[1];
 
-       fd = open_file_or_dir(path);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-               return 12;
-       }
+       fd = btrfs_open_dir(path, &dirstream, 1);
+       if (fd < 0)
+               return 1;
 
        ret = ioctl(fd, BTRFS_IOC_BALANCE_CTL, BTRFS_BALANCE_CTL_CANCEL);
        e = errno;
-       close(fd);
+       close_file_or_dir(fd, dirstream);
 
        if (ret < 0) {
                fprintf(stderr, "ERROR: balance cancel on '%s' failed - %s\n",
                        path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
-               return 19;
+               if (e == ENOTCONN)
+                       return 2;
+               else
+                       return 1;
        }
 
        return 0;
 }
 
 static const char * const cmd_balance_resume_usage[] = {
-       "btrfs [filesystem] balance resume <path>",
+       "btrfs balance resume <path>",
        "Resume interrupted balance",
        NULL
 };
@@ -558,6 +603,7 @@ static int cmd_balance_resume(int argc, char **argv)
 {
        struct btrfs_ioctl_balance_args args;
        const char *path;
+       DIR *dirstream = NULL;
        int fd;
        int ret;
        int e;
@@ -567,18 +613,16 @@ static int cmd_balance_resume(int argc, char **argv)
 
        path = argv[1];
 
-       fd = open_file_or_dir(path);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-               return 12;
-       }
+       fd = btrfs_open_dir(path, &dirstream, 1);
+       if (fd < 0)
+               return 1;
 
        memset(&args, 0, sizeof(args));
        args.flags |= BTRFS_BALANCE_RESUME;
 
        ret = ioctl(fd, BTRFS_IOC_BALANCE_V2, &args);
        e = errno;
-       close(fd);
+       close_file_or_dir(fd, dirstream);
 
        if (ret < 0) {
                if (e == ECANCELED) {
@@ -591,12 +635,15 @@ static int cmd_balance_resume(int argc, char **argv)
                                "failed - %s\n", path,
                                (e == ENOTCONN) ? "Not in progress" :
                                                  "Already running");
-                       return 19;
+                       if (e == ENOTCONN)
+                               return 2;
+                       else
+                               return 1;
                } else {
                        fprintf(stderr,
 "ERROR: error during balancing '%s' - %s\n"
 "There may be more info in syslog - try dmesg | tail\n", path, strerror(e));
-                       return 19;
+                       return 1;
                }
        } else {
                printf("Done, had to relocate %llu out of %llu chunks\n",
@@ -608,17 +655,24 @@ static int cmd_balance_resume(int argc, char **argv)
 }
 
 static const char * const cmd_balance_status_usage[] = {
-       "btrfs [filesystem] balance status [-v] <path>",
+       "btrfs balance status [-v] <path>",
        "Show status of running or paused balance",
        "",
        "-v     be verbose",
        NULL
 };
 
+/* Checks the status of the balance if any
+ * return codes:
+ *   2 : Error failed to know if there is any pending balance
+ *   1 : Successful to know status of a pending balance
+ *   0 : When there is no pending balance or completed
+ */
 static int cmd_balance_status(int argc, char **argv)
 {
        struct btrfs_ioctl_balance_args args;
        const char *path;
+       DIR *dirstream = NULL;
        int fd;
        int verbose = 0;
        int ret;
@@ -626,13 +680,13 @@ static int cmd_balance_status(int argc, char **argv)
 
        optind = 1;
        while (1) {
-               int longindex;
-               static struct option longopts[] = {
+               int opt;
+               static const struct option longopts[] = {
                        { "verbose", no_argument, NULL, 'v' },
-                       { 0, 0, 0, 0}
+                       { NULL, 0, NULL, 0 }
                };
 
-               int opt = getopt_long(argc, argv, "v", longopts, &longindex);
+               opt = getopt_long(argc, argv, "v", longopts, NULL);
                if (opt < 0)
                        break;
 
@@ -650,20 +704,22 @@ static int cmd_balance_status(int argc, char **argv)
 
        path = argv[optind];
 
-       fd = open_file_or_dir(path);
-       if (fd < 0) {
-               fprintf(stderr, "ERROR: can't access to '%s'\n", path);
-               return 12;
-       }
+       fd = btrfs_open_dir(path, &dirstream, 1);
+       if (fd < 0)
+               return 2;
 
        ret = ioctl(fd, BTRFS_IOC_BALANCE_PROGRESS, &args);
        e = errno;
-       close(fd);
+       close_file_or_dir(fd, dirstream);
 
        if (ret < 0) {
+               if (e == ENOTCONN) {
+                       printf("No balance found on '%s'\n", path);
+                       return 0;
+               }
                fprintf(stderr, "ERROR: balance status on '%s' failed - %s\n",
-                       path, (e == ENOTCONN) ? "Not in progress" : strerror(e));
-               return 19;
+                       path, strerror(e));
+               return 2;
        }
 
        if (args.state & BTRFS_BALANCE_STATE_RUNNING) {
@@ -687,9 +743,12 @@ static int cmd_balance_status(int argc, char **argv)
        if (verbose)
                dump_ioctl_balance_args(&args);
 
-       return 0;
+       return 1;
 }
 
+static const char balance_cmd_group_info[] =
+"balance data accross devices, or change block groups using filters";
+
 const struct cmd_group balance_cmd_group = {
        balance_cmd_group_usage, balance_cmd_group_info, {
                { "start", cmd_balance_start, cmd_balance_start_usage, NULL, 0 },
@@ -697,7 +756,7 @@ const struct cmd_group balance_cmd_group = {
                { "cancel", cmd_balance_cancel, cmd_balance_cancel_usage, NULL, 0 },
                { "resume", cmd_balance_resume, cmd_balance_resume_usage, NULL, 0 },
                { "status", cmd_balance_status, cmd_balance_status_usage, NULL, 0 },
-               { 0, 0, 0, 0, 0 }
+               NULL_CMD_STRUCT
        }
 };