1 /*
2  * Copyright 2009-2017 Citrix Ltd and other contributors
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU Lesser General Public License as published
6  * by the Free Software Foundation; version 2.1 only. with the special
7  * exception on linking described in file LICENSE.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  */
14 
15 #include "libxl_osdeps.h"
16 
17 #include "libxl_internal.h"
18 
19 #define BACKEND_STRING_SIZE 5
20 
disk_eject_xswatch_callback(libxl__egc * egc,libxl__ev_xswatch * w,const char * wpath,const char * epath)21 static void disk_eject_xswatch_callback(libxl__egc *egc, libxl__ev_xswatch *w,
22                                         const char *wpath, const char *epath) {
23     EGC_GC;
24     libxl_evgen_disk_eject *evg = (void*)w;
25     const char *backend;
26     char *value;
27     char backend_type[BACKEND_STRING_SIZE+1];
28     int rc;
29 
30     value = libxl__xs_read(gc, XBT_NULL, wpath);
31 
32     if (!value || strcmp(value,  "eject"))
33         return;
34 
35     if (libxl__xs_printf(gc, XBT_NULL, wpath, "")) {
36         LIBXL__EVENT_DISASTER(egc, "xs_write failed acknowledging eject",
37                               errno, LIBXL_EVENT_TYPE_DISK_EJECT);
38         return;
39     }
40 
41     libxl_event *ev = NEW_EVENT(egc, DISK_EJECT, evg->domid, evg->user);
42     libxl_device_disk *disk = &ev->u.disk_eject.disk;
43 
44     rc = libxl__xs_read_checked(gc, XBT_NULL, evg->be_ptr_path, &backend);
45     if (rc) {
46         LIBXL__EVENT_DISASTER(egc, "xs_read failed reading be_ptr_path",
47                               errno, LIBXL_EVENT_TYPE_DISK_EJECT);
48         return;
49     }
50     if (!backend) {
51         /* device has been removed, not simply ejected */
52         return;
53     }
54 
55     sscanf(backend,
56             "/local/domain/%d/backend/%" TOSTRING(BACKEND_STRING_SIZE)
57            "[a-z]/%*d/%*d",
58            &disk->backend_domid, backend_type);
59     if (!strcmp(backend_type, "tap") || !strcmp(backend_type, "vbd")) {
60         disk->backend = LIBXL_DISK_BACKEND_TAP;
61     } else if (!strcmp(backend_type, "qdisk")) {
62         disk->backend = LIBXL_DISK_BACKEND_QDISK;
63     } else {
64         disk->backend = LIBXL_DISK_BACKEND_UNKNOWN;
65     }
66 
67     disk->pdev_path = strdup(""); /* xxx fixme malloc failure */
68     disk->format = LIBXL_DISK_FORMAT_EMPTY;
69     /* this value is returned to the user: do not free right away */
70     disk->vdev = libxl__strdup(NOGC, evg->vdev);
71     disk->removable = 1;
72     disk->readwrite = 0;
73     disk->is_cdrom = 1;
74 
75     libxl__event_occurred(egc, ev);
76 }
77 
libxl_evenable_disk_eject(libxl_ctx * ctx,uint32_t guest_domid,const char * vdev,libxl_ev_user user,libxl_evgen_disk_eject ** evgen_out)78 int libxl_evenable_disk_eject(libxl_ctx *ctx, uint32_t guest_domid,
79                               const char *vdev, libxl_ev_user user,
80                               libxl_evgen_disk_eject **evgen_out) {
81     GC_INIT(ctx);
82     CTX_LOCK;
83     int rc;
84     char *path;
85     libxl_evgen_disk_eject *evg = NULL;
86 
87     evg = malloc(sizeof(*evg));  if (!evg) { rc = ERROR_NOMEM; goto out; }
88     memset(evg, 0, sizeof(*evg));
89     evg->user = user;
90     evg->domid = guest_domid;
91     LIBXL_LIST_INSERT_HEAD(&CTX->disk_eject_evgens, evg, entry);
92 
93     uint32_t domid = libxl_get_stubdom_id(ctx, guest_domid);
94 
95     if (!domid)
96         domid = guest_domid;
97 
98     int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
99 
100     path = GCSPRINTF("%s/device/vbd/%d/eject",
101                  libxl__xs_get_dompath(gc, domid),
102                  devid);
103     if (!path) { rc = ERROR_NOMEM; goto out; }
104 
105     const char *libxl_path = GCSPRINTF("%s/device/vbd/%d",
106                                  libxl__xs_libxl_path(gc, domid),
107                                  devid);
108     evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
109 
110     const char *configured_vdev;
111     rc = libxl__xs_read_checked(gc, XBT_NULL,
112             GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
113     if (rc) goto out;
114 
115     evg->vdev = libxl__strdup(NOGC, configured_vdev);
116 
117     rc = libxl__ev_xswatch_register(gc, &evg->watch,
118                                     disk_eject_xswatch_callback, path);
119     if (rc) goto out;
120 
121     *evgen_out = evg;
122     CTX_UNLOCK;
123     GC_FREE;
124     return 0;
125 
126  out:
127     if (evg)
128         libxl__evdisable_disk_eject(gc, evg);
129     CTX_UNLOCK;
130     GC_FREE;
131     return rc;
132 }
133 
libxl__evdisable_disk_eject(libxl__gc * gc,libxl_evgen_disk_eject * evg)134 void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
135     CTX_LOCK;
136 
137     LIBXL_LIST_REMOVE(evg, entry);
138 
139     if (libxl__ev_xswatch_isregistered(&evg->watch))
140         libxl__ev_xswatch_deregister(gc, &evg->watch);
141 
142     free(evg->vdev);
143     free(evg->be_ptr_path);
144     free(evg);
145 
146     CTX_UNLOCK;
147 }
148 
libxl_evdisable_disk_eject(libxl_ctx * ctx,libxl_evgen_disk_eject * evg)149 void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
150     GC_INIT(ctx);
151     libxl__evdisable_disk_eject(gc, evg);
152     GC_FREE;
153 }
154 
libxl__device_disk_setdefault(libxl__gc * gc,uint32_t domid,libxl_device_disk * disk,bool hotplug)155 static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
156                                          libxl_device_disk *disk, bool hotplug)
157 {
158     int rc;
159 
160     libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
161     libxl_defbool_setdefault(&disk->colo_enable, false);
162     libxl_defbool_setdefault(&disk->colo_restore_enable, false);
163 
164     rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
165     if (rc < 0) return rc;
166 
167     /* Force Qdisk backend for CDROM devices of guests with a device model. */
168     if (disk->is_cdrom != 0 &&
169         libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
170         if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
171               disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
172             LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
173             return ERROR_FAIL;
174         }
175         disk->backend = LIBXL_DISK_BACKEND_QDISK;
176     }
177 
178     rc = libxl__device_disk_set_backend(gc, disk);
179     return rc;
180 }
181 
libxl__device_from_disk(libxl__gc * gc,uint32_t domid,const libxl_device_disk * disk,libxl__device * device)182 static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
183                                    const libxl_device_disk *disk,
184                                    libxl__device *device)
185 {
186     int devid;
187 
188     devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
189     if (devid==-1) {
190         LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
191              disk->vdev);
192         return ERROR_INVAL;
193     }
194 
195     device->backend_domid = disk->backend_domid;
196     device->backend_devid = devid;
197 
198     switch (disk->backend) {
199         case LIBXL_DISK_BACKEND_PHY:
200             device->backend_kind = LIBXL__DEVICE_KIND_VBD;
201             break;
202         case LIBXL_DISK_BACKEND_TAP:
203             device->backend_kind = LIBXL__DEVICE_KIND_VBD;
204             break;
205         case LIBXL_DISK_BACKEND_QDISK:
206             device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
207             break;
208         default:
209             LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
210                  disk->backend);
211             return ERROR_INVAL;
212     }
213 
214     device->domid = domid;
215     device->devid = devid;
216     device->kind  = LIBXL__DEVICE_KIND_VBD;
217 
218     return 0;
219 }
220 
221 /* Specific function called directly only by local disk attach,
222  * all other users should instead use the regular
223  * libxl__device_disk_add wrapper
224  *
225  * The (optionally) passed function get_vdev will be used to
226  * set the vdev the disk should be attached to. When it is set the caller
227  * must also pass get_vdev_user, which will be passed to get_vdev.
228  *
229  * The passed get_vdev function is also in charge of printing
230  * the corresponding error message when appropiate.
231  */
device_disk_add(libxl__egc * egc,uint32_t domid,libxl_device_disk * disk,libxl__ao_device * aodev,char * get_vdev (libxl__gc *,void *,xs_transaction_t),void * get_vdev_user)232 static void device_disk_add(libxl__egc *egc, uint32_t domid,
233                            libxl_device_disk *disk,
234                            libxl__ao_device *aodev,
235                            char *get_vdev(libxl__gc *, void *,
236                                           xs_transaction_t),
237                            void *get_vdev_user)
238 {
239     STATE_AO_GC(aodev->ao);
240     flexarray_t *front = NULL;
241     flexarray_t *back = NULL;
242     char *dev = NULL, *script;
243     libxl__device *device;
244     int rc;
245     libxl_ctx *ctx = gc->owner;
246     xs_transaction_t t = XBT_NULL;
247     libxl_domain_config d_config;
248     libxl_device_disk disk_saved;
249     libxl__domain_userdata_lock *lock = NULL;
250 
251     libxl_domain_config_init(&d_config);
252     libxl_device_disk_init(&disk_saved);
253     libxl_device_disk_copy(ctx, &disk_saved, disk);
254 
255     libxl_domain_type type = libxl__domain_type(gc, domid);
256     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
257         rc = ERROR_FAIL;
258         goto out;
259     }
260 
261     /*
262      * get_vdev != NULL -> local attach
263      * get_vdev == NULL -> block attach
264      *
265      * We don't care about local attach state because it's only
266      * intermediate state.
267      */
268     if (!get_vdev && aodev->update_json) {
269         lock = libxl__lock_domain_userdata(gc, domid);
270         if (!lock) {
271             rc = ERROR_LOCK_FAIL;
272             goto out;
273         }
274 
275         rc = libxl__get_domain_configuration(gc, domid, &d_config);
276         if (rc) goto out;
277 
278         device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
279                                  &disk_saved);
280 
281         rc = libxl__dm_check_start(gc, &d_config, domid);
282         if (rc) goto out;
283     }
284 
285     for (;;) {
286         rc = libxl__xs_transaction_start(gc, &t);
287         if (rc) goto out;
288 
289         if (get_vdev) {
290             assert(get_vdev_user);
291             disk->vdev = get_vdev(gc, get_vdev_user, t);
292             if (disk->vdev == NULL) {
293                 rc = ERROR_FAIL;
294                 goto out;
295             }
296         }
297 
298         rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
299         if (rc) goto out;
300 
301         front = flexarray_make(gc, 16, 1);
302         back = flexarray_make(gc, 16, 1);
303 
304         GCNEW(device);
305         rc = libxl__device_from_disk(gc, domid, disk, device);
306         if (rc != 0) {
307             LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
308                  disk->vdev);
309             goto out;
310         }
311 
312         rc = libxl__device_exists(gc, t, device);
313         if (rc < 0) goto out;
314         if (rc == 1) {              /* already exists in xenstore */
315             LOGD(ERROR, domid, "device already exists in xenstore");
316             aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
317             rc = ERROR_DEVICE_EXISTS;
318             goto out;
319         }
320 
321         switch (disk->backend) {
322             case LIBXL_DISK_BACKEND_PHY:
323                 dev = disk->pdev_path;
324 
325         do_backend_phy:
326                 flexarray_append(back, "params");
327                 flexarray_append(back, dev);
328 
329                 script = libxl__abs_path(gc, disk->script?: "block",
330                                          libxl__xen_script_dir_path());
331                 flexarray_append_pair(back, "script", script);
332 
333                 assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
334                 break;
335 
336             case LIBXL_DISK_BACKEND_TAP:
337                 if (dev == NULL) {
338                     dev = libxl__blktap_devpath(gc, disk->pdev_path,
339                                                 disk->format);
340                     if (!dev) {
341                         LOGD(ERROR, domid, "Failed to get blktap devpath for %p",
342                              disk->pdev_path);
343                         rc = ERROR_FAIL;
344                         goto out;
345                     }
346                 }
347                 flexarray_append(back, "tapdisk-params");
348                 flexarray_append(back, GCSPRINTF("%s:%s",
349                     libxl__device_disk_string_of_format(disk->format),
350                     disk->pdev_path));
351 
352                 /* tap backends with scripts are rejected by
353                  * libxl__device_disk_set_backend */
354                 assert(!disk->script);
355 
356                 /* now create a phy device to export the device to the guest */
357                 goto do_backend_phy;
358             case LIBXL_DISK_BACKEND_QDISK:
359                 flexarray_append(back, "params");
360                 flexarray_append(back, GCSPRINTF("%s:%s",
361                               libxl__device_disk_string_of_format(disk->format),
362                               disk->pdev_path ? : ""));
363                 if (libxl_defbool_val(disk->colo_enable)) {
364                     flexarray_append(back, "colo-host");
365                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
366                     flexarray_append(back, "colo-port");
367                     flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
368                     flexarray_append(back, "colo-export");
369                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
370                     flexarray_append(back, "active-disk");
371                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
372                     flexarray_append(back, "hidden-disk");
373                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
374                 }
375                 assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
376                 break;
377             default:
378                 LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
379                      disk->backend);
380                 rc = ERROR_INVAL;
381                 goto out;
382         }
383 
384         flexarray_append(back, "frontend-id");
385         flexarray_append(back, GCSPRINTF("%d", domid));
386         flexarray_append(back, "online");
387         flexarray_append(back, "1");
388         flexarray_append(back, "removable");
389         flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
390         flexarray_append(back, "bootable");
391         flexarray_append(back, GCSPRINTF("%d", 1));
392         flexarray_append(back, "state");
393         flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
394         flexarray_append(back, "dev");
395         flexarray_append(back, disk->vdev);
396         flexarray_append(back, "type");
397         flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
398         flexarray_append(back, "mode");
399         flexarray_append(back, disk->readwrite ? "w" : "r");
400         flexarray_append(back, "device-type");
401         flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
402         if (disk->direct_io_safe) {
403             flexarray_append(back, "direct-io-safe");
404             flexarray_append(back, "1");
405         }
406         flexarray_append_pair(back, "discard-enable",
407                               libxl_defbool_val(disk->discard_enable) ?
408                               "1" : "0");
409 
410         flexarray_append(front, "backend-id");
411         flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
412         flexarray_append(front, "state");
413         flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
414         flexarray_append(front, "virtual-device");
415         flexarray_append(front, GCSPRINTF("%d", device->devid));
416         flexarray_append(front, "device-type");
417         flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
418 
419         /*
420          * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
421          * write disk native protocol to frontend node. Xend does this, port
422          * this behaviour to xl.
423          *
424          * New kernels write this node themselves. In that case it just
425          * overwrites an existing node which is OK.
426          */
427         if (type == LIBXL_DOMAIN_TYPE_PV) {
428             const char *protocol =
429                 xc_domain_get_native_protocol(ctx->xch, domid);
430             if (protocol) {
431                 flexarray_append(front, "protocol");
432                 flexarray_append(front, libxl__strdup(gc, protocol));
433             }
434         }
435 
436         if (!get_vdev && aodev->update_json) {
437             rc = libxl__set_domain_configuration(gc, domid, &d_config);
438             if (rc) goto out;
439         }
440 
441         libxl__device_generic_add(gc, t, device,
442                                   libxl__xs_kvs_of_flexarray(gc, back),
443                                   libxl__xs_kvs_of_flexarray(gc, front),
444                                   NULL);
445 
446         rc = libxl__xs_transaction_commit(gc, &t);
447         if (!rc) break;
448         if (rc < 0) goto out;
449     }
450 
451     aodev->dev = device;
452     aodev->action = LIBXL__DEVICE_ACTION_ADD;
453     libxl__wait_device_connection(egc, aodev);
454 
455     rc = 0;
456 
457 out:
458     libxl__xs_transaction_abort(gc, &t);
459     if (lock) libxl__unlock_domain_userdata(lock);
460     libxl_device_disk_dispose(&disk_saved);
461     libxl_domain_config_dispose(&d_config);
462     aodev->rc = rc;
463     if (rc) aodev->callback(egc, aodev);
464     return;
465 }
466 
libxl__device_disk_add(libxl__egc * egc,uint32_t domid,libxl_device_disk * disk,libxl__ao_device * aodev)467 static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
468                                    libxl_device_disk *disk,
469                                    libxl__ao_device *aodev)
470 {
471     device_disk_add(egc, domid, disk, aodev, NULL, NULL);
472 }
473 
libxl__disk_from_xenstore(libxl__gc * gc,const char * libxl_path,libxl_devid devid,libxl_device_disk * disk)474 static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
475                                      libxl_devid devid,
476                                      libxl_device_disk *disk)
477 {
478     libxl_ctx *ctx = libxl__gc_owner(gc);
479     unsigned int len;
480     char *tmp;
481     int rc;
482 
483     const char *backend_path;
484     rc = libxl__xs_read_checked(gc, XBT_NULL,
485                                 GCSPRINTF("%s/backend", libxl_path),
486                                 &backend_path);
487     if (rc) goto out;
488 
489     if (!backend_path) {
490         LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
491         rc = ERROR_FAIL;
492         goto out;
493     }
494 
495     rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
496     if (rc) {
497         LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
498         goto out;
499     }
500 
501     /*
502      * "params" may not be present; but everything else must be.
503      * colo releated entries(colo-host, colo-port, colo-export,
504      * active-disk and hidden-disk) are present only if colo is
505      * enabled.
506      */
507     tmp = xs_read(ctx->xsh, XBT_NULL,
508                   GCSPRINTF("%s/params", libxl_path), &len);
509     if (tmp && strchr(tmp, ':')) {
510         disk->pdev_path = strdup(strchr(tmp, ':') + 1);
511         free(tmp);
512     } else {
513         disk->pdev_path = tmp;
514     }
515 
516     tmp = xs_read(ctx->xsh, XBT_NULL,
517                   GCSPRINTF("%s/colo-host", libxl_path), &len);
518     if (tmp) {
519         libxl_defbool_set(&disk->colo_enable, true);
520         disk->colo_host = tmp;
521 
522         tmp = xs_read(ctx->xsh, XBT_NULL,
523                       GCSPRINTF("%s/colo-port", libxl_path), &len);
524         if (!tmp) {
525             LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
526             goto cleanup;
527         }
528         disk->colo_port = atoi(tmp);
529 
530 #define XS_READ_COLO(param, item) do {                                  \
531         tmp = xs_read(ctx->xsh, XBT_NULL,                               \
532                       GCSPRINTF("%s/"#param"", libxl_path), &len);         \
533         if (!tmp) {                                                     \
534             LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path);   \
535             goto cleanup;                                               \
536         }                                                               \
537         disk->item = tmp;                                               \
538 } while (0)
539         XS_READ_COLO(colo-export, colo_export);
540         XS_READ_COLO(active-disk, active_disk);
541         XS_READ_COLO(hidden-disk, hidden_disk);
542 #undef XS_READ_COLO
543     } else {
544         libxl_defbool_set(&disk->colo_enable, false);
545     }
546 
547     tmp = libxl__xs_read(gc, XBT_NULL,
548                          GCSPRINTF("%s/type", libxl_path));
549     if (!tmp) {
550         LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
551         goto cleanup;
552     }
553     libxl_string_to_backend(ctx, tmp, &(disk->backend));
554 
555     disk->vdev = xs_read(ctx->xsh, XBT_NULL,
556                          GCSPRINTF("%s/dev", libxl_path), &len);
557     if (!disk->vdev) {
558         LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
559         goto cleanup;
560     }
561 
562     tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
563                          (gc, "%s/removable", libxl_path));
564     if (!tmp) {
565         LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
566         goto cleanup;
567     }
568     disk->removable = atoi(tmp);
569 
570     tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
571     if (!tmp) {
572         LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
573         goto cleanup;
574     }
575     if (!strcmp(tmp, "w"))
576         disk->readwrite = 1;
577     else
578         disk->readwrite = 0;
579 
580     tmp = libxl__xs_read(gc, XBT_NULL,
581                          GCSPRINTF("%s/device-type", libxl_path));
582     if (!tmp) {
583         LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
584         goto cleanup;
585     }
586     disk->is_cdrom = !strcmp(tmp, "cdrom");
587 
588     disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
589 
590     return 0;
591 cleanup:
592     rc = ERROR_FAIL;
593  out:
594     libxl_device_disk_dispose(disk);
595     return rc;
596 }
597 
libxl_vdev_to_device_disk(libxl_ctx * ctx,uint32_t domid,const char * vdev,libxl_device_disk * disk)598 int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
599                               const char *vdev, libxl_device_disk *disk)
600 {
601     GC_INIT(ctx);
602     char *dom_xl_path, *libxl_path;
603     int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
604     int rc = ERROR_FAIL;
605 
606     if (devid < 0)
607         return ERROR_INVAL;
608 
609     libxl_device_disk_init(disk);
610 
611     dom_xl_path = libxl__xs_libxl_path(gc, domid);
612     if (!dom_xl_path) {
613         goto out;
614     }
615     libxl_path = GCSPRINTF("%s/device/vbd/%d", dom_xl_path, devid);
616 
617     rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
618 out:
619     GC_FREE;
620     return rc;
621 }
622 
libxl_device_disk_getinfo(libxl_ctx * ctx,uint32_t domid,libxl_device_disk * disk,libxl_diskinfo * diskinfo)623 int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
624                               libxl_device_disk *disk, libxl_diskinfo *diskinfo)
625 {
626     GC_INIT(ctx);
627     char *dompath, *fe_path, *libxl_path;
628     char *val;
629     int rc;
630 
631     diskinfo->backend = NULL;
632 
633     dompath = libxl__xs_get_dompath(gc, domid);
634     diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
635 
636     /* tap devices entries in xenstore are written as vbd devices. */
637     fe_path = GCSPRINTF("%s/device/vbd/%d", dompath, diskinfo->devid);
638     libxl_path = GCSPRINTF("%s/device/vbd/%d",
639                            libxl__xs_libxl_path(gc, domid), diskinfo->devid);
640     diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
641                                 GCSPRINTF("%s/backend", libxl_path), NULL);
642     if (!diskinfo->backend) {
643         GC_FREE;
644         return ERROR_FAIL;
645     }
646     rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
647                                         &diskinfo->backend_id);
648     if (rc) goto out;
649 
650     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
651     diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
652     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
653     diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
654     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
655     diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
656     diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
657                                  GCSPRINTF("%s/frontend", libxl_path), NULL);
658     diskinfo->frontend_id = domid;
659 
660     GC_FREE;
661     return 0;
662 
663  out:
664     free(diskinfo->backend);
665     return rc;
666 }
667 
libxl_cdrom_insert(libxl_ctx * ctx,uint32_t domid,libxl_device_disk * disk,const libxl_asyncop_how * ao_how)668 int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
669                        const libxl_asyncop_how *ao_how)
670 {
671     AO_CREATE(ctx, domid, ao_how);
672     int num = 0, i;
673     libxl_device_disk *disks = NULL, disk_saved, disk_empty;
674     libxl_domain_config d_config;
675     int rc, dm_ver;
676     libxl__device device;
677     const char *be_path, *libxl_path;
678     char * tmp;
679     libxl__domain_userdata_lock *lock = NULL;
680     xs_transaction_t t = XBT_NULL;
681     flexarray_t *insert = NULL, *empty = NULL;
682 
683     libxl_domain_config_init(&d_config);
684     libxl_device_disk_init(&disk_empty);
685     libxl_device_disk_init(&disk_saved);
686     libxl_device_disk_copy(ctx, &disk_saved, disk);
687 
688     disk_empty.format = LIBXL_DISK_FORMAT_EMPTY;
689     disk_empty.vdev = libxl__strdup(NOGC, disk->vdev);
690     disk_empty.pdev_path = libxl__strdup(NOGC, "");
691     disk_empty.is_cdrom = 1;
692     libxl__device_disk_setdefault(gc, domid, &disk_empty, false);
693 
694     libxl_domain_type type = libxl__domain_type(gc, domid);
695     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
696         rc = ERROR_FAIL;
697         goto out;
698     }
699     if (type != LIBXL_DOMAIN_TYPE_HVM) {
700         LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
701         rc = ERROR_INVAL;
702         goto out;
703     }
704 
705     if (libxl_get_stubdom_id(ctx, domid) != 0) {
706         LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
707         rc = ERROR_INVAL;
708         goto out;
709     }
710 
711     dm_ver = libxl__device_model_version_running(gc, domid);
712     if (dm_ver == -1) {
713         LOGD(ERROR, domid, "Cannot determine device model version");
714         rc = ERROR_FAIL;
715         goto out;
716     }
717 
718     disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
719     for (i = 0; i < num; i++) {
720         if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
721         {
722             /* Found.  Set backend type appropriately. */
723             disk->backend=disks[i].backend;
724             break;
725         }
726     }
727     if (i == num) {
728         LOGD(ERROR, domid, "Virtual device not found");
729         rc = ERROR_FAIL;
730         goto out;
731     }
732 
733     rc = libxl__device_disk_setdefault(gc, domid, disk, false);
734     if (rc) goto out;
735 
736     if (!disk->pdev_path) {
737         disk->pdev_path = libxl__strdup(NOGC, "");
738         disk->format = LIBXL_DISK_FORMAT_EMPTY;
739     }
740 
741     rc = libxl__device_from_disk(gc, domid, disk, &device);
742     if (rc) goto out;
743 
744     be_path = libxl__device_backend_path(gc, &device);
745     libxl_path = libxl__device_libxl_path(gc, &device);
746 
747     insert = flexarray_make(gc, 4, 1);
748 
749     flexarray_append_pair(insert, "type",
750                           libxl__device_disk_string_of_backend(disk->backend));
751     if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
752         flexarray_append_pair(insert, "params",
753                         GCSPRINTF("%s:%s",
754                             libxl__device_disk_string_of_format(disk->format),
755                             disk->pdev_path));
756     else
757         flexarray_append_pair(insert, "params", "");
758 
759     empty = flexarray_make(gc, 4, 1);
760     flexarray_append_pair(empty, "type",
761                           libxl__device_disk_string_of_backend(disk->backend));
762     flexarray_append_pair(empty, "params", "");
763 
764     /* Note: CTX lock is already held at this point so lock hierarchy
765      * is maintained.
766      */
767     lock = libxl__lock_domain_userdata(gc, domid);
768     if (!lock) {
769         rc = ERROR_LOCK_FAIL;
770         goto out;
771     }
772 
773     /* We need to eject the original image first. This is implemented
774      * by inserting empty media. JSON is not updated.
775      */
776     if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
777         rc = libxl__qmp_insert_cdrom(gc, domid, &disk_empty);
778         if (rc) goto out;
779     }
780 
781     for (;;) {
782         rc = libxl__xs_transaction_start(gc, &t);
783         if (rc) goto out;
784         /* Sanity check: make sure the device exists before writing here */
785         tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
786         if (!tmp)
787         {
788             LOGD(ERROR, domid, "Internal error: %s does not exist",
789                  GCSPRINTF("%s/frontend", libxl_path));
790             rc = ERROR_FAIL;
791             goto out;
792         }
793 
794         char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
795 
796         rc = libxl__xs_writev(gc, t, be_path, kvs);
797         if (rc) goto out;
798 
799         rc = libxl__xs_writev(gc, t, libxl_path, kvs);
800         if (rc) goto out;
801 
802         rc = libxl__xs_transaction_commit(gc, &t);
803         if (!rc) break;
804         if (rc < 0) goto out;
805     }
806 
807     rc = libxl__get_domain_configuration(gc, domid, &d_config);
808     if (rc) goto out;
809 
810     device_add_domain_config(gc, &d_config, &libxl__disk_devtype, &disk_saved);
811 
812     rc = libxl__dm_check_start(gc, &d_config, domid);
813     if (rc) goto out;
814 
815     if (dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
816         rc = libxl__qmp_insert_cdrom(gc, domid, disk);
817         if (rc) goto out;
818     }
819 
820     for (;;) {
821         rc = libxl__xs_transaction_start(gc, &t);
822         if (rc) goto out;
823         /* Sanity check: make sure the device exists before writing here */
824         tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
825         if (!tmp)
826         {
827             LOGD(ERROR, domid, "Internal error: %s does not exist",
828                  GCSPRINTF("%s/frontend", libxl_path));
829             rc = ERROR_FAIL;
830             goto out;
831         }
832 
833         rc = libxl__set_domain_configuration(gc, domid, &d_config);
834         if (rc) goto out;
835 
836         char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
837 
838         rc = libxl__xs_writev(gc, t, be_path, kvs);
839         if (rc) goto out;
840 
841         rc = libxl__xs_writev(gc, t, libxl_path, kvs);
842         if (rc) goto out;
843 
844         rc = libxl__xs_transaction_commit(gc, &t);
845         if (!rc) break;
846         if (rc < 0) goto out;
847     }
848 
849     /* success, no actual async */
850     libxl__ao_complete(egc, ao, 0);
851 
852     rc = 0;
853 
854 out:
855     libxl__xs_transaction_abort(gc, &t);
856     libxl__device_list_free(&libxl__disk_devtype, disks, num);
857     libxl_device_disk_dispose(&disk_empty);
858     libxl_device_disk_dispose(&disk_saved);
859     libxl_domain_config_dispose(&d_config);
860 
861     if (lock) libxl__unlock_domain_userdata(lock);
862 
863     if (rc) return AO_CREATE_FAIL(rc);
864     return AO_INPROGRESS;
865 }
866 
867 /* libxl__alloc_vdev only works on the local domain, that is the domain
868  * where the toolstack is running */
libxl__alloc_vdev(libxl__gc * gc,void * get_vdev_user,xs_transaction_t t)869 static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
870         xs_transaction_t t)
871 {
872     const char *blkdev_start = (const char *) get_vdev_user;
873     int devid = 0, disk = 0, part = 0;
874     char *libxl_dom_path = libxl__xs_libxl_path(gc, LIBXL_TOOLSTACK_DOMID);
875 
876     libxl__device_disk_dev_number(blkdev_start, &disk, &part);
877     if (part != 0) {
878         LOG(ERROR, "blkdev_start is invalid");
879         return NULL;
880     }
881 
882     do {
883         devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
884                 NULL, NULL);
885         if (devid < 0)
886             return NULL;
887         if (libxl__xs_read(gc, t,
888                     GCSPRINTF("%s/device/vbd/%d/backend",
889                         libxl_dom_path, devid)) == NULL) {
890             if (errno == ENOENT)
891                 return libxl__devid_to_vdev(gc, devid);
892             else
893                 return NULL;
894         }
895         disk++;
896     } while (1);
897     return NULL;
898 }
899 
900 /* Callbacks */
901 
libxl__device_disk_find_local_path(libxl__gc * gc,libxl_domid guest_domid,const libxl_device_disk * disk,bool qdisk_direct)902 char *libxl__device_disk_find_local_path(libxl__gc *gc,
903                                           libxl_domid guest_domid,
904                                           const libxl_device_disk *disk,
905                                           bool qdisk_direct)
906 {
907     char *path = NULL;
908 
909     /* No local paths for driver domains */
910     if (disk->backend_domname != NULL) {
911         LOG(DEBUG, "Non-local backend, can't access locally.\n");
912         goto out;
913     }
914 
915     /*
916      * If this is in raw format, and we're not using a script or a
917      * driver domain, we can access the target path directly.
918      */
919     if (disk->format == LIBXL_DISK_FORMAT_RAW
920         && disk->script == NULL) {
921         path = libxl__strdup(gc, disk->pdev_path);
922         LOG(DEBUG, "Directly accessing local RAW disk %s", path);
923         goto out;
924     }
925 
926     /*
927      * If we're being called for a qemu path, we can pass the target
928      * string directly as well
929      */
930     if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
931         path = libxl__strdup(gc, disk->pdev_path);
932         LOG(DEBUG, "Directly accessing local QDISK target %s", path);
933         goto out;
934     }
935 
936     /*
937      * If the format isn't raw and / or we're using a script, then see
938      * if the script has written a path to the "cooked" node
939      */
940     if (disk->script && guest_domid != INVALID_DOMID) {
941         libxl__device device;
942         char *be_path, *pdpath;
943         int rc;
944 
945         LOGD(DEBUG, guest_domid,
946              "Run from a script; checking for physical-device-path (vdev %s)",
947              disk->vdev);
948 
949         rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
950         if (rc < 0)
951             goto out;
952 
953         be_path = libxl__device_backend_path(gc, &device);
954 
955         pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
956 
957         LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
958         path = libxl__xs_read(gc, XBT_NULL, pdpath);
959 
960         if (path)
961             LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
962         else
963             LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
964 
965         goto out;
966     }
967 
968  out:
969     return path;
970 }
971 
972 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
973 
libxl__device_disk_local_initiate_attach(libxl__egc * egc,libxl__disk_local_state * dls)974 void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
975                                      libxl__disk_local_state *dls)
976 {
977     STATE_AO_GC(dls->ao);
978     int rc;
979     const libxl_device_disk *in_disk = dls->in_disk;
980     libxl_device_disk *disk = &dls->disk;
981     const char *blkdev_start = dls->blkdev_start;
982 
983     assert(in_disk->pdev_path);
984 
985     disk->vdev = NULL;
986 
987     if (dls->diskpath)
988         LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
989 
990     LOG(DEBUG, "Trying to find local path");
991 
992     dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
993                                                        in_disk, false);
994     if (dls->diskpath) {
995         LOG(DEBUG, "Local path found, executing callback.");
996         dls->callback(egc, dls, 0);
997     } else {
998         LOG(DEBUG, "Local path not found, initiating attach.");
999 
1000         memcpy(disk, in_disk, sizeof(libxl_device_disk));
1001         disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
1002         if (in_disk->script != NULL)
1003             disk->script = libxl__strdup(gc, in_disk->script);
1004         disk->vdev = NULL;
1005 
1006         rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
1007                                            false);
1008         if (rc) goto out;
1009 
1010         libxl__prepare_ao_device(ao, &dls->aodev);
1011         dls->aodev.callback = local_device_attach_cb;
1012         device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
1013                         libxl__alloc_vdev, (void *) blkdev_start);
1014     }
1015 
1016     return;
1017 
1018  out:
1019     assert(rc);
1020     dls->rc = rc;
1021     libxl__device_disk_local_initiate_detach(egc, dls);
1022     dls->callback(egc, dls, rc);
1023 }
1024 
local_device_attach_cb(libxl__egc * egc,libxl__ao_device * aodev)1025 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1026 {
1027     STATE_AO_GC(aodev->ao);
1028     libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1029     char *be_path = NULL;
1030     int rc;
1031     libxl__device device;
1032     libxl_device_disk *disk = &dls->disk;
1033 
1034     rc = aodev->rc;
1035     if (rc) {
1036         LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
1037         goto out;
1038     }
1039 
1040     rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
1041     if (rc < 0)
1042         goto out;
1043     be_path = libxl__device_backend_path(gc, &device);
1044     rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
1045     if (rc < 0)
1046         goto out;
1047 
1048     dls->diskpath = GCSPRINTF("/dev/%s",
1049                               libxl__devid_to_localdev(gc, device.devid));
1050     LOG(DEBUG, "locally attached disk %s", dls->diskpath);
1051 
1052     dls->callback(egc, dls, 0);
1053     return;
1054 
1055  out:
1056     assert(rc);
1057     dls->rc = rc;
1058     libxl__device_disk_local_initiate_detach(egc, dls);
1059     return;
1060 }
1061 
1062 /* Callbacks for local detach */
1063 
1064 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
1065 
libxl__device_disk_local_initiate_detach(libxl__egc * egc,libxl__disk_local_state * dls)1066 void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
1067                                      libxl__disk_local_state *dls)
1068 {
1069     STATE_AO_GC(dls->ao);
1070     int rc = 0;
1071     libxl_device_disk *disk = &dls->disk;
1072     libxl__device *device;
1073     libxl__ao_device *aodev = &dls->aodev;
1074     libxl__prepare_ao_device(ao, aodev);
1075 
1076     if (!dls->diskpath) goto out;
1077 
1078     if (disk->vdev != NULL) {
1079         GCNEW(device);
1080         rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
1081                                      disk, device);
1082         if (rc != 0) goto out;
1083 
1084         aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
1085         aodev->dev = device;
1086         aodev->callback = local_device_detach_cb;
1087         aodev->force = 0;
1088         libxl__initiate_device_generic_remove(egc, aodev);
1089         return;
1090     }
1091 
1092 out:
1093     aodev->rc = rc;
1094     local_device_detach_cb(egc, aodev);
1095     return;
1096 }
1097 
local_device_detach_cb(libxl__egc * egc,libxl__ao_device * aodev)1098 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1099 {
1100     STATE_AO_GC(aodev->ao);
1101     libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1102     int rc;
1103 
1104     if (aodev->rc) {
1105         LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
1106                      libxl__device_action_to_string(aodev->action),
1107                      libxl__device_kind_to_string(aodev->dev->kind),
1108                      aodev->dev->devid);
1109         goto out;
1110     }
1111 
1112 out:
1113     /*
1114      * If there was an error in dls->rc, it means we have been called from
1115      * a failed execution of libxl__device_disk_local_initiate_attach,
1116      * so return the original error.
1117      */
1118     rc = dls->rc ? dls->rc : aodev->rc;
1119     dls->callback(egc, dls, rc);
1120     return;
1121 }
1122 
1123 /* The following functions are defined:
1124  * libxl_device_disk_add
1125  * libxl__add_disks
1126  * libxl_device_disk_remove
1127  * libxl_device_disk_destroy
1128  */
1129 LIBXL_DEFINE_DEVICE_ADD(disk)
LIBXL_DEFINE_DEVICES_ADD(disk)1130 LIBXL_DEFINE_DEVICES_ADD(disk)
1131 LIBXL_DEFINE_DEVICE_REMOVE(disk)
1132 
1133 static int libxl_device_disk_compare(libxl_device_disk *d1,
1134                                      libxl_device_disk *d2)
1135 {
1136     return COMPARE_DISK(d1, d2);
1137 }
1138 
1139 /* Take care of removable device. We maintain invariant in the
1140  * insert / remove operation so that:
1141  * 1. if xenstore is "empty" while JSON is not, the result
1142  *    is "empty"
1143  * 2. if xenstore has a different media than JSON, use the
1144  *    one in JSON
1145  * 3. if xenstore and JSON have the same media, well, you
1146  *    know the answer :-)
1147  *
1148  * Currently there is only one removable device -- CDROM.
1149  * Look for libxl_cdrom_insert for reference.
1150  */
libxl_device_disk_merge(libxl_ctx * ctx,void * d1,void * d2)1151 static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
1152 {
1153     GC_INIT(ctx);
1154     libxl_device_disk *src = d1;
1155     libxl_device_disk *dst = d2;
1156 
1157     if (src->removable) {
1158         if (!src->pdev_path || *src->pdev_path == '\0') {
1159             /* 1, no media in drive */
1160             free(dst->pdev_path);
1161             dst->pdev_path = libxl__strdup(NOGC, "");
1162             dst->format = LIBXL_DISK_FORMAT_EMPTY;
1163         } else {
1164             /* 2 and 3, use JSON, no need to touch anything */
1165             ;
1166         }
1167     }
1168 }
1169 
libxl_device_disk_dm_needed(void * e,unsigned domid)1170 static int libxl_device_disk_dm_needed(void *e, unsigned domid)
1171 {
1172     libxl_device_disk *elem = e;
1173 
1174     return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
1175            elem->backend_domid == domid;
1176 }
1177 
1178 LIBXL_DEFINE_DEVICE_LIST(disk)
1179 
1180 #define libxl__device_disk_update_devid NULL
1181 
1182 DEFINE_DEVICE_TYPE_STRUCT_X(disk, disk, vbd,
1183     .merge       = libxl_device_disk_merge,
1184     .dm_needed   = libxl_device_disk_dm_needed,
1185     .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
1186     .skip_attach = 1,
1187 );
1188 
1189 /*
1190  * Local variables:
1191  * mode: C
1192  * c-basic-offset: 4
1193  * indent-tabs-mode: nil
1194  * End:
1195  */
1196