[libvirt] [RFC] Storage volume clone API

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

Attached is a stab at an API for cloning a storage volume. This patch
implements the command for FS volumes, along with a virsh command
'vol-clone'. This builds on the previously posted 'drop pool lock during
volume creation' work.

The new API call looks like:

/**
 * virStorageVolClone:
 * @vol: pointer to storage vol to clone
 * @xmldesc: description of volume to create
 * @flags: flags for creation (unused, pass 0)
 *
 * Clone a storage volume within the parent pool.
 * Information for the new volume (name, perms)
 * are passed via a typical volume XML description
 * Not all pools support cloning of volumes
 *
 * return the storage volume, or NULL on error
 */
virStorageVolPtr
virStorageVolClone(virStorageVolPtr vol,
                   const char *xmldesc,
                   unsigned int flags)

The user passes information about the new volume via <volume> XML.
Currently we only use the passed name and permissions values, but
backingStore info could also be relevant, and 'format' if we allow
cloning between image formats.

Current limitations:

- Parsing the 'clone' xml will complain if the 'capacity' element hasn't
been specified, even though it is meaningless for the new volume. Not
too sure what the right thing to do here is.

- A clone of a raw sparse file will turn out fully allocated. Could
probably borrow code from 'cp' to try and get this right.

- Doesn't handle backing stores. I think we would need to call out to
'qemu-img convert' to have any chance of getting this right. And if we
do that, we could also allow cloning from one image format to another.

A lot of the patch is just plumbing, which I will break out into
separate patches on the next post. For now, most of the interesting bits
are at near the bottom.

Feedback appreciated.

