1 /* nbdkit
2 * Copyright (C) 2018-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 <stdbool.h>
38 #include <inttypes.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42
43 #define NBDKIT_API_VERSION 2
44
45 #include <nbdkit-plugin.h>
46
47 #include "cleanup.h"
48 #include "ascii-string.h"
49
50 #include "call.h"
51 #include "methods.h"
52
53 void
sh_dump_plugin(void)54 sh_dump_plugin (void)
55 {
56 const char *method = "dump_plugin";
57 const char *script = get_script (method);
58 const char *args[] = { script, method, NULL };
59 CLEANUP_FREE char *o = NULL;
60 size_t olen;
61
62 if (script) {
63 /* Call dump_plugin method. */
64 switch (call_read (&o, &olen, args)) {
65 case OK:
66 printf ("%s", o);
67 break;
68
69 case MISSING:
70 /* Ignore if the method was missing. */
71 break;
72
73 case ERROR:
74 break;
75
76 case RET_FALSE:
77 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
78 script, method);
79 errno = EIO;
80 return;
81
82 default: abort ();
83 }
84 }
85 }
86
87 int
sh_thread_model(void)88 sh_thread_model (void)
89 {
90 const char *method = "thread_model";
91 const char *script = get_script (method);
92 const char *args[] = { script, method, NULL };
93 CLEANUP_FREE char *s = NULL;
94 size_t slen;
95 int r;
96
97 /* For historical compatibility: the lack of a script is assumed to
98 * be parallel, but an existing script with missing or unparseable
99 * thread_model remains at the older (but safe)
100 * serialize_all_requests.
101 */
102 if (!script)
103 return NBDKIT_THREAD_MODEL_PARALLEL;
104
105 switch (call_read (&s, &slen, args)) {
106 case OK:
107 if (slen > 0 && s[slen-1] == '\n')
108 s[slen-1] = '\0';
109 if (ascii_strcasecmp (s, "parallel") == 0)
110 r = NBDKIT_THREAD_MODEL_PARALLEL;
111 else if (ascii_strcasecmp (s, "serialize_requests") == 0 ||
112 ascii_strcasecmp (s, "serialize-requests") == 0)
113 r = NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS;
114 else if (ascii_strcasecmp (s, "serialize_all_requests") == 0 ||
115 ascii_strcasecmp (s, "serialize-all-requests") == 0)
116 r = NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS;
117 else if (ascii_strcasecmp (s, "serialize_connections") == 0 ||
118 ascii_strcasecmp (s, "serialize-connections") == 0)
119 r = NBDKIT_THREAD_MODEL_SERIALIZE_CONNECTIONS;
120 else {
121 nbdkit_debug ("%s: ignoring unrecognized thread model: %s",
122 script, s);
123 r = NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS;
124 }
125 return r;
126
127 case MISSING:
128 return NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS;
129
130 case ERROR:
131 return -1;
132
133 case RET_FALSE:
134 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
135 script, method);
136 errno = EIO;
137 return -1;
138
139 default: abort ();
140 }
141 }
142
143 int
sh_get_ready(void)144 sh_get_ready (void)
145 {
146 const char *method = "get_ready";
147 const char *script = get_script (method);
148 const char *args[] = { script, method, NULL };
149
150 switch (call (args)) {
151 case OK:
152 case MISSING:
153 return 0;
154
155 case ERROR:
156 return -1;
157
158 case RET_FALSE:
159 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
160 script, method);
161 errno = EIO;
162 return -1;
163
164 default: abort ();
165 }
166 }
167
168 int
sh_preconnect(int readonly)169 sh_preconnect (int readonly)
170 {
171 const char *method = "preconnect";
172 const char *script = get_script (method);
173 const char *args[] =
174 { script, method,
175 readonly ? "true" : "false",
176 nbdkit_export_name () ? : "",
177 NULL };
178
179 switch (call (args)) {
180 case OK:
181 case MISSING:
182 return 0;
183
184 case ERROR:
185 return -1;
186
187 case RET_FALSE:
188 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
189 script, method);
190 errno = EIO;
191 return -1;
192
193 default: abort ();
194 }
195 }
196
197 struct sh_handle {
198 char *h;
199 int can_flush;
200 int can_zero;
201 };
202
203 void *
sh_open(int readonly)204 sh_open (int readonly)
205 {
206 const char *method = "open";
207 const char *script = get_script (method);
208 size_t hlen;
209 const char *args[] =
210 { script, method,
211 readonly ? "true" : "false",
212 nbdkit_export_name () ? : "",
213 NULL };
214 struct sh_handle *h = malloc (sizeof *h);
215
216 if (!h) {
217 nbdkit_error ("malloc: %m");
218 return NULL;
219 }
220 h->can_flush = -1;
221 h->can_zero = -1;
222
223 /* We store the string returned by open in the handle. */
224 switch (call_read (&h->h, &hlen, args)) {
225 case OK:
226 /* Remove final newline if present. */
227 if (hlen > 0 && h->h[hlen-1] == '\n') {
228 h->h[hlen-1] = '\0';
229 hlen--;
230 }
231 if (hlen > 0)
232 nbdkit_debug ("sh: handle: %s", h->h);
233 return h;
234
235 case MISSING:
236 /* Unlike regular C plugins, open is not required. If it is
237 * missing then we return "" as the handle. Allocate a new string
238 * for it because we don't know what call_read returned here.
239 */
240 free (h->h);
241 h->h = strdup ("");
242 if (h->h == NULL)
243 nbdkit_error ("strdup: %m");
244 return h;
245
246 case ERROR:
247 free (h->h);
248 free (h);
249 return NULL;
250
251 case RET_FALSE:
252 free (h->h);
253 free (h);
254 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
255 script, method);
256 errno = EIO;
257 return NULL;
258
259 default: abort ();
260 }
261 }
262
263 void
sh_close(void * handle)264 sh_close (void *handle)
265 {
266 const char *method = "close";
267 const char *script = get_script (method);
268 struct sh_handle *h = handle;
269 const char *args[] = { script, method, h->h, NULL };
270
271 switch (call (args)) {
272 case OK:
273 case MISSING:
274 case ERROR:
275 case RET_FALSE:
276 free (h->h);
277 free (h);
278 return;
279 default: abort ();
280 }
281 }
282
283 int64_t
sh_get_size(void * handle)284 sh_get_size (void *handle)
285 {
286 const char *method = "get_size";
287 const char *script = get_script (method);
288 struct sh_handle *h = handle;
289 const char *args[] = { script, method, h->h, NULL };
290 CLEANUP_FREE char *s = NULL;
291 size_t slen;
292 int64_t r;
293
294 switch (call_read (&s, &slen, args)) {
295 case OK:
296 if (slen > 0 && s[slen-1] == '\n')
297 s[slen-1] = '\0';
298 r = nbdkit_parse_size (s);
299 if (r == -1)
300 nbdkit_error ("%s: could not parse output from get_size method: %s",
301 script, s);
302 return r;
303
304 case MISSING:
305 nbdkit_error ("%s: the get_size method is required", script);
306 return -1;
307
308 case ERROR:
309 return -1;
310
311 case RET_FALSE:
312 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
313 script, method);
314 errno = EIO;
315 return -1;
316
317 default: abort ();
318 }
319 }
320
321 int
sh_pread(void * handle,void * buf,uint32_t count,uint64_t offset,uint32_t flags)322 sh_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
323 uint32_t flags)
324 {
325 const char *method = "pread";
326 const char *script = get_script (method);
327 struct sh_handle *h = handle;
328 char cbuf[32], obuf[32];
329 const char *args[] = { script, method, h->h, cbuf, obuf, NULL };
330 CLEANUP_FREE char *data = NULL;
331 size_t len;
332
333 snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
334 snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
335
336 switch (call_read (&data, &len, args)) {
337 case OK:
338 if (count != len) {
339 nbdkit_error ("%s: incorrect amount of data read: "
340 "expecting %" PRIu32 " bytes but "
341 "received %zu bytes from the script",
342 script, count, len);
343 return -1;
344 }
345 memcpy (buf, data, count);
346 return 0;
347
348 case MISSING:
349 nbdkit_error ("%s: the pread method is required", script);
350 return -1;
351
352 case ERROR:
353 return -1;
354
355 case RET_FALSE:
356 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
357 script, method);
358 errno = EIO;
359 return -1;
360
361 default: abort ();
362 }
363 }
364
365 /* Convert NBDKIT_FLAG_* to flags string. */
366 static void flag_append (const char *str, bool *comma, char **buf, size_t *len);
367
368 static void
flags_string(uint32_t flags,char * buf,size_t len)369 flags_string (uint32_t flags, char *buf, size_t len)
370 {
371 bool comma = false;
372
373 buf[0] = '\0';
374
375 if (flags & NBDKIT_FLAG_FUA)
376 flag_append ("fua", &comma, &buf, &len);
377
378 if (flags & NBDKIT_FLAG_MAY_TRIM)
379 flag_append ("may_trim", &comma, &buf, &len);
380
381 if (flags & NBDKIT_FLAG_REQ_ONE)
382 flag_append ("req_one", &comma, &buf, &len);
383
384 if (flags & NBDKIT_FLAG_FAST_ZERO)
385 flag_append ("fast", &comma, &buf, &len);
386 }
387
388 static void
flag_append(const char * str,bool * comma,char ** buf,size_t * len)389 flag_append (const char *str, bool *comma, char **buf, size_t *len)
390 {
391 size_t slen = strlen (str);
392
393 if (*comma) {
394 /* Too short flags buffer is an internal error so abort. */
395 if (*len <= 1) abort ();
396 strcpy (*buf, ",");
397 (*buf)++;
398 (*len)--;
399 }
400
401 if (*len <= slen) abort ();
402 strcpy (*buf, str);
403 (*buf) += slen;
404 (*len) -= slen;
405
406 *comma = true;
407 }
408
409 int
sh_pwrite(void * handle,const void * buf,uint32_t count,uint64_t offset,uint32_t flags)410 sh_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
411 uint32_t flags)
412 {
413 const char *method = "pwrite";
414 const char *script = get_script (method);
415 struct sh_handle *h = handle;
416 char cbuf[32], obuf[32], fbuf[32];
417 const char *args[] = { script, method, h->h, cbuf, obuf, fbuf, NULL };
418
419 snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
420 snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
421 flags_string (flags, fbuf, sizeof fbuf);
422
423 switch (call_write (buf, count, args)) {
424 case OK:
425 return 0;
426
427 case MISSING:
428 nbdkit_error ("pwrite not implemented");
429 return -1;
430
431 case ERROR:
432 return -1;
433
434 case RET_FALSE:
435 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
436 script, method);
437 errno = EIO;
438 return -1;
439
440 default: abort ();
441 }
442 }
443
444 /* Common code for handling all boolean methods like can_write etc. */
445 static int
boolean_method(const char * script,const char * method,void * handle,int def)446 boolean_method (const char *script, const char *method,
447 void *handle, int def)
448 {
449 struct sh_handle *h = handle;
450 const char *args[] = { script, method, h->h, NULL };
451
452 switch (call (args)) {
453 case OK: /* true */
454 return 1;
455 case RET_FALSE: /* false */
456 return 0;
457 case MISSING: /* missing => caller chooses default */
458 return def;
459 case ERROR: /* error cases */
460 return -1;
461 default: abort ();
462 }
463 }
464
465 int
sh_can_write(void * handle)466 sh_can_write (void *handle)
467 {
468 const char *method = "can_write";
469 const char *script = get_script (method);
470 return boolean_method (script, method, handle, 0);
471 }
472
473 int
sh_can_flush(void * handle)474 sh_can_flush (void *handle)
475 {
476 const char *method = "can_flush";
477 const char *script;
478 struct sh_handle *h = handle;
479
480 if (h->can_flush >= 0)
481 return h->can_flush;
482
483 script = get_script (method);
484 return h->can_flush = boolean_method (script, method, handle, 0);
485 }
486
487 int
sh_is_rotational(void * handle)488 sh_is_rotational (void *handle)
489 {
490 const char *method = "is_rotational";
491 const char *script = get_script (method);
492 return boolean_method (script, method, handle, 0);
493 }
494
495 int
sh_can_trim(void * handle)496 sh_can_trim (void *handle)
497 {
498 const char *method = "can_trim";
499 const char *script = get_script (method);
500 return boolean_method (script, method, handle, 0);
501 }
502
503 int
sh_can_zero(void * handle)504 sh_can_zero (void *handle)
505 {
506 const char *method = "can_zero";
507 const char *script;
508 struct sh_handle *h = handle;
509
510 if (h->can_zero >= 0)
511 return h->can_zero;
512
513 script = get_script (method);
514 return h->can_zero = boolean_method (script, method, handle, 0);
515 }
516
517 int
sh_can_extents(void * handle)518 sh_can_extents (void *handle)
519 {
520 const char *method = "can_extents";
521 const char *script = get_script (method);
522 return boolean_method (script, method, handle, 0);
523 }
524
525 int
sh_can_multi_conn(void * handle)526 sh_can_multi_conn (void *handle)
527 {
528 const char *method = "can_multi_conn";
529 const char *script = get_script (method);
530 return boolean_method (script, method, handle, 0);
531 }
532
533 /* Not a boolean method, the method prints "none", "emulate" or "native". */
534 int
sh_can_fua(void * handle)535 sh_can_fua (void *handle)
536 {
537 const char *method = "can_fua";
538 const char *script = get_script (method);
539 struct sh_handle *h = handle;
540 const char *args[] = { script, method, h->h, NULL };
541 CLEANUP_FREE char *s = NULL;
542 size_t slen;
543 int r;
544
545 switch (call_read (&s, &slen, args)) {
546 case OK:
547 if (slen > 0 && s[slen-1] == '\n')
548 s[slen-1] = '\0';
549 if (ascii_strcasecmp (s, "none") == 0)
550 r = NBDKIT_FUA_NONE;
551 else if (ascii_strcasecmp (s, "emulate") == 0)
552 r = NBDKIT_FUA_EMULATE;
553 else if (ascii_strcasecmp (s, "native") == 0)
554 r = NBDKIT_FUA_NATIVE;
555 else {
556 nbdkit_error ("%s: could not parse output from %s method: %s",
557 script, method, s);
558 r = -1;
559 }
560 return r;
561
562 case MISSING:
563 /* Check if the plugin claims to support flush. */
564 switch (sh_can_flush (handle)) {
565 case -1:
566 return -1;
567 case 0:
568 return NBDKIT_FUA_NONE;
569 case 1:
570 return NBDKIT_FUA_EMULATE;
571 default:
572 abort ();
573 }
574
575 case ERROR:
576 return -1;
577
578 case RET_FALSE:
579 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
580 script, method);
581 errno = EIO;
582 return -1;
583
584 default: abort ();
585 }
586 }
587
588 /* Not a boolean method, the method prints "none", "emulate" or "native". */
589 int
sh_can_cache(void * handle)590 sh_can_cache (void *handle)
591 {
592 const char *method = "can_cache";
593 const char *script = get_script (method);
594 struct sh_handle *h = handle;
595 const char *args[] = { script, method, h->h, NULL };
596 CLEANUP_FREE char *s = NULL;
597 size_t slen;
598 int r;
599
600 switch (call_read (&s, &slen, args)) {
601 case OK:
602 if (slen > 0 && s[slen-1] == '\n')
603 s[slen-1] = '\0';
604 if (ascii_strcasecmp (s, "none") == 0)
605 r = NBDKIT_CACHE_NONE;
606 else if (ascii_strcasecmp (s, "emulate") == 0)
607 r = NBDKIT_CACHE_EMULATE;
608 else if (ascii_strcasecmp (s, "native") == 0)
609 r = NBDKIT_CACHE_NATIVE;
610 else {
611 nbdkit_error ("%s: could not parse output from %s method: %s",
612 script, method, s);
613 r = -1;
614 }
615 return r;
616
617 case MISSING:
618 /* NBDKIT_CACHE_EMULATE means that nbdkit will call .pread. However
619 * we cannot know if that fallback would be efficient, so the safest
620 * default is to return NBDKIT_CACHE_NONE.
621 */
622 return NBDKIT_CACHE_NONE;
623
624 case ERROR:
625 return -1;
626
627 case RET_FALSE:
628 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
629 script, method);
630 errno = EIO;
631 return -1;
632
633 default: abort ();
634 }
635 }
636
637 int
sh_can_fast_zero(void * handle)638 sh_can_fast_zero (void *handle)
639 {
640 const char *method = "can_fast_zero";
641 const char *script = get_script (method);
642 int r = boolean_method (script, method, handle, 2);
643 if (r < 2)
644 return r;
645 /* We need to duplicate the logic of plugins.c: if can_fast_zero is
646 * missing, we advertise fast fail support when can_zero is false.
647 */
648 r = sh_can_zero (handle);
649 if (r == -1)
650 return -1;
651 return !r;
652 }
653
654 int
sh_flush(void * handle,uint32_t flags)655 sh_flush (void *handle, uint32_t flags)
656 {
657 const char *method = "flush";
658 const char *script = get_script (method);
659 struct sh_handle *h = handle;
660 const char *args[] = { script, method, h->h, NULL };
661
662 switch (call (args)) {
663 case OK:
664 return 0;
665
666 case MISSING:
667 /* Ignore lack of flush callback. */
668 return 0;
669
670 case ERROR: /* error cases */
671 return -1;
672
673 case RET_FALSE:
674 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
675 script, method);
676 errno = EIO;
677 return -1;
678
679 default: abort ();
680 }
681 }
682
683 int
sh_trim(void * handle,uint32_t count,uint64_t offset,uint32_t flags)684 sh_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
685 {
686 const char *method = "trim";
687 const char *script = get_script (method);
688 struct sh_handle *h = handle;
689 char cbuf[32], obuf[32], fbuf[32];
690 const char *args[] = { script, method, h->h, cbuf, obuf, fbuf, NULL };
691
692 snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
693 snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
694 flags_string (flags, fbuf, sizeof fbuf);
695
696 switch (call (args)) {
697 case OK:
698 return 0;
699
700 case MISSING:
701 /* Ignore lack of trim callback. */
702 return 0;
703
704 case ERROR:
705 return -1;
706
707 case RET_FALSE:
708 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
709 script, method);
710 errno = EIO;
711 return -1;
712
713 default: abort ();
714 }
715 }
716
717 int
sh_zero(void * handle,uint32_t count,uint64_t offset,uint32_t flags)718 sh_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
719 {
720 const char *method = "zero";
721 const char *script = get_script (method);
722 struct sh_handle *h = handle;
723 char cbuf[32], obuf[32], fbuf[32];
724 const char *args[] = { script, method, h->h, cbuf, obuf, fbuf, NULL };
725
726 snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
727 snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
728 flags_string (flags, fbuf, sizeof fbuf);
729
730 switch (call (args)) {
731 case OK:
732 return 0;
733
734 case MISSING:
735 nbdkit_debug ("zero falling back to pwrite");
736 errno = EOPNOTSUPP;
737 return -1;
738
739 case ERROR:
740 return -1;
741
742 case RET_FALSE:
743 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
744 script, method);
745 errno = EIO;
746 return -1;
747
748 default: abort ();
749 }
750 }
751
752 static int
parse_extents(const char * script,const char * s,size_t slen,struct nbdkit_extents * extents)753 parse_extents (const char *script,
754 const char *s, size_t slen, struct nbdkit_extents *extents)
755 {
756 FILE *fp = NULL;
757 CLEANUP_FREE char *line = NULL;
758 size_t linelen = 0;
759 ssize_t len;
760 int ret = -1;
761
762 fp = fmemopen ((void *) s, slen, "r");
763 if (!fp) {
764 nbdkit_error ("%s: extents: fmemopen: %m", script);
765 goto out;
766 }
767
768 while ((len = getline (&line, &linelen, fp)) != -1) {
769 const char *delim = " \t";
770 char *sp, *p;
771 int64_t offset, length;
772 uint32_t type;
773
774 if (len > 0 && line[len-1] == '\n') {
775 line[len-1] = '\0';
776 len--;
777 }
778
779 if ((p = strtok_r (line, delim, &sp)) == NULL) {
780 parse_error:
781 nbdkit_error ("%s: extents: cannot parse %s", script, line);
782 goto out;
783 }
784 offset = nbdkit_parse_size (p);
785 if (offset == -1)
786 goto out;
787
788 if ((p = strtok_r (NULL, delim, &sp)) == NULL)
789 goto parse_error;
790 length = nbdkit_parse_size (p);
791 if (length == -1)
792 goto out;
793
794 if ((p = strtok_r (NULL, delim, &sp)) == NULL)
795 /* empty type field means allocated data (0) */
796 type = 0;
797 else if (sscanf (p, "%" SCNu32, &type) == 1)
798 ;
799 else {
800 type = 0;
801 if (strstr (p, "hole") != NULL)
802 type |= NBDKIT_EXTENT_HOLE;
803 if (strstr (p, "zero") != NULL)
804 type |= NBDKIT_EXTENT_ZERO;
805 }
806
807 nbdkit_debug ("%s: adding extent %" PRIi64 " %" PRIi64 " %" PRIu32,
808 script, offset, length, type);
809 if (nbdkit_add_extent (extents, offset, length, type) == -1)
810 goto out;
811 }
812
813 ret = 0;
814
815 out:
816 if (fp)
817 fclose (fp);
818 return ret;
819 }
820
821 int
sh_extents(void * handle,uint32_t count,uint64_t offset,uint32_t flags,struct nbdkit_extents * extents)822 sh_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
823 struct nbdkit_extents *extents)
824 {
825 const char *method = "extents";
826 const char *script = get_script (method);
827 struct sh_handle *h = handle;
828 char cbuf[32], obuf[32], fbuf[32];
829 const char *args[] = { script, method, h->h, cbuf, obuf, fbuf, NULL };
830 CLEANUP_FREE char *s = NULL;
831 size_t slen;
832 int r;
833
834 snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
835 snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
836 flags_string (flags, fbuf, sizeof fbuf);
837
838 switch (call_read (&s, &slen, args)) {
839 case OK:
840 r = parse_extents (script, s, slen, extents);
841 return r;
842
843 case MISSING:
844 /* extents method should not have been called unless the script
845 * defined a can_extents method which returns true, so if this
846 * happens it's a script error.
847 */
848 nbdkit_error ("%s: can_extents returned true, "
849 "but extents method is not defined",
850 script);
851 errno = EIO;
852 return -1;
853
854 case ERROR:
855 return -1;
856
857 case RET_FALSE:
858 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
859 script, method);
860 errno = EIO;
861 return -1;
862
863 default: abort ();
864 }
865 }
866
867 int
sh_cache(void * handle,uint32_t count,uint64_t offset,uint32_t flags)868 sh_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
869 {
870 const char *method = "cache";
871 const char *script = get_script (method);
872 struct sh_handle *h = handle;
873 char cbuf[32], obuf[32];
874 const char *args[] = { script, method, h->h, cbuf, obuf, NULL };
875
876 snprintf (cbuf, sizeof cbuf, "%" PRIu32, count);
877 snprintf (obuf, sizeof obuf, "%" PRIu64, offset);
878 assert (!flags);
879
880 switch (call (args)) {
881 case OK:
882 return 0;
883
884 case MISSING:
885 /* Ignore lack of cache callback. */
886 return 0;
887
888 case ERROR:
889 return -1;
890
891 case RET_FALSE:
892 nbdkit_error ("%s: %s method returned unexpected code (3/false)",
893 script, method);
894 errno = EIO;
895 return -1;
896
897 default: abort ();
898 }
899 }
900