Update Red Hat Copyright Notices
[nbdkit.git] / plugins / cdi / cdi.c
blob72a678e6d6c6000510cad437245f4b6d0d539273
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 <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
43 #include <nbdkit-plugin.h>
45 #include "cleanup.h"
46 #include "utils.h"
48 /* Parameters. */
49 static const char *name; /* Name or URI of container image. */
50 static int layer = 0; /* Layer (may be negative to count from end). */
52 /* The script that we run to pull and unpack the image. */
53 static const char script[] =
54 /* Exit on errors. */
55 "set -e\n"
56 /* Avoid stdin/stdout leaking (because of nbdkit -s).
57 * XXX Capture errors to a temporary file.
59 "exec </dev/null >/dev/null\n"
60 /* Create a temporary directory to extract the image to. */
61 "d=\"$tmpfile.d\"\n"
62 "podman pull \"$name\"\n"
63 "podman save --format oci-dir -o \"$d\" \"$name\"\n"
64 "f=\"$d/$( jq -r \".layers[$layer].digest\" < \"$d/manifest.json\" |\n"
65 " cut -d: -f2 )\"\n"
66 "if ! test -f \"$f\"; then\n"
67 " echo \"cdi: could not extract layer\"\n"
68 " rm -rf \"$d\"\n"
69 " exit 1\n"
70 "fi\n"
71 "mv \"$f\" \"$tmpfile\"\n"
72 "rm -rf \"$d\"\n";
74 /* The temporary file. */
75 static int fd = -1;
77 /* Construct the temporary file. */
78 static int
79 make_layer (void)
81 const char *tmpdir;
82 CLEANUP_FREE char *template = NULL;
83 CLEANUP_FREE char *command = NULL;
84 size_t command_len = 0;
85 FILE *fp;
86 int r;
88 /* Path for temporary file. */
89 tmpdir = getenv ("TMPDIR");
90 if (tmpdir == NULL)
91 tmpdir = LARGE_TMPDIR;
92 if (asprintf (&template, "%s/imageXXXXXX", tmpdir) == -1) {
93 nbdkit_error ("asprintf: %m");
94 return -1;
97 fd = mkstemp (template);
98 if (fd == -1) {
99 nbdkit_error ("mkstemp: %s: %m", template);
100 return -1;
103 /* Construct the podman script. */
104 fp = open_memstream (&command, &command_len);
105 if (fp == NULL) {
106 nbdkit_error ("open_memstream: %m");
107 return -1;
109 fprintf (fp, "name="); shell_quote (name, fp); putc ('\n', fp);
110 fprintf (fp, "layer=%d\n", layer);
111 fprintf (fp, "tmpfile="); shell_quote (template, fp); putc ('\n', fp);
112 fprintf (fp, "\n");
113 fprintf (fp, "%s", script);
114 if (fclose (fp) == EOF) {
115 nbdkit_error ("memstream failed: %m");
116 return -1;
119 /* Run the command. */
120 nbdkit_debug ("%s", command);
121 r = system (command);
122 if (exit_status_to_nbd_error (r, "podman") == -1)
123 return -1;
125 /* Expect that the script creates 'template'. */
126 if (access (template, F_OK) != 0) {
127 nbdkit_error ("internal error: expected %s to be created", template);
128 return -1;
131 /* Since the script likely overwrites the file, we need to reopen it. */
132 close (fd);
133 fd = open (template, O_RDONLY|O_CLOEXEC);
134 if (fd == -1) {
135 nbdkit_error ("open: %s: %m", template);
136 unlink (template);
137 return -1;
140 /* Since we've opened the file, we can unlink it. */
141 unlink (template);
143 return 0;
146 static void
147 cdi_unload (void)
149 if (fd >= 0)
150 close (fd);
153 static int
154 cdi_config (const char *key, const char *value)
156 if (strcmp (key, "name") == 0)
157 name = value;
158 else if (strcmp (key, "layer") == 0) {
159 if (nbdkit_parse_int ("layer", value, &layer) == -1)
160 return -1;
162 else {
163 nbdkit_error ("unknown parameter '%s'", key);
164 return -1;
167 return 0;
170 static int
171 cdi_config_complete (void)
173 if (name == NULL) {
174 nbdkit_error ("you must supply the 'name' parameter "
175 "after the plugin name on the command line");
176 return -1;
179 return 0;
182 #define cdi_config_help \
183 "name=NAME[:TAG|@DIGEST] (required) Name or URI of container image.\n" \
184 "layer=<N> Layer of image to export."
186 static int
187 cdi_get_ready (void)
189 return make_layer ();
192 static void *
193 cdi_open (int readonly)
195 return NBDKIT_HANDLE_NOT_NEEDED;
198 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
200 /* Get the file size. */
201 static int64_t
202 cdi_get_size (void *handle)
204 struct stat statbuf;
206 if (fstat (fd, &statbuf) == -1) {
207 nbdkit_error ("fstat: %m");
208 return -1;
211 return statbuf.st_size;
214 /* Serves the same data over multiple connections. */
215 static int
216 cdi_can_multi_conn (void *handle)
218 return 1;
221 static int
222 cdi_can_cache (void *handle)
224 /* Let nbdkit call pread to populate the file system cache. */
225 return NBDKIT_CACHE_EMULATE;
228 /* Read data from the file. */
229 static int
230 cdi_pread (void *handle, void *buf, uint32_t count, uint64_t offset)
232 while (count > 0) {
233 ssize_t r = pread (fd, buf, count, offset);
234 if (r == -1) {
235 nbdkit_error ("pread: %m");
236 return -1;
238 if (r == 0) {
239 nbdkit_error ("pread: unexpected end of file");
240 return -1;
242 buf += r;
243 count -= r;
244 offset += r;
247 return 0;
250 static struct nbdkit_plugin plugin = {
251 .name = "cdi",
252 .longname = "nbdkit containerized data importer plugin",
253 .version = PACKAGE_VERSION,
254 .unload = cdi_unload,
255 .config = cdi_config,
256 .config_complete = cdi_config_complete,
257 .config_help = cdi_config_help,
258 .magic_config_key = "name",
259 .get_ready = cdi_get_ready,
260 .open = cdi_open,
261 .get_size = cdi_get_size,
262 .can_multi_conn = cdi_can_multi_conn,
263 .can_cache = cdi_can_cache,
264 .pread = cdi_pread,
265 .errno_is_preserved = 1,
268 NBDKIT_REGISTER_PLUGIN (plugin)