Add libguestfs plugin.
[nbdkit/ericb.git] / plugins / xz / xz.c
blob9a1f5e5f11d170a12c7dc2df8a1e0ae3e5d85f5a
1 /* nbdkit
2 * Copyright (C) 2013 Red Hat Inc.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of Red Hat nor the names of its contributors may be
17 * used to endorse or promote products derived from this software without
18 * specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
24 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
27 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
28 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
30 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
34 #include <config.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdarg.h>
39 #include <stdint.h>
40 #include <inttypes.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <errno.h>
45 #include <lzma.h>
47 #include <nbdkit-plugin.h>
49 #include "xzfile.h"
50 #include "blkcache.h"
52 static char *filename = NULL;
53 static uint64_t maxblock = 512 * 1024 * 1024;
54 static size_t maxdepth = 8;
56 static void
57 xz_unload (void)
59 free (filename);
62 /* Called for each key=value passed on the command line. This plugin
63 * only accepts file=<filename>, which is required.
65 static int
66 xz_config (const char *key, const char *value)
68 if (strcmp (key, "file") == 0) {
69 /* See FILENAMES AND PATHS in nbdkit-plugin(3). */
70 filename = nbdkit_absolute_path (value);
71 if (!filename)
72 return -1;
74 else if (strcmp (key, "maxblock") == 0) {
75 int64_t r = nbdkit_parse_size (value);
76 if (r == -1)
77 return -1;
78 maxblock = (uint64_t) r;
80 else if (strcmp (key, "maxdepth") == 0) {
81 size_t r;
83 if (sscanf (value, "%zu", &r) != 1) {
84 nbdkit_error ("could not parse 'maxdepth' parameter");
85 return -1;
87 if (r == 0) {
88 nbdkit_error ("'maxdepth' parameter must be >= 1");
89 return -1;
92 maxdepth = r;
94 else {
95 nbdkit_error ("unknown parameter '%s'", key);
96 return -1;
99 return 0;
102 /* Check the user did pass a file=<FILENAME> parameter. */
103 static int
104 xz_config_complete (void)
106 if (filename == NULL) {
107 nbdkit_error ("you must supply the file=<FILENAME> parameter after the plugin name on the command line");
108 return -1;
111 return 0;
114 #define xz_config_help \
115 "file=<FILENAME> (required) The filename to serve.\n" \
116 "maxblock=<SIZE> (optional) Maximum block size allowed (default: 512M)\n"\
117 "maxdepth=<N> (optional) Maximum blocks in cache (default: 4)\n"
119 /* Translate a gzerror to nbdkit_error. */
120 #define nbdkit_gzerror(gz, fs, ...) \
121 do { \
122 int gzerrnum; \
123 const char *gzerr = gzerror ((gz), &gzerrnum); \
124 if (gzerrnum == Z_ERRNO) { \
125 nbdkit_error ((fs ": %m"), ## __VA_ARGS__); \
126 } else { \
127 nbdkit_error ((fs ": %s"), ## __VA_ARGS__, gzerr); \
129 } while (0)
131 /* The per-connection handle. */
132 struct xz_handle {
133 xzfile *xz;
135 /* Block cache. */
136 blkcache *c;
139 /* Create the per-connection handle. */
140 static void *
141 xz_open (int readonly)
143 struct xz_handle *h;
145 h = malloc (sizeof *h);
146 if (h == NULL) {
147 nbdkit_error ("malloc: %m");
148 return NULL;
151 h->c = new_blkcache (maxdepth);
152 if (h->c == NULL)
153 goto err1;
155 h->xz = xzfile_open (filename);
156 if (!h->xz)
157 goto err2;
159 if (maxblock < xzfile_max_uncompressed_block_size (h->xz)) {
160 nbdkit_error ("%s: xz file largest block is bigger than maxblock\n"
161 "Either recompress the xz file with smaller blocks (see nbdkit-xz-plugin(1))\n"
162 "or make maxblock parameter bigger.\n"
163 "maxblock = %" PRIu64 " (bytes)\n"
164 "largest block in xz file = %" PRIu64 " (bytes)",
165 filename,
166 maxblock,
167 xzfile_max_uncompressed_block_size (h->xz));
168 goto err3;
171 return h;
173 err3:
174 xzfile_close (h->xz);
175 err2:
176 free_blkcache (h->c);
177 err1:
178 free (h);
179 return NULL;
182 /* Free up the per-connection handle. */
183 static void
184 xz_close (void *handle)
186 struct xz_handle *h = handle;
187 blkcache_stats stats;
189 blkcache_get_stats (h->c, &stats);
191 nbdkit_debug ("cache: hits = %" PRIu64 ", misses = %" PRIu64,
192 stats.hits, stats.misses);
194 xzfile_close (h->xz);
195 free_blkcache (h->c);
196 free (h);
199 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
201 /* Get the file size. */
202 static int64_t
203 xz_get_size (void *handle)
205 struct xz_handle *h = handle;
207 return xzfile_get_size (h->xz);
210 /* Read data from the file. */
211 static int
212 xz_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
214 struct xz_handle *h = handle;
215 char *data;
216 uint64_t start, size;
217 uint32_t n;
219 /* Find the block in the cache. */
220 data = get_block (h->c, offset, &start, &size);
221 if (!data) {
222 /* Not in the cache. We need to read the block from the xz file. */
223 data = xzfile_read_block (h->xz, offset, &start, &size);
224 if (data == NULL)
225 return -1;
226 put_block (h->c, start, size, data);
229 /* It's possible if the blocks are really small or oddly aligned or
230 * if the requests are large that we need to read the following
231 * block to satisfy the request.
233 n = count;
234 if (start + size - offset < n)
235 n = start + size - offset;
237 memcpy (buf, &data[offset-start], n);
238 buf += n;
239 count -= n;
240 offset += n;
241 if (count > 0)
242 return xz_pread (h, buf, count, offset);
244 return 0;
247 static struct nbdkit_plugin plugin = {
248 .name = "xz",
249 .version = PACKAGE_VERSION,
250 .unload = xz_unload,
251 .config = xz_config,
252 .config_complete = xz_config_complete,
253 .config_help = xz_config_help,
254 .open = xz_open,
255 .close = xz_close,
256 .get_size = xz_get_size,
257 .pread = xz_pread,
260 NBDKIT_REGISTER_PLUGIN(plugin)