Merge branch 'dedupe_bugfix' of https://github.com/bardavid/fio
[fio.git] / parse.c
diff --git a/parse.c b/parse.c
index 5d88d910e0ce6437ff302b469cb382eeffdb1e28..45f4f2d3dd6db800f4f8e11a168c8ef36e788d3b 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -37,6 +37,7 @@ static const char *opt_type_names[] = {
        "OPT_BOOL",
        "OPT_FLOAT_LIST",
        "OPT_STR_SET",
+       "OPT_STR_VAL_ZONE",
        "OPT_DEPRECATED",
        "OPT_SOFT_DEPRECATED",
        "OPT_UNSUPPORTED",
@@ -373,12 +374,16 @@ int str_to_decimal(const char *str, long long *val, int kilo, void *data,
 #endif
 
        if (rc == 1) {
+               char *endptr;
+
                if (strstr(str, "0x") || strstr(str, "0X"))
                        base = 16;
                else
                        base = 10;
 
-               *val = strtoll(str, NULL, base);
+               *val = strtoll(str, &endptr, base);
+               if (*val == 0 && endptr == str)
+                       return 1;
                if (*val == LONG_MAX && errno == ERANGE)
                        return 1;
        }
@@ -497,7 +502,7 @@ static int str_match_len(const struct value_pair *vp, const char *str)
 
 static const char *opt_type_name(const struct fio_option *o)
 {
-       compiletime_assert(ARRAY_SIZE(opt_type_names) - 1 == FIO_OPT_UNSUPPORTED,
+       compiletime_assert(FIO_ARRAY_SIZE(opt_type_names) - 1 == FIO_OPT_UNSUPPORTED,
                                "opt_type_names[] index");
 
        if (o->type <= FIO_OPT_UNSUPPORTED)
@@ -506,6 +511,33 @@ static const char *opt_type_name(const struct fio_option *o)
        return "OPT_UNKNOWN?";
 }
 
+static bool val_too_large(const struct fio_option *o, unsigned long long val,
+                         bool is_uint)
+{
+       if (!o->maxval)
+               return false;
+
+       if (is_uint) {
+               if ((int) val < 0)
+                       return (int) val > (int) o->maxval;
+               return (unsigned int) val > o->maxval;
+       }
+
+       return val > o->maxval;
+}
+
+static bool val_too_small(const struct fio_option *o, unsigned long long val,
+                         bool is_uint)
+{
+       if (!o->minval)
+               return false;
+
+       if (is_uint)
+               return (int) val < o->minval;
+
+       return val < o->minval;
+}
+
 static int __handle_option(const struct fio_option *o, const char *ptr,
                           void *data, int first, int more, int curr)
 {
@@ -565,18 +597,43 @@ static int __handle_option(const struct fio_option *o, const char *ptr,
        }
        case FIO_OPT_STR_VAL_TIME:
                is_time = 1;
-               /* fall through */
+               fallthrough;
        case FIO_OPT_ULL:
        case FIO_OPT_INT:
-       case FIO_OPT_STR_VAL: {
+       case FIO_OPT_STR_VAL:
+       case FIO_OPT_STR_VAL_ZONE:
+       {
                fio_opt_str_val_fn *fn = o->cb;
                char tmp[128], *p;
+               size_t len = strlen(ptr);
+
+               if (len > 0 && ptr[len - 1] == 'z') {
+                       if (o->type == FIO_OPT_STR_VAL_ZONE) {
+                               char *ep;
+                               unsigned long long val;
+
+                               errno = 0;
+                               val = strtoul(ptr, &ep, 10);
+                               if (errno == 0 && ep != ptr && *ep == 'z') {
+                                       ull = ZONE_BASE_VAL + (uint32_t)val;
+                                       ret = 0;
+                                       goto store_option_value;
+                               } else {
+                                       log_err("%s: unexpected zone value '%s'\n",
+                                               o->name, ptr);
+                                       return 1;
+                               }
+                       } else {
+                               log_err("%s: 'z' suffix isn't applicable\n",
+                                       o->name);
+                               return 1;
+                       }
+               }
 
                if (!is_time && o->is_time)
                        is_time = o->is_time;
 
-               tmp[sizeof(tmp) - 1] = '\0';
-               strncpy(tmp, ptr, sizeof(tmp) - 1);
+               snprintf(tmp, sizeof(tmp), "%s", ptr);
                p = strchr(tmp, ',');
                if (p)
                        *p = '\0';
@@ -595,14 +652,14 @@ static int __handle_option(const struct fio_option *o, const char *ptr,
                        return 1;
                }
 
-               if (o->maxval && ull > o->maxval) {
-                       log_err("max value out of range: %llu"
-                                       " (%llu max)\n", ull, o->maxval);
+               if (val_too_large(o, ull, o->type == FIO_OPT_INT)) {
+                       log_err("%s: max value out of range: %llu"
+                               " (%llu max)\n", o->name, ull, o->maxval);
                        return 1;
                }
-               if (o->minval && ull < o->minval) {
-                       log_err("min value out of range: %lld"
-                                       " (%d min)\n", ull, o->minval);
+               if (val_too_small(o, ull, o->type == FIO_OPT_INT)) {
+                       log_err("%s: min value out of range: %lld"
+                               " (%d min)\n", o->name, ull, o->minval);
                        return 1;
                }
                if (o->posval[0].ival) {
@@ -625,6 +682,7 @@ static int __handle_option(const struct fio_option *o, const char *ptr,
                        }
                }
 
+store_option_value:
                if (fn)
                        ret = fn(data, &ull);
                else {
@@ -756,6 +814,11 @@ static int __handle_option(const struct fio_option *o, const char *ptr,
                if (o->off1) {
                        cp = td_var(data, o, o->off1);
                        *cp = strdup(ptr);
+                       if (strlen(ptr) > o->maxlen - 1) {
+                               log_err("value exceeds max length of %d\n",
+                                       o->maxlen);
+                               return 1;
+                       }
                }
 
                if (fn)
@@ -802,8 +865,7 @@ static int __handle_option(const struct fio_option *o, const char *ptr,
                char tmp[128];
                char *p1, *p2;
 
-               tmp[sizeof(tmp) - 1] = '\0';
-               strncpy(tmp, ptr, sizeof(tmp) - 1);
+               snprintf(tmp, sizeof(tmp), "%s", ptr);
 
                /* Handle bsrange with separate read,write values: */
                p1 = strchr(tmp, ',');
@@ -912,7 +974,7 @@ static int __handle_option(const struct fio_option *o, const char *ptr,
        }
        case FIO_OPT_DEPRECATED:
                ret = 1;
-               /* fall through */
+               fallthrough;
        case FIO_OPT_SOFT_DEPRECATED:
                log_info("Option %s is deprecated\n", o->name);
                break;
@@ -1019,7 +1081,20 @@ struct fio_option *find_option(struct fio_option *options, const char *opt)
 const struct fio_option *
 find_option_c(const struct fio_option *options, const char *opt)
 {
-       return find_option((struct fio_option *)options, opt);
+       const struct fio_option *o;
+
+       for (o = &options[0]; o->name; o++) {
+               if (!o_match(o, opt))
+                       continue;
+               if (o->type == FIO_OPT_UNSUPPORTED) {
+                       log_err("Option <%s>: %s\n", o->name, o->help);
+                       continue;
+               }
+
+               return o;
+       }
+
+       return NULL;
 }
 
 static const struct fio_option *