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
40 #include <nbdkit-filter.h>
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)
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.
60 checkwrite_can_write (nbdkit_next
*next
,
67 checkwrite_can_flush (nbdkit_next
*next
,
74 checkwrite_can_fua (nbdkit_next
*next
,
77 return NBDKIT_FUA_NATIVE
;
81 checkwrite_can_trim (nbdkit_next
*next
,
88 checkwrite_can_zero (nbdkit_next
*next
,
91 return NBDKIT_ZERO_NATIVE
;
95 checkwrite_can_fast_zero (nbdkit_next
*next
,
98 /* It is better to advertise support, even if we always reject fast
99 * zero attempts when the plugin lacks .can_extents.
105 checkwrite_can_multi_conn (nbdkit_next
*next
,
112 data_does_not_match (int *err
)
115 nbdkit_error ("data written does not match expected");
119 /* Provide write-like operations which perform the additional checks. */
121 checkwrite_pwrite (nbdkit_next
*next
,
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
) {
131 nbdkit_error ("malloc: %m");
135 /* Read underlying plugin data into the buffer. */
136 if (next
->pread (next
, expected
, count
, offset
, 0, err
) == -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
);
147 checkwrite_flush (nbdkit_next
*next
,
148 void *handle
, uint32_t flags
, int *err
)
150 /* Does nothing, we just have to support it. */
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).
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
)) {
171 CLEANUP_EXTENTS_FREE
struct nbdkit_extents
*exts
=
172 nbdkit_extents_full (next
, count
, offset
, 0, err
);
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
);
190 /* Otherwise we have to read the underlying data and check. */
191 if (flags
& NBDKIT_FLAG_FAST_ZERO
) {
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
);
202 nbdkit_error ("malloc: %m");
206 if (next
->pread (next
, buf
, buflen
, offset
, 0, err
) == -1)
208 if (! is_zero (buf
, buflen
))
209 return data_does_not_match (err
);
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.
224 /* Otherwise the plugin does not support extents, so do this the
228 CLEANUP_FREE
char *buf
;
230 if (flags
& NBDKIT_FLAG_FAST_ZERO
) {
234 buf
= malloc (MIN (MAX_REQUEST_SIZE
, count
));
237 nbdkit_error ("malloc: %m");
242 uint32_t n
= MIN (MAX_REQUEST_SIZE
, count
);
244 if (next
->pread (next
, buf
, n
, offset
, 0, err
) == -1)
246 if (! is_zero (buf
, n
))
247 return data_does_not_match (err
);
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
)