From a1acd0c8680cabe3f3423238d0af3cbeddcf11e0 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Sep 2017 14:02:42 +0100 Subject: [PATCH 1/3] [functional-tests] fix bug in run_tests if command line pattern was shorter than "re:" --- functional-tests/run-tests | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/functional-tests/run-tests b/functional-tests/run-tests index 2488742..c08f40f 100755 --- a/functional-tests/run-tests +++ b/functional-tests/run-tests @@ -55,10 +55,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))) From 742629fb8d9670eef75a8a12afd9f9ff7f11c649 Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Tue, 26 Sep 2017 15:16:45 +0100 Subject: [PATCH 2/3] [functional-tests] start working on dm-ioctl bindings --- functional-tests/device-mapper/Makefile | 11 + functional-tests/device-mapper/dm-ioctl.c | 573 ++++++++++++++++++++ functional-tests/device-mapper/dm-tests.scm | 22 + functional-tests/device-mapper/ioctl.scm | 78 +++ functional-tests/run-tests | 2 + 5 files changed, 686 insertions(+) create mode 100644 functional-tests/device-mapper/Makefile create mode 100644 functional-tests/device-mapper/dm-ioctl.c create mode 100644 functional-tests/device-mapper/dm-tests.scm create mode 100644 functional-tests/device-mapper/ioctl.scm diff --git a/functional-tests/device-mapper/Makefile b/functional-tests/device-mapper/Makefile new file mode 100644 index 0000000..0102820 --- /dev/null +++ b/functional-tests/device-mapper/Makefile @@ -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 + + diff --git a/functional-tests/device-mapper/dm-ioctl.c b/functional-tests/device-mapper/dm-ioctl.c new file mode 100644 index 0000000..d643199 --- /dev/null +++ b/functional-tests/device-mapper/dm-ioctl.c @@ -0,0 +1,573 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//---------------------------------------------------------------- + +// 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->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; +} + +//---------------------------------------------------------------- diff --git a/functional-tests/device-mapper/dm-tests.scm b/functional-tests/device-mapper/dm-tests.scm new file mode 100644 index 0000000..1c174cc --- /dev/null +++ b/functional-tests/device-mapper/dm-tests.scm @@ -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)) + + ) diff --git a/functional-tests/device-mapper/ioctl.scm b/functional-tests/device-mapper/ioctl.scm new file mode 100644 index 0000000..bf5c37b --- /dev/null +++ b/functional-tests/device-mapper/ioctl.scm @@ -0,0 +1,78 @@ +(library + (device-mapper ioctl) + + (export dm-open + dm-close + with-dm + + get-version) + + (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-ftype PtrU32 (* unsigned-32)) + + (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))) + (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))) + + +) diff --git a/functional-tests/run-tests b/functional-tests/run-tests index c08f40f..333ea90 100755 --- a/functional-tests/run-tests +++ b/functional-tests/run-tests @@ -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) @@ -179,6 +180,7 @@ (register-cache-tests) (register-era-tests) (register-bcache-tests) +(register-dm-tests) (with-dir "test-output" ((parse-command-line))) From 5e2cd1e9f2b97174cec951cc1758808b6232a03d Mon Sep 17 00:00:00 2001 From: Joe Thornber Date: Wed, 27 Sep 2017 15:50:16 +0100 Subject: [PATCH 3/3] [functional-tests] more work on the dm-ioctl bindings --- functional-tests/device-mapper/dm-ioctl.c | 3 +- functional-tests/device-mapper/ioctl.scm | 150 ++++++++++++++++++++-- 2 files changed, 141 insertions(+), 12 deletions(-) diff --git a/functional-tests/device-mapper/dm-ioctl.c b/functional-tests/device-mapper/dm-ioctl.c index d643199..c7d7ca5 100644 --- a/functional-tests/device-mapper/dm-ioctl.c +++ b/functional-tests/device-mapper/dm-ioctl.c @@ -144,6 +144,7 @@ static int dlb_append(struct dev_list_builder *dlb, if (!dl) return -ENOMEM; + dl->next = NULL; dl->major = major; dl->minor = minor; dl->name = strdup(name); @@ -266,8 +267,8 @@ int dm_list_devices(struct dm_interface *dmi, struct dev_list **devs) if (!ctl) return -ENOMEM; } - free_ctl(ctl); + free_ctl(ctl); return r; } diff --git a/functional-tests/device-mapper/ioctl.scm b/functional-tests/device-mapper/ioctl.scm index bf5c37b..8093569 100644 --- a/functional-tests/device-mapper/ioctl.scm +++ b/functional-tests/device-mapper/ioctl.scm @@ -5,7 +5,11 @@ dm-close with-dm - get-version) + get-version + remove-all + list-devices + + create-device) (import (chezscheme) (fmt fmt) @@ -46,8 +50,6 @@ (define-record-type dm-version (fields major minor patch)) - (define-ftype PtrU32 (* unsigned-32)) - (define (get-version dm) (define get (foreign-procedure "dm_version" ((* DMIoctlInterface) @@ -65,14 +67,140 @@ (let ((major (alloc-u32)) (minor (alloc-u32)) (patch (alloc-u32))) - (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))) + (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")))) )