#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_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);
}
int
pmem_pool_checksum(const void *base_pool_addr)
{
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);
} else {
struct pool_hdr hdrp;
memcpy(&hdrp, base_pool_addr, sizeof(hdrp));
return util_checksum(&hdrp, sizeof(hdrp),
&hdrp.checksum, 0);
}
}
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_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;
}
}
int
util_validate_checksum(void *addr, size_t len, uint64_t *csum)
{
int csum_valid = util_checksum(addr, len, csum, 0);
if (!csum_valid)
util_checksum(addr, len, csum, 1);
return csum_valid;
}
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;
}
int
util_parse_mode(const char *str, mode_t *mode)
{
mode_t m = 0;
int digits = 0;
while (*str == '0')
str++;
while (digits < 3 && *str != '\0') {
if (*str < '0' || *str > '7')
return -1;
m = (mode_t)(m << 3) | (mode_t)(*str - '0');
digits++;
str++;
}
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;
}
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;
}
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;
}
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;
}
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);
if (dash == str)
return util_parse_range_to(str, rangep, entire);
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;
}
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;
}
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;
}
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;
}
int
util_ranges_empty(const struct ranges *rangesp)
{
return LIST_EMPTY(&rangesp->head);
}
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);
}
}
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;
}
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;
}
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);
if (ret < 0) {
outv_err("cannot open pool set -- '%s'", fname);
return -1;
}
return util_pool_open_nocheck(*poolset, rdonly);
}
int fd = util_file_open(fname, NULL, 0, O_RDONLY);
if (fd < 0)
return -1;
struct pool_set *set;
if (util_poolset_parse(&set, fname, fd)) {
outv_err("parsing poolset file failed\n");
os_close(fd);
return -1;
}
os_close(fd);
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);
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;
}
unsigned nlanes = 1;
if (util_pool_open(poolset, fname, rdonly, 0 ,
hdr.signature, hdr.major,
hdr.compat_features,
hdr.incompat_features,
hdr.ro_compat_features, &nlanes, NULL)) {
outv_err("opening poolset failed\n");
return -1;
}
return 0;
err_pool_set:
util_poolset_free(set);
return -1;
}
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;
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) {
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);
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;
if (mprotect(addr, set->replica[0]->repsize,
PROT_READ) < 0) {
outv_err("!mprotect");
goto out_close;
}
} else {
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));
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;
}
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;
}
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;
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;
}
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;
}
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;
}
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);
}
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);
}
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;
}
void
util_options_free(struct options *opts)
{
free(opts->bitmap);
free(opts);
}
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;
}
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;
}
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;
}
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);
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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) {
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 {
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);
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;
}
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;
}
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);
}
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;
}
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;
}
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;
}
size_t
pool_set_file_nreplicas(struct pool_set_file *file)
{
return file->poolset->nreplicas;
}
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;
}
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);
}