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