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
42 #include <nbdkit-filter.h>
44 #include "ispowerof2.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
;
55 static enum { EP_ALLOW
, EP_ERROR
} error_policy
= EP_ALLOW
;
58 policy_config (nbdkit_next_config
*next
, nbdkit_backend
*nxdata
,
59 const char *key
, const char *value
)
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
;
69 nbdkit_error ("unknown %s: %s", key
, value
);
74 else if (strcmp (key
, "blocksize-minimum") == 0) {
75 r
= nbdkit_parse_size (value
);
76 if (r
== -1 || r
> UINT32_MAX
) {
78 nbdkit_error ("%s: could not parse %s", key
, value
);
84 else if (strcmp (key
, "blocksize-preferred") == 0) {
85 r
= nbdkit_parse_size (value
);
86 if (r
== -1 || r
> UINT32_MAX
) goto parse_error
;
90 else if (strcmp (key
, "blocksize-maximum") == 0) {
91 r
= nbdkit_parse_size (value
);
92 if (r
== -1 || r
> UINT32_MAX
) goto parse_error
;
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
;
103 return next (nxdata
, key
, value
);
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");
119 if (config_minimum
> 65536) {
120 nbdkit_error ("blocksize-minimum must be <= 64K");
125 if (config_preferred
) {
126 if (! is_power_of_2 (config_preferred
)) {
127 nbdkit_error ("blocksize-preferred must be a power of 2");
130 if (config_preferred
< 512 || config_preferred
> 32 * 1024 * 1024) {
131 nbdkit_error ("blocksize-preferred must be between 512 and 32M");
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");
145 if (config_minimum
&& config_preferred
) {
146 if (config_minimum
> config_preferred
) {
147 nbdkit_error ("blocksize-minimum must be <= blocksize-preferred");
152 if (config_preferred
&& config_maximum
) {
153 if (config_preferred
> config_maximum
) {
154 nbdkit_error ("blocksize-preferred must be <= blocksize-maximum");
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");
167 return next (nxdata
);
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
;
184 /* Otherwise, ask the plugin. */
185 if (next
->block_size (next
, minimum
, preferred
, maximum
) == -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
)
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
199 if (*minimum
== 0) { /* Plugin didn't set anything. */
201 *minimum
= config_minimum
;
205 if (config_preferred
)
206 *preferred
= config_preferred
;
211 *maximum
= config_maximum
;
212 else if (config_disconnect
)
213 *maximum
= ROUND_DOWN (config_disconnect
, *minimum
);
215 *maximum
= 0xffffffff;
217 else { /* Plugin set some values. */
219 *minimum
= config_minimum
;
221 if (config_preferred
)
222 *preferred
= config_preferred
;
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
);
238 /* This function checks the error policy for all request functions
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
245 * The NBD specification mandates EINVAL for block size constraint
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
)
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
264 if (policy_block_size (next
, handle
,
265 &minimum
, &preferred
, &maximum
) == -1) {
266 *err
= errno
? : EINVAL
;
270 /* If there are no constraints, allow. */
274 /* Check constraints. */
275 if (count
< minimum
) {
277 nbdkit_error ("client %s request rejected: "
278 "count %" PRIu32
" is smaller than minimum size %" PRIu32
,
279 type
, count
, minimum
);
282 if (data
&& count
> maximum
) { /* Only do this for pread/pwrite. */
284 nbdkit_error ("client %s request rejected: "
285 "count %" PRIu32
" is larger than maximum size %" PRIu32
,
286 type
, count
, maximum
);
289 if ((count
% minimum
) != 0) {
291 nbdkit_error ("client %s request rejected: "
292 "count %" PRIu32
" is not a multiple "
293 "of minimum size %" PRIu32
,
294 type
, count
, minimum
);
297 if ((offset
% minimum
) != 0) {
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
);
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)
317 return next
->pread (next
, buf
, count
, offset
, flags
, err
);
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);
332 if (check_policy (next
, handle
, "pwrite", true, count
, offset
, err
) == -1)
335 return next
->pwrite (next
, buf
, count
, offset
, flags
, err
);
339 policy_zero (nbdkit_next
*next
,
340 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
343 if (check_policy (next
, handle
, "zero", false, count
, offset
, err
) == -1)
346 return next
->zero (next
, count
, offset
, flags
, err
);
350 policy_trim (nbdkit_next
*next
,
351 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
354 if (check_policy (next
, handle
, "trim", false, count
, offset
, err
) == -1)
357 return next
->trim (next
, count
, offset
, flags
, err
);
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)
368 return next
->cache (next
, count
, offset
, flags
, err
);
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)
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
,
394 .cache
= policy_cache
,
395 .extents
= policy_extents
,
398 NBDKIT_REGISTER_FILTER (filter
)