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
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
45 #include <nbdkit-filter.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
;
61 if (blk_init () == -1)
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)
78 return NBDKIT_HANDLE_NOT_NEEDED
;
81 /* Get the file size and ensure the overlay is the correct size. */
83 cow_get_size (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
89 size
= next_ops
->get_size (nxdata
);
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
);
104 /* Force an early call to cow_get_size, consequently truncating the
105 * overlay to the correct size.
108 cow_prepare (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
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.
121 cow_can_write (struct nbdkit_next_ops
*next_ops
, void *nxdata
, void *handle
)
127 cow_can_trim (struct nbdkit_next_ops
*next_ops
, void *nxdata
, void *handle
)
133 cow_can_extents (struct nbdkit_next_ops
*next_ops
, void *nxdata
, void *handle
)
139 cow_can_flush (struct nbdkit_next_ops
*next_ops
, void *nxdata
, void *handle
)
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
);
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
);
163 nbdkit_error ("malloc: %m");
168 uint64_t blknum
, blkoffs
, n
;
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 */
177 pthread_mutex_lock (&lock
);
178 r
= blk_read (next_ops
, nxdata
, blknum
, block
, err
);
179 pthread_mutex_unlock (&lock
);
183 memcpy (buf
, &block
[blkoffs
], n
);
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
);
204 nbdkit_error ("malloc: %m");
209 uint64_t blknum
, blkoffs
, n
;
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 */
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
);
224 memcpy (&block
[blkoffs
], buf
, n
);
225 r
= blk_write (blknum
, block
, err
);
227 pthread_mutex_unlock (&lock
);
236 if (flags
& NBDKIT_FLAG_FUA
)
237 return cow_flush (next_ops
, nxdata
, handle
, 0, err
);
243 cow_zero (struct nbdkit_next_ops
*next_ops
, void *nxdata
,
244 void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
247 CLEANUP_FREE
uint8_t *block
= NULL
;
249 block
= malloc (BLKSIZE
);
252 nbdkit_error ("malloc: %m");
257 uint64_t blknum
, blkoffs
, n
;
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 */
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
);
275 memset (&block
[blkoffs
], 0, n
);
276 r
= blk_write (blknum
, block
, err
);
278 pthread_mutex_unlock (&lock
);
286 if (flags
& NBDKIT_FLAG_FUA
)
287 return cow_flush (next_ops
, nxdata
, handle
, 0, err
);
292 cow_flush (struct nbdkit_next_ops
*next_ops
, void *nxdata
, void *handle
,
293 uint32_t flags
, int *err
)
297 pthread_mutex_lock (&lock
);
301 pthread_mutex_unlock (&lock
);
305 static struct nbdkit_filter filter
= {
307 .longname
= "nbdkit copy-on-write (COW) filter",
308 .version
= PACKAGE_VERSION
,
310 .unload
= cow_unload
,
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
,
320 .pwrite
= cow_pwrite
,
325 NBDKIT_REGISTER_FILTER(filter
)