Update Red Hat Copyright Notices
[nbdkit.git] / filters / xz / xz.c
blob0c92cfff482b617ccadaab1e7e04a847a32c83bb
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 <stdarg.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <errno.h>
44 #include <lzma.h>
46 #include <nbdkit-filter.h>
48 #include "xzfile.h"
49 #include "blkcache.h"
50 #include "cleanup.h"
52 static uint64_t maxblock = 512 * 1024 * 1024;
53 static uint32_t maxdepth = 8;
55 static int
56 xz_config (nbdkit_next_config *next, nbdkit_backend *nxdata,
57 const char *key, const char *value)
59 if (strcmp (key, "xz-max-block") == 0) {
60 int64_t r = nbdkit_parse_size (value);
61 if (r == -1)
62 return -1;
63 maxblock = (uint64_t) r;
64 return 0;
66 else if (strcmp (key, "xz-max-depth") == 0) {
67 if (nbdkit_parse_uint32_t ("xz-max-depth", value, &maxdepth) == -1)
68 return -1;
69 if (maxdepth == 0) {
70 nbdkit_error ("'xz-max-depth' parameter must be >= 1");
71 return -1;
73 return 0;
75 else
76 return next (nxdata, key, value);
79 #define xz_config_help \
80 "xz-max-block=<SIZE> (optional) Maximum block size allowed (default: 512M)\n"\
81 "xz-max-depth=<N> (optional) Maximum blocks in cache (default: 4)\n"
83 /* The per-connection handle. */
84 struct xz_handle {
85 xzfile *xz;
87 /* Block cache. */
88 blkcache *c;
91 /* Create the per-connection handle. */
92 static void *
93 xz_open (nbdkit_next_open *next, nbdkit_context *nxdata,
94 int readonly, const char *exportname, int is_tls)
96 struct xz_handle *h;
98 /* Always pass readonly=1 to the underlying plugin. */
99 if (next (nxdata, 1, exportname) == -1)
100 return NULL;
102 h = malloc (sizeof *h);
103 if (h == NULL) {
104 nbdkit_error ("malloc: %m");
105 return NULL;
108 h->c = new_blkcache (maxdepth);
109 if (!h->c) {
110 free (h);
111 return NULL;
114 /* Initialized in xz_prepare. */
115 h->xz = NULL;
117 return h;
120 /* Free up the per-connection handle. */
121 static void
122 xz_close (void *handle)
124 struct xz_handle *h = handle;
125 blkcache_stats stats;
127 blkcache_get_stats (h->c, &stats);
128 nbdkit_debug ("cache: hits = %zu, misses = %zu", stats.hits, stats.misses);
130 xzfile_close (h->xz);
131 free_blkcache (h->c);
132 free (h);
135 static int
136 xz_prepare (nbdkit_next *next, void *handle,
137 int readonly)
139 struct xz_handle *h = handle;
141 h->xz = xzfile_open (next);
142 if (!h->xz)
143 return -1;
145 if (maxblock < xzfile_max_uncompressed_block_size (h->xz)) {
146 nbdkit_error ("xz file largest block is bigger than maxblock\n"
147 "Either recompress the xz file with smaller blocks "
148 "(see nbdkit-xz-filter(1))\n"
149 "or make maxblock parameter bigger.\n"
150 "maxblock = %" PRIu64 " (bytes)\n"
151 "largest block in xz file = %" PRIu64 " (bytes)",
152 maxblock,
153 xzfile_max_uncompressed_block_size (h->xz));
154 return -1;
157 return 0;
160 /* Description. */
161 static const char *
162 xz_export_description (nbdkit_next *next,
163 void *handle)
165 const char *base = next->export_description (next);
167 if (!base)
168 return NULL;
169 return nbdkit_printf_intern ("expansion of xz-compressed image: %s", base);
172 /* Get the file size. */
173 static int64_t
174 xz_get_size (nbdkit_next *next, void *handle)
176 struct xz_handle *h = handle;
178 return xzfile_get_size (h->xz);
181 /* We need this because otherwise the layer below can_write is called
182 * and that might return true (eg. if the plugin has a pwrite method
183 * at all), resulting in writes being passed through to the layer
184 * below. This is possibly a bug in nbdkit.
186 static int
187 xz_can_write (nbdkit_next *next,
188 void *handle)
190 return 0;
193 /* Whatever the plugin says, this filter is consistent across connections. */
194 static int
195 xz_can_multi_conn (nbdkit_next *next,
196 void *handle)
198 return 1;
201 /* Similar to above. However xz files themselves do support
202 * sparseness so in future we should generate extents information. XXX
204 static int
205 xz_can_extents (nbdkit_next *next,
206 void *handle)
208 return 0;
211 /* Cache */
212 static int
213 xz_can_cache (nbdkit_next *next,
214 void *handle)
216 /* We are already operating as a cache regardless of the plugin's
217 * underlying .can_cache, but it's easiest to just rely on nbdkit's
218 * behavior of calling .pread for caching.
220 return NBDKIT_CACHE_EMULATE;
223 /* Read data from the file. */
224 static int
225 xz_pread (nbdkit_next *next,
226 void *handle, void *buf, uint32_t count, uint64_t offset,
227 uint32_t flags, int *err)
229 struct xz_handle *h = handle;
230 char *data;
231 uint64_t start, size;
232 uint32_t n;
234 /* Find the block in the cache. */
235 data = get_block (h->c, offset, &start, &size);
236 if (!data) {
237 /* Not in the cache. We need to read the block from the xz file. */
238 data = xzfile_read_block (h->xz, next, flags, err,
239 offset, &start, &size);
240 if (data == NULL)
241 return -1;
242 put_block (h->c, start, size, data);
245 /* It's possible if the blocks are really small or oddly aligned or
246 * if the requests are large that we need to read the following
247 * block to satisfy the request.
249 n = count;
250 if (start + size - offset < n)
251 n = start + size - offset;
253 memcpy (buf, &data[offset-start], n);
254 buf += n;
255 count -= n;
256 offset += n;
257 if (count > 0)
258 return xz_pread (next, h, buf, count, offset, flags, err);
260 return 0;
263 static int xz_thread_model (void)
265 return NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS;
268 static struct nbdkit_filter filter = {
269 .name = "xz",
270 .longname = "nbdkit XZ filter",
271 .config = xz_config,
272 .config_help = xz_config_help,
273 .thread_model = xz_thread_model,
274 .open = xz_open,
275 .close = xz_close,
276 .prepare = xz_prepare,
277 .export_description = xz_export_description,
278 .get_size = xz_get_size,
279 .can_write = xz_can_write,
280 .can_extents = xz_can_extents,
281 .can_cache = xz_can_cache,
282 .can_multi_conn = xz_can_multi_conn,
283 .pread = xz_pread,
286 NBDKIT_REGISTER_FILTER (filter)