提交 6cc4d6a3 编写于 作者: E Eric Blake

storage: use valid XML for awkward volume names

$ touch /var/lib/libvirt/images/'a<b>c'
$ virsh pool-refresh default
$ virsh vol-dumpxml 'a<b>c' default | head -n2
<volume>
  <name>a<b>c</name>

Oops.  That's not valid XML.  And when we fix the XML
generation, it fails RelaxNG validation.

I'm also tired of seeing <key>(null)</key> in the example
output for volume xml; while we used NULLSTR() to avoid
a NULL deref rather than relying on glibc's printf
extension behavior, it's even better if we avoid the issue
in the first place.  But this requires being careful that
we don't invalidate any storage backends that were relying
on key being unassigned during virStoragVolCreateXML[From].

I would have split this into two patches (one for escaping,
one for avoiding <key>(null)</key>), but since they both
end up touching a lot of the same test files, I ended up
merging it into one.

Note that this patch allows pretty much any volume name
that can appear in a directory (excluding . and .. because
those are special), but does nothing to change the current
(unenforced) RelaxNG claim that pool names will consist
only of letters, numbers, _, -, and +.  Tightening the C
code to match RelaxNG patterns and/or relaxing the grammar
to match the C code for pool names is a task for another
day (but remember, we DID recently tighten C code for
domain names to exclude a leading '.').

