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