Update Red Hat Copyright Notices
[nbdkit.git] / filters / protect / protect.c
blob1ccca28db17a8d7c4050e9537ace4e42fd475e62
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 <stdbool.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <assert.h>
43 #include <nbdkit-filter.h>
45 #include "cleanup.h"
46 #include "iszero.h"
47 #include "minmax.h"
48 #include "regions.h"
49 #include "strndup.h"
50 #include "vector.h"
52 /* Range definition.
54 * A single struct range stores a range from start-end (inclusive).
55 * end can be INT64_MAX to indicate the end of the file.
57 * 'range_list' stores the list of protected ranges, unsorted.
59 struct range { uint64_t start, end; const char *description; };
60 DEFINE_VECTOR_TYPE (ranges, struct range);
61 static ranges range_list;
63 /* region_list covers the whole address space with protected and
64 * unprotected ranges.
66 static regions region_list;
68 static void
69 protect_unload (void)
71 free (region_list.ptr);
72 free (range_list.ptr);
75 /* Parse "START-END" into a range or "~START-END" into two ranges.
76 * Adds the range(s) to range_list, or exits with an error.
78 static void
79 parse_range (const char *value)
81 CLEANUP_FREE char *start_str = NULL;
82 const char *end_str;
83 uint64_t start, end;
84 bool negate = false;
85 const char *description = value;
87 if (value[0] == '~') {
88 negate = true;
89 value++;
92 end_str = strchr (value, '-');
93 if (end_str == NULL) {
94 nbdkit_error ("cannot parse range, missing '-': %s", description);
95 exit (EXIT_FAILURE);
97 start_str = strndup (value, end_str - value);
98 if (start_str == NULL) {
99 nbdkit_error ("strndup: %m");
100 exit (EXIT_FAILURE);
102 end_str++;
104 if (start_str[0] == '\0')
105 start = 0;
106 else if (nbdkit_parse_uint64_t ("range", start_str, &start) == -1)
107 exit (EXIT_FAILURE);
109 if (end_str[0] == '\0')
110 end = INT64_MAX;
111 else if (nbdkit_parse_uint64_t ("range", end_str, &end) == -1)
112 exit (EXIT_FAILURE);
114 if (end < start) {
115 nbdkit_error ("invalid range, end < start: %s", description);
116 exit (EXIT_FAILURE);
119 if (!negate) {
120 struct range range =
121 { .start = start, .end = end, .description = description };
123 if (ranges_append (&range_list, range) == -1) {
124 nbdkit_error ("ranges_append: %m");
125 exit (EXIT_FAILURE);
128 else { /* ~START-END creates up to two ranges */
129 struct range range = { .description = description };
131 if (start > 0) {
132 range.start = 0;
133 range.end = start - 1;
134 if (ranges_append (&range_list, range) == -1) {
135 nbdkit_error ("ranges_append: %m");
136 exit (EXIT_FAILURE);
139 if (end < INT64_MAX) {
140 range.start = end + 1;
141 range.end = INT64_MAX;
142 if (ranges_append (&range_list, range) == -1) {
143 nbdkit_error ("ranges_append: %m");
144 exit (EXIT_FAILURE);
150 static int
151 protect_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
152 const char *key, const char *value)
154 if (strcmp (key, "protect") == 0) {
155 parse_range (value);
156 return 0;
158 else
159 return next (nxdata, key, value);
162 static int
163 compare_ranges (const struct range *r1, const struct range *r2)
165 if (r1->start < r2->start)
166 return -1;
167 else if (r1->start > r2->start)
168 return 1;
169 else
170 return 0;
173 static void
174 append_unprotected_region (uint64_t end)
176 if (append_region_end (&region_list, "unprotected", end,
177 0, 0, region_data, NULL) == -1) {
178 nbdkit_error ("append region: %m");
179 exit (EXIT_FAILURE);
183 static void
184 append_protected_region (struct range range)
186 assert (virtual_size (&region_list) == range.start);
188 if (append_region_end (&region_list, range.description, range.end,
189 0, 0, region_data, "protected") == -1) {
190 nbdkit_error ("append region: %m");
191 exit (EXIT_FAILURE);
195 static int
196 protect_config_complete (nbdkit_next_config_complete *next,
197 nbdkit_backend *nxdata)
199 size_t i;
201 if (range_list.len > 0) {
202 /* Order the ranges and combine adjacent and overlapping ranges. */
203 ranges_sort (&range_list, compare_ranges);
205 /* Combine adjacent and overlapping ranges. */
206 for (i = 0; i < range_list.len - 1; ++i) {
207 /* This is true because we've sorted the ranges. */
208 assert (range_list.ptr[i].start <= range_list.ptr[i+1].start);
210 /* Adjacent or overlapping with the next range? */
211 if (range_list.ptr[i].end + 1 >= range_list.ptr[i+1].start) {
212 range_list.ptr[i].end = range_list.ptr[i+1].end;
213 ranges_remove (&range_list, i+1);
214 i--;
215 continue;
220 /* Now convert these to a complete list of regions covering the
221 * whole 64 bit address space.
223 * Insert an initial unprotected region before the first protected
224 * range.
226 if (range_list.len != 0 && range_list.ptr[0].start > 0)
227 append_unprotected_region (range_list.ptr[0].start - 1);
229 for (i = 0; i < range_list.len; ++i) {
230 append_protected_region (range_list.ptr[i]);
232 /* Insert an unprotected region before the next range. */
233 if (i+1 < range_list.len)
234 append_unprotected_region (range_list.ptr[i+1].start - 1);
237 /* Insert a final unprotected region at the end. */
238 if ((uint64_t) virtual_size (&region_list) < (uint64_t) INT64_MAX)
239 append_unprotected_region (INT64_MAX);
241 return next (nxdata);
244 #define protect_config_help \
245 "protect=<START>-<END> Protect range of bytes START-END (inclusive)."
247 /* -D protect.write=1 to debug write checks. */
248 NBDKIT_DLL_PUBLIC int protect_debug_write = 0;
250 /* Check the proposed write operation.
252 * If offset and count contain any protected ranges, then we check
253 * that the write does not modify those ranges. If buf != NULL then
254 * we check that the data proposed to be written to the protected
255 * ranges matches what we read from the plugin. If buf == NULL then
256 * we check that the plugin reads zero for the protected ranges.
258 static int
259 check_write (nbdkit_next *next,
260 uint32_t count, uint64_t offset, const void *buf, int *err)
262 while (count > 0) {
263 const struct region *region;
264 bool protected;
265 uint64_t len;
266 int r;
268 region = find_region (&region_list, offset);
269 assert (region != NULL);
270 assert (region->type == region_data);
272 protected = region->u.data != NULL;
273 len = MIN (region->end - offset + 1, (uint64_t) count);
274 assert (len > 0);
276 if (protect_debug_write)
277 nbdkit_debug ("protect: %s offset %" PRIu64 " length %" PRIu64,
278 protected ? "checking protected region"
279 : "skipping unprotected region",
280 offset, len);
282 if (protected) {
283 bool matches;
284 CLEANUP_FREE char *expected = malloc (len);
285 if (expected == NULL) {
286 nbdkit_error ("malloc: %m");
287 *err = errno;
288 return -1;
291 /* Read the underlying plugin. */
292 r = next->pread (next, expected, len, offset, 0, err);
293 if (r == -1)
294 return -1; /* read error */
296 /* Expected data should match buffer (or zero if buf == NULL). */
297 if (buf)
298 matches = memcmp (expected, buf, len) == 0;
299 else
300 matches = is_zero (expected, len);
301 if (!matches) {
302 nbdkit_error ("protect filter prevented write to protected range %s",
303 region->description);
304 *err = EPERM;
305 return -1;
309 count -= len;
310 buf += len;
311 offset += len;
314 return 0;
317 /* Write data. */
318 static int
319 protect_pwrite (nbdkit_next *next,
320 void *handle,
321 const void *buf,
322 uint32_t count, uint64_t offset, uint32_t flags,
323 int *err)
325 if (check_write (next, count, offset, buf, err) == -1)
326 return -1;
328 return next->pwrite (next, buf, count, offset, flags, err);
331 /* Trim data. */
332 static int
333 protect_trim (nbdkit_next *next,
334 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
335 int *err)
337 if (check_write (next, count, offset, NULL, err) == -1)
338 return -1;
340 return next->trim (next, count, offset, flags, err);
343 /* Zero data. */
344 static int
345 protect_zero (nbdkit_next *next,
346 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
347 int *err)
349 if (check_write (next, count, offset, NULL, err) == -1)
350 return -1;
352 return next->zero (next, count, offset, flags, err);
355 static struct nbdkit_filter filter = {
356 .name = "protect",
357 .longname = "nbdkit protect filter",
358 .unload = protect_unload,
359 .config = protect_config,
360 .config_complete = protect_config_complete,
361 .config_help = protect_config_help,
362 .pwrite = protect_pwrite,
363 .trim = protect_trim,
364 .zero = protect_zero,
367 NBDKIT_REGISTER_FILTER (filter)