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(gc, "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(gc, "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/eject",
101                     libxl__domain_device_frontend_path(gc, domid, devid,
102                     LIBXL__DEVICE_KIND_VBD));
103     if (!path) { rc = ERROR_NOMEM; goto out; }
104 
105     const char *libxl_path = libxl__domain_device_frontend_path(gc, domid, devid,
106                                                                 LIBXL__DEVICE_KIND_VBD);
107     evg->be_ptr_path = libxl__sprintf(NOGC, "%s/backend", libxl_path);
108 
109     const char *configured_vdev;
110     rc = libxl__xs_read_checked(gc, XBT_NULL,
111             GCSPRINTF("%s/dev", libxl_path), &configured_vdev);
112     if (rc) goto out;
113 
114     evg->vdev = libxl__strdup(NOGC, configured_vdev);
115 
116     rc = libxl__ev_xswatch_register(gc, &evg->watch,
117                                     disk_eject_xswatch_callback, path);
118     if (rc) goto out;
119 
120     *evgen_out = evg;
121     CTX_UNLOCK;
122     GC_FREE;
123     return 0;
124 
125  out:
126     if (evg)
127         libxl__evdisable_disk_eject(gc, evg);
128     CTX_UNLOCK;
129     GC_FREE;
130     return rc;
131 }
132 
libxl__evdisable_disk_eject(libxl__gc * gc,libxl_evgen_disk_eject * evg)133 void libxl__evdisable_disk_eject(libxl__gc *gc, libxl_evgen_disk_eject *evg) {
134     CTX_LOCK;
135 
136     LIBXL_LIST_REMOVE(evg, entry);
137 
138     if (libxl__ev_xswatch_isregistered(&evg->watch))
139         libxl__ev_xswatch_deregister(gc, &evg->watch);
140 
141     free(evg->vdev);
142     free(evg->be_ptr_path);
143     free(evg);
144 
145     CTX_UNLOCK;
146 }
147 
libxl_evdisable_disk_eject(libxl_ctx * ctx,libxl_evgen_disk_eject * evg)148 void libxl_evdisable_disk_eject(libxl_ctx *ctx, libxl_evgen_disk_eject *evg) {
149     GC_INIT(ctx);
150     libxl__evdisable_disk_eject(gc, evg);
151     GC_FREE;
152 }
153 
libxl__device_disk_setdefault(libxl__gc * gc,uint32_t domid,libxl_device_disk * disk,bool hotplug)154 static int libxl__device_disk_setdefault(libxl__gc *gc, uint32_t domid,
155                                          libxl_device_disk *disk, bool hotplug)
156 {
157     int rc;
158 
159     libxl_defbool_setdefault(&disk->discard_enable, !!disk->readwrite);
160     libxl_defbool_setdefault(&disk->colo_enable, false);
161     libxl_defbool_setdefault(&disk->colo_restore_enable, false);
162 
163     rc = libxl__resolve_domid(gc, disk->backend_domname, &disk->backend_domid);
164     if (rc < 0) return rc;
165 
166     /* Force Qdisk backend for CDROM devices of guests with a device model. */
167     if (disk->is_cdrom != 0 &&
168         libxl__domain_type(gc, domid) == LIBXL_DOMAIN_TYPE_HVM) {
169         if (!(disk->backend == LIBXL_DISK_BACKEND_QDISK ||
170               disk->backend == LIBXL_DISK_BACKEND_UNKNOWN)) {
171             LOGD(ERROR, domid, "Backend for CD devices on HVM guests must be Qdisk");
172             return ERROR_FAIL;
173         }
174         disk->backend = LIBXL_DISK_BACKEND_QDISK;
175     }
176 
177     rc = libxl__device_disk_set_backend(gc, disk);
178     return rc;
179 }
180 
libxl__device_from_disk(libxl__gc * gc,uint32_t domid,const libxl_device_disk * disk,libxl__device * device)181 static int libxl__device_from_disk(libxl__gc *gc, uint32_t domid,
182                                    const libxl_device_disk *disk,
183                                    libxl__device *device)
184 {
185     int devid;
186 
187     devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
188     if (devid==-1) {
189         LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
190              disk->vdev);
191         return ERROR_INVAL;
192     }
193 
194     device->backend_domid = disk->backend_domid;
195     device->backend_devid = devid;
196 
197     switch (disk->backend) {
198         case LIBXL_DISK_BACKEND_PHY:
199             device->backend_kind = LIBXL__DEVICE_KIND_VBD;
200             break;
201         case LIBXL_DISK_BACKEND_TAP:
202             device->backend_kind = LIBXL__DEVICE_KIND_VBD;
203             break;
204         case LIBXL_DISK_BACKEND_QDISK:
205             device->backend_kind = LIBXL__DEVICE_KIND_QDISK;
206             break;
207         default:
208             LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
209                  disk->backend);
210             return ERROR_INVAL;
211     }
212 
213     device->domid = domid;
214     device->devid = devid;
215     device->kind  = LIBXL__DEVICE_KIND_VBD;
216 
217     return 0;
218 }
219 
220 /* Specific function called directly only by local disk attach,
221  * all other users should instead use the regular
222  * libxl__device_disk_add wrapper
223  *
224  * The (optionally) passed function get_vdev will be used to
225  * set the vdev the disk should be attached to. When it is set the caller
226  * must also pass get_vdev_user, which will be passed to get_vdev.
227  *
228  * The passed get_vdev function is also in charge of printing
229  * the corresponding error message when appropiate.
230  */
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)231 static void device_disk_add(libxl__egc *egc, uint32_t domid,
232                            libxl_device_disk *disk,
233                            libxl__ao_device *aodev,
234                            char *get_vdev(libxl__gc *, void *,
235                                           xs_transaction_t),
236                            void *get_vdev_user)
237 {
238     STATE_AO_GC(aodev->ao);
239     flexarray_t *front = NULL;
240     flexarray_t *back = NULL;
241     char *dev = NULL, *script;
242     libxl__device *device;
243     int rc;
244     libxl_ctx *ctx = gc->owner;
245     xs_transaction_t t = XBT_NULL;
246     libxl_domain_config d_config;
247     libxl_device_disk disk_saved;
248     libxl__flock *lock = NULL;
249 
250     libxl_domain_config_init(&d_config);
251     libxl_device_disk_init(&disk_saved);
252     libxl_device_disk_copy(ctx, &disk_saved, disk);
253 
254     libxl_domain_type type = libxl__domain_type(gc, domid);
255     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
256         rc = ERROR_FAIL;
257         goto out;
258     }
259 
260     /*
261      * get_vdev != NULL -> local attach
262      * get_vdev == NULL -> block attach
263      *
264      * We don't care about local attach state because it's only
265      * intermediate state.
266      */
267     if (!get_vdev && aodev->update_json) {
268         lock = libxl__lock_domain_userdata(gc, domid);
269         if (!lock) {
270             rc = ERROR_LOCK_FAIL;
271             goto out;
272         }
273 
274         rc = libxl__get_domain_configuration(gc, domid, &d_config);
275         if (rc) goto out;
276 
277         device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
278                                  &disk_saved);
279 
280         rc = libxl__dm_check_start(gc, &d_config, domid);
281         if (rc) goto out;
282     }
283 
284     for (;;) {
285         rc = libxl__xs_transaction_start(gc, &t);
286         if (rc) goto out;
287 
288         if (get_vdev) {
289             assert(get_vdev_user);
290             disk->vdev = get_vdev(gc, get_vdev_user, t);
291             if (disk->vdev == NULL) {
292                 rc = ERROR_FAIL;
293                 goto out;
294             }
295         }
296 
297         rc = libxl__device_disk_setdefault(gc, domid, disk, aodev->update_json);
298         if (rc) goto out;
299 
300         front = flexarray_make(gc, 16, 1);
301         back = flexarray_make(gc, 16, 1);
302 
303         GCNEW(device);
304         rc = libxl__device_from_disk(gc, domid, disk, device);
305         if (rc != 0) {
306             LOGD(ERROR, domid, "Invalid or unsupported"" virtual disk identifier %s",
307                  disk->vdev);
308             goto out;
309         }
310 
311         rc = libxl__device_exists(gc, t, device);
312         if (rc < 0) goto out;
313         if (rc == 1) {              /* already exists in xenstore */
314             LOGD(ERROR, domid, "device already exists in xenstore");
315             aodev->action = LIBXL__DEVICE_ACTION_ADD; /* for error message */
316             rc = ERROR_DEVICE_EXISTS;
317             goto out;
318         }
319 
320         switch (disk->backend) {
321             case LIBXL_DISK_BACKEND_PHY:
322                 dev = disk->pdev_path;
323 
324                 flexarray_append(back, "params");
325                 flexarray_append(back, dev);
326 
327                 script = libxl__abs_path(gc, disk->script?: "block",
328                                          libxl__xen_script_dir_path());
329                 flexarray_append_pair(back, "script", script);
330 
331                 assert(device->backend_kind == LIBXL__DEVICE_KIND_VBD);
332                 break;
333 
334             case LIBXL_DISK_BACKEND_TAP:
335                 LOG(ERROR, "blktap is not supported");
336                 rc = ERROR_FAIL;
337                 goto out;
338             case LIBXL_DISK_BACKEND_QDISK:
339                 flexarray_append(back, "params");
340                 flexarray_append(back, GCSPRINTF("%s:%s",
341                               libxl__device_disk_string_of_format(disk->format),
342                               disk->pdev_path ? : ""));
343                 if (libxl_defbool_val(disk->colo_enable)) {
344                     flexarray_append(back, "colo-host");
345                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_host));
346                     flexarray_append(back, "colo-port");
347                     flexarray_append(back, libxl__sprintf(gc, "%d", disk->colo_port));
348                     flexarray_append(back, "colo-export");
349                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->colo_export));
350                     flexarray_append(back, "active-disk");
351                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->active_disk));
352                     flexarray_append(back, "hidden-disk");
353                     flexarray_append(back, libxl__sprintf(gc, "%s", disk->hidden_disk));
354                 }
355                 assert(device->backend_kind == LIBXL__DEVICE_KIND_QDISK);
356                 break;
357             default:
358                 LOGD(ERROR, domid, "Unrecognized disk backend type: %d",
359                      disk->backend);
360                 rc = ERROR_INVAL;
361                 goto out;
362         }
363 
364         flexarray_append(back, "frontend-id");
365         flexarray_append(back, GCSPRINTF("%d", domid));
366         flexarray_append(back, "online");
367         flexarray_append(back, "1");
368         flexarray_append(back, "removable");
369         flexarray_append(back, GCSPRINTF("%d", (disk->removable) ? 1 : 0));
370         flexarray_append(back, "bootable");
371         flexarray_append(back, GCSPRINTF("%d", 1));
372         flexarray_append(back, "state");
373         flexarray_append(back, GCSPRINTF("%d", XenbusStateInitialising));
374         flexarray_append(back, "dev");
375         flexarray_append(back, disk->vdev);
376         flexarray_append(back, "type");
377         flexarray_append(back, libxl__device_disk_string_of_backend(disk->backend));
378         flexarray_append(back, "mode");
379         flexarray_append(back, disk->readwrite ? "w" : "r");
380         flexarray_append(back, "device-type");
381         flexarray_append(back, disk->is_cdrom ? "cdrom" : "disk");
382         if (disk->direct_io_safe) {
383             flexarray_append(back, "direct-io-safe");
384             flexarray_append(back, "1");
385         }
386         flexarray_append_pair(back, "discard-enable",
387                               libxl_defbool_val(disk->discard_enable) ?
388                               "1" : "0");
389 
390         flexarray_append(front, "backend-id");
391         flexarray_append(front, GCSPRINTF("%d", disk->backend_domid));
392         flexarray_append(front, "state");
393         flexarray_append(front, GCSPRINTF("%d", XenbusStateInitialising));
394         flexarray_append(front, "virtual-device");
395         flexarray_append(front, GCSPRINTF("%d", device->devid));
396         flexarray_append(front, "device-type");
397         flexarray_append(front, disk->is_cdrom ? "cdrom" : "disk");
398 
399         /*
400          * Old PV kernel disk frontends before 2.6.26 rely on tool stack to
401          * write disk native protocol to frontend node. Xend does this, port
402          * this behaviour to xl.
403          *
404          * New kernels write this node themselves. In that case it just
405          * overwrites an existing node which is OK.
406          */
407         if (type == LIBXL_DOMAIN_TYPE_PV) {
408             const char *protocol =
409                 xc_domain_get_native_protocol(ctx->xch, domid);
410             if (protocol) {
411                 flexarray_append(front, "protocol");
412                 flexarray_append(front, libxl__strdup(gc, protocol));
413             }
414         }
415 
416         if (!get_vdev && aodev->update_json) {
417             rc = libxl__set_domain_configuration(gc, domid, &d_config);
418             if (rc) goto out;
419         }
420 
421         libxl__device_generic_add(gc, t, device,
422                                   libxl__xs_kvs_of_flexarray(gc, back),
423                                   libxl__xs_kvs_of_flexarray(gc, front),
424                                   NULL);
425 
426         rc = libxl__xs_transaction_commit(gc, &t);
427         if (!rc) break;
428         if (rc < 0) goto out;
429     }
430 
431     aodev->dev = device;
432     aodev->action = LIBXL__DEVICE_ACTION_ADD;
433     libxl__wait_device_connection(egc, aodev);
434 
435     rc = 0;
436 
437 out:
438     libxl__xs_transaction_abort(gc, &t);
439     if (lock) libxl__unlock_file(lock);
440     libxl_device_disk_dispose(&disk_saved);
441     libxl_domain_config_dispose(&d_config);
442     aodev->rc = rc;
443     if (rc) aodev->callback(egc, aodev);
444     return;
445 }
446 
libxl__device_disk_add(libxl__egc * egc,uint32_t domid,libxl_device_disk * disk,libxl__ao_device * aodev)447 static void libxl__device_disk_add(libxl__egc *egc, uint32_t domid,
448                                    libxl_device_disk *disk,
449                                    libxl__ao_device *aodev)
450 {
451     device_disk_add(egc, domid, disk, aodev, NULL, NULL);
452 }
453 
libxl__disk_from_xenstore(libxl__gc * gc,const char * libxl_path,libxl_devid devid,libxl_device_disk * disk)454 static int libxl__disk_from_xenstore(libxl__gc *gc, const char *libxl_path,
455                                      libxl_devid devid,
456                                      libxl_device_disk *disk)
457 {
458     libxl_ctx *ctx = libxl__gc_owner(gc);
459     unsigned int len;
460     char *tmp;
461     int rc;
462 
463     const char *backend_path;
464     rc = libxl__xs_read_checked(gc, XBT_NULL,
465                                 GCSPRINTF("%s/backend", libxl_path),
466                                 &backend_path);
467     if (rc) goto out;
468 
469     if (!backend_path) {
470         LOG(ERROR, "disk %s does not exist (no backend path", libxl_path);
471         rc = ERROR_FAIL;
472         goto out;
473     }
474 
475     rc = libxl__backendpath_parse_domid(gc, backend_path, &disk->backend_domid);
476     if (rc) {
477         LOG(ERROR, "Unable to fetch device backend domid from %s", backend_path);
478         goto out;
479     }
480 
481     /*
482      * "params" may not be present; but everything else must be.
483      * colo releated entries(colo-host, colo-port, colo-export,
484      * active-disk and hidden-disk) are present only if colo is
485      * enabled.
486      */
487     tmp = xs_read(ctx->xsh, XBT_NULL,
488                   GCSPRINTF("%s/params", libxl_path), &len);
489     if (tmp && strchr(tmp, ':')) {
490         disk->pdev_path = strdup(strchr(tmp, ':') + 1);
491         free(tmp);
492     } else {
493         disk->pdev_path = tmp;
494     }
495 
496     tmp = xs_read(ctx->xsh, XBT_NULL,
497                   GCSPRINTF("%s/colo-host", libxl_path), &len);
498     if (tmp) {
499         libxl_defbool_set(&disk->colo_enable, true);
500         disk->colo_host = tmp;
501 
502         tmp = xs_read(ctx->xsh, XBT_NULL,
503                       GCSPRINTF("%s/colo-port", libxl_path), &len);
504         if (!tmp) {
505             LOG(ERROR, "Missing xenstore node %s/colo-port", libxl_path);
506             goto cleanup;
507         }
508         disk->colo_port = atoi(tmp);
509 
510 #define XS_READ_COLO(param, item) do {                                  \
511         tmp = xs_read(ctx->xsh, XBT_NULL,                               \
512                       GCSPRINTF("%s/"#param"", libxl_path), &len);         \
513         if (!tmp) {                                                     \
514             LOG(ERROR, "Missing xenstore node %s/"#param"", libxl_path);   \
515             goto cleanup;                                               \
516         }                                                               \
517         disk->item = tmp;                                               \
518 } while (0)
519         XS_READ_COLO(colo-export, colo_export);
520         XS_READ_COLO(active-disk, active_disk);
521         XS_READ_COLO(hidden-disk, hidden_disk);
522 #undef XS_READ_COLO
523     } else {
524         libxl_defbool_set(&disk->colo_enable, false);
525     }
526 
527     tmp = libxl__xs_read(gc, XBT_NULL,
528                          GCSPRINTF("%s/type", libxl_path));
529     if (!tmp) {
530         LOG(ERROR, "Missing xenstore node %s/type", libxl_path);
531         goto cleanup;
532     }
533     libxl_string_to_backend(ctx, tmp, &(disk->backend));
534 
535     disk->vdev = xs_read(ctx->xsh, XBT_NULL,
536                          GCSPRINTF("%s/dev", libxl_path), &len);
537     if (!disk->vdev) {
538         LOG(ERROR, "Missing xenstore node %s/dev", libxl_path);
539         goto cleanup;
540     }
541 
542     tmp = libxl__xs_read(gc, XBT_NULL, libxl__sprintf
543                          (gc, "%s/removable", libxl_path));
544     if (!tmp) {
545         LOG(ERROR, "Missing xenstore node %s/removable", libxl_path);
546         goto cleanup;
547     }
548     disk->removable = atoi(tmp);
549 
550     tmp = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/mode", libxl_path));
551     if (!tmp) {
552         LOG(ERROR, "Missing xenstore node %s/mode", libxl_path);
553         goto cleanup;
554     }
555     if (!strcmp(tmp, "w"))
556         disk->readwrite = 1;
557     else
558         disk->readwrite = 0;
559 
560     tmp = libxl__xs_read(gc, XBT_NULL,
561                          GCSPRINTF("%s/device-type", libxl_path));
562     if (!tmp) {
563         LOG(ERROR, "Missing xenstore node %s/device-type", libxl_path);
564         goto cleanup;
565     }
566     disk->is_cdrom = !strcmp(tmp, "cdrom");
567 
568     disk->format = LIBXL_DISK_FORMAT_UNKNOWN;
569 
570     return 0;
571 cleanup:
572     rc = ERROR_FAIL;
573  out:
574     libxl_device_disk_dispose(disk);
575     return rc;
576 }
577 
libxl_vdev_to_device_disk(libxl_ctx * ctx,uint32_t domid,const char * vdev,libxl_device_disk * disk)578 int libxl_vdev_to_device_disk(libxl_ctx *ctx, uint32_t domid,
579                               const char *vdev, libxl_device_disk *disk)
580 {
581     GC_INIT(ctx);
582     char *libxl_path;
583     int devid = libxl__device_disk_dev_number(vdev, NULL, NULL);
584     int rc = ERROR_FAIL;
585 
586     if (devid < 0)
587         return ERROR_INVAL;
588 
589     libxl_device_disk_init(disk);
590 
591     libxl_path = libxl__domain_device_libxl_path(gc, domid, devid,
592                                                  LIBXL__DEVICE_KIND_VBD);
593 
594     rc = libxl__disk_from_xenstore(gc, libxl_path, devid, disk);
595 
596     GC_FREE;
597     return rc;
598 }
599 
libxl_device_disk_getinfo(libxl_ctx * ctx,uint32_t domid,const libxl_device_disk * disk,libxl_diskinfo * diskinfo)600 int libxl_device_disk_getinfo(libxl_ctx *ctx, uint32_t domid,
601                               const libxl_device_disk *disk,
602                               libxl_diskinfo *diskinfo)
603 {
604     GC_INIT(ctx);
605     char *fe_path, *libxl_path;
606     char *val;
607     int rc;
608 
609     diskinfo->backend = NULL;
610 
611     diskinfo->devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
612 
613     /* tap devices entries in xenstore are written as vbd devices. */
614     fe_path = libxl__domain_device_frontend_path(gc, domid, diskinfo->devid,
615                                                  LIBXL__DEVICE_KIND_VBD);
616     libxl_path = libxl__domain_device_libxl_path(gc, domid, diskinfo->devid,
617                                                  LIBXL__DEVICE_KIND_VBD);
618     diskinfo->backend = xs_read(ctx->xsh, XBT_NULL,
619                                 GCSPRINTF("%s/backend", libxl_path), NULL);
620     if (!diskinfo->backend) {
621         GC_FREE;
622         return ERROR_FAIL;
623     }
624     rc = libxl__backendpath_parse_domid(gc, diskinfo->backend,
625                                         &diskinfo->backend_id);
626     if (rc) goto out;
627 
628     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/state", fe_path));
629     diskinfo->state = val ? strtoul(val, NULL, 10) : -1;
630     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/event-channel", fe_path));
631     diskinfo->evtch = val ? strtoul(val, NULL, 10) : -1;
632     val = libxl__xs_read(gc, XBT_NULL, GCSPRINTF("%s/ring-ref", fe_path));
633     diskinfo->rref = val ? strtoul(val, NULL, 10) : -1;
634     diskinfo->frontend = xs_read(ctx->xsh, XBT_NULL,
635                                  GCSPRINTF("%s/frontend", libxl_path), NULL);
636     diskinfo->frontend_id = domid;
637 
638     GC_FREE;
639     return 0;
640 
641  out:
642     free(diskinfo->backend);
643     return rc;
644 }
645 
646 typedef struct {
647     libxl__ao *ao;
648     libxl_domid domid;
649     libxl_device_disk *disk;
650     libxl_device_disk disk_saved;
651     libxl__ev_slowlock qmp_lock;
652     int dm_ver;
653     libxl__ev_time time;
654     libxl__ev_qmp qmp;
655 } libxl__cdrom_insert_state;
656 
657 static void cdrom_insert_lock_acquired(libxl__egc *, libxl__ev_slowlock *,
658                                        int rc);
659 static void cdrom_insert_ejected(libxl__egc *egc, libxl__ev_qmp *,
660                                  const libxl__json_object *, int rc);
661 static void cdrom_insert_addfd_cb(libxl__egc *egc, libxl__ev_qmp *,
662                                   const libxl__json_object *, int rc);
663 static void cdrom_insert_inserted(libxl__egc *egc, libxl__ev_qmp *,
664                                   const libxl__json_object *, int rc);
665 static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
666                                 const struct timeval *requested_abs,
667                                 int rc);
668 static void cdrom_insert_done(libxl__egc *egc,
669                               libxl__cdrom_insert_state *cis,
670                               int rc);
671 
libxl_cdrom_insert(libxl_ctx * ctx,uint32_t domid,libxl_device_disk * disk,const libxl_asyncop_how * ao_how)672 int libxl_cdrom_insert(libxl_ctx *ctx, uint32_t domid, libxl_device_disk *disk,
673                        const libxl_asyncop_how *ao_how)
674 {
675     AO_CREATE(ctx, domid, ao_how);
676     int num = 0, i;
677     libxl_device_disk *disks = NULL;
678     int rc;
679     libxl__cdrom_insert_state *cis;
680 
681     GCNEW(cis);
682     cis->ao = ao;
683     cis->domid = domid;
684     cis->disk = disk;
685     libxl_device_disk_init(&cis->disk_saved);
686     libxl_device_disk_copy(ctx, &cis->disk_saved, disk);
687     libxl__ev_devlock_init(&cis->qmp_lock);
688     cis->qmp_lock.ao = ao;
689     cis->qmp_lock.domid = domid;
690     libxl__ev_time_init(&cis->time);
691     libxl__ev_qmp_init(&cis->qmp);
692     cis->qmp.ao = ao;
693     cis->qmp.domid = domid;
694     cis->qmp.payload_fd = -1;
695 
696     libxl_domain_type type = libxl__domain_type(gc, domid);
697     if (type == LIBXL_DOMAIN_TYPE_INVALID) {
698         rc = ERROR_FAIL;
699         goto out;
700     }
701     if (type != LIBXL_DOMAIN_TYPE_HVM) {
702         LOGD(ERROR, domid, "cdrom-insert requires an HVM domain");
703         rc = ERROR_INVAL;
704         goto out;
705     }
706 
707     if (libxl_get_stubdom_id(ctx, domid) != 0) {
708         LOGD(ERROR, domid, "cdrom-insert doesn't work for stub domains");
709         rc = ERROR_INVAL;
710         goto out;
711     }
712 
713     cis->dm_ver = libxl__device_model_version_running(gc, domid);
714     if (cis->dm_ver == -1) {
715         LOGD(ERROR, domid, "Cannot determine device model version");
716         rc = ERROR_FAIL;
717         goto out;
718     }
719 
720     disks = libxl__device_list(gc, &libxl__disk_devtype, domid, &num);
721     for (i = 0; i < num; i++) {
722         if (disks[i].is_cdrom && !strcmp(disk->vdev, disks[i].vdev))
723         {
724             /* Found.  Set backend type appropriately. */
725             disk->backend=disks[i].backend;
726             break;
727         }
728     }
729     if (i == num) {
730         LOGD(ERROR, domid, "Virtual device not found");
731         rc = ERROR_FAIL;
732         goto out;
733     }
734 
735     rc = libxl__device_disk_setdefault(gc, domid, disk, false);
736     if (rc) goto out;
737 
738     if (!disk->pdev_path) {
739         disk->pdev_path = libxl__strdup(NOGC, "");
740         disk->format = LIBXL_DISK_FORMAT_EMPTY;
741     }
742 
743 out:
744     libxl__device_list_free(&libxl__disk_devtype, disks, num);
745     if (rc) {
746         cdrom_insert_done(egc, cis, rc); /* must be last */
747     } else {
748         cis->qmp_lock.callback = cdrom_insert_lock_acquired;
749         libxl__ev_slowlock_lock(egc, &cis->qmp_lock); /* must be last */
750     }
751     return AO_INPROGRESS;
752 }
753 
cdrom_insert_lock_acquired(libxl__egc * egc,libxl__ev_slowlock * lock,int rc)754 static void cdrom_insert_lock_acquired(libxl__egc *egc,
755                                        libxl__ev_slowlock *lock,
756                                        int rc)
757 {
758     libxl__cdrom_insert_state *cis = CONTAINER_OF(lock, *cis, qmp_lock);
759     STATE_AO_GC(cis->ao);
760 
761     if (rc) goto out;
762 
763     rc = libxl__ev_time_register_rel(ao, &cis->time,
764                                      cdrom_insert_timout,
765                                      LIBXL_HOTPLUG_TIMEOUT * 1000);
766     if (rc) goto out;
767 
768     /* We need to eject the original image first.
769      * JSON is not updated.
770      */
771 
772     if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN) {
773         libxl__json_object *args = NULL;
774         int devid = libxl__device_disk_dev_number(cis->disk->vdev,
775                                                   NULL, NULL);
776 
777         QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
778         cis->qmp.callback = cdrom_insert_ejected;
779         rc = libxl__ev_qmp_send(egc, &cis->qmp, "eject", args);
780         if (rc) goto out;
781     } else {
782         cdrom_insert_ejected(egc, &cis->qmp, NULL, 0); /* must be last */
783     }
784     return;
785 
786 out:
787     cdrom_insert_done(egc, cis, rc); /* must be last */
788 }
789 
cdrom_insert_ejected(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)790 static void cdrom_insert_ejected(libxl__egc *egc,
791                                  libxl__ev_qmp *qmp,
792                                  const libxl__json_object *response,
793                                  int rc)
794 {
795     EGC_GC;
796     libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
797     libxl__flock *data_lock = NULL;
798     libxl__device device;
799     const char *be_path, *libxl_path;
800     flexarray_t *empty = NULL;
801     xs_transaction_t t = XBT_NULL;
802     char *tmp;
803     libxl_domain_config d_config;
804     bool has_callback = false;
805 
806     /* convenience aliases */
807     libxl_domid domid = cis->domid;
808     libxl_device_disk *disk = cis->disk;
809 
810     libxl_domain_config_init(&d_config);
811 
812     if (rc) goto out;
813 
814     rc = libxl__device_from_disk(gc, domid, disk, &device);
815     if (rc) goto out;
816     be_path = libxl__device_backend_path(gc, &device);
817     libxl_path = libxl__device_libxl_path(gc, &device);
818 
819     data_lock = libxl__lock_domain_userdata(gc, domid);
820     if (!data_lock) {
821         rc = ERROR_LOCK_FAIL;
822         goto out;
823     }
824 
825     empty = flexarray_make(gc, 4, 1);
826     flexarray_append_pair(empty, "type",
827                           libxl__device_disk_string_of_backend(disk->backend));
828     flexarray_append_pair(empty, "params", "");
829 
830     for (;;) {
831         rc = libxl__xs_transaction_start(gc, &t);
832         if (rc) goto out;
833         /* Sanity check: make sure the device exists before writing here */
834         tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
835         if (!tmp)
836         {
837             LOGD(ERROR, domid, "Internal error: %s does not exist",
838                  GCSPRINTF("%s/frontend", libxl_path));
839             rc = ERROR_FAIL;
840             goto out;
841         }
842 
843         char **kvs = libxl__xs_kvs_of_flexarray(gc, empty);
844 
845         rc = libxl__xs_writev(gc, t, be_path, kvs);
846         if (rc) goto out;
847 
848         rc = libxl__xs_writev(gc, t, libxl_path, kvs);
849         if (rc) goto out;
850 
851         rc = libxl__xs_transaction_commit(gc, &t);
852         if (!rc) break;
853         if (rc < 0) goto out;
854     }
855 
856     /*
857      * Now that the drive is empty, we can insert the new media.
858      */
859 
860     rc = libxl__get_domain_configuration(gc, domid, &d_config);
861     if (rc) goto out;
862 
863     device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
864                              &cis->disk_saved);
865 
866     rc = libxl__dm_check_start(gc, &d_config, domid);
867     if (rc) goto out;
868 
869     if (cis->dm_ver == LIBXL_DEVICE_MODEL_VERSION_QEMU_XEN &&
870         disk->format != LIBXL_DISK_FORMAT_EMPTY) {
871         libxl__json_object *args = NULL;
872 
873         assert(qmp->payload_fd == -1);
874         qmp->payload_fd = open(disk->pdev_path, O_RDONLY);
875         if (qmp->payload_fd < 0) {
876             LOGED(ERROR, domid, "Failed to open cdrom file %s",
877                   disk->pdev_path);
878             rc = ERROR_FAIL;
879             goto out;
880         }
881 
882         /* This free form parameter is not use by QEMU or libxl. */
883         QMP_PARAMETERS_SPRINTF(&args, "opaque", "%s:%s",
884                                libxl_disk_format_to_string(disk->format),
885                                disk->pdev_path);
886         qmp->callback = cdrom_insert_addfd_cb;
887         rc = libxl__ev_qmp_send(egc, qmp, "add-fd", args);
888         if (rc) goto out;
889         has_callback = true;
890     } else {
891         has_callback = false;
892     }
893 
894     rc = 0;
895 
896 out:
897     libxl__xs_transaction_abort(gc, &t);
898     libxl_domain_config_dispose(&d_config);
899     if (data_lock) libxl__unlock_file(data_lock);
900     if (rc) {
901         cdrom_insert_done(egc, cis, rc); /* must be last */
902     } else if (!has_callback) {
903         /* Only called if no asynchronous callback are set. */
904         cdrom_insert_inserted(egc, qmp, NULL, 0); /* must be last */
905     }
906 }
907 
cdrom_insert_addfd_cb(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)908 static void cdrom_insert_addfd_cb(libxl__egc *egc,
909                                   libxl__ev_qmp *qmp,
910                                   const libxl__json_object *response,
911                                   int rc)
912 {
913     EGC_GC;
914     libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
915     libxl__json_object *args = NULL;
916     const libxl__json_object *o;
917     int devid;
918     int fdset;
919 
920     /* convenience aliases */
921     libxl_device_disk *disk = cis->disk;
922 
923     close(qmp->payload_fd);
924     qmp->payload_fd = -1;
925 
926     if (rc) goto out;
927 
928     o = libxl__json_map_get("fdset-id", response, JSON_INTEGER);
929     if (!o) {
930         rc = ERROR_FAIL;
931         goto out;
932     }
933     fdset = libxl__json_object_get_integer(o);
934 
935     devid = libxl__device_disk_dev_number(disk->vdev, NULL, NULL);
936     QMP_PARAMETERS_SPRINTF(&args, "device", "ide-%i", devid);
937     QMP_PARAMETERS_SPRINTF(&args, "target", "/dev/fdset/%d", fdset);
938     libxl__qmp_param_add_string(gc, &args, "arg",
939         libxl__qemu_disk_format_string(disk->format));
940     qmp->callback = cdrom_insert_inserted;
941     rc = libxl__ev_qmp_send(egc, qmp, "change", args);
942 out:
943     if (rc)
944         cdrom_insert_done(egc, cis, rc); /* must be last */
945 }
946 
cdrom_insert_inserted(libxl__egc * egc,libxl__ev_qmp * qmp,const libxl__json_object * response,int rc)947 static void cdrom_insert_inserted(libxl__egc *egc,
948                                   libxl__ev_qmp *qmp,
949                                   const libxl__json_object *response,
950                                   int rc)
951 {
952     EGC_GC;
953     libxl__cdrom_insert_state *cis = CONTAINER_OF(qmp, *cis, qmp);
954     libxl__flock *data_lock = NULL;
955     libxl_domain_config d_config;
956     flexarray_t *insert = NULL;
957     xs_transaction_t t = XBT_NULL;
958     libxl__device device;
959     const char *be_path, *libxl_path;
960     char *tmp;
961 
962     /* convenience aliases */
963     libxl_domid domid = cis->domid;
964     libxl_device_disk *disk = cis->disk;
965 
966     libxl_domain_config_init(&d_config);
967 
968     if (rc) goto out;
969 
970     rc = libxl__device_from_disk(gc, domid, disk, &device);
971     if (rc) goto out;
972     be_path = libxl__device_backend_path(gc, &device);
973     libxl_path = libxl__device_libxl_path(gc, &device);
974 
975     data_lock = libxl__lock_domain_userdata(gc, domid);
976     if (!data_lock) {
977         rc = ERROR_LOCK_FAIL;
978         goto out;
979     }
980 
981     rc = libxl__get_domain_configuration(gc, domid, &d_config);
982     if (rc) goto out;
983 
984     device_add_domain_config(gc, &d_config, &libxl__disk_devtype,
985                              &cis->disk_saved);
986 
987     insert = flexarray_make(gc, 4, 1);
988     flexarray_append_pair(insert, "type",
989                       libxl__device_disk_string_of_backend(disk->backend));
990     if (disk->format != LIBXL_DISK_FORMAT_EMPTY)
991         flexarray_append_pair(insert, "params",
992                     GCSPRINTF("%s:%s",
993                         libxl__device_disk_string_of_format(disk->format),
994                         disk->pdev_path));
995     else
996         flexarray_append_pair(insert, "params", "");
997 
998     for (;;) {
999         rc = libxl__xs_transaction_start(gc, &t);
1000         if (rc) goto out;
1001         /* Sanity check: make sure the device exists before writing here */
1002         tmp = libxl__xs_read(gc, t, GCSPRINTF("%s/frontend", libxl_path));
1003         if (!tmp)
1004         {
1005             LOGD(ERROR, domid, "Internal error: %s does not exist",
1006                  GCSPRINTF("%s/frontend", libxl_path));
1007             rc = ERROR_FAIL;
1008             goto out;
1009         }
1010 
1011         rc = libxl__set_domain_configuration(gc, domid, &d_config);
1012         if (rc) goto out;
1013 
1014         char **kvs = libxl__xs_kvs_of_flexarray(gc, insert);
1015 
1016         rc = libxl__xs_writev(gc, t, be_path, kvs);
1017         if (rc) goto out;
1018 
1019         rc = libxl__xs_writev(gc, t, libxl_path, kvs);
1020         if (rc) goto out;
1021 
1022         rc = libxl__xs_transaction_commit(gc, &t);
1023         if (!rc) break;
1024         if (rc < 0) goto out;
1025     }
1026 
1027     rc = 0;
1028 
1029 out:
1030     libxl__xs_transaction_abort(gc, &t);
1031     libxl_domain_config_dispose(&d_config);
1032     if (data_lock) libxl__unlock_file(data_lock);
1033     cdrom_insert_done(egc, cis, rc); /* must be last */
1034 }
1035 
cdrom_insert_timout(libxl__egc * egc,libxl__ev_time * ev,const struct timeval * requested_abs,int rc)1036 static void cdrom_insert_timout(libxl__egc *egc, libxl__ev_time *ev,
1037                                 const struct timeval *requested_abs,
1038                                 int rc)
1039 {
1040     EGC_GC;
1041     libxl__cdrom_insert_state *cis = CONTAINER_OF(ev, *cis, time);
1042     LOGD(ERROR, cis->domid, "cdrom insertion timed out");
1043     cdrom_insert_done(egc, cis, rc);
1044 }
1045 
cdrom_insert_done(libxl__egc * egc,libxl__cdrom_insert_state * cis,int rc)1046 static void cdrom_insert_done(libxl__egc *egc,
1047                               libxl__cdrom_insert_state *cis,
1048                               int rc)
1049 {
1050     EGC_GC;
1051 
1052     libxl__ev_time_deregister(gc, &cis->time);
1053     libxl__ev_qmp_dispose(gc, &cis->qmp);
1054     if (cis->qmp.payload_fd >= 0) close(cis->qmp.payload_fd);
1055     libxl__ev_slowlock_unlock(gc, &cis->qmp_lock);
1056     libxl_device_disk_dispose(&cis->disk_saved);
1057     libxl__ao_complete(egc, cis->ao, rc);
1058 }
1059 
1060 /* libxl__alloc_vdev only works on the local domain, that is the domain
1061  * where the toolstack is running */
libxl__alloc_vdev(libxl__gc * gc,void * get_vdev_user,xs_transaction_t t)1062 static char * libxl__alloc_vdev(libxl__gc *gc, void *get_vdev_user,
1063         xs_transaction_t t)
1064 {
1065     const char *blkdev_start = (const char *) get_vdev_user;
1066     int devid = 0, disk = 0, part = 0;
1067 
1068     libxl__device_disk_dev_number(blkdev_start, &disk, &part);
1069     if (part != 0) {
1070         LOG(ERROR, "blkdev_start is invalid");
1071         return NULL;
1072     }
1073 
1074     do {
1075         devid = libxl__device_disk_dev_number(GCSPRINTF("d%dp0", disk),
1076                 NULL, NULL);
1077         if (devid < 0)
1078             return NULL;
1079         if (libxl__xs_read(gc, t, GCSPRINTF("%s/backend",
1080                            libxl__domain_device_libxl_path(gc,
1081                            LIBXL_TOOLSTACK_DOMID, devid,
1082                            LIBXL__DEVICE_KIND_VBD))) == NULL) {
1083             if (errno == ENOENT)
1084                 return libxl__devid_to_vdev(gc, devid);
1085             else
1086                 return NULL;
1087         }
1088         disk++;
1089     } while (1);
1090     return NULL;
1091 }
1092 
1093 /* Callbacks */
1094 
libxl__device_disk_find_local_path(libxl__gc * gc,libxl_domid guest_domid,const libxl_device_disk * disk,bool qdisk_direct)1095 char *libxl__device_disk_find_local_path(libxl__gc *gc,
1096                                           libxl_domid guest_domid,
1097                                           const libxl_device_disk *disk,
1098                                           bool qdisk_direct)
1099 {
1100     char *path = NULL;
1101 
1102     /* No local paths for driver domains */
1103     if (disk->backend_domname != NULL) {
1104         LOG(DEBUG, "Non-local backend, can't access locally.\n");
1105         goto out;
1106     }
1107 
1108     /*
1109      * If this is in raw format, and we're not using a script or a
1110      * driver domain, we can access the target path directly.
1111      */
1112     if (disk->format == LIBXL_DISK_FORMAT_RAW
1113         && disk->script == NULL) {
1114         path = libxl__strdup(gc, disk->pdev_path);
1115         LOG(DEBUG, "Directly accessing local RAW disk %s", path);
1116         goto out;
1117     }
1118 
1119     /*
1120      * If we're being called for a qemu path, we can pass the target
1121      * string directly as well
1122      */
1123     if (qdisk_direct && disk->backend == LIBXL_DISK_BACKEND_QDISK) {
1124         path = libxl__strdup(gc, disk->pdev_path);
1125         LOG(DEBUG, "Directly accessing local QDISK target %s", path);
1126         goto out;
1127     }
1128 
1129     /*
1130      * If the format isn't raw and / or we're using a script, then see
1131      * if the script has written a path to the "cooked" node
1132      */
1133     if (disk->script && guest_domid != INVALID_DOMID) {
1134         libxl__device device;
1135         char *be_path, *pdpath;
1136         int rc;
1137 
1138         LOGD(DEBUG, guest_domid,
1139              "Run from a script; checking for physical-device-path (vdev %s)",
1140              disk->vdev);
1141 
1142         rc = libxl__device_from_disk(gc, guest_domid, disk, &device);
1143         if (rc < 0)
1144             goto out;
1145 
1146         be_path = libxl__device_backend_path(gc, &device);
1147 
1148         pdpath = libxl__sprintf(gc, "%s/physical-device-path", be_path);
1149 
1150         LOGD(DEBUG, guest_domid, "Attempting to read node %s", pdpath);
1151         path = libxl__xs_read(gc, XBT_NULL, pdpath);
1152 
1153         if (path)
1154             LOGD(DEBUG, guest_domid, "Accessing cooked block device %s", path);
1155         else
1156             LOGD(DEBUG, guest_domid, "No physical-device-path, can't access locally.");
1157 
1158         goto out;
1159     }
1160 
1161  out:
1162     return path;
1163 }
1164 
1165 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev);
1166 
libxl__device_disk_local_initiate_attach(libxl__egc * egc,libxl__disk_local_state * dls)1167 void libxl__device_disk_local_initiate_attach(libxl__egc *egc,
1168                                      libxl__disk_local_state *dls)
1169 {
1170     STATE_AO_GC(dls->ao);
1171     int rc;
1172     const libxl_device_disk *in_disk = dls->in_disk;
1173     libxl_device_disk *disk = &dls->disk;
1174     const char *blkdev_start = dls->blkdev_start;
1175 
1176     assert(in_disk->pdev_path);
1177 
1178     disk->vdev = NULL;
1179 
1180     if (dls->diskpath)
1181         LOG(DEBUG, "Strange, dls->diskpath already set: %s", dls->diskpath);
1182 
1183     LOG(DEBUG, "Trying to find local path");
1184 
1185     dls->diskpath = libxl__device_disk_find_local_path(gc, INVALID_DOMID,
1186                                                        in_disk, false);
1187     if (dls->diskpath) {
1188         LOG(DEBUG, "Local path found, executing callback.");
1189         dls->callback(egc, dls, 0);
1190     } else {
1191         LOG(DEBUG, "Local path not found, initiating attach.");
1192 
1193         memcpy(disk, in_disk, sizeof(libxl_device_disk));
1194         disk->pdev_path = libxl__strdup(gc, in_disk->pdev_path);
1195         if (in_disk->script != NULL)
1196             disk->script = libxl__strdup(gc, in_disk->script);
1197         disk->vdev = NULL;
1198 
1199         rc = libxl__device_disk_setdefault(gc, LIBXL_TOOLSTACK_DOMID, disk,
1200                                            false);
1201         if (rc) goto out;
1202 
1203         libxl__prepare_ao_device(ao, &dls->aodev);
1204         dls->aodev.callback = local_device_attach_cb;
1205         device_disk_add(egc, LIBXL_TOOLSTACK_DOMID, disk, &dls->aodev,
1206                         libxl__alloc_vdev, (void *) blkdev_start);
1207     }
1208 
1209     return;
1210 
1211  out:
1212     assert(rc);
1213     dls->rc = rc;
1214     libxl__device_disk_local_initiate_detach(egc, dls);
1215     dls->callback(egc, dls, rc);
1216 }
1217 
local_device_attach_cb(libxl__egc * egc,libxl__ao_device * aodev)1218 static void local_device_attach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1219 {
1220     STATE_AO_GC(aodev->ao);
1221     libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1222     char *be_path = NULL;
1223     int rc;
1224     libxl__device device;
1225     libxl_device_disk *disk = &dls->disk;
1226 
1227     rc = aodev->rc;
1228     if (rc) {
1229         LOGE(ERROR, "unable locally attach device: %s", disk->pdev_path);
1230         goto out;
1231     }
1232 
1233     rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID, disk, &device);
1234     if (rc < 0)
1235         goto out;
1236     be_path = libxl__device_backend_path(gc, &device);
1237     rc = libxl__wait_for_backend(gc, be_path, GCSPRINTF("%d", XenbusStateConnected));
1238     if (rc < 0)
1239         goto out;
1240 
1241     dls->diskpath = GCSPRINTF("/dev/%s",
1242                               libxl__devid_to_localdev(gc, device.devid));
1243     LOG(DEBUG, "locally attached disk %s", dls->diskpath);
1244 
1245     dls->callback(egc, dls, 0);
1246     return;
1247 
1248  out:
1249     assert(rc);
1250     dls->rc = rc;
1251     libxl__device_disk_local_initiate_detach(egc, dls);
1252     return;
1253 }
1254 
1255 /* Callbacks for local detach */
1256 
1257 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev);
1258 
libxl__device_disk_local_initiate_detach(libxl__egc * egc,libxl__disk_local_state * dls)1259 void libxl__device_disk_local_initiate_detach(libxl__egc *egc,
1260                                      libxl__disk_local_state *dls)
1261 {
1262     STATE_AO_GC(dls->ao);
1263     int rc = 0;
1264     libxl_device_disk *disk = &dls->disk;
1265     libxl__device *device;
1266     libxl__ao_device *aodev = &dls->aodev;
1267     libxl__prepare_ao_device(ao, aodev);
1268 
1269     if (!dls->diskpath) goto out;
1270 
1271     if (disk->vdev != NULL) {
1272         GCNEW(device);
1273         rc = libxl__device_from_disk(gc, LIBXL_TOOLSTACK_DOMID,
1274                                      disk, device);
1275         if (rc != 0) goto out;
1276 
1277         aodev->action = LIBXL__DEVICE_ACTION_REMOVE;
1278         aodev->dev = device;
1279         aodev->callback = local_device_detach_cb;
1280         aodev->force = 0;
1281         libxl__initiate_device_generic_remove(egc, aodev);
1282         return;
1283     }
1284 
1285 out:
1286     aodev->rc = rc;
1287     local_device_detach_cb(egc, aodev);
1288     return;
1289 }
1290 
local_device_detach_cb(libxl__egc * egc,libxl__ao_device * aodev)1291 static void local_device_detach_cb(libxl__egc *egc, libxl__ao_device *aodev)
1292 {
1293     STATE_AO_GC(aodev->ao);
1294     libxl__disk_local_state *dls = CONTAINER_OF(aodev, *dls, aodev);
1295     int rc;
1296 
1297     if (aodev->rc) {
1298         LOGED(ERROR, aodev->dev->domid, "Unable to %s %s with id %u",
1299                      libxl__device_action_to_string(aodev->action),
1300                      libxl__device_kind_to_string(aodev->dev->kind),
1301                      aodev->dev->devid);
1302         goto out;
1303     }
1304 
1305 out:
1306     /*
1307      * If there was an error in dls->rc, it means we have been called from
1308      * a failed execution of libxl__device_disk_local_initiate_attach,
1309      * so return the original error.
1310      */
1311     rc = dls->rc ? dls->rc : aodev->rc;
1312     dls->callback(egc, dls, rc);
1313     return;
1314 }
1315 
1316 /* The following functions are defined:
1317  * libxl_device_disk_add
1318  * libxl__add_disks
1319  * libxl_device_disk_remove
1320  * libxl_device_disk_destroy
1321  */
1322 LIBXL_DEFINE_DEVICE_ADD(disk)
LIBXL_DEFINE_DEVICES_ADD(disk)1323 LIBXL_DEFINE_DEVICES_ADD(disk)
1324 LIBXL_DEFINE_DEVICE_REMOVE(disk)
1325 
1326 static int libxl_device_disk_compare(const libxl_device_disk *d1,
1327                                      const libxl_device_disk *d2)
1328 {
1329     return COMPARE_DISK(d1, d2);
1330 }
1331 
1332 /* Take care of removable device. We maintain invariant in the
1333  * insert / remove operation so that:
1334  * 1. if xenstore is "empty" while JSON is not, the result
1335  *    is "empty"
1336  * 2. if xenstore has a different media than JSON, use the
1337  *    one in JSON
1338  * 3. if xenstore and JSON have the same media, well, you
1339  *    know the answer :-)
1340  *
1341  * Currently there is only one removable device -- CDROM.
1342  * Look for libxl_cdrom_insert for reference.
1343  */
libxl_device_disk_merge(libxl_ctx * ctx,void * d1,void * d2)1344 static void libxl_device_disk_merge(libxl_ctx *ctx, void *d1, void *d2)
1345 {
1346     GC_INIT(ctx);
1347     libxl_device_disk *src = d1;
1348     libxl_device_disk *dst = d2;
1349 
1350     if (src->removable) {
1351         if (!src->pdev_path || *src->pdev_path == '\0') {
1352             /* 1, no media in drive */
1353             free(dst->pdev_path);
1354             dst->pdev_path = libxl__strdup(NOGC, "");
1355             dst->format = LIBXL_DISK_FORMAT_EMPTY;
1356         } else {
1357             /* 2 and 3, use JSON, no need to touch anything */
1358             ;
1359         }
1360     }
1361 }
1362 
libxl_device_disk_dm_needed(void * e,unsigned domid)1363 static int libxl_device_disk_dm_needed(void *e, unsigned domid)
1364 {
1365     libxl_device_disk *elem = e;
1366 
1367     return elem->backend == LIBXL_DISK_BACKEND_QDISK &&
1368            elem->backend_domid == domid;
1369 }
1370 
1371 LIBXL_DEFINE_DEVICE_LIST(disk)
1372 
1373 #define libxl__device_disk_update_devid NULL
1374 
1375 DEFINE_DEVICE_TYPE_STRUCT(disk, VBD,
1376     .merge       = libxl_device_disk_merge,
1377     .dm_needed   = libxl_device_disk_dm_needed,
1378     .from_xenstore = (device_from_xenstore_fn_t)libxl__disk_from_xenstore,
1379     .skip_attach = 1,
1380 );
1381 
1382 /*
1383  * Local variables:
1384  * mode: C
1385  * c-basic-offset: 4
1386  * indent-tabs-mode: nil
1387  * End:
1388  */
1389