static int rnbd_clt_parse_map_options()

in rnbd/rnbd-clt-sysfs.c [65:221]


static int rnbd_clt_parse_map_options(const char *buf, size_t max_path_cnt,
				       struct rnbd_map_options *opt)
{
	char *options, *sep_opt;
	char *p;
	substring_t args[MAX_OPT_ARGS];
	int opt_mask = 0;
	int token;
	int ret = -EINVAL;
	int nr_poll_queues = 0;
	int dest_port = 0;
	int p_cnt = 0;
	int i;

	options = kstrdup(buf, GFP_KERNEL);
	if (!options)
		return -ENOMEM;

	sep_opt = strstrip(options);
	while ((p = strsep(&sep_opt, " ")) != NULL) {
		if (!*p)
			continue;

		token = match_token(p, rnbd_opt_tokens, args);
		opt_mask |= token;

		switch (token) {
		case RNBD_OPT_SESSNAME:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			if (strlen(p) > NAME_MAX) {
				pr_err("map_device: sessname too long\n");
				ret = -EINVAL;
				kfree(p);
				goto out;
			}
			strscpy(opt->sessname, p, NAME_MAX);
			kfree(p);
			break;

		case RNBD_OPT_PATH:
			if (p_cnt >= max_path_cnt) {
				pr_err("map_device: too many (> %zu) paths provided\n",
				       max_path_cnt);
				ret = -ENOMEM;
				goto out;
			}
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}

			ret = rtrs_addr_to_sockaddr(p, strlen(p),
						    *opt->dest_port,
						    &opt->paths[p_cnt]);
			if (ret) {
				pr_err("Can't parse path %s: %d\n", p, ret);
				kfree(p);
				goto out;
			}

			p_cnt++;

			kfree(p);
			break;

		case RNBD_OPT_DEV_PATH:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}
			if (strlen(p) > NAME_MAX) {
				pr_err("map_device: Device path too long\n");
				ret = -EINVAL;
				kfree(p);
				goto out;
			}
			strscpy(opt->pathname, p, NAME_MAX);
			kfree(p);
			break;

		case RNBD_OPT_DEST_PORT:
			if (match_int(args, &dest_port) || dest_port < 0 ||
			    dest_port > 65535) {
				pr_err("bad destination port number parameter '%d'\n",
				       dest_port);
				ret = -EINVAL;
				goto out;
			}
			*opt->dest_port = dest_port;
			break;

		case RNBD_OPT_ACCESS_MODE:
			p = match_strdup(args);
			if (!p) {
				ret = -ENOMEM;
				goto out;
			}

			if (!strcmp(p, "ro")) {
				*opt->access_mode = RNBD_ACCESS_RO;
			} else if (!strcmp(p, "rw")) {
				*opt->access_mode = RNBD_ACCESS_RW;
			} else if (!strcmp(p, "migration")) {
				*opt->access_mode = RNBD_ACCESS_MIGRATION;
			} else {
				pr_err("map_device: Invalid access_mode: '%s'\n",
				       p);
				ret = -EINVAL;
				kfree(p);
				goto out;
			}

			kfree(p);
			break;

		case RNBD_OPT_NR_POLL_QUEUES:
			if (match_int(args, &nr_poll_queues) || nr_poll_queues < -1 ||
			    nr_poll_queues > (int)nr_cpu_ids) {
				pr_err("bad nr_poll_queues parameter '%d'\n",
				       nr_poll_queues);
				ret = -EINVAL;
				goto out;
			}
			if (nr_poll_queues == -1)
				nr_poll_queues = nr_cpu_ids;
			*opt->nr_poll_queues = nr_poll_queues;
			break;

		default:
			pr_err("map_device: Unknown parameter or missing value '%s'\n",
			       p);
			ret = -EINVAL;
			goto out;
		}
	}

	for (i = 0; i < ARRAY_SIZE(rnbd_opt_mandatory); i++) {
		if ((opt_mask & rnbd_opt_mandatory[i])) {
			ret = 0;
		} else {
			pr_err("map_device: Parameters missing\n");
			ret = -EINVAL;
			break;
		}
	}

out:
	*opt->path_cnt = p_cnt;
	kfree(options);
	return ret;
}