On Tue, Feb 12, 2008 at 04:37:57AM +0000, Daniel P. Berrange wrote: > 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 | 1123 ++++++++++++++++++++++++++++++++++++++++++ b/src/storage_backend_fs.h | 49 + configure.in | 52 + libvirt.spec.in | 24 po/POTFILES.in | 1 src/Makefile.am | 1 src/storage_backend.c | 34 + 16 files changed, 1375 insertions(+) diff -r 686cf593fe28 configure.in --- a/configure.in Tue Feb 19 17:04:59 2008 -0500 +++ b/configure.in Tue Feb 19 17:22:04 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 686cf593fe28 docs/storage/pool-dir.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-dir.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,6 @@ +<pool type="dir"> + <name>virtimages</name> + <target> + <path>/var/lib/virt/images</path> + </target> +</pool> diff -r 686cf593fe28 docs/storage/pool-fs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-fs.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,15 @@ +<pool type="fs"> + <name>virtimages</name> + <source> + <device path="/dev/VolGroup00/VirtImages"/> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>system_u:object_r:xen_image_t:s0</label> + </permissions> + </target> +</pool> diff -r 686cf593fe28 docs/storage/pool-netfs.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/pool-netfs.xml Tue Feb 19 17:22:04 2008 -0500 @@ -0,0 +1,16 @@ +<pool type="netfs"> + <name>virtimages</name> + <source> + <host name="nfs.example.com"/> + <directory path="/var/lib/virt/images"/> + </source> + <target> + <path>/var/lib/virt/images</path> + <permissions> + <mode>0700</mode> + <owner>0</owner> + <group>0</group> + <label>system_u:object_r:xen_image_t:s0</label> + </permissions> + </target> +</pool> diff -r 686cf593fe28 docs/storage/vol-cow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-cow.xml Tue Feb 19 17:22:04 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 686cf593fe28 docs/storage/vol-qcow.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow.xml Tue Feb 19 17:22:04 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 686cf593fe28 docs/storage/vol-qcow2.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-qcow2.xml Tue Feb 19 17:22:04 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 686cf593fe28 docs/storage/vol-raw.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-raw.xml Tue Feb 19 17:22:04 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 686cf593fe28 docs/storage/vol-sparse.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-sparse.xml Tue Feb 19 17:22:04 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 686cf593fe28 docs/storage/vol-vmdk.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/docs/storage/vol-vmdk.xml Tue Feb 19 17:22:04 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 686cf593fe28 libvirt.spec.in --- a/libvirt.spec.in Tue Feb 19 17:04:59 2008 -0500 +++ b/libvirt.spec.in Tue Feb 19 17:22:04 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 686cf593fe28 po/POTFILES.in --- a/po/POTFILES.in Tue Feb 19 17:04:59 2008 -0500 +++ b/po/POTFILES.in Tue Feb 19 17:22:04 2008 -0500 @@ -11,6 +11,7 @@ src/qemu_driver.c src/qemu_driver.c src/remote_internal.c src/storage_backend.c +src/storage_backend_fs.c src/storage_conf.c src/storage_driver.c src/sexpr.c diff -r 686cf593fe28 src/Makefile.am --- a/src/Makefile.am Tue Feb 19 17:04:59 2008 -0500 +++ b/src/Makefile.am Tue Feb 19 17:22:04 2008 -0500 @@ -62,6 +62,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 686cf593fe28 src/storage_backend.c --- a/src/storage_backend.c Tue Feb 19 17:04:59 2008 -0500 +++ b/src/storage_backend.c Tue Feb 19 17:22:04 2008 -0500 @@ -40,10 +40,24 @@ #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; @@ -68,6 +82,15 @@ virStorageBackendVolOptionsForType(int t 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; @@ -75,6 +98,17 @@ virStorageBackendFromString(const char * 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 686cf593fe28 src/storage_backend_fs.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.c Tue Feb 19 17:22:05 2008 -0500 @@ -0,0 +1,1123 @@ +/* + * 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_DIR, + 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, "dir")) + return VIR_STORAGE_VOL_DIR; + 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_DIR: + return "dir"; + 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': %s"), + def->target.path, 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': %s"), + def->target.path, 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]); + } 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]); + } + /* Avoid unlikely, but theoretically possible overflow */ + if (def->capacity > (ULLONG_MAX / fileTypeInfo[i].sizeMultiplier)) + continue; + 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); + 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 if 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) { + 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': %s"), + pool->def->target.path, 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': %s"), + pool->def->target.path, 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': %s"), + pool->def->target.path, 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': %s"), + pool->def->target.path, 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_RDWR | O_CREAT | O_EXCL, + vol->target.perms.mode)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create path '%s': %s"), + vol->target.path, 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': %s"), + vol->target.path, 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': %s"), + vol->target.path, strerror(errno)); + unlink(vol->target.path); + close(fd); + return -1; + } + } else if (vol->target.format == VIR_STORAGE_VOL_DIR) { + if (mkdir(vol->target.path, vol->target.perms.mode) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot create path '%s': %s"), + vol->target.path, strerror(errno)); + return -1; + } + + if ((fd = open(vol->target.path, O_RDWR)) < 0) { + virStorageReportError(conn, VIR_ERR_INTERNAL_ERROR, + _("cannot read path '%s': %s"), + vol->target.path, strerror(errno)); + 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) { + 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': %s"), + vol->target.path, 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) { + 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': %s"), + vol->target.path, 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': %s"), + vol->target.path, 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': %s"), + vol->target.path, 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': %s"), + vol->target.path, 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': %s"), + vol->target.path, 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 686cf593fe28 src/storage_backend_fs.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/storage_backend_fs.h Tue Feb 19 17:22:05 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