filters: Use CLEANUP_FREE macro in a few places.
[nbdkit/ericb.git] / filters / cow / cow.c
blob04ec44f94b32f7bb84de78395800b7946eb4c7a2
1 /* nbdkit
2 * Copyright (C) 2018 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
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 <stdbool.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <errno.h>
43 #include <pthread.h>
45 #include <nbdkit-filter.h>
47 #include "cleanup.h"
49 #include "blk.h"
51 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
53 /* In order to handle parallel requests safely, this lock must be held
54 * when calling any blk_* functions.
56 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
58 static void
59 cow_load (void)
61 if (blk_init () == -1)
62 exit (EXIT_FAILURE);
65 static void
66 cow_unload (void)
68 blk_free ();
71 static void *
72 cow_open (nbdkit_next_open *next, void *nxdata, int readonly)
74 /* Always pass readonly=1 to the underlying plugin. */
75 if (next (nxdata, 1) == -1)
76 return NULL;
78 return NBDKIT_HANDLE_NOT_NEEDED;
81 /* Get the file size and ensure the overlay is the correct size. */
82 static int64_t
83 cow_get_size (struct nbdkit_next_ops *next_ops, void *nxdata,
84 void *handle)
86 int64_t size;
87 int r;
89 size = next_ops->get_size (nxdata);
90 if (size == -1)
91 return -1;
93 nbdkit_debug ("cow: underlying file size: %" PRIi64, size);
95 pthread_mutex_lock (&lock);
96 r = blk_set_size (size);
97 pthread_mutex_unlock (&lock);
98 if (r == -1)
99 return -1;
101 return size;
104 /* Force an early call to cow_get_size, consequently truncating the
105 * overlay to the correct size.
107 static int
108 cow_prepare (struct nbdkit_next_ops *next_ops, void *nxdata,
109 void *handle)
111 int64_t r;
113 r = cow_get_size (next_ops, nxdata, handle);
114 return r >= 0 ? 0 : -1;
117 /* Whatever the underlying plugin can or can't do, we can write, we
118 * cannot trim or detect extents, and we can flush.
120 static int
121 cow_can_write (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
123 return 1;
126 static int
127 cow_can_trim (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
129 return 0;
132 static int
133 cow_can_extents (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
135 return 0;
138 static int
139 cow_can_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
141 return 1;
144 static int
145 cow_can_fua (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle)
147 return NBDKIT_FUA_EMULATE;
150 static int cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle, uint32_t flags, int *err);
152 /* Read data. */
153 static int
154 cow_pread (struct nbdkit_next_ops *next_ops, void *nxdata,
155 void *handle, void *buf, uint32_t count, uint64_t offset,
156 uint32_t flags, int *err)
158 CLEANUP_FREE uint8_t *block = NULL;
160 block = malloc (BLKSIZE);
161 if (block == NULL) {
162 *err = errno;
163 nbdkit_error ("malloc: %m");
164 return -1;
167 while (count > 0) {
168 uint64_t blknum, blkoffs, n;
169 int r;
171 blknum = offset / BLKSIZE; /* block number */
172 blkoffs = offset % BLKSIZE; /* offset within the block */
173 n = BLKSIZE - blkoffs; /* max bytes we can read from this block */
174 if (n > count)
175 n = count;
177 pthread_mutex_lock (&lock);
178 r = blk_read (next_ops, nxdata, blknum, block, err);
179 pthread_mutex_unlock (&lock);
180 if (r == -1)
181 return -1;
183 memcpy (buf, &block[blkoffs], n);
185 buf += n;
186 count -= n;
187 offset += n;
190 return 0;
193 /* Write data. */
194 static int
195 cow_pwrite (struct nbdkit_next_ops *next_ops, void *nxdata,
196 void *handle, const void *buf, uint32_t count, uint64_t offset,
197 uint32_t flags, int *err)
199 CLEANUP_FREE uint8_t *block = NULL;
201 block = malloc (BLKSIZE);
202 if (block == NULL) {
203 *err = errno;
204 nbdkit_error ("malloc: %m");
205 return -1;
208 while (count > 0) {
209 uint64_t blknum, blkoffs, n;
210 int r;
212 blknum = offset / BLKSIZE; /* block number */
213 blkoffs = offset % BLKSIZE; /* offset within the block */
214 n = BLKSIZE - blkoffs; /* max bytes we can read from this block */
215 if (n > count)
216 n = count;
218 /* Do a read-modify-write operation on the current block.
219 * Hold the lock over the whole operation.
221 pthread_mutex_lock (&lock);
222 r = blk_read (next_ops, nxdata, blknum, block, err);
223 if (r != -1) {
224 memcpy (&block[blkoffs], buf, n);
225 r = blk_write (blknum, block, err);
227 pthread_mutex_unlock (&lock);
228 if (r == -1)
229 return -1;
231 buf += n;
232 count -= n;
233 offset += n;
236 if (flags & NBDKIT_FLAG_FUA)
237 return cow_flush (next_ops, nxdata, handle, 0, err);
238 return 0;
241 /* Zero data. */
242 static int
243 cow_zero (struct nbdkit_next_ops *next_ops, void *nxdata,
244 void *handle, uint32_t count, uint64_t offset, uint32_t flags,
245 int *err)
247 CLEANUP_FREE uint8_t *block = NULL;
249 block = malloc (BLKSIZE);
250 if (block == NULL) {
251 *err = errno;
252 nbdkit_error ("malloc: %m");
253 return -1;
256 while (count > 0) {
257 uint64_t blknum, blkoffs, n;
258 int r;
260 blknum = offset / BLKSIZE; /* block number */
261 blkoffs = offset % BLKSIZE; /* offset within the block */
262 n = BLKSIZE - blkoffs; /* max bytes we can read from this block */
263 if (n > count)
264 n = count;
266 /* Do a read-modify-write operation on the current block.
267 * Hold the lock over the whole operation.
269 * XXX There is the possibility of optimizing this: ONLY if we are
270 * writing a whole, aligned block, then use FALLOC_FL_ZERO_RANGE.
272 pthread_mutex_lock (&lock);
273 r = blk_read (next_ops, nxdata, blknum, block, err);
274 if (r != -1) {
275 memset (&block[blkoffs], 0, n);
276 r = blk_write (blknum, block, err);
278 pthread_mutex_unlock (&lock);
279 if (r == -1)
280 return -1;
282 count -= n;
283 offset += n;
286 if (flags & NBDKIT_FLAG_FUA)
287 return cow_flush (next_ops, nxdata, handle, 0, err);
288 return 0;
291 static int
292 cow_flush (struct nbdkit_next_ops *next_ops, void *nxdata, void *handle,
293 uint32_t flags, int *err)
295 int r;
297 pthread_mutex_lock (&lock);
298 r = blk_flush ();
299 if (r == -1)
300 *err = errno;
301 pthread_mutex_unlock (&lock);
302 return r;
305 static struct nbdkit_filter filter = {
306 .name = "cow",
307 .longname = "nbdkit copy-on-write (COW) filter",
308 .version = PACKAGE_VERSION,
309 .load = cow_load,
310 .unload = cow_unload,
311 .open = cow_open,
312 .prepare = cow_prepare,
313 .get_size = cow_get_size,
314 .can_write = cow_can_write,
315 .can_flush = cow_can_flush,
316 .can_trim = cow_can_trim,
317 .can_extents = cow_can_extents,
318 .can_fua = cow_can_fua,
319 .pread = cow_pread,
320 .pwrite = cow_pwrite,
321 .zero = cow_zero,
322 .flush = cow_flush,
325 NBDKIT_REGISTER_FILTER(filter)