text: /*
   * Copyright 2014-2018, Intel Corporation
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   *     * Redistributions of source code must retain the above copyright
   *       notice, this list of conditions and the following disclaimer.
   *
   *     * Redistributions in binary form must reproduce the above copyright
   *       notice, this list of conditions and the following disclaimer in
   *       the documentation and/or other materials provided with the
   *       distribution.
   *
   *     * Neither the name of the copyright holder nor the names of its
   *       contributors may be used to endorse or promote products derived
   *       from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   */
  
  /*
   * common.c -- definitions of common functions
   */
  
  #include <stdlib.h>
  #include <stdio.h>
  #include <stdarg.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <stdint.h>
  #include <string.h>
  #include <err.h>
  #include <sys/param.h>
  #include <sys/mman.h>
  #include <ctype.h>
  #include <assert.h>
  #include <getopt.h>
  #include <unistd.h>
  #include <endian.h>
  
  #include "common.h"
  
  #include "output.h"
  #include "libpmem.h"
  #include "libpmemblk.h"
  #include "libpmemlog.h"
  #include "libpmemobj.h"
  #include "libpmemcto.h"
  #include "btt.h"
  #include "file.h"
  #include "os.h"
  #include "set.h"
  #include "out.h"
  #include "mmap.h"
  #include "util_pmem.h"
  
  #define REQ_BUFF_SIZE	2048U
  #define Q_BUFF_SIZE	8192
  
  typedef const char *(*enum_to_str_fn)(int);
  
  /*
   * pmem_pool_type -- return pool type based on first two pages.
   * If pool header's content suggests that pool may be BTT device
   * (first page zeroed and no correct signature for pool header),
   * signature from second page is checked to prove that it's BTT device layout.
   */
  pmem_pool_type_t
  pmem_pool_type(const void *base_pool_addr)
  {
  	struct pool_hdr *hdrp = (struct pool_hdr *)base_pool_addr;
  
  	if (util_is_zeroed(hdrp, DEFAULT_HDR_SIZE)) {
  		return util_get_pool_type_second_page(base_pool_addr);
  	}
  
  	pmem_pool_type_t type = pmem_pool_type_parse_hdr(hdrp);
  	if (type != PMEM_POOL_TYPE_UNKNOWN)
  		return type;
  	else
  		return util_get_pool_type_second_page(base_pool_addr);
  }
  
  /*
   * pmem_pool_checksum -- return true if checksum is correct
   * based on first two pages
   */
  int
  pmem_pool_checksum(const void *base_pool_addr)
  {
  	/* check whether it's btt device -> first page zeroed */
  	if (util_is_zeroed(base_pool_addr, DEFAULT_HDR_SIZE)) {
  		struct btt_info bttinfo;
  		void *sec_page_addr = (char *)base_pool_addr + DEFAULT_HDR_SIZE;
  		memcpy(&bttinfo, sec_page_addr, sizeof(bttinfo));
  		btt_info_convert2h(&bttinfo);
  		return util_checksum(&bttinfo, sizeof(bttinfo),
  			&bttinfo.checksum, 0, 0);
  	} else {
  		/* it's not btt device - first page contains header */
  		struct pool_hdr hdrp;
  		memcpy(&hdrp, base_pool_addr, sizeof(hdrp));
  		return util_checksum(&hdrp, sizeof(hdrp),
  			&hdrp.checksum, 0, POOL_HDR_CSUM_END_OFF);
  	}
  }
  
  /*
   * pmem_pool_type_parse_hdr -- return pool type based only on signature
   */
  pmem_pool_type_t
  pmem_pool_type_parse_hdr(const struct pool_hdr *hdrp)
  {
  	if (memcmp(hdrp->signature, LOG_HDR_SIG, POOL_HDR_SIG_LEN) == 0)
  		return PMEM_POOL_TYPE_LOG;
  	else if (memcmp(hdrp->signature, BLK_HDR_SIG, POOL_HDR_SIG_LEN) == 0)
  		return PMEM_POOL_TYPE_BLK;
  	else if (memcmp(hdrp->signature, OBJ_HDR_SIG, POOL_HDR_SIG_LEN) == 0)
  		return PMEM_POOL_TYPE_OBJ;
  	else if (memcmp(hdrp->signature, CTO_HDR_SIG, POOL_HDR_SIG_LEN) == 0)
  		return PMEM_POOL_TYPE_CTO;
  	else
  		return PMEM_POOL_TYPE_UNKNOWN;
  }
  
  /*
   * pmem_pool_type_parse_str -- returns pool type from command line arg
   */
  pmem_pool_type_t
  pmem_pool_type_parse_str(const char *str)
  {
  	if (strcmp(str, "blk") == 0) {
  		return PMEM_POOL_TYPE_BLK;
  	} else if (strcmp(str, "log") == 0) {
  		return PMEM_POOL_TYPE_LOG;
  	} else if (strcmp(str, "obj") == 0) {
  		return PMEM_POOL_TYPE_OBJ;
  	} else if (strcmp(str, "btt") == 0) {
  		return PMEM_POOL_TYPE_BTT;
  	} else if (strcmp(str, "cto") == 0) {
  		return PMEM_POOL_TYPE_CTO;
  	} else {
  		return PMEM_POOL_TYPE_UNKNOWN;
  	}
  }
  
  /*
   * util_validate_checksum -- validate checksum and return valid one
   */
  int
  util_validate_checksum(void *addr, size_t len, uint64_t *csum,
  	uint64_t skip_off)
  {
  	/* validate checksum */
  	int csum_valid = util_checksum(addr, len, csum, 0, skip_off);
  	/* get valid one */
  	if (!csum_valid)
  		util_checksum(addr, len, csum, 1, skip_off);
  	return csum_valid;
  }
  
  /*
   * util_get_pool_type_second_page -- return type based on second page content
   */
  pmem_pool_type_t
  util_get_pool_type_second_page(const void *pool_base_addr)
  {
  	struct btt_info bttinfo;
  
  	void *sec_page_addr = (char *)pool_base_addr + DEFAULT_HDR_SIZE;
  	memcpy(&bttinfo, sec_page_addr, sizeof(bttinfo));
  	btt_info_convert2h(&bttinfo);
  
  	if (util_is_zeroed(&bttinfo, sizeof(bttinfo)))
  		return PMEM_POOL_TYPE_UNKNOWN;
  
  	if (memcmp(bttinfo.sig, BTTINFO_SIG, BTTINFO_SIG_LEN) == 0)
  		return PMEM_POOL_TYPE_BTT;
  
  	return PMEM_POOL_TYPE_UNKNOWN;
  }
  
  /*
   * util_parse_mode -- parse file mode from octal string
   */
  int
  util_parse_mode(const char *str, mode_t *mode)
  {
  	mode_t m = 0;
  	int digits = 0;
  
  	/* skip leading zeros */
  	while (*str == '0')
  		str++;
  
  	/* parse at most 3 octal digits */
  	while (digits < 3 && *str != '\0') {
  		if (*str < '0' || *str > '7')
  			return -1;
  		m = (mode_t)(m << 3) | (mode_t)(*str - '0');
  		digits++;
  		str++;
  	}
  
  	/* more than 3 octal digits */
  	if (digits == 3 && *str != '\0')
  		return -1;
  
  	if (mode)
  		*mode = m;
  
  	return 0;
  }
  
  static void
  util_range_limit(struct range *rangep, struct range limit)
  {
  	if (rangep->first < limit.first)
  		rangep->first = limit.first;
  	if (rangep->last > limit.last)
  		rangep->last = limit.last;
  }
  
  /*
   * util_parse_range_from -- parse range string as interval from specified number
   */
  static int
  util_parse_range_from(char *str, struct range *rangep, struct range entire)
  {
  	size_t str_len = strlen(str);
  	if (str[str_len - 1] != '-')
  		return -1;
  
  	str[str_len - 1] = '\0';
  
  	if (util_parse_size(str, (size_t *)&rangep->first))
  		return -1;
  
  	rangep->last = entire.last;
  	util_range_limit(rangep, entire);
  
  	return 0;
  }
  
  /*
   * util_parse_range_to -- parse range string as interval to specified number
   */
  static int
  util_parse_range_to(char *str, struct range *rangep, struct range entire)
  {
  
  	if (str[0] != '-' || str[1] == '\0')
  		return -1;
  
  	if (util_parse_size(str + 1, (size_t *)&rangep->last))
  		return -1;
  
  	rangep->first = entire.first;
  	util_range_limit(rangep, entire);
  
  	return 0;
  }
  
  /*
   * util_parse_range_number -- parse range string as a single number
   */
  static int
  util_parse_range_number(char *str, struct range *rangep, struct range entire)
  {
  	if (util_parse_size(str, (size_t *)&rangep->first) != 0)
  		return -1;
  	rangep->last = rangep->first;
  	if (rangep->first > entire.last ||
  	    rangep->last < entire.first)
  		return -1;
  	util_range_limit(rangep, entire);
  	return 0;
  }
  
  /*
   * util_parse_range -- parse single range string
   */
  static int
  util_parse_range(char *str, struct range *rangep, struct range entire)
  {
  	char *dash = strchr(str, '-');
  	if (!dash)
  		return util_parse_range_number(str, rangep, entire);
  
  	/* '-' at the beginning */
  	if (dash == str)
  		return util_parse_range_to(str, rangep, entire);
  
  	/* '-' at the end */
  	if (dash[1] == '\0')
  		return util_parse_range_from(str, rangep, entire);
  
  	*dash = '\0';
  	dash++;
  
  	if (util_parse_size(str, (size_t *)&rangep->first))
  		return -1;
  
  	if (util_parse_size(dash, (size_t *)&rangep->last))
  		return -1;
  
  	if (rangep->first > rangep->last) {
  		uint64_t tmp = rangep->first;
  		rangep->first = rangep->last;
  		rangep->last = tmp;
  	}
  
  	util_range_limit(rangep, entire);
  
  	return 0;
  }
  
  /*
   * util_ranges_overlap -- return 1 if two ranges are overlapped
   */
  static int
  util_ranges_overlap(struct range *rangep1, struct range *rangep2)
  {
  	if (rangep1->last + 1 < rangep2->first ||
  	    rangep2->last + 1 < rangep1->first)
  		return 0;
  	else
  		return 1;
  }
  
  /*
   * util_ranges_add -- create and add range
   */
  int
  util_ranges_add(struct ranges *rangesp, struct range range)
  {
  	struct range *rangep = malloc(sizeof(struct range));
  	if (!rangep)
  		err(1, "Cannot allocate memory for range\n");
  	memcpy(rangep, &range, sizeof(*rangep));
  
  	struct range *curp, *next;
  	uint64_t first = rangep->first;
  	uint64_t last = rangep->last;
  
  	curp = LIST_FIRST(&rangesp->head);
  	while (curp) {
  		next = LIST_NEXT(curp, next);
  		if (util_ranges_overlap(curp, rangep)) {
  			LIST_REMOVE(curp, next);
  			if (curp->first < first)
  				first = curp->first;
  			if (curp->last > last)
  				last = curp->last;
  			free(curp);
  		}
  		curp = next;
  	}
  
  	rangep->first = first;
  	rangep->last = last;
  
  	LIST_FOREACH(curp, &rangesp->head, next) {
  		if (curp->first < rangep->first) {
  			LIST_INSERT_AFTER(curp, rangep, next);
  			return 0;
  		}
  	}
  
  	LIST_INSERT_HEAD(&rangesp->head, rangep, next);
  
  	return 0;
  }
  
  /*
   * util_ranges_contain -- return 1 if ranges contain the number n
   */
  int
  util_ranges_contain(const struct ranges *rangesp, uint64_t n)
  {
  	struct range *curp  = NULL;
  	LIST_FOREACH(curp, &rangesp->head, next) {
  		if (curp->first <= n && n <= curp->last)
  			return 1;
  	}
  
  	return 0;
  }
  
  /*
   * util_ranges_empty -- return 1 if ranges are empty
   */
  int
  util_ranges_empty(const struct ranges *rangesp)
  {
  	return LIST_EMPTY(&rangesp->head);
  }
  
  /*
   * util_ranges_clear -- clear list of ranges
   */
  void
  util_ranges_clear(struct ranges *rangesp)
  {
  	while (!LIST_EMPTY(&rangesp->head)) {
  		struct range *rangep = LIST_FIRST(&rangesp->head);
  		LIST_REMOVE(rangep, next);
  		free(rangep);
  	}
  }
  
  /*
   * util_parse_ranges -- parser ranges from string
   *
   * The valid formats of range are:
   * - 'n-m' -- from n to m
   * - '-m'  -- from minimum passed in entirep->first to m
   * - 'n-'  -- from n to maximum passed in entirep->last
   * - 'n'   -- n'th byte/block
   * Multiple ranges may be separated by comma:
   * 'n1-m1,n2-,-m3,n4'
   */
  int
  util_parse_ranges(const char *ptr, struct ranges *rangesp, struct range entire)
  {
  	if (ptr == NULL)
  		return util_ranges_add(rangesp, entire);
  
  	char *dup = strdup(ptr);
  	if (!dup)
  		err(1, "Cannot allocate memory for ranges");
  	char *str = dup;
  	int ret = 0;
  	char *next = str;
  	do {
  		str = next;
  		next = strchr(str, ',');
  		if (next != NULL) {
  			*next = '\0';
  			next++;
  		}
  		struct range range;
  		if (util_parse_range(str, &range, entire)) {
  			ret = -1;
  			goto out;
  		} else if (util_ranges_add(rangesp, range)) {
  			ret = -1;
  			goto out;
  		}
  	} while (next != NULL);
  out:
  	free(dup);
  	return ret;
  }
  
  /*
   * pmem_pool_get_min_size -- return minimum size of pool for specified type
   */
  uint64_t
  pmem_pool_get_min_size(pmem_pool_type_t type)
  {
  	switch (type) {
  	case PMEM_POOL_TYPE_LOG:
  		return PMEMLOG_MIN_POOL;
  	case PMEM_POOL_TYPE_BLK:
  		return PMEMBLK_MIN_POOL;
  	case PMEM_POOL_TYPE_OBJ:
  		return PMEMOBJ_MIN_POOL;
  	default:
  		break;
  	}
  
  	return 0;
  }
  
  /*
   * util_poolset_map -- map poolset
   */
  int
  util_poolset_map(const char *fname, struct pool_set **poolset, int rdonly)
  {
  	if (util_is_poolset_file(fname) != 1) {
  		int ret = util_poolset_create_set(poolset, fname, 0, 0, true);
  		if (ret < 0) {
  			outv_err("cannot open pool set -- '%s'", fname);
  			return -1;
  		}
  		return util_pool_open_nocheck(*poolset, rdonly);
  	}
  
  	/* open poolset file */
  	int fd = util_file_open(fname, NULL, 0, O_RDONLY);
  	if (fd < 0)
  		return -1;
  
  	struct pool_set *set;
  
  	/* parse poolset file */
  	if (util_poolset_parse(&set, fname, fd)) {
  		outv_err("parsing poolset file failed\n");
  		os_close(fd);
  		return -1;
  	}
  	set->ignore_sds = true;
  	os_close(fd);
  
  	/* read the pool header from first pool set file */
  	const char *part0_path = PART(REP(set, 0), 0).path;
  	struct pool_hdr hdr;
  	if (util_file_pread(part0_path, &hdr, sizeof(hdr), 0) !=
  			sizeof(hdr)) {
  		outv_err("cannot read pool header from poolset\n");
  		goto err_pool_set;
  	}
  
  	util_poolset_free(set);
  
  	util_convert2h_hdr_nocheck(&hdr);
  
  	/* parse pool type from first pool set file */
  	pmem_pool_type_t type = pmem_pool_type_parse_hdr(&hdr);
  	if (type == PMEM_POOL_TYPE_UNKNOWN) {
  		outv_err("cannot determine pool type from poolset\n");
  		return -1;
  	}
  
  	/*
  	 * Just use one thread - there is no need for multi-threaded access
  	 * to remote pool.
  	 */
  	unsigned nlanes = 1;
  
  	/*
  	 * Open the poolset, the values passed to util_pool_open are read
  	 * from the first poolset file, these values are then compared with
  	 * the values from all headers of poolset files.
  	 */
  	if (util_pool_open(poolset, fname, rdonly, 0 /* minpartsize */,
  			hdr.signature, hdr.major,
  			hdr.compat_features,
  			hdr.incompat_features,
  			hdr.ro_compat_features, &nlanes, true,
  			NULL)) {
  		outv_err("opening poolset failed\n");
  		return -1;
  	}
  
  	return 0;
  
  err_pool_set:
  	util_poolset_free(set);
  	return -1;
  }
  
  /*
   * pmem_pool_parse_params -- parse pool type, file size and block size
   */
  int
  pmem_pool_parse_params(const char *fname, struct pmem_pool_params *paramsp,
  		int check)
  {
  	paramsp->type = PMEM_POOL_TYPE_UNKNOWN;
  	char pool_str_addr[POOL_HDR_DESC_SIZE];
  
  	paramsp->is_poolset = util_is_poolset_file(fname) == 1;
  	int fd = util_file_open(fname, NULL, 0, O_RDONLY);
  	if (fd < 0)
  		return -1;
  
  	/* get file size and mode */
  	os_stat_t stat_buf;
  	if (os_fstat(fd, &stat_buf)) {
  		os_close(fd);
  		return -1;
  	}
  
  	int ret = 0;
  
  	assert(stat_buf.st_size >= 0);
  	paramsp->size = (uint64_t)stat_buf.st_size;
  	paramsp->mode = stat_buf.st_mode;
  
  	void *addr = NULL;
  	struct pool_set *set = NULL;
  	if (paramsp->is_poolset) {
  		/* close the file */
  		os_close(fd);
  		fd = -1;
  
  		if (check) {
  			if (util_poolset_map(fname, &set, 0)) {
  				ret = -1;
  				goto out_close;
  			}
  		} else {
  			ret = util_poolset_create_set(&set, fname, 0, 0, true);
  			if (ret < 0) {
  				outv_err("cannot open pool set -- '%s'", fname);
  				ret = -1;
  				goto out_close;
  			}
  			if (util_pool_open_nocheck(set, 0)) {
  				ret = -1;
  				goto out_close;
  			}
  		}
  
  		paramsp->size = set->poolsize;
  		addr = set->replica[0]->part[0].addr;
  
  		/*
  		 * XXX mprotect for device dax with length not aligned to its
  		 * page granularity causes SIGBUS on the next page fault.
  		 * The length argument of this call should be changed to
  		 * set->poolsize once the kernel issue is solved.
  		 */
  		if (mprotect(addr, set->replica[0]->repsize,
  			PROT_READ) < 0) {
  			outv_err("!mprotect");
  			goto out_close;
  		}
  	} else {
  		/* read first two pages */
  		if (util_file_is_device_dax(fname)) {
  			addr = util_file_map_whole(fname);
  			if (addr == NULL) {
  				ret = -1;
  				goto out_close;
  			}
  		} else {
  			ssize_t num = read(fd, pool_str_addr,
  					POOL_HDR_DESC_SIZE);
  			if (num < (ssize_t)POOL_HDR_DESC_SIZE) {
  				outv_err("!read");
  				ret = -1;
  				goto out_close;
  			}
  			addr = pool_str_addr;
  		}
  	}
  
  	struct pool_hdr hdr;
  	memcpy(&hdr, addr, sizeof(hdr));
  
  	util_convert2h_hdr_nocheck(&hdr);
  
  	memcpy(paramsp->signature, hdr.signature, sizeof(paramsp->signature));
  
  	/*
  	 * Check if file is a part of pool set by comparing
  	 * the UUID with the next part UUID. If it is the same
  	 * it means the pool consist of a single file.
  	 */
  	paramsp->is_part = !paramsp->is_poolset &&
  		(memcmp(hdr.uuid, hdr.next_part_uuid, POOL_HDR_UUID_LEN) ||
  		memcmp(hdr.uuid, hdr.prev_part_uuid, POOL_HDR_UUID_LEN) ||
  		memcmp(hdr.uuid, hdr.next_repl_uuid, POOL_HDR_UUID_LEN) ||
  		memcmp(hdr.uuid, hdr.prev_repl_uuid, POOL_HDR_UUID_LEN));
  
  	if (check)
  		paramsp->type = pmem_pool_type(addr);
  	else
  		paramsp->type = pmem_pool_type_parse_hdr(addr);
  
  	paramsp->is_checksum_ok = pmem_pool_checksum(addr);
  
  	if (paramsp->type == PMEM_POOL_TYPE_BLK) {
  		struct pmemblk *pbp = addr;
  		paramsp->blk.bsize = le32toh(pbp->bsize);
  	} else if (paramsp->type == PMEM_POOL_TYPE_OBJ) {
  		struct pmemobjpool *pop = addr;
  		memcpy(paramsp->obj.layout, pop->layout, PMEMOBJ_MAX_LAYOUT);
  	}
  
  	if (paramsp->is_poolset)
  		util_poolset_close(set, DO_NOT_DELETE_PARTS);
  
  out_close:
  	if (fd >= 0)
  		(void) os_close(fd);
  	return ret;
  }
  
  /*
   * util_check_memory -- check if memory contains single value
   */
  int
  util_check_memory(const uint8_t *buff, size_t len, uint8_t val)
  {
  	size_t i;
  	for (i = 0; i < len; i++) {
  		if (buff[i] != val)
  			return -1;
  	}
  
  	return 0;
  }
  
  /*
   * ask -- print question and wait for single-char answer
   */
  char
  ask(char op, char *answers, char def_ans, const char *fmt, va_list ap)
  {
  	char qbuff[Q_BUFF_SIZE];
  	char ans = '\0';
  	int valid = 0;
  	if (op != '?')
  		return op;
  
  	vsnprintf(qbuff, Q_BUFF_SIZE, fmt, ap);
  
  	int is_tty = isatty(fileno(stdin));
  	printf("%s", qbuff);
  	size_t len = strlen(answers);
  	size_t i;
  	char def_anslo = (char)tolower(def_ans);
  	printf(" [");
  	for (i = 0; i < len; i++) {
  		char anslo = (char)tolower(answers[i]);
  		printf("%c", anslo == def_anslo ?
  				toupper(anslo) : anslo);
  		if (i != len - 1)
  			printf("/");
  	}
  	printf("] ");
  
  	int c = getchar();
  	if (c == EOF)
  		ans = INV_ANS;
  	else
  		ans = (char)tolower(c);
  
  	if (ans != '\n') {
  		valid = 1;
  		/*
  		 * Valid answer must consist of a single letter and new
  		 * line character just after it. Otherwise it is
  		 * invalid.
  		 */
  		while ((char)(c = getchar()) != '\n' && c != EOF)
  			valid = 0;
  	}
  
  	char ret = ans == '\n' ? def_ans : ans;
  
  	if (!is_tty)
  		printf("%c\n", ret);
  
  	if (!valid || strchr(answers, ans) == NULL)
  		ret = INV_ANS;
  
  	return ret;
  }
  
  char
  ask_yn(char op, char def_ans, const char *fmt, va_list ap)
  {
  	char ret = ask(op, "yn", def_ans, fmt, ap);
  	return ret;
  }
  
  char
  ask_Yn(char op, const char *fmt, ...)
  {
  	va_list ap;
  	va_start(ap, fmt);
  	char ret = ask_yn(op, 'y', fmt, ap);
  	va_end(ap);
  	return ret;
  }
  
  char
  ask_yN(char op, const char *fmt, ...)
  {
  	va_list ap;
  	va_start(ap, fmt);
  	char ret = ask_yn(op, 'n', fmt, ap);
  	va_end(ap);
  	return ret;
  }
  
  /*
   * util_parse_enum -- parse single enum and store to bitmap
   */
  static int
  util_parse_enum(const char *str, int first, int max, uint64_t *bitmap,
  		enum_to_str_fn enum_to_str)
  {
  	for (int i = first; i < max; i++) {
  		if (strcmp(str, enum_to_str(i)) == 0) {
  			*bitmap |= (uint64_t)1<<i;
  			return 0;
  		}
  	}
  
  	return -1;
  }
  
  /*
   * util_parse_enums -- parse enums and store to bitmap
   */
  static int
  util_parse_enums(const char *str, int first, int max, uint64_t *bitmap,
  		enum_to_str_fn enum_to_str)
  {
  	char *dup = strdup(str);
  	if (!dup)
  		err(1, "Cannot allocate memory for enum str");
  
  	char *ptr = dup;
  	int ret = 0;
  	char *comma;
  	do {
  		comma = strchr(ptr, ',');
  		if (comma) {
  			*comma = '\0';
  			comma++;
  		}
  
  		if ((ret = util_parse_enum(ptr, first, max,
  				bitmap, enum_to_str))) {
  			goto out;
  		}
  
  		ptr = comma;
  	} while (ptr);
  out:
  	free(dup);
  	return ret;
  }
  
  /*
   * util_parse_chunk_types -- parse chunk types strings
   */
  int
  util_parse_chunk_types(const char *str, uint64_t *types)
  {
  	assert(MAX_CHUNK_TYPE < 8 * sizeof(*types));
  	return util_parse_enums(str, 0, MAX_CHUNK_TYPE, types,
  			(enum_to_str_fn)out_get_chunk_type_str);
  }
  
  /*
   * util_parse_lane_section -- parse lane section strings
   */
  int
  util_parse_lane_sections(const char *str, uint64_t *types)
  {
  	assert(MAX_LANE_SECTION < 8 * sizeof(*types));
  	return util_parse_enums(str, 0, MAX_LANE_SECTION, types,
  			(enum_to_str_fn)out_get_lane_section_str);
  }
  
  /*
   * util_options_alloc -- allocate and initialize options structure
   */
  struct options *
  util_options_alloc(const struct option *options,
  		size_t nopts, const struct option_requirement *req)
  {
  	struct options *opts = calloc(1, sizeof(*opts));
  	if (!opts)
  		err(1, "Cannot allocate memory for options structure");
  
  	opts->opts = options;
  	opts->noptions = nopts;
  	opts->req = req;
  	size_t bitmap_size = howmany(nopts, 8);
  	opts->bitmap = calloc(bitmap_size, 1);
  	if (!opts->bitmap)
  		err(1, "Cannot allocate memory for options bitmap");
  
  	return opts;
  }
  
  /*
   * util_options_free -- free options structure
   */
  void
  util_options_free(struct options *opts)
  {
  	free(opts->bitmap);
  	free(opts);
  }
  
  /*
   * util_opt_get_index -- return index of specified option in global
   * array of options
   */
  static int
  util_opt_get_index(const struct options *opts, int opt)
  {
  	const struct option *lopt = &opts->opts[0];
  	int ret = 0;
  	while (lopt->name) {
  		if ((lopt->val & ~OPT_MASK) == opt)
  			return ret;
  		lopt++;
  		ret++;
  	}
  	return -1;
  }
  
  /*
   * util_opt_get_req -- get required option for specified option
   */
  static struct option_requirement *
  util_opt_get_req(const struct options *opts, int opt, pmem_pool_type_t type)
  {
  	size_t n = 0;
  	struct option_requirement *ret = NULL;
  	struct option_requirement *tmp = NULL;
  	const struct option_requirement *req = &opts->req[0];
  	while (req->opt) {
  		if (req->opt == opt && (req->type & type)) {
  			n++;
  			tmp = realloc(ret, n * sizeof(*ret));
  			if (!tmp)
  				err(1, "Cannot allocate memory for"
  					" option requirements");
  			ret = tmp;
  			ret[n - 1] = *req;
  		}
  		req++;
  	}
  
  	if (ret) {
  		tmp = realloc(ret, (n + 1) * sizeof(*ret));
  		if (!tmp)
  			err(1, "Cannot allocate memory for"
  				" option requirements");
  		ret = tmp;
  		memset(&ret[n], 0, sizeof(*ret));
  	}
  
  	return ret;
  }
  
  /*
   * util_opt_check_requirements -- check if requirements has been fulfilled
   */
  static int
  util_opt_check_requirements(const struct options *opts,
  		const struct option_requirement *req)
  {
  	int count = 0;
  	int set = 0;
  	uint64_t tmp;
  	while ((tmp = req->req) != 0) {
  		while (tmp) {
  			int req_idx =
  				util_opt_get_index(opts, tmp & OPT_REQ_MASK);
  
  			if (req_idx >= 0 && util_isset(opts->bitmap, req_idx)) {
  				set++;
  				break;
  			}
  
  			tmp >>= OPT_REQ_SHIFT;
  		}
  		req++;
  		count++;
  	}
  
  	return count != set;
  }
  
  /*
   * util_opt_print_requirements -- print requirements for specified option
   */
  static void
  util_opt_print_requirements(const struct options *opts,
  		const struct option_requirement *req)
  {
  	char buff[REQ_BUFF_SIZE];
  	unsigned n = 0;
  	uint64_t tmp;
  	const struct option *opt =
  		&opts->opts[util_opt_get_index(opts, req->opt)];
  	int sn;
  
  	sn = snprintf(&buff[n], REQ_BUFF_SIZE - n,
  			"option [-%c|--%s] requires: ", opt->val, opt->name);
  	assert(sn >= 0);
  	if (sn >= 0)
  		n += (unsigned)sn;
  
  	size_t rc = 0;
  	while ((tmp = req->req) != 0) {
  		if (rc != 0) {
  			sn = snprintf(&buff[n], REQ_BUFF_SIZE - n, " and ");
  			assert(sn >= 0);
  			if (sn >= 0)
  				n += (unsigned)sn;
  		}
  
  		size_t c = 0;
  		while (tmp) {
  			if (c == 0)
  				sn = snprintf(&buff[n], REQ_BUFF_SIZE - n, "[");
  			else
  				sn = snprintf(&buff[n], REQ_BUFF_SIZE - n, "|");
  			assert(sn >= 0);
  			if (sn >= 0)
  				n += (unsigned)sn;
  
  			int req_opt_ind =
  				util_opt_get_index(opts, tmp & OPT_REQ_MASK);
  			const struct option *req_option =
  				&opts->opts[req_opt_ind];
  
  			sn = snprintf(&buff[n], REQ_BUFF_SIZE - n,
  				"-%c|--%s", req_option->val, req_option->name);
  			assert(sn >= 0);
  			if (sn >= 0)
  				n += (unsigned)sn;
  
  			tmp >>= OPT_REQ_SHIFT;
  			c++;
  		}
  		sn = snprintf(&buff[n], REQ_BUFF_SIZE - n, "]");
  		assert(sn >= 0);
  		if (sn >= 0)
  			n += (unsigned)sn;
  
  		req++;
  		rc++;
  	}
  
  	outv_err("%s\n", buff);
  }
  
  /*
   * util_opt_verify_requirements -- verify specified requirements for options
   */
  static int
  util_opt_verify_requirements(const struct options *opts, size_t index,
  		pmem_pool_type_t type)
  {
  	const struct option *opt = &opts->opts[index];
  	int val = opt->val & ~OPT_MASK;
  	struct option_requirement *req;
  
  	if ((req = util_opt_get_req(opts, val, type)) == NULL)
  		return 0;
  
  	int ret = 0;
  
  	if (util_opt_check_requirements(opts, req)) {
  		ret = -1;
  		util_opt_print_requirements(opts, req);
  	}
  
  	free(req);
  	return ret;
  }
  
  /*
   * util_opt_verify_type -- check if used option matches pool type
   */
  static int
  util_opt_verify_type(const struct options *opts, pmem_pool_type_t type,
  		size_t index)
  {
  	const struct option *opt = &opts->opts[index];
  	int val = opt->val & ~OPT_MASK;
  	int opt_type = opt->val;
  	opt_type >>= OPT_SHIFT;
  	if (!(opt_type & (1<<type))) {
  		outv_err("'--%s|-%c' -- invalid option specified"
  			" for pool type '%s'\n",
  			opt->name, val,
  			out_get_pool_type_str(type));
  		return -1;
  	}
  
  	return 0;
  }
  
  /*
   * util_options_getopt -- wrapper for getopt_long which sets bitmap
   */
  int
  util_options_getopt(int argc, char *argv[], const char *optstr,
  		const struct options *opts)
  {
  	int opt = getopt_long(argc, argv, optstr, opts->opts, NULL);
  	if (opt == -1 || opt == '?')
  		return opt;
  
  	opt &= ~OPT_MASK;
  	int option_index = util_opt_get_index(opts, opt);
  	assert(option_index >= 0);
  
  	util_setbit((uint8_t *)opts->bitmap, (unsigned)option_index);
  
  	return opt;
  }
  
  /*
   * util_options_verify -- verify options
   */
  int
  util_options_verify(const struct options *opts, pmem_pool_type_t type)
  {
  	for (size_t i = 0; i < opts->noptions; i++) {
  		if (util_isset(opts->bitmap, i)) {
  			if (util_opt_verify_type(opts, type, i))
  				return -1;
  
  			if (opts->req)
  				if (util_opt_verify_requirements(opts, i, type))
  					return -1;
  		}
  	}
  
  	return 0;
  }
  
  /*
   * util_heap_max_zone -- get number of zones
   */
  unsigned
  util_heap_max_zone(size_t size)
  {
  	unsigned max_zone = 0;
  	size -= sizeof(struct heap_header);
  
  	while (size >= ZONE_MIN_SIZE) {
  		max_zone++;
  		size -= size <= ZONE_MAX_SIZE ? size : ZONE_MAX_SIZE;
  	}
  
  	return max_zone;
  }
  
  /*
   * util_heap_get_bitmap_params -- return bitmap parameters of given block size
   *
   * The function returns the following values:
   * - number of allocations
   * - number of used values in bitmap
   * - initial value of last used entry
   */
  int
  util_heap_get_bitmap_params(uint64_t block_size, uint64_t *nallocsp,
  		uint64_t *nvalsp, uint64_t *last_valp)
  {
  	assert(RUNSIZE / block_size <= UINT32_MAX);
  	uint32_t nallocs = (uint32_t)(RUNSIZE / block_size);
  
  	assert(nallocs <= RUN_BITMAP_SIZE);
  	unsigned unused_bits = RUN_BITMAP_SIZE - nallocs;
  
  	unsigned unused_values = unused_bits / BITS_PER_VALUE;
  
  	assert(MAX_BITMAP_VALUES >= unused_values);
  	uint64_t nvals = MAX_BITMAP_VALUES - unused_values;
  
  	assert(unused_bits >= unused_values * BITS_PER_VALUE);
  	unused_bits -= unused_values * BITS_PER_VALUE;
  
  	uint64_t last_val = unused_bits ? (((1ULL << unused_bits) - 1ULL) <<
  				(BITS_PER_VALUE - unused_bits)) : 0;
  
  	if (nvals >= MAX_BITMAP_VALUES || nvals == 0)
  		return -1;
  
  	if (nallocsp)
  		*nallocsp = nallocs;
  	if (nvalsp)
  		*nvalsp = nvals;
  	if (last_valp)
  		*last_valp = last_val;
  
  	return 0;
  }
  
  /*
   * pool_set_file_open -- opens pool set file or regular file
   */
  struct pool_set_file *
  pool_set_file_open(const char *fname,
  		int rdonly, int check)
  {
  	struct pool_set_file *file = calloc(1, sizeof(*file));
  	if (!file)
  		return NULL;
  
  	file->replica = 0;
  	file->fname = strdup(fname);
  	if (!file->fname)
  		goto err;
  
  	os_stat_t buf;
  	if (os_stat(fname, &buf)) {
  		warn("%s", fname);
  		goto err_free_fname;
  	}
  
  	file->mtime = buf.st_mtime;
  	file->mode = buf.st_mode;
  	if (S_ISBLK(file->mode))
  		file->fileio = true;
  
  	if (file->fileio) {
  		/* Simple file open for BTT device */
  		int fd = util_file_open(fname, NULL, 0, O_RDONLY);
  		if (fd < 0) {
  			outv_err("util_file_open failed\n");
  			goto err_free_fname;
  		}
  
  		os_off_t seek_size = os_lseek(fd, 0, SEEK_END);
  		if (seek_size == -1) {
  			outv_err("lseek SEEK_END failed\n");
  			os_close(fd);
  			goto err_free_fname;
  		}
  
  		file->size = (size_t)seek_size;
  		file->fd = fd;
  	} else {
  		/*
  		 * The check flag indicates whether the headers from each pool
  		 * set file part should be checked for valid values.
  		 */
  		if (check) {
  			if (util_poolset_map(file->fname,
  					&file->poolset, rdonly))
  				goto err_free_fname;
  		} else {
  			int ret = util_poolset_create_set(&file->poolset,
  				file->fname, 0, 0, true);
  
  			if (ret < 0) {
  				outv_err("cannot open pool set -- '%s'",
  					file->fname);
  				goto err_free_fname;
  			}
  			if (util_pool_open_nocheck(file->poolset, rdonly))
  				goto err_free_fname;
  		}
  
  		/* get modification time from the first part of first replica */
  		const char *path = file->poolset->replica[0]->part[0].path;
  		if (os_stat(path, &buf)) {
  			warn("%s", path);
  			goto err_close_poolset;
  		}
  		file->size = file->poolset->poolsize;
  		file->addr = file->poolset->replica[0]->part[0].addr;
  	}
  	return file;
  
  err_close_poolset:
  	util_poolset_close(file->poolset, DO_NOT_DELETE_PARTS);
  err_free_fname:
  	free(file->fname);
  err:
  	free(file);
  	return NULL;
  }
  
  /*
   * pool_set_file_close -- closes pool set file or regular file
   */
  void
  pool_set_file_close(struct pool_set_file *file)
  {
  	if (!file->fileio) {
  		if (file->poolset)
  			util_poolset_close(file->poolset, DO_NOT_DELETE_PARTS);
  		else if (file->addr) {
  			munmap(file->addr, file->size);
  			os_close(file->fd);
  		}
  	}
  	free(file->fname);
  	free(file);
  }
  
  /*
   * pool_set_file_read -- read from pool set file or regular file
   *
   * 'buff' has to be a buffer at least 'nbytes' long
   * 'off' is an offset from the beginning of the file
   */
  int
  pool_set_file_read(struct pool_set_file *file, void *buff,
  		size_t nbytes, uint64_t off)
  {
  	if (off + nbytes > file->size)
  		return -1;
  
  	if (file->fileio) {
  		ssize_t num = pread(file->fd, buff, nbytes, (os_off_t)off);
  		if (num < (ssize_t)nbytes)
  			return -1;
  	} else {
  		memcpy(buff, (char *)file->addr + off, nbytes);
  	}
  	return 0;
  }
  
  /*
   * pool_set_file_write -- write to pool set file or regular file
   *
   * 'buff' has to be a buffer at least 'nbytes' long
   * 'off' is an offset from the beginning of the file
   */
  int
  pool_set_file_write(struct pool_set_file *file, void *buff,
  		size_t nbytes, uint64_t off)
  {
  	if (off + nbytes > file->size)
  		return -1;
  
  	if (file->fileio) {
  		ssize_t num = pwrite(file->fd, buff, nbytes, (os_off_t)off);
  		if (num < (ssize_t)nbytes)
  			return -1;
  	} else {
  		memcpy((char *)file->addr + off, buff, nbytes);
  		util_persist_auto(util_file_is_device_dax(file->fname),
  				(char *)file->addr + off, nbytes);
  	}
  	return 0;
  }
  
  /*
   * pool_set_file_set_replica -- change replica for pool set file
   */
  int
  pool_set_file_set_replica(struct pool_set_file *file, size_t replica)
  {
  	if (!replica)
  		return 0;
  
  	if (!file->poolset)
  		return -1;
  
  	if (replica >= file->poolset->nreplicas)
  		return -1;
  
  	if (file->poolset->replica[replica]->remote) {
  		outv_err("reading from remote replica not supported");
  		return -1;
  	}
  
  	file->replica = replica;
  	file->addr = file->poolset->replica[replica]->part[0].addr;
  
  	return 0;
  }
  
  /*
   * pool_set_file_nreplicas -- return number of replicas
   */
  size_t
  pool_set_file_nreplicas(struct pool_set_file *file)
  {
  	return file->poolset->nreplicas;
  }
  
  /*
   * pool_set_file_map -- return mapped address at given offset
   */
  void *
  pool_set_file_map(struct pool_set_file *file, uint64_t offset)
  {
  	if (file->addr == MAP_FAILED)
  		return NULL;
  	return (char *)file->addr + offset;
  }
  
  /*
   * pool_set_file_persist -- propagates and persists changes to a memory range
   *
   * 'addr' points to the beginning of data in the master replica that has to be
   *        propagated
   * 'len' is the number of bytes to be propagated to other replicas
   */
  void
  pool_set_file_persist(struct pool_set_file *file, const void *addr, size_t len)
  {
  	uintptr_t offset = (uintptr_t)((char *)addr -
  		(char *)file->poolset->replica[0]->part[0].addr);
  
  	for (unsigned r = 1; r < file->poolset->nreplicas; ++r) {
  		struct pool_replica *rep = file->poolset->replica[r];
  		void *dst = (char *)rep->part[0].addr + offset;
  		memcpy(dst, addr, len);
  		util_persist(rep->is_pmem, dst, len);
  	}
  	struct pool_replica *rep = file->poolset->replica[0];
  	util_persist(rep->is_pmem, (void *)addr, len);
  }
  