2 * Copyright (C) 2013 Red Hat Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
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
47 #include <nbdkit-plugin.h>
52 static char *filename
= NULL
;
53 static uint64_t maxblock
= 512 * 1024 * 1024;
54 static size_t maxdepth
= 8;
62 /* Called for each key=value passed on the command line. This plugin
63 * only accepts file=<filename>, which is required.
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
);
74 else if (strcmp (key
, "maxblock") == 0) {
75 int64_t r
= nbdkit_parse_size (value
);
78 maxblock
= (uint64_t) r
;
80 else if (strcmp (key
, "maxdepth") == 0) {
83 if (sscanf (value
, "%zu", &r
) != 1) {
84 nbdkit_error ("could not parse 'maxdepth' parameter");
88 nbdkit_error ("'maxdepth' parameter must be >= 1");
95 nbdkit_error ("unknown parameter '%s'", key
);
102 /* Check the user did pass a file=<FILENAME> parameter. */
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");
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, ...) \
123 const char *gzerr = gzerror ((gz), &gzerrnum); \
124 if (gzerrnum == Z_ERRNO) { \
125 nbdkit_error ((fs ": %m"), ## __VA_ARGS__); \
127 nbdkit_error ((fs ": %s"), ## __VA_ARGS__, gzerr); \
131 /* The per-connection handle. */
139 /* Create the per-connection handle. */
141 xz_open (int readonly
)
145 h
= malloc (sizeof *h
);
147 nbdkit_error ("malloc: %m");
151 h
->c
= new_blkcache (maxdepth
);
155 h
->xz
= xzfile_open (filename
);
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)",
167 xzfile_max_uncompressed_block_size (h
->xz
));
174 xzfile_close (h
->xz
);
176 free_blkcache (h
->c
);
182 /* Free up the per-connection handle. */
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
);
199 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_REQUESTS
201 /* Get the file size. */
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. */
212 xz_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
)
214 struct xz_handle
*h
= handle
;
216 uint64_t start
, size
;
219 /* Find the block in the cache. */
220 data
= get_block (h
->c
, offset
, &start
, &size
);
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
);
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.
234 if (start
+ size
- offset
< n
)
235 n
= start
+ size
- offset
;
237 memcpy (buf
, &data
[offset
-start
], n
);
242 return xz_pread (h
, buf
, count
, offset
);
247 static struct nbdkit_plugin plugin
= {
249 .version
= PACKAGE_VERSION
,
252 .config_complete
= xz_config_complete
,
253 .config_help
= xz_config_help
,
256 .get_size
= xz_get_size
,
260 NBDKIT_REGISTER_PLUGIN(plugin
)