From: "Daniel P. Berrange" <berrange(a)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(a)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(a)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(a)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(a)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(a)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(a)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