Update Red Hat Copyright Notices
[nbdkit.git] / filters / truncate / truncate.c
blobe3516cdfed98f33dce78449ef5fa0fb1760b28d9
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 <string.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <inttypes.h>
43 #include <nbdkit-filter.h>
45 #include "cleanup.h"
46 #include "ispowerof2.h"
47 #include "iszero.h"
48 #include "rounding.h"
50 /* These are the parameters. */
51 static int64_t truncate_size = -1;
52 static unsigned round_up = 0, round_down = 0;
54 static int
55 parse_round_param (const char *key, const char *value, unsigned *ret)
57 int64_t r;
58 unsigned u;
60 /* Parse it as a "size" quantity so we allow round-up=1M and similar. */
61 r = nbdkit_parse_size (value);
62 if (r == -1)
63 return -1;
65 /* Must not be zero or larger than an unsigned int. */
66 if (r == 0) {
67 nbdkit_error ("if set, the %s parameter must be > 0", key);
68 return -1;
70 if (r > UINT_MAX) {
71 nbdkit_error ("the %s parameter is too large", key);
72 return -1;
74 u = r;
76 /* Must be a power of 2. We could relax this in future. */
77 if (!is_power_of_2 (u)) {
78 nbdkit_error ("the %s parameter must be a power of 2", key);
79 return -1;
82 *ret = u;
83 return 0;
86 /* Called for each key=value passed on the command line. */
87 static int
88 truncate_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
89 const char *key, const char *value)
91 if (strcmp (key, "truncate") == 0) {
92 truncate_size = nbdkit_parse_size (value);
93 if (truncate_size == -1)
94 return -1;
95 return 0;
97 else if (strcmp (key, "round-up") == 0) {
98 return parse_round_param (key, value, &round_up);
100 else if (strcmp (key, "round-down") == 0) {
101 return parse_round_param (key, value, &round_down);
103 else
104 return next (nxdata, key, value);
107 #define truncate_config_help \
108 "truncate=<SIZE> The new size.\n" \
109 "round-up=<N> Round up to next multiple of N.\n" \
110 "round-down=<N> Round down to multiple of N."
112 /* Per-connection state. Until the NBD protocol gains dynamic resize
113 * support, each connection remembers the size of the underlying
114 * plugin at open (even if that size differs between connections
115 * because the plugin tracks external resize effects).
117 struct handle {
118 /* The real size of the underlying plugin. */
119 uint64_t real_size;
121 /* The calculated size after applying the parameters. */
122 uint64_t size;
125 /* Open a connection. */
126 static void *
127 truncate_open (nbdkit_next_open *next, nbdkit_context *nxdata,
128 int readonly, const char *exportname, int is_tls)
130 struct handle *h;
132 if (next (nxdata, readonly, exportname) == -1)
133 return NULL;
135 h = malloc (sizeof *h); /* h is populated during .prepare */
136 if (h == NULL) {
137 nbdkit_error ("malloc: %m");
138 return NULL;
141 return h;
144 static void
145 truncate_close (void *handle)
147 struct handle *h = handle;
149 free (h);
152 /* In prepare, force a call to next->get_size in order to set
153 * per-connection real_size & size; these values are not changed
154 * during the life of the connection.
156 static int
157 truncate_prepare (nbdkit_next *next,
158 void *handle, int readonly)
160 int64_t r;
161 struct handle *h = handle;
163 r = next->get_size (next);
164 if (r == -1)
165 return -1;
167 h->real_size = h->size = r;
169 /* The truncate, round-up and round-down parameters are treated as
170 * separate operations. It's possible to specify more than one,
171 * although perhaps not very useful.
173 if (truncate_size >= 0)
174 h->size = truncate_size;
175 if (round_up > 0) {
176 if (ROUND_UP (h->size, round_up) > INT64_MAX) {
177 nbdkit_error ("cannot round size %" PRId64 " up to next boundary of %u",
178 h->size, round_up);
179 return -1;
181 h->size = ROUND_UP (h->size, round_up);
183 if (round_down > 0)
184 h->size = ROUND_DOWN (h->size, round_down);
186 return r >= 0 ? 0 : -1;
189 /* Get the size. */
190 static int64_t
191 truncate_get_size (nbdkit_next *next,
192 void *handle)
194 struct handle *h = handle;
196 /* If the NBD protocol and nbdkit adds dynamic resize, we'll need a
197 * rwlock where get_size holds write lock and all other ops hold
198 * read lock. Until then, NBD sizes are unchanging (even if the
199 * underlying plugin can react to external size changes), so just
200 * returned what we cached at connection open.
202 return h->size;
205 /* Advertise extents support. */
206 static int
207 truncate_can_extents (nbdkit_next *next,
208 void *handle)
210 /* Advertise unconditional support for the image tail, but also call
211 * into next to ensure next->extents doesn't fail later.
213 int r = next->can_extents (next);
214 if (r == -1)
215 return -1;
216 return 1;
219 /* Override the plugin's .can_fast_zero, because zeroing a tail is fast. */
220 static int
221 truncate_can_fast_zero (nbdkit_next *next,
222 void *handle)
224 /* Cache next->can_fast_zero now, so later calls don't fail,
225 * even though we override the answer here.
227 int r = next->can_fast_zero (next);
228 if (r == -1)
229 return -1;
230 return 1;
233 /* Read data. */
234 static int
235 truncate_pread (nbdkit_next *next,
236 void *handle, void *buf, uint32_t count, uint64_t offset,
237 uint32_t flags, int *err)
239 int r;
240 uint32_t n;
241 struct handle *h = handle;
243 if (offset < h->real_size) {
244 if (offset + count <= h->real_size)
245 n = count;
246 else
247 n = h->real_size - offset;
248 r = next->pread (next, buf, n, offset, flags, err);
249 if (r == -1)
250 return -1;
251 count -= n;
252 buf += n;
255 if (count > 0)
256 memset (buf, 0, count);
258 return 0;
261 /* Write data. */
262 static int
263 truncate_pwrite (nbdkit_next *next,
264 void *handle,
265 const void *buf, uint32_t count, uint64_t offset,
266 uint32_t flags, int *err)
268 int r;
269 uint32_t n;
270 struct handle *h = handle;
272 if (offset < h->real_size) {
273 if (offset + count <= h->real_size)
274 n = count;
275 else
276 n = h->real_size - offset;
277 r = next->pwrite (next, buf, n, offset, flags, err);
278 if (r == -1)
279 return -1;
280 count -= n;
281 buf += n;
284 if (count > 0) {
285 /* The caller must be writing zeroes, else it's an error. */
286 if (!is_zero (buf, count)) {
287 nbdkit_error ("truncate: write beyond end of underlying device");
288 *err = ENOSPC;
289 return -1;
293 return 0;
296 /* Trim data. */
297 static int
298 truncate_trim (nbdkit_next *next,
299 void *handle, uint32_t count, uint64_t offset,
300 uint32_t flags, int *err)
302 uint32_t n;
303 struct handle *h = handle;
305 if (offset < h->real_size) {
306 if (offset + count <= h->real_size)
307 n = count;
308 else
309 n = h->real_size - offset;
310 return next->trim (next, n, offset, flags, err);
312 return 0;
315 /* Zero data. */
316 static int
317 truncate_zero (nbdkit_next *next,
318 void *handle, uint32_t count, uint64_t offset,
319 uint32_t flags, int *err)
321 uint32_t n;
322 struct handle *h = handle;
324 if (offset < h->real_size) {
325 if (offset + count <= h->real_size)
326 n = count;
327 else
328 n = h->real_size - offset;
329 if (flags & NBDKIT_FLAG_FAST_ZERO &&
330 next->can_fast_zero (next) == 0) {
331 *err = ENOTSUP;
332 return -1;
334 return next->zero (next, n, offset, flags, err);
336 return 0;
339 /* Extents. */
340 static int
341 truncate_extents (nbdkit_next *next,
342 void *handle, uint32_t count, uint64_t offset,
343 uint32_t flags, struct nbdkit_extents *extents, int *err)
345 uint32_t n;
346 struct handle *h = handle;
347 CLEANUP_EXTENTS_FREE struct nbdkit_extents *extents2 = NULL;
348 size_t i;
350 /* If the entire request is beyond the end of the underlying plugin
351 * then this is the easy case: return a hole up to the end of the
352 * file.
354 if (offset >= h->real_size) {
355 int r = nbdkit_add_extent (extents,
356 h->real_size, truncate_size - h->real_size,
357 NBDKIT_EXTENT_ZERO|NBDKIT_EXTENT_HOLE);
358 if (r == -1)
359 *err = errno;
360 return r;
363 /* We're asked first for extents information about the plugin, then
364 * possibly (if truncating larger) for the hole after the plugin.
365 * Since we're not required to provide all of this information, the
366 * easiest thing is to only return data from the plugin. We will be
367 * called later about the hole. However we do need to make sure
368 * that the extents array is truncated to the real size, hence we
369 * have to create a new extents array, ask the plugin, then copy the
370 * returned data to the original array.
372 extents2 = nbdkit_extents_new (offset, h->real_size);
373 if (extents2 == NULL) {
374 *err = errno;
375 return -1;
377 if (offset + count <= h->real_size)
378 n = count;
379 else
380 n = h->real_size - offset;
381 if (next->extents (next, n, offset, flags, extents2, err) == -1)
382 return -1;
384 for (i = 0; i < nbdkit_extents_count (extents2); ++i) {
385 struct nbdkit_extent e = nbdkit_get_extent (extents2, i);
387 if (nbdkit_add_extent (extents, e.offset, e.length, e.type) == -1) {
388 *err = errno;
389 return -1;
393 return 0;
396 /* Cache. */
397 static int
398 truncate_cache (nbdkit_next *next,
399 void *handle, uint32_t count, uint64_t offset,
400 uint32_t flags, int *err)
402 int r;
403 uint32_t n;
404 struct handle *h = handle;
406 if (offset < h->real_size) {
407 if (offset + count <= h->real_size)
408 n = count;
409 else
410 n = h->real_size - offset;
411 r = next->cache (next, n, offset, flags, err);
412 if (r == -1)
413 return -1;
416 return 0;
419 static struct nbdkit_filter filter = {
420 .name = "truncate",
421 .longname = "nbdkit truncate filter",
422 .config = truncate_config,
423 .config_help = truncate_config_help,
424 .can_extents = truncate_can_extents,
425 .open = truncate_open,
426 .close = truncate_close,
427 .prepare = truncate_prepare,
428 .get_size = truncate_get_size,
429 .can_fast_zero = truncate_can_fast_zero,
430 .pread = truncate_pread,
431 .pwrite = truncate_pwrite,
432 .trim = truncate_trim,
433 .zero = truncate_zero,
434 .extents = truncate_extents,
435 .cache = truncate_cache,
438 NBDKIT_REGISTER_FILTER (filter)