Thanks,
Cole
diff --git a/docs/devhelp/libvirt-libvirt.html b/docs/devhelp/libvirt-libvirt.html
index be5eb44..0696a02 100644
--- a/docs/devhelp/libvirt-libvirt.html
+++ b/docs/devhelp/libvirt-libvirt.html
@@ -261,6 +261,7 @@ const char *	<a href="#virNodeDeviceGetParent">virNodeDeviceGetParent</a>	(<a hr
 <a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a>	<a href="#virConnectOpen">virConnectOpen</a>		(const char * name);
 <a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a>	<a href="#virDomainCreateXML">virDomainCreateXML</a>	(<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/>					 const char * xmlDesc, <br/>					 unsigned int flags);
 int	<a href="#virNodeDeviceRef">virNodeDeviceRef</a>		(<a href="libvirt-libvirt.html#virNodeDevicePtr">virNodeDevicePtr</a> dev);
+<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	<a href="#virStorageVolClone">virStorageVolClone</a>	(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br/>						 const char * xmldesc, <br/>						 unsigned int flags);
 int	<a href="#virDomainSetVcpus">virDomainSetVcpus</a>		(<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain, <br/>					 unsigned int nvcpus);
 int	<a href="#virDomainRef">virDomainRef</a>			(<a href="libvirt-libvirt.html#virDomainPtr">virDomainPtr</a> domain);
 int	<a href="#virConnectDomainEventRegister">virConnectDomainEventRegister</a>	(<a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a> conn, <br/>					 <a href="libvirt-libvirt.html#virConnectDomainEventCallback">virConnectDomainEventCallback</a> cb, <br/>					 void * opaque, <br/>					 <a href="libvirt-libvirt.html#virFreeCallback">virFreeCallback</a> freecb);
@@ -1320,6 +1321,10 @@ The content of this structure is not made public by the API.
 </pre><p>Undefine an inactive storage pool</p>
 <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div></div>
         <hr/>
+        <div class="refsect2" lang="en"><h3><a name="virStorageVolClone"/>virStorageVolClone ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	virStorageVolClone	(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br/>						 const char * xmldesc, <br/>						 unsigned int flags)<br/>
+</pre><p>Clone a storage volume within the parent pool. Information for the new volume (name, perms) are passed via a typical volume XML description Not all pools support cloning of volumes</p>
+<div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>vol</tt></i>:</span></td><td>pointer to storage vol to clone</td></tr><tr><td><span class="term"><i><tt>xmldesc</tt></i>:</span></td><td>description of volume to create</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags for creation (unused, pass 0)</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>the storage volume, or NULL on error</td></tr></tbody></table></div></div>
+        <hr/>
         <div class="refsect2" lang="en"><h3><a name="virStorageVolCreateXML"/>virStorageVolCreateXML ()</h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	virStorageVolCreateXML	(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br/>						 const char * xmldesc, <br/>						 unsigned int flags)<br/>
 </pre><p>Create a storage volume within a pool based on an XML description. Not all pools support creation of volumes</p>
 <div class="variablelist"><table border="0"><col align="left"/><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>xmldesc</tt></i>:</span></td><td>description of volume to create</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags for creation (unused, pass 0)</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>the storage volume, or NULL on error</td></tr></tbody></table></div></div>
diff --git a/docs/html/libvirt-libvirt.html b/docs/html/libvirt-libvirt.html
index f2c9a5d..2766de1 100644
--- a/docs/html/libvirt-libvirt.html
+++ b/docs/html/libvirt-libvirt.html
@@ -243,6 +243,7 @@ int	<a href="#virStoragePoolRef">virStoragePoolRef</a>		(<a href="libvirt-libvir
 int	<a href="#virStoragePoolRefresh">virStoragePoolRefresh</a>		(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />					 unsigned int flags)
 int	<a href="#virStoragePoolSetAutostart">virStoragePoolSetAutostart</a>	(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />					 int autostart)
 int	<a href="#virStoragePoolUndefine">virStoragePoolUndefine</a>		(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool)
+<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	<a href="#virStorageVolClone">virStorageVolClone</a>	(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br />						 const char * xmldesc, <br />						 unsigned int flags)
 <a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	<a href="#virStorageVolCreateXML">virStorageVolCreateXML</a>	(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />						 const char * xmldesc, <br />						 unsigned int flags)
 int	<a href="#virStorageVolDelete">virStorageVolDelete</a>		(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br />					 unsigned int flags)
 int	<a href="#virStorageVolFree">virStorageVolFree</a>		(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol)
@@ -500,7 +501,8 @@ int	<a href="#virStorageVolRef">virStorageVolRef</a>		(<a href="libvirt-libvirt.
 </pre><p>Increment the reference count on the pool. For each additional call to this method, there shall be a corresponding call to <a href="libvirt-libvirt.html#virStoragePoolFree">virStoragePoolFree</a> to release the reference count, once the caller no longer needs the reference to this object. This method is typically useful for applications where multiple threads are using a connection, and it is required that the connection remain open until all threads have finished using it. ie, each new thread using a pool would increment the reference count.</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td></td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td></td></tr></tbody></table></div><h3><a name="virStoragePoolRefresh" id="virStoragePoolRefresh"><code>virStoragePoolRefresh</code></a></h3><pre class="programlisting">int	virStoragePoolRefresh		(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />					 unsigned int flags)<br />
 </pre><p>Request that the pool refresh its list of volumes. This may involve communicating with a remote server, and/or initializing new devices at the OS layer</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags to control refresh behaviour (currently unused, use 0)</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 if the volume list was refreshed, -1 on failure</td></tr></tbody></table></div><h3><a name="virStoragePoolSetAutostart" id="virStoragePoolSetAutostart"><code>virStoragePoolSetAutostart</code></a></h3><pre class="programlisting">int	virStoragePoolSetAutostart	(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />					 int autostart)<br />
 </pre><p>Sets the autostart flag</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>autostart</tt></i>:</span></td><td>new flag setting</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, -1 on failure</td></tr></tbody></table></div><h3><a name="virStoragePoolUndefine" id="virStoragePoolUndefine"><code>virStoragePoolUndefine</code></a></h3><pre class="programlisting">int	virStoragePoolUndefine		(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool)<br />
-</pre><p>Undefine an inactive storage pool</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div><h3><a name="virStorageVolCreateXML" id="virStorageVolCreateXML"><code>virStorageVolCreateXML</code></a></h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	virStorageVolCreateXML	(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />						 const char * xmldesc, <br />						 unsigned int flags)<br />
+</pre><p>Undefine an inactive storage pool</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>a <a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> object, or NULL if creation failed</td></tr></tbody></table></div><h3><a name="virStorageVolClone" id="virStorageVolClone"><code>virStorageVolClone</code></a></h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	virStorageVolClone	(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br />						 const char * xmldesc, <br />						 unsigned int flags)<br />
+</pre><p>Clone a storage volume within the parent pool. Information for the new volume (name, perms) are passed via a typical volume XML description Not all pools support cloning of volumes</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>vol</tt></i>:</span></td><td>pointer to storage vol to clone</td></tr><tr><td><span class="term"><i><tt>xmldesc</tt></i>:</span></td><td>description of volume to create</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags for creation (unused, pass 0)</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>the storage volume, or NULL on error</td></tr></tbody></table></div><h3><a name="virStorageVolCreateXML" id="virStorageVolCreateXML"><code>virStorageVolCreateXML</code></a></h3><pre class="programlisting"><a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a>	virStorageVolCreateXML	(<a href="libvirt-libvirt.html#virStoragePoolPtr">virStoragePoolPtr</a> pool, <br />						 const char * xmldesc, <br />						 unsigned int flags)<br />
 </pre><p>Create a storage volume within a pool based on an XML description. Not all pools support creation of volumes</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>pool</tt></i>:</span></td><td>pointer to storage pool</td></tr><tr><td><span class="term"><i><tt>xmldesc</tt></i>:</span></td><td>description of volume to create</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>flags for creation (unused, pass 0)</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>the storage volume, or NULL on error</td></tr></tbody></table></div><h3><a name="virStorageVolDelete" id="virStorageVolDelete"><code>virStorageVolDelete</code></a></h3><pre class="programlisting">int	virStorageVolDelete		(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol, <br />					 unsigned int flags)<br />
 </pre><p>Delete the storage volume from the pool</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>vol</tt></i>:</span></td><td>pointer to storage volume</td></tr><tr><td><span class="term"><i><tt>flags</tt></i>:</span></td><td>future flags, use 0 for now</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 on error</td></tr></tbody></table></div><h3><a name="virStorageVolFree" id="virStorageVolFree"><code>virStorageVolFree</code></a></h3><pre class="programlisting">int	virStorageVolFree		(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol)<br />
 </pre><p>Release the storage volume handle. The underlying storage volume continues to exist.</p><div class="variablelist"><table border="0"><col align="left" /><tbody><tr><td><span class="term"><i><tt>vol</tt></i>:</span></td><td>pointer to storage volume</td></tr><tr><td><span class="term"><i><tt>Returns</tt></i>:</span></td><td>0 on success, or -1 on error</td></tr></tbody></table></div><h3><a name="virStorageVolGetConnect" id="virStorageVolGetConnect"><code>virStorageVolGetConnect</code></a></h3><pre class="programlisting"><a href="libvirt-libvirt.html#virConnectPtr">virConnectPtr</a>	virStorageVolGetConnect	(<a href="libvirt-libvirt.html#virStorageVolPtr">virStorageVolPtr</a> vol)<br />
diff --git a/docs/libvirt-api.xml b/docs/libvirt-api.xml
index 81bfbda..0e586e5 100644
--- a/docs/libvirt-api.xml
+++ b/docs/libvirt-api.xml
@@ -311,6 +311,7 @@
      <exports symbol='virConnectOpen' type='function'/>
      <exports symbol='virDomainCreateXML' type='function'/>
      <exports symbol='virNodeDeviceRef' type='function'/>
+     <exports symbol='virStorageVolClone' type='function'/>
      <exports symbol='virDomainSetVcpus' type='function'/>
      <exports symbol='virDomainRef' type='function'/>
      <exports symbol='virConnectDomainEventRegister' type='function'/>
@@ -1753,6 +1754,13 @@ see note above'/>
       <return type='int' info='a virStoragePoolPtr object, or NULL if creation failed'/>
       <arg name='pool' type='virStoragePoolPtr' info='pointer to storage pool'/>
     </function>
+    <function name='virStorageVolClone' file='libvirt' module='libvirt'>
+      <info>Clone a storage volume within the parent pool. Information for the new volume (name, perms) are passed via a typical volume XML description Not all pools support cloning of volumes</info>
+      <return type='virStorageVolPtr' info='the storage volume, or NULL on error'/>
+      <arg name='vol' type='virStorageVolPtr' info='pointer to storage vol to clone'/>
+      <arg name='xmldesc' type='const char *' info='description of volume to create'/>
+      <arg name='flags' type='unsigned int' info='flags for creation (unused, pass 0)'/>
+    </function>
     <function name='virStorageVolCreateXML' file='libvirt' module='libvirt'>
       <info>Create a storage volume within a pool based on an XML description. Not all pools support creation of volumes</info>
       <return type='virStorageVolPtr' info='the storage volume, or NULL on error'/>
diff --git a/docs/libvirt-refs.xml b/docs/libvirt-refs.xml
index 86ffb46..f66a19c 100644
--- a/docs/libvirt-refs.xml
+++ b/docs/libvirt-refs.xml
@@ -396,6 +396,7 @@
     <reference name='virStoragePoolState' href='html/libvirt-libvirt.html#virStoragePoolState'/>
     <reference name='virStoragePoolUndefine' href='html/libvirt-libvirt.html#virStoragePoolUndefine'/>
     <reference name='virStorageVol' href='html/libvirt-libvirt.html#virStorageVol'/>
+    <reference name='virStorageVolClone' href='html/libvirt-libvirt.html#virStorageVolClone'/>
     <reference name='virStorageVolCreateXML' href='html/libvirt-libvirt.html#virStorageVolCreateXML'/>
     <reference name='virStorageVolDelete' href='html/libvirt-libvirt.html#virStorageVolDelete'/>
     <reference name='virStorageVolDeleteFlags' href='html/libvirt-libvirt.html#virStorageVolDeleteFlags'/>
@@ -821,6 +822,7 @@
       <ref name='virStoragePoolState'/>
       <ref name='virStoragePoolUndefine'/>
       <ref name='virStorageVol'/>
+      <ref name='virStorageVolClone'/>
       <ref name='virStorageVolCreateXML'/>
       <ref name='virStorageVolDelete'/>
       <ref name='virStorageVolDeleteFlags'/>
@@ -897,6 +899,7 @@
       <ref name='virStoragePoolLookupByVolume'/>
     </type>
     <type name='virStorageVolPtr'>
+      <ref name='virStorageVolClone'/>
       <ref name='virStorageVolCreateXML'/>
       <ref name='virStorageVolLookupByKey'/>
       <ref name='virStorageVolLookupByName'/>
@@ -960,6 +963,7 @@
       <ref name='virStoragePoolDelete'/>
       <ref name='virStoragePoolGetXMLDesc'/>
       <ref name='virStoragePoolRefresh'/>
+      <ref name='virStorageVolClone'/>
       <ref name='virStorageVolCreateXML'/>
       <ref name='virStorageVolDelete'/>
       <ref name='virStorageVolGetXMLDesc'/>
@@ -1210,6 +1214,7 @@
     </type>
     <type name='virStorageVolPtr'>
       <ref name='virStoragePoolLookupByVolume'/>
+      <ref name='virStorageVolClone'/>
       <ref name='virStorageVolDelete'/>
       <ref name='virStorageVolFree'/>
       <ref name='virStorageVolGetConnect'/>
@@ -1534,6 +1539,7 @@
       <ref name='virStoragePoolState'/>
       <ref name='virStoragePoolUndefine'/>
       <ref name='virStorageVol'/>
+      <ref name='virStorageVolClone'/>
       <ref name='virStorageVolCreateXML'/>
       <ref name='virStorageVolDelete'/>
       <ref name='virStorageVolDeleteFlags'/>
@@ -1759,6 +1765,9 @@
         <word name='Change'>
           <ref name='virDomainSetSchedulerParameters'/>
         </word>
+        <word name='Clone'>
+          <ref name='virStorageVolClone'/>
+        </word>
         <word name='Collect'>
           <ref name='virConnectListDomains'/>
           <ref name='virConnectListNetworks'/>
@@ -1972,6 +1981,9 @@
           <ref name='virDomainBlockStats'/>
           <ref name='virDomainInterfaceStats'/>
         </word>
+        <word name='Information'>
+          <ref name='virStorageVolClone'/>
+        </word>
         <word name='Initialize'>
           <ref name='virInitialize'/>
         </word>
@@ -2025,6 +2037,8 @@
           <ref name='virNodeDeviceLookupByName'/>
         </word>
       </letter>
+    </chunk>
+    <chunk name='chunk1'>
       <letter name='M'>
         <word name='Macro'>
           <ref name='LIBVIR_VERSION_NUMBER'/>
@@ -2049,8 +2063,6 @@
           <ref name='virDomainMigrate'/>
         </word>
       </letter>
-    </chunk>
-    <chunk name='chunk1'>
       <letter name='N'>
         <word name='NFS'>
           <ref name='VIR_SECURITY_LABEL_BUFLEN'/>
@@ -2070,6 +2082,7 @@
           <ref name='virConnectGetURI'/>
         </word>
         <word name='Not'>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
         <word name='Note'>
@@ -2381,26 +2394,6 @@
         </word>
       </letter>
       <letter name='X'>
-        <word name='XML'>
-          <ref name='virConnectFindStoragePoolSources'/>
-          <ref name='virConnectGetCapabilities'/>
-          <ref name='virConnectGetMaxVcpus'/>
-          <ref name='virDomainAttachDevice'/>
-          <ref name='virDomainCreateLinux'/>
-          <ref name='virDomainCreateXML'/>
-          <ref name='virDomainDefineXML'/>
-          <ref name='virDomainDetachDevice'/>
-          <ref name='virDomainGetXMLDesc'/>
-          <ref name='virNetworkCreateXML'/>
-          <ref name='virNetworkDefineXML'/>
-          <ref name='virNetworkGetXMLDesc'/>
-          <ref name='virNodeDeviceGetXMLDesc'/>
-          <ref name='virStoragePoolCreateXML'/>
-          <ref name='virStoragePoolDefineXML'/>
-          <ref name='virStoragePoolGetXMLDesc'/>
-          <ref name='virStorageVolCreateXML'/>
-          <ref name='virStorageVolGetXMLDesc'/>
-        </word>
         <word name='Xen'>
           <ref name='_virDomainBlockStats'/>
           <ref name='virDomainCoreDump'/>
@@ -2526,6 +2519,7 @@
           <ref name='virStoragePoolFree'/>
           <ref name='virStoragePoolGetXMLDesc'/>
           <ref name='virStoragePoolRef'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
           <ref name='virStorageVolGetXMLDesc'/>
           <ref name='virStorageVolRef'/>
@@ -2646,6 +2640,7 @@
           <ref name='virNodeDeviceReset'/>
           <ref name='virSetErrorFunc'/>
           <ref name='virStoragePoolRef'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolRef'/>
         </word>
         <word name='area'>
@@ -3027,6 +3022,12 @@
         <word name='client'>
           <ref name='virConnectGetCapabilities'/>
         </word>
+        <word name='clone'>
+          <ref name='virStorageVolClone'/>
+        </word>
+        <word name='cloning'>
+          <ref name='virStorageVolClone'/>
+        </word>
         <word name='closes'>
           <ref name='virConnectClose'/>
         </word>
@@ -3207,12 +3208,14 @@
         </word>
         <word name='create'>
           <ref name='virNetworkDefineXML'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
         <word name='creation'>
           <ref name='virStoragePoolCreateXML'/>
           <ref name='virStoragePoolDefineXML'/>
           <ref name='virStoragePoolUndefine'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
         <word name='credentials'>
@@ -3323,6 +3326,7 @@
           <ref name='virNetworkGetXMLDesc'/>
           <ref name='virStoragePoolCreateXML'/>
           <ref name='virStoragePoolDefineXML'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
         <word name='descriptor'>
@@ -3727,6 +3731,7 @@
           <ref name='virStoragePoolDelete'/>
           <ref name='virStoragePoolGetXMLDesc'/>
           <ref name='virStoragePoolRefresh'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
           <ref name='virStorageVolDelete'/>
           <ref name='virStorageVolGetXMLDesc'/>
@@ -4956,6 +4961,7 @@
         </word>
         <word name='parent'>
           <ref name='virNodeDeviceGetParent'/>
+          <ref name='virStorageVolClone'/>
         </word>
         <word name='part'>
           <ref name='_virError'/>
@@ -4981,12 +4987,14 @@
           <ref name='virNodeDeviceGetXMLDesc'/>
           <ref name='virNodeListDevices'/>
           <ref name='virNodeNumOfDevices'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
           <ref name='virStorageVolGetXMLDesc'/>
         </word>
         <word name='passed'>
           <ref name='virConnectDomainEventRegister'/>
           <ref name='virConnectGetURI'/>
+          <ref name='virStorageVolClone'/>
         </word>
         <word name='path'>
           <ref name='virDomainBlockPeek'/>
@@ -4996,6 +5004,7 @@
           <ref name='virDomainMigrate'/>
           <ref name='virDomainRestore'/>
           <ref name='virDomainSave'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolGetPath'/>
           <ref name='virStorageVolLookupByPath'/>
         </word>
@@ -5056,6 +5065,7 @@
           <ref name='virDomainCreate'/>
           <ref name='virNetworkCreate'/>
           <ref name='virStoragePoolNumOfVolumes'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
         <word name='port'>
@@ -5814,6 +5824,7 @@
           <ref name='virDomainInterfaceStats'/>
           <ref name='virDomainMigrate'/>
           <ref name='virDomainSetVcpus'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
         <word name='supported'>
@@ -6024,6 +6035,9 @@
           <ref name='virConnectFindStoragePoolSources'/>
           <ref name='virDomainMigrate'/>
         </word>
+        <word name='typical'>
+          <ref name='virStorageVolClone'/>
+        </word>
         <word name='typically'>
           <ref name='virConnectRef'/>
           <ref name='virDomainMigrate'/>
@@ -6099,6 +6113,7 @@
           <ref name='virNodeListDevices'/>
           <ref name='virNodeNumOfDevices'/>
           <ref name='virStoragePoolRefresh'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
           <ref name='virStorageVolGetXMLDesc'/>
         </word>
@@ -6272,6 +6287,7 @@
         </word>
         <word name='via'>
           <ref name='virConnectOpenAuth'/>
+          <ref name='virStorageVolClone'/>
         </word>
         <word name='virConnect'>
           <ref name='virConnectDomainEventCallback'/>
@@ -6491,6 +6507,7 @@
           <ref name='virDomainSetVcpus'/>
         </word>
         <word name='vol'>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolRef'/>
         </word>
         <word name='volatile'>
@@ -6500,6 +6517,7 @@
         <word name='volumes'>
           <ref name='virStoragePoolNumOfVolumes'/>
           <ref name='virStoragePoolRefresh'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
         </word>
       </letter>
@@ -6584,6 +6602,7 @@
           <ref name='virDomainBlockStats'/>
           <ref name='virDomainInterfaceStats'/>
           <ref name='virStoragePoolNumOfVolumes'/>
+          <ref name='virStorageVolClone'/>
           <ref name='virStorageVolCreateXML'/>
           <ref name='virStorageVolGetName'/>
           <ref name='virStorageVolLookupByName'/>
@@ -6671,8 +6690,8 @@
       </letter>
     </chunk>
     <chunks>
-      <chunk name='chunk0' start='A' end='M'/>
-      <chunk name='chunk1' start='N' end='a'/>
+      <chunk name='chunk0' start='A' end='L'/>
+      <chunk name='chunk1' start='M' end='a'/>
       <chunk name='chunk2' start='b' end='c'/>
       <chunk name='chunk3' start='d' end='e'/>
       <chunk name='chunk4' start='f' end='g'/>
diff --git a/include/libvirt/libvirt.h b/include/libvirt/libvirt.h
index bd94a18..2612b10 100644
--- a/include/libvirt/libvirt.h
+++ b/include/libvirt/libvirt.h
@@ -1047,6 +1047,9 @@ const char*             virStorageVolGetKey             (virStorageVolPtr vol);
 virStorageVolPtr        virStorageVolCreateXML          (virStoragePoolPtr pool,
                                                          const char *xmldesc,
                                                          unsigned int flags);
+virStorageVolPtr        virStorageVolClone              (virStorageVolPtr vol,
+                                                         const char *xmldesc,
+                                                         unsigned int flags);
 int                     virStorageVolDelete             (virStorageVolPtr vol,
                                                          unsigned int flags);
 int                     virStorageVolRef                (virStorageVolPtr vol);
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index a028b21..0f4dd27 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -1047,6 +1047,9 @@ const char*             virStorageVolGetKey             (virStorageVolPtr vol);
 virStorageVolPtr        virStorageVolCreateXML          (virStoragePoolPtr pool,
                                                          const char *xmldesc,
                                                          unsigned int flags);
+virStorageVolPtr        virStorageVolClone              (virStorageVolPtr vol,
+                                                         const char *xmldesc,
+                                                         unsigned int flags);
 int                     virStorageVolDelete             (virStorageVolPtr vol,
                                                          unsigned int flags);
 int                     virStorageVolRef                (virStorageVolPtr vol);
diff --git a/qemud/remote.c b/qemud/remote.c
index e27820f..b5e27df 100644
--- a/qemud/remote.c
+++ b/qemud/remote.c
@@ -3831,6 +3831,32 @@ remoteDispatchStorageVolCreateXml (struct qemud_server *server ATTRIBUTE_UNUSED,
     return 0;
 }
 
+static int
+remoteDispatchStorageVolClone (struct qemud_server *server ATTRIBUTE_UNUSED,
+                               struct qemud_client *client ATTRIBUTE_UNUSED,
+                               virConnectPtr conn,
+                               remote_error *rerr,
+                               remote_storage_vol_clone_args *args,
+                               remote_storage_vol_clone_ret *ret)
+{
+    virStorageVolPtr vol, newvol;
+
+    vol = get_nonnull_storage_vol (conn, args->vol);
+    if (vol == NULL) {
+        remoteDispatchConnError(rerr, conn);
+        return -1;
+    }
+
+    newvol = virStorageVolClone (vol, args->xml, args->flags);
+    if (newvol == NULL) {
+        remoteDispatchConnError(rerr, conn);
+        return -1;
+    }
+
+    make_nonnull_storage_vol (&ret->vol, newvol);
+    virStorageVolFree(newvol);
+    return 0;
+}
 
 static int
 remoteDispatchStorageVolDelete (struct qemud_server *server ATTRIBUTE_UNUSED,
diff --git a/qemud/remote_dispatch_args.h b/qemud/remote_dispatch_args.h
index 6573729..b115038 100644
--- a/qemud/remote_dispatch_args.h
+++ b/qemud/remote_dispatch_args.h
@@ -103,3 +103,4 @@
     remote_node_device_re_attach_args val_remote_node_device_re_attach_args;
     remote_node_device_reset_args val_remote_node_device_reset_args;
     remote_domain_get_security_label_args val_remote_domain_get_security_label_args;
+    remote_storage_vol_clone_args val_remote_storage_vol_clone_args;
diff --git a/qemud/remote_dispatch_prototypes.h b/qemud/remote_dispatch_prototypes.h
index c44e5ce..b671fbc 100644
--- a/qemud/remote_dispatch_prototypes.h
+++ b/qemud/remote_dispatch_prototypes.h
@@ -793,6 +793,13 @@ static int remoteDispatchStoragePoolUndefine(
     remote_error *err,
     remote_storage_pool_undefine_args *args,
     void *ret);
+static int remoteDispatchStorageVolClone(
+    struct qemud_server *server,
+    struct qemud_client *client,
+    virConnectPtr conn,
+    remote_error *err,
+    remote_storage_vol_clone_args *args,
+    remote_storage_vol_clone_ret *ret);
 static int remoteDispatchStorageVolCreateXml(
     struct qemud_server *server,
     struct qemud_client *client,
diff --git a/qemud/remote_dispatch_ret.h b/qemud/remote_dispatch_ret.h
index 136b1cc..e89f02f 100644
--- a/qemud/remote_dispatch_ret.h
+++ b/qemud/remote_dispatch_ret.h
@@ -88,3 +88,4 @@
     remote_node_device_list_caps_ret val_remote_node_device_list_caps_ret;
     remote_domain_get_security_label_ret val_remote_domain_get_security_label_ret;
     remote_node_get_security_model_ret val_remote_node_get_security_model_ret;
+    remote_storage_vol_clone_ret val_remote_storage_vol_clone_ret;
diff --git a/qemud/remote_dispatch_table.h b/qemud/remote_dispatch_table.h
index 75f184b..e19a299 100644
--- a/qemud/remote_dispatch_table.h
+++ b/qemud/remote_dispatch_table.h
@@ -607,13 +607,18 @@
     .args_filter = (xdrproc_t) xdr_remote_node_device_reset_args,
     .ret_filter = (xdrproc_t) xdr_void,
 },
-{   /* DomainGetSecurityLabel => 118 */
+{   /* DomainGetSecurityLabel => 121 */
     .fn = (dispatch_fn) remoteDispatchDomainGetSecurityLabel,
     .args_filter = (xdrproc_t) xdr_remote_domain_get_security_label_args,
     .ret_filter = (xdrproc_t) xdr_remote_domain_get_security_label_ret,
 },
-{   /* NodeGetSecurityModel => 119 */
+{   /* NodeGetSecurityModel => 122 */
     .fn = (dispatch_fn) remoteDispatchNodeGetSecurityModel,
     .args_filter = (xdrproc_t) xdr_void,
     .ret_filter = (xdrproc_t) xdr_remote_node_get_security_model_ret,
 },
+{   /* StorageVolClone => 123 */
+    .fn = (dispatch_fn) remoteDispatchStorageVolClone,
+    .args_filter = (xdrproc_t) xdr_remote_storage_vol_clone_args,
+    .ret_filter = (xdrproc_t) xdr_remote_storage_vol_clone_ret,
+},
diff --git a/qemud/remote_protocol.c b/qemud/remote_protocol.c
index 7f7c628..605321f 100644
--- a/qemud/remote_protocol.c
+++ b/qemud/remote_protocol.c
@@ -1992,6 +1992,28 @@ xdr_remote_storage_vol_create_xml_ret (XDR *xdrs, remote_storage_vol_create_xml_
 }
 
 bool_t
+xdr_remote_storage_vol_clone_args (XDR *xdrs, remote_storage_vol_clone_args *objp)
+{
+
+         if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol))
+                 return FALSE;
+         if (!xdr_remote_nonnull_string (xdrs, &objp->xml))
+                 return FALSE;
+         if (!xdr_u_int (xdrs, &objp->flags))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
+xdr_remote_storage_vol_clone_ret (XDR *xdrs, remote_storage_vol_clone_ret *objp)
+{
+
+         if (!xdr_remote_nonnull_storage_vol (xdrs, &objp->vol))
+                 return FALSE;
+        return TRUE;
+}
+
+bool_t
 xdr_remote_storage_vol_delete_args (XDR *xdrs, remote_storage_vol_delete_args *objp)
 {
 
diff --git a/qemud/remote_protocol.h b/qemud/remote_protocol.h
index 75def5e..a22694e 100644
--- a/qemud/remote_protocol.h
+++ b/qemud/remote_protocol.h
@@ -1120,6 +1120,18 @@ struct remote_storage_vol_create_xml_ret {
 };
 typedef struct remote_storage_vol_create_xml_ret remote_storage_vol_create_xml_ret;
 
+struct remote_storage_vol_clone_args {
+        remote_nonnull_storage_vol vol;
+        remote_nonnull_string xml;
+        u_int flags;
+};
+typedef struct remote_storage_vol_clone_args remote_storage_vol_clone_args;
+
+struct remote_storage_vol_clone_ret {
+        remote_nonnull_storage_vol vol;
+};
+typedef struct remote_storage_vol_clone_ret remote_storage_vol_clone_ret;
+
 struct remote_storage_vol_delete_args {
         remote_nonnull_storage_vol vol;
         u_int flags;
@@ -1397,6 +1409,7 @@ enum remote_procedure {
         REMOTE_PROC_NODE_DEVICE_RESET = 120,
         REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL = 121,
         REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
+        REMOTE_PROC_STORAGE_VOL_CLONE = 123,
 };
 typedef enum remote_procedure remote_procedure;
 
@@ -1605,6 +1618,8 @@ extern  bool_t xdr_remote_storage_vol_lookup_by_path_args (XDR *, remote_storage
 extern  bool_t xdr_remote_storage_vol_lookup_by_path_ret (XDR *, remote_storage_vol_lookup_by_path_ret*);
 extern  bool_t xdr_remote_storage_vol_create_xml_args (XDR *, remote_storage_vol_create_xml_args*);
 extern  bool_t xdr_remote_storage_vol_create_xml_ret (XDR *, remote_storage_vol_create_xml_ret*);
+extern  bool_t xdr_remote_storage_vol_clone_args (XDR *, remote_storage_vol_clone_args*);
+extern  bool_t xdr_remote_storage_vol_clone_ret (XDR *, remote_storage_vol_clone_ret*);
 extern  bool_t xdr_remote_storage_vol_delete_args (XDR *, remote_storage_vol_delete_args*);
 extern  bool_t xdr_remote_storage_vol_dump_xml_args (XDR *, remote_storage_vol_dump_xml_args*);
 extern  bool_t xdr_remote_storage_vol_dump_xml_ret (XDR *, remote_storage_vol_dump_xml_ret*);
@@ -1816,6 +1831,8 @@ extern bool_t xdr_remote_storage_vol_lookup_by_path_args ();
 extern bool_t xdr_remote_storage_vol_lookup_by_path_ret ();
 extern bool_t xdr_remote_storage_vol_create_xml_args ();
 extern bool_t xdr_remote_storage_vol_create_xml_ret ();
+extern bool_t xdr_remote_storage_vol_clone_args ();
+extern bool_t xdr_remote_storage_vol_clone_ret ();
 extern bool_t xdr_remote_storage_vol_delete_args ();
 extern bool_t xdr_remote_storage_vol_dump_xml_args ();
 extern bool_t xdr_remote_storage_vol_dump_xml_ret ();
diff --git a/qemud/remote_protocol.x b/qemud/remote_protocol.x
index 2d8e6a2..2b5d8c8 100644
--- a/qemud/remote_protocol.x
+++ b/qemud/remote_protocol.x
@@ -1002,6 +1002,16 @@ struct remote_storage_vol_create_xml_ret {
     remote_nonnull_storage_vol vol;
 };
 
+struct remote_storage_vol_clone_args {
+    remote_nonnull_storage_vol vol;
+    remote_nonnull_string xml;
+    unsigned flags;
+};
+
+struct remote_storage_vol_clone_ret {
+    remote_nonnull_storage_vol vol;
+};
+
 struct remote_storage_vol_delete_args {
     remote_nonnull_storage_vol vol;
     unsigned flags;
@@ -1270,7 +1280,9 @@ enum remote_procedure {
     REMOTE_PROC_NODE_DEVICE_RESET = 120,
 
     REMOTE_PROC_DOMAIN_GET_SECURITY_LABEL = 121,
-    REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122
+    REMOTE_PROC_NODE_GET_SECURITY_MODEL = 122,
+
+    REMOTE_PROC_STORAGE_VOL_CLONE = 123
 };
 
 /* Custom RPC structure. */
diff --git a/src/driver.h b/src/driver.h
index 88c2b0a..bb308ab 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -585,6 +585,10 @@ typedef char *
 typedef char *
     (*virDrvStorageVolGetPath)               (virStorageVolPtr vol);
 
+typedef virStorageVolPtr
+    (*virDrvStorageVolClone)                 (virStorageVolPtr vol,
+                                              const char *xmldesc,
+                                              unsigned int flags);
 
 
 typedef struct _virStorageDriver virStorageDriver;
@@ -636,6 +640,7 @@ struct _virStorageDriver {
     virDrvStorageVolGetInfo volGetInfo;
     virDrvStorageVolGetXMLDesc volGetXMLDesc;
     virDrvStorageVolGetPath volGetPath;
+    virDrvStorageVolClone volClone;
 };
 
 #ifdef WITH_LIBVIRTD
diff --git a/src/libvirt.c b/src/libvirt.c
index d6de773..9047d55 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -6718,7 +6718,7 @@ virStorageVolCreateXML(virStoragePoolPtr pool,
     virResetLastError();
 
     if (!VIR_IS_STORAGE_POOL(pool)) {
-        virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__);
+        virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_POOL, __FUNCTION__);
         return (NULL);
     }
 
@@ -6985,6 +6985,53 @@ error:
 }
 
 
+/**
+ * virStorageVolClone:
+ * @vol: pointer to storage vol to clone
+ * @xmldesc: description of volume to create
+ * @flags: flags for creation (unused, pass 0)
+ *
+ * Clone a storage volume within the parent pool.
+ * Information for the new volume (name, perms)
+ * are passed via a typical volume XML description
+ * Not all pools support cloning of volumes
+ *
+ * return the storage volume, or NULL on error
+ */
+virStorageVolPtr
+virStorageVolClone(virStorageVolPtr vol,
+                   const char *xmldesc,
+                   unsigned int flags)
+{
+    DEBUG("vol=%p, flags=%u", vol, flags);
+
+    virResetLastError();
+
+    if (!VIR_IS_STORAGE_VOL(vol)) {
+        virLibConnError(NULL, VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__);
+        return (NULL);
+    }
+
+    if (vol->conn->flags & VIR_CONNECT_RO) {
+        virLibConnError(vol->conn, VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+        goto error;
+    }
+
+    if (vol->conn->storageDriver && vol->conn->storageDriver->volClone) {
+        virStorageVolPtr ret;
+        ret = vol->conn->storageDriver->volClone (vol, xmldesc, flags);
+        if (!ret)
+            goto error;
+        return ret;
+    }
+
+    virLibConnError (vol->conn, VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+    /* Copy to connection error object for back compatability */
+    virSetConnError(vol->conn);
+    return NULL;
+}
 
 
 /**
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index f7ebbc3..0f92e68 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -244,6 +244,7 @@ LIBVIRT_0.6.0 {
 	virStoragePoolRef;
 	virStorageVolRef;
 	virNodeDeviceRef;
+	virStorageVolClone;
 
 } LIBVIRT_0.5.0;
 
diff --git a/src/remote_internal.c b/src/remote_internal.c
index cc658c2..46d0628 100644
--- a/src/remote_internal.c
+++ b/src/remote_internal.c
@@ -4599,6 +4599,35 @@ done:
     return rv;
 }
 
+static virStorageVolPtr
+remoteStorageVolClone (virStorageVolPtr vol, const char *xmlDesc,
+                       unsigned int flags)
+{
+    virStorageVolPtr newvol = NULL;
+    remote_storage_vol_clone_args args;
+    remote_storage_vol_clone_ret ret;
+    struct private_data *priv = vol->conn->storagePrivateData;
+
+    remoteDriverLock(priv);
+
+    make_nonnull_storage_vol (&args.vol, vol);
+    args.xml = (char *) xmlDesc;
+    args.flags = flags;
+
+    memset (&ret, 0, sizeof ret);
+    if (call (vol->conn, priv, 0, REMOTE_PROC_STORAGE_VOL_CLONE,
+              (xdrproc_t) xdr_remote_storage_vol_clone_args, (char *) &args,
+              (xdrproc_t) xdr_remote_storage_vol_clone_ret, (char *) &ret) == -1)
+        goto done;
+
+    newvol = get_nonnull_storage_vol (vol->conn, ret.vol);
+    xdr_free ((xdrproc_t) &xdr_remote_storage_vol_clone_ret, (char *) &ret);
+
+done:
+    remoteDriverUnlock(priv);
+    return newvol;
+}
+
 
 /*----------------------------------------------------------------------*/
 
@@ -6961,6 +6990,7 @@ static virStorageDriver storage_driver = {
     .volGetInfo = remoteStorageVolGetInfo,
     .volGetXMLDesc = remoteStorageVolDumpXML,
     .volGetPath = remoteStorageVolGetPath,
+    .volClone = remoteStorageVolClone,
 };
 
 static virDeviceMonitor dev_monitor = {
diff --git a/src/storage_backend.h b/src/storage_backend.h
index c9c1e35..af3ae59 100644
--- a/src/storage_backend.h
+++ b/src/storage_backend.h
@@ -38,6 +38,7 @@ typedef int (*virStorageBackendBuildVol)(virConnectPtr conn, virStorageVolDefPtr
 typedef int (*virStorageBackendCreateVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol);
 typedef int (*virStorageBackendRefreshVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol);
 typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr vol, unsigned int flags);
+typedef int (*virStorageBackendCloneVol)(virConnectPtr conn, virStorageVolDefPtr origvol, virStorageVolDefPtr newvol, unsigned int flags);
 
 
 typedef struct _virStorageBackend virStorageBackend;
@@ -57,6 +58,7 @@ struct _virStorageBackend {
     virStorageBackendCreateVol createVol;
     virStorageBackendRefreshVol refreshVol;
     virStorageBackendDeleteVol deleteVol;
+    virStorageBackendCloneVol cloneVol;
 };
 
 virStorageBackendPtr virStorageBackendForType(int type);
diff --git a/src/storage_backend_fs.c b/src/storage_backend_fs.c
index 7a1bba8..29a644a 100644
--- a/src/storage_backend_fs.c
+++ b/src/storage_backend_fs.c
@@ -980,7 +980,6 @@ virStorageBackendFileSystemDelete(virConnectPtr conn,
     return 0;
 }
 
-
 /**
  * Set up a volume definition to be added to a pool's volume list, but
  * don't do any file creation or allocation. By separating the two processes,
@@ -1232,6 +1231,116 @@ virStorageBackendFileSystemVolBuild(virConnectPtr conn,
     return 0;
 }
 
+/*
+ * Create a copy of a volume
+ */
+static int
+virStorageBackendFileSystemVolClone(virConnectPtr conn,
+                                    virStorageVolDefPtr origvol,
+                                    virStorageVolDefPtr newvol,
+                                    unsigned int flags ATTRIBUTE_UNUSED)
+{
+
+    int origfd = -1;
+    int newfd = -1;
+    int ret = -1;
+    size_t bytes = 1024 * 1024;
+
+    if ((origfd = open(origvol->target.path, O_RDONLY)) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot open path '%s'"),
+                             origvol->target.path);
+        goto cleanup;
+    }
+
+    if ((newfd = open(newvol->target.path, O_RDWR | O_CREAT | O_EXCL,
+                      newvol->target.perms.mode)) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot create path '%s'"),
+                             newvol->target.path);
+        goto cleanup;
+    }
+
+    newvol->capacity = origvol->capacity;
+
+    /* Seek to the final size, so the capacity is available upfront
+     * for progress reporting */
+    if (ftruncate(newfd, newvol->capacity) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot extend file '%s'"),
+                             newvol->target.path);
+        goto cleanup;
+    }
+
+    int amtread = -1;
+    while (amtread != 0) {
+        char *buf[bytes];
+
+        if ((amtread = saferead(origfd, buf, bytes)) < 0) {
+            virReportSystemError(conn, errno,
+                                 _("failed reading from file '%s'"),
+                                 origvol->target.path);
+            goto cleanup;
+        }
+
+        if (safewrite(newfd, buf, amtread) < 0) {
+            virReportSystemError(conn, errno,
+                                 _("failed writing to file '%s'"),
+                                 newvol->target.path);
+            goto cleanup;
+        }
+    }
+
+    /* We can only chown/grp if root */
+    if (getuid() == 0) {
+        if (fchown(newfd, newvol->target.perms.uid,
+                   newvol->target.perms.gid) < 0) {
+            virReportSystemError(conn, errno,
+                                 _("cannot set file owner '%s'"),
+                                 newvol->target.path);
+            goto cleanup;
+        }
+    }
+    if (fchmod(newfd, newvol->target.perms.mode) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot set file mode '%s'"),
+                             newvol->target.path);
+        goto cleanup;
+    }
+
+    /* Refresh allocation / permissions info */
+    if (virStorageBackendUpdateVolTargetInfoFD(conn, &newvol->target, newfd,
+                                               &newvol->allocation,
+                                               NULL) < 0) {
+        goto cleanup;
+    }
+
+    if (close(origfd) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot close file '%s'"),
+                             origvol->target.path);
+        goto cleanup;
+    }
+    origfd = -1;
+
+    if (close(newfd) < 0) {
+        virReportSystemError(conn, errno,
+                             _("cannot close file '%s'"),
+                             newvol->target.path);
+        goto cleanup;
+    }
+    newfd = -1;
+
+    ret = 0;
+
+cleanup:
+    if (origfd != -1)
+        close(origfd);
+    if (newfd != -1)
+        close(newfd);
+
+    return ret;
+}
 
 /**
  * Remove a volume - just unlinks for now
@@ -1263,7 +1372,9 @@ virStorageBackendFileSystemVolRefresh(virConnectPtr conn,
                                       virStoragePoolObjPtr pool ATTRIBUTE_UNUSED,
                                       virStorageVolDefPtr vol)
 {
-    /* Refresh allocation / permissions info in case its changed */
+    /* Refresh allocation / permissions info in case its changed,
+    * but not 'capacity', since for formats like qcow2 this is only
+    * determined via the file header */
     return virStorageBackendUpdateVolInfo(conn, vol, 0);
 }
 
@@ -1277,6 +1388,7 @@ virStorageBackend virStorageBackendDirectory = {
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
+    .cloneVol = virStorageBackendFileSystemVolClone,
 };
 
 #if WITH_STORAGE_FS
@@ -1292,6 +1404,7 @@ virStorageBackend virStorageBackendFileSystem = {
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
+    .cloneVol = virStorageBackendFileSystemVolClone,
 };
 virStorageBackend virStorageBackendNetFileSystem = {
     .type = VIR_STORAGE_POOL_NETFS,
@@ -1306,5 +1419,6 @@ virStorageBackend virStorageBackendNetFileSystem = {
     .createVol = virStorageBackendFileSystemVolCreate,
     .refreshVol = virStorageBackendFileSystemVolRefresh,
     .deleteVol = virStorageBackendFileSystemVolDelete,
+    .cloneVol = virStorageBackendFileSystemVolClone,
 };
 #endif /* WITH_STORAGE_FS */
diff --git a/src/storage_driver.c b/src/storage_driver.c
index 3803474..86903bb 100644
--- a/src/storage_driver.c
+++ b/src/storage_driver.c
@@ -1350,7 +1350,6 @@ storageVolumeDelete(virStorageVolPtr obj,
         virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
                               _("volume '%s' is still being allocated."),
                               vol->name);
-
         goto cleanup;
     }
 
@@ -1532,6 +1531,118 @@ cleanup:
 
 
 
+static virStorageVolPtr
+storageVolumeClone(virStorageVolPtr obj,
+                   const char *xmldesc,
+                   unsigned int flags) {
+    virStorageDriverStatePtr driver = obj->conn->storagePrivateData;
+    virStoragePoolObjPtr pool;
+    virStorageBackendPtr backend;
+    virStorageVolDefPtr origvol = NULL, newvol = NULL;
+    virStorageVolPtr ret = NULL, volobj = NULL;
+    int buildret;
+
+    storageDriverLock(driver);
+    pool = virStoragePoolObjFindByName(&driver->pools, obj->pool);
+    storageDriverUnlock(driver);
+
+    if (!pool) {
+        virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL,
+                              "%s", _("no storage pool with matching uuid"));
+        goto cleanup;
+    }
+
+    if (!virStoragePoolObjIsActive(pool)) {
+        virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+                              "%s", _("storage pool is not active"));
+        goto cleanup;
+    }
+
+    if ((backend = virStorageBackendForType(pool->def->type)) == NULL)
+        goto cleanup;
+
+    origvol = virStorageVolDefFindByName(pool, obj->name);
+
+    if (!origvol) {
+        virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL,
+                              "%s", _("no storage vol with matching name"));
+        goto cleanup;
+    }
+
+    newvol = virStorageVolDefParse(obj->conn, pool->def, xmldesc, NULL);
+    if (newvol == NULL)
+        goto cleanup;
+
+    if (virStorageVolDefFindByName(pool, newvol->name)) {
+        virStorageReportError(obj->conn, VIR_ERR_INVALID_STORAGE_POOL,
+                              _("storage vol '%s' already exists"),
+                              newvol->name);
+        goto cleanup;
+    }
+
+    if (!backend->cloneVol || !backend->buildVol) {
+        virStorageReportError(obj->conn, VIR_ERR_NO_SUPPORT,
+                              "%s", _("storage pool does not support volume cloning"));
+        goto cleanup;
+    }
+
+    if (origvol->building) {
+        virStorageReportError(obj->conn, VIR_ERR_INTERNAL_ERROR,
+                              _("volume '%s' is still being allocated."),
+                              origvol->name);
+        goto cleanup;
+    }
+
+    if (VIR_REALLOC_N(pool->volumes.objs,
+                      pool->volumes.count+1) < 0) {
+        virReportOOMError(obj->conn);
+        goto cleanup;
+    }
+
+    if (backend->refreshVol &&
+        backend->refreshVol(obj->conn, pool, origvol) < 0)
+        goto cleanup;
+
+    /* If backend supports async volume creation, we can use it here */
+    if (backend->createVol(obj->conn, pool, newvol) < 0) {
+        goto cleanup;
+    }
+
+    pool->volumes.objs[pool->volumes.count++] = newvol;
+    volobj = virGetStorageVol(obj->conn, pool->def->name, newvol->name,
+                              newvol->key);
+
+    /* Drop the pool lock during volume allocation */
+    pool->asyncjobs++;
+    virStoragePoolObjUnlock(pool);
+
+    newvol->building = 1;
+    buildret = backend->cloneVol(obj->conn, origvol, newvol, flags);
+    newvol->building = 0;
+    newvol = NULL;
+
+    virStoragePoolObjLock(pool);
+    pool->asyncjobs--;
+
+    if (buildret < 0) {
+        virStoragePoolObjUnlock(pool);
+        storageVolumeDelete(volobj, 0);
+        pool = NULL;
+        goto cleanup;
+    }
+
+    ret = volobj;
+    volobj = NULL;
+
+cleanup:
+    if (volobj)
+        virUnrefStorageVol(volobj);
+    virStorageVolDefFree(newvol);
+    if (pool)
+        virStoragePoolObjUnlock(pool);
+    return ret;
+}
+
 
 
 static virStorageDriver storageDriver = {
@@ -1569,6 +1680,7 @@ static virStorageDriver storageDriver = {
     .volGetInfo = storageVolumeGetInfo,
     .volGetXMLDesc = storageVolumeGetXMLDesc,
     .volGetPath = storageVolumeGetPath,
+    .volClone = storageVolumeClone,
 };
 
 
diff --git a/src/virsh.c b/src/virsh.c
index 26764a7..feaef94 100644
--- a/src/virsh.c
+++ b/src/virsh.c
@@ -4031,6 +4031,74 @@ cmdVolCreate(vshControl *ctl, const vshCmd *cmd)
 }
 
 /*
+ * "vol-clone" command
+ */
+static const vshCmdInfo info_vol_clone[] = {
+    {"help", gettext_noop("clone a volume.")},
+    {"desc", gettext_noop("Clone an existing volume.")},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_vol_clone[] = {
+    {"pool", VSH_OT_STRING, 0, gettext_noop("pool name or uuid")},
+    {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, gettext_noop("orig vol name or key")},
+    {"newname", VSH_OT_STRING, VSH_OFLAG_REQ, gettext_noop("clone name")},
+    {NULL, 0, 0, NULL}
+};
+
+static int
+cmdVolClone(vshControl *ctl, const vshCmd *cmd)
+{
+    virStorageVolPtr origvol = NULL, newvol = NULL;
+    virBuffer buf = VIR_BUFFER_INITIALIZER;
+    char *name, *xml = NULL;
+    int found;
+    int ret = FALSE;
+
+    if (!vshConnectionUsability(ctl, ctl->conn, TRUE))
+        goto cleanup;
+
+    if (!(origvol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL)))
+        goto cleanup;
+
+    name = vshCommandOptString(cmd, "newname", &found);
+    if (!found)
+        goto cleanup;
+
+    virBufferAddLit(&buf, "<volume>\n");
+    virBufferVSprintf(&buf, "  <name>%s</name>\n", name);
+    virBufferVSprintf(&buf, "  <capacity>1</capacity>\n");
+    virBufferAddLit(&buf, "</volume>\n");
+
+    if (virBufferError(&buf)) {
+        vshPrint(ctl, "%s", _("Failed to allocate XML buffer"));
+        goto cleanup;
+    }
+    xml = virBufferContentAndReset(&buf);
+
+    newvol = virStorageVolClone(origvol, xml, 0);
+
+    if (newvol != NULL) {
+        vshPrint(ctl, _("Vol %s cloned from %s\n"),
+                 virStorageVolGetName(newvol), virStorageVolGetName(origvol));
+        virStorageVolFree(newvol);
+    } else {
+        vshError(ctl, FALSE, _("Failed to clone vol from %s"),
+                 virStorageVolGetName(origvol));
+        goto cleanup;
+    }
+
+    ret = TRUE;
+
+cleanup:
+    if (xml)
+        free(xml);
+    if (origvol)
+        virStorageVolFree(origvol);
+    return ret;
+}
+
+/*
  * "vol-delete" command
  */
 static const vshCmdInfo info_vol_delete[] = {
@@ -5931,6 +5999,7 @@ static const vshCmdDef commands[] = {
 
     {"vol-create", cmdVolCreate, opts_vol_create, info_vol_create},
     {"vol-create-as", cmdVolCreateAs, opts_vol_create_as, info_vol_create_as},
+    {"vol-clone", cmdVolClone, opts_vol_clone, info_vol_clone},
     {"vol-delete", cmdVolDelete, opts_vol_delete, info_vol_delete},
     {"vol-dumpxml", cmdVolDumpXML, opts_vol_dumpxml, info_vol_dumpxml},
     {"vol-info", cmdVolInfo, opts_vol_info, info_vol_info},
--
Libvir-list mailing list
Libvir-list@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libvir-list

[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]