1 /*
2  * backup_conf.c: domain backup XML processing
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library 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 GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library.  If not, see
16  * <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <config.h>
20 
21 #include "configmake.h"
22 #include "internal.h"
23 #include "virbuffer.h"
24 #include "datatypes.h"
25 #include "domain_conf.h"
26 #include "virlog.h"
27 #include "viralloc.h"
28 #include "backup_conf.h"
29 #include "storage_source_conf.h"
30 #include "virfile.h"
31 #include "virerror.h"
32 #include "virxml.h"
33 #include "virstring.h"
34 #include "virhash.h"
35 #include "virenum.h"
36 
37 #define VIR_FROM_THIS VIR_FROM_DOMAIN
38 
39 VIR_LOG_INIT("conf.backup_conf");
40 
41 VIR_ENUM_DECL(virDomainBackup);
42 VIR_ENUM_IMPL(virDomainBackup,
43               VIR_DOMAIN_BACKUP_TYPE_LAST,
44               "default",
45               "push",
46               "pull");
47 
48 /* following values appear in the status XML */
49 VIR_ENUM_DECL(virDomainBackupDiskState);
50 VIR_ENUM_IMPL(virDomainBackupDiskState,
51               VIR_DOMAIN_BACKUP_DISK_STATE_LAST,
52               "",
53               "running",
54               "complete",
55               "failed",
56               "cancelling",
57               "cancelled");
58 
59 VIR_ENUM_DECL(virDomainBackupDiskBackupMode);
60 VIR_ENUM_IMPL(virDomainBackupDiskBackupMode,
61               VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_LAST,
62               "",
63               "full",
64               "incremental");
65 
66 void
virDomainBackupDefFree(virDomainBackupDef * def)67 virDomainBackupDefFree(virDomainBackupDef *def)
68 {
69     size_t i;
70 
71     if (!def)
72         return;
73 
74     g_free(def->incremental);
75     g_free(def->errmsg);
76     virStorageNetHostDefFree(1, def->server);
77 
78     for (i = 0; i < def->ndisks; i++) {
79         virDomainBackupDiskDef *disk = def->disks + i;
80 
81         g_free(disk->name);
82         g_free(disk->incremental);
83         g_free(disk->exportname);
84         g_free(disk->exportbitmap);
85         virObjectUnref(disk->store);
86     }
87 
88     g_free(def->disks);
89 
90     g_free(def->tlsAlias);
91     g_free(def->tlsSecretAlias);
92 
93     g_free(def);
94 }
95 
96 
97 static int
virDomainBackupDiskDefParseXML(xmlNodePtr node,xmlXPathContextPtr ctxt,virDomainBackupDiskDef * def,bool push,unsigned int flags,virDomainXMLOption * xmlopt)98 virDomainBackupDiskDefParseXML(xmlNodePtr node,
99                                xmlXPathContextPtr ctxt,
100                                virDomainBackupDiskDef *def,
101                                bool push,
102                                unsigned int flags,
103                                virDomainXMLOption *xmlopt)
104 {
105     VIR_XPATH_NODE_AUTORESTORE(ctxt)
106     g_autofree char *type = NULL;
107     g_autofree char *format = NULL;
108     g_autofree char *idx = NULL;
109     xmlNodePtr srcNode;
110     unsigned int storageSourceParseFlags = 0;
111     bool internal = flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL;
112 
113     if (internal)
114         storageSourceParseFlags = VIR_DOMAIN_DEF_PARSE_STATUS;
115 
116     ctxt->node = node;
117 
118     if (!(def->name = virXMLPropString(node, "name"))) {
119         virReportError(VIR_ERR_XML_ERROR, "%s",
120                        _("missing name from disk backup element"));
121         return -1;
122     }
123 
124     if (virXMLPropTristateBool(node, "backup", VIR_XML_PROP_NONE,
125                                &def->backup) < 0)
126         return -1;
127 
128     if (def->backup == VIR_TRISTATE_BOOL_ABSENT)
129         def->backup = VIR_TRISTATE_BOOL_YES;
130 
131     /* don't parse anything else if backup is disabled */
132     if (def->backup == VIR_TRISTATE_BOOL_NO)
133         return 0;
134 
135     if (!push) {
136         def->exportname = virXMLPropString(node, "exportname");
137         def->exportbitmap = virXMLPropString(node, "exportbitmap");
138     }
139 
140     if (virXMLPropEnum(node, "backupmode",
141                        virDomainBackupDiskBackupModeTypeFromString,
142                        VIR_XML_PROP_NONE, &def->backupmode) < 0)
143         return -1;
144 
145     def->incremental = virXMLPropString(node, "incremental");
146 
147     if (internal) {
148         if (virXMLPropEnum(node, "state",
149                            virDomainBackupDiskStateTypeFromString,
150                            VIR_XML_PROP_REQUIRED, &def->state) < 0)
151             return -1;
152     }
153 
154     type = virXMLPropString(node, "type");
155     format = virXPathString("string(./driver/@type)", ctxt);
156     if (internal)
157         idx = virXMLPropString(node, "index");
158 
159     if (!(def->store = virDomainStorageSourceParseBase(type, format, idx)))
160           return -1;
161 
162     if (def->store->type != VIR_STORAGE_TYPE_FILE &&
163         def->store->type != VIR_STORAGE_TYPE_BLOCK) {
164         virReportError(VIR_ERR_XML_ERROR,
165                        _("unsupported disk backup type '%s'"), type);
166         return -1;
167     }
168 
169     if (push)
170         srcNode = virXPathNode("./target", ctxt);
171     else
172         srcNode = virXPathNode("./scratch", ctxt);
173 
174     if (srcNode &&
175         virDomainStorageSourceParse(srcNode, ctxt, def->store,
176                                     storageSourceParseFlags, xmlopt) < 0)
177         return -1;
178 
179     return 0;
180 }
181 
182 
183 static void
virDomainBackupDefParsePrivate(virDomainBackupDef * def,xmlXPathContextPtr ctxt,unsigned int flags)184 virDomainBackupDefParsePrivate(virDomainBackupDef *def,
185                                xmlXPathContextPtr ctxt,
186                                unsigned int flags)
187 {
188     if (!(flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL))
189         return;
190 
191     def->tlsSecretAlias = virXPathString("string(./privateData/objects/secret[@type='tlskey']/@alias)", ctxt);
192     def->tlsAlias = virXPathString("string(./privateData/objects/TLSx509/@alias)", ctxt);
193 }
194 
195 
196 static virDomainBackupDef *
virDomainBackupDefParse(xmlXPathContextPtr ctxt,virDomainXMLOption * xmlopt,unsigned int flags)197 virDomainBackupDefParse(xmlXPathContextPtr ctxt,
198                         virDomainXMLOption *xmlopt,
199                         unsigned int flags)
200 {
201     g_autoptr(virDomainBackupDef) def = NULL;
202     g_autofree xmlNodePtr *nodes = NULL;
203     xmlNodePtr node = NULL;
204     g_autofree char *mode = NULL;
205     bool push;
206     size_t i;
207     int n;
208 
209     def = g_new0(virDomainBackupDef, 1);
210 
211     def->type = VIR_DOMAIN_BACKUP_TYPE_PUSH;
212 
213     if ((mode = virXMLPropString(ctxt->node, "mode"))) {
214         if ((def->type = virDomainBackupTypeFromString(mode)) <= 0) {
215             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
216                            _("unknown backup mode '%s'"), mode);
217             return NULL;
218         }
219     }
220 
221     push = def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH;
222 
223     def->incremental = virXPathString("string(./incremental)", ctxt);
224 
225     if ((node = virXPathNode("./server", ctxt))) {
226         if (def->type != VIR_DOMAIN_BACKUP_TYPE_PULL) {
227             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
228                            _("use of <server> requires pull mode backup"));
229             return NULL;
230         }
231 
232         def->server = g_new0(virStorageNetHostDef, 1);
233 
234         if (virDomainStorageNetworkParseHost(node, def->server) < 0)
235             return NULL;
236 
237         if (def->server->transport == VIR_STORAGE_NET_HOST_TRANS_RDMA) {
238             virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
239                            _("transport rdma is not supported for <server>"));
240             return NULL;
241         }
242 
243         if (def->server->transport == VIR_STORAGE_NET_HOST_TRANS_UNIX &&
244             !g_path_is_absolute(def->server->socket)) {
245             virReportError(VIR_ERR_XML_ERROR,
246                            _("backup socket path '%s' must be absolute"),
247                            def->server->socket);
248             return NULL;
249         }
250 
251         if (virXMLPropTristateBool(node, "tls", VIR_XML_PROP_NONE,
252                                    &def->tls) < 0)
253             return NULL;
254     }
255 
256     if ((n = virXPathNodeSet("./disks/*", ctxt, &nodes)) < 0)
257         return NULL;
258 
259     def->disks = g_new0(virDomainBackupDiskDef, n);
260 
261     def->ndisks = n;
262     for (i = 0; i < def->ndisks; i++) {
263         if (virDomainBackupDiskDefParseXML(nodes[i], ctxt,
264                                            &def->disks[i], push,
265                                            flags, xmlopt) < 0)
266             return NULL;
267     }
268 
269     virDomainBackupDefParsePrivate(def, ctxt, flags);
270 
271     return g_steal_pointer(&def);
272 }
273 
274 
275 virDomainBackupDef *
virDomainBackupDefParseString(const char * xmlStr,virDomainXMLOption * xmlopt,unsigned int flags)276 virDomainBackupDefParseString(const char *xmlStr,
277                               virDomainXMLOption *xmlopt,
278                               unsigned int flags)
279 {
280     virDomainBackupDef *ret = NULL;
281     g_autoptr(xmlDoc) xml = NULL;
282     int keepBlanksDefault = xmlKeepBlanksDefault(0);
283 
284     if ((xml = virXMLParse(NULL, xmlStr, _("(domain_backup)"), "domainbackup.rng",
285                            !(flags & VIR_DOMAIN_BACKUP_PARSE_INTERNAL)))) {
286         xmlKeepBlanksDefault(keepBlanksDefault);
287         ret = virDomainBackupDefParseNode(xml, xmlDocGetRootElement(xml),
288                                           xmlopt, flags);
289     }
290     xmlKeepBlanksDefault(keepBlanksDefault);
291 
292     return ret;
293 }
294 
295 
296 virDomainBackupDef *
virDomainBackupDefParseNode(xmlDocPtr xml,xmlNodePtr root,virDomainXMLOption * xmlopt,unsigned int flags)297 virDomainBackupDefParseNode(xmlDocPtr xml,
298                             xmlNodePtr root,
299                             virDomainXMLOption *xmlopt,
300                             unsigned int flags)
301 {
302     g_autoptr(xmlXPathContext) ctxt = NULL;
303 
304     if (!virXMLNodeNameEqual(root, "domainbackup")) {
305         virReportError(VIR_ERR_XML_ERROR, "%s", _("domainbackup"));
306         return NULL;
307     }
308 
309     if (!(ctxt = virXMLXPathContextNew(xml)))
310         return NULL;
311 
312     ctxt->node = root;
313     return virDomainBackupDefParse(ctxt, xmlopt, flags);
314 }
315 
316 
317 static int
virDomainBackupDiskDefFormat(virBuffer * buf,virDomainBackupDiskDef * disk,bool push,bool internal,virDomainXMLOption * xmlopt)318 virDomainBackupDiskDefFormat(virBuffer *buf,
319                              virDomainBackupDiskDef *disk,
320                              bool push,
321                              bool internal,
322                              virDomainXMLOption *xmlopt)
323 {
324     g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
325     g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
326     const char *sourcename = "scratch";
327     unsigned int storageSourceFormatFlags = 0;
328 
329     if (push)
330         sourcename = "target";
331 
332     if (internal)
333         storageSourceFormatFlags |= VIR_DOMAIN_DEF_FORMAT_STATUS;
334 
335     virBufferEscapeString(&attrBuf, " name='%s'", disk->name);
336     virBufferAsprintf(&attrBuf, " backup='%s'", virTristateBoolTypeToString(disk->backup));
337     if (internal && disk->state != VIR_DOMAIN_BACKUP_DISK_STATE_NONE)
338         virBufferAsprintf(&attrBuf, " state='%s'", virDomainBackupDiskStateTypeToString(disk->state));
339 
340     if (disk->backup == VIR_TRISTATE_BOOL_YES) {
341         virBufferAsprintf(&attrBuf, " type='%s'", virStorageTypeToString(disk->store->type));
342 
343         if (disk->backupmode != VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT) {
344             virBufferAsprintf(&attrBuf, " backupmode='%s'",
345                               virDomainBackupDiskBackupModeTypeToString(disk->backupmode));
346         }
347 
348         virBufferEscapeString(&attrBuf, " incremental='%s'", disk->incremental);
349 
350         virBufferEscapeString(&attrBuf, " exportname='%s'", disk->exportname);
351         virBufferEscapeString(&attrBuf, " exportbitmap='%s'", disk->exportbitmap);
352 
353         if (disk->store->id != 0)
354             virBufferAsprintf(&attrBuf, " index='%u'", disk->store->id);
355 
356         if (disk->store->format > 0)
357             virBufferEscapeString(&childBuf, "<driver type='%s'/>\n",
358                                   virStorageFileFormatTypeToString(disk->store->format));
359 
360         if (virDomainDiskSourceFormat(&childBuf, disk->store, sourcename,
361                                       0, false, storageSourceFormatFlags,
362                                       false, false, xmlopt) < 0)
363             return -1;
364     }
365 
366     virXMLFormatElement(buf, "disk", &attrBuf, &childBuf);
367     return 0;
368 }
369 
370 
371 static void
virDomainBackupDefFormatPrivate(virBuffer * buf,virDomainBackupDef * def,bool internal)372 virDomainBackupDefFormatPrivate(virBuffer *buf,
373                                 virDomainBackupDef *def,
374                                 bool internal)
375 {
376     g_auto(virBuffer) privChildBuf = VIR_BUFFER_INIT_CHILD(buf);
377     g_auto(virBuffer) objectsChildBuf = VIR_BUFFER_INIT_CHILD(&privChildBuf);
378 
379     if (!internal)
380         return;
381 
382     virBufferEscapeString(&objectsChildBuf, "<secret type='tlskey' alias='%s'/>\n",
383                           def->tlsSecretAlias);
384     virBufferEscapeString(&objectsChildBuf, "<TLSx509 alias='%s'/>\n", def->tlsAlias);
385 
386     virXMLFormatElement(&privChildBuf, "objects", NULL, &objectsChildBuf);
387     virXMLFormatElement(buf, "privateData", NULL, &privChildBuf);
388 }
389 
390 
391 int
virDomainBackupDefFormat(virBuffer * buf,virDomainBackupDef * def,bool internal,virDomainXMLOption * xmlopt)392 virDomainBackupDefFormat(virBuffer *buf,
393                          virDomainBackupDef *def,
394                          bool internal,
395                          virDomainXMLOption *xmlopt)
396 {
397     g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
398     g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
399     g_auto(virBuffer) serverAttrBuf = VIR_BUFFER_INITIALIZER;
400     g_auto(virBuffer) disksChildBuf = VIR_BUFFER_INIT_CHILD(&childBuf);
401     size_t i;
402 
403     virBufferAsprintf(&attrBuf, " mode='%s'", virDomainBackupTypeToString(def->type));
404 
405     virBufferEscapeString(&childBuf, "<incremental>%s</incremental>\n", def->incremental);
406 
407     if (def->server) {
408         virBufferAsprintf(&serverAttrBuf, " transport='%s'",
409                           virStorageNetHostTransportTypeToString(def->server->transport));
410         if (def->tls != VIR_TRISTATE_BOOL_ABSENT)
411             virBufferAsprintf(&serverAttrBuf, " tls='%s'", virTristateBoolTypeToString(def->tls));
412         virBufferEscapeString(&serverAttrBuf, " name='%s'", def->server->name);
413         if (def->server->port)
414             virBufferAsprintf(&serverAttrBuf, " port='%u'", def->server->port);
415         virBufferEscapeString(&serverAttrBuf, " socket='%s'", def->server->socket);
416     }
417 
418     virXMLFormatElement(&childBuf, "server", &serverAttrBuf, NULL);
419 
420     for (i = 0; i < def->ndisks; i++) {
421         if (virDomainBackupDiskDefFormat(&disksChildBuf, &def->disks[i],
422                                          def->type == VIR_DOMAIN_BACKUP_TYPE_PUSH,
423                                          internal, xmlopt) < 0)
424             return -1;
425     }
426 
427     virXMLFormatElement(&childBuf, "disks", NULL, &disksChildBuf);
428 
429     virDomainBackupDefFormatPrivate(&childBuf, def, internal);
430 
431     virXMLFormatElement(buf, "domainbackup", &attrBuf, &childBuf);
432 
433     return 0;
434 }
435 
436 
437 static int
virDomainBackupDefAssignStore(virDomainBackupDiskDef * disk,virStorageSource * src,const char * suffix)438 virDomainBackupDefAssignStore(virDomainBackupDiskDef *disk,
439                               virStorageSource *src,
440                               const char *suffix)
441 {
442     if (virStorageSourceIsEmpty(src)) {
443         if (disk->store) {
444             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
445                            _("disk '%s' has no media"), disk->name);
446             return -1;
447         }
448     } else if (!disk->store) {
449         if (virStorageSourceGetActualType(src) == VIR_STORAGE_TYPE_FILE) {
450             disk->store = virStorageSourceNew();
451             disk->store->type = VIR_STORAGE_TYPE_FILE;
452             disk->store->path = g_strdup_printf("%s.%s", src->path, suffix);
453         } else {
454             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
455                            _("refusing to generate file name for disk '%s'"),
456                            disk->name);
457             return -1;
458         }
459     }
460 
461     return 0;
462 }
463 
464 
465 int
virDomainBackupAlignDisks(virDomainBackupDef * def,virDomainDef * dom,const char * suffix)466 virDomainBackupAlignDisks(virDomainBackupDef *def,
467                           virDomainDef *dom,
468                           const char *suffix)
469 {
470     g_autoptr(GHashTable) disks = virHashNew(NULL);
471     size_t i;
472     int ndisks;
473     bool backup_all = false;
474 
475     /* Unlikely to have a guest without disks but technically possible.  */
476     if (!dom->ndisks) {
477         virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
478                        _("domain must have at least one disk to perform backup"));
479         return -1;
480     }
481 
482     /* Double check requested disks.  */
483     for (i = 0; i < def->ndisks; i++) {
484         virDomainBackupDiskDef *backupdisk = &def->disks[i];
485         virDomainDiskDef *domdisk;
486 
487         if (!(domdisk = virDomainDiskByTarget(dom, backupdisk->name))) {
488             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
489                            _("no disk named '%s'"), backupdisk->name);
490             return -1;
491         }
492 
493         if (virHashAddEntry(disks, backupdisk->name, NULL) < 0) {
494             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
495                            _("disk '%s' specified twice"),
496                            backupdisk->name);
497             return -1;
498         }
499 
500         if (backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL &&
501             !backupdisk->incremental &&
502             !def->incremental) {
503             virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
504                            _("'incremental' backup mode of disk '%s' requires setting 'incremental' field for disk or backup"),
505                            backupdisk->name);
506             return -1;
507         }
508 
509 
510         if (backupdisk->backup == VIR_TRISTATE_BOOL_YES &&
511             virDomainBackupDefAssignStore(backupdisk, domdisk->src, suffix) < 0)
512             return -1;
513     }
514 
515     if (def->ndisks == 0)
516         backup_all = true;
517 
518     ndisks = def->ndisks;
519     VIR_EXPAND_N(def->disks, def->ndisks, dom->ndisks - def->ndisks);
520 
521     for (i = 0; i < dom->ndisks; i++) {
522         virDomainBackupDiskDef *backupdisk = NULL;
523         virDomainDiskDef *domdisk =  dom->disks[i];
524 
525         if (virHashHasEntry(disks, domdisk->dst))
526             continue;
527 
528         backupdisk = &def->disks[ndisks++];
529         backupdisk->name = g_strdup(domdisk->dst);
530 
531         if (backup_all &&
532             !virStorageSourceIsEmpty(domdisk->src) &&
533             !domdisk->src->readonly) {
534             backupdisk->backup = VIR_TRISTATE_BOOL_YES;
535 
536             if (virDomainBackupDefAssignStore(backupdisk, domdisk->src, suffix) < 0)
537                 return -1;
538         } else {
539             backupdisk->backup = VIR_TRISTATE_BOOL_NO;
540         }
541     }
542 
543     for (i = 0; i < def->ndisks; i++) {
544         virDomainBackupDiskDef *backupdisk = &def->disks[i];
545 
546         if (backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_DEFAULT) {
547             if (def->incremental || backupdisk->incremental) {
548                 backupdisk->backupmode = VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL;
549             } else {
550                 backupdisk->backupmode = VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_FULL;
551             }
552         }
553 
554         if (!backupdisk->incremental &&
555             backupdisk->backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL)
556             backupdisk->incremental = g_strdup(def->incremental);
557     }
558 
559     return 0;
560 }
561