Update Red Hat Copyright Notices
[nbdkit.git] / filters / checkwrite / checkwrite.c
blob294681b34d66a27bbff62ca758346b8821ecc892
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 <string.h>
38 #include <errno.h>
40 #include <nbdkit-filter.h>
42 #include "cleanup.h"
43 #include "iszero.h"
44 #include "minmax.h"
46 static void *
47 checkwrite_open (nbdkit_next_open *next, nbdkit_context *nxdata,
48 int readonly, const char *exportname, int is_tls)
50 /* Ignore readonly flag passed in, open the plugin readonly. */
51 if (next (nxdata, 1, exportname) == -1)
52 return NULL;
53 return NBDKIT_HANDLE_NOT_NEEDED;
56 /* Whatever the underlying plugin can or can't do, we can do all the
57 * write-like operations.
59 static int
60 checkwrite_can_write (nbdkit_next *next,
61 void *handle)
63 return 1;
66 static int
67 checkwrite_can_flush (nbdkit_next *next,
68 void *handle)
70 return 1;
73 static int
74 checkwrite_can_fua (nbdkit_next *next,
75 void *handle)
77 return NBDKIT_FUA_NATIVE;
80 static int
81 checkwrite_can_trim (nbdkit_next *next,
82 void *handle)
84 return 1;
87 static int
88 checkwrite_can_zero (nbdkit_next *next,
89 void *handle)
91 return NBDKIT_ZERO_NATIVE;
94 static int
95 checkwrite_can_fast_zero (nbdkit_next *next,
96 void *handle)
98 /* It is better to advertise support, even if we always reject fast
99 * zero attempts when the plugin lacks .can_extents.
101 return 1;
104 static int
105 checkwrite_can_multi_conn (nbdkit_next *next,
106 void *handle)
108 return 1;
111 static inline int
112 data_does_not_match (int *err)
114 *err = EIO;
115 nbdkit_error ("data written does not match expected");
116 return -1;
119 /* Provide write-like operations which perform the additional checks. */
120 static int
121 checkwrite_pwrite (nbdkit_next *next,
122 void *handle,
123 const void *buf, uint32_t count, uint64_t offset,
124 uint32_t flags, int *err)
126 CLEANUP_FREE char *expected;
128 expected = malloc (count);
129 if (expected == NULL) {
130 *err = errno;
131 nbdkit_error ("malloc: %m");
132 return -1;
135 /* Read underlying plugin data into the buffer. */
136 if (next->pread (next, expected, count, offset, 0, err) == -1)
137 return -1;
139 /* If data written doesn't match data expected, inject EIO. */
140 if (memcmp (buf, expected, count) != 0)
141 return data_does_not_match (err);
143 return 0;
146 static int
147 checkwrite_flush (nbdkit_next *next,
148 void *handle, uint32_t flags, int *err)
150 /* Does nothing, we just have to support it. */
151 return 0;
154 #define MAX_REQUEST_SIZE (64 * 1024 * 1024) /* XXX */
156 /* Trim and zero are effectively the same operation for this plugin.
157 * We have to check that the underlying plugin contains all zeroes.
159 * Note we don't check that the extents exactly match since a valid
160 * copying operation is to either add sparseness (qemu-img convert -S)
161 * or create a fully allocated target (nbdcopy --allocated).
163 static int
164 checkwrite_trim_zero (nbdkit_next *next,
165 void *handle, uint32_t count, uint64_t offset,
166 uint32_t flags, int *err)
168 /* If the plugin supports extents, speed this up by using them. */
169 if (next->can_extents (next)) {
170 size_t i, n;
171 CLEANUP_EXTENTS_FREE struct nbdkit_extents *exts =
172 nbdkit_extents_full (next, count, offset, 0, err);
173 if (exts == NULL)
174 return -1;
176 n = nbdkit_extents_count (exts);
177 for (i = 0; i < n && count > 0; ++i) {
178 const struct nbdkit_extent e = nbdkit_get_extent (exts, i);
179 const uint64_t next_extent_offset = e.offset + e.length;
181 /* Anything that reads back as zero is good. */
182 if ((e.type & NBDKIT_EXTENT_ZERO) != 0) {
183 const uint64_t zerolen = MIN (count, next_extent_offset - offset);
185 offset += zerolen;
186 count -= zerolen;
187 continue;
190 /* Otherwise we have to read the underlying data and check. */
191 if (flags & NBDKIT_FLAG_FAST_ZERO) {
192 *err = ENOTSUP;
193 return -1;
195 while (offset < next_extent_offset && count > 0) {
196 size_t buflen = MIN (MAX_REQUEST_SIZE, count);
197 buflen = MIN (buflen, next_extent_offset - offset);
199 CLEANUP_FREE char *buf = malloc (buflen);
200 if (buf == NULL) {
201 *err = errno;
202 nbdkit_error ("malloc: %m");
203 return -1;
206 if (next->pread (next, buf, buflen, offset, 0, err) == -1)
207 return -1;
208 if (! is_zero (buf, buflen))
209 return data_does_not_match (err);
211 count -= buflen;
212 offset += buflen;
214 } /* for extent */
216 /* Assert that the loop above has actually checked the whole
217 * region. If this fires then it could be because
218 * nbdkit_extents_full isn't returning a full range of extents for
219 * the whole region ... or maybe the loop above is wrong.
221 assert (count == 0);
224 /* Otherwise the plugin does not support extents, so do this the
225 * slow way.
227 else {
228 CLEANUP_FREE char *buf;
230 if (flags & NBDKIT_FLAG_FAST_ZERO) {
231 *err = ENOTSUP;
232 return -1;
234 buf = malloc (MIN (MAX_REQUEST_SIZE, count));
235 if (buf == NULL) {
236 *err = errno;
237 nbdkit_error ("malloc: %m");
238 return -1;
241 while (count > 0) {
242 uint32_t n = MIN (MAX_REQUEST_SIZE, count);
244 if (next->pread (next, buf, n, offset, 0, err) == -1)
245 return -1;
246 if (! is_zero (buf, n))
247 return data_does_not_match (err);
248 count -= n;
249 offset += n;
253 return 0;
256 static struct nbdkit_filter filter = {
257 .name = "checkwrite",
258 .longname = "nbdkit checkwrite filter",
260 .open = checkwrite_open,
261 .can_write = checkwrite_can_write,
262 .can_flush = checkwrite_can_flush,
263 .can_fua = checkwrite_can_fua,
264 .can_trim = checkwrite_can_trim,
265 .can_zero = checkwrite_can_zero,
266 .can_fast_zero = checkwrite_can_fast_zero,
267 .can_multi_conn = checkwrite_can_multi_conn,
269 .pwrite = checkwrite_pwrite,
270 .flush = checkwrite_flush,
271 .trim = checkwrite_trim_zero,
272 .zero = checkwrite_trim_zero,
275 NBDKIT_REGISTER_FILTER (filter)