Update Red Hat Copyright Notices
[nbdkit.git] / filters / blocksize-policy / policy.c
blobafeb724b281acad1f1178544677f8bf86272bfeb
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 <inttypes.h>
39 #include <string.h>
40 #include <errno.h>
42 #include <nbdkit-filter.h>
44 #include "ispowerof2.h"
45 #include "rounding.h"
46 #include "windows-compat.h"
48 /* Block size constraints configured on the command line (0 = unset). */
49 static uint32_t config_minimum;
50 static uint32_t config_preferred;
51 static uint32_t config_maximum;
52 static uint32_t config_disconnect;
54 /* Error policy. */
55 static enum { EP_ALLOW, EP_ERROR } error_policy = EP_ALLOW;
57 static int
58 policy_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
59 const char *key, const char *value)
61 int64_t r;
63 if (strcmp (key, "blocksize-error-policy") == 0) {
64 if (strcmp (value, "allow") == 0)
65 error_policy = EP_ALLOW;
66 else if (strcmp (value, "error") == 0)
67 error_policy = EP_ERROR;
68 else {
69 nbdkit_error ("unknown %s: %s", key, value);
70 return -1;
72 return 0;
74 else if (strcmp (key, "blocksize-minimum") == 0) {
75 r = nbdkit_parse_size (value);
76 if (r == -1 || r > UINT32_MAX) {
77 parse_error:
78 nbdkit_error ("%s: could not parse %s", key, value);
79 return -1;
81 config_minimum = r;
82 return 0;
84 else if (strcmp (key, "blocksize-preferred") == 0) {
85 r = nbdkit_parse_size (value);
86 if (r == -1 || r > UINT32_MAX) goto parse_error;
87 config_preferred = r;
88 return 0;
90 else if (strcmp (key, "blocksize-maximum") == 0) {
91 r = nbdkit_parse_size (value);
92 if (r == -1 || r > UINT32_MAX) goto parse_error;
93 config_maximum = r;
94 return 0;
96 else if (strcmp (key, "blocksize-write-disconnect") == 0) {
97 r = nbdkit_parse_size (value);
98 if (r == -1 || r > UINT32_MAX) goto parse_error;
99 config_disconnect = r;
100 return 0;
103 return next (nxdata, key, value);
106 static int
107 policy_config_complete (nbdkit_next_config_complete *next,
108 nbdkit_backend *nxdata)
110 /* These checks roughly reflect the same checks made in
111 * server/plugins.c: plugin_block_size
114 if (config_minimum) {
115 if (! is_power_of_2 (config_minimum)) {
116 nbdkit_error ("blocksize-minimum must be a power of 2");
117 return -1;
119 if (config_minimum > 65536) {
120 nbdkit_error ("blocksize-minimum must be <= 64K");
121 return -1;
125 if (config_preferred) {
126 if (! is_power_of_2 (config_preferred)) {
127 nbdkit_error ("blocksize-preferred must be a power of 2");
128 return -1;
130 if (config_preferred < 512 || config_preferred > 32 * 1024 * 1024) {
131 nbdkit_error ("blocksize-preferred must be between 512 and 32M");
132 return -1;
136 if (config_minimum && config_maximum) {
137 if (config_maximum != (uint32_t)-1 &&
138 (config_maximum % config_maximum) != 0) {
139 nbdkit_error ("blocksize-maximum must be -1 "
140 "or a multiple of blocksize-minimum");
141 return -1;
145 if (config_minimum && config_preferred) {
146 if (config_minimum > config_preferred) {
147 nbdkit_error ("blocksize-minimum must be <= blocksize-preferred");
148 return -1;
152 if (config_preferred && config_maximum) {
153 if (config_preferred > config_maximum) {
154 nbdkit_error ("blocksize-preferred must be <= blocksize-maximum");
155 return -1;
159 if (config_minimum && config_disconnect) {
160 if (config_disconnect <= config_minimum) {
161 nbdkit_error ("blocksize-write-disonnect must be larger than "
162 "blocksize-minimum");
163 return -1;
167 return next (nxdata);
170 static int
171 policy_block_size (nbdkit_next *next, void *handle,
172 uint32_t *minimum, uint32_t *preferred, uint32_t *maximum)
174 /* If the user has set all of the block size parameters then we
175 * don't need to ask the plugin, we can go ahead and advertise them.
177 if (config_minimum && config_preferred && config_maximum) {
178 *minimum = config_minimum;
179 *preferred = config_preferred;
180 *maximum = config_maximum;
181 return 0;
184 /* Otherwise, ask the plugin. */
185 if (next->block_size (next, minimum, preferred, maximum) == -1)
186 return -1;
188 /* If the user of this filter didn't configure anything, then return
189 * the plugin values (even if unset).
191 if (!config_minimum && !config_preferred && !config_maximum)
192 return 0;
194 /* Now we get to the awkward case where the user configured some
195 * values but not others. There's all kinds of room for things to
196 * go wrong here, so try to check for obvious user errors as best we
197 * can.
199 if (*minimum == 0) { /* Plugin didn't set anything. */
200 if (config_minimum)
201 *minimum = config_minimum;
202 else
203 *minimum = 1;
205 if (config_preferred)
206 *preferred = config_preferred;
207 else
208 *preferred = 4096;
210 if (config_maximum)
211 *maximum = config_maximum;
212 else if (config_disconnect)
213 *maximum = ROUND_DOWN (config_disconnect, *minimum);
214 else
215 *maximum = 0xffffffff;
217 else { /* Plugin set some values. */
218 if (config_minimum)
219 *minimum = config_minimum;
221 if (config_preferred)
222 *preferred = config_preferred;
224 if (config_maximum)
225 *maximum = config_maximum;
228 if (*minimum > *preferred || *preferred > *maximum) {
229 nbdkit_error ("computed block size values are invalid, minimum %" PRIu32
230 " > preferred %" PRIu32
231 " or preferred > maximum %" PRIu32,
232 *minimum, *preferred, *maximum);
233 return -1;
235 return 0;
238 /* This function checks the error policy for all request functions
239 * below.
241 * The 'data' flag is true for pread and pwrite (where we check the
242 * maximum bound). We don't check maximum for non-data-carrying
243 * calls like zero.
245 * The NBD specification mandates EINVAL for block size constraint
246 * problems.
248 static int
249 check_policy (nbdkit_next *next, void *handle,
250 const char *type, bool data,
251 uint32_t count, uint64_t offset, int *err)
253 uint32_t minimum, preferred, maximum;
255 if (error_policy == EP_ALLOW)
256 return 0;
258 /* Get the current block size constraints. Note these are cached in
259 * the backend so if they've already been computed then this simply
260 * returns the cached values. The plugin is only asked once per
261 * connection.
263 errno = 0;
264 if (policy_block_size (next, handle,
265 &minimum, &preferred, &maximum) == -1) {
266 *err = errno ? : EINVAL;
267 return -1;
270 /* If there are no constraints, allow. */
271 if (minimum == 0)
272 return 0;
274 /* Check constraints. */
275 if (count < minimum) {
276 *err = EINVAL;
277 nbdkit_error ("client %s request rejected: "
278 "count %" PRIu32 " is smaller than minimum size %" PRIu32,
279 type, count, minimum);
280 return -1;
282 if (data && count > maximum) { /* Only do this for pread/pwrite. */
283 *err = EINVAL;
284 nbdkit_error ("client %s request rejected: "
285 "count %" PRIu32 " is larger than maximum size %" PRIu32,
286 type, count, maximum);
287 return -1;
289 if ((count % minimum) != 0) {
290 *err = EINVAL;
291 nbdkit_error ("client %s request rejected: "
292 "count %" PRIu32 " is not a multiple "
293 "of minimum size %" PRIu32,
294 type, count, minimum);
295 return -1;
297 if ((offset % minimum) != 0) {
298 *err = EINVAL;
299 nbdkit_error ("client %s request rejected: "
300 "offset %" PRIu64 " is not aligned to a multiple "
301 "of minimum size %" PRIu32,
302 type, offset, minimum);
303 return -1;
306 return 0;
309 static int
310 policy_pread (nbdkit_next *next,
311 void *handle, void *buf, uint32_t count, uint64_t offset,
312 uint32_t flags, int *err)
314 if (check_policy (next, handle, "pread", true, count, offset, err) == -1)
315 return -1;
317 return next->pread (next, buf, count, offset, flags, err);
320 static int
321 policy_pwrite (nbdkit_next *next,
322 void *handle, const void *buf, uint32_t count, uint64_t offset,
323 uint32_t flags, int *err)
325 if (config_disconnect && count > config_disconnect) {
326 nbdkit_error ("disconnecting client due to oversize write request");
327 nbdkit_disconnect (true);
328 *err = ESHUTDOWN;
329 return -1;
332 if (check_policy (next, handle, "pwrite", true, count, offset, err) == -1)
333 return -1;
335 return next->pwrite (next, buf, count, offset, flags, err);
338 static int
339 policy_zero (nbdkit_next *next,
340 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
341 int *err)
343 if (check_policy (next, handle, "zero", false, count, offset, err) == -1)
344 return -1;
346 return next->zero (next, count, offset, flags, err);
349 static int
350 policy_trim (nbdkit_next *next,
351 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
352 int *err)
354 if (check_policy (next, handle, "trim", false, count, offset, err) == -1)
355 return -1;
357 return next->trim (next, count, offset, flags, err);
360 static int
361 policy_cache (nbdkit_next *next,
362 void *handle, uint32_t count, uint64_t offset,
363 uint32_t flags, int *err)
365 if (check_policy (next, handle, "cache", false, count, offset, err) == -1)
366 return -1;
368 return next->cache (next, count, offset, flags, err);
371 static int
372 policy_extents (nbdkit_next *next,
373 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
374 struct nbdkit_extents *extents, int *err)
376 if (check_policy (next, handle, "extents", false, count, offset, err) == -1)
377 return -1;
379 return next->extents (next, count, offset, flags, extents, err);
382 static struct nbdkit_filter filter = {
383 .name = "blocksize-policy",
384 .longname = "nbdkit blocksize policy filter",
385 .config = policy_config,
386 .config_complete = policy_config_complete,
388 .block_size = policy_block_size,
390 .pread = policy_pread,
391 .pwrite = policy_pwrite,
392 .zero = policy_zero,
393 .trim = policy_trim,
394 .cache = policy_cache,
395 .extents = policy_extents,
398 NBDKIT_REGISTER_FILTER (filter)