- /* Domain XML. */
- xml = virDomainGetXMLDesc (dom, 0);
-
- if (!xml) {
- err = virConnGetLastError (conn);
- fprintf (stderr, _("guestfish: error reading libvirt XML information about '%s': %s\n"),
- guest, err->message);
- goto cleanup;
- }
-
- /* Now the horrible task of parsing out the fields we need from the XML.
- * http://www.xmlsoft.org/examples/xpath1.c
- */
- doc = xmlParseMemory (xml, strlen (xml));
- if (doc == NULL) {
- fprintf (stderr, _("guestfish: unable to parse XML information returned by libvirt\n"));
- goto cleanup;
- }
-
- xpathCtx = xmlXPathNewContext (doc);
- if (xpathCtx == NULL) {
- fprintf (stderr, _("guestfish: unable to create new XPath context\n"));
- goto cleanup;
- }
-
- /* This gives us a set of all the <disk> nodes. */
- xpathObj = xmlXPathEvalExpression (BAD_CAST "//devices/disk", xpathCtx);
- if (xpathObj == NULL) {
- fprintf (stderr, _("guestfish: unable to evaluate XPath expression\n"));
- goto cleanup;
- }
-
- xmlNodeSetPtr nodes = xpathObj->nodesetval;
- for (i = 0; i < nodes->nodeNr; ++i) {
- xmlXPathObjectPtr xpfilename;
- xmlXPathObjectPtr xpformat;
-
- /* Change the context to the current <disk> node.
- * DV advises to reset this before each search since older versions of
- * libxml2 might overwrite it.
- */
- xpathCtx->node = nodes->nodeTab[i];
-
- /* Filename can be in <source dev=..> or <source file=..> attribute. */
- xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@dev", xpathCtx);
- if (xpfilename == NULL ||
- xpfilename->nodesetval == NULL ||
- xpfilename->nodesetval->nodeNr == 0) {
- xmlXPathFreeObject (xpfilename);
- xpathCtx->node = nodes->nodeTab[i];
- xpfilename = xmlXPathEvalExpression (BAD_CAST "./source/@file", xpathCtx);
- if (xpfilename == NULL ||
- xpfilename->nodesetval == NULL ||
- xpfilename->nodesetval->nodeNr == 0) {
- xmlXPathFreeObject (xpfilename);
- continue; /* disk filename not found, skip this */
- }
- }
-
- assert (xpfilename->nodesetval->nodeTab[0]);
- assert (xpfilename->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
- xmlAttrPtr attr = (xmlAttrPtr) xpfilename->nodesetval->nodeTab[0];
- char *filename = (char *) xmlNodeListGetString (doc, attr->children, 1);
-
- /* Get the disk format (may not be set). */
- xpathCtx->node = nodes->nodeTab[i];
- xpformat = xmlXPathEvalExpression (BAD_CAST "./driver/@type", xpathCtx);
- char *format = NULL;
- if (xpformat != NULL &&
- xpformat->nodesetval &&
- xpformat->nodesetval->nodeNr > 0) {
- assert (xpformat->nodesetval->nodeTab[0]);
- assert (xpformat->nodesetval->nodeTab[0]->type == XML_ATTRIBUTE_NODE);
- attr = (xmlAttrPtr) xpformat->nodesetval->nodeTab[0];
- format = (char *) xmlNodeListGetString (doc, attr->children, 1);
- }
-
- /* Add the disk, with optional format. */
- struct guestfs_add_drive_opts_argv optargs = { .bitmask = 0 };
- if (read_only) {
- optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_READONLY_BITMASK;
- optargs.readonly = read_only;
- }
- if (format) {
- optargs.bitmask |= GUESTFS_ADD_DRIVE_OPTS_FORMAT_BITMASK;
- optargs.format = format;
- }
-
- int t = guestfs_add_drive_opts_argv (g, filename, &optargs);
-
- xmlFree (filename);
- xmlFree (format);
- xmlXPathFreeObject (xpfilename);
- xmlXPathFreeObject (xpformat);
-
- if (t == -1)
- goto cleanup;
-
- nr_added++;
- }
-
- if (nr_added == 0) {
- fprintf (stderr, _("guestfish: libvirt domain '%s' has no disks\n"),
- guest);
- goto cleanup;
- }
-
- /* Successful. */
- r = nr_added;
-
-cleanup:
- free (xml);
- if (xpathObj) xmlXPathFreeObject (xpathObj);
- if (xpathCtx) xmlXPathFreeContext (xpathCtx);
- if (doc) xmlFreeDoc (doc);
- if (dom) virDomainFree (dom);
- if (conn) virConnectClose (conn);
-
- return r;