* src/conf/storage_conf.c (virStoragePoolSourceFormat)
(virStoragePoolDefFormat, virStorageVolTargetDefFormat)
(virStorageVolDefFormat): Escape user-controlled strings.
(virStorageVolDefParseXML): Parse key, for use in unit tests.
* src/storage/storage_driver.c (storageVolCreateXML)
(storageVolCreateXMLFrom): Ensure parsed key doesn't confuse
volume creation.
* docs/schemas/basictypes.rng (volName): Relax definition.
* tests/storagepoolxml2xmltest.c (mymain): Test it.
* tests/storagevolxml2xmltest.c (mymain): Likewise.
* tests/storagepoolxml2xmlin/pool-dir-naming.xml: New file.
* tests/storagepoolxml2xmlout/pool-dir-naming.xml: Likewise.
* tests/storagevolxml2xmlin/vol-file-naming.xml: Likewise.
* tests/storagevolxml2xmlout/vol-file-naming.xml: Likewise.
* tests/storagevolxml2xmlout/vol-*.xml: Fix fallout.
Signed-off-by: NEric Blake <eblake@redhat.com>
上级 6b90d742
...@@ -291,8 +291,15 @@ ...@@ -291,8 +291,15 @@
</define> </define>
<define name='volName'> <define name='volName'>
<!-- directory pools allow almost any file name as a volume name -->
<data type='string'> <data type='string'>
<param name="pattern">[a-zA-Z0-9_\+\-\.]+</param> <param name="pattern">[^/]+</param>
<except>
<choice>
<value>.</value>
<value>..</value>
</choice>
</except>
</data> </data>
</define> </define>
......
...@@ -1056,7 +1056,8 @@ virStoragePoolSourceFormat(virBufferPtr buf, ...@@ -1056,7 +1056,8 @@ virStoragePoolSourceFormat(virBufferPtr buf,
virBufferAddLit(buf, " <source>\n"); virBufferAddLit(buf, " <source>\n");
if ((options->flags & VIR_STORAGE_POOL_SOURCE_HOST) && src->nhost) { if ((options->flags & VIR_STORAGE_POOL_SOURCE_HOST) && src->nhost) {
for (i = 0; i < src->nhost; i++) { for (i = 0; i < src->nhost; i++) {
virBufferAsprintf(buf, " <host name='%s'", src->hosts[i].name); virBufferEscapeString(buf, " <host name='%s'",
src->hosts[i].name);
if (src->hosts[i].port) if (src->hosts[i].port)
virBufferAsprintf(buf, " port='%d'", src->hosts[i].port); virBufferAsprintf(buf, " port='%d'", src->hosts[i].port);
virBufferAddLit(buf, "/>\n"); virBufferAddLit(buf, "/>\n");
...@@ -1067,7 +1068,7 @@ virStoragePoolSourceFormat(virBufferPtr buf, ...@@ -1067,7 +1068,7 @@ virStoragePoolSourceFormat(virBufferPtr buf,
src->ndevice) { src->ndevice) {
for (i = 0; i < src->ndevice; i++) { for (i = 0; i < src->ndevice; i++) {
if (src->devices[i].nfreeExtent) { if (src->devices[i].nfreeExtent) {
virBufferAsprintf(buf, " <device path='%s'>\n", virBufferEscapeString(buf, " <device path='%s'>\n",
src->devices[i].path); src->devices[i].path);
for (j = 0; j < src->devices[i].nfreeExtent; j++) { for (j = 0; j < src->devices[i].nfreeExtent; j++) {
virBufferAsprintf(buf, " <freeExtent start='%llu' end='%llu'/>\n", virBufferAsprintf(buf, " <freeExtent start='%llu' end='%llu'/>\n",
...@@ -1076,15 +1077,14 @@ virStoragePoolSourceFormat(virBufferPtr buf, ...@@ -1076,15 +1077,14 @@ virStoragePoolSourceFormat(virBufferPtr buf,
} }
virBufferAddLit(buf, " </device>\n"); virBufferAddLit(buf, " </device>\n");
} else { } else {
virBufferAsprintf(buf, " <device path='%s'/>\n", virBufferEscapeString(buf, " <device path='%s'/>\n",
src->devices[i].path); src->devices[i].path);
} }
} }
} }
if ((options->flags & VIR_STORAGE_POOL_SOURCE_DIR) && if (options->flags & VIR_STORAGE_POOL_SOURCE_DIR)
src->dir) virBufferEscapeString(buf, " <dir path='%s'/>\n", src->dir);
virBufferAsprintf(buf, " <dir path='%s'/>\n", src->dir);
if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER)) { if ((options->flags & VIR_STORAGE_POOL_SOURCE_ADAPTER)) {
if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST || if (src->adapter.type == VIR_STORAGE_POOL_SOURCE_ADAPTER_TYPE_FC_HOST ||
...@@ -1104,9 +1104,8 @@ virStoragePoolSourceFormat(virBufferPtr buf, ...@@ -1104,9 +1104,8 @@ virStoragePoolSourceFormat(virBufferPtr buf,
} }
} }
if ((options->flags & VIR_STORAGE_POOL_SOURCE_NAME) && if (options->flags & VIR_STORAGE_POOL_SOURCE_NAME)
src->name) virBufferEscapeString(buf, " <name>%s</name>\n", src->name);
virBufferAsprintf(buf, " <name>%s</name>\n", src->name);
if ((options->flags & VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN) && if ((options->flags & VIR_STORAGE_POOL_SOURCE_INITIATOR_IQN) &&
src->initiator.iqn) { src->initiator.iqn) {
...@@ -1129,8 +1128,9 @@ virStoragePoolSourceFormat(virBufferPtr buf, ...@@ -1129,8 +1128,9 @@ virStoragePoolSourceFormat(virBufferPtr buf,
if (src->authType == VIR_STORAGE_POOL_AUTH_CHAP || if (src->authType == VIR_STORAGE_POOL_AUTH_CHAP ||
src->authType == VIR_STORAGE_POOL_AUTH_CEPHX) { src->authType == VIR_STORAGE_POOL_AUTH_CEPHX) {
virBufferAsprintf(buf, " <auth type='%s' username='%s'>\n", virBufferAsprintf(buf, " <auth type='%s' ",
virStoragePoolAuthTypeTypeToString(src->authType), virStoragePoolAuthTypeTypeToString(src->authType));
virBufferEscapeString(buf, "username='%s'>\n",
(src->authType == VIR_STORAGE_POOL_AUTH_CHAP ? (src->authType == VIR_STORAGE_POOL_AUTH_CHAP ?
src->auth.chap.username : src->auth.chap.username :
src->auth.cephx.username)); src->auth.cephx.username));
...@@ -1149,13 +1149,8 @@ virStoragePoolSourceFormat(virBufferPtr buf, ...@@ -1149,13 +1149,8 @@ virStoragePoolSourceFormat(virBufferPtr buf,
virBufferAddLit(buf, " </auth>\n"); virBufferAddLit(buf, " </auth>\n");
} }
if (src->vendor != NULL) {
virBufferEscapeString(buf, " <vendor name='%s'/>\n", src->vendor); virBufferEscapeString(buf, " <vendor name='%s'/>\n", src->vendor);
}
if (src->product != NULL) {
virBufferEscapeString(buf, " <product name='%s'/>\n", src->product); virBufferEscapeString(buf, " <product name='%s'/>\n", src->product);
}
virBufferAddLit(buf, " </source>\n"); virBufferAddLit(buf, " </source>\n");
...@@ -1182,7 +1177,7 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def) ...@@ -1182,7 +1177,7 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def)
goto cleanup; goto cleanup;
} }
virBufferAsprintf(&buf, "<pool type='%s'>\n", type); virBufferAsprintf(&buf, "<pool type='%s'>\n", type);
virBufferAsprintf(&buf, " <name>%s</name>\n", def->name); virBufferEscapeString(&buf, " <name>%s</name>\n", def->name);
virUUIDFormat(def->uuid, uuid); virUUIDFormat(def->uuid, uuid);
virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", uuid); virBufferAsprintf(&buf, " <uuid>%s</uuid>\n", uuid);
...@@ -1203,8 +1198,7 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def) ...@@ -1203,8 +1198,7 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def)
def->type != VIR_STORAGE_POOL_SHEEPDOG) { def->type != VIR_STORAGE_POOL_SHEEPDOG) {
virBufferAddLit(&buf, " <target>\n"); virBufferAddLit(&buf, " <target>\n");
if (def->target.path) virBufferEscapeString(&buf, " <path>%s</path>\n", def->target.path);
virBufferAsprintf(&buf, " <path>%s</path>\n", def->target.path);
virBufferAddLit(&buf, " <permissions>\n"); virBufferAddLit(&buf, " <permissions>\n");
virBufferAsprintf(&buf, " <mode>0%o</mode>\n", virBufferAsprintf(&buf, " <mode>0%o</mode>\n",
...@@ -1214,8 +1208,7 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def) ...@@ -1214,8 +1208,7 @@ virStoragePoolDefFormat(virStoragePoolDefPtr def)
virBufferAsprintf(&buf, " <group>%d</group>\n", virBufferAsprintf(&buf, " <group>%d</group>\n",
(int) def->target.perms.gid); (int) def->target.perms.gid);
if (def->target.perms.label) virBufferEscapeString(&buf, " <label>%s</label>\n",
virBufferAsprintf(&buf, " <label>%s</label>\n",
def->target.perms.label); def->target.perms.label);
virBufferAddLit(&buf, " </permissions>\n"); virBufferAddLit(&buf, " </permissions>\n");
...@@ -1282,8 +1275,8 @@ virStorageVolDefParseXML(virStoragePoolDefPtr pool, ...@@ -1282,8 +1275,8 @@ virStorageVolDefParseXML(virStoragePoolDefPtr pool,
goto error; goto error;
} }
/* Auto-generated so deliberately ignore */ /* Normally generated by pool refresh, but useful for unit tests */
/* ret->key = virXPathString("string(./key)", ctxt); */ ret->key = virXPathString("string(./key)", ctxt);
capacity = virXPathString("string(./capacity)", ctxt); capacity = virXPathString("string(./capacity)", ctxt);
unit = virXPathString("string(./capacity/@unit)", ctxt); unit = virXPathString("string(./capacity/@unit)", ctxt);
...@@ -1485,11 +1478,11 @@ static int ...@@ -1485,11 +1478,11 @@ static int
virStorageVolTargetDefFormat(virStorageVolOptionsPtr options, virStorageVolTargetDefFormat(virStorageVolOptionsPtr options,
virBufferPtr buf, virBufferPtr buf,
virStorageVolTargetPtr def, virStorageVolTargetPtr def,
const char *type) { const char *type)
{
virBufferAsprintf(buf, " <%s>\n", type); virBufferAsprintf(buf, " <%s>\n", type);
if (def->path) virBufferEscapeString(buf, " <path>%s</path>\n", def->path);
virBufferAsprintf(buf, " <path>%s</path>\n", def->path);
if (options->formatToString) { if (options->formatToString) {
const char *format = (options->formatToString)(def->format); const char *format = (options->formatToString)(def->format);
...@@ -1511,8 +1504,7 @@ virStorageVolTargetDefFormat(virStorageVolOptionsPtr options, ...@@ -1511,8 +1504,7 @@ virStorageVolTargetDefFormat(virStorageVolOptionsPtr options,
(unsigned int) def->perms.gid); (unsigned int) def->perms.gid);
if (def->perms.label) virBufferEscapeString(buf, " <label>%s</label>\n",
virBufferAsprintf(buf, " <label>%s</label>\n",
def->perms.label); def->perms.label);
virBufferAddLit(buf, " </permissions>\n"); virBufferAddLit(buf, " </permissions>\n");
...@@ -1572,8 +1564,8 @@ virStorageVolDefFormat(virStoragePoolDefPtr pool, ...@@ -1572,8 +1564,8 @@ virStorageVolDefFormat(virStoragePoolDefPtr pool,
return NULL; return NULL;
virBufferAddLit(&buf, "<volume>\n"); virBufferAddLit(&buf, "<volume>\n");
virBufferAsprintf(&buf, " <name>%s</name>\n", def->name); virBufferEscapeString(&buf, " <name>%s</name>\n", def->name);
virBufferAsprintf(&buf, " <key>%s</key>\n", NULLSTR(def->key)); virBufferEscapeString(&buf, " <key>%s</key>\n", def->key);
virBufferAddLit(&buf, " <source>\n"); virBufferAddLit(&buf, " <source>\n");
if (def->source.nextent) { if (def->source.nextent) {
...@@ -1585,7 +1577,7 @@ virStorageVolDefFormat(virStoragePoolDefPtr pool, ...@@ -1585,7 +1577,7 @@ virStorageVolDefFormat(virStoragePoolDefPtr pool,
if (thispath != NULL) if (thispath != NULL)
virBufferAddLit(&buf, " </device>\n"); virBufferAddLit(&buf, " </device>\n");
virBufferAsprintf(&buf, " <device path='%s'>\n", virBufferEscapeString(&buf, " <device path='%s'>\n",
def->source.extents[i].path); def->source.extents[i].path);
} }
......
...@@ -1554,6 +1554,9 @@ storageVolCreateXML(virStoragePoolPtr obj, ...@@ -1554,6 +1554,9 @@ storageVolCreateXML(virStoragePoolPtr obj,
goto cleanup; goto cleanup;
} }
/* Wipe any key the user may have suggested, as volume creation
* will generate the canonical key. */
VIR_FREE(voldef->key);
if (backend->createVol(obj->conn, pool, voldef) < 0) { if (backend->createVol(obj->conn, pool, voldef) < 0) {
goto cleanup; goto cleanup;
} }
...@@ -1729,7 +1732,10 @@ storageVolCreateXMLFrom(virStoragePoolPtr obj, ...@@ -1729,7 +1732,10 @@ storageVolCreateXMLFrom(virStoragePoolPtr obj,
pool->volumes.count+1) < 0) pool->volumes.count+1) < 0)
goto cleanup; goto cleanup;
/* 'Define' the new volume so we get async progress reporting */ /* 'Define' the new volume so we get async progress reporting.
* Wipe any key the user may have suggested, as volume creation
* will generate the canonical key. */
VIR_FREE(newvol->key);
if (backend->createVol(obj->conn, pool, newvol) < 0) { if (backend->createVol(obj->conn, pool, newvol) < 0) {
goto cleanup; goto cleanup;
} }
......
<pool type='dir'>
<name>virtimages</name>
<uuid>70a7eb15-6c34-ee9c-bf57-69e8e5ff3fb2</uuid>
<capacity>0</capacity>
<allocation>0</allocation>
<available>0</available>
<source>
</source>
<target>
<path>///var/////lib/libvirt/&lt;images&gt;//</path>
<permissions>
<mode>0700</mode>
<owner>-1</owner>
<group>-1</group>
<label>some_label_t</label>
</permissions>
</target>
</pool>
<pool type='dir'>
<name>virtimages</name>
<uuid>70a7eb15-6c34-ee9c-bf57-69e8e5ff3fb2</uuid>
<capacity unit='bytes'>0</capacity>
<allocation unit='bytes'>0</allocation>
<available unit='bytes'>0</available>
<source>
</source>
<target>
<path>/var/lib/libvirt/&lt;images&gt;</path>
<permissions>
<mode>0700</mode>
<owner>-1</owner>
<group>-1</group>
<label>some_label_t</label>
</permissions>
</target>
</pool>
...@@ -85,6 +85,7 @@ mymain(void) ...@@ -85,6 +85,7 @@ mymain(void)
ret = -1 ret = -1
DO_TEST("pool-dir"); DO_TEST("pool-dir");
DO_TEST("pool-dir-naming");
DO_TEST("pool-fs"); DO_TEST("pool-fs");
DO_TEST("pool-logical"); DO_TEST("pool-logical");
DO_TEST("pool-logical-nopath"); DO_TEST("pool-logical-nopath");
......
<volume> <volume>
<name>sparse.img</name> <name>sparse.img</name>
<key>/var/lib/libvirt/images/sparse.img</key>
<source/> <source/>
<capacity unit='GB'>10</capacity> <capacity unit='GB'>10</capacity>
<allocation unit='MiB'>0</allocation> <allocation unit='MiB'>0</allocation>
......
<volume>
<name>&lt;sparse&gt;.img</name>
<source/>
<capacity unit="TiB">1</capacity>
<allocation unit="bytes">0</allocation>
<target>
<path>/var/lib/libvirt/images/&lt;sparse&gt;.img</path>
<permissions>
<mode>0</mode>
<owner>0744</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
<timestamps>
<atime>1341933637.273190990</atime>
<mtime>1341930622.047245868</mtime>
<ctime>1341930622.047245868</ctime>
</timestamps>
</target>
</volume>
<volume> <volume>
<name>sparse.img</name> <name>sparse.img</name>
<key>(null)</key> <key>/var/lib/libvirt/images/sparse.img</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>10000000000</capacity> <capacity unit='bytes'>10000000000</capacity>
......
<volume>
<name>&lt;sparse&gt;.img</name>
<source>
</source>
<capacity unit='bytes'>1099511627776</capacity>
<allocation unit='bytes'>0</allocation>
<target>
<path>/var/lib/libvirt/images/&lt;sparse&gt;.img</path>
<format type='raw'/>
<permissions>
<mode>00</mode>
<owner>744</owner>
<group>0</group>
<label>virt_image_t</label>
</permissions>
</target>
</volume>
<volume> <volume>
<name>sparse.img</name> <name>sparse.img</name>
<key>(null)</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>1099511627776</capacity> <capacity unit='bytes'>1099511627776</capacity>
......
<volume> <volume>
<name>Swap</name> <name>Swap</name>
<key>(null)</key> <key>r4xkCv-MQhr-WKIT-R66x-Epn2-e8hG-1Z5gY0</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>2080374784</capacity> <capacity unit='bytes'>2080374784</capacity>
......
<volume> <volume>
<name>Swap</name> <name>Swap</name>
<key>(null)</key> <key>r4xkCv-MQhr-WKIT-R66x-Epn2-e8hG-1Z5gY0</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>2080374784</capacity> <capacity unit='bytes'>2080374784</capacity>
......
<volume> <volume>
<name>sda1</name> <name>sda1</name>
<key>(null)</key> <key>/dev/sda1</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>106896384</capacity> <capacity unit='bytes'>106896384</capacity>
......
<volume> <volume>
<name>OtherDemo.img</name> <name>OtherDemo.img</name>
<key>(null)</key> <key>/var/lib/libvirt/images/OtherDemo.img</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>5368709120</capacity> <capacity unit='bytes'>5368709120</capacity>
......
<volume> <volume>
<name>OtherDemo.img</name> <name>OtherDemo.img</name>
<key>(null)</key> <key>/var/lib/libvirt/images/OtherDemo.img</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>5368709120</capacity> <capacity unit='bytes'>5368709120</capacity>
......
<volume> <volume>
<name>OtherDemo.img</name> <name>OtherDemo.img</name>
<key>(null)</key> <key>/var/lib/libvirt/images/OtherDemo.img</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>5368709120</capacity> <capacity unit='bytes'>5368709120</capacity>
......
<volume> <volume>
<name>OtherDemo.img</name> <name>OtherDemo.img</name>
<key>(null)</key> <key>/var/lib/libvirt/images/OtherDemo.img</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>5368709120</capacity> <capacity unit='bytes'>5368709120</capacity>
......
<volume> <volume>
<name>OtherDemo.img</name> <name>OtherDemo.img</name>
<key>(null)</key> <key>/var/lib/libvirt/images/OtherDemo.img</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>5368709120</capacity> <capacity unit='bytes'>5368709120</capacity>
......
<volume> <volume>
<name>test2</name> <name>test2</name>
<key>(null)</key>
<source> <source>
</source> </source>
<capacity unit='bytes'>1024</capacity> <capacity unit='bytes'>1024</capacity>
......
...@@ -110,6 +110,7 @@ mymain(void) ...@@ -110,6 +110,7 @@ mymain(void)
while (0); while (0);
DO_TEST("pool-dir", "vol-file"); DO_TEST("pool-dir", "vol-file");
DO_TEST("pool-dir", "vol-file-naming");
DO_TEST("pool-dir", "vol-file-backing"); DO_TEST("pool-dir", "vol-file-backing");
DO_TEST("pool-dir", "vol-qcow2"); DO_TEST("pool-dir", "vol-qcow2");
DO_TEST("pool-dir", "vol-qcow2-1.1"); DO_TEST("pool-dir", "vol-qcow2-1.1");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册