]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/storage/nbd.c
build: add src/include to build and simplify header inclusions
[mirror_lxc.git] / src / lxc / storage / nbd.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #include <errno.h>
7 #include <stdbool.h>
8 #include <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/prctl.h>
13 #include <sys/wait.h>
14
15 #include "config.h"
16 #include "log.h"
17 #include "memory_utils.h"
18 #include "nbd.h"
19 #include "storage.h"
20 #include "storage_utils.h"
21 #include "syscall_wrappers.h"
22 #include "utils.h"
23
24 #ifndef HAVE_STRLCPY
25 #include "strlcpy.h"
26 #endif
27
28 lxc_log_define(nbd, lxc);
29
30 struct nbd_attach_data {
31 const char *nbd;
32 const char *path;
33 };
34
35 static int do_attach_nbd(void *d);
36 static bool clone_attach_nbd(const char *nbd, const char *path);
37 static bool nbd_busy(int idx);
38 static void nbd_detach(const char *path);
39 static int nbd_get_partition(const char *src);
40 static bool wait_for_partition(const char *path);
41
42 bool attach_nbd(char *src, struct lxc_conf *conf)
43 {
44 __do_free char *orig = NULL;
45 char *p, path[50];
46 int i = 0;
47
48 orig = must_copy_string(src);
49 /* if path is followed by a partition, drop that for now */
50 p = strchr(orig, ':');
51 if (p)
52 *p = '\0';
53
54 for (;;) {
55 sprintf(path, "/dev/nbd%d", i);
56
57 if (!file_exists(path))
58 return false;
59
60 if (nbd_busy(i)) {
61 i++;
62 continue;
63 }
64
65 if (!clone_attach_nbd(path, orig))
66 return false;
67
68 conf->nbd_idx = i;
69 return true;
70 }
71 }
72
73 void detach_nbd_idx(int idx)
74 {
75 int ret;
76 char path[50];
77
78 ret = snprintf(path, 50, "/dev/nbd%d", idx);
79 if (ret < 0 || ret >= 50)
80 return;
81
82 nbd_detach(path);
83 }
84
85 int nbd_clonepaths(struct lxc_storage *orig, struct lxc_storage *new,
86 const char *oldname, const char *cname, const char *oldpath,
87 const char *lxcpath, int snap, uint64_t newsize,
88 struct lxc_conf *conf)
89 {
90 return -ENOSYS;
91 }
92
93 int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
94 struct bdev_specs *specs, const struct lxc_conf *conf)
95 {
96 return -ENOSYS;
97 }
98
99 int nbd_destroy(struct lxc_storage *orig)
100 {
101 return -ENOSYS;
102 }
103
104 bool nbd_detect(const char *path)
105 {
106 if (!strncmp(path, "nbd:", 4))
107 return true;
108
109 return false;
110 }
111
112 int nbd_mount(struct lxc_storage *bdev)
113 {
114 int ret = -1, partition;
115 const char *src;
116 char path[50];
117
118 if (strcmp(bdev->type, "nbd"))
119 return -22;
120
121 if (!bdev->src || !bdev->dest)
122 return -22;
123
124 /* nbd_idx should have been copied by bdev_init from the lxc_conf */
125 if (bdev->nbd_idx < 0)
126 return -22;
127
128 src = lxc_storage_get_path(bdev->src, bdev->type);
129 partition = nbd_get_partition(src);
130 if (partition)
131 ret = snprintf(path, 50, "/dev/nbd%dp%d", bdev->nbd_idx,
132 partition);
133 else
134 ret = snprintf(path, 50, "/dev/nbd%d", bdev->nbd_idx);
135 if (ret < 0 || ret >= 50) {
136 ERROR("Error setting up nbd device path");
137 return ret;
138 }
139
140 /* It might take awhile for the partition files to show up */
141 if (partition)
142 if (!wait_for_partition(path))
143 return -2;
144
145 ret = mount_unknown_fs(path, bdev->dest, bdev->mntopts);
146 if (ret < 0)
147 ERROR("Error mounting %s", bdev->src);
148
149 return ret;
150 }
151
152 int nbd_umount(struct lxc_storage *bdev)
153 {
154 if (strcmp(bdev->type, "nbd"))
155 return -22;
156
157 if (!bdev->src || !bdev->dest)
158 return -22;
159
160 return umount(bdev->dest);
161 }
162
163 bool requires_nbd(const char *path)
164 {
165 if (strncmp(path, "nbd:", 4) == 0)
166 return true;
167
168 return false;
169 }
170
171 static int do_attach_nbd(void *d)
172 {
173 struct nbd_attach_data *data = d;
174 const char *nbd, *path;
175 pid_t pid;
176 sigset_t mask;
177 int sfd;
178 ssize_t s;
179 struct signalfd_siginfo fdsi;
180
181 sigemptyset(&mask);
182 sigaddset(&mask, SIGHUP);
183 sigaddset(&mask, SIGCHLD);
184
185 nbd = data->nbd;
186 path = data->path;
187
188 if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
189 SYSERROR("Error blocking signals for nbd watcher");
190 exit(EXIT_FAILURE);
191 }
192
193 sfd = signalfd(-1, &mask, 0);
194 if (sfd == -1) {
195 SYSERROR("Error opening signalfd for nbd task");
196 exit(EXIT_FAILURE);
197 }
198
199 if (prctl(PR_SET_PDEATHSIG, prctl_arg(SIGHUP), prctl_arg(0),
200 prctl_arg(0), prctl_arg(0)) < 0)
201 SYSERROR("Error setting parent death signal for nbd watcher");
202
203 pid = fork();
204 if (pid) {
205 for (;;) {
206 s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
207 if (s != sizeof(struct signalfd_siginfo))
208 SYSERROR("Error reading from signalfd");
209
210 if (fdsi.ssi_signo == SIGHUP) {
211 /* container has exited */
212 nbd_detach(nbd);
213 exit(EXIT_SUCCESS);
214 } else if (fdsi.ssi_signo == SIGCHLD) {
215 int status;
216
217 /* If qemu-nbd fails, or is killed by a signal,
218 * then exit */
219 while (waitpid(-1, &status, WNOHANG) > 0) {
220 if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) ||
221 WIFSIGNALED(status)) {
222 nbd_detach(nbd);
223 exit(EXIT_FAILURE);
224 }
225 }
226 }
227 }
228 }
229
230 close(sfd);
231
232 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
233 WARN("Warning: unblocking signals for nbd watcher");
234
235 execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, (char *)NULL);
236 SYSERROR("Error executing qemu-nbd");
237 _exit(EXIT_FAILURE);
238 }
239
240 static bool clone_attach_nbd(const char *nbd, const char *path)
241 {
242 pid_t pid;
243 struct nbd_attach_data data;
244
245 data.nbd = nbd;
246 data.path = path;
247
248 pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID, NULL);
249 if (pid < 0)
250 return false;
251
252 return true;
253 }
254
255 static bool nbd_busy(int idx)
256 {
257 char path[100];
258 int ret;
259
260 ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx);
261 if (ret < 0 || ret >= 100)
262 return true;
263
264 return file_exists(path);
265 }
266
267 static void nbd_detach(const char *path)
268 {
269 int ret;
270 pid_t pid = fork();
271
272 if (pid < 0) {
273 SYSERROR("Error forking to detach nbd");
274 return;
275 }
276
277 if (pid) {
278 ret = wait_for_pid(pid);
279 if (ret < 0)
280 ERROR("nbd disconnect returned an error");
281 return;
282 }
283
284 execlp("qemu-nbd", "qemu-nbd", "-d", path, (char *)NULL);
285 SYSERROR("Error executing qemu-nbd");
286 _exit(EXIT_FAILURE);
287 }
288
289 /*
290 * Pick the partition # off the end of a nbd:file:p
291 * description. Return 1-9 for the partition id, or 0
292 * for no partition.
293 */
294 static int nbd_get_partition(const char *src)
295 {
296 char *p = strchr(src, ':');
297 if (!p)
298 return 0;
299
300 p = strchr(p+1, ':');
301 if (!p)
302 return 0;
303
304 p++;
305
306 if (*p < '1' || *p > '9')
307 return 0;
308
309 return *p - '0';
310 }
311
312 static bool wait_for_partition(const char *path)
313 {
314 int count = 0;
315
316 while (count < 5) {
317 if (file_exists(path))
318 return true;
319
320 sleep(1);
321 count++;
322 }
323
324 ERROR("Device %s did not show up after 5 seconds", path);
325 return false;
326 }