Update Red Hat Copyright Notices
[nbdkit.git] / filters / error / error.c
blobac4da8c16c7675085dab88f3c98b5e58e3d27edc
1 /* nbdkit
2 * Copyright Red Hat
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
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
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdint.h>
38 #include <stdbool.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <time.h>
42 #include <errno.h>
44 #include <pthread.h>
46 #include <nbdkit-filter.h>
48 #include "cleanup.h"
49 #include "random.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;
68 /* Random state.
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;
74 static void
75 error_load (void)
77 xsrandom (time (NULL), &random_state);
80 static void
81 error_unload (void)
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[] = {
92 { "EPERM", EPERM },
93 { "EIO", EIO },
94 { "ENOMEM", ENOMEM },
95 { "EINVAL", EINVAL },
96 { "ENOSPC", ENOSPC },
97 { "ESHUTDOWN", ESHUTDOWN },
98 { NULL }
101 static const char *
102 error_as_string (int error)
104 size_t i;
106 for (i = 0; errors[i].name != NULL; ++i) {
107 if (errors[i].error == error)
108 return errors[i].name;
110 abort ();
113 static int
114 parse_error (const char *key, const char *value, int *retp)
116 size_t i;
118 for (i = 0; errors[i].name != NULL; ++i) {
119 if (strcmp (value, errors[i].name) == 0) {
120 *retp = errors[i].error;
121 return 0;
125 nbdkit_error ("%s: unknown error name '%s'", key, value);
126 return -1;
129 static int
130 parse_error_rate (const char *key, const char *value, double *retp)
132 double d;
133 int n;
135 if (sscanf (value, "%lg%n", &d, &n) == 1) {
136 if (strcmp (&value[n], "%") == 0) /* percentage */
137 d /= 100.0;
138 else if (strcmp (&value[n], "") == 0) /* probability */
140 else
141 goto bad_parse;
143 else {
144 bad_parse:
145 nbdkit_error ("%s: could not parse rate '%s'", key, value);
146 return -1;
148 if (d < 0 || d > 1) {
149 nbdkit_error ("%s: rate out of range: '%s' parsed as %g", key, value, d);
150 return -1;
152 *retp = d;
153 return 0;
156 /* Called for each key=value passed on the command line. */
157 static int
158 error_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
159 const char *key, const char *value)
161 int i;
162 double d;
164 if (strcmp (key, "error") == 0) {
165 if (parse_error (key, value, &i) == -1)
166 return -1;
167 pread_settings.error = pwrite_settings.error =
168 trim_settings.error = zero_settings.error =
169 extents_settings.error = cache_settings.error = i;
170 return 0;
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)
187 return -1;
188 pread_settings.rate = pwrite_settings.rate =
189 trim_settings.rate = zero_settings.rate =
190 extents_settings.rate = cache_settings.rate = d;
191 return 0;
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);
222 return 0;
224 else if (strcmp (key, "error-pread-file") == 0) {
225 free (pread_settings.file);
226 pread_settings.file = nbdkit_absolute_path (value);
227 return 0;
229 else if (strcmp (key, "error-pwrite-file") == 0) {
230 free (pwrite_settings.file);
231 pwrite_settings.file = nbdkit_absolute_path (value);
232 return 0;
234 else if (strcmp (key, "error-trim-file") == 0) {
235 free (trim_settings.file);
236 trim_settings.file = nbdkit_absolute_path (value);
237 return 0;
239 else if (strcmp (key, "error-zero-file") == 0) {
240 free (zero_settings.file);
241 zero_settings.file = nbdkit_absolute_path (value);
242 return 0;
244 else if (strcmp (key, "error-extents-file") == 0) {
245 free (extents_settings.file);
246 extents_settings.file = nbdkit_absolute_path (value);
247 return 0;
249 else if (strcmp (key, "error-cache-file") == 0) {
250 free (cache_settings.file);
251 cache_settings.file = nbdkit_absolute_path (value);
252 return 0;
255 else
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. */
268 static bool
269 random_error (const struct error_settings *error_settings,
270 const char *fn, int *err)
272 uint64_t rand;
274 if (error_settings->rate <= 0) /* 0% = never inject */
275 return false;
277 /* Does the trigger file exist? */
278 if (error_settings->file != NULL) {
279 if (access (error_settings->file, F_OK) == -1)
280 return false;
283 if (error_settings->rate >= 1) /* 100% = always inject */
284 goto 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)
295 return false;
297 inject:
298 *err = error_settings->error;
299 nbdkit_error ("injecting %s error into %s", error_as_string (*err), fn);
300 return true;
303 /* Read data. */
304 static int
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))
310 return -1;
312 return next->pread (next, buf, count, offset, flags, err);
315 /* Write data. */
316 static int
317 error_pwrite (nbdkit_next *next,
318 void *handle,
319 const void *buf, uint32_t count, uint64_t offset,
320 uint32_t flags, int *err)
322 if (random_error (&pwrite_settings, "pwrite", err))
323 return -1;
325 return next->pwrite (next, buf, count, offset, flags, err);
328 /* Trim data. */
329 static int
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))
335 return -1;
337 return next->trim (next, count, offset, flags, err);
340 /* Zero data. */
341 static int
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))
347 return -1;
349 return next->zero (next, count, offset, flags, err);
352 /* Extents. */
353 static int
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))
359 return -1;
361 return next->extents (next, count, offset, flags, extents, err);
364 /* Extents. */
365 static int
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))
371 return -1;
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",
379 .load = error_load,
380 .unload = error_unload,
381 .config = error_config,
382 .config_help = error_config_help,
383 .pread = error_pread,
384 .pwrite = error_pwrite,
385 .trim = error_trim,
386 .zero = error_zero,
387 .extents = error_extents,
388 .cache = error_cache,
391 NBDKIT_REGISTER_FILTER (filter)