1 /* nbdkit
2 * Copyright (C) 2014-2020 Red Hat Inc.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #include <config.h>
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <errno.h>
39
40 #include <caml/alloc.h>
41 #include <caml/callback.h>
42 #include <caml/fail.h>
43 #include <caml/memory.h>
44 #include <caml/mlvalues.h>
45 #include <caml/printexc.h>
46 #include <caml/threads.h>
47
48 #define NBDKIT_API_VERSION 2
49
50 #include <nbdkit-plugin.h>
51
52 /* Replacement if caml_alloc_initialized_string is missing, added
53 * to OCaml runtime in 2017.
54 */
55 #ifndef HAVE_CAML_ALLOC_INITIALIZED_STRING
56 static inline value
caml_alloc_initialized_string(mlsize_t len,const char * p)57 caml_alloc_initialized_string (mlsize_t len, const char *p)
58 {
59 value sv = caml_alloc_string (len);
60 memcpy ((char *) String_val (sv), p, len);
61 return sv;
62 }
63 #endif
64
65 /* This constructor runs when the plugin loads, and initializes the
66 * OCaml runtime, and lets the plugin set up its callbacks.
67 */
68 static void constructor (void) __attribute__((constructor));
69 static void
constructor(void)70 constructor (void)
71 {
72 char *argv[2] = { "nbdkit", NULL };
73
74 /* Initialize OCaml runtime. */
75 caml_startup (argv);
76 }
77
78 /* Instead of using the NBDKIT_REGISTER_PLUGIN macro, we construct the
79 * nbdkit_plugin struct and return it from our own plugin_init
80 * function.
81 */
82 static void unload_wrapper (void);
83 static void remove_roots (void);
84
85 static struct nbdkit_plugin plugin = {
86 ._struct_size = sizeof (plugin),
87 ._api_version = NBDKIT_API_VERSION,
88 ._thread_model = NBDKIT_THREAD_MODEL_PARALLEL,
89
90 /* The following field is used as a canary to detect whether the
91 * OCaml code started up and called us back successfully. If it's
92 * still set to NULL when plugin_init is called, then we can print a
93 * suitable error message.
94 */
95 .name = NULL,
96
97 .unload = unload_wrapper,
98 };
99
100 struct nbdkit_plugin *
plugin_init(void)101 plugin_init (void)
102 {
103 if (plugin.name == NULL) {
104 fprintf (stderr, "error: OCaml code did not call NBDKit.register_plugin\n");
105 exit (EXIT_FAILURE);
106 }
107 return &plugin;
108 }
109
110 /* These globals store the OCaml functions that we actually call.
111 * Also the assigned ones are roots to ensure the GC doesn't free them.
112 */
113 static value load_fn;
114 static value unload_fn;
115
116 static value dump_plugin_fn;
117
118 static value config_fn;
119 static value config_complete_fn;
120 static value thread_model_fn;
121
122 static value get_ready_fn;
123
124 static value preconnect_fn;
125 static value open_fn;
126 static value close_fn;
127
128 static value get_size_fn;
129
130 static value can_cache_fn;
131 static value can_extents_fn;
132 static value can_fast_zero_fn;
133 static value can_flush_fn;
134 static value can_fua_fn;
135 static value can_multi_conn_fn;
136 static value can_trim_fn;
137 static value can_write_fn;
138 static value can_zero_fn;
139 static value is_rotational_fn;
140
141 static value pread_fn;
142 static value pwrite_fn;
143 static value flush_fn;
144 static value trim_fn;
145 static value zero_fn;
146 static value extents_fn;
147 static value cache_fn;
148
149 /*----------------------------------------------------------------------*/
150 /* Wrapper functions that translate calls from C (ie. nbdkit) to OCaml. */
151
152 static void
load_wrapper(void)153 load_wrapper (void)
154 {
155 caml_leave_blocking_section ();
156 caml_callback (load_fn, Val_unit);
157 caml_enter_blocking_section ();
158 }
159
160 /* We always have an unload function, since it also has to free the
161 * globals we allocated.
162 */
163 static void
unload_wrapper(void)164 unload_wrapper (void)
165 {
166 if (unload_fn) {
167 caml_leave_blocking_section ();
168 caml_callback (unload_fn, Val_unit);
169 caml_enter_blocking_section ();
170 }
171
172 free ((char *) plugin.name);
173 free ((char *) plugin.longname);
174 free ((char *) plugin.version);
175 free ((char *) plugin.description);
176 free ((char *) plugin.config_help);
177
178 remove_roots ();
179 }
180
181 static void
dump_plugin_wrapper(void)182 dump_plugin_wrapper (void)
183 {
184 CAMLparam0 ();
185 CAMLlocal1 (rv);
186
187 caml_leave_blocking_section ();
188
189 rv = caml_callback_exn (dump_plugin_fn, Val_unit);
190 if (Is_exception_result (rv))
191 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
192 caml_enter_blocking_section ();
193 CAMLreturn0;
194 }
195
196 static int
config_wrapper(const char * key,const char * val)197 config_wrapper (const char *key, const char *val)
198 {
199 CAMLparam0 ();
200 CAMLlocal3 (keyv, valv, rv);
201
202 caml_leave_blocking_section ();
203
204 keyv = caml_copy_string (key);
205 valv = caml_copy_string (val);
206
207 rv = caml_callback2_exn (config_fn, keyv, valv);
208 if (Is_exception_result (rv)) {
209 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
210 caml_enter_blocking_section ();
211 CAMLreturnT (int, -1);
212 }
213
214 caml_enter_blocking_section ();
215 CAMLreturnT (int, 0);
216 }
217
218 static int
config_complete_wrapper(void)219 config_complete_wrapper (void)
220 {
221 CAMLparam0 ();
222 CAMLlocal1 (rv);
223
224 caml_leave_blocking_section ();
225
226 rv = caml_callback_exn (config_complete_fn, Val_unit);
227 if (Is_exception_result (rv)) {
228 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
229 caml_enter_blocking_section ();
230 CAMLreturnT (int, -1);
231 }
232
233 caml_enter_blocking_section ();
234 CAMLreturnT (int, 0);
235 }
236
237 static int
thread_model_wrapper(void)238 thread_model_wrapper (void)
239 {
240 CAMLparam0 ();
241 CAMLlocal1 (rv);
242
243 caml_leave_blocking_section ();
244
245 rv = caml_callback_exn (thread_model_fn, Val_unit);
246 if (Is_exception_result (rv)) {
247 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
248 caml_enter_blocking_section ();
249 CAMLreturnT (int, -1);
250 }
251
252 caml_enter_blocking_section ();
253 CAMLreturnT (int, Int_val (rv));
254 }
255
256 static int
get_ready_wrapper(void)257 get_ready_wrapper (void)
258 {
259 CAMLparam0 ();
260 CAMLlocal1 (rv);
261
262 caml_leave_blocking_section ();
263
264 rv = caml_callback_exn (get_ready_fn, Val_unit);
265 if (Is_exception_result (rv)) {
266 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
267 caml_enter_blocking_section ();
268 CAMLreturnT (int, -1);
269 }
270
271 caml_enter_blocking_section ();
272 CAMLreturnT (int, 0);
273 }
274
275 static int
preconnect_wrapper(int readonly)276 preconnect_wrapper (int readonly)
277 {
278 CAMLparam0 ();
279 CAMLlocal1 (rv);
280
281 caml_leave_blocking_section ();
282
283 rv = caml_callback_exn (preconnect_fn, Val_bool (readonly));
284 if (Is_exception_result (rv)) {
285 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
286 caml_enter_blocking_section ();
287 CAMLreturnT (int, -1);
288 }
289
290 caml_enter_blocking_section ();
291 CAMLreturnT (int, 0);
292 }
293
294 static void *
open_wrapper(int readonly)295 open_wrapper (int readonly)
296 {
297 CAMLparam0 ();
298 CAMLlocal1 (rv);
299 value *ret;
300
301 caml_leave_blocking_section ();
302
303 rv = caml_callback_exn (open_fn, Val_bool (readonly));
304 if (Is_exception_result (rv)) {
305 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
306 caml_enter_blocking_section ();
307 CAMLreturnT (void *, NULL);
308 }
309
310 /* Allocate a root on the C heap that points to the OCaml handle. */
311 ret = malloc (sizeof *ret);
312 if (ret == NULL) abort ();
313 *ret = rv;
314 caml_register_generational_global_root (ret);
315
316 caml_enter_blocking_section ();
317 CAMLreturnT (void *, ret);
318 }
319
320 static void
close_wrapper(void * h)321 close_wrapper (void *h)
322 {
323 CAMLparam0 ();
324 CAMLlocal1 (rv);
325
326 caml_leave_blocking_section ();
327
328 rv = caml_callback_exn (close_fn, *(value *) h);
329 if (Is_exception_result (rv)) {
330 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
331 /*FALLTHROUGH*/
332 }
333
334 caml_remove_generational_global_root (h);
335 free (h);
336
337 caml_enter_blocking_section ();
338 CAMLreturn0;
339 }
340
341 static int64_t
get_size_wrapper(void * h)342 get_size_wrapper (void *h)
343 {
344 CAMLparam0 ();
345 CAMLlocal1 (rv);
346 int64_t r;
347
348 caml_leave_blocking_section ();
349
350 rv = caml_callback_exn (get_size_fn, *(value *) h);
351 if (Is_exception_result (rv)) {
352 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
353 caml_enter_blocking_section ();
354 CAMLreturnT (int64_t, -1);
355 }
356
357 r = Int64_val (rv);
358
359 caml_enter_blocking_section ();
360 CAMLreturnT (int64_t, r);
361 }
362
363 static int
can_write_wrapper(void * h)364 can_write_wrapper (void *h)
365 {
366 CAMLparam0 ();
367 CAMLlocal1 (rv);
368
369 caml_leave_blocking_section ();
370
371 rv = caml_callback_exn (can_write_fn, *(value *) h);
372 if (Is_exception_result (rv)) {
373 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
374 caml_enter_blocking_section ();
375 CAMLreturnT (int, -1);
376 }
377
378 caml_enter_blocking_section ();
379 CAMLreturnT (int, Bool_val (rv));
380 }
381
382 static int
can_flush_wrapper(void * h)383 can_flush_wrapper (void *h)
384 {
385 CAMLparam0 ();
386 CAMLlocal1 (rv);
387
388 caml_leave_blocking_section ();
389
390 rv = caml_callback_exn (can_flush_fn, *(value *) h);
391 if (Is_exception_result (rv)) {
392 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
393 caml_enter_blocking_section ();
394 CAMLreturnT (int, -1);
395 }
396
397 caml_enter_blocking_section ();
398 CAMLreturnT (int, Bool_val (rv));
399 }
400
401 static int
is_rotational_wrapper(void * h)402 is_rotational_wrapper (void *h)
403 {
404 CAMLparam0 ();
405 CAMLlocal1 (rv);
406
407 caml_leave_blocking_section ();
408
409 rv = caml_callback_exn (is_rotational_fn, *(value *) h);
410 if (Is_exception_result (rv)) {
411 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
412 caml_enter_blocking_section ();
413 CAMLreturnT (int, -1);
414 }
415
416 caml_enter_blocking_section ();
417 CAMLreturnT (int, Bool_val (rv));
418 }
419
420 static int
can_trim_wrapper(void * h)421 can_trim_wrapper (void *h)
422 {
423 CAMLparam0 ();
424 CAMLlocal1 (rv);
425
426 caml_leave_blocking_section ();
427
428 rv = caml_callback_exn (can_trim_fn, *(value *) h);
429 if (Is_exception_result (rv)) {
430 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
431 caml_enter_blocking_section ();
432 CAMLreturnT (int, -1);
433 }
434
435 caml_enter_blocking_section ();
436 CAMLreturnT (int, Bool_val (rv));
437 }
438
439 static int
can_zero_wrapper(void * h)440 can_zero_wrapper (void *h)
441 {
442 CAMLparam0 ();
443 CAMLlocal1 (rv);
444
445 caml_leave_blocking_section ();
446
447 rv = caml_callback_exn (can_zero_fn, *(value *) h);
448 if (Is_exception_result (rv)) {
449 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
450 caml_enter_blocking_section ();
451 CAMLreturnT (int, -1);
452 }
453
454 caml_enter_blocking_section ();
455 CAMLreturnT (int, Bool_val (rv));
456 }
457
458 static int
can_fua_wrapper(void * h)459 can_fua_wrapper (void *h)
460 {
461 CAMLparam0 ();
462 CAMLlocal1 (rv);
463
464 caml_leave_blocking_section ();
465
466 rv = caml_callback_exn (can_fua_fn, *(value *) h);
467 if (Is_exception_result (rv)) {
468 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
469 caml_enter_blocking_section ();
470 CAMLreturnT (int, -1);
471 }
472
473 caml_enter_blocking_section ();
474 CAMLreturnT (int, Int_val (rv));
475 }
476
477 static int
can_fast_zero_wrapper(void * h)478 can_fast_zero_wrapper (void *h)
479 {
480 CAMLparam0 ();
481 CAMLlocal1 (rv);
482
483 caml_leave_blocking_section ();
484
485 rv = caml_callback_exn (can_fast_zero_fn, *(value *) h);
486 if (Is_exception_result (rv)) {
487 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
488 caml_enter_blocking_section ();
489 CAMLreturnT (int, -1);
490 }
491
492 caml_enter_blocking_section ();
493 CAMLreturnT (int, Bool_val (rv));
494 }
495
496 static int
can_cache_wrapper(void * h)497 can_cache_wrapper (void *h)
498 {
499 CAMLparam0 ();
500 CAMLlocal1 (rv);
501
502 caml_leave_blocking_section ();
503
504 rv = caml_callback_exn (can_cache_fn, *(value *) h);
505 if (Is_exception_result (rv)) {
506 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
507 caml_enter_blocking_section ();
508 CAMLreturnT (int, -1);
509 }
510
511 caml_enter_blocking_section ();
512 CAMLreturnT (int, Int_val (rv));
513 }
514
515 static int
can_extents_wrapper(void * h)516 can_extents_wrapper (void *h)
517 {
518 CAMLparam0 ();
519 CAMLlocal1 (rv);
520
521 caml_leave_blocking_section ();
522
523 rv = caml_callback_exn (can_extents_fn, *(value *) h);
524 if (Is_exception_result (rv)) {
525 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
526 caml_enter_blocking_section ();
527 CAMLreturnT (int, -1);
528 }
529
530 caml_enter_blocking_section ();
531 CAMLreturnT (int, Bool_val (rv));
532 }
533
534 static int
can_multi_conn_wrapper(void * h)535 can_multi_conn_wrapper (void *h)
536 {
537 CAMLparam0 ();
538 CAMLlocal1 (rv);
539
540 caml_leave_blocking_section ();
541
542 rv = caml_callback_exn (can_multi_conn_fn, *(value *) h);
543 if (Is_exception_result (rv)) {
544 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
545 caml_enter_blocking_section ();
546 CAMLreturnT (int, -1);
547 }
548
549 caml_enter_blocking_section ();
550 CAMLreturnT (int, Bool_val (rv));
551 }
552
553 static value
Val_flags(uint32_t flags)554 Val_flags (uint32_t flags)
555 {
556 CAMLparam0 ();
557 CAMLlocal2 (consv, rv);
558
559 rv = Val_unit;
560 if (flags & NBDKIT_FLAG_MAY_TRIM) {
561 consv = caml_alloc (2, 0);
562 Store_field (consv, 0, 0); /* 0 = May_trim */
563 Store_field (consv, 1, rv);
564 rv = consv;
565 }
566 if (flags & NBDKIT_FLAG_FUA) {
567 consv = caml_alloc (2, 0);
568 Store_field (consv, 0, 1); /* 1 = FUA */
569 Store_field (consv, 1, rv);
570 rv = consv;
571 }
572 if (flags & NBDKIT_FLAG_REQ_ONE) {
573 consv = caml_alloc (2, 0);
574 Store_field (consv, 0, 2); /* 2 = Req_one */
575 Store_field (consv, 1, rv);
576 rv = consv;
577 }
578
579 CAMLreturn (rv);
580 }
581
582 static int
pread_wrapper(void * h,void * buf,uint32_t count,uint64_t offset,uint32_t flags)583 pread_wrapper (void *h, void *buf, uint32_t count, uint64_t offset,
584 uint32_t flags)
585 {
586 CAMLparam0 ();
587 CAMLlocal4 (rv, countv, offsetv, flagsv);
588 mlsize_t len;
589
590 caml_leave_blocking_section ();
591
592 countv = caml_copy_int32 (count);
593 offsetv = caml_copy_int64 (offset);
594 flagsv = Val_flags (flags);
595
596 value args[] = { *(value *) h, countv, offsetv, flagsv };
597 rv = caml_callbackN_exn (pread_fn, sizeof args / sizeof args[0], args);
598 if (Is_exception_result (rv)) {
599 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
600 caml_enter_blocking_section ();
601 CAMLreturnT (int, -1);
602 }
603
604 len = caml_string_length (rv);
605 if (len < count) {
606 nbdkit_error ("buffer returned from pread is too small");
607 caml_enter_blocking_section ();
608 CAMLreturnT (int, -1);
609 }
610
611 memcpy (buf, String_val (rv), count);
612
613 caml_enter_blocking_section ();
614 CAMLreturnT (int, 0);
615 }
616
617 static int
pwrite_wrapper(void * h,const void * buf,uint32_t count,uint64_t offset,uint32_t flags)618 pwrite_wrapper (void *h, const void *buf, uint32_t count, uint64_t offset,
619 uint32_t flags)
620 {
621 CAMLparam0 ();
622 CAMLlocal4 (rv, strv, offsetv, flagsv);
623
624 caml_leave_blocking_section ();
625
626 strv = caml_alloc_initialized_string (count, buf);
627 offsetv = caml_copy_int64 (offset);
628 flagsv = Val_flags (flags);
629
630 value args[] = { *(value *) h, strv, offsetv, flagsv };
631 rv = caml_callbackN_exn (pwrite_fn, sizeof args / sizeof args[0], args);
632 if (Is_exception_result (rv)) {
633 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
634 caml_enter_blocking_section ();
635 CAMLreturnT (int, -1);
636 }
637
638 caml_enter_blocking_section ();
639 CAMLreturnT (int, 0);
640 }
641
642 static int
flush_wrapper(void * h,uint32_t flags)643 flush_wrapper (void *h, uint32_t flags)
644 {
645 CAMLparam0 ();
646 CAMLlocal2 (rv, flagsv);
647
648 caml_leave_blocking_section ();
649
650 flagsv = Val_flags (flags);
651
652 rv = caml_callback2_exn (flush_fn, *(value *) h, flagsv);
653 if (Is_exception_result (rv)) {
654 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
655 CAMLreturnT (int, -1);
656 }
657
658 caml_enter_blocking_section ();
659 CAMLreturnT (int, 0);
660 }
661
662 static int
trim_wrapper(void * h,uint32_t count,uint64_t offset,uint32_t flags)663 trim_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags)
664 {
665 CAMLparam0 ();
666 CAMLlocal4 (rv, countv, offsetv, flagsv);
667
668 caml_leave_blocking_section ();
669
670 countv = caml_copy_int32 (count);
671 offsetv = caml_copy_int32 (offset);
672 flagsv = Val_flags (flags);
673
674 value args[] = { *(value *) h, countv, offsetv, flagsv };
675 rv = caml_callbackN_exn (trim_fn, sizeof args / sizeof args[0], args);
676 if (Is_exception_result (rv)) {
677 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
678 CAMLreturnT (int, -1);
679 }
680
681 caml_enter_blocking_section ();
682 CAMLreturnT (int, 0);
683 }
684
685 static int
zero_wrapper(void * h,uint32_t count,uint64_t offset,uint32_t flags)686 zero_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags)
687 {
688 CAMLparam0 ();
689 CAMLlocal4 (rv, countv, offsetv, flagsv);
690
691 caml_leave_blocking_section ();
692
693 countv = caml_copy_int32 (count);
694 offsetv = caml_copy_int32 (offset);
695 flagsv = Val_flags (flags);
696
697 value args[] = { *(value *) h, countv, offsetv, flagsv };
698 rv = caml_callbackN_exn (zero_fn, sizeof args / sizeof args[0], args);
699 if (Is_exception_result (rv)) {
700 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
701 CAMLreturnT (int, -1);
702 }
703
704 caml_enter_blocking_section ();
705 CAMLreturnT (int, 0);
706 }
707
708 static int
extents_wrapper(void * h,uint32_t count,uint64_t offset,uint32_t flags,struct nbdkit_extents * extents)709 extents_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags,
710 struct nbdkit_extents *extents)
711 {
712 CAMLparam0 ();
713 CAMLlocal5 (rv, countv, offsetv, flagsv, v);
714
715 caml_leave_blocking_section ();
716
717 countv = caml_copy_int32 (count);
718 offsetv = caml_copy_int32 (offset);
719 flagsv = Val_flags (flags);
720
721 value args[] = { *(value *) h, countv, offsetv, flagsv };
722 rv = caml_callbackN_exn (extents_fn, sizeof args / sizeof args[0], args);
723 if (Is_exception_result (rv)) {
724 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
725 caml_enter_blocking_section ();
726 CAMLreturnT (int, -1);
727 }
728
729 /* Convert extents list into calls to nbdkit_add_extent. */
730 while (rv != Val_int (0)) {
731 uint64_t length;
732 uint32_t type = 0;
733
734 v = Field (rv, 0); /* extent struct */
735 offset = Int64_val (Field (v, 0));
736 length = Int64_val (Field (v, 1));
737 if (Bool_val (Field (v, 2)))
738 type |= NBDKIT_EXTENT_HOLE;
739 if (Bool_val (Field (v, 3)))
740 type |= NBDKIT_EXTENT_ZERO;
741 if (nbdkit_add_extent (extents, offset, length, type) == -1) {
742 caml_enter_blocking_section ();
743 CAMLreturnT (int, -1);
744 }
745
746 rv = Field (rv, 1);
747 }
748
749 caml_enter_blocking_section ();
750 CAMLreturnT (int, 0);
751 }
752
753 static int
cache_wrapper(void * h,uint32_t count,uint64_t offset,uint32_t flags)754 cache_wrapper (void *h, uint32_t count, uint64_t offset, uint32_t flags)
755 {
756 CAMLparam0 ();
757 CAMLlocal4 (rv, countv, offsetv, flagsv);
758
759 caml_leave_blocking_section ();
760
761 countv = caml_copy_int32 (count);
762 offsetv = caml_copy_int32 (offset);
763 flagsv = Val_flags (flags);
764
765 value args[] = { *(value *) h, countv, offsetv, flagsv };
766 rv = caml_callbackN_exn (cache_fn, sizeof args / sizeof args[0], args);
767 if (Is_exception_result (rv)) {
768 nbdkit_error ("%s", caml_format_exception (Extract_exception (rv)));
769 CAMLreturnT (int, -1);
770 }
771
772 caml_enter_blocking_section ();
773 CAMLreturnT (int, 0);
774 }
775
776 /*----------------------------------------------------------------------*/
777 /* set_* functions called from OCaml code at load time to initialize
778 * fields in the plugin struct.
779 */
780
781 value
ocaml_nbdkit_set_name(value namev)782 ocaml_nbdkit_set_name (value namev)
783 {
784 plugin.name = strdup (String_val (namev));
785 return Val_unit;
786 }
787
788 value
ocaml_nbdkit_set_longname(value longnamev)789 ocaml_nbdkit_set_longname (value longnamev)
790 {
791 plugin.longname = strdup (String_val (longnamev));
792 return Val_unit;
793 }
794
795 value
ocaml_nbdkit_set_version(value versionv)796 ocaml_nbdkit_set_version (value versionv)
797 {
798 plugin.version = strdup (String_val (versionv));
799 return Val_unit;
800 }
801
802 value
ocaml_nbdkit_set_description(value descriptionv)803 ocaml_nbdkit_set_description (value descriptionv)
804 {
805 plugin.description = strdup (String_val (descriptionv));
806 return Val_unit;
807 }
808
809 value
ocaml_nbdkit_set_config_help(value helpv)810 ocaml_nbdkit_set_config_help (value helpv)
811 {
812 plugin.config_help = strdup (String_val (helpv));
813 return Val_unit;
814 }
815
816 #define SET(fn) \
817 value \
818 ocaml_nbdkit_set_##fn (value fv) \
819 { \
820 plugin.fn = fn##_wrapper; \
821 fn##_fn = fv; \
822 caml_register_generational_global_root (&fn##_fn); \
823 return Val_unit; \
824 }
825
826 SET(load)
SET(unload)827 SET(unload)
828
829 SET(dump_plugin)
830
831 SET(config)
832 SET(config_complete)
833 SET(thread_model)
834
835 SET(get_ready)
836
837 SET(preconnect)
838 SET(open)
839 SET(close)
840
841 SET(get_size)
842
843 SET(can_write)
844 SET(can_flush)
845 SET(is_rotational)
846 SET(can_trim)
847 SET(can_zero)
848 SET(can_fua)
849 SET(can_multi_conn)
850 SET(can_extents)
851 SET(can_cache)
852 SET(can_fast_zero)
853
854 SET(pread)
855 SET(pwrite)
856 SET(flush)
857 SET(trim)
858 SET(zero)
859 SET(extents)
860 SET(cache)
861
862 #undef SET
863
864 static void
865 remove_roots (void)
866 {
867 #define REMOVE(fn) \
868 if (fn##_fn) caml_remove_generational_global_root (&fn##_fn)
869 REMOVE (load);
870 REMOVE (unload);
871
872 REMOVE (dump_plugin);
873
874 REMOVE (config);
875 REMOVE (config_complete);
876 REMOVE (thread_model);
877
878 REMOVE (get_ready);
879
880 REMOVE (preconnect);
881 REMOVE (open);
882 REMOVE (close);
883
884 REMOVE (get_size);
885
886 REMOVE (can_cache);
887 REMOVE (can_extents);
888 REMOVE (can_fast_zero);
889 REMOVE (can_flush);
890 REMOVE (can_fua);
891 REMOVE (can_multi_conn);
892 REMOVE (can_trim);
893 REMOVE (can_write);
894 REMOVE (can_zero);
895 REMOVE (is_rotational);
896
897 REMOVE (pread);
898 REMOVE (pwrite);
899 REMOVE (flush);
900 REMOVE (trim);
901 REMOVE (zero);
902
903 REMOVE (extents);
904
905 REMOVE (cache);
906
907 #undef REMOVE
908 }
909
910 /*----------------------------------------------------------------------*/
911 /* Bindings for miscellaneous nbdkit_* utility functions. */
912
913 /* NB: noalloc function. */
914 value
ocaml_nbdkit_set_error(value nv)915 ocaml_nbdkit_set_error (value nv)
916 {
917 int err;
918
919 switch (Int_val (nv)) {
920 /* Host errno values that will map to NBD protocol values */
921 case 1: err = EPERM; break;
922 case 2: err = EIO; break;
923 case 3: err = ENOMEM; break;
924 case 4: err = EINVAL; break;
925 case 5: err = ENOSPC; break;
926 case 6: err = ESHUTDOWN; break;
927 case 7: err = EOVERFLOW; break;
928 case 8: err = EOPNOTSUPP; break;
929 /* Other errno values that server/protocol.c treats specially */
930 case 9: err = EROFS; break;
931 case 10: err = EFBIG; break;
932 default: abort ();
933 }
934
935 nbdkit_set_error (err);
936
937 return Val_unit;
938 }
939
940 value
ocaml_nbdkit_parse_size(value strv)941 ocaml_nbdkit_parse_size (value strv)
942 {
943 CAMLparam1 (strv);
944 CAMLlocal1 (rv);
945 int64_t r;
946
947 r = nbdkit_parse_size (String_val (strv));
948 if (r == -1)
949 caml_invalid_argument ("nbdkit_parse_size");
950 rv = caml_copy_int64 (r);
951
952 CAMLreturn (rv);
953 }
954
955 value
ocaml_nbdkit_parse_bool(value strv)956 ocaml_nbdkit_parse_bool (value strv)
957 {
958 CAMLparam1 (strv);
959 CAMLlocal1 (rv);
960 int r;
961
962 r = nbdkit_parse_bool (String_val (strv));
963 if (r == -1)
964 caml_invalid_argument ("nbdkit_parse_bool");
965 rv = Val_bool (r);
966
967 CAMLreturn (rv);
968 }
969
970 value
ocaml_nbdkit_read_password(value strv)971 ocaml_nbdkit_read_password (value strv)
972 {
973 CAMLparam1 (strv);
974 CAMLlocal1 (rv);
975 char *password;
976 int r;
977
978 r = nbdkit_read_password (String_val (strv), &password);
979 if (r == -1)
980 caml_invalid_argument ("nbdkit_read_password");
981 rv = caml_copy_string (password);
982 free (password);
983
984 CAMLreturn (rv);
985 }
986
987 value
ocaml_nbdkit_realpath(value strv)988 ocaml_nbdkit_realpath (value strv)
989 {
990 CAMLparam1 (strv);
991 CAMLlocal1 (rv);
992 char *ret;
993
994 ret = nbdkit_realpath (String_val (strv));
995 if (ret == NULL)
996 caml_failwith ("nbdkit_realpath");
997 rv = caml_copy_string (ret);
998 free (ret);
999
1000 CAMLreturn (rv);
1001 }
1002
1003 value
ocaml_nbdkit_nanosleep(value secv,value nsecv)1004 ocaml_nbdkit_nanosleep (value secv, value nsecv)
1005 {
1006 CAMLparam2 (secv, nsecv);
1007 int r;
1008
1009 caml_enter_blocking_section ();
1010 r = nbdkit_nanosleep (Int_val (secv), Int_val (nsecv));
1011 caml_leave_blocking_section ();
1012 if (r == -1)
1013 caml_failwith ("nbdkit_nanosleep");
1014
1015 CAMLreturn (Val_unit);
1016 }
1017
1018 value
ocaml_nbdkit_export_name(value unitv)1019 ocaml_nbdkit_export_name (value unitv)
1020 {
1021 CAMLparam1 (unitv);
1022 CAMLlocal1 (rv);
1023 const char *ret;
1024
1025 ret = nbdkit_export_name ();
1026 /* Note that NULL indicates error. Default export name is [""] even
1027 * for oldstyle.
1028 */
1029 if (ret == NULL)
1030 caml_failwith ("nbdkit_export_name");
1031 rv = caml_copy_string (ret);
1032
1033 CAMLreturn (rv);
1034 }
1035
1036 /* NB: noalloc function. */
1037 value
ocaml_nbdkit_shutdown(value unitv)1038 ocaml_nbdkit_shutdown (value unitv)
1039 {
1040 CAMLparam1 (unitv);
1041
1042 nbdkit_shutdown ();
1043 CAMLreturn (Val_unit);
1044 }
1045
1046 /* NB: noalloc function. */
1047 value
ocaml_nbdkit_debug(value strv)1048 ocaml_nbdkit_debug (value strv)
1049 {
1050 nbdkit_debug ("%s", String_val (strv));
1051
1052 return Val_unit;
1053 }
1054