Viewing: libiam.c
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* Use is subject to license terms.
*
* Copyright (c) 2014, 2015, Intel Corporation.
*/
/*
* This file is part of Lustre, http://www.lustre.org/
*
* lustre/utils/libiam.c
*
* iam user level library
*
* Author: Wang Di <wangdi@clusterfs.com>
* Author: Nikita Danilov <nikita@clusterfs.com>
* Author: Fan Yong <fanyong@clusterfs.com>
*/
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <endian.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <libcfs/util/string.h>
#include <lustre/libiam.h>
/*
* Stores @val at @dst, where the latter is possibly unaligned. Uses
* memcpy(). This macro is needed to avoid dependency of user level tools on
* the kernel headers.
*/
#define STORE_UNALIGNED(val, dst) \
({ \
typeof(*(dst)) __val = (val); \
\
memcpy(dst, &__val, sizeof *(dst)); \
})
static int lfix_root_limit(int blocksize, int size)
{
return root_limit(sizeof(struct iam_lfix_root), 0, blocksize, size);
}
static void lfix_root(void *buf,
int blocksize, int keysize, int ptrsize, int recsize)
{
struct iam_lfix_root *root;
struct dx_countlimit *limit;
void *entry;
root = buf;
*root = (typeof(*root)) {
.ilr_magic = htole64(IAM_LFIX_ROOT_MAGIC),
.ilr_keysize = htole16(keysize),
.ilr_recsize = htole16(recsize),
.ilr_ptrsize = htole16(ptrsize),
.ilr_indirect_levels = 0
};
limit = (void *)(root + 1);
*limit = (typeof(*limit)){
/*
* limit itself + one pointer to the leaf.
*/
.count = htole16(2),
.limit = lfix_root_limit(blocksize, keysize + ptrsize)
};
entry = root + 1;
/*
* Skip over @limit.
*/
entry += keysize + ptrsize;
/*
* Entry format is <key> followed by <ptr>. In the minimal tree
* consisting of a root and single node, <key> is a minimal possible
* key.
*
* XXX: this key is hard-coded to be a sequence of 0's.
*/
entry += keysize;
/* now @entry points to <ptr> */
if (ptrsize == 4)
STORE_UNALIGNED(htole32(1), (u_int32_t *)entry);
else
STORE_UNALIGNED(htole64(1), (u_int64_t *)entry);
}
static void lfix_leaf(void *buf,
int blocksize, int keysize, int ptrsize, int recsize)
{
struct iam_leaf_head *head;
/* form leaf */
head = buf;
*head = (typeof(*head)) {
.ill_magic = htole16(IAM_LEAF_HEADER_MAGIC),
/*
* Leaf contains an entry with the smallest possible key
* (created by zeroing).
*/
.ill_count = htole16(1),
};
}
static int lvar_root_limit(int blocksize, int size)
{
return root_limit(sizeof(struct lvar_root), 0, blocksize, size);
}
static void lvar_root(void *buf,
int blocksize, int keysize, int ptrsize, int recsize)
{
struct lvar_root *root;
struct dx_countlimit *limit;
void *entry;
int isize;
isize = sizeof(lvar_hash_t) + ptrsize;
root = buf;
*root = (typeof(*root)) {
.vr_magic = htole32(IAM_LVAR_ROOT_MAGIC),
.vr_recsize = htole16(recsize),
.vr_ptrsize = htole16(ptrsize),
.vr_indirect_levels = 0
};
limit = (void *)(root + 1);
*limit = (typeof(*limit)) {
/*
* limit itself + one pointer to the leaf.
*/
.count = htole16(2),
.limit = lvar_root_limit(blocksize, keysize + ptrsize)
};
entry = root + 1;
/*
* Skip over @limit.
*/
entry += isize;
/*
* Entry format is <key> followed by <ptr>. In the minimal tree
* consisting of a root and single node, <key> is a minimal possible
* key.
*
* XXX: this key is hard-coded to be a sequence of 0's.
*/
entry += sizeof(lvar_hash_t);
/* now @entry points to <ptr> */
if (ptrsize == 4)
STORE_UNALIGNED(htole32(1), (u_int32_t *)entry);
else
STORE_UNALIGNED(htole64(1), (u_int64_t *)entry);
}
static int lvar_esize(int namelen, int recsize)
{
return (offsetof(struct lvar_leaf_entry, vle_key) +
namelen + recsize + LVAR_ROUND) & ~LVAR_ROUND;
}
static void lvar_leaf(void *buf,
int blocksize, int keysize, int ptrsize, int recsize)
{
struct lvar_leaf_header *head;
char *rec;
/* form leaf */
head = buf;
*head = (typeof(*head)) {
.vlh_magic = htole16(IAM_LVAR_LEAF_MAGIC),
.vlh_used = htole16(sizeof(*head) + lvar_esize(0, recsize))
};
rec = (void *)(head + 1);
rec[offsetof(struct lvar_leaf_entry, vle_key)] = recsize;
}
struct iam_uapi_op {
void *iul_key;
void *iul_rec;
};
struct iam_uapi_it {
struct iam_uapi_op iui_op;
__u16 iui_state;
};
enum iam_ioctl_cmd {
IAM_IOC_INIT = _IOW('i', 1, struct iam_uapi_info),
IAM_IOC_GETINFO = _IOR('i', 2, struct iam_uapi_info),
IAM_IOC_INSERT = _IOR('i', 3, struct iam_uapi_op),
IAM_IOC_LOOKUP = _IOWR('i', 4, struct iam_uapi_op),
IAM_IOC_DELETE = _IOR('i', 5, struct iam_uapi_op),
IAM_IOC_IT_START = _IOR('i', 6, struct iam_uapi_it),
IAM_IOC_IT_NEXT = _IOW('i', 7, struct iam_uapi_it),
IAM_IOC_IT_STOP = _IOR('i', 8, struct iam_uapi_it),
IAM_IOC_POLYMORPH = _IOR('i', 9, unsigned long)
};
static unsigned char hex2dec(unsigned char hex)
{
if (('0' <= hex) && (hex <= '9'))
return hex - '0';
else if (('a' <= hex) && (hex <= 'f'))
return hex - 'a' + 10;
else if (('A' <= hex) && (hex <= 'F'))
return hex - 'A' + 10;
exit(1);
}
static unsigned char *packdigit(unsigned char *number)
{
unsigned char *area;
unsigned char *scan;
area = calloc(strlen((char *)number) / 2 + 2, sizeof(char));
if (area) {
for (scan = area; *number; number += 2, scan++)
*scan = (hex2dec(number[0]) << 4) | hex2dec(number[1]);
}
return area;
}
static char *iam_convert(int size, int need_convert, char *source)
{
char *ptr;
unsigned char *opt;
if (!source)
return NULL;
if (need_convert) {
ptr = calloc(size + 1, sizeof(char));
if (!ptr)
return NULL;
opt = packdigit((unsigned char *)source);
if (!opt) {
free(ptr);
return NULL;
}
memcpy(ptr, opt, size + 1);
free(opt);
} else {
ptr = strdup(source);
}
return ptr;
}
static int iam_doop(int fd, struct iam_uapi_info *ua, int cmd,
int key_need_convert, char *key_buf,
int *keysize, char *save_key,
int rec_need_convert, char *rec_buf,
int *recsize, char *save_rec)
{
int ret;
char *key;
char *rec;
struct iam_uapi_op op;
key = iam_convert(ua->iui_keysize, key_need_convert, key_buf);
if (!key)
return -1;
rec = iam_convert(ua->iui_recsize, rec_need_convert, rec_buf);
if (!rec) {
free(key);
return -1;
}
op.iul_key = key;
op.iul_rec = rec;
ret = ioctl(fd, cmd, &op);
if (ret == 0) {
if ((keysize) && (*keysize > 0) && (save_key)) {
if (*keysize > ua->iui_keysize)
*keysize = ua->iui_keysize;
memcpy(save_key, key, *keysize);
}
if ((recsize) && (*recsize > 0) && (save_rec)) {
if (*recsize > ua->iui_recsize)
*recsize = ua->iui_recsize;
memcpy(save_rec, rec, *recsize);
}
}
free(key);
free(rec);
return ret;
}
/*
* Creat an iam file, but do NOT open it.
* Return 0 if success, else -1.
*/
int iam_creat(char *filename, enum iam_fmt_t fmt,
int blocksize, int keysize, int recsize, int ptrsize)
{
int fd;
char *buf;
if (!filename) {
errno = EINVAL;
return -1;
}
if ((fmt != FMT_LFIX) && (fmt != FMT_LVAR)) {
errno = EOPNOTSUPP;
return -1;
}
if (blocksize <= 100) {
errno = EINVAL;
return -1;
}
if (keysize < 1) {
errno = EINVAL;
return -1;
}
if (recsize < 0) {
errno = EINVAL;
return -1;
}
if (ptrsize != 4 && ptrsize != 8) {
errno = EINVAL;
return -1;
}
if (keysize + recsize + sizeof(struct iam_leaf_head) > blocksize / 3) {
errno = EINVAL;
return -1;
}
fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0600);
if (fd < 0)
return -1;
buf = malloc(blocksize);
if (!buf) {
close(fd);
return -1;
}
memset(buf, 0, blocksize);
if (fmt == FMT_LFIX)
lfix_root(buf, blocksize, keysize, ptrsize, recsize);
else
lvar_root(buf, blocksize, keysize, ptrsize, recsize);
if (write(fd, buf, blocksize) != blocksize) {
close(fd);
free(buf);
return -1;
}
memset(buf, 0, blocksize);
if (fmt == FMT_LFIX)
lfix_leaf(buf, blocksize, keysize, ptrsize, recsize);
else
lvar_leaf(buf, blocksize, keysize, ptrsize, recsize);
if (write(fd, buf, blocksize) != blocksize) {
close(fd);
free(buf);
return -1;
}
close(fd);
free(buf);
return 0;
}
/*
* Open an iam file, but do NOT creat it if the file doesn't exist.
* Please use iam_creat for creating the file before use iam_open.
* Return file id (fd) if success, else -1.
*/
int iam_open(char *filename, struct iam_uapi_info *ua)
{
int fd;
if (!filename) {
errno = EINVAL;
return -1;
}
if (!ua) {
errno = EINVAL;
return -1;
}
fd = open(filename, O_RDONLY);
if (fd < 0)
return -1;
if (ioctl(fd, IAM_IOC_INIT, ua) != 0) {
close(fd);
return -1;
}
if (ioctl(fd, IAM_IOC_GETINFO, ua) != 0) {
close(fd);
return -1;
}
return fd;
}
/*
* Close file opened by iam_open.
*/
int iam_close(int fd)
{
return close(fd);
}
/*
* Please use iam_open before use this function.
*/
int iam_insert(int fd, struct iam_uapi_info *ua,
int key_need_convert, char *key_buf,
int rec_need_convert, char *rec_buf)
{
return iam_doop(fd, ua, IAM_IOC_INSERT,
key_need_convert, key_buf, NULL, NULL,
rec_need_convert, rec_buf, NULL, NULL);
}
/*
* Please use iam_open before use this function.
*/
int iam_lookup(int fd, struct iam_uapi_info *ua,
int key_need_convert, char *key_buf,
int *keysize, char *save_key,
int rec_need_convert, char *rec_buf,
int *recsize, char *save_rec)
{
return iam_doop(fd, ua, IAM_IOC_LOOKUP,
key_need_convert, key_buf, keysize, save_key,
rec_need_convert, rec_buf, recsize, save_rec);
}
/*
* Please use iam_open before use this function.
*/
int iam_delete(int fd, struct iam_uapi_info *ua,
int key_need_convert, char *key_buf,
int rec_need_convert, char *rec_buf)
{
return iam_doop(fd, ua, IAM_IOC_DELETE,
key_need_convert, key_buf, NULL, NULL,
rec_need_convert, rec_buf, NULL, NULL);
}
/*
* Please use iam_open before use this function.
*/
int iam_it_start(int fd, struct iam_uapi_info *ua,
int key_need_convert, char *key_buf,
int *keysize, char *save_key,
int rec_need_convert, char *rec_buf,
int *recsize, char *save_rec)
{
return iam_doop(fd, ua, IAM_IOC_IT_START,
key_need_convert, key_buf, keysize, save_key,
rec_need_convert, rec_buf, recsize, save_rec);
}
/*
* Please use iam_open before use this function.
*/
int iam_it_next(int fd, struct iam_uapi_info *ua,
int key_need_convert, char *key_buf,
int *keysize, char *save_key,
int rec_need_convert, char *rec_buf,
int *recsize, char *save_rec)
{
return iam_doop(fd, ua, IAM_IOC_IT_NEXT,
key_need_convert, key_buf, keysize, save_key,
rec_need_convert, rec_buf, recsize, save_rec);
}
/*
* Please use iam_open before use this function.
*/
int iam_it_stop(int fd, struct iam_uapi_info *ua,
int key_need_convert, char *key_buf,
int rec_need_convert, char *rec_buf)
{
return iam_doop(fd, ua, IAM_IOC_IT_STOP,
key_need_convert, key_buf, NULL, NULL,
rec_need_convert, rec_buf, NULL, NULL);
}
/*
* Change iam file mode.
*/
int iam_polymorph(char *filename, unsigned long mode)
{
int fd;
int ret;
if (!filename) {
errno = EINVAL;
return -1;
}
fd = open(filename, O_RDONLY);
if (fd < 0)
return -1;
ret = ioctl(fd, IAM_IOC_POLYMORPH, mode);
close(fd);
return ret;
}