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