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

[libvirt] [PATCH 20/28] Rename storage_file.{c, h} to virstoragefile.{c, h}



From: "Daniel P. Berrange" <berrange redhat com>

---
 po/POTFILES.in                      |    2 +-
 src/Makefile.am                     |    2 +-
 src/conf/domain_conf.c              |    2 +-
 src/conf/domain_conf.h              |    2 +-
 src/conf/snapshot_conf.c            |    2 +-
 src/conf/storage_conf.c             |    2 +-
 src/esx/esx_storage_backend_iscsi.c |    2 +-
 src/esx/esx_storage_backend_vmfs.c  |    2 +-
 src/libxl/libxl_conf.c              |    2 +-
 src/parallels/parallels_driver.c    |    2 +-
 src/parallels/parallels_storage.c   |    2 +-
 src/qemu/qemu_command.c             |    2 +-
 src/qemu/qemu_domain.c              |    2 +-
 src/qemu/qemu_driver.c              |    2 +-
 src/qemu/qemu_hotplug.c             |    2 +-
 src/qemu/qemu_migration.c           |    2 +-
 src/security/security_dac.c         |    2 +-
 src/security/security_selinux.c     |    2 +-
 src/storage/storage_backend.c       |    2 +-
 src/storage/storage_backend_fs.c    |    2 +-
 src/util/storage_file.c             | 1397 -----------------------------------
 src/util/storage_file.h             |  109 ---
 src/util/util.c                     |    2 +-
 src/util/virstoragefile.c           | 1397 +++++++++++++++++++++++++++++++++++
 src/util/virstoragefile.h           |  109 +++
 src/vbox/vbox_tmpl.c                |    2 +-
 src/xenxs/xen_sxpr.c                |    2 +-
 src/xenxs/xen_xm.c                  |    2 +-
 28 files changed, 1530 insertions(+), 1530 deletions(-)
 delete mode 100644 src/util/storage_file.c
 delete mode 100644 src/util/storage_file.h
 create mode 100644 src/util/virstoragefile.c
 create mode 100644 src/util/virstoragefile.h

diff --git a/po/POTFILES.in b/po/POTFILES.in
index ecb4498..417f128 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -138,7 +138,6 @@ src/test/test_driver.c
 src/uml/uml_conf.c
 src/uml/uml_driver.c
 src/util/iohelper.c
-src/util/storage_file.c
 src/util/sysinfo.c
 src/util/util.c
 src/util/viraudit.c
@@ -174,6 +173,7 @@ src/util/virrandom.c
 src/util/virsexpr.c
 src/util/virsocketaddr.c
 src/util/virstatslinux.c
+src/util/virstoragefile.c
 src/util/virterror.c
 src/util/virterror_internal.h
 src/util/virtime.c
diff --git a/src/Makefile.am b/src/Makefile.am
index feb4b77..911c041 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -53,7 +53,6 @@ augeastest_DATA =
 # These files are not related to driver APIs. Simply generic
 # helper APIs for various purposes
 UTIL_SOURCES =							\
-		util/storage_file.c util/storage_file.h		\
 		util/sysinfo.c util/sysinfo.h			\
 		util/threads.c util/threads.h			\
 		util/threads-pthread.h				\
@@ -86,6 +85,7 @@ UTIL_SOURCES =							\
 		util/virprocess.c util/virprocess.h		\
 		util/virsexpr.c util/virsexpr.h			\
 		util/virstatslinux.c util/virstatslinux.h	\
+		util/virstoragefile.c util/virstoragefile.h	\
 		util/virtypedparam.c util/virtypedparam.h	\
 		util/xml.c util/xml.h				\
 		util/virterror.c util/virterror_internal.h	\
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index f80f5f1..b3c3557 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -42,7 +42,7 @@
 #include "virbuffer.h"
 #include "virlog.h"
 #include "nwfilter_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "virfile.h"
 #include "virbitmap.h"
 #include "count-one-bits.h"
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 26d2264..6bac92f 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -47,7 +47,7 @@
 # include "virobject.h"
 # include "device_conf.h"
 # include "virbitmap.h"
