This commit adds a new storage pool driver, btrfs, which can be used to
manage btrfs subvolumes on an existing btrfs filesystem. The driver can
create new blank subvolumes and snapshots of existing subvolumes as well
as delete existing subvolumes.
The subvolumes created are automatically made visible on the host side
and can be attached to domains using the <filesystem> tags as defined in
'format domain' documentation.
Libvirt volumes do not implement quotas at the moment because the current
(btrfs-progs-0.20.rc1.20130501git7854c8b-4.fc20.x86_64) support for quota
management in btrfs-progs is lacking the necessary features, for example
it's not possible to see the quota assigned to a certain subvolume and
usage information is only updated on syncfs(2). Quota support will be
implemented once the tools gain the necessary features.
Signed-off-by: Oskari Saarenmaa <os(a)ohmu.fi>
---
configure.ac | 23 +-
docs/drivers.html.in | 1 +
docs/schemas/storagepool.rng | 11 +
docs/storage.html.in | 60 +++++
include/libvirt/libvirt.h.in | 1 +
libvirt.spec.in | 2 +
src/Makefile.am | 8 +
src/conf/storage_conf.c | 12 +-
src/conf/storage_conf.h | 1 +
src/storage/storage_backend.c | 6 +
src/storage/storage_backend_btrfs.c | 284 ++++++++++++++++++++++
src/storage/storage_backend_btrfs.h | 30 +++
tests/storagepoolxml2xmlin/pool-btrfs.xml | 7 +
tests/storagepoolxml2xmlout/pool-btrfs.xml | 17 ++
tests/storagepoolxml2xmltest.c | 1 +
tests/storagevolxml2xmlin/vol-btrfs-snapshot.xml | 11 +
tests/storagevolxml2xmlin/vol-btrfs.xml | 8 +
tests/storagevolxml2xmlout/vol-btrfs-snapshot.xml | 26 ++
tests/storagevolxml2xmlout/vol-btrfs.xml | 17 ++
tests/storagevolxml2xmltest.c | 2 +
20 files changed, 525 insertions(+), 3 deletions(-)
create mode 100644 src/storage/storage_backend_btrfs.c
create mode 100644 src/storage/storage_backend_btrfs.h
create mode 100644 tests/storagepoolxml2xmlin/pool-btrfs.xml
create mode 100644 tests/storagepoolxml2xmlout/pool-btrfs.xml
create mode 100644 tests/storagevolxml2xmlin/vol-btrfs-snapshot.xml
create mode 100644 tests/storagevolxml2xmlin/vol-btrfs.xml
create mode 100644 tests/storagevolxml2xmlout/vol-btrfs-snapshot.xml
create mode 100644 tests/storagevolxml2xmlout/vol-btrfs.xml
diff --git a/configure.ac b/configure.ac
index f853e03..100c87d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1562,6 +1562,8 @@ AC_ARG_WITH([storage-rbd],
AC_HELP_STRING([--with-storage-rbd], [with RADOS Block Device backend for the storage
driver @<:@default=check@:>@]),[],[with_storage_rbd=check])
AC_ARG_WITH([storage-sheepdog],
AC_HELP_STRING([--with-storage-sheepdog], [with Sheepdog backend for the storage driver
@<:@default=check@:>@]),[],[with_storage_sheepdog=check])
+AC_ARG_WITH([storage-btrfs],
+ AC_HELP_STRING([--with-storage-btrfs], [with Btrfs backend for the storage driver
@<:@default=check@:>@]),[],[with_storage_btrfs=check])
if test "$with_libvirtd" = "no"; then
with_storage_dir=no
@@ -1774,6 +1776,24 @@ fi
AM_CONDITIONAL([WITH_STORAGE_SHEEPDOG],
[test "$with_storage_sheepdog" = "yes"])
+if test "$with_storage_btrfs" = "yes" || test
"$with_storage_btrfs" = "check"; then
+ AC_PATH_PROG([BTRFS], [btrfs], [], [$PATH:/sbin:/usr/sbin])
+
+ if test "$with_storage_btrfs" = "yes" ; then
+ if test -z "$BTRFS" ; then AC_MSG_ERROR([We need btrfs -command for btrfs
storage driver]) ; fi
+ else
+ if test -z "$BTRFS" ; then with_storage_btrfs=no ; fi
+ if test "$with_storage_btrfs" = "check" ; then
with_storage_btrfs=yes ; fi
+ fi
+
+ if test "$with_storage_btrfs" = "yes" ; then
+ AC_DEFINE_UNQUOTED([WITH_STORAGE_BTRFS], 1, [whether Btrfs backend for storage driver
is enabled])
+ AC_DEFINE_UNQUOTED([BTRFS],["$BTRFS"],[Location of btrfs program])
+ fi
+fi
+AM_CONDITIONAL([WITH_STORAGE_BTRFS], [test "$with_storage_btrfs" =
"yes"])
+
+
LIBPARTED_CFLAGS=
LIBPARTED_LIBS=
@@ -1861,7 +1881,7 @@ AC_SUBST([DEVMAPPER_CFLAGS])
AC_SUBST([DEVMAPPER_LIBS])
with_storage=no
-for backend in dir fs lvm iscsi scsi mpath rbd disk; do
+for backend in dir fs lvm iscsi scsi mpath rbd disk btrfs; do
if eval test \$with_storage_$backend = yes; then
with_storage=yes
break
@@ -2573,6 +2593,7 @@ AC_MSG_NOTICE([ mpath: $with_storage_mpath])
AC_MSG_NOTICE([ Disk: $with_storage_disk])
AC_MSG_NOTICE([ RBD: $with_storage_rbd])
AC_MSG_NOTICE([Sheepdog: $with_storage_sheepdog])
+AC_MSG_NOTICE([ Btrfs: $with_storage_btrfs])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Security Drivers])
AC_MSG_NOTICE([])
diff --git a/docs/drivers.html.in b/docs/drivers.html.in
index 7aa44f3..d9eb6d7 100644
--- a/docs/drivers.html.in
+++ b/docs/drivers.html.in
@@ -48,6 +48,7 @@
<li><strong><a
href="storage.html#StorageBackendMultipath">Multipath
backend</a></strong></li>
<li><strong><a
href="storage.html#StorageBackendRBD">RBD (RADOS Block Device)
backend</a></strong></li>
<li><strong><a
href="storage.html#StorageBackendSheepdog">Sheepdog
backend</a></strong></li>
+ <li><strong><a
href="storage.html#StorageBackendBtrfs">Btrfs subvolume
backend</a></strong></li>
</ul>
</body>
</html>
diff --git a/docs/schemas/storagepool.rng b/docs/schemas/storagepool.rng
index a4ef5af..6435566 100644
--- a/docs/schemas/storagepool.rng
+++ b/docs/schemas/storagepool.rng
@@ -21,6 +21,7 @@
<ref name='poolmpath'/>
<ref name='poolrbd'/>
<ref name='poolsheepdog'/>
+ <ref name='poolbtrfs'/>
</choice>
</element>
</define>
@@ -125,6 +126,16 @@
<ref name='sourcesheepdog'/>
</define>
+ <define name='poolbtrfs'>
+ <attribute name='type'>
+ <value>btrfs</value>
+ </attribute>
+ <ref name='commonmetadata'/>
+ <ref name='sizing'/>
+ <ref name='sourcedir'/>
+ <ref name='target'/>
+ </define>
+
<define name='sourceinfovendor'>
<optional>
<element name='vendor'>
diff --git a/docs/storage.html.in b/docs/storage.html.in
index 1181444..4d4690c 100644
--- a/docs/storage.html.in
+++ b/docs/storage.html.in
@@ -114,6 +114,9 @@
<li>
<a href="#StorageBackendSheepdog">Sheepdog backend</a>
</li>
+ <li>
+ <a href="#StorageBackendBtrfs">Btrfs subvolume backend</a>
+ </li>
</ul>
<h2><a name="StorageBackendDir">Directory
pool</a></h2>
@@ -647,5 +650,62 @@
The Sheepdog pool does not use the volume format type element.
</p>
+ <h2><a name="StorageBackendBtrfs">Btrfs subolume
pools</a></h2>
+ <p>
+ This provides a pool based on a Btrfs filesystem using subvolumes and
+ optionally their snapshots. Btrfs subvolume pool can be used to create
+ subvolumes ans snapshots on the host side and the created volumes can
+ be attached as filesystems to LXC domains.
+
+ The btrfs filesystem must already be formatted and mounted. Subvolume
+ quotas are not supported at the moment.
+
+ <span class="since">Since 1.1.3</span>
+ </p>
+
+ <h3>Example pool input</h3>
+ <pre>
+ <pool type="btrfs">
+ <name>lxcdomains</name>
+ <target>
+ <path>/lxc</path>
+ </target>
+ </pool></pre>
+
+ <h3>Example volume output</h3>
+ <pre>
+ <volume>
+ <name>vanilla</name>
+ <key>/lxc/vanilla</key>
+ <source>
+ </source>
+ <capacity unit='bytes'>0</capacity>
+ <allocation unit='bytes'>0</allocation>
+ <target>
+ <path>/lxc/vanilla</path>
+ <permissions>
+ <mode>00</mode>
+ <owner>0</owner>
+ <group>0</group>
+ </permissions>
+ </target>
+ </volume></pre>
+
+ <h3>Example filesystem attachment</h3>
+ <p>Btrfs volumes can be attached to LXC guests. Information about
+ attaching a filesystem to a guest can be found at the
+ <a href="formatdomain.html#elementsFilesystems">format
domain</a>
+ page.</p>
+
+ <h3>Valid pool format types</h3>
+ <p>
+ The Btrfs pool does not use the pool format type element.
+ </p>
+
+ <h3>Valid volume format types</h3>
+ <p>
+ The Btrfs pool does not use the volume format type element.
+ </p>
+
</body>
</html>
diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index a47e33c..6106ad5 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -3028,6 +3028,7 @@ typedef enum {
VIR_CONNECT_LIST_STORAGE_POOLS_MPATH = 1 << 13,
VIR_CONNECT_LIST_STORAGE_POOLS_RBD = 1 << 14,
VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG = 1 << 15,
+ VIR_CONNECT_LIST_STORAGE_POOLS_BTRFS = 1 << 16,
} virConnectListAllStoragePoolsFlags;
int virConnectListAllStoragePools(virConnectPtr conn,
diff --git a/libvirt.spec.in b/libvirt.spec.in
index e94901a..3867b99 100644
--- a/libvirt.spec.in
+++ b/libvirt.spec.in
@@ -730,6 +730,8 @@ Requires: device-mapper
Requires: device-mapper
%endif
%if %{with_storage_sheepdog}
+# For Btrfs storage
+Requires: btrfs-progs
# For Sheepdog support
Requires: sheepdog
%endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 636bcbc..cdcda36 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -789,6 +789,9 @@ STORAGE_DRIVER_RBD_SOURCES = \
STORAGE_DRIVER_SHEEPDOG_SOURCES = \
storage/storage_backend_sheepdog.h storage/storage_backend_sheepdog.c
+STORAGE_DRIVER_BTRFS_SOURCES = \
+ storage/storage_backend_btrfs.h storage/storage_backend_btrfs.c
+
STORAGE_HELPER_DISK_SOURCES = \
storage/parthelper.c
@@ -1402,6 +1405,10 @@ if WITH_STORAGE_SHEEPDOG
libvirt_driver_storage_impl_la_SOURCES += $(STORAGE_DRIVER_SHEEPDOG_SOURCES)
endif
+if WITH_STORAGE_BTRFS
+libvirt_driver_storage_impl_la_SOURCES += $(STORAGE_DRIVER_BTRFS_SOURCES)
+endif
+
if WITH_NODE_DEVICES
# Needed to keep automake quiet about conditionals
if WITH_DRIVER_MODULES
@@ -1594,6 +1601,7 @@ EXTRA_DIST += \
$(STORAGE_DRIVER_DISK_SOURCES) \
$(STORAGE_DRIVER_RBD_SOURCES) \
$(STORAGE_DRIVER_SHEEPDOG_SOURCES) \
+ $(STORAGE_DRIVER_BTRFS_SOURCES) \
$(NODE_DEVICE_DRIVER_SOURCES) \
$(NODE_DEVICE_DRIVER_HAL_SOURCES) \
$(NODE_DEVICE_DRIVER_UDEV_SOURCES) \
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 002663f..ce7c999 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -55,7 +55,8 @@ VIR_ENUM_IMPL(virStoragePool,
VIR_STORAGE_POOL_LAST,
"dir", "fs", "netfs",
"logical", "disk", "iscsi",
- "scsi", "mpath", "rbd",
"sheepdog")
+ "scsi", "mpath", "rbd",
"sheepdog",
+ "btrfs")
VIR_ENUM_IMPL(virStoragePoolFormatFileSystem,
VIR_STORAGE_POOL_FS_LAST,
@@ -261,6 +262,11 @@ static virStoragePoolTypeInfo poolTypeInfo[] = {
.formatFromString = virStorageVolFormatDiskTypeFromString,
.formatToString = virStorageVolFormatDiskTypeToString,
},
+ },
+ {.poolType = VIR_STORAGE_POOL_BTRFS,
+ .volOptions = {
+ .formatToString = virStoragePoolFormatDiskTypeToString,
+ }
}
};
@@ -2189,7 +2195,9 @@ virStoragePoolMatch(virStoragePoolObjPtr poolobj,
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_RBD) &&
(poolobj->def->type == VIR_STORAGE_POOL_RBD)) ||
(MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_SHEEPDOG) &&
- (poolobj->def->type == VIR_STORAGE_POOL_SHEEPDOG))))
+ (poolobj->def->type == VIR_STORAGE_POOL_SHEEPDOG)) ||
+ (MATCH(VIR_CONNECT_LIST_STORAGE_POOLS_BTRFS) &&
+ (poolobj->def->type == VIR_STORAGE_POOL_BTRFS))))
return false;
}
diff --git a/src/conf/storage_conf.h b/src/conf/storage_conf.h
index 62ff1fd..3d02c2a 100644
--- a/src/conf/storage_conf.h
+++ b/src/conf/storage_conf.h
@@ -128,6 +128,7 @@ enum virStoragePoolType {
VIR_STORAGE_POOL_MPATH, /* Multipath devices */
VIR_STORAGE_POOL_RBD, /* RADOS Block Device */
VIR_STORAGE_POOL_SHEEPDOG, /* Sheepdog device */
+ VIR_STORAGE_POOL_BTRFS, /* btrfs subvolumes / snapshots */
VIR_STORAGE_POOL_LAST,
};
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index 4ebe11b..1518dec 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -80,6 +80,9 @@
#if WITH_STORAGE_SHEEPDOG
# include "storage_backend_sheepdog.h"
#endif
+#if WITH_STORAGE_BTRFS
+# include "storage_backend_btrfs.h"
+#endif
#define VIR_FROM_THIS VIR_FROM_STORAGE
@@ -112,6 +115,9 @@ static virStorageBackendPtr backends[] = {
#if WITH_STORAGE_SHEEPDOG
&virStorageBackendSheepdog,
#endif
+#if WITH_STORAGE_BTRFS
+ &virStorageBackendBtrfs,
+#endif
NULL
};
diff --git a/src/storage/storage_backend_btrfs.c b/src/storage/storage_backend_btrfs.c
new file mode 100644
index 0000000..f0b7563
--- /dev/null
+++ b/src/storage/storage_backend_btrfs.c
@@ -0,0 +1,284 @@
+/*
+ * storage_backend_btrfs.c: btrfs subvolume / snapshot backed storage
+ *
+ * Copyright (C) 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2013 Oskari Saarenmaa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Oskari Saarenmaa <os(a)ohmu.fi>
+ */
+
+#include <config.h>
+
+#include <sys/statvfs.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "storage_backend.h"
+#include "viralloc.h"
+#include "virfile.h"
+#include "virstring.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+static char *
+btrfsPoolVolPath(virStoragePoolObjPtr pool,
+ const char *name)
+{
+ char *res = NULL;
+ int rc = -1;
+ if (STREQLEN(name, pool->def->target.path,
+ strlen(pool->def->target.path))) {
+ rc = VIR_STRDUP(res, name);
+ } else {
+ rc = virAsprintf(&res, "%s/%s", pool->def->target.path,
name);
+ }
+ return (rc < 0) ? NULL : res;
+}
+
+static int
+btrfsPoolCheck(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool,
+ bool *isActive)
+{
+ *isActive = (access(pool->def->target.path, R_OK | X_OK) == 0);
+ return 0;
+}
+
+struct btrfsVolS {
+ int count;
+ struct {
+ char *name;
+ char *uuid;
+ } *vols;
+};
+
+static int
+btrfsPoolFindVols(virStoragePoolObjPtr pool,
+ char **const groups,
+ void *data)
+{
+ struct btrfsVolS *bvols = data;
+ virStorageVolDefPtr vol = NULL;
+ int idx, ret = -1;
+
+ if (VIR_ALLOC(vol) < 0)
+ goto cleanup;
+
+ if (VIR_STRDUP(vol->name, groups[2]) < 0)
+ goto cleanup;
+
+ vol->type = VIR_STORAGE_VOL_DIR;
+ if (virAsprintf(&vol->target.path, "%s/%s",
+ pool->def->target.path,
+ vol->name) == -1)
+ goto cleanup;
+
+ if (VIR_STRDUP(vol->key, vol->target.path) < 0)
+ goto cleanup;
+
+ /* Store uuid and name in case a snapshot targets this volume */
+ if (VIR_REALLOC_N(bvols->vols, bvols->count+1) < 0)
+ goto cleanup;
+ idx = bvols->count++;
+ bvols->vols[idx].name = NULL;
+ bvols->vols[idx].uuid = NULL;
+ if (VIR_STRDUP(bvols->vols[idx].name, groups[2]) < 0 ||
+ VIR_STRDUP(bvols->vols[idx].uuid, groups[1]) < 0)
+ goto cleanup;
+
+ /* Is this a snapshot? */
+ if (STRNEQ(groups[0], "-")) {
+ for (idx = 0; idx < bvols->count; idx++) {
+ if (STREQ(bvols->vols[idx].uuid, groups[0])) {
+ vol->backingStore.path = btrfsPoolVolPath(pool,
bvols->vols[idx].name);
+ if (vol->backingStore.path == NULL)
+ goto cleanup;
+ break;
+ }
+ }
+ }
+
+ if (VIR_REALLOC_N(pool->volumes.objs,
+ pool->volumes.count+1) < 0)
+ goto cleanup;
+
+ pool->volumes.objs[pool->volumes.count++] = vol;
+ vol = NULL;
+ ret = 0;
+
+cleanup:
+ virStorageVolDefFree(vol);
+ return ret;
+}
+
+static int
+btrfsPoolRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool)
+{
+ struct statvfs sb;
+ struct btrfsVolS bvols;
+ int idx, vars[] = {3};
+ const char *regexes[] = {
+ "^ID [0-9]+ gen [0-9]+ top level [0-9]+ "
+ "parent_uuid ([-a-f0-9]+) uuid ([-a-f0-9]+) path (\\S+)$"};
+ virCommandPtr cmd = virCommandNewArgList(
+ "btrfs", "subvolume", "list", "-uq",
pool->def->target.path, NULL);
+
+ bvols.count = 0;
+ bvols.vols = NULL;
+ if (virStorageBackendRunProgRegex(pool, cmd, 1, regexes, vars,
+ btrfsPoolFindVols, &bvols, NULL) < 0) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
+ _("failed to parse btrfs subvolume list"));
+ goto cleanup;
+ }
+
+ if (statvfs(pool->def->target.path, &sb) < 0) {
+ virReportSystemError(errno,
+ _("cannot statvfs path '%s'"),
+ pool->def->target.path);
+ goto cleanup;
+ }
+ pool->def->capacity = ((unsigned long long)sb.f_frsize *
+ (unsigned long long)sb.f_blocks);
+ pool->def->available = ((unsigned long long)sb.f_bfree *
+ (unsigned long long)sb.f_frsize);
+ pool->def->allocation = pool->def->capacity -
pool->def->available;
+
+ return 0;
+
+cleanup:
+ virStoragePoolObjClearVols(pool);
+ for (idx = 0; idx < bvols.count; idx++) {
+ VIR_FREE(bvols.vols[idx].name);
+ VIR_FREE(bvols.vols[idx].uuid);
+ }
+ VIR_FREE(bvols.vols);
+
+ return -1;
+}
+
+static int
+btrfsPoolVolCreate(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol)
+{
+ vol->type = VIR_STORAGE_VOL_DIR;
+
+ VIR_FREE(vol->target.path);
+ vol->target.path = btrfsPoolVolPath(pool, vol->name);
+ if (vol->target.path == NULL)
+ return -1;
+
+ if (virFileExists(vol->target.path)) {
+ virReportError(VIR_ERR_OPERATION_INVALID,
+ _("volume target path '%s' already exists"),
+ vol->target.path);
+ return -1;
+ }
+
+ VIR_FREE(vol->key);
+ return VIR_STRDUP(vol->key, vol->target.path);
+}
+
+static int
+btrfsPoolVolBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ char *target_volume = NULL, *snapshot_source = NULL;
+ virCommandPtr cmd = virCommandNew("btrfs");
+ struct stat st;
+
+ if (!cmd)
+ goto cleanup;
+
+ target_volume = btrfsPoolVolPath(pool, vol->target.path);
+ if (target_volume == NULL)
+ goto cleanup;
+
+ if (vol->backingStore.path == NULL) {
+ virCommandAddArgList(cmd, "subvolume", "create",
target_volume, NULL);
+ } else {
+ int accessRetCode = -1;
+ snapshot_source = btrfsPoolVolPath(pool, vol->backingStore.path);
+ if (snapshot_source == NULL)
+ goto cleanup;
+
+ accessRetCode = access(snapshot_source, R_OK | X_OK);
+ if (accessRetCode != 0) {
+ virReportSystemError(errno,
+ _("inaccessible backing store volume %s"),
+ vol->backingStore.path);
+ goto cleanup;
+ }
+
+ virCommandAddArgList(cmd, "subvolume", "snapshot",
snapshot_source,
+ target_volume, NULL);
+ }
+
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+ if (stat(target_volume, &st) < 0) {
+ virReportSystemError(errno,
+ _("failed to create %s"), vol->target.path);
+ goto cleanup;
+ }
+ ret = 0;
+
+cleanup:
+ VIR_FREE(target_volume);
+ VIR_FREE(snapshot_source);
+ virCommandFree(cmd);
+ return ret;
+}
+
+static int
+btrfsPoolVolDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
+ virStoragePoolObjPtr pool,
+ virStorageVolDefPtr vol,
+ unsigned int flags ATTRIBUTE_UNUSED)
+{
+ int ret = -1;
+ char *target_volume = btrfsPoolVolPath(pool, vol->target.path);
+ virCommandPtr cmd = virCommandNewArgList("btrfs", "subvolume",
+ "delete", target_volume, NULL);
+ if (target_volume == NULL || cmd == NULL)
+ goto cleanup;
+ if (virCommandRun(cmd, NULL) < 0)
+ goto cleanup;
+ ret = 0;
+
+cleanup:
+ VIR_FREE(target_volume);
+ virCommandFree(cmd);
+ return ret;
+}
+
+virStorageBackend virStorageBackendBtrfs = {
+ .type = VIR_STORAGE_POOL_BTRFS,
+
+ .checkPool = btrfsPoolCheck,
+ .refreshPool = btrfsPoolRefresh,
+
+ .buildVol = btrfsPoolVolBuild,
+ .createVol = btrfsPoolVolCreate,
+ .deleteVol = btrfsPoolVolDelete,
+};
diff --git a/src/storage/storage_backend_btrfs.h b/src/storage/storage_backend_btrfs.h
new file mode 100644
index 0000000..7d65f58
--- /dev/null
+++ b/src/storage/storage_backend_btrfs.h
@@ -0,0 +1,30 @@
+/*
+ * storage_backend_btrfs.c: btrfs subvolume / snapshot backed storage
+ *
+ * Copyright (C) 2013 Oskari Saarenmaa
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <
http://www.gnu.org/licenses/>.
+ *
+ * Author: Oskari Saarenmaa <os(a)ohmu.fi>
+ */
+
+#ifndef __VIR_STORAGE_BACKEND_BTRFS_H__
+# define __VIR_STORAGE_BACKEND_BTRFS_H__
+
+# include "storage_backend.h"
+
+extern virStorageBackend virStorageBackendBtrfs;
+
+#endif /* __VIR_STORAGE_BACKEND_BTRFS_H__ */
diff --git a/tests/storagepoolxml2xmlin/pool-btrfs.xml
b/tests/storagepoolxml2xmlin/pool-btrfs.xml
new file mode 100644
index 0000000..c9e0626
--- /dev/null
+++ b/tests/storagepoolxml2xmlin/pool-btrfs.xml
@@ -0,0 +1,7 @@
+<pool type='btrfs'>
+ <name>lxc</name>
+ <uuid>96a73758-5481-4e79-b557-e130b81adf4c</uuid>
+ <target>
+ <path>/lxc</path>
+ </target>
+</pool>
diff --git a/tests/storagepoolxml2xmlout/pool-btrfs.xml
b/tests/storagepoolxml2xmlout/pool-btrfs.xml
new file mode 100644
index 0000000..c2cdec9
--- /dev/null
+++ b/tests/storagepoolxml2xmlout/pool-btrfs.xml
@@ -0,0 +1,17 @@
+<pool type='btrfs'>
+ <name>lxc</name>
+ <uuid>96a73758-5481-4e79-b557-e130b81adf4c</uuid>
+ <capacity unit='bytes'>0</capacity>
+ <allocation unit='bytes'>0</allocation>
+ <available unit='bytes'>0</available>
+ <source>
+ </source>
+ <target>
+ <path>/lxc</path>
+ <permissions>
+ <mode>0755</mode>
+ <owner>-1</owner>
+ <group>-1</group>
+ </permissions>
+ </target>
+</pool>
diff --git a/tests/storagepoolxml2xmltest.c b/tests/storagepoolxml2xmltest.c
index d59cff9..cf61d24 100644
--- a/tests/storagepoolxml2xmltest.c
+++ b/tests/storagepoolxml2xmltest.c
@@ -100,6 +100,7 @@ mymain(void)
DO_TEST("pool-iscsi-multiiqn");
DO_TEST("pool-iscsi-vendor-product");
DO_TEST("pool-sheepdog");
+ DO_TEST("pool-btrfs");
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
diff --git a/tests/storagevolxml2xmlin/vol-btrfs-snapshot.xml
b/tests/storagevolxml2xmlin/vol-btrfs-snapshot.xml
new file mode 100644
index 0000000..f412e44
--- /dev/null
+++ b/tests/storagevolxml2xmlin/vol-btrfs-snapshot.xml
@@ -0,0 +1,11 @@
+<volume>
+ <name>clone</name>
+ <capacity unit="bytes">0</capacity>
+ <allocation unit="bytes">0</allocation>
+ <target>
+ <path>/lxc/clone</path>
+ </target>
+ <backingStore>
+ <path>/lxc/vanilla</path>
+ </backingStore>
+</volume>
diff --git a/tests/storagevolxml2xmlin/vol-btrfs.xml
b/tests/storagevolxml2xmlin/vol-btrfs.xml
new file mode 100644
index 0000000..9522029
--- /dev/null
+++ b/tests/storagevolxml2xmlin/vol-btrfs.xml
@@ -0,0 +1,8 @@
+<volume>
+ <name>vanilla</name>
+ <capacity unit="bytes">0</capacity>
+ <allocation unit="bytes">0</allocation>
+ <target>
+ <path>/lxc/vanilla</path>
+ </target>
+</volume>
diff --git a/tests/storagevolxml2xmlout/vol-btrfs-snapshot.xml
b/tests/storagevolxml2xmlout/vol-btrfs-snapshot.xml
new file mode 100644
index 0000000..b96b5f0
--- /dev/null
+++ b/tests/storagevolxml2xmlout/vol-btrfs-snapshot.xml
@@ -0,0 +1,26 @@
+<volume>
+ <name>clone</name>
+ <key>(null)</key>
+ <source>
+ </source>
+ <capacity unit='bytes'>0</capacity>
+ <allocation unit='bytes'>0</allocation>
+ <target>
+ <path>/lxc/clone</path>
+ <format type='unknown'/>
+ <permissions>
+ <mode>0600</mode>
+ <owner>4294967295</owner>
+ <group>4294967295</group>
+ </permissions>
+ </target>
+ <backingStore>
+ <path>/lxc/vanilla</path>
+ <format type='unknown'/>
+ <permissions>
+ <mode>0600</mode>
+ <owner>4294967295</owner>
+ <group>4294967295</group>
+ </permissions>
+ </backingStore>
+</volume>
diff --git a/tests/storagevolxml2xmlout/vol-btrfs.xml
b/tests/storagevolxml2xmlout/vol-btrfs.xml
new file mode 100644
index 0000000..bef5a1a
--- /dev/null
+++ b/tests/storagevolxml2xmlout/vol-btrfs.xml
@@ -0,0 +1,17 @@
+<volume>
+ <name>vanilla</name>
+ <key>(null)</key>
+ <source>
+ </source>
+ <capacity unit='bytes'>0</capacity>
+ <allocation unit='bytes'>0</allocation>
+ <target>
+ <path>/lxc/vanilla</path>
+ <format type='unknown'/>
+ <permissions>
+ <mode>0600</mode>
+ <owner>4294967295</owner>
+ <group>4294967295</group>
+ </permissions>
+ </target>
+</volume>
diff --git a/tests/storagevolxml2xmltest.c b/tests/storagevolxml2xmltest.c
index 5b0a60b..fdfcc55 100644
--- a/tests/storagevolxml2xmltest.c
+++ b/tests/storagevolxml2xmltest.c
@@ -120,6 +120,8 @@ mymain(void)
DO_TEST("pool-logical", "vol-logical");
DO_TEST("pool-logical", "vol-logical-backing");
DO_TEST("pool-sheepdog", "vol-sheepdog");
+ DO_TEST("pool-btrfs", "vol-btrfs");
+ DO_TEST("pool-btrfs", "vol-btrfs-snapshot");
return ret==0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
--
1.8.3.1