]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/storage/nbd.c
secure coding: #2 strcpy => strlcpy
[mirror_lxc.git] / src / lxc / storage / nbd.c
CommitLineData
bf76c012
CB
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
bf76c012 34#include "log.h"
28d832c4
CB
35#include "nbd.h"
36#include "storage.h"
f2d5a09d 37#include "storage_utils.h"
bf76c012
CB
38#include "utils.h"
39
43f984ea
DJ
40#ifndef HAVE_STRLCPY
41#include "include/strlcpy.h"
42#endif
43
10bc1861 44lxc_log_define(nbd, lxc);
bf76c012
CB
45
46struct nbd_attach_data {
47 const char *nbd;
48 const char *path;
49};
50
51static bool clone_attach_nbd(const char *nbd, const char *path);
52static int do_attach_nbd(void *d);
53static bool nbd_busy(int idx);
54static void nbd_detach(const char *path);
55static int nbd_get_partition(const char *src);
56static bool wait_for_partition(const char *path);
57
58bool attach_nbd(char *src, struct lxc_conf *conf)
59{
43f984ea 60 char *orig, *p, path[50];
bf76c012 61 int i = 0;
43f984ea
DJ
62 size_t len;
63
64 len = strlen(src);
65 orig = alloca(len + 1);
66 (void)strlcpy(orig, src, len + 1);
bf76c012 67
bf76c012
CB
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
87void 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
10bc1861
CB
99int 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)
bf76c012
CB
103{
104 return -ENOSYS;
105}
106
10bc1861
CB
107int nbd_create(struct lxc_storage *bdev, const char *dest, const char *n,
108 struct bdev_specs *specs)
bf76c012
CB
109{
110 return -ENOSYS;
111}
112
10bc1861 113int nbd_destroy(struct lxc_storage *orig)
bf76c012
CB
114{
115 return -ENOSYS;
116}
117
3d2ae1e2 118bool nbd_detect(const char *path)
bf76c012 119{
f7ac4459 120 if (!strncmp(path, "nbd:", 4))
3d2ae1e2 121 return true;
f7ac4459 122
3d2ae1e2 123 return false;
bf76c012
CB
124}
125
10bc1861 126int nbd_mount(struct lxc_storage *bdev)
bf76c012
CB
127{
128 int ret = -1, partition;
41dc7155 129 const char *src;
bf76c012
CB
130 char path[50];
131
132 if (strcmp(bdev->type, "nbd"))
133 return -22;
094c1904 134
bf76c012
CB
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;
094c1904
CB
141
142 src = lxc_storage_get_path(bdev->src, bdev->type);
143 partition = nbd_get_partition(src);
bf76c012
CB
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
10bc1861 166int nbd_umount(struct lxc_storage *bdev)
bf76c012 167{
bf76c012
CB
168 if (strcmp(bdev->type, "nbd"))
169 return -22;
094c1904 170
bf76c012
CB
171 if (!bdev->src || !bdev->dest)
172 return -22;
094c1904
CB
173
174 return umount(bdev->dest);
bf76c012
CB
175}
176
177bool requires_nbd(const char *path)
178{
179 if (strncmp(path, "nbd:", 4) == 0)
180 return true;
181 return false;
182}
183
184static 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, SIGHUP, 0, 0, 0) < 0)
213 SYSERROR("Error setting parent death signal for nbd watcher");
214
215 pid = fork();
216 if (pid) {
217 for (;;) {
218 s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
219 if (s != sizeof(struct signalfd_siginfo))
220 SYSERROR("Error reading from signalfd");
221
222 if (fdsi.ssi_signo == SIGHUP) {
223 /* container has exited */
224 nbd_detach(nbd);
225 exit(0);
226 } else if (fdsi.ssi_signo == SIGCHLD) {
227 int status;
228 /* If qemu-nbd fails, or is killed by a signal,
229 * then exit */
230 while (waitpid(-1, &status, WNOHANG) > 0) {
231 if ((WIFEXITED(status) && WEXITSTATUS(status) != 0) ||
232 WIFSIGNALED(status)) {
233 nbd_detach(nbd);
234 exit(1);
235 }
236 }
237 }
238 }
239 }
240
241 close(sfd);
242 if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1)
243 WARN("Warning: unblocking signals for nbd watcher");
244
acf47e1b 245 execlp("qemu-nbd", "qemu-nbd", "-c", nbd, path, (char *)NULL);
bf76c012
CB
246 SYSERROR("Error executing qemu-nbd");
247 exit(1);
248}
249
250static bool clone_attach_nbd(const char *nbd, const char *path)
251{
252 pid_t pid;
253 struct nbd_attach_data data;
254
255 data.nbd = nbd;
256 data.path = path;
257
258 pid = lxc_clone(do_attach_nbd, &data, CLONE_NEWPID);
259 if (pid < 0)
260 return false;
261 return true;
262}
263
264static bool nbd_busy(int idx)
265{
266 char path[100];
267 int ret;
268
269 ret = snprintf(path, 100, "/sys/block/nbd%d/pid", idx);
270 if (ret < 0 || ret >= 100)
271 return true;
272 return file_exists(path);
273}
274
275static void nbd_detach(const char *path)
276{
277 int ret;
278 pid_t pid = fork();
279
280 if (pid < 0) {
281 SYSERROR("Error forking to detach nbd");
282 return;
283 }
284 if (pid) {
285 ret = wait_for_pid(pid);
286 if (ret < 0)
287 ERROR("nbd disconnect returned an error");
288 return;
289 }
acf47e1b 290 execlp("qemu-nbd", "qemu-nbd", "-d", path, (char *)NULL);
bf76c012
CB
291 SYSERROR("Error executing qemu-nbd");
292 exit(1);
293}
294
295/*
296 * Pick the partition # off the end of a nbd:file:p
297 * description. Return 1-9 for the partition id, or 0
298 * for no partition.
299 */
300static int nbd_get_partition(const char *src)
301{
302 char *p = strchr(src, ':');
303 if (!p)
304 return 0;
305 p = strchr(p+1, ':');
306 if (!p)
307 return 0;
308 p++;
309 if (*p < '1' || *p > '9')
310 return 0;
311 return *p - '0';
312}
313
314static bool wait_for_partition(const char *path)
315{
316 int count = 0;
317 while (count < 5) {
318 if (file_exists(path))
319 return true;
320 sleep(1);
321 count++;
322 }
323 ERROR("Device %s did not show up after 5 seconds", path);
324 return false;
325}