(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 "../lib/libft.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))))) ;; targets should be dlambdas with 'size, 'type and 'format methods (define (load-table dm name targets) (define load (foreign-procedure "dm_load" ((* DMIoctlInterface) string (* Target)) int)) (define (dlambda->target t) (make-target (t 'size) (t 'type) (t 'format))) (let* ((ctargets (build-c-targets (map dlambda->target targets))) (r (load dm name ctargets))) (free-c-targets ctargets) (unless (zero? r) (fail "dm_load failed")))) )