This patch implements 3 storage pools in one go. This provides the base level 'directory' storage pool, where an existing directory contains files as volumes. This is guarenteed to be available on any OS since it just uses plain POSIX apis for creation/deletion. If qemu-img or qcow2-create command line tools are available it can also create various non-raw fileformats such as QCow2, VMDK. If the mount/unmount commands are available, the 'fs' and 'netfs' pools become available. This allow a local disk, or remote filesystem to be mounted on the local filesystem, and files managed within. This driver contains code for probing the non-raw file formats to determine their logical capacity. b/docs/storage/pool-dir.xml | 6 b/docs/storage/pool-fs.xml | 15 b/docs/storage/pool-netfs.xml | 16 b/docs/storage/vol-cow.xml | 10 b/docs/storage/vol-qcow.xml | 10 b/docs/storage/vol-qcow2.xml | 10 b/docs/storage/vol-raw.xml | 7 b/docs/storage/vol-sparse.xml | 7 b/docs/storage/vol-vmdk.xml | 10 b/src/storage_backend_fs.c | 1047 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_fs.h | 49 + configure.in | 52 ++ libvirt.spec.in | 24 src/Makefile.am | 1 src/storage_backend.c | 33 + 15 files changed, 1297 insertions(+) diff -r 739490b4a2f6 configure.in --- a/configure.in Thu Feb 07 12:59:42 2008 -0500 +++ b/configure.in Thu Feb 07 13:44:25 2008 -0500 @@ -551,6 +551,52 @@ AC_SUBST(VIRSH_LIBS) AC_SUBST(WITH_XEN) AC_SUBST(LIBVIRT_FEATURES) + + +dnl +dnl Storage driver checks +dnl + +AC_ARG_WITH(storage-fs, +[ --with-storage-fs with FileSystem backend for the storage driver (on)],[],[with_storage_fs=check]) + +if test "$with_storage_fs" = "yes" -o "$with_storage_fs" = "check"; then + AC_PATH_PROG(MOUNT, [mount], [], [$PATH:/sbin:/usr/sbin]) + AC_PATH_PROG(UMOUNT, [umount], [], [$PATH:/sbin:/usr/sbin]) + if test "$with_storage_fs" = "yes" ; then + if test -z "$MOUNT" ; then AC_MSG_ERROR(We need mount for FS storage driver) ; fi + if test -z "$UMOUNT" ; then AC_MSG_ERROR(We need mount for FS storage driver) ; fi + else + if test -z "$MOUNT" ; then with_storage_fs=no ; fi + if test -z "$UMOUNT" ; then with_storage_fs=no ; fi + + if test "$with_storage_fs" = "check" ; then with_storage_fs=yes ; fi + fi + + if test "$with_storage_fs" = "yes" ; then + AC_DEFINE_UNQUOTED(WITH_STORAGE_FS, 1, [whether FS backend for storage driver is enabled]) + AC_DEFINE_UNQUOTED([MOUNT],["$MOUNT"], + [Location or name of the mount program]) + AC_DEFINE_UNQUOTED([UMOUNT],["$UMOUNT"], + [Location or name of the mount program]) + fi +fi +AM_CONDITIONAL(WITH_STORAGE_FS, [test "$with_storage_fs" = "yes"]) + +AC_PATH_PROG(QEMU_IMG, [qemu-img], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) +if test -n "$QEMU_IMG" ; then + AC_DEFINE_UNQUOTED(HAVE_QEMU_IMG, 1, [whether qemu-img is available for non-raw files]) + AC_DEFINE_UNQUOTED([QEMU_IMG],["$QEMU_IMG"], + [Location or name of the qemu-img program]) +fi + +AC_PATH_PROG(QCOW_CREATE, [qcow-create], [], [$PATH:/sbin:/usr/sbin:/bin:/usr/bin]) +if test -n "$QCOW_CREATE" ; then + AC_DEFINE_UNQUOTED(HAVE_QCOW_CREATE, 1, [whether qcow-create is available for non-raw files]) + AC_DEFINE_UNQUOTED([QCOW_CREATE],["$QCOW_CREATE"], + [Location or name of the qcow-create program]) +fi + dnl dnl check for python @@ -760,6 +806,12 @@ AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([ Remote: $with_remote]) AC_MSG_NOTICE([Libvirtd: $with_libvirtd]) AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Storage Drivers]) +AC_MSG_NOTICE([]) +AC_MSG_NOTICE([ Dir: yes]) +AC_MSG_NOTICE([ FS: $with_storage_fs]) +AC_MSG_NOTICE([ NetFS: $with_storage_fs]) +AC_MSG_NOTICE([]) AC_MSG_NOTICE([Libraries]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ libxml: $LIBXML_CFLAGS $LIBXML_LIBS]) diff -r 739490b4a2f6 docs/storage/pool-dir.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-dir.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,6 @@ +<pool type="dir"> + <name>virtimages</name> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> diff -r 739490b4a2f6 docs/storage/pool-fs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-fs.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,15 @@ +<pool type="fs"> + <name>virtimages</name> + <source> + <device>/dev/VolGroup00/VirtImages</device> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>xen_image_t</label> + </permissions> + </target> +</pool> diff -r 739490b4a2f6 docs/storage/pool-netfs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-netfs.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,16 @@ +<pool type="netfs"> + <name>virtimages</name> + <source> + <host>nfs.example.com</host> + <path>/var/lib/virt/images</path> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>xen_image_t</label> + </permissions> + </target> +</pool> diff -r 739490b4a2f6 docs/storage/vol-cow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-cow.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>cow.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="cow"/> + </target> +</volume> diff -r 739490b4a2f6 docs/storage/vol-qcow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>qcow.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="qcow"/> + </target> +</volume> diff -r 739490b4a2f6 docs/storage/vol-qcow2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow2.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>qcow2.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="qcow2"/> + </target> +</volume> diff -r 739490b4a2f6 docs/storage/vol-raw.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-raw.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,7 @@ +<volume type="file"> + <name>raw.img</name> + <storage> + <allocation unit="M">10</allocation> + <capacity unit="M">1000</capacity> + </storage> +</volume> diff -r 739490b4a2f6 docs/storage/vol-sparse.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-sparse.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,7 @@ +<volume type="file"> + <name>sparse.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> +</volume> diff -r 739490b4a2f6 docs/storage/vol-vmdk.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-vmdk.xml Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,10 @@ +<volume type="file"> + <name>vmdk3.img</name> + <storage> + <allocation>0</allocation> + <capacity unit="T">1</capacity> + </storage> + <target> + <format type="vmdk"/> + </target> +</volume> diff -r 739490b4a2f6 libvirt.spec.in --- a/libvirt.spec.in Thu Feb 07 12:59:42 2008 -0500 +++ b/libvirt.spec.in Thu Feb 07 13:44:25 2008 -0500 @@ -6,6 +6,12 @@ %else %define with_polkit 0 %define with_proxy yes +%endif + +%if "%{fedora}" +%define with_qemu 1 +%else +%define with_qemu 0 %endif Summary: Library providing a simple API virtualization @@ -34,6 +40,15 @@ Requires: cyrus-sasl-md5 %if %{with_polkit} Requires: PolicyKit >= 0.6 %endif +# For mount/umount in FS driver +BuildRequires: util-linux +%if %{with_qemu} +# From QEMU RPMs +Requires: /usr/bin/qemu-img +%else +# From Xen RPMs +Requires: /usr/sbin/qcow-create +%endif BuildRequires: xen-devel BuildRequires: libxml2-devel BuildRequires: readline-devel @@ -48,6 +63,15 @@ BuildRequires: cyrus-sasl-devel BuildRequires: cyrus-sasl-devel %if %{with_polkit} BuildRequires: PolicyKit-devel >= 0.6 +%endif +# For mount/umount in FS driver +BuildRequires: util-linux +%if %{with_qemu} +# From QEMU RPMs +BuildRequires: /usr/bin/qemu-img +%else +# From Xen RPMs +BuildRequires: /usr/sbin/qcow-create %endif Obsoletes: libvir ExclusiveArch: i386 x86_64 ia64 diff -r 739490b4a2f6 src/Makefile.am --- a/src/Makefile.am Thu Feb 07 12:59:42 2008 -0500 +++ b/src/Makefile.am Thu Feb 07 13:44:25 2008 -0500 @@ -63,6 +63,7 @@ CLIENT_SOURCES = \ storage_conf.h storage_conf.c \ storage_driver.h storage_driver.c \ storage_backend.h storage_backend.c \ + storage_backend_fs.h storage_backend_fs.c \ util.c util.h SERVER_SOURCES = \ diff -r 739490b4a2f6 src/storage_backend.c --- a/src/storage_backend.c Thu Feb 07 12:59:42 2008 -0500 +++ b/src/storage_backend.c Thu Feb 07 13:44:25 2008 -0500 @@ -40,9 +40,23 @@ #include "util.h" #include "storage_backend.h" +#include "storage_backend_fs.h" + +static virStorageBackendPtr backends[] = { + &virStorageBackendDirectory, +#if WITH_STORAGE_FS + &virStorageBackendFileSystem, + &virStorageBackendNetFileSystem, +#endif +}; virStorageBackendPtr virStorageBackendForType(int type) { + unsigned int i; + for (i = 0 ; i < (sizeof(backends)/sizeof(backends[0])) ; i++) + if (backends[i]->type == type) + return backends[i]; + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "missing backend for pool type %d", type); return NULL; } @@ -63,11 +77,30 @@ virStorageBackendVolOptionsPtr virStorag int virStorageBackendFromString(const char *type) { + if (STREQ(type, "dir")) + return VIR_STORAGE_POOL_DIR; +#if WITH_STORAGE_FS + if (STREQ(type, "fs")) + return VIR_STORAGE_POOL_FS; + if (STREQ(type, "netfs")) + return VIR_STORAGE_POOL_NETFS; +#endif virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %s", type); return -1; } const char *virStorageBackendToString(int type) { + switch (type) { + case VIR_STORAGE_POOL_DIR: + return "dir"; +#if WITH_STORAGE_FS + case VIR_STORAGE_POOL_FS: + return "fs"; + case VIR_STORAGE_POOL_NETFS: + return "netfs"; +#endif + } + virStorageReportError(NULL, VIR_ERR_INTERNAL_ERROR, "unknown storage backend type %d", type); return NULL; } diff -r 739490b4a2f6 src/storage_backend_fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.c Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,1047 @@ +/* + * storage_backend_fs.c: storage backend for FS and directory handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#include <config.h> + +#include <sys/statvfs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <endian.h> +#include <byteswap.h> +#include <mntent.h> +#include <string.h> + +#include "storage_backend_fs.h" +#include "storage_conf.h" +#include "util.h" +#include "config.h" + + +enum { + VIR_STORAGE_POOL_FS_AUTO = 0, + VIR_STORAGE_POOL_FS_EXT2, + VIR_STORAGE_POOL_FS_EXT3, + VIR_STORAGE_POOL_FS_EXT4, + VIR_STORAGE_POOL_FS_UFS, + VIR_STORAGE_POOL_FS_ISO, + VIR_STORAGE_POOL_FS_UDF, + VIR_STORAGE_POOL_FS_GFS, + VIR_STORAGE_POOL_FS_GFS2, + VIR_STORAGE_POOL_FS_VFAT, + VIR_STORAGE_POOL_FS_HFSPLUS, + VIR_STORAGE_POOL_FS_XFS, +}; + +enum { + VIR_STORAGE_POOL_NETFS_AUTO = 0, + VIR_STORAGE_POOL_NETFS_NFS, +}; + + + +enum { + VIR_STORAGE_VOL_RAW, + VIR_STORAGE_VOL_BOCHS, + VIR_STORAGE_VOL_CLOOP, + VIR_STORAGE_VOL_COW, + VIR_STORAGE_VOL_DMG, + VIR_STORAGE_VOL_ISO, + VIR_STORAGE_VOL_QCOW, + VIR_STORAGE_VOL_QCOW2, + VIR_STORAGE_VOL_VMDK, + VIR_STORAGE_VOL_VPC, +}; + +/* Either 'magic' or 'extension' *must* be provided */ +struct { + int type; /* One of the constants above */ + const char *magic; /* Optional string of file magic + * to check at head of file */ + const char *extension; /* Optional file extension to check */ + int endian; /* Endianness of file format */ + int versionOffset; /* Byte offset from start of file + * where we find version number, + * -1 to skip version test */ + int versionNumber; /* Version number to validate */ + int sizeOffset; /* Byte offset from start of file + * where we find capacity info, + * -1 to use st_size as capacity */ + int sizeBytes; /* Number of bytes for size field */ + int sizeMultiplier; /* A scaling factor if size is not in bytes */ +} fileTypeInfo[] = { + /* Bochs */ + /* XXX Untested + { VIR_STORAGE_VOL_BOCHS, "Bochs Virtual HD Image", NULL, + __LITTLE_ENDIAN, 64, 0x20000, + 32+16+16+4+4+4+4+4, 8, 1 },*/ + /* CLoop */ + /* XXX Untested + { VIR_STORAGE_VOL_CLOOP, "#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", NULL, + __LITTLE_ENDIAN, -1, 0, + -1, 0, 0 }, */ + /* Cow */ + { VIR_STORAGE_VOL_COW, "OOOM", NULL, + __BIG_ENDIAN, 4, 2, + 4+4+1024+4, 8, 1 }, + /* DMG */ + /* XXX QEMU says there's no magic for dmg, but we should check... */ + { VIR_STORAGE_VOL_DMG, NULL, ".dmg", + 0, -1, 0, + -1, 0, 0 }, + /* XXX there's probably some magic for iso we can validate too... */ + { VIR_STORAGE_VOL_ISO, NULL, ".iso", + 0, -1, 0, + -1, 0, 0 }, + /* Parallels */ + /* XXX Untested + { VIR_STORAGE_VOL_PARALLELS, "WithoutFreeSpace", NULL, + __LITTLE_ENDIAN, 16, 2, + 16+4+4+4+4, 4, 512 }, + */ + /* QCow */ + { VIR_STORAGE_VOL_QCOW, "QFI", NULL, + __BIG_ENDIAN, 4, 1, + 4+4+8+4+4, 8, 1 }, + /* QCow 2 */ + { VIR_STORAGE_VOL_QCOW2, "QFI", NULL, + __BIG_ENDIAN, 4, 2, + 4+4+8+4+4, 8, 1 }, + /* VMDK 3 */ + /* XXX Untested + { VIR_STORAGE_VOL_VMDK, "COWD", NULL, + __LITTLE_ENDIAN, 4, 1, + 4+4+4, 4, 512 }, + */ + /* VMDK 4 */ + { VIR_STORAGE_VOL_VMDK, "KDMV", NULL, + __LITTLE_ENDIAN, 4, 1, + 4+4+4, 8, 512 }, + /* Connectix / VirtualPC */ + /* XXX Untested + { VIR_STORAGE_VOL_VPC, "conectix", NULL, + __BIG_ENDIAN, -1, 0, + -1, 0, 0}, + */ +}; + + + + +static int virStorageBackendFileSystemVolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_VOL_RAW; + + if (STREQ(format, "raw")) + return VIR_STORAGE_VOL_RAW; + if (STREQ(format, "bochs")) + return VIR_STORAGE_VOL_BOCHS; + if (STREQ(format, "cow")) + return VIR_STORAGE_VOL_COW; + if (STREQ(format, "cloop")) + return VIR_STORAGE_VOL_CLOOP; + if (STREQ(format, "dmg")) + return VIR_STORAGE_VOL_DMG; + if (STREQ(format, "iso")) + return VIR_STORAGE_VOL_ISO; + if (STREQ(format, "qcow")) + return VIR_STORAGE_VOL_QCOW; + if (STREQ(format, "qcow2")) + return VIR_STORAGE_VOL_QCOW2; + if (STREQ(format, "vmdk")) + return VIR_STORAGE_VOL_VMDK; + if (STREQ(format, "vpc")) + return VIR_STORAGE_VOL_VPC; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendFileSystemVolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_VOL_RAW: + return "raw"; + case VIR_STORAGE_VOL_BOCHS: + return "bochs"; + case VIR_STORAGE_VOL_CLOOP: + return "cloop"; + case VIR_STORAGE_VOL_COW: + return "cow"; + case VIR_STORAGE_VOL_DMG: + return "dmg"; + case VIR_STORAGE_VOL_ISO: + return "iso"; + case VIR_STORAGE_VOL_QCOW: + return "qcow"; + case VIR_STORAGE_VOL_QCOW2: + return "qcow2"; + case VIR_STORAGE_VOL_VMDK: + return "vmdk"; + case VIR_STORAGE_VOL_VPC: + return "vpc"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + + +static int virStorageBackendFileSystemPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_FS_AUTO; + + if (STREQ(format, "auto")) + return VIR_STORAGE_POOL_FS_AUTO; + if (STREQ(format, "ext2")) + return VIR_STORAGE_POOL_FS_EXT2; + if (STREQ(format, "ext3")) + return VIR_STORAGE_POOL_FS_EXT3; + if (STREQ(format, "ext4")) + return VIR_STORAGE_POOL_FS_EXT4; + if (STREQ(format, "ufs")) + return VIR_STORAGE_POOL_FS_UFS; + if (STREQ(format, "iso9660")) + return VIR_STORAGE_POOL_FS_ISO; + if (STREQ(format, "udf")) + return VIR_STORAGE_POOL_FS_UDF; + if (STREQ(format, "gfs")) + return VIR_STORAGE_POOL_FS_GFS; + if (STREQ(format, "gfs2")) + return VIR_STORAGE_POOL_FS_GFS2; + if (STREQ(format, "vfat")) + return VIR_STORAGE_POOL_FS_VFAT; + if (STREQ(format, "hfs+")) + return VIR_STORAGE_POOL_FS_HFSPLUS; + if (STREQ(format, "xfs")) + return VIR_STORAGE_POOL_FS_XFS; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendFileSystemPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_FS_AUTO: + return "auto"; + case VIR_STORAGE_POOL_FS_EXT2: + return "ext2"; + case VIR_STORAGE_POOL_FS_EXT3: + return "ext3"; + case VIR_STORAGE_POOL_FS_EXT4: + return "ext4"; + case VIR_STORAGE_POOL_FS_UFS: + return "ufs"; + case VIR_STORAGE_POOL_FS_ISO: + return "iso"; + case VIR_STORAGE_POOL_FS_UDF: + return "udf"; + case VIR_STORAGE_POOL_FS_GFS: + return "gfs"; + case VIR_STORAGE_POOL_FS_GFS2: + return "gfs2"; + case VIR_STORAGE_POOL_FS_VFAT: + return "vfat"; + case VIR_STORAGE_POOL_FS_HFSPLUS: + return "hfs+"; + case VIR_STORAGE_POOL_FS_XFS: + return "xfs"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + + +static int virStorageBackendFileSystemNetPoolFormatFromString(virConnectPtr conn, + const char *format) { + if (format == NULL) + return VIR_STORAGE_POOL_NETFS_AUTO; + + if (STREQ(format, "auto")) + return VIR_STORAGE_POOL_NETFS_AUTO; + if (STREQ(format, "nfs")) + return VIR_STORAGE_POOL_NETFS_NFS; + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %s", format); + return -1; +} + +static const char *virStorageBackendFileSystemNetPoolFormatToString(virConnectPtr conn, + int format) { + switch (format) { + case VIR_STORAGE_POOL_NETFS_AUTO: + return "auto"; + case VIR_STORAGE_POOL_NETFS_NFS: + return "nfs"; + } + + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "unsupported volume format %d", format); + return NULL; +} + + +/** + * Probe the header of a file to determine what type of disk image + * it is, and info about its capacity if available. + */ +static int virStorageBackendProbeFile(virConnectPtr conn, + virStorageVolDefPtr def) { + int fd; + char head[4096]; + int len, i, ret; + + if ((fd = open(def->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot open volume '%s': %d (%s)", + def->target.path, errno, strerror(errno)); + return -1; + } + + if ((ret = virStorageBackendUpdateVolInfoFD(conn, def, fd, 1)) < 0) { + close(fd); + return ret; /* Take care to propagate ret, it is not always -1 */ + } + + if ((len = read(fd, head, sizeof(head))) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read header '%s': %d (%s)", + def->target, errno, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + + /* First check file magic */ + for (i = 0 ; i < sizeof(fileTypeInfo)/sizeof(fileTypeInfo[0]) ; i++) { + int mlen; + if (fileTypeInfo[i].magic == NULL) + continue; + + /* Validate magic data */ + mlen = strlen(fileTypeInfo[i].magic); + if (mlen > len) + continue; + if (memcmp(head, fileTypeInfo[i].magic, mlen) != 0) + continue; + + /* Validate version number info */ + if (fileTypeInfo[i].versionNumber != -1) { + int version; + + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + version = (head[fileTypeInfo[i].versionOffset+3] << 24) | + (head[fileTypeInfo[i].versionOffset+2] << 16) | + (head[fileTypeInfo[i].versionOffset+1] << 8) | + head[fileTypeInfo[i].versionOffset]; + } else { + version = (head[fileTypeInfo[i].versionOffset] << 24) | + (head[fileTypeInfo[i].versionOffset+1] << 16) | + (head[fileTypeInfo[i].versionOffset+2] << 8) | + head[fileTypeInfo[i].versionOffset+3]; + } + if (version != fileTypeInfo[i].versionNumber) + continue; + } + + /* Optionally extract capacity from file */ + if (fileTypeInfo[i].sizeOffset != -1) { + if (fileTypeInfo[i].endian == __LITTLE_ENDIAN) { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset]); + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } else { + def->capacity = + ((unsigned long long)head[fileTypeInfo[i].sizeOffset] << 56) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+1] << 48) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+2] << 40) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+3] << 32) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+4] << 24) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+5] << 16) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+6] << 8) | + ((unsigned long long)head[fileTypeInfo[i].sizeOffset+7]); + def->capacity *= fileTypeInfo[i].sizeMultiplier; + } + } + + /* Validation passed, we know the file format now */ + def->target.format = fileTypeInfo[i].type; + return 0; + } + + /* No magic, so check file extension */ + for (i = 0 ; i < sizeof(fileTypeInfo)/sizeof(fileTypeInfo[0]) ; i++) { + if (fileTypeInfo[i].extension == NULL) + continue; + + if (!virFileHasSuffix(def->target.path, fileTypeInfo[i].extension)) + continue; + + def->target.format = fileTypeInfo[i].type; + return 0; + } + + /* All fails, so call it a raw file */ + def->target.format = VIR_STORAGE_VOL_RAW; + return 0; +} + +#if WITH_STORAGE_FS +/** + * @conn connection to report errors against + * @pool storage pool to check for status + * + * Determine if a storage pool is already mounted + * + * Return 0 if not mounted, 1 if mounted, -1 on error + */ +static int virStorageBackendFileSystemIsMounted(virConnectPtr conn, + virStoragePoolObjPtr pool) { + FILE *mtab; + struct mntent *ent; + + if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read %s: %s", + _PATH_MOUNTED, strerror(errno)); + return -1; + } + + while ((ent = getmntent(mtab)) != NULL) { + if (STREQ(ent->mnt_dir, pool->def->target.path)) { + fclose(mtab); + return 1; + } + } + + fclose(mtab); + return 0; +} + +/** + * @conn connection to report errors against + * @pool storage pool to mount + * + * Ensure that a FS storage pool is mounted on its target location. + * If already mounted, this is a no-op + * + * Returns 0 if successfully mounted, -1 on error + */ +static int virStorageBackendFileSystemMount(virConnectPtr conn, + virStoragePoolObjPtr pool) { + char *src; + const char *mntargv[] = { + MOUNT, + "-t", + pool->def->type == VIR_STORAGE_POOL_FS ? + virStorageBackendFileSystemPoolFormatToString(conn, pool->def->source.format) : + virStorageBackendFileSystemNetPoolFormatToString(conn, pool->def->source.format), + NULL, /* Fill in shortly - careful not to add extra fields before this */ + pool->def->target.path, + NULL, + }; + int ret; + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + if (pool->def->source.dir == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source path"); + return -1; + } + } else { + if (pool->def->source.ndevice != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + } + + /* Short-circuit is already mounted */ + if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 0) { + if (ret < 0) + return -1; + else + return 0; + } + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + src = malloc(strlen(pool->def->source.host.name) + 1 + strlen(pool->def->source.dir) + 1); + strcpy(src, pool->def->source.host.name); + strcat(src, ":"); + strcat(src, pool->def->source.dir); + } else { + src = strdup(pool->def->source.devices[0].path); + } + if (src == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "source"); + return -1; + } + mntargv[3] = src; + + if (virRun(conn, (char**)mntargv, NULL) < 0) { + free(src); + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot mount %s on %s: %d", + src, pool->def->target.path, strerror(errno)); + return -1; + } + free(src); + return 0; +} + +/** + * @conn connection to report errors against + * @pool storage pool to unmount + * + * Ensure that a FS storage pool is not mounted on its target location. + * If already unmounted, this is a no-op + * + * Returns 0 if successfully unmounted, -1 on error + */ +static int virStorageBackendFileSystemUnmount(virConnectPtr conn, + virStoragePoolObjPtr pool) { + const char *mntargv[3]; + int ret; + + if (pool->def->type == VIR_STORAGE_POOL_NETFS) { + if (pool->def->source.host.name == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source host"); + return -1; + } + if (pool->def->source.dir == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source dir"); + return -1; + } + } else { + if (pool->def->source.ndevice != 1) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "missing source device"); + return -1; + } + } + + /* Short-circuit is already unmounted */ + if ((ret = virStorageBackendFileSystemIsMounted(conn, pool)) != 1) { + if (ret < 0) + return -1; + else + return 0; + } + + mntargv[0] = UMOUNT; + mntargv[1] = pool->def->target.path; + mntargv[2] = NULL; + + if (virRun(conn, (char**)mntargv, NULL) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot unmount %s: %d", + pool->def->target.path, strerror(errno)); + return -1; + } + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to start + * + * Starts a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +#if WITH_STORAGE_FS +static int virStorageBackendFileSystemStart(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (pool->def->type != VIR_STORAGE_POOL_DIR && + virStorageBackendFileSystemMount(conn, pool) < 0) + return -1; + + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to build + * + * Build a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +static int virStorageBackendFileSystemBuild(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + if (virFileMakePath(pool->def->target.path) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot create path '%s': %d (%s)", + pool->def->target, errno, strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * Iterate over the pool's directory and enumerate all disk images + * within it. This is non-recursive. + */ +static int virStorageBackendFileSystemRefresh(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + DIR *dir; + struct dirent *ent; + struct statvfs sb; + + if (!(dir = opendir(pool->def->target.path))) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot open path '%s': %d (%s)", + pool->def->target.path, errno, strerror(errno)); + goto cleanup; + } + + while ((ent = readdir(dir)) != NULL) { + virStorageVolDefPtr vol; + int ret; + + vol = calloc(1, sizeof(virStorageVolDef)); + if (vol == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume"); + goto cleanup; + } + + vol->name = strdup(ent->d_name); + if (vol->name == NULL) { + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume name"); + goto cleanup; + } + + vol->target.format = VIR_STORAGE_VOL_RAW; /* Real value is filled in during probe */ + vol->target.path = malloc(strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1); + if (vol->target.path == NULL) { + free(vol->target.path); + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume name"); + goto cleanup; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + if ((vol->key = strdup(vol->target.path)) == NULL) { + free(vol->name); + free(vol->target.path); + free(vol); + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "volume key"); + goto cleanup; + } + + if ((ret = virStorageBackendProbeFile(conn, vol) < 0)) { + free(vol->key); + free(vol->name); + free(vol->target.path); + free(vol); + if (ret == -1) + goto cleanup; + else /* Silently ignore non-regular files, eg '.' '..', 'lost+found' */ + continue; + } + + vol->next = pool->volumes; + pool->volumes = vol; + pool->nvolumes++; + continue; + } + closedir(dir); + + + if (statvfs(pool->def->target.path, &sb) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot statvfs path '%s': %d (%s)", + pool->def->target.path, errno, strerror(errno)); + return -1; + } + 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_bsize); + pool->def->allocation = pool->def->capacity - pool->def->available; + + return 0; + + cleanup: + closedir(dir); + virStoragePoolObjClearVols(pool); + return -1; +} + + +/** + * @conn connection to report errors against + * @pool storage pool to start + * + * Stops a directory or FS based storage pool. + * + * - If it is a FS based pool, unmounts the unlying source device on the pool + * - Releases all cached data about volumes + */ +#if WITH_STORAGE_FS +static int virStorageBackendFileSystemStop(virConnectPtr conn, + virStoragePoolObjPtr pool) +{ + if (pool->def->type != VIR_STORAGE_POOL_DIR && + virStorageBackendFileSystemUnmount(conn, pool) < 0) + return -1; + + return 0; +} +#endif /* WITH_STORAGE_FS */ + + +/** + * @conn connection to report errors against + * @pool storage pool to build + * + * Build a directory or FS based storage pool. + * + * - If it is a FS based pool, mounts the unlying source device on the pool + * + * Returns 0 on success, -1 on error + */ +static int virStorageBackendFileSystemDelete(virConnectPtr conn, + virStoragePoolObjPtr pool, + unsigned int flags ATTRIBUTE_UNUSED) +{ + /* XXX delete all vols first ? */ + + if (unlink(pool->def->target.path) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot unlink path '%s': %d (%s)", + pool->def->target, errno, strerror(errno)); + return -1; + } + + return 0; +} + + +/** + * Allocate a new file as a volume. This is either done directly + * for raw/sparse files, or by calling qemu-img/qcow-create for + * special kinds of files + */ +static int virStorageBackendFileSystemVolCreate(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol) +{ + int fd; + + vol->target.path = malloc(strlen(pool->def->target.path) + 1 + strlen(vol->name) + 1); + if (vol->target.path == NULL) { + virStorageReportError(conn, VIR_ERR_NO_MEMORY, "target"); + return -1; + } + strcpy(vol->target.path, pool->def->target.path); + strcat(vol->target.path, "/"); + strcat(vol->target.path, vol->name); + vol->key = strdup(vol->target.path); + if (vol->key == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "storage vol key"); + return -1; + } + + if (vol->target.format == VIR_STORAGE_VOL_RAW) { + if ((fd = open(vol->target.path, O_WRONLY | O_CREAT | O_EXCL, vol->target.perms.mode)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot create path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + return -1; + } + + /* Pre-allocate any data if requested */ + /* XXX slooooooooooooooooow. Need to add in progress bars & bg thread somehow */ + if (vol->allocation) { + unsigned long long remain = vol->allocation; + static const char const zeros[4096]; + while (remain) { + int bytes = sizeof(zeros); + if (bytes > remain) + bytes = remain; + if ((bytes = write(fd, zeros, bytes)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot fill file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + remain -= bytes; + } + } + + /* Now seek to final size, possibly making the file sparse */ + if (ftruncate(fd, vol->capacity) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot extend file '%s': %d (%s)", + vol->target, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } else { +#if HAVE_QEMU_IMG + const char *type; + char size[100]; + const char *imgargv[7]; + + if ((type = virStorageBackendFileSystemVolFormatToString(conn, vol->target.format)) == NULL) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unknown storage vol type %d", vol->target.format); + return -1; + } + + /* Size in KB */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024); + + imgargv[0] = QEMU_IMG; + imgargv[1] = "create"; + imgargv[2] = "-f"; + imgargv[3] = type; + imgargv[4] = vol->target.path; + imgargv[5] = size; + imgargv[6] = NULL; + + if (virRun(conn, (char **)imgargv, NULL) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unable to run 'qemu-img create -f %s %s %s': %s", + type, vol->target.path, size, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + return -1; + } +#elif HAVE_QCOW_CREATE + /* + * Xen removed the fully-functional qemu-img, and replaced it + * with a partially functional qcow-create. Go figure ??!? + */ + char size[100]; + const char *imgargv[4]; + + if (vol->target.format != VIR_STORAGE_VOL_QCOW2) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unsupported storage vol type %d", vol->target.format); + return -1; + } + + /* Size in MB - yes different units to qemu-img :-( */ + snprintf(size, sizeof(size), "%llu", vol->capacity/1024/1024); + + imgargv[0] = QCOW_CREATE; + imgargv[1] = size; + imgargv[2] = vol->target.path; + imgargv[3] = NULL; + + if (virRun(conn, (char **)imgargv, NULL) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + "unable to run 'qcow-create %s %s': %s", + size, vol->target.path, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + if ((fd = open(vol->target.path, O_RDONLY)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot read path '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + return -1; + } +#else + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "creation of non-raw images is not supported without qemu-img"); + return -1; +#endif + } + + /* We can only chown/grp if root */ + if (getuid() == 0) { + if (fchown(fd, vol->target.perms.uid, vol->target.perms.gid) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot set file owner '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } + if (fchmod(fd, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot set file mode '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + + /* Refresh allocation / permissions info, but not capacity */ + if (virStorageBackendUpdateVolInfoFD(conn, vol, fd, 0) < 0) { + unlink(vol->target.path); + close(fd); + return -1; + } + + if (close(fd) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot close file '%s': %d (%s)", + vol->target.path, errno, strerror(errno)); + unlink(vol->target.path); + return -1; + } + + return 0; +} + + +/** + * Remove a volume - just unlinks for now + */ +static int virStorageBackendFileSystemVolDelete(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned int flags ATTRIBUTE_UNUSED) +{ + if (unlink(vol->target.path) < 0) { + /* Silently ignore failures where the vol has already gone away */ + if (errno != ENOENT) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, "cannot unlink file '%s': %d (%s)", + vol->target, errno, strerror(errno)); + return -1; + } + } + return 0; +} + + +/** + * Update info about a volume's capacity/allocation + */ +static int virStorageBackendFileSystemVolRefresh(virConnectPtr conn, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol) +{ + /* Refresh allocation / permissions info in case its changed */ + return virStorageBackendUpdateVolInfo(conn, vol, 0); +} + +virStorageBackend virStorageBackendDirectory = { + .type = VIR_STORAGE_POOL_DIR, + + .buildPool = virStorageBackendFileSystemBuild, + .refreshPool = virStorageBackendFileSystemRefresh, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; + +#if WITH_STORAGE_FS +virStorageBackend virStorageBackendFileSystem = { + .type = VIR_STORAGE_POOL_FS, + + .buildPool = virStorageBackendFileSystemBuild, + .startPool = virStorageBackendFileSystemStart, + .refreshPool = virStorageBackendFileSystemRefresh, + .stopPool = virStorageBackendFileSystemStop, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_DEVICE), + .formatFromString = virStorageBackendFileSystemPoolFormatFromString, + .formatToString = virStorageBackendFileSystemPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; +virStorageBackend virStorageBackendNetFileSystem = { + .type = VIR_STORAGE_POOL_NETFS, + + .buildPool = virStorageBackendFileSystemBuild, + .startPool = virStorageBackendFileSystemStart, + .refreshPool = virStorageBackendFileSystemRefresh, + .stopPool = virStorageBackendFileSystemStop, + .deletePool = virStorageBackendFileSystemDelete, + .createVol = virStorageBackendFileSystemVolCreate, + .refreshVol = virStorageBackendFileSystemVolRefresh, + .deleteVol = virStorageBackendFileSystemVolDelete, + + .poolOptions = { + .flags = (VIR_STORAGE_BACKEND_POOL_SOURCE_HOST | + VIR_STORAGE_BACKEND_POOL_SOURCE_DIR), + .formatFromString = virStorageBackendFileSystemNetPoolFormatFromString, + .formatToString = virStorageBackendFileSystemNetPoolFormatToString, + }, + .volOptions = { + .formatFromString = virStorageBackendFileSystemVolFormatFromString, + .formatToString = virStorageBackendFileSystemVolFormatToString, + }, + .volType = VIR_STORAGE_VOL_FILE, +}; +#endif /* WITH_STORAGE_FS */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ diff -r 739490b4a2f6 src/storage_backend_fs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.h Thu Feb 07 13:44:25 2008 -0500 @@ -0,0 +1,49 @@ +/* + * storage_backend_fs.h: storage backend for FS and directory handling + * + * Copyright (C) 2007-2008 Red Hat, Inc. + * Copyright (C) 2007-2008 Daniel P. Berrange + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Daniel P. Berrange <berrange@xxxxxxxxxx> + */ + +#ifndef __VIR_STORAGE_BACKEND_FS_H__ +#define __VIR_STORAGE_BACKEND_FS_H__ + +#include "storage_backend.h" + +#if WITH_STORAGE_FS +extern virStorageBackend virStorageBackendFileSystem; +extern virStorageBackend virStorageBackendNetFileSystem; +#endif +extern virStorageBackend virStorageBackendDirectory; + +#endif /* __VIR_STORAGE_BACKEND_FS_H__ */ + +/* + * vim: set tabstop=4: + * vim: set shiftwidth=4: + * vim: set expandtab: + */ +/* + * Local variables: + * indent-tabs-mode: nil + * c-indent-level: 4 + * c-basic-offset: 4 + * tab-width: 4 + * End: + */ -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list