Merge branch 'master' of github.com:jthornber/thin-provisioning-tools
This commit is contained in:
commit
8035e10b2a
11
functional-tests/device-mapper/Makefile
Normal file
11
functional-tests/device-mapper/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
dm-ioctl.so: dm-ioctl.o
|
||||
gcc -shared -o $@ $< -laio
|
||||
|
||||
dm-ioctl.o: dm-ioctl.c
|
||||
gcc -std=gnu11 -fpic -I. -Wall -c -o $@ $<
|
||||
|
||||
.PHONEY: clean
|
||||
clean:
|
||||
rm -f dm-ioctl.so dm-ioctl.o
|
||||
|
||||
|
574
functional-tests/device-mapper/dm-ioctl.c
Normal file
574
functional-tests/device-mapper/dm-ioctl.c
Normal file
@ -0,0 +1,574 @@
|
||||
#include <linux/dm-ioctl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// assuming div is a power of 2
|
||||
static size_t round_up(size_t n, size_t div)
|
||||
{
|
||||
size_t mask = div - 1;
|
||||
return (n + mask) & ~mask;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static void *zalloc(size_t len)
|
||||
{
|
||||
void *ptr = malloc(len);
|
||||
if (ptr)
|
||||
memset(ptr, 0, len);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static void *payload(struct dm_ioctl *ctl)
|
||||
{
|
||||
return ((unsigned char *) ctl) + ctl->data_start;
|
||||
}
|
||||
|
||||
static struct dm_ioctl *alloc_ctl(size_t payload_size)
|
||||
{
|
||||
size_t len = sizeof(struct dm_ioctl) + payload_size;
|
||||
struct dm_ioctl *ctl = zalloc(len);
|
||||
|
||||
if (ctl) {
|
||||
ctl->version[0] = DM_VERSION_MAJOR;
|
||||
ctl->version[1] = DM_VERSION_MINOR;
|
||||
ctl->version[2] = DM_VERSION_PATCHLEVEL;
|
||||
ctl->data_size = len;
|
||||
ctl->data_start = sizeof(*ctl);
|
||||
}
|
||||
|
||||
return ctl;
|
||||
}
|
||||
|
||||
static void free_ctl(struct dm_ioctl *ctl)
|
||||
{
|
||||
free(ctl);
|
||||
}
|
||||
|
||||
// realloc only copies the dm_ioctl struct, not the payload.
|
||||
// old is always freed, even in case of error.
|
||||
static struct dm_ioctl *realloc_ctl(struct dm_ioctl *old, size_t extra_payload)
|
||||
{
|
||||
struct dm_ioctl *ctl;
|
||||
size_t old_payload_size = old->data_size - sizeof(struct dm_ioctl);
|
||||
size_t new_payload_size = old_payload_size + extra_payload;
|
||||
|
||||
ctl = alloc_ctl(new_payload_size);
|
||||
if (ctl)
|
||||
memcpy(payload(ctl), payload(old), sizeof(*ctl));
|
||||
|
||||
free_ctl(old);
|
||||
return ctl;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct dm_interface {
|
||||
int fd;
|
||||
};
|
||||
|
||||
// FIXME: pass in some form of log object?
|
||||
struct dm_interface *dm_open()
|
||||
{
|
||||
int fd;
|
||||
char path[1024];
|
||||
struct dm_interface *dmi;
|
||||
|
||||
snprintf(path, sizeof(path), "/dev/%s/%s", DM_DIR, DM_CONTROL_NODE);
|
||||
fd = open(path, O_RDWR | O_EXCL);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
|
||||
dmi = zalloc(sizeof(*dmi));
|
||||
if (dmi)
|
||||
dmi->fd = fd;
|
||||
else
|
||||
close(fd);
|
||||
|
||||
return dmi;
|
||||
}
|
||||
|
||||
void dm_close(struct dm_interface *dmi)
|
||||
{
|
||||
close(dmi->fd);
|
||||
free(dmi);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct dev_list {
|
||||
struct dev_list *next;
|
||||
unsigned major;
|
||||
unsigned minor;
|
||||
char *name;
|
||||
};
|
||||
|
||||
void free_dev_list(struct dev_list *dl)
|
||||
{
|
||||
struct dev_list *next;
|
||||
|
||||
while (dl) {
|
||||
next = dl->next;
|
||||
free(dl->name);
|
||||
free(dl);
|
||||
dl = next;
|
||||
}
|
||||
}
|
||||
|
||||
struct dev_list_builder {
|
||||
struct dev_list *head, *tail;
|
||||
};
|
||||
|
||||
static void dlb_init(struct dev_list_builder *dlb)
|
||||
{
|
||||
dlb->head = dlb->tail = NULL;
|
||||
}
|
||||
|
||||
static int dlb_append(struct dev_list_builder *dlb,
|
||||
unsigned major, unsigned minor, const char *name)
|
||||
{
|
||||
struct dev_list *dl = malloc(sizeof(*dl));
|
||||
|
||||
if (!dl)
|
||||
return -ENOMEM;
|
||||
|
||||
dl->next = NULL;
|
||||
dl->major = major;
|
||||
dl->minor = minor;
|
||||
dl->name = strdup(name);
|
||||
|
||||
if (dlb->head) {
|
||||
dlb->tail->next = dl;
|
||||
dlb->tail = dl;
|
||||
} else
|
||||
dlb->head = dlb->tail = dl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dev_list *dlb_get(struct dev_list_builder *dlb)
|
||||
{
|
||||
return dlb->head;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
static int copy_string(char *dest, const char *src, size_t max)
|
||||
{
|
||||
if (strlen(src) + 1 > max)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(dest, src);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int copy_name(struct dm_ioctl *ctl, const char *name)
|
||||
{
|
||||
return copy_string(ctl->name, name, DM_NAME_LEN);
|
||||
}
|
||||
|
||||
static int copy_uuid(struct dm_ioctl *ctl, const char *uuid)
|
||||
{
|
||||
return copy_string(ctl->uuid, uuid, DM_UUID_LEN);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
int dm_version(struct dm_interface *dmi, uint32_t *major, uint32_t *minor, uint32_t *patch)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *ctl = alloc_ctl(0);
|
||||
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
r = ioctl(dmi->fd, DM_VERSION, ctl);
|
||||
*major = ctl->version[0];
|
||||
*minor = ctl->version[1];
|
||||
*patch = ctl->version[2];
|
||||
free_ctl(ctl);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_remove_all(struct dm_interface *dmi)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *ctl = alloc_ctl(0);
|
||||
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
r = ioctl(dmi->fd, DM_REMOVE_ALL, ctl);
|
||||
free_ctl(ctl);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool list_devices(struct dm_interface *dmi, struct dm_ioctl *ctl,
|
||||
size_t payload_size, struct dev_list **devs,
|
||||
int *r)
|
||||
{
|
||||
struct dm_name_list *nl;
|
||||
struct dev_list_builder dlb;
|
||||
|
||||
*r = ioctl(dmi->fd, DM_LIST_DEVICES, ctl);
|
||||
if (*r < 0)
|
||||
return true;
|
||||
|
||||
if (ctl->flags & DM_BUFFER_FULL_FLAG) {
|
||||
free_ctl(ctl);
|
||||
return false;
|
||||
}
|
||||
|
||||
dlb_init(&dlb);
|
||||
nl = (struct dm_name_list *) payload(ctl);
|
||||
|
||||
if (nl->dev) {
|
||||
for (;;) {
|
||||
dlb_append(&dlb, major(nl->dev), minor(nl->dev), nl->name);
|
||||
|
||||
if (!nl->next)
|
||||
break;
|
||||
|
||||
nl = (struct dm_name_list *) (((unsigned char *) nl) + nl->next);
|
||||
}
|
||||
}
|
||||
|
||||
*devs = dlb_get(&dlb);
|
||||
return true;
|
||||
}
|
||||
|
||||
int dm_list_devices(struct dm_interface *dmi, struct dev_list **devs)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *ctl;
|
||||
size_t payload_size = 8192;
|
||||
|
||||
ctl = alloc_ctl(payload_size);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
while (!list_devices(dmi, ctl, payload_size, devs, &r)) {
|
||||
payload_size *= 2;
|
||||
ctl = realloc_ctl(ctl, payload_size);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
free_ctl(ctl);
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_create_device(struct dm_interface *dmi, const char *name, const char *uuid)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *ctl = alloc_ctl(0);
|
||||
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
r = copy_name(ctl, name);
|
||||
if (r) {
|
||||
free_ctl(ctl);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = copy_uuid(ctl, name);
|
||||
if (r) {
|
||||
free_ctl(ctl);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = ioctl(dmi->fd, DM_DEV_CREATE, ctl);
|
||||
free_ctl(ctl);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dev_cmd(struct dm_interface *dmi, const char *name, int request, unsigned flags)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *ctl = alloc_ctl(0);
|
||||
ctl->flags = flags;
|
||||
r = copy_name(ctl, name);
|
||||
if (r) {
|
||||
free_ctl(ctl);
|
||||
return -ENOMEM;
|
||||
}
|
||||
r = ioctl(dmi->fd, request);
|
||||
free_ctl(ctl);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_remove_device(struct dm_interface *dmi, const char *name)
|
||||
{
|
||||
return dev_cmd(dmi, name, DM_DEV_REMOVE, 0);
|
||||
}
|
||||
|
||||
int dm_suspend_device(struct dm_interface *dmi, const char *name)
|
||||
{
|
||||
return dev_cmd(dmi, name, DM_DEV_SUSPEND, DM_SUSPEND_FLAG);
|
||||
}
|
||||
|
||||
int dm_resume_device(struct dm_interface *dmi, const char *name)
|
||||
{
|
||||
return dev_cmd(dmi, name, DM_DEV_SUSPEND, 0);
|
||||
}
|
||||
|
||||
int dm_clear_device(struct dm_interface *dmi, const char *name)
|
||||
{
|
||||
return dev_cmd(dmi, name, DM_TABLE_CLEAR, 0);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
struct target {
|
||||
struct target *next;
|
||||
|
||||
uint64_t len;
|
||||
char *type;
|
||||
char *args;
|
||||
};
|
||||
|
||||
void free_targets(struct target *t)
|
||||
{
|
||||
while (t) {
|
||||
struct target *next = t->next;
|
||||
free(t->type);
|
||||
free(t->args);
|
||||
t = next;
|
||||
}
|
||||
}
|
||||
|
||||
struct target_builder {
|
||||
struct target *head, *tail;
|
||||
};
|
||||
|
||||
static void tb_init(struct target_builder *tb)
|
||||
{
|
||||
tb->head = tb->tail = NULL;
|
||||
}
|
||||
|
||||
static int tb_append(struct target_builder *tb, uint64_t len, char *type, char *args)
|
||||
{
|
||||
struct target *t = malloc(sizeof(*t));
|
||||
if (!t)
|
||||
return -ENOMEM;
|
||||
|
||||
t->next = NULL;
|
||||
t->len = len;
|
||||
t->type = strdup(type);
|
||||
t->args = strdup(args);
|
||||
|
||||
if (tb->head) {
|
||||
tb->tail->next = t;
|
||||
tb->tail = t;
|
||||
} else
|
||||
tb->head = tb->tail = t;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct target *tb_get(struct target_builder *tb)
|
||||
{
|
||||
return tb->head;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// FIXME: provide some way of freeing a target list.
|
||||
// FIXME: check the result from alloc_ctl is always being checked.
|
||||
|
||||
static size_t calc_load_payload(struct target *t)
|
||||
{
|
||||
size_t space = 0;
|
||||
|
||||
while (t) {
|
||||
space += sizeof(struct dm_target_spec);
|
||||
space += strlen(t->args) + 16;
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
return space + 128;
|
||||
}
|
||||
|
||||
static int prep_load(struct dm_ioctl *ctl, size_t payload_size,
|
||||
const char *name, struct target *t)
|
||||
{
|
||||
int r;
|
||||
uint64_t current_sector = 0;
|
||||
struct dm_target_spec *spec;
|
||||
|
||||
spec = payload(ctl);
|
||||
|
||||
while (t) {
|
||||
spec->sector_start = current_sector;
|
||||
current_sector += t->len;
|
||||
spec->length = t->len;
|
||||
spec->status = 0;
|
||||
r = copy_string(spec->target_type, t->type, DM_MAX_TYPE_NAME);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
r = copy_string((char *) spec + 1, t->args, payload_size);
|
||||
if (r)
|
||||
return r;
|
||||
|
||||
spec->next = sizeof(*spec) + round_up(strlen(t->args) + 1, 8);
|
||||
payload_size -= spec->next;
|
||||
spec = (struct dm_target_spec *) (((char *) spec) + spec->next);
|
||||
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int dm_load(struct dm_interface *dmi, const char *name,
|
||||
struct target *targets)
|
||||
{
|
||||
int r;
|
||||
size_t payload_size = calc_load_payload(targets);
|
||||
struct dm_ioctl *ctl = alloc_ctl(payload_size);
|
||||
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
r = prep_load(ctl, payload_size, name, targets);
|
||||
if (r) {
|
||||
free_ctl(ctl);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = copy_name(ctl, name);
|
||||
if (r) {
|
||||
free_ctl(ctl);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = ioctl(dmi->fd, DM_TABLE_LOAD, ctl);
|
||||
free_ctl(ctl);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// returns false if control buffer too small.
|
||||
static bool get_status(struct dm_interface *dmi, struct dm_ioctl *ctl,
|
||||
const char *name, unsigned flags,
|
||||
int *result)
|
||||
{
|
||||
*result = copy_name(ctl, name);
|
||||
if (*result) {
|
||||
free_ctl(ctl);
|
||||
return true;
|
||||
}
|
||||
|
||||
ctl->flags = flags;
|
||||
ctl->target_count = 0;
|
||||
|
||||
*result = ioctl(dmi->fd, DM_TABLE_STATUS, ctl);
|
||||
if (*result)
|
||||
return true;
|
||||
|
||||
if (ctl->flags & DM_BUFFER_FULL_FLAG)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int unpack_status(struct dm_ioctl *ctl, struct target **result)
|
||||
{
|
||||
unsigned i;
|
||||
struct target_builder tb;
|
||||
struct dm_target_spec *spec = payload(ctl);
|
||||
|
||||
tb_init(&tb);
|
||||
for (i = 0; i < ctl->target_count; i++) {
|
||||
tb_append(&tb, spec->length, spec->target_type, (char *) (spec + 1));
|
||||
spec = (struct dm_target_spec *) (((char *) spec) + spec->next);
|
||||
}
|
||||
|
||||
*result = tb_get(&tb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int status_cmd(struct dm_interface *dmi, const char *name,
|
||||
struct target **targets, unsigned flags)
|
||||
{
|
||||
int r;
|
||||
size_t payload_size = 8192;
|
||||
struct dm_ioctl *ctl = NULL;
|
||||
|
||||
ctl = alloc_ctl(payload_size);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
retry:
|
||||
if (!get_status(dmi, ctl, name, flags, &r)) {
|
||||
payload_size *= 2;
|
||||
ctl = realloc_ctl(ctl, payload_size);
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
|
||||
goto retry;
|
||||
}
|
||||
|
||||
r = unpack_status(ctl, targets);
|
||||
free_ctl(ctl);
|
||||
return r;
|
||||
}
|
||||
|
||||
int dm_status(struct dm_interface *dmi, const char *name, struct target **targets)
|
||||
{
|
||||
return status_cmd(dmi, name, targets, 0);
|
||||
}
|
||||
|
||||
int dm_table(struct dm_interface *dmi, const char *name, struct target **targets)
|
||||
{
|
||||
return status_cmd(dmi, name, targets, DM_STATUS_TABLE_FLAG);
|
||||
}
|
||||
|
||||
#if 0
|
||||
int dm_info(struct dm_interface *dmi, const char *name, struct target **targets)
|
||||
{
|
||||
return status_cmd(dmi, name, targets, DM_STATUS_INFO_FLAG);
|
||||
}
|
||||
#endif
|
||||
|
||||
int dm_message(struct dm_interface *dmi, const char *name, uint64_t sector,
|
||||
const char *msg_str)
|
||||
{
|
||||
int r;
|
||||
size_t msg_len = strlen(msg_str) + 1;
|
||||
size_t payload_size = msg_len + 32;
|
||||
struct dm_ioctl *ctl = alloc_ctl(payload_size);
|
||||
struct dm_target_msg *msg;
|
||||
|
||||
if (!ctl)
|
||||
return -ENOMEM;
|
||||
msg = payload(ctl);
|
||||
copy_name(ctl, name);
|
||||
msg->sector = sector;
|
||||
memcpy(msg->message, msg_str, msg_len);
|
||||
|
||||
r = ioctl(dmi->fd, DM_TARGET_MSG, ctl);
|
||||
free_ctl(ctl);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
22
functional-tests/device-mapper/dm-tests.scm
Normal file
22
functional-tests/device-mapper/dm-tests.scm
Normal file
@ -0,0 +1,22 @@
|
||||
(library
|
||||
(device-mapper dm-tests)
|
||||
(export register-dm-tests)
|
||||
(import (device-mapper ioctl)
|
||||
(chezscheme)
|
||||
(functional-tests)
|
||||
(fmt fmt)
|
||||
(process)
|
||||
(temp-file))
|
||||
|
||||
;; We have to export something that forces all the initialisation expressions
|
||||
;; to run.
|
||||
(define (register-dm-tests) #t)
|
||||
|
||||
;;;-----------------------------------------------------------
|
||||
;;; scenarios
|
||||
;;;-----------------------------------------------------------
|
||||
(define-scenario (dm create-interface)
|
||||
"create and destroy an ioctl interface object"
|
||||
(with-dm (dm) #t))
|
||||
|
||||
)
|
206
functional-tests/device-mapper/ioctl.scm
Normal file
206
functional-tests/device-mapper/ioctl.scm
Normal file
@ -0,0 +1,206 @@
|
||||
(library
|
||||
(device-mapper ioctl)
|
||||
|
||||
(export dm-open
|
||||
dm-close
|
||||
with-dm
|
||||
|
||||
get-version
|
||||
remove-all
|
||||
list-devices
|
||||
|
||||
create-device)
|
||||
|
||||
(import (chezscheme)
|
||||
(fmt fmt)
|
||||
(srfi s8 receive)
|
||||
(utils))
|
||||
|
||||
(define __ (load-shared-object "./device-mapper/dm-ioctl.so"))
|
||||
|
||||
(define (fail msg)
|
||||
(raise
|
||||
(condition
|
||||
(make-error)
|
||||
(make-message-condition msg))))
|
||||
|
||||
(define-ftype DMIoctlInterface
|
||||
(struct
|
||||
(fd int)))
|
||||
|
||||
(define open% (foreign-procedure "dm_open" () (* DMIoctlInterface)))
|
||||
|
||||
(define (dm-open)
|
||||
(let ((ptr (open%)))
|
||||
(if (ftype-pointer-null? ptr)
|
||||
(fail "couldn't open ioctl interface (permissions?)")
|
||||
ptr)))
|
||||
|
||||
(define dm-close
|
||||
(foreign-procedure "dm_close" ((* DMIoctlInterface)) void))
|
||||
|
||||
(define-syntax with-dm
|
||||
(syntax-rules ()
|
||||
((_ (name) b1 b2 ...)
|
||||
(let ((name (dm-open)))
|
||||
(dynamic-wind
|
||||
(lambda () #f)
|
||||
(lambda () b1 b2 ...)
|
||||
(lambda () (dm-close name)))))))
|
||||
|
||||
(define-record-type dm-version (fields major minor patch))
|
||||
|
||||
(define (get-version dm)
|
||||
(define get
|
||||
(foreign-procedure "dm_version" ((* DMIoctlInterface)
|
||||
(* unsigned-32)
|
||||
(* unsigned-32)
|
||||
(* unsigned-32)) int))
|
||||
|
||||
(define (alloc-u32)
|
||||
(make-ftype-pointer unsigned-32
|
||||
(foreign-alloc (ftype-sizeof unsigned-32))))
|
||||
|
||||
(define (deref-u32 p)
|
||||
(ftype-ref unsigned-32 () p))
|
||||
|
||||
(let ((major (alloc-u32))
|
||||
(minor (alloc-u32))
|
||||
(patch (alloc-u32)))
|
||||
(if (zero? (get dm major minor patch))
|
||||
(let ((r (make-dm-version (deref-u32 major)
|
||||
(deref-u32 minor)
|
||||
(deref-u32 patch))))
|
||||
(foreign-free (ftype-pointer-address major))
|
||||
(foreign-free (ftype-pointer-address minor))
|
||||
(foreign-free (ftype-pointer-address patch))
|
||||
r)
|
||||
(fail "couldn't get dm version"))))
|
||||
|
||||
(define (remove-all dm)
|
||||
(define do-it
|
||||
(foreign-procedure "dm_remove_all" ((* DMIoctlInterface)) int))
|
||||
|
||||
(let ((r (do-it dm)))
|
||||
(unless (zero? r)
|
||||
(fail "remove-all failed"))))
|
||||
|
||||
(define-ftype DevList
|
||||
(struct
|
||||
(next (* DevList))
|
||||
(major unsigned)
|
||||
(minor unsigned)
|
||||
(name (* unsigned-8))))
|
||||
|
||||
(define-ftype DevListPtr (* DevList))
|
||||
|
||||
(define-record-type device-details
|
||||
(fields name major minor))
|
||||
|
||||
(define (cstring->string str)
|
||||
(let loop ((i 0)
|
||||
(acc '()))
|
||||
(let ((c (ftype-ref unsigned-8 () str i)))
|
||||
(if (zero? c)
|
||||
(list->string (reverse acc))
|
||||
(loop (+ i 1) (cons (integer->char c) acc))))))
|
||||
|
||||
(define (string->cstring str)
|
||||
(let* ((len (string-length str))
|
||||
(cstr (make-ftype-pointer unsigned-8
|
||||
(foreign-alloc (+ 1 len)))))
|
||||
(let loop ((i 0))
|
||||
(if (= i len)
|
||||
(begin
|
||||
(ftype-set! unsigned-8 () cstr i 0)
|
||||
cstr)
|
||||
(ftype-set! unsigned-8 () cstr i (string-ref str i))))))
|
||||
|
||||
;;; FIXME: put a dynamic wind in to ensure the dev list gets freed
|
||||
(define (list-devices dm)
|
||||
(define list-devs
|
||||
(foreign-procedure "dm_list_devices" ((* DMIoctlInterface) (* DevListPtr)) int))
|
||||
|
||||
(let ((pp (make-ftype-pointer DevListPtr
|
||||
(foreign-alloc (ftype-sizeof DevListPtr)))))
|
||||
(if (zero? (list-devs dm pp))
|
||||
(let loop ((dl (ftype-ref DevListPtr () pp))
|
||||
(acc '()))
|
||||
;(fmt #t "dl: " dl ", acc: " acc)
|
||||
(if (ftype-pointer-null? dl)
|
||||
acc
|
||||
(loop (ftype-ref DevList (next) dl)
|
||||
(cons (make-device-details
|
||||
(cstring->string (ftype-ref DevList (name) dl))
|
||||
(ftype-ref DevList (major) dl)
|
||||
(ftype-ref DevList (minor) dl))
|
||||
acc))))
|
||||
(fail "dm_list_devices ioctl failed"))))
|
||||
|
||||
(define (create-device dm name uuid)
|
||||
(define create
|
||||
(foreign-procedure "dm_create_device" ((* DMIoctlInterface) string string) int))
|
||||
|
||||
(unless (zero? (create dm name uuid))
|
||||
(fail "create-device failed")))
|
||||
|
||||
(define-syntax define-dev-cmd
|
||||
(syntax-rules ()
|
||||
((_ nm proc)
|
||||
(define (nm dm name)
|
||||
(define fn
|
||||
(foreign-procedure proc ((* DMIoctlInterface) string) int))
|
||||
|
||||
(unless (zero? (fn dm name))
|
||||
(fail (string-append proc " failed")))))))
|
||||
|
||||
(define-dev-cmd remove-device "dm_remove_device")
|
||||
(define-dev-cmd suspend-device "dm_suspend_device")
|
||||
(define-dev-cmd resume-device "dm_resume_device")
|
||||
(define-dev-cmd clear-device "dm_clear_device")
|
||||
|
||||
(define-ftype Target
|
||||
(struct
|
||||
(next (* Target))
|
||||
(len unsigned-64)
|
||||
(type (* unsigned-8))
|
||||
(args (* unsigned-8))))
|
||||
|
||||
(define-ftype TargetPtr (* Target))
|
||||
(define-record-type target
|
||||
(fields (mutable len) (mutable type) (mutable args)))
|
||||
|
||||
(define (build-c-target next len type args)
|
||||
(let ((t (make-ftype-pointer Target
|
||||
(foreign-alloc
|
||||
(ftype-sizeof Target)))))
|
||||
(ftype-set! Target (next) t next)
|
||||
(ftype-set! Target (len) t len)
|
||||
(ftype-set! Target (type) t (string->cstring type))
|
||||
(ftype-set! Target (args) t (string->cstring args))))
|
||||
|
||||
(define (build-c-targets targets)
|
||||
(let loop ((t targets)
|
||||
(tail (make-ftype-pointer Target 0)))
|
||||
(if (null? t)
|
||||
tail
|
||||
(loop (cdr targets)
|
||||
(build-c-target tail (target-len t) (target-type t) (target-args t))))))
|
||||
|
||||
(define (free-c-targets t)
|
||||
(let loop ((t t)
|
||||
(acc '()))
|
||||
(if (ftype-pointer-null? t)
|
||||
(map foreign-free acc)
|
||||
(loop (ftype-ref Target (next) t) (cons t acc)))))
|
||||
|
||||
(define (load-table dm name targets)
|
||||
(define load
|
||||
(foreign-procedure "dm_load" ((* DMIoctlInterface) string (* Target)) int))
|
||||
|
||||
(let* ((ctargets (build-c-targets targets))
|
||||
(r (load dm name ctargets)))
|
||||
(free-c-targets ctargets)
|
||||
(unless (zero? r)
|
||||
(fail "dm_load failed"))))
|
||||
)
|
@ -7,6 +7,7 @@
|
||||
(functional-tests)
|
||||
(bcache bcache-tests)
|
||||
(cache-functional-tests)
|
||||
(device-mapper dm-tests)
|
||||
(era-functional-tests)
|
||||
(parser-combinators)
|
||||
(only (srfi s1 lists) break)
|
||||
@ -55,10 +56,14 @@
|
||||
(intersperse "/"
|
||||
(map symbol->string keys)))))))
|
||||
|
||||
(define (string-prefix? p str)
|
||||
(and (>= (string-length str) (string-length p))
|
||||
(string=? p (substring str 0 (string-length p)))))
|
||||
|
||||
;; If the filter begins with 're:' then we make a regex matcher, otherwise
|
||||
;; we use a simple string matcher.
|
||||
(define (mk-single-matcher pattern)
|
||||
(if (string=? (substring pattern 0 3) "re:")
|
||||
(if (string-prefix? "re:" pattern)
|
||||
(mk-regex-matcher (substring pattern 3 (string-length pattern)))
|
||||
(mk-string-matcher pattern)))
|
||||
|
||||
@ -175,6 +180,7 @@
|
||||
(register-cache-tests)
|
||||
(register-era-tests)
|
||||
(register-bcache-tests)
|
||||
(register-dm-tests)
|
||||
|
||||
(with-dir "test-output"
|
||||
((parse-command-line)))
|
||||
|
Loading…
Reference in New Issue
Block a user