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