From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> --- 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@xxxxxxxxxx> - */ - -#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@xxxxxxxxxx", - 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@xxxxxxxxxx> - */ - -#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@xxxxxxxxxx> + */ + +#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@xxxxxxxxxx", + 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@xxxxxxxxxx> + */ + +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list