4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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.
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.
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
46 #include <nbdkit-filter.h>
50 #include "windows-compat.h"
52 struct error_settings
{
53 int error
; /* errno, eg. EIO */
54 double rate
; /* rate, 0.0 = never, 1.0 = always */
55 char *file
; /* error file, NULL = no file */
58 #define ERROR_DEFAULT { .error = EIO, .rate = 0, .file = NULL }
60 /* Settings for each type of request, read from the command line. */
61 static struct error_settings pread_settings
= ERROR_DEFAULT
;
62 static struct error_settings pwrite_settings
= ERROR_DEFAULT
;
63 static struct error_settings trim_settings
= ERROR_DEFAULT
;
64 static struct error_settings zero_settings
= ERROR_DEFAULT
;
65 static struct error_settings extents_settings
= ERROR_DEFAULT
;
66 static struct error_settings cache_settings
= ERROR_DEFAULT
;
69 * This must only be accessed when holding the lock (except for load).
71 static struct random_state random_state
;
72 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
77 xsrandom (time (NULL
), &random_state
);
83 free (pread_settings
.file
);
84 free (pwrite_settings
.file
);
85 free (trim_settings
.file
);
86 free (zero_settings
.file
);
87 free (extents_settings
.file
);
88 free (cache_settings
.file
);
91 static const struct { const char *name
; int error
; } errors
[] = {
97 { "ESHUTDOWN", ESHUTDOWN
},
102 error_as_string (int error
)
106 for (i
= 0; errors
[i
].name
!= NULL
; ++i
) {
107 if (errors
[i
].error
== error
)
108 return errors
[i
].name
;
114 parse_error (const char *key
, const char *value
, int *retp
)
118 for (i
= 0; errors
[i
].name
!= NULL
; ++i
) {
119 if (strcmp (value
, errors
[i
].name
) == 0) {
120 *retp
= errors
[i
].error
;
125 nbdkit_error ("%s: unknown error name '%s'", key
, value
);
130 parse_error_rate (const char *key
, const char *value
, double *retp
)
135 if (sscanf (value
, "%lg%n", &d
, &n
) == 1) {
136 if (strcmp (&value
[n
], "%") == 0) /* percentage */
138 else if (strcmp (&value
[n
], "") == 0) /* probability */
145 nbdkit_error ("%s: could not parse rate '%s'", key
, value
);
148 if (d
< 0 || d
> 1) {
149 nbdkit_error ("%s: rate out of range: '%s' parsed as %g", key
, value
, d
);
156 /* Called for each key=value passed on the command line. */
158 error_config (nbdkit_next_config
*next
, nbdkit_backend
*nxdata
,
159 const char *key
, const char *value
)
164 if (strcmp (key
, "error") == 0) {
165 if (parse_error (key
, value
, &i
) == -1)
167 pread_settings
.error
= pwrite_settings
.error
=
168 trim_settings
.error
= zero_settings
.error
=
169 extents_settings
.error
= cache_settings
.error
= i
;
172 else if (strcmp (key
, "error-pread") == 0)
173 return parse_error (key
, value
, &pread_settings
.error
);
174 else if (strcmp (key
, "error-pwrite") == 0)
175 return parse_error (key
, value
, &pwrite_settings
.error
);
176 else if (strcmp (key
, "error-trim") == 0)
177 return parse_error (key
, value
, &trim_settings
.error
);
178 else if (strcmp (key
, "error-zero") == 0)
179 return parse_error (key
, value
, &zero_settings
.error
);
180 else if (strcmp (key
, "error-extents") == 0)
181 return parse_error (key
, value
, &extents_settings
.error
);
182 else if (strcmp (key
, "error-cache") == 0)
183 return parse_error (key
, value
, &cache_settings
.error
);
185 else if (strcmp (key
, "error-rate") == 0) {
186 if (parse_error_rate (key
, value
, &d
) == -1)
188 pread_settings
.rate
= pwrite_settings
.rate
=
189 trim_settings
.rate
= zero_settings
.rate
=
190 extents_settings
.rate
= cache_settings
.rate
= d
;
193 else if (strcmp (key
, "error-pread-rate") == 0)
194 return parse_error_rate (key
, value
, &pread_settings
.rate
);
195 else if (strcmp (key
, "error-pwrite-rate") == 0)
196 return parse_error_rate (key
, value
, &pwrite_settings
.rate
);
197 else if (strcmp (key
, "error-trim-rate") == 0)
198 return parse_error_rate (key
, value
, &trim_settings
.rate
);
199 else if (strcmp (key
, "error-zero-rate") == 0)
200 return parse_error_rate (key
, value
, &zero_settings
.rate
);
201 else if (strcmp (key
, "error-extents-rate") == 0)
202 return parse_error_rate (key
, value
, &extents_settings
.rate
);
203 else if (strcmp (key
, "error-cache-rate") == 0)
204 return parse_error_rate (key
, value
, &cache_settings
.rate
);
206 /* NB: We are using nbdkit_absolute_path here because the trigger
207 * file probably doesn't exist yet.
209 else if (strcmp (key
, "error-file") == 0) {
210 free (pread_settings
.file
);
211 pread_settings
.file
= nbdkit_absolute_path (value
);
212 free (pwrite_settings
.file
);
213 pwrite_settings
.file
= nbdkit_absolute_path (value
);
214 free (trim_settings
.file
);
215 trim_settings
.file
= nbdkit_absolute_path (value
);
216 free (zero_settings
.file
);
217 zero_settings
.file
= nbdkit_absolute_path (value
);
218 free (extents_settings
.file
);
219 extents_settings
.file
= nbdkit_absolute_path (value
);
220 free (cache_settings
.file
);
221 cache_settings
.file
= nbdkit_absolute_path (value
);
224 else if (strcmp (key
, "error-pread-file") == 0) {
225 free (pread_settings
.file
);
226 pread_settings
.file
= nbdkit_absolute_path (value
);
229 else if (strcmp (key
, "error-pwrite-file") == 0) {
230 free (pwrite_settings
.file
);
231 pwrite_settings
.file
= nbdkit_absolute_path (value
);
234 else if (strcmp (key
, "error-trim-file") == 0) {
235 free (trim_settings
.file
);
236 trim_settings
.file
= nbdkit_absolute_path (value
);
239 else if (strcmp (key
, "error-zero-file") == 0) {
240 free (zero_settings
.file
);
241 zero_settings
.file
= nbdkit_absolute_path (value
);
244 else if (strcmp (key
, "error-extents-file") == 0) {
245 free (extents_settings
.file
);
246 extents_settings
.file
= nbdkit_absolute_path (value
);
249 else if (strcmp (key
, "error-cache-file") == 0) {
250 free (cache_settings
.file
);
251 cache_settings
.file
= nbdkit_absolute_path (value
);
256 return next (nxdata
, key
, value
);
259 #define error_config_help \
260 "error=EPERM|EIO|ENOMEM|EINVAL|ENOSPC|ESHUTDOWN\n" \
261 " The error indication to return.\n" \
262 "error-rate=0%..100%|0..1 Rate of errors to generate.\n" \
263 "error-file=TRIGGER Set trigger filename.\n" \
264 "error-pread*, error-pwrite*, error-trim*, error-zero*, error-extents*\n" \
265 " Apply settings only to read/write/etc"
267 /* This function injects a random error. */
269 random_error (const struct error_settings
*error_settings
,
270 const char *fn
, int *err
)
274 if (error_settings
->rate
<= 0) /* 0% = never inject */
277 /* Does the trigger file exist? */
278 if (error_settings
->file
!= NULL
) {
279 if (access (error_settings
->file
, F_OK
) == -1)
283 if (error_settings
->rate
>= 1) /* 100% = always inject */
286 /* To avoid the question if (double)1.0 * UINT64_MAX is
287 * representable in a 64 bit integer, and because we don't need all
288 * this precision anyway, let's work in 32 bits.
291 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
292 rand
= xrandom (&random_state
) & UINT32_MAX
;
294 if (rand
>= error_settings
->rate
* UINT32_MAX
)
298 *err
= error_settings
->error
;
299 nbdkit_error ("injecting %s error into %s", error_as_string (*err
), fn
);
305 error_pread (nbdkit_next
*next
,
306 void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
307 uint32_t flags
, int *err
)
309 if (random_error (&pread_settings
, "pread", err
))
312 return next
->pread (next
, buf
, count
, offset
, flags
, err
);
317 error_pwrite (nbdkit_next
*next
,
319 const void *buf
, uint32_t count
, uint64_t offset
,
320 uint32_t flags
, int *err
)
322 if (random_error (&pwrite_settings
, "pwrite", err
))
325 return next
->pwrite (next
, buf
, count
, offset
, flags
, err
);
330 error_trim (nbdkit_next
*next
,
331 void *handle
, uint32_t count
, uint64_t offset
,
332 uint32_t flags
, int *err
)
334 if (random_error (&trim_settings
, "trim", err
))
337 return next
->trim (next
, count
, offset
, flags
, err
);
342 error_zero (nbdkit_next
*next
,
343 void *handle
, uint32_t count
, uint64_t offset
,
344 uint32_t flags
, int *err
)
346 if (random_error (&zero_settings
, "zero", err
))
349 return next
->zero (next
, count
, offset
, flags
, err
);
354 error_extents (nbdkit_next
*next
,
355 void *handle
, uint32_t count
, uint64_t offset
,
356 uint32_t flags
, struct nbdkit_extents
*extents
, int *err
)
358 if (random_error (&extents_settings
, "extents", err
))
361 return next
->extents (next
, count
, offset
, flags
, extents
, err
);
366 error_cache (nbdkit_next
*next
,
367 void *handle
, uint32_t count
, uint64_t offset
,
368 uint32_t flags
, int *err
)
370 if (random_error (&cache_settings
, "cache", err
))
373 return next
->cache (next
, count
, offset
, flags
, err
);
376 static struct nbdkit_filter filter
= {
377 .name
= "error-inject",
378 .longname
= "nbdkit error injection filter",
380 .unload
= error_unload
,
381 .config
= error_config
,
382 .config_help
= error_config_help
,
383 .pread
= error_pread
,
384 .pwrite
= error_pwrite
,
387 .extents
= error_extents
,
388 .cache
= error_cache
,
391 NBDKIT_REGISTER_FILTER (filter
)