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
43 #include <nbdkit-filter.h>
46 #include "ispowerof2.h"
50 /* These are the parameters. */
51 static int64_t truncate_size
= -1;
52 static unsigned round_up
= 0, round_down
= 0;
55 parse_round_param (const char *key
, const char *value
, unsigned *ret
)
60 /* Parse it as a "size" quantity so we allow round-up=1M and similar. */
61 r
= nbdkit_parse_size (value
);
65 /* Must not be zero or larger than an unsigned int. */
67 nbdkit_error ("if set, the %s parameter must be > 0", key
);
71 nbdkit_error ("the %s parameter is too large", key
);
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
);
86 /* Called for each key=value passed on the command line. */
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)
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
);
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).
118 /* The real size of the underlying plugin. */
121 /* The calculated size after applying the parameters. */
125 /* Open a connection. */
127 truncate_open (nbdkit_next_open
*next
, nbdkit_context
*nxdata
,
128 int readonly
, const char *exportname
, int is_tls
)
132 if (next (nxdata
, readonly
, exportname
) == -1)
135 h
= malloc (sizeof *h
); /* h is populated during .prepare */
137 nbdkit_error ("malloc: %m");
145 truncate_close (void *handle
)
147 struct handle
*h
= handle
;
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.
157 truncate_prepare (nbdkit_next
*next
,
158 void *handle
, int readonly
)
161 struct handle
*h
= handle
;
163 r
= next
->get_size (next
);
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
;
176 if (ROUND_UP (h
->size
, round_up
) > INT64_MAX
) {
177 nbdkit_error ("cannot round size %" PRId64
" up to next boundary of %u",
181 h
->size
= ROUND_UP (h
->size
, round_up
);
184 h
->size
= ROUND_DOWN (h
->size
, round_down
);
186 return r
>= 0 ? 0 : -1;
191 truncate_get_size (nbdkit_next
*next
,
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.
205 /* Advertise extents support. */
207 truncate_can_extents (nbdkit_next
*next
,
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
);
219 /* Override the plugin's .can_fast_zero, because zeroing a tail is fast. */
221 truncate_can_fast_zero (nbdkit_next
*next
,
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
);
235 truncate_pread (nbdkit_next
*next
,
236 void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
237 uint32_t flags
, int *err
)
241 struct handle
*h
= handle
;
243 if (offset
< h
->real_size
) {
244 if (offset
+ count
<= h
->real_size
)
247 n
= h
->real_size
- offset
;
248 r
= next
->pread (next
, buf
, n
, offset
, flags
, err
);
256 memset (buf
, 0, count
);
263 truncate_pwrite (nbdkit_next
*next
,
265 const void *buf
, uint32_t count
, uint64_t offset
,
266 uint32_t flags
, int *err
)
270 struct handle
*h
= handle
;
272 if (offset
< h
->real_size
) {
273 if (offset
+ count
<= h
->real_size
)
276 n
= h
->real_size
- offset
;
277 r
= next
->pwrite (next
, buf
, n
, offset
, flags
, err
);
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");
298 truncate_trim (nbdkit_next
*next
,
299 void *handle
, uint32_t count
, uint64_t offset
,
300 uint32_t flags
, int *err
)
303 struct handle
*h
= handle
;
305 if (offset
< h
->real_size
) {
306 if (offset
+ count
<= h
->real_size
)
309 n
= h
->real_size
- offset
;
310 return next
->trim (next
, n
, offset
, flags
, err
);
317 truncate_zero (nbdkit_next
*next
,
318 void *handle
, uint32_t count
, uint64_t offset
,
319 uint32_t flags
, int *err
)
322 struct handle
*h
= handle
;
324 if (offset
< h
->real_size
) {
325 if (offset
+ count
<= h
->real_size
)
328 n
= h
->real_size
- offset
;
329 if (flags
& NBDKIT_FLAG_FAST_ZERO
&&
330 next
->can_fast_zero (next
) == 0) {
334 return next
->zero (next
, n
, offset
, flags
, err
);
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
)
346 struct handle
*h
= handle
;
347 CLEANUP_EXTENTS_FREE
struct nbdkit_extents
*extents2
= NULL
;
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
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
);
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
) {
377 if (offset
+ count
<= h
->real_size
)
380 n
= h
->real_size
- offset
;
381 if (next
->extents (next
, n
, offset
, flags
, extents2
, err
) == -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) {
398 truncate_cache (nbdkit_next
*next
,
399 void *handle
, uint32_t count
, uint64_t offset
,
400 uint32_t flags
, int *err
)
404 struct handle
*h
= handle
;
406 if (offset
< h
->real_size
) {
407 if (offset
+ count
<= h
->real_size
)
410 n
= h
->real_size
- offset
;
411 r
= next
->cache (next
, n
, offset
, flags
, err
);
419 static struct nbdkit_filter filter
= {
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
)