debuggers.hg

view tools/libxl/libxl_device.c @ 22855:1d1eec7e1fb4

xl: Perform minimal validation of virtual disk file while parsing config file

This patch performs some very basic validation on the virtual disk
file passed through the config file. This validation ensures that we
don't go too far with the initialization like spawn qemu and more
while there could be some potentially fundamental issues.

[ Patch fixed up to work with PHYSTYPE_EMPTY 22808:6ec61438713a -iwj ]

Signed-off-by: Kamala Narasimhan <kamala.narasimhan@citrix.com>
Acked-by: Ian Jackson <ian.jackson@eu.citrix.com>
Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com>
Committed-by: Ian Jackson <ian.jackson@eu.citrix.com>
author Kamala Narasimhan <kamala.narasimhan@gmail.com>
date Tue Jan 25 18:09:49 2011 +0000 (2011-01-25)
parents 6ec61438713a
children 29eaad8e388a
line source
1 /*
2 * Copyright (C) 2009 Citrix Ltd.
3 * Author Vincent Hanquez <vincent.hanquez@eu.citrix.com>
4 * Author Stefano Stabellini <stefano.stabellini@eu.citrix.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; version 2.1 only. with the special
9 * exception on linking described in file LICENSE.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 */
17 #include "libxl_osdeps.h"
19 #include <string.h>
20 #include <stdio.h>
21 #include <sys/time.h> /* for struct timeval */
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
28 #include "libxl.h"
29 #include "libxl_internal.h"
31 static const char *string_of_kinds[] = {
32 [DEVICE_VIF] = "vif",
33 [DEVICE_VIF2] = "vif2",
34 [DEVICE_VBD] = "vbd",
35 [DEVICE_TAP] = "tap",
36 [DEVICE_QDISK] = "qdisk",
37 [DEVICE_PCI] = "pci",
38 [DEVICE_VFB] = "vfb",
39 [DEVICE_VKBD] = "vkbd",
40 [DEVICE_CONSOLE] = "console",
41 };
43 static char *libxl__device_frontend_path(libxl__gc *gc, libxl__device *device)
44 {
45 char *dom_path = libxl__xs_get_dompath(gc, device->domid);
47 /* Console 0 is a special case */
48 if (device->kind == DEVICE_CONSOLE && device->devid == 0)
49 return libxl__sprintf(gc, "%s/console", dom_path);
51 return libxl__sprintf(gc, "%s/device/%s/%d", dom_path,
52 string_of_kinds[device->kind], device->devid);
53 }
55 static char *libxl__device_backend_path(libxl__gc *gc, libxl__device *device)
56 {
57 char *dom_path = libxl__xs_get_dompath(gc, device->backend_domid);
59 return libxl__sprintf(gc, "%s/backend/%s/%u/%d", dom_path,
60 string_of_kinds[device->backend_kind],
61 device->domid, device->devid);
62 }
64 int libxl__device_generic_add(libxl_ctx *ctx, libxl__device *device,
65 char **bents, char **fents)
66 {
67 libxl__gc gc = LIBXL_INIT_GC(ctx);
68 char *frontend_path, *backend_path;
69 xs_transaction_t t;
70 struct xs_permissions frontend_perms[2];
71 struct xs_permissions backend_perms[2];
72 int rc;
74 if (!is_valid_device_kind(device->backend_kind) || !is_valid_device_kind(device->kind)) {
75 rc = ERROR_INVAL;
76 goto out;
77 }
79 frontend_path = libxl__device_frontend_path(&gc, device);
80 backend_path = libxl__device_backend_path(&gc, device);
82 frontend_perms[0].id = device->domid;
83 frontend_perms[0].perms = XS_PERM_NONE;
84 frontend_perms[1].id = device->backend_domid;
85 frontend_perms[1].perms = XS_PERM_READ;
87 backend_perms[0].id = device->backend_domid;
88 backend_perms[0].perms = XS_PERM_NONE;
89 backend_perms[1].id = device->domid;
90 backend_perms[1].perms = XS_PERM_READ;
92 retry_transaction:
93 t = xs_transaction_start(ctx->xsh);
94 /* FIXME: read frontend_path and check state before removing stuff */
96 if (fents) {
97 xs_rm(ctx->xsh, t, frontend_path);
98 xs_mkdir(ctx->xsh, t, frontend_path);
99 xs_set_permissions(ctx->xsh, t, frontend_path, frontend_perms, ARRAY_SIZE(frontend_perms));
100 xs_write(ctx->xsh, t, libxl__sprintf(&gc, "%s/backend", frontend_path), backend_path, strlen(backend_path));
101 libxl__xs_writev(&gc, t, frontend_path, fents);
102 }
104 if (bents) {
105 xs_rm(ctx->xsh, t, backend_path);
106 xs_mkdir(ctx->xsh, t, backend_path);
107 xs_set_permissions(ctx->xsh, t, backend_path, backend_perms, ARRAY_SIZE(backend_perms));
108 xs_write(ctx->xsh, t, libxl__sprintf(&gc, "%s/frontend", backend_path), frontend_path, strlen(frontend_path));
109 libxl__xs_writev(&gc, t, backend_path, bents);
110 }
112 if (!xs_transaction_end(ctx->xsh, t, 0)) {
113 if (errno == EAGAIN)
114 goto retry_transaction;
115 else
116 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "xs transaction failed");
117 }
118 rc = 0;
119 out:
120 libxl__free_all(&gc);
121 return rc;
122 }
124 char *libxl__device_disk_string_of_phystype(libxl_disk_phystype phystype)
125 {
126 switch (phystype) {
127 case PHYSTYPE_QCOW: return "qcow";
128 case PHYSTYPE_QCOW2: return "qcow2";
129 case PHYSTYPE_VHD: return "vhd";
130 case PHYSTYPE_AIO: return "aio";
131 case PHYSTYPE_FILE: return "file";
132 case PHYSTYPE_PHY: return "phy";
133 case PHYSTYPE_EMPTY: return "file";
134 default: return NULL;
135 }
136 }
138 char *libxl__device_disk_backend_type_of_phystype(libxl_disk_phystype phystype)
139 {
140 switch (phystype) {
141 case PHYSTYPE_QCOW: return "tap";
142 case PHYSTYPE_QCOW2: return "tap";
143 case PHYSTYPE_VHD: return "tap";
144 case PHYSTYPE_AIO: return "tap";
145 /* let's pretend file is tap:aio */
146 case PHYSTYPE_FILE: return "tap";
147 case PHYSTYPE_EMPTY: return "tap";
148 case PHYSTYPE_PHY: return "phy";
149 default: return NULL;
150 }
151 }
153 int libxl__device_physdisk_major_minor(const char *physpath, int *major, int *minor)
154 {
155 struct stat buf;
156 if (stat(physpath, &buf) < 0)
157 return -1;
158 *major = major(buf.st_rdev);
159 *minor = minor(buf.st_rdev);
160 return 0;
161 }
163 static int device_virtdisk_matches(const char *virtpath, const char *devtype,
164 int *index_r, int max_index,
165 int *partition_r, int max_partition) {
166 const char *p;
167 char *ep;
168 int tl, c;
169 long pl;
171 tl = strlen(devtype);
172 if (memcmp(virtpath, devtype, tl))
173 return 0;
175 /* We decode the drive letter as if it were in base 52
176 * with digits a-zA-Z, more or less */
177 *index_r = -1;
178 p = virtpath + tl;
179 for (;;) {
180 c = *p++;
181 if (c >= 'a' && c <= 'z') {
182 c -= 'a';
183 } else {
184 --p;
185 break;
186 }
187 (*index_r)++;
188 (*index_r) *= 26;
189 (*index_r) += c;
191 if (*index_r > max_index)
192 return 0;
193 }
195 if (!*p) {
196 *partition_r = 0;
197 return 1;
198 }
200 if (*p=='0')
201 return 0; /* leading zeroes not permitted in partition number */
203 pl = strtoul(p, &ep, 10);
204 if (pl > max_partition || *ep)
205 return 0;
207 *partition_r = pl;
208 return 1;
209 }
211 int libxl__device_disk_dev_number(char *virtpath)
212 {
213 int disk, partition;
214 char *ep;
215 unsigned long ul;
216 int chrused;
218 chrused = -1;
219 if ((sscanf(virtpath, "d%ip%i%n", &disk, &partition, &chrused) >= 2
220 && chrused == strlen(virtpath) && disk < (1<<20) && partition < 256)
221 ||
222 device_virtdisk_matches(virtpath, "xvd",
223 &disk, (1<<20)-1,
224 &partition, 255)) {
225 if (disk <= 15 && partition <= 15)
226 return (202 << 8) | (disk << 4) | partition;
227 else
228 return (1 << 28) | (disk << 8) | partition;
229 }
231 errno = 0;
232 ul = strtoul(virtpath, &ep, 0);
233 if (!errno && !*ep && ul <= INT_MAX)
234 return ul;
236 if (device_virtdisk_matches(virtpath, "hd",
237 &disk, 3,
238 &partition, 63)) {
239 return ((disk<2 ? 3 : 22) << 8) | ((disk & 1) << 6) | partition;
240 }
241 if (device_virtdisk_matches(virtpath, "sd",
242 &disk, 15,
243 &partition, 15)) {
244 return (8 << 8) | (disk << 4) | partition;
245 }
246 return -1;
247 }
249 int libxl__device_destroy(libxl_ctx *ctx, char *be_path, int force)
250 {
251 libxl__gc gc = LIBXL_INIT_GC(ctx);
252 xs_transaction_t t;
253 char *state_path = libxl__sprintf(&gc, "%s/state", be_path);
254 char *state = libxl__xs_read(&gc, XBT_NULL, state_path);
255 int rc = 0;
257 if (!state)
258 goto out;
259 if (atoi(state) != 4) {
260 xs_rm(ctx->xsh, XBT_NULL, be_path);
261 goto out;
262 }
264 retry_transaction:
265 t = xs_transaction_start(ctx->xsh);
266 xs_write(ctx->xsh, t, libxl__sprintf(&gc, "%s/online", be_path), "0", strlen("0"));
267 xs_write(ctx->xsh, t, state_path, "5", strlen("5"));
268 if (!xs_transaction_end(ctx->xsh, t, 0)) {
269 if (errno == EAGAIN)
270 goto retry_transaction;
271 else {
272 rc = -1;
273 goto out;
274 }
275 }
276 if (!force) {
277 xs_watch(ctx->xsh, state_path, be_path);
278 rc = 1;
279 }
280 out:
281 libxl__free_all(&gc);
282 return rc;
283 }
285 static int wait_for_dev_destroy(libxl_ctx *ctx, struct timeval *tv)
286 {
287 libxl__gc gc = LIBXL_INIT_GC(ctx);
288 int nfds, rc;
289 unsigned int n;
290 fd_set rfds;
291 char **l1 = NULL;
293 rc = 1;
294 nfds = xs_fileno(ctx->xsh) + 1;
295 FD_ZERO(&rfds);
296 FD_SET(xs_fileno(ctx->xsh), &rfds);
297 if (select(nfds, &rfds, NULL, NULL, tv) > 0) {
298 l1 = xs_read_watch(ctx->xsh, &n);
299 if (l1 != NULL) {
300 char *state = libxl__xs_read(&gc, XBT_NULL, l1[XS_WATCH_PATH]);
301 if (!state || atoi(state) == 6) {
302 xs_unwatch(ctx->xsh, l1[0], l1[1]);
303 xs_rm(ctx->xsh, XBT_NULL, l1[XS_WATCH_TOKEN]);
304 LIBXL__LOG(ctx, LIBXL__LOG_DEBUG, "Destroyed device backend at %s", l1[XS_WATCH_TOKEN]);
305 rc = 0;
306 }
307 free(l1);
308 }
309 }
310 libxl__free_all(&gc);
311 return rc;
312 }
314 int libxl__devices_destroy(libxl_ctx *ctx, uint32_t domid, int force)
315 {
316 libxl__gc gc = LIBXL_INIT_GC(ctx);
317 char *path, *be_path, *fe_path;
318 unsigned int num1, num2;
319 char **l1 = NULL, **l2 = NULL;
320 int i, j, n = 0, n_watches = 0;
321 flexarray_t *toremove;
323 toremove = flexarray_make(16, 1);
324 path = libxl__sprintf(&gc, "/local/domain/%d/device", domid);
325 l1 = libxl__xs_directory(&gc, XBT_NULL, path, &num1);
326 if (!l1) {
327 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "%s is empty", path);
328 goto out;
329 }
330 for (i = 0; i < num1; i++) {
331 if (!strcmp("vfs", l1[i]))
332 continue;
333 path = libxl__sprintf(&gc, "/local/domain/%d/device/%s", domid, l1[i]);
334 l2 = libxl__xs_directory(&gc, XBT_NULL, path, &num2);
335 if (!l2)
336 continue;
337 for (j = 0; j < num2; j++) {
338 fe_path = libxl__sprintf(&gc, "/local/domain/%d/device/%s/%s", domid, l1[i], l2[j]);
339 be_path = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/backend", fe_path));
340 if (be_path != NULL) {
341 if (libxl__device_destroy(ctx, be_path, force) > 0)
342 n_watches++;
343 flexarray_set(toremove, n++, libxl__dirname(&gc, be_path));
344 } else {
345 xs_rm(ctx->xsh, XBT_NULL, path);
346 }
347 }
348 }
350 /* console 0 frontend directory is not under /local/domain/<domid>/device */
351 fe_path = libxl__sprintf(&gc, "/local/domain/%d/console", domid);
352 be_path = libxl__xs_read(&gc, XBT_NULL, libxl__sprintf(&gc, "%s/backend", fe_path));
353 if (be_path && strcmp(be_path, "")) {
354 if (libxl__device_destroy(ctx, be_path, force) > 0)
355 n_watches++;
356 flexarray_set(toremove, n++, libxl__dirname(&gc, be_path));
357 }
359 if (!force) {
360 /* Linux-ism. Most implementations leave the timeout
361 * untouched after select. Linux, however, will chip
362 * away the elapsed time from it, which is what we
363 * need to enforce a single time span waiting for
364 * device destruction. */
365 struct timeval tv;
366 tv.tv_sec = LIBXL_DESTROY_TIMEOUT;
367 tv.tv_usec = 0;
368 while (n_watches > 0) {
369 if (wait_for_dev_destroy(ctx, &tv)) {
370 break;
371 } else {
372 n_watches--;
373 }
374 }
375 }
376 for (i = 0; i < n; i++) {
377 flexarray_get(toremove, i, (void**) &path);
378 xs_rm(ctx->xsh, XBT_NULL, path);
379 }
380 out:
381 flexarray_free(toremove);
382 libxl__free_all(&gc);
383 return 0;
384 }
386 int libxl__device_del(libxl_ctx *ctx, libxl__device *dev, int wait)
387 {
388 libxl__gc gc = LIBXL_INIT_GC(ctx);
389 char *backend_path;
390 int rc;
392 backend_path = libxl__device_backend_path(&gc, dev);
394 rc = libxl__device_destroy(ctx, backend_path, !wait);
395 if (rc == -1) {
396 rc = ERROR_FAIL;
397 goto out;
398 }
400 if (wait) {
401 struct timeval tv;
402 tv.tv_sec = LIBXL_DESTROY_TIMEOUT;
403 tv.tv_usec = 0;
404 (void)wait_for_dev_destroy(ctx, &tv);
405 }
407 rc = 0;
409 out:
410 libxl__free_all(&gc);
411 return rc;
412 }
414 int libxl__wait_for_device_model(libxl_ctx *ctx,
415 uint32_t domid, char *state,
416 int (*check_callback)(libxl_ctx *ctx,
417 uint32_t domid,
418 const char *state,
419 void *userdata),
420 void *check_callback_userdata)
421 {
422 libxl__gc gc = LIBXL_INIT_GC(ctx);
423 char *path;
424 char *p;
425 unsigned int len;
426 int rc = 0;
427 struct xs_handle *xsh;
428 int nfds;
429 fd_set rfds;
430 struct timeval tv;
431 unsigned int num;
432 char **l = NULL;
434 xsh = xs_daemon_open();
435 if (xsh == NULL) {
436 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Unable to open xenstore connection");
437 goto err;
438 }
440 path = libxl__sprintf(&gc, "/local/domain/0/device-model/%d/state", domid);
441 xs_watch(xsh, path, path);
442 tv.tv_sec = LIBXL_DEVICE_MODEL_START_TIMEOUT;
443 tv.tv_usec = 0;
444 nfds = xs_fileno(xsh) + 1;
445 while (rc > 0 || (!rc && tv.tv_sec > 0)) {
446 p = xs_read(xsh, XBT_NULL, path, &len);
447 if ( NULL == p )
448 goto again;
450 if ( NULL != state && strcmp(p, state) )
451 goto again;
453 if ( NULL != check_callback ) {
454 rc = (*check_callback)(ctx, domid, p, check_callback_userdata);
455 if ( rc > 0 )
456 goto again;
457 }
459 free(p);
460 xs_unwatch(xsh, path, path);
461 xs_daemon_close(xsh);
462 libxl__free_all(&gc);
463 return rc;
464 again:
465 free(p);
466 FD_ZERO(&rfds);
467 FD_SET(xs_fileno(xsh), &rfds);
468 rc = select(nfds, &rfds, NULL, NULL, &tv);
469 if (rc > 0) {
470 l = xs_read_watch(xsh, &num);
471 if (l != NULL)
472 free(l);
473 else
474 goto again;
475 }
476 }
477 xs_unwatch(xsh, path, path);
478 xs_daemon_close(xsh);
479 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Device Model not ready");
480 err:
481 libxl__free_all(&gc);
482 return -1;
483 }
485 int libxl__wait_for_backend(libxl_ctx *ctx, char *be_path, char *state)
486 {
487 libxl__gc gc = LIBXL_INIT_GC(ctx);
488 int watchdog = 100;
489 unsigned int len;
490 char *p;
491 char *path = libxl__sprintf(&gc, "%s/state", be_path);
492 int rc = -1;
494 while (watchdog > 0) {
495 p = xs_read(ctx->xsh, XBT_NULL, path, &len);
496 if (p == NULL) {
497 if (errno == ENOENT) {
498 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Backend %s does not exist",
499 be_path);
500 } else {
501 LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_ERROR, "Failed to access backend %s",
502 be_path);
503 }
504 goto out;
505 } else {
506 if (!strcmp(p, state)) {
507 rc = 0;
508 goto out;
509 } else {
510 usleep(100000);
511 watchdog--;
512 }
513 }
514 }
515 LIBXL__LOG(ctx, LIBXL__LOG_ERROR, "Backend %s not ready", be_path);
516 out:
517 libxl__free_all(&gc);
518 return rc;
519 }