2 * Copyright (C) 2018-2019 Red Hat Inc.
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
42 #include <nbdkit-filter.h>
45 #include "ispowerof2.h"
49 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
51 /* These are the parameters. */
52 static int64_t truncate_size
= -1;
53 static unsigned round_up
= 0, round_down
= 0;
56 parse_round_param (const char *key
, const char *value
, unsigned *ret
)
61 /* Parse it as a "size" quantity so we allow round-up=1M and similar. */
62 r
= nbdkit_parse_size (value
);
66 /* Must not be zero or larger than an unsigned int. */
68 nbdkit_error ("if set, the %s parameter must be > 0", key
);
72 nbdkit_error ("the %s parameter is too large", key
);
77 /* Must be a power of 2. We could relax this in future. */
78 if (!is_power_of_2 (u
)) {
79 nbdkit_error ("the %s parameter must be a power of 2", key
);
87 /* Called for each key=value passed on the command line. */
89 truncate_config (nbdkit_next_config
*next
, void *nxdata
,
90 const char *key
, const char *value
)
92 if (strcmp (key
, "truncate") == 0) {
93 truncate_size
= nbdkit_parse_size (value
);
94 if (truncate_size
== -1)
98 else if (strcmp (key
, "round-up") == 0) {
99 return parse_round_param (key
, value
, &round_up
);
101 else if (strcmp (key
, "round-down") == 0) {
102 return parse_round_param (key
, value
, &round_down
);
105 return next (nxdata
, key
, value
);
108 #define truncate_config_help \
109 "truncate=<SIZE> The new size.\n" \
110 "round-up=<N> Round up to next multiple of N.\n" \
111 "round-down=<N> Round down to multiple of N."
113 /* Per-connection state. Until the NBD protocol gains dynamic resize
114 * support, each connection remembers the size of the underlying
115 * plugin at open (even if that size differs between connections
116 * because the plugin tracks external resize effects).
119 /* The real size of the underlying plugin. */
122 /* The calculated size after applying the parameters. */
126 /* Open a connection. */
128 truncate_open (nbdkit_next_open
*next
, void *nxdata
, int readonly
)
132 if (next (nxdata
, readonly
) == -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_ops->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 (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
161 struct handle
*h
= handle
;
163 r
= next_ops
->get_size (nxdata
);
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 h
->size
= ROUND_UP (h
->size
, round_up
);
178 h
->size
= ROUND_DOWN (h
->size
, round_down
);
180 return r
>= 0 ? 0 : -1;
185 truncate_get_size (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
188 struct handle
*h
= handle
;
190 /* If the NBD protocol and nbdkit adds dynamic resize, we'll need a
191 * rwlock where get_size holds write lock and all other ops hold
192 * read lock. Until then, NBD sizes are unchanging (even if the
193 * underlying plugin can react to external size changes), so just
194 * returned what we cached at connection open.
201 truncate_pread (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
202 void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
203 uint32_t flags
, int *err
)
207 struct handle
*h
= handle
;
209 if (offset
< h
->real_size
) {
210 if (offset
+ count
<= h
->real_size
)
213 n
= h
->real_size
- offset
;
214 r
= next_ops
->pread (nxdata
, buf
, n
, offset
, flags
, err
);
222 memset (buf
, 0, count
);
229 truncate_pwrite (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
231 const void *buf
, uint32_t count
, uint64_t offset
,
232 uint32_t flags
, int *err
)
236 struct handle
*h
= handle
;
238 if (offset
< h
->real_size
) {
239 if (offset
+ count
<= h
->real_size
)
242 n
= h
->real_size
- offset
;
243 r
= next_ops
->pwrite (nxdata
, buf
, n
, offset
, flags
, err
);
251 /* The caller must be writing zeroes, else it's an error. */
252 if (!is_zero (buf
, count
)) {
253 nbdkit_error ("truncate: write beyond end of underlying device");
264 truncate_trim (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
265 void *handle
, uint32_t count
, uint64_t offset
,
266 uint32_t flags
, int *err
)
269 struct handle
*h
= handle
;
271 if (offset
< h
->real_size
) {
272 if (offset
+ count
<= h
->real_size
)
275 n
= h
->real_size
- offset
;
276 return next_ops
->trim (nxdata
, n
, offset
, flags
, err
);
283 truncate_zero (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
284 void *handle
, uint32_t count
, uint64_t offset
,
285 uint32_t flags
, int *err
)
288 struct handle
*h
= handle
;
290 if (offset
< h
->real_size
) {
291 if (offset
+ count
<= h
->real_size
)
294 n
= h
->real_size
- offset
;
295 return next_ops
->zero (nxdata
, n
, offset
, flags
, err
);
302 truncate_extents (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
303 void *handle
, uint32_t count
, uint64_t offset
,
304 uint32_t flags
, struct nbdkit_extents
*extents
, int *err
)
307 struct handle
*h
= handle
;
308 CLEANUP_EXTENTS_FREE
struct nbdkit_extents
*extents2
= NULL
;
311 /* If the entire request is beyond the end of the underlying plugin
312 * then this is the easy case: return a hole up to the end of the
315 if (offset
>= h
->real_size
) {
316 int r
= nbdkit_add_extent (extents
,
317 h
->real_size
, truncate_size
- h
->real_size
,
318 NBDKIT_EXTENT_ZERO
|NBDKIT_EXTENT_HOLE
);
324 /* We're asked first for extents information about the plugin, then
325 * possibly (if truncating larger) for the hole after the plugin.
326 * Since we're not required to provide all of this information, the
327 * easiest thing is to only return data from the plugin. We will be
328 * called later about the hole. However we do need to make sure
329 * that the extents array is truncated to the real size, hence we
330 * have to create a new extents array, ask the plugin, then copy the
331 * returned data to the original array.
333 extents2
= nbdkit_extents_new (offset
, h
->real_size
);
334 if (extents2
== NULL
) {
338 if (offset
+ count
<= h
->real_size
)
341 n
= h
->real_size
- offset
;
342 if (next_ops
->extents (nxdata
, n
, offset
, flags
, extents2
, err
) == -1)
345 for (i
= 0; i
< nbdkit_extents_count (extents2
); ++i
) {
346 struct nbdkit_extent e
= nbdkit_get_extent (extents2
, i
);
348 if (nbdkit_add_extent (extents
, e
.offset
, e
.length
, e
.type
) == -1) {
357 static struct nbdkit_filter filter
= {
359 .longname
= "nbdkit truncate filter",
360 .version
= PACKAGE_VERSION
,
361 .config
= truncate_config
,
362 .config_help
= truncate_config_help
,
363 .open
= truncate_open
,
364 .close
= truncate_close
,
365 .prepare
= truncate_prepare
,
366 .get_size
= truncate_get_size
,
367 .pread
= truncate_pread
,
368 .pwrite
= truncate_pwrite
,
369 .trim
= truncate_trim
,
370 .zero
= truncate_zero
,
371 .extents
= truncate_extents
,
374 NBDKIT_REGISTER_FILTER(filter
)