-# include "storage_file.h"
+# include "virstoragefile.h"
 
 /* forward declarations of all device types, required by
  * virDomainDeviceDef
diff --git a/src/conf/snapshot_conf.c b/src/conf/snapshot_conf.c
index 9c16a88..5c40e97 100644
--- a/src/conf/snapshot_conf.c
+++ b/src/conf/snapshot_conf.c
@@ -41,7 +41,7 @@
 #include "nwfilter_conf.h"
 #include "secret_conf.h"
 #include "snapshot_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "util.h"
 #include "uuid.h"
 #include "virfile.h"
diff --git a/src/conf/storage_conf.c b/src/conf/storage_conf.c
index 0e00588..5cd2393 100644
--- a/src/conf/storage_conf.c
+++ b/src/conf/storage_conf.c
@@ -36,7 +36,7 @@
 #include "virterror_internal.h"
 #include "datatypes.h"
 #include "storage_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 #include "xml.h"
 #include "uuid.h"
diff --git a/src/esx/esx_storage_backend_iscsi.c b/src/esx/esx_storage_backend_iscsi.c
index 9d481d2..5ad885a 100644
--- a/src/esx/esx_storage_backend_iscsi.c
+++ b/src/esx/esx_storage_backend_iscsi.c
@@ -33,7 +33,7 @@
 #include "virlog.h"
 #include "uuid.h"
 #include "storage_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "esx_storage_backend_iscsi.h"
 #include "esx_private.h"
 #include "esx_vi.h"
diff --git a/src/esx/esx_storage_backend_vmfs.c b/src/esx/esx_storage_backend_vmfs.c
index bca637b..4886fc3 100644
--- a/src/esx/esx_storage_backend_vmfs.c
+++ b/src/esx/esx_storage_backend_vmfs.c
@@ -36,7 +36,7 @@
 #include "virlog.h"
 #include "uuid.h"
 #include "storage_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "esx_storage_backend_vmfs.h"
 #include "esx_private.h"
 #include "esx_vi.h"
diff --git a/src/libxl/libxl_conf.c b/src/libxl/libxl_conf.c
index ac55cf3..eb6738c 100644
--- a/src/libxl/libxl_conf.c
+++ b/src/libxl/libxl_conf.c
@@ -43,7 +43,7 @@
 #include "libxl_driver.h"
 #include "libxl_conf.h"
 #include "libxl_utils.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 
 #define VIR_FROM_THIS VIR_FROM_LIBXL
diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c
index 2d3dc4a..07c1463 100644
--- a/src/parallels/parallels_driver.c
+++ b/src/parallels/parallels_driver.c
@@ -48,7 +48,7 @@
 #include "virlog.h"
 #include "vircommand.h"
 #include "configmake.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "nodeinfo.h"
 #include "c-ctype.h"
 
diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c
index f546d28..0e6c100 100644
--- a/src/parallels/parallels_storage.c
+++ b/src/parallels/parallels_storage.c
@@ -33,7 +33,7 @@
 #include "datatypes.h"
 #include "viralloc.h"
 #include "configmake.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "virterror_internal.h"
 
 #include "parallels_utils.h"
diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c
index 8a57cb5..464288f 100644
--- a/src/qemu/qemu_command.c
+++ b/src/qemu/qemu_command.c
@@ -43,7 +43,7 @@
 #include "virnetdevtap.h"
 #include "base64.h"
 #include "device_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 #include <sys/utsname.h>
 #include <sys/stat.h>
diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c
index 8dcadbc..3e1081a 100644
--- a/src/qemu/qemu_domain.c
+++ b/src/qemu/qemu_domain.c
@@ -36,7 +36,7 @@
 #include "virfile.h"
 #include "domain_event.h"
 #include "virtime.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 #include <sys/time.h>
 #include <fcntl.h>
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index c5bd054..fd36dfc 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -81,7 +81,7 @@
 #include "sysinfo.h"
 #include "domain_nwfilter.h"
 #include "virhooks.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "virfile.h"
 #include "fdstream.h"
 #include "configmake.h"
diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c
index e120988..e5b28da 100644
--- a/src/qemu/qemu_hotplug.c
+++ b/src/qemu/qemu_hotplug.c
@@ -45,7 +45,7 @@
 #include "virnetdevbridge.h"
 #include "virnetdevtap.h"
 #include "device_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c
index 3e9ff03..a77beb6 100644
--- a/src/qemu/qemu_migration.c
+++ b/src/qemu/qemu_migration.c
@@ -47,7 +47,7 @@
 #include "virtime.h"
 #include "locking/domain_lock.h"
 #include "rpc/virnetsocket.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "viruri.h"
 #include "virhooks.h"
 
diff --git a/src/security/security_dac.c b/src/security/security_dac.c
index e4f016a..f9752ef 100644
--- a/src/security/security_dac.c
+++ b/src/security/security_dac.c
@@ -30,7 +30,7 @@
 #include "virlog.h"
 #include "virpci.h"
 #include "virusb.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 #define VIR_FROM_THIS VIR_FROM_SECURITY
 #define SECURITY_DAC_NAME "dac"
diff --git a/src/security/security_selinux.c b/src/security/security_selinux.c
index 2adf5c9..8918257 100644
--- a/src/security/security_selinux.c
+++ b/src/security/security_selinux.c
@@ -39,7 +39,7 @@
 #include "virlog.h"
 #include "virpci.h"
 #include "virusb.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "virfile.h"
 #include "virhash.h"
 #include "virrandom.h"
diff --git a/src/storage/storage_backend.c b/src/storage/storage_backend.c
index cdc5bda..9b98dbb 100644
--- a/src/storage/storage_backend.c
+++ b/src/storage/storage_backend.c
@@ -52,7 +52,7 @@
 #include "internal.h"
 #include "secret_conf.h"
 #include "uuid.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "storage_backend.h"
 #include "virlog.h"
 #include "virfile.h"
diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
index f7b4656..b744fb4 100644
--- a/src/storage/storage_backend_fs.c
+++ b/src/storage/storage_backend_fs.c
@@ -44,7 +44,7 @@
 #include "virterror_internal.h"
 #include "storage_backend_fs.h"
 #include "storage_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "vircommand.h"
 #include "viralloc.h"
 #include "xml.h"
diff --git a/src/util/storage_file.c b/src/util/storage_file.c
deleted file mode 100644
index a020bb2..0000000
--- a/src/util/storage_file.c
+++ /dev/null
@@ -1,1397 +0,0 @@
-/*
- * storage_file.c: file utility functions for FS storage backend
- *
- * Copyright (C) 2007-2012 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, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange redhat com>
- */
-
-#include <config.h>
-#include "storage_file.h"
-
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <stdlib.h>
-#ifdef __linux__
-# if HAVE_LINUX_MAGIC_H
-#  include <linux/magic.h>
-# endif
-# include <sys/statfs.h>
-#endif
-#include "dirname.h"
-#include "viralloc.h"
-#include "virterror_internal.h"
-#include "virlog.h"
-#include "virfile.h"
-#include "c-ctype.h"
-#include "vircommand.h"
-#include "virhash.h"
-
-#define VIR_FROM_THIS VIR_FROM_STORAGE
-
-VIR_ENUM_IMPL(virStorageFileFormat,
-              VIR_STORAGE_FILE_LAST,
-              "none",
-              "raw", "dir", "bochs",
-              "cloop", "cow", "dmg", "iso",
-              "qcow", "qcow2", "qed", "vmdk", "vpc",
-              "fat", "vhd")
-
-enum lv_endian {
-    LV_LITTLE_ENDIAN = 1, /* 1234 */
-    LV_BIG_ENDIAN         /* 4321 */
-};
-
-enum {
-    BACKING_STORE_OK,
-    BACKING_STORE_INVALID,
-    BACKING_STORE_ERROR,
-};
-
-/* Either 'magic' or 'extension' *must* be provided */
-struct FileTypeInfo {
-    const char *magic;  /* Optional string of file magic
-                         * to check at head of file */
-    const char *extension; /* Optional file extension to check */
-    enum lv_endian 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 */
-                          /* Store a COW base image path (possibly relative),
-                           * or NULL if there is no COW base image, to RES;
-                           * return BACKING_STORE_* */
-    int qcowCryptOffset;  /* Byte offset from start of file
-                           * where to find encryption mode,
-                           * -1 if encryption is not used */
-    int (*getBackingStore)(char **res, int *format,
-                           const unsigned char *buf, size_t buf_size);
-};
-
-static int cowGetBackingStore(char **, int *,
-                              const unsigned char *, size_t);
-static int qcow1GetBackingStore(char **, int *,
-                                const unsigned char *, size_t);
-static int qcow2GetBackingStore(char **, int *,
-                                const unsigned char *, size_t);
-static int vmdk4GetBackingStore(char **, int *,
-                                const unsigned char *, size_t);
-static int
-qedGetBackingStore(char **, int *, const unsigned char *, size_t);
-
-#define QCOWX_HDR_VERSION (4)
-#define QCOWX_HDR_BACKING_FILE_OFFSET (QCOWX_HDR_VERSION+4)
-#define QCOWX_HDR_BACKING_FILE_SIZE (QCOWX_HDR_BACKING_FILE_OFFSET+8)
-#define QCOWX_HDR_IMAGE_SIZE (QCOWX_HDR_BACKING_FILE_SIZE+4+4)
-
-#define QCOW1_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8+1+1)
-#define QCOW2_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8)
-
-#define QCOW1_HDR_TOTAL_SIZE (QCOW1_HDR_CRYPT+4+8)
-#define QCOW2_HDR_TOTAL_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8)
-
-#define QCOW2_HDR_EXTENSION_END 0
-#define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA
-
-#define QED_HDR_FEATURES_OFFSET (4+4+4+4)
-#define QED_HDR_IMAGE_SIZE (QED_HDR_FEATURES_OFFSET+8+8+8+8)
-#define QED_HDR_BACKING_FILE_OFFSET (QED_HDR_IMAGE_SIZE+8)
-#define QED_HDR_BACKING_FILE_SIZE (QED_HDR_BACKING_FILE_OFFSET+4)
-#define QED_F_BACKING_FILE 0x01
-#define QED_F_BACKING_FORMAT_NO_PROBE 0x04
-
-/* VMDK needs at least this to find backing store,
- * other formats need less */
-#define STORAGE_MAX_HEAD (20*512)
-
-
-static struct FileTypeInfo const fileTypeInfo[] = {
-    [VIR_STORAGE_FILE_NONE] = { NULL, NULL, LV_LITTLE_ENDIAN,
-                                -1, 0, 0, 0, 0, 0, NULL },
-    [VIR_STORAGE_FILE_RAW] = { NULL, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, 0, 0, 0, 0, NULL },
-    [VIR_STORAGE_FILE_DIR] = { NULL, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, 0, 0, 0, 0, NULL },
-    [VIR_STORAGE_FILE_BOCHS] = {
-        /*"Bochs Virtual HD Image", */ /* Untested */ NULL,
-        NULL,
-        LV_LITTLE_ENDIAN, 64, 0x20000,
-        32+16+16+4+4+4+4+4, 8, 1, -1, NULL
-    },
-    [VIR_STORAGE_FILE_CLOOP] = {
-        /*"#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", */ /* Untested */ NULL,
-        NULL,
-        LV_LITTLE_ENDIAN, -1, 0,
-        -1, 0, 0, -1, NULL
-    },
-    [VIR_STORAGE_FILE_COW] = {
-        "OOOM", NULL,
-        LV_BIG_ENDIAN, 4, 2,
-        4+4+1024+4, 8, 1, -1, cowGetBackingStore
-    },
-    [VIR_STORAGE_FILE_DMG] = {
-        NULL, /* XXX QEMU says there's no magic for dmg, but we should check... */
-        ".dmg",
-        0, -1, 0,
-        -1, 0, 0, -1, NULL
-    },
-    [VIR_STORAGE_FILE_ISO] = {
-        NULL, /* XXX there's probably some magic for iso we can validate too... */
-        ".iso",
-        0, -1, 0,
-        -1, 0, 0, -1, NULL
-    },
-    [VIR_STORAGE_FILE_QCOW] = {
-        "QFI", NULL,
-        LV_BIG_ENDIAN, 4, 1,
-        QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW1_HDR_CRYPT, qcow1GetBackingStore,
-    },
-    [VIR_STORAGE_FILE_QCOW2] = {
-        "QFI", NULL,
-        LV_BIG_ENDIAN, 4, 2,
-        QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow2GetBackingStore,
-    },
-    [VIR_STORAGE_FILE_QED] = {
-        /* http://wiki.qemu.org/Features/QED */
-        "QED", NULL,
-        LV_LITTLE_ENDIAN, -2, -1,
-        QED_HDR_IMAGE_SIZE, 8, 1, -1, qedGetBackingStore,
-    },
-    [VIR_STORAGE_FILE_VMDK] = {
-        "KDMV", NULL,
-        LV_LITTLE_ENDIAN, 4, 1,
-        4+4+4, 8, 512, -1, vmdk4GetBackingStore
-    },
-    [VIR_STORAGE_FILE_VPC] = {
-        "conectix", NULL,
-        LV_BIG_ENDIAN, 12, 0x10000,
-        8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, -1, NULL
-    },
-    /* Not direct file formats, but used for various drivers */
-    [VIR_STORAGE_FILE_FAT] = { NULL, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, 0, 0, 0, 0, NULL },
-    [VIR_STORAGE_FILE_VHD] = { NULL, NULL, LV_LITTLE_ENDIAN,
-                               -1, 0, 0, 0, 0, 0, NULL },
-};
-verify(ARRAY_CARDINALITY(fileTypeInfo) == VIR_STORAGE_FILE_LAST);
-
-static int
-cowGetBackingStore(char **res,
-                   int *format,
-                   const unsigned char *buf,
-                   size_t buf_size)
-{
-#define COW_FILENAME_MAXLEN 1024
-    *res = NULL;
-    *format = VIR_STORAGE_FILE_AUTO;
-
-    if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
-        return BACKING_STORE_INVALID;
-    if (buf[4+4] == '\0') { /* cow_header_v2.backing_file[0] */
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-
-    *res = strndup((const char*)buf + 4+4, COW_FILENAME_MAXLEN);
-    if (*res == NULL) {
-        virReportOOMError();
-        return BACKING_STORE_ERROR;
-    }
-    return BACKING_STORE_OK;
-}
-
-
-static int
-qcow2GetBackingStoreFormat(int *format,
-                           const unsigned char *buf,
-                           size_t buf_size,
-                           size_t extension_start,
-                           size_t extension_end)
-{
-    size_t offset = extension_start;
-
-    /*
-     * The extensions take format of
-     *
-     * int32: magic
-     * int32: length
-     * byte[length]: payload
-     *
-     * Unknown extensions can be ignored by skipping
-     * over "length" bytes in the data stream.
-     */
-    while (offset < (buf_size-8) &&
-           offset < (extension_end-8)) {
-        unsigned int magic =
-            (buf[offset] << 24) +
-            (buf[offset+1] << 16) +
-            (buf[offset+2] << 8) +
-            (buf[offset+3]);
-        unsigned int len =
-            (buf[offset+4] << 24) +
-            (buf[offset+5] << 16) +
-            (buf[offset+6] << 8) +
-            (buf[offset+7]);
-
-        offset += 8;
-
-        if ((offset + len) < offset)
-            break;
-
-        if ((offset + len) > buf_size)
-            break;
-
-        switch (magic) {
-        case QCOW2_HDR_EXTENSION_END:
-            goto done;
-
-        case QCOW2_HDR_EXTENSION_BACKING_FORMAT:
-            if (buf[offset+len] != '\0')
-                break;
-            *format = virStorageFileFormatTypeFromString(
-                ((const char *)buf)+offset);
-            if (*format <= VIR_STORAGE_FILE_NONE)
-                return -1;
-        }
-
-        offset += len;
-    }
-
-done:
-
-    return 0;
-}
-
-
-static int
-qcowXGetBackingStore(char **res,
-                     int *format,
-                     const unsigned char *buf,
-                     size_t buf_size,
-                     bool isQCow2)
-{
-    unsigned long long offset;
-    unsigned int size;
-
-    *res = NULL;
-    if (format)
-        *format = VIR_STORAGE_FILE_AUTO;
-
-    if (buf_size < QCOWX_HDR_BACKING_FILE_OFFSET+8+4)
-        return BACKING_STORE_INVALID;
-    offset = (((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET] << 56)
-              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+1] << 48)
-              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+2] << 40)
-              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+3] << 32)
-              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+4] << 24)
-              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+5] << 16)
-              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+6] << 8)
-              | buf[QCOWX_HDR_BACKING_FILE_OFFSET+7]); /* QCowHeader.backing_file_offset */
-    if (offset > buf_size)
-        return BACKING_STORE_INVALID;
-    size = ((buf[QCOWX_HDR_BACKING_FILE_SIZE] << 24)
-            | (buf[QCOWX_HDR_BACKING_FILE_SIZE+1] << 16)
-            | (buf[QCOWX_HDR_BACKING_FILE_SIZE+2] << 8)
-            | buf[QCOWX_HDR_BACKING_FILE_SIZE+3]); /* QCowHeader.backing_file_size */
-    if (size == 0) {
-        if (format)
-            *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-    if (offset + size > buf_size || offset + size < offset)
-        return BACKING_STORE_INVALID;
-    if (size + 1 == 0)
-        return BACKING_STORE_INVALID;
-    if (VIR_ALLOC_N(*res, size + 1) < 0) {
-        virReportOOMError();
-        return BACKING_STORE_ERROR;
-    }
-    memcpy(*res, buf + offset, size);
-    (*res)[size] = '\0';
-
-    /*
-     * Traditionally QCow2 files had a layout of
-     *
-     * [header]
-     * [backingStoreName]
-     *
-     * Although the backingStoreName typically followed
-     * the header immediately, this was not required by
-     * the format. By specifying a higher byte offset for
-     * the backing file offset in the header, it was
-     * possible to leave space between the header and
-     * start of backingStore.
-     *
-     * This hack is now used to store extensions to the
-     * qcow2 format:
-     *
-     * [header]
-     * [extensions]
-     * [backingStoreName]
-     *
-     * Thus the file region to search for extensions is
-     * between the end of the header (QCOW2_HDR_TOTAL_SIZE)
-     * and the start of the backingStoreName (offset)
-     */
-    if (isQCow2 && format &&
-        qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE,
-                                   offset) < 0)
-        return BACKING_STORE_INVALID;
-
-    return BACKING_STORE_OK;
-}
-
-
-static int
-qcow1GetBackingStore(char **res,
-                     int *format,
-                     const unsigned char *buf,
-                     size_t buf_size)
-{
-    int ret;
-
-    /* QCow1 doesn't have the extensions capability
-     * used to store backing format */
-    *format = VIR_STORAGE_FILE_AUTO;
-    ret = qcowXGetBackingStore(res, NULL, buf, buf_size, false);
-    if (ret == 0 && *buf == '\0')
-        *format = VIR_STORAGE_FILE_NONE;
-    return ret;
-}
-
-static int
-qcow2GetBackingStore(char **res,
-                     int *format,
-                     const unsigned char *buf,
-                     size_t buf_size)
-{
-    return qcowXGetBackingStore(res, format, buf, buf_size, true);
-}
-
-
-static int
-vmdk4GetBackingStore(char **res,
-                     int *format,
-                     const unsigned char *buf,
-                     size_t buf_size)
-{
-    static const char prefix[] = "parentFileNameHint=\"";
-    char *desc, *start, *end;
-    size_t len;
-    int ret = BACKING_STORE_ERROR;
-
-    if (VIR_ALLOC_N(desc, STORAGE_MAX_HEAD + 1) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    *res = NULL;
-    /*
-     * Technically this should have been VMDK, since
-     * VMDK spec / VMWare impl only support VMDK backed
-     * by VMDK. QEMU isn't following this though and
-     * does probing on VMDK backing files, hence we set
-     * AUTO
-     */
-    *format = VIR_STORAGE_FILE_AUTO;
-
-    if (buf_size <= 0x200) {
-        ret = BACKING_STORE_INVALID;
-        goto cleanup;
-    }
-    len = buf_size - 0x200;
-    if (len > STORAGE_MAX_HEAD)
-        len = STORAGE_MAX_HEAD;
-    memcpy(desc, buf + 0x200, len);
-    desc[len] = '\0';
-    start = strstr(desc, prefix);
-    if (start == NULL) {
-        *format = VIR_STORAGE_FILE_NONE;
-        ret = BACKING_STORE_OK;
-        goto cleanup;
-    }
-    start += strlen(prefix);
-    end = strchr(start, '"');
-    if (end == NULL) {
-        ret = BACKING_STORE_INVALID;
-        goto cleanup;
-    }
-    if (end == start) {
-        *format = VIR_STORAGE_FILE_NONE;
-        ret = BACKING_STORE_OK;
-        goto cleanup;
-    }
-    *end = '\0';
-    *res = strdup(start);
-    if (*res == NULL) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    ret = BACKING_STORE_OK;
-
-cleanup:
-    VIR_FREE(desc);
-    return ret;
-}
-
-static unsigned long
-qedGetHeaderUL(const unsigned char *loc)
-{
-    return (((unsigned long)loc[3] << 24) |
-            ((unsigned long)loc[2] << 16) |
-            ((unsigned long)loc[1] << 8) |
-            ((unsigned long)loc[0] << 0));
-}
-
-static unsigned long long
-qedGetHeaderULL(const unsigned char *loc)
-{
-    return (((unsigned long long)loc[7] << 56) |
-            ((unsigned long long)loc[6] << 48) |
-            ((unsigned long long)loc[5] << 40) |
-            ((unsigned long long)loc[4] << 32) |
-            ((unsigned long long)loc[3] << 24) |
-            ((unsigned long long)loc[2] << 16) |
-            ((unsigned long long)loc[1] << 8) |
-            ((unsigned long long)loc[0] << 0));
-}
-
-static int
-qedGetBackingStore(char **res,
-                   int *format,
-                   const unsigned char *buf,
-                   size_t buf_size)
-{
-    unsigned long long flags;
-    unsigned long offset, size;
-
-    *res = NULL;
-    /* Check if this image has a backing file */
-    if (buf_size < QED_HDR_FEATURES_OFFSET+8)
-        return BACKING_STORE_INVALID;
-    flags = qedGetHeaderULL(buf + QED_HDR_FEATURES_OFFSET);
-    if (!(flags & QED_F_BACKING_FILE)) {
-        *format = VIR_STORAGE_FILE_NONE;
-        return BACKING_STORE_OK;
-    }
-
-    /* Parse the backing file */
-    if (buf_size < QED_HDR_BACKING_FILE_OFFSET+8)
-        return BACKING_STORE_INVALID;
-    offset = qedGetHeaderUL(buf + QED_HDR_BACKING_FILE_OFFSET);
-    if (offset > buf_size)
-        return BACKING_STORE_INVALID;
-    size = qedGetHeaderUL(buf + QED_HDR_BACKING_FILE_SIZE);
-    if (size == 0)
-        return BACKING_STORE_OK;
-    if (offset + size > buf_size || offset + size < offset)
-        return BACKING_STORE_INVALID;
-    if (VIR_ALLOC_N(*res, size + 1) < 0) {
-        virReportOOMError();
-        return BACKING_STORE_ERROR;
-    }
-    memcpy(*res, buf + offset, size);
-    (*res)[size] = '\0';
-
-    if (flags & QED_F_BACKING_FORMAT_NO_PROBE)
-        *format = VIR_STORAGE_FILE_RAW;
-    else
-        *format = VIR_STORAGE_FILE_AUTO_SAFE;
-
-    return BACKING_STORE_OK;
-}
-
-/**
- * Return an absolute path corresponding to PATH, which is absolute or relative
- * to the directory containing BASE_FILE, or NULL on error
- */
-static char *
-absolutePathFromBaseFile(const char *base_file, const char *path)
-{
-    char *res = NULL;
-    char *tmp = NULL;
-    size_t d_len = dir_len(base_file);
-
-    /* If path is already absolute, or if dirname(base_file) is ".",
-       just return a copy of path.  */
-    if (*path == '/' || d_len == 0) {
-        if (!(res = canonicalize_file_name(path)))
-            virReportSystemError(errno,
-                                 _("Can't canonicalize path '%s'"), path);
-
-        goto cleanup;
-    }
-
-    /* Ensure that the following cast-to-int is valid.  */
-    if (d_len > INT_MAX) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("Directory name too long: '%s'"), base_file);
-        goto cleanup;
-    }
-
-    if (virAsprintf(&tmp, "%.*s/%s", (int) d_len, base_file, path) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    if (!(res = canonicalize_file_name(tmp)))
-        virReportSystemError(errno, _("Can't canonicalize path '%s'"), path);
-
-cleanup:
-    VIR_FREE(tmp);
-    return res;
-}
-
-
-static bool
-virStorageFileMatchesMagic(int format,
-                           unsigned char *buf,
-                           size_t buflen)
-{
-    int mlen;
-
-    if (fileTypeInfo[format].magic == NULL)
-        return false;
-
-    /* Validate magic data */
-    mlen = strlen(fileTypeInfo[format].magic);
-    if (mlen > buflen)
-        return false;
-
-    if (memcmp(buf, fileTypeInfo[format].magic, mlen) != 0)
-        return false;
-
-    return true;
-}
-
-
-static bool
-virStorageFileMatchesExtension(int format,
-                               const char *path)
-{
-    if (fileTypeInfo[format].extension == NULL)
-        return false;
-
-    if (virFileHasSuffix(path, fileTypeInfo[format].extension))
-        return true;
-
-    return false;
-}
-
-
-static bool
-virStorageFileMatchesVersion(int format,
-                             unsigned char *buf,
-                             size_t buflen)
-{
-    int version;
-
-    /* Validate version number info */
-    if (fileTypeInfo[format].versionOffset == -1)
-        return false;
-
-    /* -2 == non-versioned file format, so trivially match */
-    if (fileTypeInfo[format].versionOffset == -2)
-        return true;
-
-    if ((fileTypeInfo[format].versionOffset + 4) > buflen)
-        return false;
-
-    if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) {
-        version =
-            (buf[fileTypeInfo[format].versionOffset+3] << 24) |
-            (buf[fileTypeInfo[format].versionOffset+2] << 16) |
-            (buf[fileTypeInfo[format].versionOffset+1] << 8) |
-            (buf[fileTypeInfo[format].versionOffset]);
-    } else {
-        version =
-            (buf[fileTypeInfo[format].versionOffset] << 24) |
-            (buf[fileTypeInfo[format].versionOffset+1] << 16) |
-            (buf[fileTypeInfo[format].versionOffset+2] << 8) |
-            (buf[fileTypeInfo[format].versionOffset+3]);
-    }
-
-    VIR_DEBUG("Compare detected version %d vs expected version %d",
-              version, fileTypeInfo[format].versionNumber);
-    if (version != fileTypeInfo[format].versionNumber)
-        return false;
-
-    return true;
-}
-
-static bool
-virBackingStoreIsFile(const char *backing)
-{
-    /* Backing store is a network block device or Rados block device */
-    if (STRPREFIX(backing, "nbd:") || STRPREFIX(backing, "rbd:"))
-        return false;
-    return true;
-}
-
-static int
-virStorageFileGetMetadataFromBuf(int format,
-                                 const char *path,
-                                 unsigned char *buf,
-                                 size_t buflen,
-                                 virStorageFileMetadata *meta)
-{
-    VIR_DEBUG("path=%s format=%d", path, format);
-
-    /* XXX we should consider moving virStorageBackendUpdateVolInfo
-     * code into this method, for non-magic files
-     */
-    if (format <= VIR_STORAGE_FILE_NONE ||
-        format >= VIR_STORAGE_FILE_LAST ||
-        !fileTypeInfo[format].magic) {
-        return 0;
-    }
-
-    /* Optionally extract capacity from file */
-    if (fileTypeInfo[format].sizeOffset != -1) {
-        if ((fileTypeInfo[format].sizeOffset + 8) > buflen)
-            return 1;
-
-        if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) {
-            meta->capacity =
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+7] << 56) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+6] << 48) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+5] << 40) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+4] << 32) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+3] << 24) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+2] << 16) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+1] << 8) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset]);
-        } else {
-            meta->capacity =
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset] << 56) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+1] << 48) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+2] << 40) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+3] << 32) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+4] << 24) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+5] << 16) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+6] << 8) |
-                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+7]);
-        }
-        /* Avoid unlikely, but theoretically possible overflow */
-        if (meta->capacity > (ULLONG_MAX / fileTypeInfo[format].sizeMultiplier))
-            return 1;
-        meta->capacity *= fileTypeInfo[format].sizeMultiplier;
-    }
-
-    if (fileTypeInfo[format].qcowCryptOffset != -1) {
-        int crypt_format;
-
-        crypt_format =
-            (buf[fileTypeInfo[format].qcowCryptOffset] << 24) |
-            (buf[fileTypeInfo[format].qcowCryptOffset+1] << 16) |
-            (buf[fileTypeInfo[format].qcowCryptOffset+2] << 8) |
-            (buf[fileTypeInfo[format].qcowCryptOffset+3]);
-        meta->encrypted = crypt_format != 0;
-    }
-
-    if (fileTypeInfo[format].getBackingStore != NULL) {
-        char *backing;
-        int backingFormat;
-        int ret = fileTypeInfo[format].getBackingStore(&backing,
-                                                       &backingFormat,
-                                                       buf, buflen);
-        if (ret == BACKING_STORE_INVALID)
-            return 1;
-
-        if (ret == BACKING_STORE_ERROR)
-            return -1;
-
-        meta->backingStoreIsFile = false;
-        if (backing != NULL) {
-            meta->backingStore = strdup(backing);
-            if (meta->backingStore == NULL) {
-                virReportOOMError();
-                VIR_FREE(backing);
-                return -1;
-            }
-            if (virBackingStoreIsFile(backing)) {
-                meta->backingStoreIsFile = true;
-                meta->backingStoreRaw = meta->backingStore;
-                meta->backingStore = absolutePathFromBaseFile(path, backing);
-                if (meta->backingStore == NULL) {
-                    /* the backing file is (currently) unavailable, treat this
-                     * file as standalone:
-                     * backingStoreRaw is kept to mark broken image chains */
-                    meta->backingStoreIsFile = false;
-                    backingFormat = VIR_STORAGE_FILE_NONE;
-                    VIR_WARN("Backing file '%s' of image '%s' is missing.",
-                             meta->backingStoreRaw, path);
-
-                }
-            }
-            VIR_FREE(backing);
-            meta->backingStoreFormat = backingFormat;
-        } else {
-            meta->backingStore = NULL;
-            meta->backingStoreFormat = VIR_STORAGE_FILE_NONE;
-        }
-    }
-
-    return 0;
-}
-
-
-static int
-virStorageFileProbeFormatFromBuf(const char *path,
-                                 unsigned char *buf,
-                                 size_t buflen)
-{
-    int format = VIR_STORAGE_FILE_RAW;
-    int i;
-    int possibleFormat = VIR_STORAGE_FILE_RAW;
-    VIR_DEBUG("path=%s", path);
-
-    /* First check file magic */
-    for (i = 0 ; i < VIR_STORAGE_FILE_LAST ; i++) {
-        if (virStorageFileMatchesMagic(i, buf, buflen)) {
-            if (!virStorageFileMatchesVersion(i, buf, buflen)) {
-                possibleFormat = i;
-                continue;
-            }
-            format = i;
-            goto cleanup;
-        }
-    }
-
-    if (possibleFormat != VIR_STORAGE_FILE_RAW)
-        VIR_WARN("File %s matches %s magic, but version is wrong. "
-                 "Please report new version to libvir-list redhat com",
-                 path, virStorageFileFormatTypeToString(possibleFormat));
-
-    /* No magic, so check file extension */
-    for (i = 0 ; i < VIR_STORAGE_FILE_LAST ; i++) {
-        if (virStorageFileMatchesExtension(i, path)) {
-            format = i;
-            goto cleanup;
-        }
-    }
-
-cleanup:
-    VIR_DEBUG("format=%d", format);
-    return format;
-}
-
-
-/**
- * virStorageFileProbeFormatFromFD:
- *
- * Probe for the format of 'fd' (which is an open file descriptor
- * pointing to 'path'), returning the detected disk format.
- *
- * Callers are advised never to trust the returned 'format'
- * unless it is listed as VIR_STORAGE_FILE_RAW, since a
- * malicious guest can turn a file into any other non-raw
- * format at will.
- *
- * Best option: Don't use this function
- */
-int
-virStorageFileProbeFormatFromFD(const char *path, int fd)
-{
-    unsigned char *head;
-    ssize_t len = STORAGE_MAX_HEAD;
-    int ret = -1;
-    struct stat sb;
-
-    if (fstat(fd, &sb) < 0) {
-        virReportSystemError(errno,
-                             _("cannot stat file '%s'"),
-                             path);
-        return -1;
-    }
-
-    /* No header to probe for directories */
-    if (S_ISDIR(sb.st_mode)) {
-        return VIR_STORAGE_FILE_DIR;
-    }
-
-    if (VIR_ALLOC_N(head, len) < 0) {
-        virReportOOMError();
-        return -1;
-    }
-
-    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
-        virReportSystemError(errno, _("cannot set to start of '%s'"), path);
-        goto cleanup;
-    }
-
-    if ((len = read(fd, head, len)) < 0) {
-        virReportSystemError(errno, _("cannot read header '%s'"), path);
-        goto cleanup;
-    }
-
-    ret = virStorageFileProbeFormatFromBuf(path, head, len);
-
-cleanup:
-    VIR_FREE(head);
-    return ret;
-}
-
-
-/**
- * virStorageFileProbeFormat:
- *
- * Probe for the format of 'path', returning the detected
- * disk format.
- *
- * Callers are advised never to trust the returned 'format'
- * unless it is listed as VIR_STORAGE_FILE_RAW, since a
- * malicious guest can turn a raw file into any other non-raw
- * format at will.
- *
- * Best option: Don't use this function
- */
-int
-virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid)
-{
-    int fd, ret;
-
-    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
-        virReportSystemError(errno, _("cannot open file '%s'"), path);
-        return -1;
-    }
-
-    ret = virStorageFileProbeFormatFromFD(path, fd);
-
-    VIR_FORCE_CLOSE(fd);
-
-    return ret;
-}
-
-/**
- * virStorageFileGetMetadataFromFD:
- *
- * Extract metadata about the storage volume with the specified
- * image format. If image format is VIR_STORAGE_FILE_AUTO, it
- * will probe to automatically identify the format.  Does not recurse.
- *
- * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
- * format, since a malicious guest can turn a raw file into any
- * other non-raw format at will.
- *
- * If the returned meta.backingStoreFormat is VIR_STORAGE_FILE_AUTO
- * it indicates the image didn't specify an explicit format for its
- * backing store. Callers are advised against probing for the
- * backing store format in this case.
- *
- * Caller MUST free the result after use via virStorageFileFreeMetadata.
- */
-virStorageFileMetadataPtr
-virStorageFileGetMetadataFromFD(const char *path,
-                                int fd,
-                                int format)
-{
-    virStorageFileMetadata *meta = NULL;
-    unsigned char *head = NULL;
-    ssize_t len = STORAGE_MAX_HEAD;
-    virStorageFileMetadata *ret = NULL;
-    struct stat sb;
-
-    if (VIR_ALLOC(meta) < 0) {
-        virReportOOMError();
-        return NULL;
-    }
-
-    if (fstat(fd, &sb) < 0) {
-        virReportSystemError(errno,
-                             _("cannot stat file '%s'"),
-                             path);
-        goto cleanup;
-    }
-
-    /* No header to probe for directories, but also no backing file */
-    if (S_ISDIR(sb.st_mode))
-        return meta;
-
-    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
-        virReportSystemError(errno, _("cannot seek to start of '%s'"), path);
-        goto cleanup;
-    }
-
-    if (VIR_ALLOC_N(head, len) < 0) {
-        virReportOOMError();
-        goto cleanup;
-    }
-
-    if ((len = read(fd, head, len)) < 0) {
-        virReportSystemError(errno, _("cannot read header '%s'"), path);
-        goto cleanup;
-    }
-
-    if (format == VIR_STORAGE_FILE_AUTO)
-        format = virStorageFileProbeFormatFromBuf(path, head, len);
-
-    if (format <= VIR_STORAGE_FILE_NONE ||
-        format >= VIR_STORAGE_FILE_LAST) {
-        virReportSystemError(EINVAL, _("unknown storage file format %d"),
-                             format);
-        goto cleanup;
-    }
-
-    if (virStorageFileGetMetadataFromBuf(format, path, head, len, meta) < 0)
-        goto cleanup;
-    ret = meta;
-    meta = NULL;
-
-cleanup:
-    virStorageFileFreeMetadata(meta);
-    VIR_FREE(head);
-    return ret;
-}
-
-/* Recursive workhorse for virStorageFileGetMetadata.  */
-static virStorageFileMetadataPtr
-virStorageFileGetMetadataRecurse(const char *path, int format,
-                                 uid_t uid, gid_t gid,
-                                 bool allow_probe, virHashTablePtr cycle)
-{
-    int fd;
-    VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d",
-              path, format, (int)uid, (int)gid, allow_probe);
-
-    virStorageFileMetadataPtr ret = NULL;
-
-    if (virHashLookup(cycle, path)) {
-        virReportError(VIR_ERR_INTERNAL_ERROR,
-                       _("backing store for %s is self-referential"),
-                       path);
-        return NULL;
-    }
-    if (virHashAddEntry(cycle, path, (void *)1) < 0)
-        return NULL;
-
-    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
-        virReportSystemError(-fd, _("cannot open file '%s'"), path);
-        return NULL;
-    }
-
-    ret = virStorageFileGetMetadataFromFD(path, fd, format);
-
-    if (VIR_CLOSE(fd) < 0)
-        VIR_WARN("could not close file %s", path);
-
-    if (ret && ret->backingStoreIsFile) {
-        if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe)
-            ret->backingStoreFormat = VIR_STORAGE_FILE_RAW;
-        else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE)
-            ret->backingStoreFormat = VIR_STORAGE_FILE_AUTO;
-        format = ret->backingStoreFormat;
-        ret->backingMeta = virStorageFileGetMetadataRecurse(ret->backingStore,
-                                                            format,
-                                                            uid, gid,
-                                                            allow_probe,
-                                                            cycle);
-    }
-
-    return ret;
-}
-
-/**
- * virStorageFileGetMetadata:
- *
- * Extract metadata about the storage volume with the specified
- * image format. If image format is VIR_STORAGE_FILE_AUTO, it
- * will probe to automatically identify the format.  Recurses through
- * the entire chain.
- *
- * Open files using UID and GID (or pass -1 for the current user/group).
- * Treat any backing files without explicit type as raw, unless ALLOW_PROBE.
- *
- * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
- * format, since a malicious guest can turn a raw file into any
- * other non-raw format at will.
- *
- * If the returned meta.backingStoreFormat is VIR_STORAGE_FILE_AUTO
- * it indicates the image didn't specify an explicit format for its
- * backing store. Callers are advised against using ALLOW_PROBE, as
- * it would probe the backing store format in this case.
- *
- * Caller MUST free result after use via virStorageFileFreeMetadata.
- */
-virStorageFileMetadataPtr
-virStorageFileGetMetadata(const char *path, int format,
-                          uid_t uid, gid_t gid,
-                          bool allow_probe)
-{
-    VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d",
-              path, format, (int)uid, (int)gid, allow_probe);
-
-    virHashTablePtr cycle = virHashCreate(5, NULL);
-    virStorageFileMetadataPtr ret;
-
-    if (!cycle)
-        return NULL;
-
-    if (format <= VIR_STORAGE_FILE_NONE)
-        format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW;
-    ret = virStorageFileGetMetadataRecurse(path, format, uid, gid,
-                                           allow_probe, cycle);
-    virHashFree(cycle);
-    return ret;
-}
-
-/**
- * virStorageFileFreeMetadata:
- *
- * Free pointers in passed structure and structure itself.
- */
-void
-virStorageFileFreeMetadata(virStorageFileMetadata *meta)
-{
-    if (!meta)
-        return;
-
-    virStorageFileFreeMetadata(meta->backingMeta);
-    VIR_FREE(meta->backingStore);
-    VIR_FREE(meta->backingStoreRaw);
-    VIR_FREE(meta);
-}
-
-/**
- * virStorageFileResize:
- *
- * Change the capacity of the raw storage file at 'path'.
- */
-int
-virStorageFileResize(const char *path, unsigned long long capacity)
-{
-    int fd = -1;
-    int ret = -1;
-
-    if ((fd = open(path, O_RDWR)) < 0) {
-        virReportSystemError(errno, _("Unable to open '%s'"), path);
-        goto cleanup;
-    }
-
-    if (ftruncate(fd, capacity) < 0) {
-        virReportSystemError(errno, _("Failed to truncate file '%s'"), path);
-        goto cleanup;
-    }
-
-    if (VIR_CLOSE(fd) < 0) {
-        virReportSystemError(errno, _("Unable to save '%s'"), path);
-        goto cleanup;
-    }
-
-    ret = 0;
-
-cleanup:
-    VIR_FORCE_CLOSE(fd);
-    return ret;
-}
-
-#ifdef __linux__
-
-# ifndef NFS_SUPER_MAGIC
-#  define NFS_SUPER_MAGIC 0x6969
-# endif
-# ifndef OCFS2_SUPER_MAGIC
-#  define OCFS2_SUPER_MAGIC 0x7461636f
-# endif
-# ifndef GFS2_MAGIC
-#  define GFS2_MAGIC 0x01161970
-# endif
-# ifndef AFS_FS_MAGIC
-#  define AFS_FS_MAGIC 0x6B414653
-# endif
-
-
-int virStorageFileIsSharedFSType(const char *path,
-                                 int fstypes)
-{
-    char *dirpath, *p;
-    struct statfs sb;
-    int statfs_ret;
-
-    if ((dirpath = strdup(path)) == NULL) {
-        virReportOOMError();
-        return -1;
-    }
-
-    do {
-
-        /* Try less and less of the path until we get to a
-         * directory we can stat. Even if we don't have 'x'
-         * permission on any directory in the path on the NFS
-         * server (assuming it's NFS), we will be able to stat the
-         * mount point, and that will properly tell us if the
-         * fstype is NFS.
-         */
-
-        if ((p = strrchr(dirpath, '/')) == NULL) {
-            virReportSystemError(EINVAL,
-                         _("Invalid relative path '%s'"), path);
-            VIR_FREE(dirpath);
-            return -1;
-        }
-
-        if (p == dirpath)
-            *(p+1) = '\0';
-        else
-            *p = '\0';
-
-        statfs_ret = statfs(dirpath, &sb);
-
-    } while ((statfs_ret < 0) && (p != dirpath));
-
-    VIR_FREE(dirpath);
-
-    if (statfs_ret < 0) {
-        virReportSystemError(errno,
-                             _("cannot determine filesystem for '%s'"),
-                             path);
-        return -1;
-    }
-
-    VIR_DEBUG("Check if path %s with FS magic %lld is shared",
-              path, (long long int)sb.f_type);
-
-    if ((fstypes & VIR_STORAGE_FILE_SHFS_NFS) &&
-        (sb.f_type == NFS_SUPER_MAGIC))
-        return 1;
-
-    if ((fstypes & VIR_STORAGE_FILE_SHFS_GFS2) &&
-        (sb.f_type == GFS2_MAGIC))
-        return 1;
-    if ((fstypes & VIR_STORAGE_FILE_SHFS_OCFS) &&
-        (sb.f_type == OCFS2_SUPER_MAGIC))
-        return 1;
-    if ((fstypes & VIR_STORAGE_FILE_SHFS_AFS) &&
-        (sb.f_type == AFS_FS_MAGIC))
-        return 1;
-
-    return 0;
-}
-#else
-int virStorageFileIsSharedFSType(const char *path ATTRIBUTE_UNUSED,
-                                 int fstypes ATTRIBUTE_UNUSED)
-{
-    /* XXX implement me :-) */
-    return 0;
-}
-#endif
-
-int virStorageFileIsSharedFS(const char *path)
-{
-    return virStorageFileIsSharedFSType(path,
-                                        VIR_STORAGE_FILE_SHFS_NFS |
-                                        VIR_STORAGE_FILE_SHFS_GFS2 |
-                                        VIR_STORAGE_FILE_SHFS_OCFS |
-                                        VIR_STORAGE_FILE_SHFS_AFS);
-}
-
-int virStorageFileIsClusterFS(const char *path)
-{
-    /* These are coherent cluster filesystems known to be safe for
-     * migration with cache != none
-     */
-    return virStorageFileIsSharedFSType(path,
-                                        VIR_STORAGE_FILE_SHFS_GFS2 |
-                                        VIR_STORAGE_FILE_SHFS_OCFS);
-}
-
-#ifdef LVS
-int virStorageFileGetLVMKey(const char *path,
-                            char **key)
-{
-    /*
-     *  # lvs --noheadings --unbuffered --nosuffix --options "uuid" LVNAME
-     *    06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky
-     */
-    int status;
-    virCommandPtr cmd = virCommandNewArgList(
-        LVS,
-        "--noheadings", "--unbuffered", "--nosuffix",
-        "--options", "uuid", path,
-        NULL
-        );
-    int ret = -1;
-
-    *key = NULL;
-
-    /* Run the program and capture its output */
-    virCommandSetOutputBuffer(cmd, key);
-    if (virCommandRun(cmd, &status) < 0)
-        goto cleanup;
-
-    /* Explicitly check status == 0, rather than passing NULL
-     * to virCommandRun because we don't want to raise an actual
-     * error in this scenario, just return a NULL key.
-     */
-
-    if (status == 0 && *key) {
-        char *nl;
-        char *tmp = *key;
-
-        /* Find first non-space character */
-        while (*tmp && c_isspace(*tmp)) {
-            tmp++;
-        }
-        /* Kill leading spaces */
-        if (tmp != *key)
-            memmove(*key, tmp, strlen(tmp)+1);
-
-        /* Kill trailing newline */
-        if ((nl = strchr(*key, '\n')))
-            *nl = '\0';
-    }
-
-    ret = 0;
-
-cleanup:
-    if (*key && STREQ(*key, ""))
-        VIR_FREE(*key);
-
-    virCommandFree(cmd);
-
-    return ret;
-}
-#else
-int virStorageFileGetLVMKey(const char *path,
-                            char **key ATTRIBUTE_UNUSED)
-{
-    virReportSystemError(ENOSYS, _("Unable to get LVM key for %s"), path);
-    return -1;
-}
-#endif
-
-#ifdef HAVE_UDEV
-int virStorageFileGetSCSIKey(const char *path,
-                             char **key)
-{
-    int status;
-    virCommandPtr cmd = virCommandNewArgList(
-        "/lib/udev/scsi_id",
-        "--replace-whitespace",
-        "--whitelisted",
-        "--device", path,
-        NULL
-        );
-    int ret = -1;
-
-    *key = NULL;
-
-    /* Run the program and capture its output */
-    virCommandSetOutputBuffer(cmd, key);
-    if (virCommandRun(cmd, &status) < 0)
-        goto cleanup;
-
-    /* Explicitly check status == 0, rather than passing NULL
-     * to virCommandRun because we don't want to raise an actual
-     * error in this scenario, just return a NULL key.
-     */
-    if (status == 0 && *key) {
-        char *nl = strchr(*key, '\n');
-        if (nl)
-            *nl = '\0';
-    }
-
-    ret = 0;
-
-cleanup:
-    if (*key && STREQ(*key, ""))
-        VIR_FREE(*key);
-
-    virCommandFree(cmd);
-
-    return ret;
-}
-#else
-int virStorageFileGetSCSIKey(const char *path,
-                             char **key ATTRIBUTE_UNUSED)
-{
-    virReportSystemError(ENOSYS, _("Unable to get SCSI key for %s"), path);
-    return -1;
-}
-#endif
-
-/* Given a CHAIN that starts at the named file START, return a string
- * pointing to either START or within CHAIN that gives the preferred
- * name for the backing file NAME within that chain.  Pass NULL for
- * NAME to find the base of the chain.  If META is not NULL, set *META
- * to the point in the chain that describes NAME (or to NULL if the
- * backing element is not a file).  If PARENT is not NULL, set *PARENT
- * to the preferred name of the parent (or to NULL if NAME matches
- * START).  Since the results point within CHAIN, they must not be
- * independently freed.  */
-const char *
-virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start,
-                          const char *name, virStorageFileMetadataPtr *meta,
-                          const char **parent)
-{
-    virStorageFileMetadataPtr owner;
-    const char *tmp;
-
-    if (!parent)
-        parent = &tmp;
-
-    *parent = NULL;
-    if (name ? STREQ(start, name) || virFileLinkPointsTo(start, name) :
-        !chain->backingStore) {
-        if (meta)
-            *meta = chain;
-        return start;
-    }
-
-    owner = chain;
-    *parent = start;
-    while (owner) {
-        if (!owner->backingStore)
-            goto error;
-        if (!name) {
-            if (!owner->backingMeta ||
-                !owner->backingMeta->backingStore)
-                break;
-        } else if (STREQ_NULLABLE(name, owner->backingStoreRaw) ||
-                   STREQ(name, owner->backingStore)) {
-            break;
-        } else if (owner->backingStoreIsFile) {
-            char *absName = absolutePathFromBaseFile(*parent, name);
-            if (absName && STREQ(absName, owner->backingStore)) {
-                VIR_FREE(absName);
-                break;
-            }
-            VIR_FREE(absName);
-        }
-        *parent = owner->backingStore;
-        owner = owner->backingMeta;
-    }
-    if (!owner)
-        goto error;
-    if (meta)
-        *meta = owner->backingMeta;
-    return owner->backingStore;
-
-error:
-    *parent = NULL;
-    if (meta)
-        *meta = NULL;
-    return NULL;
-}
diff --git a/src/util/storage_file.h b/src/util/storage_file.h
deleted file mode 100644
index 6fbd275..0000000
--- a/src/util/storage_file.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * storage_file.c: file utility functions for FS storage backend
- *
- * Copyright (C) 2007-2009, 2012 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, see
- * <http://www.gnu.org/licenses/>.
- *
- * Author: Daniel P. Berrange <berrange redhat com>
- */
-
-#ifndef __VIR_STORAGE_FILE_H__
-# define __VIR_STORAGE_FILE_H__
-
-# include "util.h"
-
-enum virStorageFileFormat {
-    VIR_STORAGE_FILE_AUTO_SAFE = -2,
-    VIR_STORAGE_FILE_AUTO = -1,
-    VIR_STORAGE_FILE_NONE = 0,
-    VIR_STORAGE_FILE_RAW,
-    VIR_STORAGE_FILE_DIR,
-    VIR_STORAGE_FILE_BOCHS,
-    VIR_STORAGE_FILE_CLOOP,
-    VIR_STORAGE_FILE_COW,
-    VIR_STORAGE_FILE_DMG,
-    VIR_STORAGE_FILE_ISO,
-    VIR_STORAGE_FILE_QCOW,
-    VIR_STORAGE_FILE_QCOW2,
-    VIR_STORAGE_FILE_QED,
-    VIR_STORAGE_FILE_VMDK,
-    VIR_STORAGE_FILE_VPC,
-    VIR_STORAGE_FILE_FAT,
-    VIR_STORAGE_FILE_VHD,
-
-    VIR_STORAGE_FILE_LAST,
-};
-
-VIR_ENUM_DECL(virStorageFileFormat);
-
-typedef struct _virStorageFileMetadata virStorageFileMetadata;
-typedef virStorageFileMetadata *virStorageFileMetadataPtr;
-struct _virStorageFileMetadata {
-    char *backingStore; /* Canonical name (absolute file, or protocol) */
-    char *backingStoreRaw; /* If file, original name, possibly relative */
-    int backingStoreFormat; /* enum virStorageFileFormat */
-    bool backingStoreIsFile;
-    virStorageFileMetadataPtr backingMeta;
-    unsigned long long capacity;
-    bool encrypted;
-};
-
-# ifndef DEV_BSIZE
-#  define DEV_BSIZE 512
-# endif
-
-int virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid);
-int virStorageFileProbeFormatFromFD(const char *path,
-                                    int fd);
-
-virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path,
-                                                    int format,
-                                                    uid_t uid, gid_t gid,
-                                                    bool allow_probe);
-virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path,
-                                                          int fd,
-                                                          int format);
-
-const char *virStorageFileChainLookup(virStorageFileMetadataPtr chain,
-                                      const char *start,
-                                      const char *name,
-                                      virStorageFileMetadataPtr *meta,
-                                      const char **parent)
-    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
-
-void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta);
-
-int virStorageFileResize(const char *path, unsigned long long capacity);
-
-enum {
-    VIR_STORAGE_FILE_SHFS_NFS = (1 << 0),
-    VIR_STORAGE_FILE_SHFS_GFS2 = (1 << 1),
-    VIR_STORAGE_FILE_SHFS_OCFS = (1 << 2),
-    VIR_STORAGE_FILE_SHFS_AFS = (1 << 3),
-};
-
-int virStorageFileIsSharedFS(const char *path);
-int virStorageFileIsClusterFS(const char *path);
-int virStorageFileIsSharedFSType(const char *path,
-                                 int fstypes);
-
-int virStorageFileGetLVMKey(const char *path,
-                            char **key);
-int virStorageFileGetSCSIKey(const char *path,
-                             char **key);
-
-#endif /* __VIR_STORAGE_FILE_H__ */
diff --git a/src/util/util.c b/src/util/util.c
index c070d94..5d32995 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -79,7 +79,7 @@
 #include "virlog.h"
 #include "virbuffer.h"
 #include "util.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "viralloc.h"
 #include "threads.h"
 #include "verify.h"
diff --git a/src/util/virstoragefile.c b/src/util/virstoragefile.c
new file mode 100644
index 0000000..f183f19
--- /dev/null
+++ b/src/util/virstoragefile.c
@@ -0,0 +1,1397 @@
+/*
+ * storage_file.c: file utility functions for FS storage backend
+ *
+ * Copyright (C) 2007-2012 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#include <config.h>
+#include "virstoragefile.h"
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#ifdef __linux__
+# if HAVE_LINUX_MAGIC_H
+#  include <linux/magic.h>
+# endif
+# include <sys/statfs.h>
+#endif
+#include "dirname.h"
+#include "viralloc.h"
+#include "virterror_internal.h"
+#include "virlog.h"
+#include "virfile.h"
+#include "c-ctype.h"
+#include "vircommand.h"
+#include "virhash.h"
+
+#define VIR_FROM_THIS VIR_FROM_STORAGE
+
+VIR_ENUM_IMPL(virStorageFileFormat,
+              VIR_STORAGE_FILE_LAST,
+              "none",
+              "raw", "dir", "bochs",
+              "cloop", "cow", "dmg", "iso",
+              "qcow", "qcow2", "qed", "vmdk", "vpc",
+              "fat", "vhd")
+
+enum lv_endian {
+    LV_LITTLE_ENDIAN = 1, /* 1234 */
+    LV_BIG_ENDIAN         /* 4321 */
+};
+
+enum {
+    BACKING_STORE_OK,
+    BACKING_STORE_INVALID,
+    BACKING_STORE_ERROR,
+};
+
+/* Either 'magic' or 'extension' *must* be provided */
+struct FileTypeInfo {
+    const char *magic;  /* Optional string of file magic
+                         * to check at head of file */
+    const char *extension; /* Optional file extension to check */
+    enum lv_endian 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 */
+                          /* Store a COW base image path (possibly relative),
+                           * or NULL if there is no COW base image, to RES;
+                           * return BACKING_STORE_* */
+    int qcowCryptOffset;  /* Byte offset from start of file
+                           * where to find encryption mode,
+                           * -1 if encryption is not used */
+    int (*getBackingStore)(char **res, int *format,
+                           const unsigned char *buf, size_t buf_size);
+};
+
+static int cowGetBackingStore(char **, int *,
+                              const unsigned char *, size_t);
+static int qcow1GetBackingStore(char **, int *,
+                                const unsigned char *, size_t);
+static int qcow2GetBackingStore(char **, int *,
+                                const unsigned char *, size_t);
+static int vmdk4GetBackingStore(char **, int *,
+                                const unsigned char *, size_t);
+static int
+qedGetBackingStore(char **, int *, const unsigned char *, size_t);
+
+#define QCOWX_HDR_VERSION (4)
+#define QCOWX_HDR_BACKING_FILE_OFFSET (QCOWX_HDR_VERSION+4)
+#define QCOWX_HDR_BACKING_FILE_SIZE (QCOWX_HDR_BACKING_FILE_OFFSET+8)
+#define QCOWX_HDR_IMAGE_SIZE (QCOWX_HDR_BACKING_FILE_SIZE+4+4)
+
+#define QCOW1_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8+1+1)
+#define QCOW2_HDR_CRYPT (QCOWX_HDR_IMAGE_SIZE+8)
+
+#define QCOW1_HDR_TOTAL_SIZE (QCOW1_HDR_CRYPT+4+8)
+#define QCOW2_HDR_TOTAL_SIZE (QCOW2_HDR_CRYPT+4+4+8+8+4+4+8)
+
+#define QCOW2_HDR_EXTENSION_END 0
+#define QCOW2_HDR_EXTENSION_BACKING_FORMAT 0xE2792ACA
+
+#define QED_HDR_FEATURES_OFFSET (4+4+4+4)
+#define QED_HDR_IMAGE_SIZE (QED_HDR_FEATURES_OFFSET+8+8+8+8)
+#define QED_HDR_BACKING_FILE_OFFSET (QED_HDR_IMAGE_SIZE+8)
+#define QED_HDR_BACKING_FILE_SIZE (QED_HDR_BACKING_FILE_OFFSET+4)
+#define QED_F_BACKING_FILE 0x01
+#define QED_F_BACKING_FORMAT_NO_PROBE 0x04
+
+/* VMDK needs at least this to find backing store,
+ * other formats need less */
+#define STORAGE_MAX_HEAD (20*512)
+
+
+static struct FileTypeInfo const fileTypeInfo[] = {
+    [VIR_STORAGE_FILE_NONE] = { NULL, NULL, LV_LITTLE_ENDIAN,
+                                -1, 0, 0, 0, 0, 0, NULL },
+    [VIR_STORAGE_FILE_RAW] = { NULL, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, 0, 0, 0, 0, NULL },
+    [VIR_STORAGE_FILE_DIR] = { NULL, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, 0, 0, 0, 0, NULL },
+    [VIR_STORAGE_FILE_BOCHS] = {
+        /*"Bochs Virtual HD Image", */ /* Untested */ NULL,
+        NULL,
+        LV_LITTLE_ENDIAN, 64, 0x20000,
+        32+16+16+4+4+4+4+4, 8, 1, -1, NULL
+    },
+    [VIR_STORAGE_FILE_CLOOP] = {
+        /*"#!/bin/sh\n#V2.0 Format\nmodprobe cloop file=$0 && mount -r -t iso9660 /dev/cloop $1\n", */ /* Untested */ NULL,
+        NULL,
+        LV_LITTLE_ENDIAN, -1, 0,
+        -1, 0, 0, -1, NULL
+    },
+    [VIR_STORAGE_FILE_COW] = {
+        "OOOM", NULL,
+        LV_BIG_ENDIAN, 4, 2,
+        4+4+1024+4, 8, 1, -1, cowGetBackingStore
+    },
+    [VIR_STORAGE_FILE_DMG] = {
+        NULL, /* XXX QEMU says there's no magic for dmg, but we should check... */
+        ".dmg",
+        0, -1, 0,
+        -1, 0, 0, -1, NULL
+    },
+    [VIR_STORAGE_FILE_ISO] = {
+        NULL, /* XXX there's probably some magic for iso we can validate too... */
+        ".iso",
+        0, -1, 0,
+        -1, 0, 0, -1, NULL
+    },
+    [VIR_STORAGE_FILE_QCOW] = {
+        "QFI", NULL,
+        LV_BIG_ENDIAN, 4, 1,
+        QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW1_HDR_CRYPT, qcow1GetBackingStore,
+    },
+    [VIR_STORAGE_FILE_QCOW2] = {
+        "QFI", NULL,
+        LV_BIG_ENDIAN, 4, 2,
+        QCOWX_HDR_IMAGE_SIZE, 8, 1, QCOW2_HDR_CRYPT, qcow2GetBackingStore,
+    },
+    [VIR_STORAGE_FILE_QED] = {
+        /* http://wiki.qemu.org/Features/QED */
+        "QED", NULL,
+        LV_LITTLE_ENDIAN, -2, -1,
+        QED_HDR_IMAGE_SIZE, 8, 1, -1, qedGetBackingStore,
+    },
+    [VIR_STORAGE_FILE_VMDK] = {
+        "KDMV", NULL,
+        LV_LITTLE_ENDIAN, 4, 1,
+        4+4+4, 8, 512, -1, vmdk4GetBackingStore
+    },
+    [VIR_STORAGE_FILE_VPC] = {
+        "conectix", NULL,
+        LV_BIG_ENDIAN, 12, 0x10000,
+        8 + 4 + 4 + 8 + 4 + 4 + 2 + 2 + 4, 8, 1, -1, NULL
+    },
+    /* Not direct file formats, but used for various drivers */
+    [VIR_STORAGE_FILE_FAT] = { NULL, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, 0, 0, 0, 0, NULL },
+    [VIR_STORAGE_FILE_VHD] = { NULL, NULL, LV_LITTLE_ENDIAN,
+                               -1, 0, 0, 0, 0, 0, NULL },
+};
+verify(ARRAY_CARDINALITY(fileTypeInfo) == VIR_STORAGE_FILE_LAST);
+
+static int
+cowGetBackingStore(char **res,
+                   int *format,
+                   const unsigned char *buf,
+                   size_t buf_size)
+{
+#define COW_FILENAME_MAXLEN 1024
+    *res = NULL;
+    *format = VIR_STORAGE_FILE_AUTO;
+
+    if (buf_size < 4+4+ COW_FILENAME_MAXLEN)
+        return BACKING_STORE_INVALID;
+    if (buf[4+4] == '\0') { /* cow_header_v2.backing_file[0] */
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+
+    *res = strndup((const char*)buf + 4+4, COW_FILENAME_MAXLEN);
+    if (*res == NULL) {
+        virReportOOMError();
+        return BACKING_STORE_ERROR;
+    }
+    return BACKING_STORE_OK;
+}
+
+
+static int
+qcow2GetBackingStoreFormat(int *format,
+                           const unsigned char *buf,
+                           size_t buf_size,
+                           size_t extension_start,
+                           size_t extension_end)
+{
+    size_t offset = extension_start;
+
+    /*
+     * The extensions take format of
+     *
+     * int32: magic
+     * int32: length
+     * byte[length]: payload
+     *
+     * Unknown extensions can be ignored by skipping
+     * over "length" bytes in the data stream.
+     */
+    while (offset < (buf_size-8) &&
+           offset < (extension_end-8)) {
+        unsigned int magic =
+            (buf[offset] << 24) +
+            (buf[offset+1] << 16) +
+            (buf[offset+2] << 8) +
+            (buf[offset+3]);
+        unsigned int len =
+            (buf[offset+4] << 24) +
+            (buf[offset+5] << 16) +
+            (buf[offset+6] << 8) +
+            (buf[offset+7]);
+
+        offset += 8;
+
+        if ((offset + len) < offset)
+            break;
+
+        if ((offset + len) > buf_size)
+            break;
+
+        switch (magic) {
+        case QCOW2_HDR_EXTENSION_END:
+            goto done;
+
+        case QCOW2_HDR_EXTENSION_BACKING_FORMAT:
+            if (buf[offset+len] != '\0')
+                break;
+            *format = virStorageFileFormatTypeFromString(
+                ((const char *)buf)+offset);
+            if (*format <= VIR_STORAGE_FILE_NONE)
+                return -1;
+        }
+
+        offset += len;
+    }
+
+done:
+
+    return 0;
+}
+
+
+static int
+qcowXGetBackingStore(char **res,
+                     int *format,
+                     const unsigned char *buf,
+                     size_t buf_size,
+                     bool isQCow2)
+{
+    unsigned long long offset;
+    unsigned int size;
+
+    *res = NULL;
+    if (format)
+        *format = VIR_STORAGE_FILE_AUTO;
+
+    if (buf_size < QCOWX_HDR_BACKING_FILE_OFFSET+8+4)
+        return BACKING_STORE_INVALID;
+    offset = (((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET] << 56)
+              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+1] << 48)
+              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+2] << 40)
+              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+3] << 32)
+              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+4] << 24)
+              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+5] << 16)
+              | ((unsigned long long)buf[QCOWX_HDR_BACKING_FILE_OFFSET+6] << 8)
+              | buf[QCOWX_HDR_BACKING_FILE_OFFSET+7]); /* QCowHeader.backing_file_offset */
+    if (offset > buf_size)
+        return BACKING_STORE_INVALID;
+    size = ((buf[QCOWX_HDR_BACKING_FILE_SIZE] << 24)
+            | (buf[QCOWX_HDR_BACKING_FILE_SIZE+1] << 16)
+            | (buf[QCOWX_HDR_BACKING_FILE_SIZE+2] << 8)
+            | buf[QCOWX_HDR_BACKING_FILE_SIZE+3]); /* QCowHeader.backing_file_size */
+    if (size == 0) {
+        if (format)
+            *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+    if (offset + size > buf_size || offset + size < offset)
+        return BACKING_STORE_INVALID;
+    if (size + 1 == 0)
+        return BACKING_STORE_INVALID;
+    if (VIR_ALLOC_N(*res, size + 1) < 0) {
+        virReportOOMError();
+        return BACKING_STORE_ERROR;
+    }
+    memcpy(*res, buf + offset, size);
+    (*res)[size] = '\0';
+
+    /*
+     * Traditionally QCow2 files had a layout of
+     *
+     * [header]
+     * [backingStoreName]
+     *
+     * Although the backingStoreName typically followed
+     * the header immediately, this was not required by
+     * the format. By specifying a higher byte offset for
+     * the backing file offset in the header, it was
+     * possible to leave space between the header and
+     * start of backingStore.
+     *
+     * This hack is now used to store extensions to the
+     * qcow2 format:
+     *
+     * [header]
+     * [extensions]
+     * [backingStoreName]
+     *
+     * Thus the file region to search for extensions is
+     * between the end of the header (QCOW2_HDR_TOTAL_SIZE)
+     * and the start of the backingStoreName (offset)
+     */
+    if (isQCow2 && format &&
+        qcow2GetBackingStoreFormat(format, buf, buf_size, QCOW2_HDR_TOTAL_SIZE,
+                                   offset) < 0)
+        return BACKING_STORE_INVALID;
+
+    return BACKING_STORE_OK;
+}
+
+
+static int
+qcow1GetBackingStore(char **res,
+                     int *format,
+                     const unsigned char *buf,
+                     size_t buf_size)
+{
+    int ret;
+
+    /* QCow1 doesn't have the extensions capability
+     * used to store backing format */
+    *format = VIR_STORAGE_FILE_AUTO;
+    ret = qcowXGetBackingStore(res, NULL, buf, buf_size, false);
+    if (ret == 0 && *buf == '\0')
+        *format = VIR_STORAGE_FILE_NONE;
+    return ret;
+}
+
+static int
+qcow2GetBackingStore(char **res,
+                     int *format,
+                     const unsigned char *buf,
+                     size_t buf_size)
+{
+    return qcowXGetBackingStore(res, format, buf, buf_size, true);
+}
+
+
+static int
+vmdk4GetBackingStore(char **res,
+                     int *format,
+                     const unsigned char *buf,
+                     size_t buf_size)
+{
+    static const char prefix[] = "parentFileNameHint=\"";
+    char *desc, *start, *end;
+    size_t len;
+    int ret = BACKING_STORE_ERROR;
+
+    if (VIR_ALLOC_N(desc, STORAGE_MAX_HEAD + 1) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    *res = NULL;
+    /*
+     * Technically this should have been VMDK, since
+     * VMDK spec / VMWare impl only support VMDK backed
+     * by VMDK. QEMU isn't following this though and
+     * does probing on VMDK backing files, hence we set
+     * AUTO
+     */
+    *format = VIR_STORAGE_FILE_AUTO;
+
+    if (buf_size <= 0x200) {
+        ret = BACKING_STORE_INVALID;
+        goto cleanup;
+    }
+    len = buf_size - 0x200;
+    if (len > STORAGE_MAX_HEAD)
+        len = STORAGE_MAX_HEAD;
+    memcpy(desc, buf + 0x200, len);
+    desc[len] = '\0';
+    start = strstr(desc, prefix);
+    if (start == NULL) {
+        *format = VIR_STORAGE_FILE_NONE;
+        ret = BACKING_STORE_OK;
+        goto cleanup;
+    }
+    start += strlen(prefix);
+    end = strchr(start, '"');
+    if (end == NULL) {
+        ret = BACKING_STORE_INVALID;
+        goto cleanup;
+    }
+    if (end == start) {
+        *format = VIR_STORAGE_FILE_NONE;
+        ret = BACKING_STORE_OK;
+        goto cleanup;
+    }
+    *end = '\0';
+    *res = strdup(start);
+    if (*res == NULL) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    ret = BACKING_STORE_OK;
+
+cleanup:
+    VIR_FREE(desc);
+    return ret;
+}
+
+static unsigned long
+qedGetHeaderUL(const unsigned char *loc)
+{
+    return (((unsigned long)loc[3] << 24) |
+            ((unsigned long)loc[2] << 16) |
+            ((unsigned long)loc[1] << 8) |
+            ((unsigned long)loc[0] << 0));
+}
+
+static unsigned long long
+qedGetHeaderULL(const unsigned char *loc)
+{
+    return (((unsigned long long)loc[7] << 56) |
+            ((unsigned long long)loc[6] << 48) |
+            ((unsigned long long)loc[5] << 40) |
+            ((unsigned long long)loc[4] << 32) |
+            ((unsigned long long)loc[3] << 24) |
+            ((unsigned long long)loc[2] << 16) |
+            ((unsigned long long)loc[1] << 8) |
+            ((unsigned long long)loc[0] << 0));
+}
+
+static int
+qedGetBackingStore(char **res,
+                   int *format,
+                   const unsigned char *buf,
+                   size_t buf_size)
+{
+    unsigned long long flags;
+    unsigned long offset, size;
+
+    *res = NULL;
+    /* Check if this image has a backing file */
+    if (buf_size < QED_HDR_FEATURES_OFFSET+8)
+        return BACKING_STORE_INVALID;
+    flags = qedGetHeaderULL(buf + QED_HDR_FEATURES_OFFSET);
+    if (!(flags & QED_F_BACKING_FILE)) {
+        *format = VIR_STORAGE_FILE_NONE;
+        return BACKING_STORE_OK;
+    }
+
+    /* Parse the backing file */
+    if (buf_size < QED_HDR_BACKING_FILE_OFFSET+8)
+        return BACKING_STORE_INVALID;
+    offset = qedGetHeaderUL(buf + QED_HDR_BACKING_FILE_OFFSET);
+    if (offset > buf_size)
+        return BACKING_STORE_INVALID;
+    size = qedGetHeaderUL(buf + QED_HDR_BACKING_FILE_SIZE);
+    if (size == 0)
+        return BACKING_STORE_OK;
+    if (offset + size > buf_size || offset + size < offset)
+        return BACKING_STORE_INVALID;
+    if (VIR_ALLOC_N(*res, size + 1) < 0) {
+        virReportOOMError();
+        return BACKING_STORE_ERROR;
+    }
+    memcpy(*res, buf + offset, size);
+    (*res)[size] = '\0';
+
+    if (flags & QED_F_BACKING_FORMAT_NO_PROBE)
+        *format = VIR_STORAGE_FILE_RAW;
+    else
+        *format = VIR_STORAGE_FILE_AUTO_SAFE;
+
+    return BACKING_STORE_OK;
+}
+
+/**
+ * Return an absolute path corresponding to PATH, which is absolute or relative
+ * to the directory containing BASE_FILE, or NULL on error
+ */
+static char *
+absolutePathFromBaseFile(const char *base_file, const char *path)
+{
+    char *res = NULL;
+    char *tmp = NULL;
+    size_t d_len = dir_len(base_file);
+
+    /* If path is already absolute, or if dirname(base_file) is ".",
+       just return a copy of path.  */
+    if (*path == '/' || d_len == 0) {
+        if (!(res = canonicalize_file_name(path)))
+            virReportSystemError(errno,
+                                 _("Can't canonicalize path '%s'"), path);
+
+        goto cleanup;
+    }
+
+    /* Ensure that the following cast-to-int is valid.  */
+    if (d_len > INT_MAX) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("Directory name too long: '%s'"), base_file);
+        goto cleanup;
+    }
+
+    if (virAsprintf(&tmp, "%.*s/%s", (int) d_len, base_file, path) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if (!(res = canonicalize_file_name(tmp)))
+        virReportSystemError(errno, _("Can't canonicalize path '%s'"), path);
+
+cleanup:
+    VIR_FREE(tmp);
+    return res;
+}
+
+
+static bool
+virStorageFileMatchesMagic(int format,
+                           unsigned char *buf,
+                           size_t buflen)
+{
+    int mlen;
+
+    if (fileTypeInfo[format].magic == NULL)
+        return false;
+
+    /* Validate magic data */
+    mlen = strlen(fileTypeInfo[format].magic);
+    if (mlen > buflen)
+        return false;
+
+    if (memcmp(buf, fileTypeInfo[format].magic, mlen) != 0)
+        return false;
+
+    return true;
+}
+
+
+static bool
+virStorageFileMatchesExtension(int format,
+                               const char *path)
+{
+    if (fileTypeInfo[format].extension == NULL)
+        return false;
+
+    if (virFileHasSuffix(path, fileTypeInfo[format].extension))
+        return true;
+
+    return false;
+}
+
+
+static bool
+virStorageFileMatchesVersion(int format,
+                             unsigned char *buf,
+                             size_t buflen)
+{
+    int version;
+
+    /* Validate version number info */
+    if (fileTypeInfo[format].versionOffset == -1)
+        return false;
+
+    /* -2 == non-versioned file format, so trivially match */
+    if (fileTypeInfo[format].versionOffset == -2)
+        return true;
+
+    if ((fileTypeInfo[format].versionOffset + 4) > buflen)
+        return false;
+
+    if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) {
+        version =
+            (buf[fileTypeInfo[format].versionOffset+3] << 24) |
+            (buf[fileTypeInfo[format].versionOffset+2] << 16) |
+            (buf[fileTypeInfo[format].versionOffset+1] << 8) |
+            (buf[fileTypeInfo[format].versionOffset]);
+    } else {
+        version =
+            (buf[fileTypeInfo[format].versionOffset] << 24) |
+            (buf[fileTypeInfo[format].versionOffset+1] << 16) |
+            (buf[fileTypeInfo[format].versionOffset+2] << 8) |
+            (buf[fileTypeInfo[format].versionOffset+3]);
+    }
+
+    VIR_DEBUG("Compare detected version %d vs expected version %d",
+              version, fileTypeInfo[format].versionNumber);
+    if (version != fileTypeInfo[format].versionNumber)
+        return false;
+
+    return true;
+}
+
+static bool
+virBackingStoreIsFile(const char *backing)
+{
+    /* Backing store is a network block device or Rados block device */
+    if (STRPREFIX(backing, "nbd:") || STRPREFIX(backing, "rbd:"))
+        return false;
+    return true;
+}
+
+static int
+virStorageFileGetMetadataFromBuf(int format,
+                                 const char *path,
+                                 unsigned char *buf,
+                                 size_t buflen,
+                                 virStorageFileMetadata *meta)
+{
+    VIR_DEBUG("path=%s format=%d", path, format);
+
+    /* XXX we should consider moving virStorageBackendUpdateVolInfo
+     * code into this method, for non-magic files
+     */
+    if (format <= VIR_STORAGE_FILE_NONE ||
+        format >= VIR_STORAGE_FILE_LAST ||
+        !fileTypeInfo[format].magic) {
+        return 0;
+    }
+
+    /* Optionally extract capacity from file */
+    if (fileTypeInfo[format].sizeOffset != -1) {
+        if ((fileTypeInfo[format].sizeOffset + 8) > buflen)
+            return 1;
+
+        if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) {
+            meta->capacity =
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+7] << 56) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+6] << 48) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+5] << 40) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+4] << 32) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+3] << 24) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+2] << 16) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+1] << 8) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset]);
+        } else {
+            meta->capacity =
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset] << 56) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+1] << 48) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+2] << 40) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+3] << 32) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+4] << 24) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+5] << 16) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+6] << 8) |
+                ((unsigned long long)buf[fileTypeInfo[format].sizeOffset+7]);
+        }
+        /* Avoid unlikely, but theoretically possible overflow */
+        if (meta->capacity > (ULLONG_MAX / fileTypeInfo[format].sizeMultiplier))
+            return 1;
+        meta->capacity *= fileTypeInfo[format].sizeMultiplier;
+    }
+
+    if (fileTypeInfo[format].qcowCryptOffset != -1) {
+        int crypt_format;
+
+        crypt_format =
+            (buf[fileTypeInfo[format].qcowCryptOffset] << 24) |
+            (buf[fileTypeInfo[format].qcowCryptOffset+1] << 16) |
+            (buf[fileTypeInfo[format].qcowCryptOffset+2] << 8) |
+            (buf[fileTypeInfo[format].qcowCryptOffset+3]);
+        meta->encrypted = crypt_format != 0;
+    }
+
+    if (fileTypeInfo[format].getBackingStore != NULL) {
+        char *backing;
+        int backingFormat;
+        int ret = fileTypeInfo[format].getBackingStore(&backing,
+                                                       &backingFormat,
+                                                       buf, buflen);
+        if (ret == BACKING_STORE_INVALID)
+            return 1;
+
+        if (ret == BACKING_STORE_ERROR)
+            return -1;
+
+        meta->backingStoreIsFile = false;
+        if (backing != NULL) {
+            meta->backingStore = strdup(backing);
+            if (meta->backingStore == NULL) {
+                virReportOOMError();
+                VIR_FREE(backing);
+                return -1;
+            }
+            if (virBackingStoreIsFile(backing)) {
+                meta->backingStoreIsFile = true;
+                meta->backingStoreRaw = meta->backingStore;
+                meta->backingStore = absolutePathFromBaseFile(path, backing);
+                if (meta->backingStore == NULL) {
+                    /* the backing file is (currently) unavailable, treat this
+                     * file as standalone:
+                     * backingStoreRaw is kept to mark broken image chains */
+                    meta->backingStoreIsFile = false;
+                    backingFormat = VIR_STORAGE_FILE_NONE;
+                    VIR_WARN("Backing file '%s' of image '%s' is missing.",
+                             meta->backingStoreRaw, path);
+
+                }
+            }
+            VIR_FREE(backing);
+            meta->backingStoreFormat = backingFormat;
+        } else {
+            meta->backingStore = NULL;
+            meta->backingStoreFormat = VIR_STORAGE_FILE_NONE;
+        }
+    }
+
+    return 0;
+}
+
+
+static int
+virStorageFileProbeFormatFromBuf(const char *path,
+                                 unsigned char *buf,
+                                 size_t buflen)
+{
+    int format = VIR_STORAGE_FILE_RAW;
+    int i;
+    int possibleFormat = VIR_STORAGE_FILE_RAW;
+    VIR_DEBUG("path=%s", path);
+
+    /* First check file magic */
+    for (i = 0 ; i < VIR_STORAGE_FILE_LAST ; i++) {
+        if (virStorageFileMatchesMagic(i, buf, buflen)) {
+            if (!virStorageFileMatchesVersion(i, buf, buflen)) {
+                possibleFormat = i;
+                continue;
+            }
+            format = i;
+            goto cleanup;
+        }
+    }
+
+    if (possibleFormat != VIR_STORAGE_FILE_RAW)
+        VIR_WARN("File %s matches %s magic, but version is wrong. "
+                 "Please report new version to libvir-list redhat com",
+                 path, virStorageFileFormatTypeToString(possibleFormat));
+
+    /* No magic, so check file extension */
+    for (i = 0 ; i < VIR_STORAGE_FILE_LAST ; i++) {
+        if (virStorageFileMatchesExtension(i, path)) {
+            format = i;
+            goto cleanup;
+        }
+    }
+
+cleanup:
+    VIR_DEBUG("format=%d", format);
+    return format;
+}
+
+
+/**
+ * virStorageFileProbeFormatFromFD:
+ *
+ * Probe for the format of 'fd' (which is an open file descriptor
+ * pointing to 'path'), returning the detected disk format.
+ *
+ * Callers are advised never to trust the returned 'format'
+ * unless it is listed as VIR_STORAGE_FILE_RAW, since a
+ * malicious guest can turn a file into any other non-raw
+ * format at will.
+ *
+ * Best option: Don't use this function
+ */
+int
+virStorageFileProbeFormatFromFD(const char *path, int fd)
+{
+    unsigned char *head;
+    ssize_t len = STORAGE_MAX_HEAD;
+    int ret = -1;
+    struct stat sb;
+
+    if (fstat(fd, &sb) < 0) {
+        virReportSystemError(errno,
+                             _("cannot stat file '%s'"),
+                             path);
+        return -1;
+    }
+
+    /* No header to probe for directories */
+    if (S_ISDIR(sb.st_mode)) {
+        return VIR_STORAGE_FILE_DIR;
+    }
+
+    if (VIR_ALLOC_N(head, len) < 0) {
+        virReportOOMError();
+        return -1;
+    }
+
+    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+        virReportSystemError(errno, _("cannot set to start of '%s'"), path);
+        goto cleanup;
+    }
+
+    if ((len = read(fd, head, len)) < 0) {
+        virReportSystemError(errno, _("cannot read header '%s'"), path);
+        goto cleanup;
+    }
+
+    ret = virStorageFileProbeFormatFromBuf(path, head, len);
+
+cleanup:
+    VIR_FREE(head);
+    return ret;
+}
+
+
+/**
+ * virStorageFileProbeFormat:
+ *
+ * Probe for the format of 'path', returning the detected
+ * disk format.
+ *
+ * Callers are advised never to trust the returned 'format'
+ * unless it is listed as VIR_STORAGE_FILE_RAW, since a
+ * malicious guest can turn a raw file into any other non-raw
+ * format at will.
+ *
+ * Best option: Don't use this function
+ */
+int
+virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid)
+{
+    int fd, ret;
+
+    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
+        virReportSystemError(errno, _("cannot open file '%s'"), path);
+        return -1;
+    }
+
+    ret = virStorageFileProbeFormatFromFD(path, fd);
+
+    VIR_FORCE_CLOSE(fd);
+
+    return ret;
+}
+
+/**
+ * virStorageFileGetMetadataFromFD:
+ *
+ * Extract metadata about the storage volume with the specified
+ * image format. If image format is VIR_STORAGE_FILE_AUTO, it
+ * will probe to automatically identify the format.  Does not recurse.
+ *
+ * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
+ * format, since a malicious guest can turn a raw file into any
+ * other non-raw format at will.
+ *
+ * If the returned meta.backingStoreFormat is VIR_STORAGE_FILE_AUTO
+ * it indicates the image didn't specify an explicit format for its
+ * backing store. Callers are advised against probing for the
+ * backing store format in this case.
+ *
+ * Caller MUST free the result after use via virStorageFileFreeMetadata.
+ */
+virStorageFileMetadataPtr
+virStorageFileGetMetadataFromFD(const char *path,
+                                int fd,
+                                int format)
+{
+    virStorageFileMetadata *meta = NULL;
+    unsigned char *head = NULL;
+    ssize_t len = STORAGE_MAX_HEAD;
+    virStorageFileMetadata *ret = NULL;
+    struct stat sb;
+
+    if (VIR_ALLOC(meta) < 0) {
+        virReportOOMError();
+        return NULL;
+    }
+
+    if (fstat(fd, &sb) < 0) {
+        virReportSystemError(errno,
+                             _("cannot stat file '%s'"),
+                             path);
+        goto cleanup;
+    }
+
+    /* No header to probe for directories, but also no backing file */
+    if (S_ISDIR(sb.st_mode))
+        return meta;
+
+    if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
+        virReportSystemError(errno, _("cannot seek to start of '%s'"), path);
+        goto cleanup;
+    }
+
+    if (VIR_ALLOC_N(head, len) < 0) {
+        virReportOOMError();
+        goto cleanup;
+    }
+
+    if ((len = read(fd, head, len)) < 0) {
+        virReportSystemError(errno, _("cannot read header '%s'"), path);
+        goto cleanup;
+    }
+
+    if (format == VIR_STORAGE_FILE_AUTO)
+        format = virStorageFileProbeFormatFromBuf(path, head, len);
+
+    if (format <= VIR_STORAGE_FILE_NONE ||
+        format >= VIR_STORAGE_FILE_LAST) {
+        virReportSystemError(EINVAL, _("unknown storage file format %d"),
+                             format);
+        goto cleanup;
+    }
+
+    if (virStorageFileGetMetadataFromBuf(format, path, head, len, meta) < 0)
+        goto cleanup;
+    ret = meta;
+    meta = NULL;
+
+cleanup:
+    virStorageFileFreeMetadata(meta);
+    VIR_FREE(head);
+    return ret;
+}
+
+/* Recursive workhorse for virStorageFileGetMetadata.  */
+static virStorageFileMetadataPtr
+virStorageFileGetMetadataRecurse(const char *path, int format,
+                                 uid_t uid, gid_t gid,
+                                 bool allow_probe, virHashTablePtr cycle)
+{
+    int fd;
+    VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d",
+              path, format, (int)uid, (int)gid, allow_probe);
+
+    virStorageFileMetadataPtr ret = NULL;
+
+    if (virHashLookup(cycle, path)) {
+        virReportError(VIR_ERR_INTERNAL_ERROR,
+                       _("backing store for %s is self-referential"),
+                       path);
+        return NULL;
+    }
+    if (virHashAddEntry(cycle, path, (void *)1) < 0)
+        return NULL;
+
+    if ((fd = virFileOpenAs(path, O_RDONLY, 0, uid, gid, 0)) < 0) {
+        virReportSystemError(-fd, _("cannot open file '%s'"), path);
+        return NULL;
+    }
+
+    ret = virStorageFileGetMetadataFromFD(path, fd, format);
+
+    if (VIR_CLOSE(fd) < 0)
+        VIR_WARN("could not close file %s", path);
+
+    if (ret && ret->backingStoreIsFile) {
+        if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO && !allow_probe)
+            ret->backingStoreFormat = VIR_STORAGE_FILE_RAW;
+        else if (ret->backingStoreFormat == VIR_STORAGE_FILE_AUTO_SAFE)
+            ret->backingStoreFormat = VIR_STORAGE_FILE_AUTO;
+        format = ret->backingStoreFormat;
+        ret->backingMeta = virStorageFileGetMetadataRecurse(ret->backingStore,
+                                                            format,
+                                                            uid, gid,
+                                                            allow_probe,
+                                                            cycle);
+    }
+
+    return ret;
+}
+
+/**
+ * virStorageFileGetMetadata:
+ *
+ * Extract metadata about the storage volume with the specified
+ * image format. If image format is VIR_STORAGE_FILE_AUTO, it
+ * will probe to automatically identify the format.  Recurses through
+ * the entire chain.
+ *
+ * Open files using UID and GID (or pass -1 for the current user/group).
+ * Treat any backing files without explicit type as raw, unless ALLOW_PROBE.
+ *
+ * Callers are advised never to use VIR_STORAGE_FILE_AUTO as a
+ * format, since a malicious guest can turn a raw file into any
+ * other non-raw format at will.
+ *
+ * If the returned meta.backingStoreFormat is VIR_STORAGE_FILE_AUTO
+ * it indicates the image didn't specify an explicit format for its
+ * backing store. Callers are advised against using ALLOW_PROBE, as
+ * it would probe the backing store format in this case.
+ *
+ * Caller MUST free result after use via virStorageFileFreeMetadata.
+ */
+virStorageFileMetadataPtr
+virStorageFileGetMetadata(const char *path, int format,
+                          uid_t uid, gid_t gid,
+                          bool allow_probe)
+{
+    VIR_DEBUG("path=%s format=%d uid=%d gid=%d probe=%d",
+              path, format, (int)uid, (int)gid, allow_probe);
+
+    virHashTablePtr cycle = virHashCreate(5, NULL);
+    virStorageFileMetadataPtr ret;
+
+    if (!cycle)
+        return NULL;
+
+    if (format <= VIR_STORAGE_FILE_NONE)
+        format = allow_probe ? VIR_STORAGE_FILE_AUTO : VIR_STORAGE_FILE_RAW;
+    ret = virStorageFileGetMetadataRecurse(path, format, uid, gid,
+                                           allow_probe, cycle);
+    virHashFree(cycle);
+    return ret;
+}
+
+/**
+ * virStorageFileFreeMetadata:
+ *
+ * Free pointers in passed structure and structure itself.
+ */
+void
+virStorageFileFreeMetadata(virStorageFileMetadata *meta)
+{
+    if (!meta)
+        return;
+
+    virStorageFileFreeMetadata(meta->backingMeta);
+    VIR_FREE(meta->backingStore);
+    VIR_FREE(meta->backingStoreRaw);
+    VIR_FREE(meta);
+}
+
+/**
+ * virStorageFileResize:
+ *
+ * Change the capacity of the raw storage file at 'path'.
+ */
+int
+virStorageFileResize(const char *path, unsigned long long capacity)
+{
+    int fd = -1;
+    int ret = -1;
+
+    if ((fd = open(path, O_RDWR)) < 0) {
+        virReportSystemError(errno, _("Unable to open '%s'"), path);
+        goto cleanup;
+    }
+
+    if (ftruncate(fd, capacity) < 0) {
+        virReportSystemError(errno, _("Failed to truncate file '%s'"), path);
+        goto cleanup;
+    }
+
+    if (VIR_CLOSE(fd) < 0) {
+        virReportSystemError(errno, _("Unable to save '%s'"), path);
+        goto cleanup;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FORCE_CLOSE(fd);
+    return ret;
+}
+
+#ifdef __linux__
+
+# ifndef NFS_SUPER_MAGIC
+#  define NFS_SUPER_MAGIC 0x6969
+# endif
+# ifndef OCFS2_SUPER_MAGIC
+#  define OCFS2_SUPER_MAGIC 0x7461636f
+# endif
+# ifndef GFS2_MAGIC
+#  define GFS2_MAGIC 0x01161970
+# endif
+# ifndef AFS_FS_MAGIC
+#  define AFS_FS_MAGIC 0x6B414653
+# endif
+
+
+int virStorageFileIsSharedFSType(const char *path,
+                                 int fstypes)
+{
+    char *dirpath, *p;
+    struct statfs sb;
+    int statfs_ret;
+
+    if ((dirpath = strdup(path)) == NULL) {
+        virReportOOMError();
+        return -1;
+    }
+
+    do {
+
+        /* Try less and less of the path until we get to a
+         * directory we can stat. Even if we don't have 'x'
+         * permission on any directory in the path on the NFS
+         * server (assuming it's NFS), we will be able to stat the
+         * mount point, and that will properly tell us if the
+         * fstype is NFS.
+         */
+
+        if ((p = strrchr(dirpath, '/')) == NULL) {
+            virReportSystemError(EINVAL,
+                         _("Invalid relative path '%s'"), path);
+            VIR_FREE(dirpath);
+            return -1;
+        }
+
+        if (p == dirpath)
+            *(p+1) = '\0';
+        else
+            *p = '\0';
+
+        statfs_ret = statfs(dirpath, &sb);
+
+    } while ((statfs_ret < 0) && (p != dirpath));
+
+    VIR_FREE(dirpath);
+
+    if (statfs_ret < 0) {
+        virReportSystemError(errno,
+                             _("cannot determine filesystem for '%s'"),
+                             path);
+        return -1;
+    }
+
+    VIR_DEBUG("Check if path %s with FS magic %lld is shared",
+              path, (long long int)sb.f_type);
+
+    if ((fstypes & VIR_STORAGE_FILE_SHFS_NFS) &&
+        (sb.f_type == NFS_SUPER_MAGIC))
+        return 1;
+
+    if ((fstypes & VIR_STORAGE_FILE_SHFS_GFS2) &&
+        (sb.f_type == GFS2_MAGIC))
+        return 1;
+    if ((fstypes & VIR_STORAGE_FILE_SHFS_OCFS) &&
+        (sb.f_type == OCFS2_SUPER_MAGIC))
+        return 1;
+    if ((fstypes & VIR_STORAGE_FILE_SHFS_AFS) &&
+        (sb.f_type == AFS_FS_MAGIC))
+        return 1;
+
+    return 0;
+}
+#else
+int virStorageFileIsSharedFSType(const char *path ATTRIBUTE_UNUSED,
+                                 int fstypes ATTRIBUTE_UNUSED)
+{
+    /* XXX implement me :-) */
+    return 0;
+}
+#endif
+
+int virStorageFileIsSharedFS(const char *path)
+{
+    return virStorageFileIsSharedFSType(path,
+                                        VIR_STORAGE_FILE_SHFS_NFS |
+                                        VIR_STORAGE_FILE_SHFS_GFS2 |
+                                        VIR_STORAGE_FILE_SHFS_OCFS |
+                                        VIR_STORAGE_FILE_SHFS_AFS);
+}
+
+int virStorageFileIsClusterFS(const char *path)
+{
+    /* These are coherent cluster filesystems known to be safe for
+     * migration with cache != none
+     */
+    return virStorageFileIsSharedFSType(path,
+                                        VIR_STORAGE_FILE_SHFS_GFS2 |
+                                        VIR_STORAGE_FILE_SHFS_OCFS);
+}
+
+#ifdef LVS
+int virStorageFileGetLVMKey(const char *path,
+                            char **key)
+{
+    /*
+     *  # lvs --noheadings --unbuffered --nosuffix --options "uuid" LVNAME
+     *    06UgP5-2rhb-w3Bo-3mdR-WeoL-pytO-SAa2ky
+     */
+    int status;
+    virCommandPtr cmd = virCommandNewArgList(
+        LVS,
+        "--noheadings", "--unbuffered", "--nosuffix",
+        "--options", "uuid", path,
+        NULL
+        );
+    int ret = -1;
+
+    *key = NULL;
+
+    /* Run the program and capture its output */
+    virCommandSetOutputBuffer(cmd, key);
+    if (virCommandRun(cmd, &status) < 0)
+        goto cleanup;
+
+    /* Explicitly check status == 0, rather than passing NULL
+     * to virCommandRun because we don't want to raise an actual
+     * error in this scenario, just return a NULL key.
+     */
+
+    if (status == 0 && *key) {
+        char *nl;
+        char *tmp = *key;
+
+        /* Find first non-space character */
+        while (*tmp && c_isspace(*tmp)) {
+            tmp++;
+        }
+        /* Kill leading spaces */
+        if (tmp != *key)
+            memmove(*key, tmp, strlen(tmp)+1);
+
+        /* Kill trailing newline */
+        if ((nl = strchr(*key, '\n')))
+            *nl = '\0';
+    }
+
+    ret = 0;
+
+cleanup:
+    if (*key && STREQ(*key, ""))
+        VIR_FREE(*key);
+
+    virCommandFree(cmd);
+
+    return ret;
+}
+#else
+int virStorageFileGetLVMKey(const char *path,
+                            char **key ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(ENOSYS, _("Unable to get LVM key for %s"), path);
+    return -1;
+}
+#endif
+
+#ifdef HAVE_UDEV
+int virStorageFileGetSCSIKey(const char *path,
+                             char **key)
+{
+    int status;
+    virCommandPtr cmd = virCommandNewArgList(
+        "/lib/udev/scsi_id",
+        "--replace-whitespace",
+        "--whitelisted",
+        "--device", path,
+        NULL
+        );
+    int ret = -1;
+
+    *key = NULL;
+
+    /* Run the program and capture its output */
+    virCommandSetOutputBuffer(cmd, key);
+    if (virCommandRun(cmd, &status) < 0)
+        goto cleanup;
+
+    /* Explicitly check status == 0, rather than passing NULL
+     * to virCommandRun because we don't want to raise an actual
+     * error in this scenario, just return a NULL key.
+     */
+    if (status == 0 && *key) {
+        char *nl = strchr(*key, '\n');
+        if (nl)
+            *nl = '\0';
+    }
+
+    ret = 0;
+
+cleanup:
+    if (*key && STREQ(*key, ""))
+        VIR_FREE(*key);
+
+    virCommandFree(cmd);
+
+    return ret;
+}
+#else
+int virStorageFileGetSCSIKey(const char *path,
+                             char **key ATTRIBUTE_UNUSED)
+{
+    virReportSystemError(ENOSYS, _("Unable to get SCSI key for %s"), path);
+    return -1;
+}
+#endif
+
+/* Given a CHAIN that starts at the named file START, return a string
+ * pointing to either START or within CHAIN that gives the preferred
+ * name for the backing file NAME within that chain.  Pass NULL for
+ * NAME to find the base of the chain.  If META is not NULL, set *META
+ * to the point in the chain that describes NAME (or to NULL if the
+ * backing element is not a file).  If PARENT is not NULL, set *PARENT
+ * to the preferred name of the parent (or to NULL if NAME matches
+ * START).  Since the results point within CHAIN, they must not be
+ * independently freed.  */
+const char *
+virStorageFileChainLookup(virStorageFileMetadataPtr chain, const char *start,
+                          const char *name, virStorageFileMetadataPtr *meta,
+                          const char **parent)
+{
+    virStorageFileMetadataPtr owner;
+    const char *tmp;
+
+    if (!parent)
+        parent = &tmp;
+
+    *parent = NULL;
+    if (name ? STREQ(start, name) || virFileLinkPointsTo(start, name) :
+        !chain->backingStore) {
+        if (meta)
+            *meta = chain;
+        return start;
+    }
+
+    owner = chain;
+    *parent = start;
+    while (owner) {
+        if (!owner->backingStore)
+            goto error;
+        if (!name) {
+            if (!owner->backingMeta ||
+                !owner->backingMeta->backingStore)
+                break;
+        } else if (STREQ_NULLABLE(name, owner->backingStoreRaw) ||
+                   STREQ(name, owner->backingStore)) {
+            break;
+        } else if (owner->backingStoreIsFile) {
+            char *absName = absolutePathFromBaseFile(*parent, name);
+            if (absName && STREQ(absName, owner->backingStore)) {
+                VIR_FREE(absName);
+                break;
+            }
+            VIR_FREE(absName);
+        }
+        *parent = owner->backingStore;
+        owner = owner->backingMeta;
+    }
+    if (!owner)
+        goto error;
+    if (meta)
+        *meta = owner->backingMeta;
+    return owner->backingStore;
+
+error:
+    *parent = NULL;
+    if (meta)
+        *meta = NULL;
+    return NULL;
+}
diff --git a/src/util/virstoragefile.h b/src/util/virstoragefile.h
new file mode 100644
index 0000000..6fbd275
--- /dev/null
+++ b/src/util/virstoragefile.h
@@ -0,0 +1,109 @@
+/*
+ * storage_file.c: file utility functions for FS storage backend
+ *
+ * Copyright (C) 2007-2009, 2012 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, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Author: Daniel P. Berrange <berrange redhat com>
+ */
+
+#ifndef __VIR_STORAGE_FILE_H__
+# define __VIR_STORAGE_FILE_H__
+
+# include "util.h"
+
+enum virStorageFileFormat {
+    VIR_STORAGE_FILE_AUTO_SAFE = -2,
+    VIR_STORAGE_FILE_AUTO = -1,
+    VIR_STORAGE_FILE_NONE = 0,
+    VIR_STORAGE_FILE_RAW,
+    VIR_STORAGE_FILE_DIR,
+    VIR_STORAGE_FILE_BOCHS,
+    VIR_STORAGE_FILE_CLOOP,
+    VIR_STORAGE_FILE_COW,
+    VIR_STORAGE_FILE_DMG,
+    VIR_STORAGE_FILE_ISO,
+    VIR_STORAGE_FILE_QCOW,
+    VIR_STORAGE_FILE_QCOW2,
+    VIR_STORAGE_FILE_QED,
+    VIR_STORAGE_FILE_VMDK,
+    VIR_STORAGE_FILE_VPC,
+    VIR_STORAGE_FILE_FAT,
+    VIR_STORAGE_FILE_VHD,
+
+    VIR_STORAGE_FILE_LAST,
+};
+
+VIR_ENUM_DECL(virStorageFileFormat);
+
+typedef struct _virStorageFileMetadata virStorageFileMetadata;
+typedef virStorageFileMetadata *virStorageFileMetadataPtr;
+struct _virStorageFileMetadata {
+    char *backingStore; /* Canonical name (absolute file, or protocol) */
+    char *backingStoreRaw; /* If file, original name, possibly relative */
+    int backingStoreFormat; /* enum virStorageFileFormat */
+    bool backingStoreIsFile;
+    virStorageFileMetadataPtr backingMeta;
+    unsigned long long capacity;
+    bool encrypted;
+};
+
+# ifndef DEV_BSIZE
+#  define DEV_BSIZE 512
+# endif
+
+int virStorageFileProbeFormat(const char *path, uid_t uid, gid_t gid);
+int virStorageFileProbeFormatFromFD(const char *path,
+                                    int fd);
+
+virStorageFileMetadataPtr virStorageFileGetMetadata(const char *path,
+                                                    int format,
+                                                    uid_t uid, gid_t gid,
+                                                    bool allow_probe);
+virStorageFileMetadataPtr virStorageFileGetMetadataFromFD(const char *path,
+                                                          int fd,
+                                                          int format);
+
+const char *virStorageFileChainLookup(virStorageFileMetadataPtr chain,
+                                      const char *start,
+                                      const char *name,
+                                      virStorageFileMetadataPtr *meta,
+                                      const char **parent)
+    ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2);
+
+void virStorageFileFreeMetadata(virStorageFileMetadataPtr meta);
+
+int virStorageFileResize(const char *path, unsigned long long capacity);
+
+enum {
+    VIR_STORAGE_FILE_SHFS_NFS = (1 << 0),
+    VIR_STORAGE_FILE_SHFS_GFS2 = (1 << 1),
+    VIR_STORAGE_FILE_SHFS_OCFS = (1 << 2),
+    VIR_STORAGE_FILE_SHFS_AFS = (1 << 3),
+};
+
+int virStorageFileIsSharedFS(const char *path);
+int virStorageFileIsClusterFS(const char *path);
+int virStorageFileIsSharedFSType(const char *path,
+                                 int fstypes);
+
+int virStorageFileGetLVMKey(const char *path,
+                            char **key);
+int virStorageFileGetSCSIKey(const char *path,
+                             char **key);
+
+#endif /* __VIR_STORAGE_FILE_H__ */
diff --git a/src/vbox/vbox_tmpl.c b/src/vbox/vbox_tmpl.c
index daa90bf..923ff04 100644
--- a/src/vbox/vbox_tmpl.c
+++ b/src/vbox/vbox_tmpl.c
@@ -48,7 +48,7 @@
 #include "virterror_internal.h"
 #include "domain_event.h"
 #include "storage_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 #include "uuid.h"
 #include "viralloc.h"
 #include "nodeinfo.h"
diff --git a/src/xenxs/xen_sxpr.c b/src/xenxs/xen_sxpr.c
index 0cbc248..b28c538 100644
--- a/src/xenxs/xen_sxpr.c
+++ b/src/xenxs/xen_sxpr.c
@@ -36,7 +36,7 @@
 #include "count-one-bits.h"
 #include "xenxs_private.h"
 #include "xen_sxpr.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 /* Get a domain id from a S-expression string */
 int xenGetDomIdFromSxprString(const char *sexpr, int xendConfigVersion)
diff --git a/src/xenxs/xen_xm.c b/src/xenxs/xen_xm.c
index c29df1c..007036b 100644
--- a/src/xenxs/xen_xm.c
+++ b/src/xenxs/xen_xm.c
@@ -37,7 +37,7 @@
 #include "xen_xm.h"
 #include "xen_sxpr.h"
 #include "domain_conf.h"
-#include "storage_file.h"
+#include "virstoragefile.h"
 
 /* Convenience method to grab a long int from the config file object */
 static int xenXMConfigGetBool(virConfPtr conf,
-- 
1.7.11.7


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