Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 0 of 8 RESEND] blktap3/libvhd: Introduce VHD library
This patch series introduces the VHD library. It is based on the blktap2 one, with changes coming from the blktap2.5 one. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com>
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 1 of 8 RESEND] blktap3/vhd: Introduce VHD-relatred headers
This patch adds the vhd.h and libvhd.h header files related to VHD manipulation. They are based on blktap2, with most changes coming from blktap2.5, with some additional clean up. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap2/include/libvhd.h b/tools/blktap3/include/libvhd.h copy from tools/blktap2/include/libvhd.h copy to tools/blktap3/include/libvhd.h --- a/tools/blktap2/include/libvhd.h +++ b/tools/blktap3/include/libvhd.h @@ -1,4 +1,5 @@ /* Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,48 +29,31 @@ #define _VHD_LIB_H_ #include <string.h> -#if defined(__linux__) #include <endian.h> #include <byteswap.h> -#elif defined(__NetBSD__) -#include <sys/endian.h> -#include <sys/bswap.h> +#include <uuid/uuid.h> +#include <sys/param.h> +#include "blktap3.h" + +#if BYTE_ORDER == LITTLE_ENDIAN +#define BE16_IN(foo) (*(foo)) = bswap_16(*(foo)) +#define BE32_IN(foo) (*(foo)) = bswap_32(*(foo)) +#define BE64_IN(foo) (*(foo)) = bswap_64(*(foo)) +#define BE16_OUT(foo) (*(foo)) = bswap_16(*(foo)) +#define BE32_OUT(foo) (*(foo)) = bswap_32(*(foo)) +#define BE64_OUT(foo) (*(foo)) = bswap_64(*(foo)) +#else +#define BE16_IN(foo) +#define BE32_IN(foo) +#define BE64_IN(foo) +#define BE32_OUT(foo) +#define BE32_OUT(foo) +#define BE64_OUT(foo) #endif -#include "vhd-uuid.h" #include "vhd.h" -#ifndef O_LARGEFILE -#define O_LARGEFILE 0 -#endif - -#if BYTE_ORDER == LITTLE_ENDIAN -#if defined(__linux__) - #define BE16_IN(foo) (*(foo)) = bswap_16(*(foo)) - #define BE32_IN(foo) (*(foo)) = bswap_32(*(foo)) - #define BE64_IN(foo) (*(foo)) = bswap_64(*(foo)) - #define BE16_OUT(foo) (*(foo)) = bswap_16(*(foo)) - #define BE32_OUT(foo) (*(foo)) = bswap_32(*(foo)) - #define BE64_OUT(foo) (*(foo)) = bswap_64(*(foo)) -#elif defined(__NetBSD__) - #define BE16_IN(foo) (*(foo)) = bswap16(*(foo)) - #define BE32_IN(foo) (*(foo)) = bswap32(*(foo)) - #define BE64_IN(foo) (*(foo)) = bswap64(*(foo)) - #define BE16_OUT(foo) (*(foo)) = bswap16(*(foo)) - #define BE32_OUT(foo) (*(foo)) = bswap32(*(foo)) - #define BE64_OUT(foo) (*(foo)) = bswap64(*(foo)) -#endif -#else - #define BE16_IN(foo) - #define BE32_IN(foo) - #define BE64_IN(foo) - #define BE32_OUT(foo) - #define BE32_OUT(foo) - #define BE64_OUT(foo) -#endif - -#define MIN(a, b) (((a) < (b)) ? (a) : (b)) -#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define BIT_MASK 0x80 #define VHD_MAX_NAME_LEN 1024 @@ -85,8 +69,11 @@ #define VHD_OPEN_FAST 0x00004 #define VHD_OPEN_STRICT 0x00008 #define VHD_OPEN_IGNORE_DISABLED 0x00010 +#define VHD_OPEN_CACHED 0x00020 +#define VHD_OPEN_IO_WRITE_SPARSE 0x00040 -#define VHD_FLAG_CREAT_PARENT_RAW 0x00001 +#define VHD_FLAG_CREAT_FILE_SIZE_FIXED 0x00001 +#define VHD_FLAG_CREAT_PARENT_RAW 0x00002 #define vhd_flag_set(word, flag) ((word) |= (flag)) #define vhd_flag_clear(word, flag) ((word) &= ~(flag)) @@ -103,6 +90,9 @@ #define FAIL_RESIZE_END 6 #define NUM_FAIL_TESTS 7 +/* + * TODO check whether this is still valid + */ #ifdef ENABLE_FAILURE_TESTING #define TEST_FAIL_AT(point) \ if (TEST_FAIL[point]) { \ @@ -138,6 +128,7 @@ struct vhd_batmap { char *map; }; +TAILQ_HEAD(tq_vhd_ctx, vhd_context); struct vhd_context { int fd; char *file; @@ -151,8 +142,25 @@ struct vhd_context { vhd_footer_t footer; vhd_bat_t bat; vhd_batmap_t batmap; + + struct vhd_context *parent; }; +static inline int test_bit(volatile char *addr, int nr) +{ + return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0; +} + +static inline void set_bit(volatile char *addr, int nr) +{ + addr[nr >> 3] |= (BIT_MASK >> (nr & 7)); +} + +static inline void clear_bit(volatile char *addr, int nr) +{ + addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7)); +} + static inline uint32_t secs_round_up(uint64_t bytes) { @@ -216,7 +224,7 @@ vhd_parent_locator_size(vhd_parent_locat static inline int vhd_parent_raw(vhd_context_t *ctx) { - return vhd_uuid_is_nil(&ctx->header.prt_uuid); + return uuid_is_null(ctx->header.prt_uuid); } void libvhd_set_log_level(int); @@ -229,7 +237,7 @@ uint32_t vhd_chs(uint64_t size); uint32_t vhd_checksum_footer(vhd_footer_t *); uint32_t vhd_checksum_header(vhd_header_t *); -uint32_t vhd_checksum_batmap(vhd_batmap_t *); +uint32_t vhd_checksum_batmap(vhd_context_t *, vhd_batmap_t *); void vhd_footer_in(vhd_footer_t *); void vhd_footer_out(vhd_footer_t *); @@ -243,30 +251,36 @@ void vhd_batmap_header_out(vhd_batmap_t int vhd_validate_footer(vhd_footer_t *footer); int vhd_validate_header(vhd_header_t *header); int vhd_validate_batmap_header(vhd_batmap_t *batmap); -int vhd_validate_batmap(vhd_batmap_t *batmap); +int vhd_validate_batmap(vhd_context_t *, vhd_batmap_t *batmap); int vhd_validate_platform_code(uint32_t code); int vhd_open(vhd_context_t *, const char *file, int flags); void vhd_close(vhd_context_t *); -int vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t); +/* vhd_create: mbytes is the virtual size for BAT/batmap preallocation - see + * vhd-util-resize.c + */ +int vhd_create(const char *name, uint64_t bytes, int type, uint64_t mbytes, + vhd_flag_creat_t); /* vhd_snapshot: the bytes parameter is optional and can be 0 if the snapshot * is to have the same size as the (first non-empty) parent */ int vhd_snapshot(const char *snapshot, uint64_t bytes, const char *parent, - vhd_flag_creat_t); + uint64_t mbytes, vhd_flag_creat_t); int vhd_hidden(vhd_context_t *, int *); int vhd_chain_depth(vhd_context_t *, int *); +int vhd_marker(vhd_context_t *, char *); +int vhd_set_marker(vhd_context_t *, char); -off_t vhd_position(vhd_context_t *); -int vhd_seek(vhd_context_t *, off_t, int); +off64_t vhd_position(vhd_context_t *); +int vhd_seek(vhd_context_t *, off64_t, int); int vhd_read(vhd_context_t *, void *, size_t); int vhd_write(vhd_context_t *, void *, size_t); int vhd_offset(vhd_context_t *, uint32_t, uint32_t *); -int vhd_end_of_headers(vhd_context_t *ctx, off_t *off); -int vhd_end_of_data(vhd_context_t *ctx, off_t *off); -int vhd_batmap_header_offset(vhd_context_t *ctx, off_t *off); +int vhd_end_of_headers(vhd_context_t *ctx, off64_t *off); +int vhd_end_of_data(vhd_context_t *ctx, off64_t *off); +int vhd_batmap_header_offset(vhd_context_t *ctx, off64_t *off); int vhd_get_header(vhd_context_t *); int vhd_get_footer(vhd_context_t *); @@ -283,38 +297,45 @@ int vhd_batmap_test(vhd_context_t *, vhd void vhd_batmap_set(vhd_context_t *, vhd_batmap_t *, uint32_t); void vhd_batmap_clear(vhd_context_t *, vhd_batmap_t *, uint32_t); -int vhd_get_phys_size(vhd_context_t *, off_t *); -int vhd_set_phys_size(vhd_context_t *, off_t); +int vhd_file_size_fixed(vhd_context_t *); +int vhd_get_phys_size(vhd_context_t *, off64_t *); +int vhd_set_phys_size(vhd_context_t *, off64_t); +int vhd_set_virt_size(vhd_context_t *, uint64_t); int vhd_bitmap_test(vhd_context_t *, char *, uint32_t); void vhd_bitmap_set(vhd_context_t *, char *, uint32_t); void vhd_bitmap_clear(vhd_context_t *, char *, uint32_t); +int vhd_initialize_header_parent_name(vhd_context_t *, const char *); +int vhd_write_parent_locators(vhd_context_t *, const char *); int vhd_parent_locator_count(vhd_context_t *); int vhd_parent_locator_get(vhd_context_t *, char **); -int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, char **); +int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, + char **); int vhd_find_parent(vhd_context_t *, const char *, char **); int vhd_parent_locator_write_at(vhd_context_t *, const char *, - off_t, uint32_t, size_t, + off64_t, uint32_t, size_t, vhd_parent_locator_t *); int vhd_header_decode_parent(vhd_context_t *, vhd_header_t *, char **); int vhd_change_parent(vhd_context_t *, char *parent_path, int raw); +int vhd_macx_encode_location(char *name, char **out, int *outlen); +int vhd_w2u_encode_location(char *name, char **out, int *outlen); int vhd_read_footer(vhd_context_t *, vhd_footer_t *); -int vhd_read_footer_at(vhd_context_t *, vhd_footer_t *, off_t); +int vhd_read_footer_at(vhd_context_t *, vhd_footer_t *, off64_t); int vhd_read_footer_strict(vhd_context_t *, vhd_footer_t *); int vhd_read_header(vhd_context_t *, vhd_header_t *); -int vhd_read_header_at(vhd_context_t *, vhd_header_t *, off_t); +int vhd_read_header_at(vhd_context_t *, vhd_header_t *, off64_t); int vhd_read_bat(vhd_context_t *, vhd_bat_t *); int vhd_read_batmap(vhd_context_t *, vhd_batmap_t *); int vhd_read_bitmap(vhd_context_t *, uint32_t block, char **bufp); int vhd_read_block(vhd_context_t *, uint32_t block, char **bufp); int vhd_write_footer(vhd_context_t *, vhd_footer_t *); -int vhd_write_footer_at(vhd_context_t *, vhd_footer_t *, off_t); +int vhd_write_footer_at(vhd_context_t *, vhd_footer_t *, off64_t); int vhd_write_header(vhd_context_t *, vhd_header_t *); -int vhd_write_header_at(vhd_context_t *, vhd_header_t *, off_t); +int vhd_write_header_at(vhd_context_t *, vhd_header_t *, off64_t); int vhd_write_bat(vhd_context_t *, vhd_bat_t *); int vhd_write_batmap(vhd_context_t *, vhd_batmap_t *); int vhd_write_bitmap(vhd_context_t *, uint32_t block, char *bitmap); @@ -322,5 +343,7 @@ int vhd_write_block(vhd_context_t *, uin int vhd_io_read(vhd_context_t *, char *, uint64_t, uint32_t); int vhd_io_write(vhd_context_t *, char *, uint64_t, uint32_t); +int vhd_io_read_bytes(vhd_context_t *, void *, size_t, uint64_t); +int vhd_io_write_bytes(vhd_context_t *, void *, size_t, uint64_t); #endif diff --git a/tools/blktap2/include/vhd.h b/tools/blktap3/include/vhd.h copy from tools/blktap2/include/vhd.h copy to tools/blktap3/include/vhd.h --- a/tools/blktap2/include/vhd.h +++ b/tools/blktap3/include/vhd.h @@ -1,4 +1,5 @@ /* Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -24,13 +25,12 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + #ifndef __VHD_H__ #define __VHD_H__ #include <inttypes.h> - -typedef uint32_t u32; -typedef uint64_t u64; +#include <uuid/uuid.h> #define DEBUG 1 @@ -41,28 +41,94 @@ typedef uint64_t u64; #define VHD_SECTOR_SIZE 512 #define VHD_SECTOR_SHIFT 9 -/* ---------------------------------------------------------------------- */ -/* This is the generic disk footer, used by all disks. */ -/* ---------------------------------------------------------------------- */ +/** + * This is the generic disk footer, used by all disks. + */ +struct hd_ftr { + /** + * Identifies original creator of the disk + */ + char cookie[8]; -struct hd_ftr { - char cookie[8]; /* Identifies original creator of the disk */ - u32 features; /* Feature Support -- see below */ - u32 ff_version; /* (major,minor) version of disk file */ - u64 data_offset; /* Abs. offset from SOF to next structure */ - u32 timestamp; /* Creation time. secs since 1/1/2000GMT */ - char crtr_app[4]; /* Creator application */ - u32 crtr_ver; /* Creator version (major,minor) */ - u32 crtr_os; /* Creator host OS */ - u64 orig_size; /* Size at creation (bytes) */ - u64 curr_size; /* Current size of disk (bytes) */ - u32 geometry; /* Disk geometry */ - u32 type; /* Disk type */ - u32 checksum; /* 1''s comp sum of this struct. */ - vhd_uuid_t uuid; /* Unique disk ID, used for naming parents */ - char saved; /* one-bit -- is this disk/VM in a saved state? */ - char hidden; /* tapdisk-specific field: is this vdi hidden? */ - char reserved[426]; /* padding */ + /** + * Feature Support -- see below + */ + uint32_t features; + + /** + * (major,minor) version of disk file + */ + uint32_t ff_version; + + /** + * Abs. offset from SOF to next structure + */ + uint64_t data_offset; + + /** + * Creation time. secs since 1/1/2000GMT + */ + uint32_t timestamp; + + /** + * Creator application + */ + char crtr_app[4]; + + /** + * Creator version (major,minor) + */ + uint32_t crtr_ver; + + /** + * Creator host OS + */ + uint32_t crtr_os; + + /** + * Size at creation (bytes) + */ + uint64_t orig_size; + + /** + * Current size of disk (bytes) + */ + uint64_t curr_size; + + /** + * Disk geometry + */ + uint32_t geometry; + + /** + * Disk type + */ + uint32_t type; + + /** + * 1''s comp sum of this struct. + */ + uint32_t checksum; + + /** + * Unique disk ID, used for naming parents + */ + uuid_t uuid; + + /** + * one-bit -- is this disk/VM in a saved state? + */ + char saved; + + /** + * tapdisk-specific field: is this vdi hidden? + */ + char hidden; + + /** + * padding + */ + char reserved[426]; }; /* VHD cookie string. */ @@ -104,26 +170,33 @@ static const char HD_COOKIE[9] = "cone #define HD_TYPE_FIXED 2 /* fixed-allocation disk */ #define HD_TYPE_DYNAMIC 3 /* dynamic disk */ #define HD_TYPE_DIFF 4 /* differencing disk */ - -/* String table for hd.type */ -static const char *HD_TYPE_STR[7] = { - "None", /* 0 */ - "Reserved (deprecated)", /* 1 */ - "Fixed hard disk", /* 2 */ - "Dynamic hard disk", /* 3 */ - "Differencing hard disk", /* 4 */ - "Reserved (deprecated)", /* 5 */ - "Reserved (deprecated)" /* 6 */ -}; - #define HD_TYPE_MAX 6 struct prt_loc { - u32 code; /* Platform code -- see defines below. */ - u32 data_space; /* Number of 512-byte sectors to store locator */ - u32 data_len; /* Actual length of parent locator in bytes */ - u32 res; /* Must be zero */ - u64 data_offset; /* Absolute offset of locator data (bytes) */ + /** + * Platform code -- see defines below. + */ + uint32_t code; + + /** + * Number of 512-byte sectors to store locator + */ + uint32_t data_space; + + /** + * Actual length of parent locator in bytes + */ + uint32_t data_len; + + /** + * Must be zero + */ + uint32_t res; + + /** + * Absolute offset of locator data (bytes) + */ + uint64_t data_offset; }; /* Platform Codes */ @@ -135,24 +208,74 @@ struct prt_loc { #define PLAT_CODE_MAC 0x4D616320 /* MacOS alias stored as a blob. */ #define PLAT_CODE_MACX 0x4D616358 /* File URL (UTF-8), see RFC 2396. */ -/* ---------------------------------------------------------------------- */ -/* This is the dynamic disk header. */ -/* ---------------------------------------------------------------------- */ +/** + * This is the dynamic disk header. + */ +struct dd_hdr { + /** + * Should contain "cxsparse" + */ + char cookie[8]; -struct dd_hdr { - char cookie[8]; /* Should contain "cxsparse" */ - u64 data_offset; /* Byte offset of next record. (Unused) 0xffs */ - u64 table_offset; /* Absolute offset to the BAT. */ - u32 hdr_ver; /* Version of the dd_hdr (major,minor) */ - u32 max_bat_size; /* Maximum number of entries in the BAT */ - u32 block_size; /* Block size in bytes. Must be power of 2. */ - u32 checksum; /* Header checksum. 1''s comp of all fields. */ - vhd_uuid_t prt_uuid; /* ID of the parent disk. */ - u32 prt_ts; /* Modification time of the parent disk */ - u32 res1; /* Reserved. */ - char prt_name[512]; /* Parent unicode name. */ - struct prt_loc loc[8]; /* Parent locator entries. */ - char res2[256]; /* Reserved. */ + /** + * Byte offset of next record. (Unused) 0xffs + */ + uint64_t data_offset; + + /** + * Absolute offset to the BAT + */ + uint64_t table_offset; + + /** + * Version of the dd_hdr (major,minor) + */ + uint32_t hdr_ver; + + /** + * Maximum number of entries in the BAT + */ + uint32_t max_bat_size; + + /** + * Block size in bytes. Must be power of 2. + */ + uint32_t block_size; + + /** + * Header checksum. 1''s comp of all fields. + */ + uint32_t checksum; + + /** + * ID of the parent disk. + */ + uuid_t prt_uuid; + + /** + * Modification time of the parent disk + */ + uint32_t prt_ts; + + /** + * Reserved. + */ + uint32_t res1; + + /** + * Parent unicode name. + */ + char prt_name[512]; + + /** + * Parent locator entries. + */ + struct prt_loc loc[8]; + + /** + * Reserved. + */ + char res2[256]; }; /* VHD cookie string. */ @@ -167,11 +290,35 @@ static const char DD_COOKIE[9] = "cxsp #define DD_BLK_UNUSED 0xFFFFFFFF struct dd_batmap_hdr { - char cookie[8]; /* should contain "tdbatmap" */ - u64 batmap_offset; /* byte offset to batmap */ - u32 batmap_size; /* batmap size in sectors */ - u32 batmap_version; /* version of batmap */ - u32 checksum; /* batmap checksum -- 1''s complement of batmap */ + /** + * should contain "tdbatmap" + */ + char cookie[8]; + + /** + * byte offset to batmap + */ + uint64_t batmap_offset; + + /** + * batmap size in sectors + */ + uint32_t batmap_size; + + /** + * version of batmap + */ + uint32_t batmap_version; + + /** + * batmap checksum -- 1''s complement of batmap + */ + uint32_t checksum; + + /** + * generic marker field + */ + char marker; }; static const char VHD_BATMAP_COOKIE[9] = "tdbatmap"; @@ -180,6 +327,7 @@ static const char VHD_BATMAP_COOKIE[9] * version 1.1: signed char checksum */ #define VHD_BATMAP_VERSION(major, minor) (((major) << 16) | ((minor) & 0x0000FFFF)) + #define VHD_BATMAP_CURRENT_VERSION VHD_BATMAP_VERSION(1, 2) /* Layout of a dynamic disk: @@ -191,7 +339,7 @@ static const char VHD_BATMAP_COOKIE[9] * +-------------------------------------------------+ * | BAT (Block allocation table) | * | - Array of absolute sector offsets into the | - * | file (u32). | + * | file (uint32_t). | * | - Rounded up to a sector boundary. | * | - Unused entries are marked as 0xFFFFFFFF | * | - max entries in dd_hdr->max_bat_size |
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 2 of 8 RESEND] blktap3/vhd: Introduce libvhdio.c
This patch import libvhdio.c from blktap2.5. It seems to contain low-level functions for opening/closing/reading/writing etc. VHD files. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap3/vhd/lib/libvhdio.c b/tools/blktap3/vhd/lib/libvhdio.c new file mode 100644 --- /dev/null +++ b/tools/blktap3/vhd/lib/libvhdio.c @@ -0,0 +1,1619 @@ +/* + * Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#ifdef _LARGEFILE_SOURCE +#undef _LARGEFILE_SOURCE +#endif + +#ifdef _LARGEFILE64_SOURCE +#undef _LARGEFILE64_SOURCE +#endif + +#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 +#undef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 32 +#endif + +/* TODO again? */ +#ifdef _LARGEFILE_SOURCE +#undef _LARGEFILE_SOURCE +#endif + +#include <time.h> +#include <stdio.h> +#include <errno.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <signal.h> +#include <malloc.h> +#include <sys/stat.h> +#include <linux/fs.h> +#include <linux/hdreg.h> + +#define _FCNTL_H +#include <bits/fcntl.h> + +#include "libvhd.h" +#include "partition.h" + +/* TODO define in a common location */ +#define _ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +#define __RESOLVE(func, name) \ + do { \ + if (!_libvhd_io_initialized) \ + _libvhd_io_init(); \ + if (!(func)) \ + (func) = _get_std_fn((name)); \ + } while (0) + +#define _RESOLVE(func) __RESOLVE((func), __func__) + +#define LIBVHD_IO_DEBUG "LIBVHD_IO_DEBUG" +#define LIBVHD_IO_DUMP "LIBVHD_IO_DUMP" +#define LIBVHD_IO_TEST "LIBVHD_IO_TEST" + +static int libvhdio_logging; +static FILE *libvhdio_log; +#define LOG(_f, _a...) \ + do { \ + if (libvhdio_logging && libvhdio_log) { \ + fprintf(libvhdio_log, _f, ##_a); \ + fflush(libvhdio_log); \ + } \ + } while (0) + +static int libvhdio_dump; +#define DUMP(_buf, _size) \ + do { \ + if (libvhdio_log && libvhdio_dump) { \ + int i; \ + LOG("''"); \ + for (i = 0; i < (_size); i++) \ + fputc(((char *)(_buf))[i], \ + libvhdio_log); \ + LOG("''\n"); \ + } \ + } while (0) + +struct _function { + const char *name; + void *fn; +}; + +struct vhd_object { + vhd_context_t vhd; + int refcnt; + uint64_t ino; + TAILQ_ENTRY(vhd_object) entry; +}; + +TAILQ_HEAD(tqh_vhd_object, vhd_object); + +struct vhd_partition { + struct vhd_object *vhd_obj; + int partition; + int flags; + off64_t start; /* in sectors */ + off64_t end; /* in sectors */ + off64_t size; /* in sectors */ +}; + +struct vhd_fd_context { + struct vhd_partition vhd_part; + off64_t off; + int users; +}; + +typedef struct vhd_object vhd_object_t; +typedef struct vhd_partition vhd_partition_t; +typedef struct vhd_fd_context vhd_fd_context_t; +typedef int (*_std_open_t) (const char *, int, int); +typedef int (*_std_close_t) (int); +typedef FILE *(*_std_fopen_t) (const char *, const char *); + +static struct _function _function_table[] = { + {.name = "open",.fn = NULL}, + {.name = "open64",.fn = NULL}, +#ifdef __open_2 + {.name = "__open_2",.fn = NULL}, +#endif // __open_2 +#ifdef __open64_2 + {.name = "__open64_2",.fn = NULL}, +#endif // __open64_2 + {.name = "close",.fn = NULL}, + {.name = "dup",.fn = NULL}, + {.name = "dup2",.fn = NULL}, +#ifdef dup3 + {.name = "dup3",.fn = NULL}, +#endif // dup3 + {.name = "lseek",.fn = NULL}, + {.name = "lseek64",.fn = NULL}, + {.name = "read",.fn = NULL}, + {.name = "write",.fn = NULL}, + {.name = "pread",.fn = NULL}, + {.name = "pread64",.fn = NULL}, + {.name = "pwrite",.fn = NULL}, + {.name = "pwrite64",.fn = NULL}, + {.name = "fsync",.fn = NULL}, + {.name = "__xstat",.fn = NULL}, + {.name = "__xstat64",.fn = NULL}, + {.name = "__fxstat",.fn = NULL}, + {.name = "__fxstat64",.fn = NULL}, + {.name = "__lxstat",.fn = NULL}, + {.name = "__lxstat64",.fn = NULL}, + {.name = "ioctl",.fn = NULL}, + {.name = "fcntl",.fn = NULL}, + + {.name = "fopen",.fn = NULL}, + {.name = "fopen64",.fn = NULL}, + {.name = "_IO_getc",.fn = NULL}, + {.name = "fread",.fn = NULL}, + + {.name = "posix_memalign",.fn = NULL}, +}; + +static int _libvhd_io_interpose = 1; +static struct tqh_vhd_object _vhd_objects; +static vhd_fd_context_t **_vhd_map; +static int _vhd_map_size; + +static int _libvhd_io_initialized; +static void _libvhd_io_init(void) __attribute__ ((constructor)); + +static volatile sig_atomic_t _libvhd_io_reset_vhds; + +static void *_load_std_fn(const char *name) +{ + void *fn; + char *msg; + + LOG("loading %s\n", name); + + fn = dlsym(RTLD_NEXT, name); + msg = dlerror(); + if (!fn || msg) { + LOG("dlsym ''%s'' failed: %s\n", name, msg); + exit(1); + } + + return fn; +} + +static void *_get_std_fn(const char *name) +{ + int i; + + for (i = 0; i < _ARRAY_SIZE(_function_table); i++) + if (!strcmp(name, _function_table[i].name)) + return _function_table[i].fn; + + return NULL; +} + +static void _init_vhd_log(void) +{ + int (*_std_dup) (int) = _load_std_fn("dup"); + int log_fd = _std_dup(STDERR_FILENO); + + libvhdio_log = fdopen(log_fd, "a"); + + if (getenv(LIBVHD_IO_DEBUG)) { + libvhdio_logging = 1; + libvhd_set_log_level(1); + } + + if (getenv(LIBVHD_IO_DUMP)) + libvhdio_dump = 1; +} + +static void _init_vhd_map(void) +{ + _vhd_map_size = sysconf(_SC_OPEN_MAX); + _vhd_map = calloc(_vhd_map_size, sizeof(vhd_fd_context_t *)); + if (!_vhd_map) { + LOG("failed to init vhd map\n"); + exit(1); + } +} + +static void _init_vhd_objs(void) +{ + TAILQ_INIT(&_vhd_objects); +} + +static void _libvhd_io_reset(void) +{ + int i, err; + + if (!_libvhd_io_interpose) + return; + + _libvhd_io_reset_vhds = 0; + + if (!_vhd_map) + return; + + _libvhd_io_interpose = 0; + + for (i = 0; i < _vhd_map_size; i++) { + int flags; + vhd_context_t *vhd; + char *child, *parent; + vhd_fd_context_t *vhd_fd = _vhd_map[i]; + + if (!vhd_fd) + continue; + + vhd = &vhd_fd->vhd_part.vhd_obj->vhd; + + flags = vhd->oflags; + child = strdup(vhd->file); + if (!child) + exit(ENOMEM); + + LOG("resetting vhd fd %d user fd %d\n", vhd->fd, i); + vhd_close(vhd); + + if (asprintf(&parent, "%s.%d.vhd", child, (int) time(NULL)) == -1) + exit(ENOMEM); + + if (rename(child, parent)) + exit(errno); + + err = vhd_snapshot(child, 0, parent, 0, 0); + if (err) { + LOG("snapshot of %s failed on reset: %d\n", child, err); + exit(1); + } + + err = vhd_open(vhd, child, flags); + if (err) { + LOG("opening new snapshot %s failed on reset: %d\n", + child, err); + exit(1); + } + + LOG("snapshot %s %s vhd fd %d user fd %d\n", + child, parent, vhd->fd, i); + + free(child); + free(parent); + } + + _libvhd_io_interpose = 1; +} + +static void _libvhd_io_continue(int signo __attribute__((unused))) +{ + _libvhd_io_reset_vhds = 1; +} + +static void _init_vhd_test(void) +{ + if (getenv(LIBVHD_IO_TEST)) { + sigset_t set; + struct sigaction act; + + if (sigemptyset(&set)) + exit(1); + + act = (struct sigaction) { + .sa_handler = _libvhd_io_continue,.sa_mask = set,.sa_flags = 0,}; + + if (sigaction(SIGCONT, &act, NULL)) { + LOG("failed to set signal handler: %d\n", errno); + exit(1); + } + + LOG("testing enabled\n"); + } +} + +static void _libvhd_io_init(void) +{ + int i; + + if (_libvhd_io_initialized) + return; + + _init_vhd_log(); + _init_vhd_map(); + _init_vhd_objs(); + _init_vhd_test(); + + for (i = 0; i < _ARRAY_SIZE(_function_table); i++) + _function_table[i].fn = _load_std_fn(_function_table[i].name); + + LOG("\n"); + _libvhd_io_initialized = 1; +} + +static vhd_object_t *_libvhd_io_get_vhd(const char *path, int flags) +{ + struct stat64 st; + int err, vhd_flags; + vhd_object_t *tmp, *obj = NULL; + + _libvhd_io_interpose = 0; + + if (stat64(path, &st)) + goto out; + + TAILQ_FOREACH(tmp, &_vhd_objects, entry) + if (tmp->ino == st.st_ino) { + obj = tmp; + if (flags & (O_RDWR | O_WRONLY) && + obj->vhd.oflags & VHD_OPEN_RDONLY) { + errno = EACCES; + obj = NULL; + } + goto out; + } + + vhd_flags = VHD_OPEN_CACHED; + + /* + * we open RDWR whenever we can since vhd objects may be shared and + * we don''t have a clean way to switch RDONLY vhds to RDWR. we''ll + * only open RDONLY when (flags & O_RDONLY) and we lack permission + * to open RDWR. + */ + if (access(path, W_OK) == -1) { + if (errno != EACCES) + goto out; + + if (flags & (O_WRONLY | O_RDWR)) + goto out; + + vhd_flags |= VHD_OPEN_RDONLY; + } else { + vhd_flags |= VHD_OPEN_RDWR; + } + + obj = malloc(sizeof(*obj)); + if (!obj) { + errno = ENOMEM; + goto out; + } + + obj->refcnt = 0; + obj->ino = st.st_ino; + + err = vhd_open(&obj->vhd, path, vhd_flags); + if (err) { + free(obj); + obj = NULL; + errno = err; + goto out; + } + + TAILQ_INSERT_HEAD(&_vhd_objects, obj, entry); + + out: + _libvhd_io_interpose = 1; + if (obj) { + obj->refcnt++; + LOG("%s: %s 0x%" PRIx64 " 0x%x\n", + __func__, path, obj->ino, obj->refcnt); + } + return obj; +} + +static void _libvhd_io_put_vhd(vhd_object_t * obj) +{ + LOG("%s: 0x%" PRIx64 " 0x%x\n", __func__, obj->ino, obj->refcnt - 1); + if (--obj->refcnt == 0) { + vhd_close(&obj->vhd); + TAILQ_REMOVE(&_vhd_objects, obj, entry); + free(obj); + } +} + +static inline vhd_fd_context_t *_libvhd_io_map_get(int idx) +{ + if (_libvhd_io_reset_vhds) + _libvhd_io_reset(); + return _vhd_map[idx]; +} + +static inline void _libvhd_io_map_set(int idx, vhd_fd_context_t * vhd_fd) +{ + vhd_fd->users++; + _vhd_map[idx] = vhd_fd; + LOG("mapping 0x%x to %s (0x%x users)\n", + idx, vhd_fd->vhd_part.vhd_obj->vhd.file, vhd_fd->users); +} + +static inline void _libvhd_io_map_clear(int idx) +{ + vhd_fd_context_t *vhd_fd; + + if (idx < 0 || idx >= _vhd_map_size) + return; + + vhd_fd = _vhd_map[idx]; + _vhd_map[idx] = NULL; + + if (vhd_fd) { + if (--vhd_fd->users == 0) { + _libvhd_io_put_vhd(vhd_fd->vhd_part.vhd_obj); + free(vhd_fd); + } + } +} + +static int +_libvhd_io_read_bytes(vhd_partition_t * vhd_part, + void *buf, size_t size, uint64_t off) +{ + int ret; + vhd_context_t *vhd = &vhd_part->vhd_obj->vhd; + + _libvhd_io_interpose = 0; + ret = vhd_io_read_bytes(vhd, buf, size, off); + _libvhd_io_interpose = 1; + + if (ret) { + LOG("vhd_io_read_bytes %s %p 0x%zx 0x%" PRIx64 " failed: %d\n", + vhd->file, buf, size, off, ret); + errno = -ret; + ret = 1; + } else { + LOG("vhd_io_read_bytes %s %p 0x%zx 0x%" PRIx64 "\n", + vhd->file, buf, size, off); + DUMP(buf, size); + } + + return ret; +} + +static int +_libvhd_io_write_bytes(vhd_partition_t * vhd_part, + const void *buf, size_t size, uint64_t off) +{ + int ret; + vhd_context_t *vhd = &vhd_part->vhd_obj->vhd; + + _libvhd_io_interpose = 0; + ret = vhd_io_write_bytes(vhd, (void *) buf, size, off); + _libvhd_io_interpose = 1; + + if (ret) { + LOG("vhd_io_write_bytes %s %p 0x%zx 0x%" PRIx64 " failed: %d\n", + vhd->file, buf, size, off, ret); + errno = -ret; + ret = 1; + } else { + LOG("vhd_io_write_bytes %s %p 0x%zx 0x%" PRIx64 "\n", + vhd->file, buf, size, off); + DUMP(buf, size); + } + + return ret; +} + +/* + * symlink pathnames like *.vhd[1-4] are treated specially + */ +static int +_libvhd_io_guess_partition(const char *path, int *partition, int *skip) +{ + char *sfx; + int err, len; + struct stat64 st; + + *skip = 0; + *partition = 0; + + _libvhd_io_interpose = 0; + err = lstat64(path, &st); + _libvhd_io_interpose = 1; + + if (err == -1) + return errno; + + if ((st.st_mode & __S_IFMT) != __S_IFLNK) { + if (st.st_size < VHD_SECTOR_SIZE) + *skip = 1; + return 0; + } + + sfx = strstr(path, ".vhd"); + if (!sfx) + return 0; + + sfx += strlen(".vhd"); + len = strlen(sfx); + if (!len) + return 0; + if (len > 1) + return EINVAL; + + switch (*sfx) { + case ''1'' ... ''4'': + *partition = atoi(sfx); + break; + default: + return EINVAL; + } + + return 0; +} + +static int +_libvhd_io_init_partition(vhd_partition_t * vhd_part, int partition) +{ + int err; + vhd_context_t *vhd; + void *_p; + struct partition_table *pt; + struct primary_partition *p; + + if (partition < 0 || partition > 4) + return ENOENT; + + vhd = &vhd_part->vhd_obj->vhd; + + if (!partition) { + vhd_part->partition = 0; + vhd_part->start = 0; + vhd_part->end = (vhd->footer.curr_size >> VHD_SECTOR_SHIFT); + vhd_part->size = vhd_part->end; + return 0; + } + + err = posix_memalign(&_p, VHD_SECTOR_SIZE, VHD_SECTOR_SIZE); + if (err) + return err; + pt = _p; + + err = _libvhd_io_read_bytes(vhd_part, pt, 512, 0); + if (err) { + LOG("reading partition failed: %d\n", err); + goto out; + } + + partition_table_in(pt); + err = partition_table_validate(pt); + if (err) { + LOG("bad partition table read\n"); + goto out; + } + + p = pt->partitions + (partition - 1); + if (!p->lba || !p->blocks) { + err = ENOENT; + goto out; + } + + vhd_part->partition = partition; + vhd_part->start = p->lba; + vhd_part->end = p->lba + p->blocks; + vhd_part->size = p->blocks; + err = 0; + + LOG("%s: opening %s partition 0x%x start 0x%08" PRIx64 " end 0x%08" + PRIx64 "\n", __func__, vhd->file, partition, vhd_part->start, + vhd_part->end); + + out: + free(pt); + return err; +} + +static int +_libvhd_io_vhd_open(vhd_partition_t * vhd_part, const char *path, + int flags) +{ + int err, skip, partition; + + memset(vhd_part, 0, sizeof(*vhd_part)); + vhd_part->flags = flags; + + err = _libvhd_io_guess_partition(path, &partition, &skip); + if (err) + return err; + + if (skip) + return EINVAL; + + LOG("%s: attempting vhd_open of %s\n", __func__, path); + + vhd_part->vhd_obj = _libvhd_io_get_vhd(path, flags); + if (!vhd_part->vhd_obj) + err = errno; + + if (!err) { + err = _libvhd_io_init_partition(vhd_part, partition); + if (err) { + _libvhd_io_put_vhd(vhd_part->vhd_obj); + memset(vhd_part, 0, sizeof(*vhd_part)); + } + } + + return (err >= 0 ? err : -err); +} + +static int +_libvhd_io_open(const char *pathname, + int flags, mode_t mode, _std_open_t _std_open) +{ + int err, fd; + vhd_fd_context_t *vhd_fd; + + errno = 0; + vhd_fd = NULL; + + vhd_fd = calloc(1, sizeof(*vhd_fd)); + if (!vhd_fd) { + err = ENOMEM; + goto fail; + } + + err = _libvhd_io_vhd_open(&vhd_fd->vhd_part, pathname, flags); + if (err) { + if (err == EINVAL || err == ENOENT) + goto std_open; + + LOG("%s: vhd_open of %s failed: %d\n", __func__, pathname, err); + goto fail; + } +#ifdef O_CLOEXEC + if (flags & (O_APPEND | O_ASYNC | O_CLOEXEC | + O_DIRECTORY | O_NONBLOCK)) { +#else + if (flags & (O_APPEND | O_ASYNC | O_DIRECTORY | O_NONBLOCK)) { +#endif //O_CLOEXEC + LOG("%s: invalid flags for vhd_open: 0x%x\n", __func__, flags); + err = EINVAL; + goto fail; + } + + fd = _std_open("/dev/null", O_RDONLY, 0); + if (fd == -1) { + err = errno; + goto fail; + } + + _libvhd_io_map_set(fd, vhd_fd); + return fd; + + std_open: + free(vhd_fd); + return _std_open(pathname, flags, mode); + + fail: + if (vhd_fd && vhd_fd->vhd_part.vhd_obj) + _libvhd_io_put_vhd(vhd_fd->vhd_part.vhd_obj); + free(vhd_fd); + errno = err; + return -1; +} + +static int _libvhd_io_close(int fd, _std_close_t _std_close) +{ + _libvhd_io_map_clear(fd); + return _std_close(fd); +} + +static FILE *_libvhd_io_fopen(const char *path, const char *mode) +{ + char *m; + FILE *f; + int fd, flags; + vhd_fd_context_t *vhd_fd; + static _std_open_t _std_open64; + + __RESOLVE(_std_open64, "open64"); + + flags = 0; + if (strchr(mode, ''a'')) { + if (strchr(mode, ''+'')) + flags |= O_APPEND | O_RDWR; + else + flags |= O_APPEND | O_WRONLY; + } + if (strchr(mode, ''r'')) { + if (strchr(mode, ''+'')) + flags |= O_RDWR; + else + flags |= O_RDONLY; + } + if (strchr(mode, ''w'')) { + errno = EINVAL; + return NULL; + } + + fd = _libvhd_io_open(path, flags, 0, _std_open64); + if (fd == -1) + return NULL; + + vhd_fd = _libvhd_io_map_get(fd); + if (vhd_fd) + m = "r"; + else + m = (char *) mode; + + f = fdopen(fd, m); + if (!f) { + int err = errno; + close(fd); + errno = err; + } + + return f; +} + +static ssize_t +_libvhd_io_pread(vhd_partition_t * vhd_part, + void *buf, size_t count, off64_t offset) +{ + ssize_t ret; + off64_t psize; + + ret = (ssize_t) - 1; + psize = vhd_part->size << VHD_SECTOR_SHIFT; + + if (vhd_part->flags & O_WRONLY) { + errno = EPERM; + goto out; + } + + if (offset >= psize) { + ret = 0; + goto out; + } + + count = MIN(count, psize - offset); + offset += (vhd_part->start << VHD_SECTOR_SHIFT); + + if (_libvhd_io_read_bytes(vhd_part, buf, count, offset)) + goto out; + + ret = count; + + out: + return ret; +} + +static ssize_t +_libvhd_io_pwrite(vhd_partition_t * vhd_part, + const void *buf, size_t count, off64_t offset) +{ + ssize_t ret; + off64_t psize; + + ret = (ssize_t) - 1; + psize = vhd_part->size << VHD_SECTOR_SHIFT; + + if (vhd_part->flags & O_RDONLY) { + errno = EPERM; + goto out; + } + + if (offset >= psize) { + ret = 0; + goto out; + } + + count = MIN(count, psize - offset); + offset += (vhd_part->start << VHD_SECTOR_SHIFT); + + if (_libvhd_io_write_bytes(vhd_part, buf, count, offset)) + goto out; + + ret = count; + + out: + return ret; +} + +static int +_libvhd_io_fstat(int version, vhd_partition_t * vhd_part, + struct stat *stats) +{ + int ret; + static int (*_std___fxstat) (int, int, struct stat *); + + __RESOLVE(_std___fxstat, "__fxstat"); + ret = _std___fxstat(version, vhd_part->vhd_obj->vhd.fd, stats); + if (ret) + return ret; + + /* + * emulate block device + */ + stats->st_size = 0; + stats->st_blocks = 0; + stats->st_blksize = getpagesize(); + stats->st_mode &= ~__S_IFREG; + stats->st_mode |= __S_IFBLK; + + return 0; +} + +static int +_libvhd_io_fstat64(int version, + vhd_partition_t * vhd_part, struct stat64 *stats) +{ + int ret; + static int (*_std___fxstat64) (int, int, struct stat64 *); + + __RESOLVE(_std___fxstat64, "__fxstat64"); + ret = _std___fxstat64(version, vhd_part->vhd_obj->vhd.fd, stats); + if (ret) + return ret; + + /* + * emulate block device + */ + stats->st_size = 0; + stats->st_blocks = 0; + stats->st_blksize = getpagesize(); + stats->st_mode &= ~__S_IFREG; + stats->st_mode |= __S_IFBLK; + + return 0; +} + +static int +_libvhd_io_stat(int version, const char *path, struct stat *stats) +{ + int err; + vhd_partition_t vhd_part; + + err = _libvhd_io_vhd_open(&vhd_part, path, O_RDONLY); + if (err) { + errno = (err > 0 ? err : -err); + return -1; + } + + err = _libvhd_io_fstat(version, &vhd_part, stats); + _libvhd_io_put_vhd(vhd_part.vhd_obj); + + return err; +} + +static int +_libvhd_io_stat64(int version, const char *path, struct stat64 *stats) +{ + int err; + vhd_partition_t vhd_part; + + err = _libvhd_io_vhd_open(&vhd_part, path, O_RDONLY); + if (err) { + errno = (err > 0 ? err : -err); + return -1; + } + + err = _libvhd_io_fstat64(version, &vhd_part, stats); + _libvhd_io_put_vhd(vhd_part.vhd_obj); + + return err; +} + +int open(const char *pathname, int flags, mode_t _mode) +{ + int fd; + mode_t mode; + static _std_open_t _std_open; + + _RESOLVE(_std_open); + mode = (flags & O_CREAT ? _mode : 0); + + if (!_libvhd_io_interpose) + return _std_open(pathname, flags, mode); + + fd = _libvhd_io_open(pathname, flags, mode, _std_open); + + LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd); + + return fd; +} + +int open64(const char *pathname, int flags, mode_t _mode) +{ + int fd; + mode_t mode; + static _std_open_t _std_open64; + + _RESOLVE(_std_open64); + mode = (flags & O_CREAT ? _mode : 0); + + if (!_libvhd_io_interpose) + return _std_open64(pathname, flags, mode); + + fd = _libvhd_io_open(pathname, flags, mode, _std_open64); + + LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd); + + return fd; +} + +int __open_2(const char *pathname, int flags, mode_t _mode) +{ + int fd; + mode_t mode; + static _std_open_t _std___open_2; + + _RESOLVE(_std___open_2); + mode = (flags & O_CREAT ? _mode : 0); + + if (!_libvhd_io_interpose) + return _std___open_2(pathname, flags, mode); + + fd = _libvhd_io_open(pathname, flags, mode, _std___open_2); + + LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd); + + return fd; +} + +int __open64_2(const char *pathname, int flags, mode_t _mode) +{ + int fd; + mode_t mode; + static _std_open_t _std___open64_2; + + _RESOLVE(_std___open64_2); + mode = (flags & O_CREAT ? _mode : 0); + + if (!_libvhd_io_interpose) + return _std___open64_2(pathname, flags, mode); + + fd = _libvhd_io_open(pathname, flags, mode, _std___open64_2); + + LOG("%s %s 0x%x 0x%x: 0x%x\n", __func__, pathname, flags, mode, fd); + + return fd; +} + +int close(int fd) +{ + static _std_close_t _std_close; + + _RESOLVE(_std_close); + + LOG("%s 0x%x\n", __func__, fd); + + return _libvhd_io_close(fd, _std_close); +} + +int dup(int oldfd) +{ + int newfd; + vhd_fd_context_t *vhd_fd; + static int (*_std_dup) (int); + + _RESOLVE(_std_dup); + vhd_fd = _libvhd_io_map_get(oldfd); + + LOG("%s 0x%x\n", __func__, oldfd); + + newfd = _std_dup(oldfd); + if (newfd != -1 && vhd_fd) + _libvhd_io_map_set(newfd, vhd_fd); + + return newfd; +} + +int dup2(int oldfd, int newfd) +{ + int ret; + vhd_fd_context_t *vhd_fd; + static int (*_std_dup2) (int, int); + + _RESOLVE(_std_dup2); + vhd_fd = _libvhd_io_map_get(oldfd); + + LOG("%s 0x%x 0x%x\n", __func__, oldfd, newfd); + + ret = _std_dup2(oldfd, newfd); + if (ret != -1 && vhd_fd) + _libvhd_io_map_set(ret, vhd_fd); + + return ret; +} + +int dup3(int oldfd, int newfd, int flags) +{ + int ret; + vhd_fd_context_t *vhd_fd; + static int (*_std_dup3) (int, int, int); + + _RESOLVE(_std_dup3); + vhd_fd = _libvhd_io_map_get(oldfd); + + LOG("%s 0x%x 0x%x 0x%x\n", __func__, oldfd, newfd, flags); + + /* + * TODO: handle O_CLOEXEC... + */ + ret = _std_dup3(oldfd, newfd, flags); + if (ret != -1 && vhd_fd) + _libvhd_io_map_set(ret, vhd_fd); + + return ret; +} + +off_t lseek(int fd, off_t offset, int whence) +{ + off_t new_off; + vhd_fd_context_t *vhd_fd; + static off_t(*_std_lseek) (int, off_t, int); + + _RESOLVE(_std_lseek); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x 0x%lx 0x%x\n", __func__, fd, offset, whence); + + if (!vhd_fd) + return _std_lseek(fd, offset, whence); + + switch (whence) { + case SEEK_SET: + new_off = offset; + break; + case SEEK_CUR: + new_off = vhd_fd->off + offset; + break; + case SEEK_END: + new_off = (vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) + offset; + break; + default: + errno = EINVAL; + return (off_t) - 1; + } + + if (new_off < 0 || new_off > vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) { + errno = EINVAL; + return (off_t) - 1; + } + + vhd_fd->off = new_off; + return vhd_fd->off; +} + +off64_t lseek64(int fd, off64_t offset, int whence) +{ + off64_t new_off; + vhd_fd_context_t *vhd_fd; + static off64_t(*_std_lseek64) (int, off64_t, int); + + _RESOLVE(_std_lseek64); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x 0x%" PRIx64 " 0x%x\n", __func__, fd, offset, whence); + + if (!vhd_fd) + return _std_lseek64(fd, offset, whence); + + switch (whence) { + case SEEK_SET: + new_off = offset; + break; + case SEEK_CUR: + new_off = vhd_fd->off + offset; + break; + case SEEK_END: + new_off = (vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) + offset; + break; + default: + errno = EINVAL; + return (off64_t) - 1; + } + + if (new_off < 0 || new_off > vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT) { + errno = EINVAL; + return (off64_t) - 1; + } + + vhd_fd->off = new_off; + return vhd_fd->off; +} + +ssize_t read(int fd, void *buf, size_t count) +{ + ssize_t ret; + vhd_fd_context_t *vhd_fd; + static ssize_t(*_std_read) (int, void *, size_t); + + _RESOLVE(_std_read); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x %p 0x%zx\n", __func__, fd, buf, count); + + if (!vhd_fd) + return _std_read(fd, buf, count); + + ret = _libvhd_io_pread(&vhd_fd->vhd_part, buf, count, vhd_fd->off); + if (ret != -1) + vhd_fd->off += count; + + return ret; +} + +ssize_t write(int fd, const void *buf, size_t count) +{ + ssize_t ret; + vhd_fd_context_t *vhd_fd; + static ssize_t(*_std_write) (int, const void *, size_t); + + _RESOLVE(_std_write); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x %p 0x%zx\n", __func__, fd, buf, count); + + if (!vhd_fd) + return _std_write(fd, buf, count); + + ret = _libvhd_io_pwrite(&vhd_fd->vhd_part, buf, count, vhd_fd->off); + if (ret != -1) + vhd_fd->off += count; + + return ret; +} + +ssize_t pread(int fd, void *buf, size_t count, off_t offset) +{ + vhd_fd_context_t *vhd_fd; + static ssize_t(*_std_pread) (int, void *, size_t, off_t); + + _RESOLVE(_std_pread); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x %p 0x%zx 0x%lx\n", __func__, fd, buf, count, offset); + + if (!vhd_fd) + return _std_pread(fd, buf, count, offset); + + return _libvhd_io_pread(&vhd_fd->vhd_part, buf, count, offset); +} + +ssize_t pread64(int fd, void *buf, size_t count, off64_t offset) +{ + vhd_fd_context_t *vhd_fd; + static ssize_t(*_std_pread64) (int, void *, size_t, off64_t); + + _RESOLVE(_std_pread64); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x %p 0x%zx 0x%" PRIx64 "\n", __func__, fd, buf, count, + offset); + + if (!vhd_fd) + return _std_pread64(fd, buf, count, offset); + + return _libvhd_io_pread(&vhd_fd->vhd_part, buf, count, offset); +} + +ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) +{ + vhd_fd_context_t *vhd_fd; + static ssize_t(*_std_pwrite) (int, const void *, size_t, off_t); + + _RESOLVE(_std_pwrite); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x %p 0x%zx, 0x%lx\n", __func__, fd, buf, count, offset); + + if (!vhd_fd) + return _std_pwrite(fd, buf, count, offset); + + return _libvhd_io_pwrite(&vhd_fd->vhd_part, buf, count, offset); +} + +ssize_t pwrite64(int fd, const void *buf, size_t count, off64_t offset) +{ + vhd_fd_context_t *vhd_fd; + static ssize_t(*_std_pwrite64) (int, const void *, size_t, off64_t); + + _RESOLVE(_std_pwrite64); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x %p 0x%zx, 0x%" PRIx64 "\n", __func__, fd, buf, count, + offset); + + if (!vhd_fd) + return _std_pwrite64(fd, buf, count, offset); + + return _libvhd_io_pwrite(&vhd_fd->vhd_part, buf, count, offset); +} + +int fsync(int fd) +{ + vhd_fd_context_t *vhd_fd; + static int (*_std_fsync) (int); + + _RESOLVE(_std_fsync); + vhd_fd = _libvhd_io_map_get(fd); + if (!vhd_fd) + return _std_fsync(fd); + + LOG("%s 0x%x\n", __func__, fd); + + return _std_fsync(vhd_fd->vhd_part.vhd_obj->vhd.fd); +} + +int __xstat(int version, const char *path, struct stat *buf) +{ + int ret; + static int (*_std___xstat) (int, const char *, struct stat *); + + _RESOLVE(_std___xstat); + if (!_libvhd_io_interpose) + return _std___xstat(version, path, buf); + + LOG("%s 0x%x %s %p\n", __func__, version, path, buf); + + ret = _libvhd_io_stat(version, path, buf); + if (ret) + ret = _std___xstat(version, path, buf); + + return ret; +} + +int __xstat64(int version, const char *path, struct stat64 *buf) +{ + int ret; + static int (*_std___xstat64) (int, const char *, struct stat64 *); + + _RESOLVE(_std___xstat64); + if (!_libvhd_io_interpose) + return _std___xstat64(version, path, buf); + + LOG("%s 0x%x %s %p\n", __func__, version, path, buf); + + ret = _libvhd_io_stat64(version, path, buf); + if (ret) + ret = _std___xstat64(version, path, buf); + + + return ret; +} + +int __fxstat(int version, int fd, struct stat *buf) +{ + vhd_fd_context_t *vhd_fd; + static int (*_std___fxstat) (int, int, struct stat *); + + _RESOLVE(_std___fxstat); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x 0x%x %p\n", __func__, version, fd, buf); + + if (vhd_fd) + return _libvhd_io_fstat(version, &vhd_fd->vhd_part, buf); + else + return _std___fxstat(version, fd, buf); +} + +int __fxstat64(int version, int fd, struct stat64 *buf) +{ + vhd_fd_context_t *vhd_fd; + static int (*_std___fxstat64) (int, int, struct stat64 *); + + _RESOLVE(_std___fxstat64); + vhd_fd = _libvhd_io_map_get(fd); + + LOG("%s 0x%x 0x%x %p\n", __func__, version, fd, buf); + + if (vhd_fd) + return _libvhd_io_fstat64(version, &vhd_fd->vhd_part, buf); + else + return _std___fxstat64(version, fd, buf); +} + +/* + * NB: symlinks to vhds will be stat''ed rather than lstat''ed. + */ +int __lxstat(int version, const char *path, struct stat *buf) +{ + int ret; + static int (*_std___lxstat) (int, const char *, struct stat *); + + _RESOLVE(_std___lxstat); + if (!_libvhd_io_interpose) + return _std___lxstat(version, path, buf); + + LOG("%s 0x%x %s %p\n", __func__, version, path, buf); + + ret = _libvhd_io_stat(version, path, buf); + if (ret) + ret = _std___lxstat(version, path, buf); + + return ret; +} + +/* + * NB: symlinks to vhds will be stat''ed rather than lstat''ed. + */ +int __lxstat64(int version, const char *path, struct stat64 *buf) +{ + int ret; + static int (*_std___lxstat64) (int, const char *, struct stat64 *); + + _RESOLVE(_std___lxstat64); + if (!_libvhd_io_interpose) + return _std___lxstat64(version, path, buf); + + LOG("%s 0x%x %s %p\n", __func__, version, path, buf); + + ret = _libvhd_io_stat64(version, path, buf); + if (ret) + ret = _std___lxstat64(version, path, buf); + + return ret; +} + +int ioctl(int fd, int request, char *argp) +{ + vhd_fd_context_t *vhd_fd; + static int (*_std_ioctl) (int, int, char *); + + _RESOLVE(_std_ioctl); + vhd_fd = _libvhd_io_map_get(fd); + if (!vhd_fd) + return _std_ioctl(fd, request, argp); + + LOG("%s 0x%x 0x%x %p\n", __func__, fd, request, argp); + +#ifdef BLKGETSIZE64 + if (request == BLKGETSIZE64) { + uint64_t *size = (uint64_t *) argp; + *size = vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT; + return 0; + } +#endif +#ifdef BLKGETSIZE + if (request == BLKGETSIZE) { + unsigned long *size = (unsigned long *) argp; + *size = vhd_fd->vhd_part.size << VHD_SECTOR_SHIFT; + return 0; + } +#endif +#ifdef BLKSSZGET + if (request == BLKSSZGET) { + int *sec_size = (int *) argp; + *sec_size = VHD_SECTOR_SIZE; + return 0; + } +#endif +#ifdef HDIO_GETGEO + if (request == HDIO_GETGEO) { + vhd_context_t *vhd = &vhd_fd->vhd_part.vhd_obj->vhd; + struct hd_geometry *geo = (struct hd_geometry *) argp; + geo->heads = GEOM_GET_HEADS(vhd->footer.geometry); + geo->sectors = GEOM_GET_SPT(vhd->footer.geometry); + geo->cylinders = GEOM_GET_CYLS(vhd->footer.geometry); + geo->start = vhd_fd->vhd_part.start; + return 0; + } +#endif + + return _std_ioctl(fd, request, argp); +} + +int fcntl(int fd, int cmd, ...) +{ + int real_fd; + va_list args; + vhd_fd_context_t *vhd_fd; + static int (*_std_fcntl) (int, int, ...); + + _RESOLVE(_std_fcntl); + + real_fd = fd; + vhd_fd = _libvhd_io_map_get(fd); + if (vhd_fd) + real_fd = vhd_fd->vhd_part.vhd_obj->vhd.fd; + + LOG("%s 0x%x 0x%x\n", __func__, fd, cmd); + + switch (cmd) { + case F_GETFD: + case F_GETFL: + case F_GETOWN: + case F_GETSIG: + case F_GETLEASE: + LOG("%s 0x%x void\n", __func__, real_fd); + return _std_fcntl(real_fd, cmd); + + case F_DUPFD: +#ifdef F_DUPFD_CLOEXEC + case F_DUPFD_CLOEXEC: +#endif // F_DUPFD_CLOEXEC + case F_SETFD: + case F_SETFL: + case F_SETOWN: + case F_SETSIG: + case F_SETLEASE: + case F_NOTIFY: + { + long arg; + va_start(args, cmd); + arg = va_arg(args, long); + va_end(args); + LOG("%s 0x%x long 0x%lx\n", __func__, real_fd, arg); + return _std_fcntl(real_fd, cmd, arg); + } + + case F_SETLK: + case F_SETLKW: + case F_GETLK: + { + struct flock *flk; + va_start(args, cmd); + flk = va_arg(args, struct flock *); + va_end(args); + LOG("%s 0x%x lock %p\n", __func__, real_fd, flk); + return _std_fcntl(real_fd, cmd, flk); + } + +#if __WORDSIZE == 32 + case F_SETLK64: + case F_SETLKW64: + case F_GETLK64: + { + struct flock64 *flk; + va_start(args, cmd); + flk = va_arg(args, struct flock64 *); + va_end(args); + LOG("%s 0x%x lock64 %p (%p)\n", + __func__, real_fd, flk, _std_fcntl); + return _std_fcntl(real_fd, cmd, flk); + } +#endif + + default: + LOG("%s unrecognized cmd\n", __func__); + errno = EINVAL; + return -1; + } +} + +FILE *fopen(const char *path, const char *mode) +{ + FILE *f; + static _std_fopen_t _std_fopen; + + _RESOLVE(_std_fopen); + + if (!_libvhd_io_interpose || strchr(mode, ''w'')) + return _std_fopen(path, mode); + + f = _libvhd_io_fopen(path, mode); + + LOG("%s %s %s: 0x%x\n", __func__, path, mode, (f ? fileno(f) : -1)); + + return f; +} + +FILE *fopen64(const char *path, const char *mode) +{ + FILE *f; + static _std_fopen_t _std_fopen64; + + _RESOLVE(_std_fopen64); + + if (!_libvhd_io_interpose || strchr(mode, ''w'')) + return _std_fopen64(path, mode); + + f = _libvhd_io_fopen(path, mode); + + LOG("%s %s %s: 0x%x\n", __func__, path, mode, (f ? fileno(f) : -1)); + + return f; +} + +int _IO_getc(FILE * f) +{ + int cnt; + unsigned char c; + vhd_fd_context_t *vhd_fd; + static int (*_std__IO_getc) (FILE *); + + _RESOLVE(_std__IO_getc); + vhd_fd = _libvhd_io_map_get(fileno(f)); + if (!vhd_fd) + return _std__IO_getc(f); + + LOG("%s %p (0x%x)\n", __func__, f, fileno(f)); + cnt = _libvhd_io_pread(&vhd_fd->vhd_part, &c, sizeof(c), vhd_fd->off); + if (cnt > 0) + vhd_fd->off += cnt; + + return (int) c; +} + +#ifdef _IO_getc_unlocked +#undef _IO_getc_unlocked +#endif +int _IO_getc_unlocked(FILE * f) +{ + return _IO_getc(f); +} + +size_t fread(void *buf, size_t size, size_t n, FILE * f) +{ + ssize_t cnt; + vhd_fd_context_t *vhd_fd; + static size_t(*_std_fread) (void *, size_t, size_t, FILE *); + + _RESOLVE(_std_fread); + vhd_fd = _libvhd_io_map_get(fileno(f)); + if (!vhd_fd) + return _std_fread(buf, size, n, f); + + LOG("%s %p 0x%zx 0x%zx %p (0x%x)\n", + __func__, buf, size, n, f, fileno(f)); + cnt = _libvhd_io_pread(&vhd_fd->vhd_part, buf, n * size, vhd_fd->off); + if (cnt > 0) { + vhd_fd->off += cnt; + cnt /= size; + } + + return cnt; +} + +#ifdef fread_unlocked +#undef fread_unlocked +#endif +size_t fread_unlocked(void *buf, size_t size, size_t n, FILE * f) +{ + return fread(buf, size, n, f); +} + +/* + * sigh... preloading with bash causes problems, since bash has its own + * malloc(), memalign(), and free() functions, but no posix_memalign(). + * this causes problems when libvhd free()''s posix_memalign()''ed memory. + */ +#define _libvhd_power_of_2(x) ((((x) - 1) & (x)) == 0) +int posix_memalign(void **memptr, size_t alignment, size_t size) +{ + if (!alignment || alignment % sizeof(void *) || + !_libvhd_power_of_2(alignment / sizeof(void *))) + return EINVAL; + + *memptr = memalign(alignment, size); + if (!*memptr) + return ENOMEM; + + return 0; +}
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 3 of 8 RESEND] blktap3/vhd: Introduce libvhd-index.c
This patch introduces libvhd-index.c, imported from blktap2.5. It seems to contain definitions for the VHD on-file header as well as functionality for accessing/manipulating it. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap3/vhd/lib/libvhd-index.c b/tools/blktap3/vhd/lib/libvhd-index.c new file mode 100644 --- /dev/null +++ b/tools/blktap3/vhd/lib/libvhd-index.c @@ -0,0 +1,1198 @@ +/* + * Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of XenSource Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include "libvhd.h" +#include "libvhd-index.h" +#include "relative-path.h" + +typedef struct vhdi_path vhdi_path_t; +typedef struct vhdi_header vhdi_header_t; +typedef struct vhdi_bat_header vhdi_bat_header_t; +typedef struct vhdi_file_table_header vhdi_file_table_header_t; +typedef struct vhdi_file_table_entry vhdi_file_table_entry_t; + +static const char VHDI_HEADER_COOKIE[] = "vhdindex"; +static const char VHDI_BAT_HEADER_COOKIE[] = "vhdi-bat"; +static const char VHDI_FILE_TABLE_HEADER_COOKIE[] = "vhdifile"; + +struct vhdi_path { + char path[VHD_MAX_NAME_LEN]; + uint16_t bytes; +}; + +struct vhdi_header { + char cookie[8]; + uint32_t vhd_block_size; + uint64_t table_offset; +}; + +struct vhdi_bat_header { + char cookie[8]; + uint64_t vhd_blocks; + uint32_t vhd_block_size; + vhdi_path_t vhd_path; + vhdi_path_t index_path; + vhdi_path_t file_table_path; + uint64_t table_offset; +}; + +struct vhdi_file_table_header { + char cookie[8]; + uint32_t files; + uint64_t table_offset; +}; + +struct vhdi_file_table_entry { + vhdi_path_t p; + vhdi_file_id_t file_id; + uuid_t vhd_uuid; + uint32_t vhd_timestamp; +}; + +static inline int vhdi_seek(vhdi_context_t * ctx, off64_t off, int whence) +{ + int err; + + err = lseek64(ctx->fd, off, whence); + if (err == (off64_t) - 1) + return -errno; + + return 0; +} + +static inline off64_t vhdi_position(vhdi_context_t * ctx) +{ + return lseek64(ctx->fd, 0, SEEK_CUR); +} + +static inline int vhdi_read(vhdi_context_t * ctx, void *buf, size_t size) +{ + int err; + + err = read(ctx->fd, buf, size); + if (err != size) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline int vhdi_write(vhdi_context_t * ctx, void *buf, size_t size) +{ + int err; + + err = write(ctx->fd, buf, size); + if (err != size) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline int vhdi_check_block_size(uint32_t block_size) +{ + int i, cnt; + + cnt = 0; + for (i = 0; i < 32; i++) + if ((block_size >> i) & 0x0001) + cnt++; + + if (cnt == 1) + return 0; + + return -EINVAL; +} + +static inline void vhdi_header_in(vhdi_header_t * header) +{ + BE32_IN(&header->vhd_block_size); + BE64_IN(&header->table_offset); +} + +static inline void vhdi_header_out(vhdi_header_t * header) +{ + BE32_OUT(&header->vhd_block_size); + BE64_OUT(&header->table_offset); +} + +static inline int vhdi_header_validate(vhdi_header_t * header) +{ + if (memcmp(header->cookie, VHDI_HEADER_COOKIE, sizeof(header->cookie))) + return -EINVAL; + + return vhdi_check_block_size(header->vhd_block_size); +} + +void vhdi_entry_in(vhdi_entry_t * entry) +{ + BE32_IN(&entry->file_id); + BE32_IN(&entry->offset); +} + +static inline vhdi_entry_t vhdi_entry_out(vhdi_entry_t * entry) +{ + vhdi_entry_t e; + + e = *entry; + BE32_OUT(&e.file_id); + BE32_OUT(&e.offset); + + return e; +} + +static inline void vhdi_path_in(vhdi_path_t * path) +{ + BE16_IN(&path->bytes); +} + +static inline void vhdi_path_out(vhdi_path_t * path) +{ + BE16_OUT(&path->bytes); +} + +static inline void vhdi_bat_header_in(vhdi_bat_header_t * header) +{ + BE64_IN(&header->vhd_blocks); + BE32_IN(&header->vhd_block_size); + vhdi_path_in(&header->vhd_path); + vhdi_path_in(&header->index_path); + vhdi_path_in(&header->file_table_path); + BE64_IN(&header->table_offset); +} + +static inline void vhdi_bat_header_out(vhdi_bat_header_t * header) +{ + BE64_OUT(&header->vhd_blocks); + BE32_OUT(&header->vhd_block_size); + vhdi_path_out(&header->vhd_path); + vhdi_path_out(&header->index_path); + vhdi_path_out(&header->file_table_path); + BE64_OUT(&header->table_offset); +} + +static inline int vhdi_path_validate(vhdi_path_t * path) +{ + int i; + + if (path->bytes >= VHD_MAX_NAME_LEN - 1) + return -ENAMETOOLONG; + + for (i = 0; i < path->bytes; i++) + if (path->path[i] == ''\0'') + return 0; + + return -EINVAL; +} + +static inline char *vhdi_path_expand(const char *src, vhdi_path_t * dest, + int *err) +{ + int len; + char *path, *base, copy[VHD_MAX_NAME_LEN]; + char *absolute_path, __absolute_path[PATH_MAX]; + + strcpy(copy, src); + base = dirname(copy); + + *err = asprintf(&path, "%s/%s", base, dest->path); + if (*err == -1) { + *err = -ENOMEM; + return NULL; + } + + absolute_path = realpath(path, __absolute_path); + free(path); + if (absolute_path) + absolute_path = strdup(absolute_path); + if (!absolute_path) { + *err = -errno; + return NULL; + } + + len = strnlen(absolute_path, VHD_MAX_NAME_LEN - 1); + if (len == VHD_MAX_NAME_LEN - 1) { + free(absolute_path); + *err = -ENAMETOOLONG; + return NULL; + } + + *err = 0; + return absolute_path; +} + +static inline int vhdi_bat_header_validate(vhdi_bat_header_t * header) +{ + int err; + + if (memcmp(header->cookie, + VHDI_BAT_HEADER_COOKIE, sizeof(header->cookie))) + return -EINVAL; + + err = vhdi_check_block_size(header->vhd_block_size); + if (err) + return err; + + err = vhdi_path_validate(&header->vhd_path); + if (err) + return err; + + err = vhdi_path_validate(&header->index_path); + if (err) + return err; + + err = vhdi_path_validate(&header->file_table_path); + if (err) + return err; + + return 0; +} + +static inline int vhdi_bat_load_header(int fd, vhdi_bat_header_t * header) +{ + int err; + + err = lseek64(fd, 0, SEEK_SET); + if (err == (off64_t) - 1) + return -errno; + + err = read(fd, header, sizeof(vhdi_bat_header_t)); + if (err != sizeof(vhdi_bat_header_t)) + return (errno ? -errno : -EIO); + + vhdi_bat_header_in(header); + return vhdi_bat_header_validate(header); +} + +static inline void +vhdi_file_table_header_in(vhdi_file_table_header_t * header) +{ + BE32_OUT(&header->files); + BE64_OUT(&header->table_offset); +} + +static inline void +vhdi_file_table_header_out(vhdi_file_table_header_t * header) +{ + BE32_OUT(&header->files); + BE64_OUT(&header->table_offset); +} + +static inline int +vhdi_file_table_header_validate(vhdi_file_table_header_t * header) +{ + if (memcmp(header->cookie, + VHDI_FILE_TABLE_HEADER_COOKIE, sizeof(header->cookie))) + return -EINVAL; + + return 0; +} + +static inline int +vhdi_file_table_load_header(int fd, vhdi_file_table_header_t * header) +{ + int err; + + err = lseek64(fd, 0, SEEK_SET); + if (err == (off64_t) - 1) + return -errno; + + err = read(fd, header, sizeof(vhdi_file_table_header_t)); + if (err != sizeof(vhdi_file_table_header_t)) + return (errno ? -errno : -EIO); + + vhdi_file_table_header_in(header); + return vhdi_file_table_header_validate(header); +} + +static inline int +vhdi_file_table_write_header(int fd, vhdi_file_table_header_t * header) +{ + int err; + + err = lseek64(fd, 0, SEEK_SET); + if (err == (off64_t) - 1) + return -errno; + + err = vhdi_file_table_header_validate(header); + if (err) + return err; + + vhdi_file_table_header_out(header); + + err = write(fd, header, sizeof(vhdi_file_table_header_t)); + if (err != sizeof(vhdi_file_table_header_t)) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline void +vhdi_file_table_entry_in(vhdi_file_table_entry_t * entry) +{ + vhdi_path_in(&entry->p); + BE32_IN(&entry->file_id); + BE32_IN(&entry->vhd_timestamp); +} + +static inline void +vhdi_file_table_entry_out(vhdi_file_table_entry_t * entry) +{ + vhdi_path_out(&entry->p); + BE32_OUT(&entry->file_id); + BE32_OUT(&entry->vhd_timestamp); +} + +static inline int +vhdi_file_table_entry_validate(vhdi_file_table_entry_t * entry) +{ + return vhdi_path_validate(&entry->p); +} + +static inline int +vhdi_file_table_entry_validate_vhd(vhdi_file_table_entry_t * entry, + const char *path) +{ + int err; + vhd_context_t vhd; + struct stat stats; + + err = stat(path, &stats); + if (err == -1) + return -errno; + + if (entry->vhd_timestamp != vhd_time(stats.st_mtime)) + return -EINVAL; + + err = vhd_open(&vhd, path, VHD_OPEN_RDONLY); + if (err) + return err; + + err = vhd_get_footer(&vhd); + if (err) + goto out; + + if (uuid_compare(entry->vhd_uuid, vhd.footer.uuid)) { + err = -EINVAL; + goto out; + } + + out: + vhd_close(&vhd); + return err; +} + +int vhdi_create(const char *name, uint32_t vhd_block_size) +{ + void *buf; + int err, fd; + size_t size; + vhdi_header_t header; + + memset(&header, 0, sizeof(vhdi_header_t)); + + err = vhdi_check_block_size(vhd_block_size); + if (err) + return err; + + err = access(name, F_OK); + if (!err || errno != ENOENT) + return (err ? err : -EEXIST); + + memcpy(header.cookie, VHDI_HEADER_COOKIE, sizeof(header.cookie)); + header.vhd_block_size = vhd_block_size; + header.table_offset = vhd_bytes_padded(sizeof(vhdi_header_t)); + + err = vhdi_header_validate(&header); + if (err) + return err; + + vhdi_header_out(&header); + + size = vhd_bytes_padded(sizeof(vhdi_header_t)); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + memset(buf, 0, size); + memcpy(buf, &header, sizeof(vhdi_header_t)); + + fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600); + if (fd == -1) + return -errno; + + err = write(fd, buf, size); + if (err != size) { + err = (errno ? -errno : -EIO); + goto fail; + } + + close(fd); + free(buf); + + return 0; + + fail: + close(fd); + free(buf); + unlink(name); + return err; +} + +int vhdi_open(vhdi_context_t * ctx, const char *file, int flags) +{ + int err, fd; + size_t size; + char *name; + void *buf; + vhdi_header_t header; + + buf = NULL; + memset(ctx, 0, sizeof(vhdi_context_t)); + + name = strdup(file); + if (!name) + return -ENOMEM; + + fd = open(file, flags | O_LARGEFILE); + if (fd == -1) { + free(name); + return -errno; + } + + size = vhd_bytes_padded(sizeof(vhdi_header_t)); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + goto fail; + } + + err = read(fd, buf, size); + if (err != size) { + err = (errno ? -errno : -EIO); + goto fail; + } + + memcpy(&header, buf, sizeof(vhdi_header_t)); + free(buf); + buf = NULL; + + vhdi_header_in(&header); + err = vhdi_header_validate(&header); + if (err) + goto fail; + + ctx->fd = fd; + ctx->name = name; + ctx->spb = header.vhd_block_size >> VHD_SECTOR_SHIFT; + ctx->vhd_block_size = header.vhd_block_size; + + return 0; + + fail: + close(fd); + free(buf); + free(name); + return err; +} + +void vhdi_close(vhdi_context_t * ctx) +{ + close(ctx->fd); + free(ctx->name); +} + +int +vhdi_read_block(vhdi_context_t * ctx, vhdi_block_t * block, + uint32_t sector) +{ + int i, err; + size_t size; + void *tab; + + err = vhdi_seek(ctx, vhd_sectors_to_bytes(sector), SEEK_SET); + if (err) + return err; + + size = vhd_bytes_padded(ctx->spb * sizeof(vhdi_entry_t)); + + block->entries = ctx->spb; + err = posix_memalign(&tab, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + block->table = tab; + + err = vhdi_read(ctx, block->table, size); + if (err) + goto fail; + + for (i = 0; i < block->entries; i++) + vhdi_entry_in(&block->table[i]); + + return 0; + + fail: + free(block->table); + return err; +} + +int +vhdi_write_block(vhdi_context_t * ctx, vhdi_block_t * block, + uint32_t sector) +{ + void *buf; + int i, err; + size_t size; + vhdi_entry_t *entries; + + err = vhdi_seek(ctx, vhd_sectors_to_bytes(sector), SEEK_SET); + if (err) + return err; + + size = vhd_bytes_padded(ctx->spb * sizeof(vhdi_entry_t)); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + memset(buf, 0, size); + entries = (vhdi_entry_t *) buf; + + for (i = 0; i < block->entries; i++) + entries[i] = vhdi_entry_out(&block->table[i]); + + err = vhdi_write(ctx, entries, size); + if (err) + goto out; + + err = 0; + + out: + free(entries); + return err; +} + +int +vhdi_append_block(vhdi_context_t * ctx, vhdi_block_t * block, + uint32_t * sector) +{ + void *buf; + int i, err; + off64_t off; + size_t size; + vhdi_entry_t *entries; + + err = vhdi_seek(ctx, 0, SEEK_END); + if (err) + return err; + + off = vhd_bytes_padded(vhdi_position(ctx)); + + err = vhdi_seek(ctx, off, SEEK_SET); + if (err) + return err; + + size = vhd_bytes_padded(block->entries * sizeof(vhdi_entry_t)); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; + + memset(buf, 0, size); + entries = buf; + + for (i = 0; i < block->entries; i++) + entries[i] = vhdi_entry_out(&block->table[i]); + + err = vhdi_write(ctx, entries, block->entries * sizeof(vhdi_entry_t)); + if (err) + goto out; + + err = 0; + *sector = off >> VHD_SECTOR_SHIFT; + out: + if (err) { + int gcc = ftruncate(ctx->fd, off); + if (gcc) { + } + } + free(entries); + return err; +} + +static int +vhdi_copy_path_to(vhdi_path_t * path, const char *src, const char *dest) +{ + int len, err; + char *file, *relative_path, copy[VHD_MAX_NAME_LEN]; + char *absolute_path, __absolute_path[PATH_MAX]; + + strcpy(copy, dest); + + file = basename(copy); + absolute_path = realpath(copy, __absolute_path); + relative_path = NULL; + + if (!absolute_path) { + err = -errno; + goto out; + } + + if (!strcmp(file, "")) { + err = -EINVAL; + goto out; + } + + relative_path = relative_path_to((char *) src, absolute_path, &err); + if (!relative_path || err) { + err = (err ? err : -EINVAL); + goto out; + } + + len = strnlen(relative_path, VHD_MAX_NAME_LEN - 1); + if (len == VHD_MAX_NAME_LEN - 1) { + err = -ENAMETOOLONG; + goto out; + } + + strcpy(path->path, relative_path); + path->bytes = len + 1; + + err = 0; + + out: + free(relative_path); + return err; +} + +int +vhdi_bat_create(const char *name, const char *vhd, + const char *index, const char *file_table) +{ + int err, fd; + off64_t off; + vhd_context_t ctx; + vhdi_bat_header_t header; + + memset(&header, 0, sizeof(vhdi_bat_header_t)); + + err = access(name, F_OK); + if (!err || errno != ENOENT) + return (err ? -err : -EEXIST); + + err = vhd_open(&ctx, vhd, VHD_OPEN_RDONLY); + if (err) + return err; + + err = vhd_get_header(&ctx); + if (err) { + vhd_close(&ctx); + return err; + } + + header.vhd_blocks = ctx.header.max_bat_size; + header.vhd_block_size = ctx.header.block_size; + + vhd_close(&ctx); + + fd = open(name, O_CREAT | O_TRUNC | O_RDWR, 0600); + if (fd == -1) + return -errno; + + err = vhdi_copy_path_to(&header.vhd_path, name, vhd); + if (err) + goto fail; + + err = vhdi_copy_path_to(&header.index_path, name, index); + if (err) + goto fail; + + err = vhdi_copy_path_to(&header.file_table_path, name, file_table); + if (err) + goto fail; + + off = vhd_bytes_padded(sizeof(vhdi_bat_header_t)); + + header.table_offset = off; + memcpy(header.cookie, VHDI_BAT_HEADER_COOKIE, sizeof(header.cookie)); + + err = vhdi_bat_header_validate(&header); + if (err) + goto fail; + + vhdi_bat_header_out(&header); + + err = write(fd, &header, sizeof(vhdi_bat_header_t)); + if (err != sizeof(vhdi_bat_header_t)) { + err = (errno ? -errno : -EIO); + goto fail; + } + + close(fd); + return 0; + + fail: + close(fd); + unlink(name); + return err; +} + +int vhdi_bat_load(const char *name, vhdi_bat_t * bat) +{ + char *path; + int err, fd; + size_t size; + uint32_t *table; + vhdi_bat_header_t header; + + table = NULL; + + fd = open(name, O_RDONLY | O_LARGEFILE); + if (fd == -1) + return -errno; + + err = vhdi_bat_load_header(fd, &header); + if (err) + goto out; + + size = header.vhd_blocks * sizeof(uint32_t); + table = malloc(size); + if (!table) { + err = -ENOMEM; + goto out; + } + + err = lseek64(fd, header.table_offset, SEEK_SET); + if (err == (off64_t) - 1) { + err = -errno; + goto out; + } + + err = read(fd, table, size); + if (err != size) { + err = (errno ? -errno : -EIO); + goto out; + } + + path = vhdi_path_expand(name, &header.vhd_path, &err); + if (err) + goto out; + strcpy(bat->vhd_path, path); + free(path); + + err = access(bat->vhd_path, F_OK); + if (err == -1) { + err = -errno; + goto out; + } + + path = vhdi_path_expand(name, &header.index_path, &err); + if (err) + goto out; + strcpy(bat->index_path, path); + free(path); + + err = access(bat->index_path, F_OK); + if (err == -1) { + err = -errno; + goto out; + } + + path = vhdi_path_expand(name, &header.file_table_path, &err); + if (err) + goto out; + strcpy(bat->file_table_path, path); + free(path); + + err = access(bat->file_table_path, F_OK); + if (err == -1) { + err = -errno; + goto out; + } + + bat->vhd_blocks = header.vhd_blocks; + bat->vhd_block_size = header.vhd_block_size; + bat->table = table; + + err = 0; + + out: + close(fd); + if (err) { + free(table); + memset(bat, 0, sizeof(vhdi_bat_t)); + } + + return err; +} + +int vhdi_bat_write(const char *name, vhdi_bat_t * bat) +{ + int err, fd; + size_t size; + vhdi_bat_header_t header; + + fd = open(name, O_RDWR | O_LARGEFILE); + if (fd == -1) + return -errno; + + err = vhdi_bat_load_header(fd, &header); + if (err) + goto out; + + if (header.vhd_blocks != bat->vhd_blocks || + header.vhd_block_size != bat->vhd_block_size) { + err = -EINVAL; + goto out; + } + + err = lseek64(fd, header.table_offset, SEEK_SET); + if (err == (off64_t) - 1) { + err = -errno; + goto out; + } + + size = bat->vhd_blocks * sizeof(uint32_t); + err = write(fd, bat->table, size); + if (err != size) { + err = (errno ? -errno : -EIO); + goto out; + } + + err = 0; + + out: + close(fd); + return err; +} + +int vhdi_file_table_create(const char *file) +{ + int err, fd; + off64_t off; + vhdi_file_table_header_t header; + + memset(&header, 0, sizeof(vhdi_file_table_header_t)); + + err = access(file, F_OK); + if (!err || errno != ENOENT) + return (err ? err : -EEXIST); + + off = vhd_bytes_padded(sizeof(vhdi_file_table_header_t)); + + header.files = 0; + header.table_offset = off; + memcpy(header.cookie, + VHDI_FILE_TABLE_HEADER_COOKIE, sizeof(header.cookie)); + + vhdi_file_table_header_out(&header); + + fd = open(file, O_CREAT | O_TRUNC | O_RDWR, 0600); + if (fd == -1) + return -errno; + + err = write(fd, &header, sizeof(vhdi_file_table_header_t)); + if (err != sizeof(vhdi_file_table_header_t)) { + err = (errno ? -errno : -EIO); + goto out; + } + + err = 0; + + out: + close(fd); + return err; +} + +int vhdi_file_table_load(const char *name, vhdi_file_table_t * table) +{ + off64_t off; + size_t size; + int err, i, fd; + vhdi_file_table_header_t header; + vhdi_file_table_entry_t *entries; + + entries = NULL; + + fd = open(name, O_RDONLY | O_LARGEFILE); + if (fd == -1) + return -errno; + + err = vhdi_file_table_load_header(fd, &header); + if (err) + goto out; + + if (!header.files) + goto out; + + table->table = calloc(header.files, sizeof(vhdi_file_ref_t)); + if (!table->table) { + err = -ENOMEM; + goto out; + } + + off = header.table_offset; + err = lseek64(fd, off, SEEK_SET); + if (err == (off64_t) - 1) { + err = -errno; + goto out; + } + + size = header.files * sizeof(vhdi_file_table_entry_t); + entries = calloc(header.files, sizeof(vhdi_file_table_entry_t)); + if (!entries) { + err = -ENOMEM; + goto out; + } + + err = read(fd, entries, size); + if (err != size) { + err = (errno ? -errno : -EIO); + goto out; + } + + for (i = 0; i < header.files; i++) { + vhdi_file_table_entry_t *entry; + + entry = entries + i; + vhdi_file_table_entry_in(entry); + + err = vhdi_file_table_entry_validate(entry); + if (err) + goto out; + + table->table[i].path = vhdi_path_expand(name, &entry->p, &err); + if (err) + goto out; + + err = vhdi_file_table_entry_validate_vhd(entry, + table->table[i].path); + if (err) + goto out; + + table->table[i].file_id = entry->file_id; + table->table[i].vhd_timestamp = entry->vhd_timestamp; + uuid_copy(table->table[i].vhd_uuid, entry->vhd_uuid); + } + + err = 0; + table->entries = header.files; + + out: + close(fd); + free(entries); + if (err) { + if (table->table) { + for (i = 0; i < header.files; i++) + free(table->table[i].path); + free(table->table); + } + memset(table, 0, sizeof(vhdi_file_table_t)); + } + + return err; +} + +static int +vhdi_file_table_next_fid(const char *name, + const char *file, vhdi_file_id_t * fid) +{ + int i, err; + char *path, __path[PATH_MAX]; + vhdi_file_id_t max; + vhdi_file_table_t files; + + max = 0; + path = NULL; + + err = vhdi_file_table_load(name, &files); + if (err) + return err; + + path = realpath(file, __path); + if (!path) { + err = -errno; + goto out; + } + + for (i = 0; i < files.entries; i++) { + if (!strcmp(path, files.table[i].path)) { + err = -EEXIST; + goto out; + } + + max = MAX(max, files.table[i].file_id); + } + + *fid = max + 1; + err = 0; + + out: + vhdi_file_table_free(&files); + + return err; +} + +static inline int +vhdi_file_table_entry_initialize(vhdi_file_table_entry_t * entry, + const char *file_table, const char *file, + vhdi_file_id_t fid) +{ + int err; + struct stat stats; + vhd_context_t vhd; + + memset(entry, 0, sizeof(vhdi_file_table_entry_t)); + + err = stat(file, &stats); + if (err == -1) + return -errno; + + entry->file_id = fid; + entry->vhd_timestamp = vhd_time(stats.st_mtime); + + err = vhd_open(&vhd, file, VHD_OPEN_RDONLY); + if (err) + goto out; + + err = vhd_get_footer(&vhd); + if (err) { + vhd_close(&vhd); + goto out; + } + + uuid_copy(entry->vhd_uuid, vhd.footer.uuid); + + vhd_close(&vhd); + + err = vhdi_copy_path_to(&entry->p, file_table, file); + if (err) + goto out; + + err = 0; + + out: + if (err) + memset(entry, 0, sizeof(vhdi_file_table_entry_t)); + + return err; +} + +int +vhdi_file_table_add(const char *name, const char *file, + vhdi_file_id_t * _fid) +{ + off64_t off; + size_t size; + vhdi_file_id_t fid; + int err, fd, len; + vhdi_file_table_entry_t entry; + vhdi_file_table_header_t header; + + off = 0; + fid = 0; + *_fid = 0; + + len = strnlen(file, VHD_MAX_NAME_LEN - 1); + if (len == VHD_MAX_NAME_LEN - 1) + return -ENAMETOOLONG; + + err = vhdi_file_table_next_fid(name, file, &fid); + if (err) + return err; + + fd = open(name, O_RDWR | O_LARGEFILE); + if (fd == -1) + return -errno; + + err = vhdi_file_table_load_header(fd, &header); + if (err) + goto out; + + size = sizeof(vhdi_file_table_entry_t); + off = header.table_offset + size * header.files; + + err = lseek64(fd, off, SEEK_SET); + if (err == (off64_t) - 1) { + err = -errno; + goto out; + } + + err = vhdi_file_table_entry_initialize(&entry, name, file, fid); + if (err) + goto out; + + vhdi_file_table_entry_out(&entry); + + err = write(fd, &entry, size); + if (err != size) { + err = (errno ? -errno : -EIO); + goto out; + } + + header.files++; + err = vhdi_file_table_write_header(fd, &header); + if (err) + goto out; + + err = 0; + *_fid = fid; + + out: + if (err && off) { + int gcc = ftruncate(fd, off); + if (gcc) { + }; + } + close(fd); + + return err; +} + +void vhdi_file_table_free(vhdi_file_table_t * table) +{ + int i; + + if (table->table) { + for (i = 0; i < table->entries; i++) + free(table->table[i].path); + free(table->table); + } + + memset(table, 0, sizeof(vhdi_file_table_t)); +}
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 4 of 8 RESEND] blktap3/vhd: Introduce core VHD library
This patch copies the core of the VHD functionality from blktap2, with most changes coming from blktap2.5. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap2/vhd/lib/libvhd.c b/tools/blktap3/vhd/lib/libvhd.c copy from tools/blktap2/vhd/lib/libvhd.c copy to tools/blktap3/vhd/lib/libvhd.c --- a/tools/blktap2/vhd/lib/libvhd.c +++ b/tools/blktap3/vhd/lib/libvhd.c @@ -1,4 +1,5 @@ /* Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,12 +36,16 @@ #include <string.h> #include <libgen.h> #include <iconv.h> +#include <limits.h> #include <sys/mman.h> #include <sys/stat.h> +#include <sys/types.h> #include "libvhd.h" #include "relative-path.h" +#define VHD_HEADER_MAX_RETRIES 10 + static int libvhd_dbg = 0; void @@ -57,7 +62,13 @@ libvhd_set_log_level(int level) __func__, ##_a); \ } while (0) -#define BIT_MASK 0x80 +#define ASSERT(_p) \ + if (!(_p)) { \ + libvhd_set_log_level(1); \ + VHDLOG("%s:%d: FAILED ASSERTION: ''%s''\n", \ + __FILE__, __LINE__, #_p); \ + *(int*)0 = 0; \ + } #ifdef ENABLE_FAILURE_TESTING const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = { @@ -69,26 +80,15 @@ const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", "VHD_UTIL_TEST_FAIL_RESIZE_END" }; + int TEST_FAIL[NUM_FAIL_TESTS]; #endif // ENABLE_FAILURE_TESTING -static inline int -test_bit (volatile char *addr, int nr) -{ - return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0; -} - -static inline void -set_bit (volatile char *addr, int nr) -{ - addr[nr >> 3] |= (BIT_MASK >> (nr & 7)); -} - -static inline void -clear_bit (volatile char *addr, int nr) -{ - addr[nr >> 3] &= ~(BIT_MASK >> (nr & 7)); -} +static void vhd_cache_init(vhd_context_t *); +static int vhd_cache_enabled(vhd_context_t *); +static int vhd_cache_load(vhd_context_t *); +static int vhd_cache_unload(vhd_context_t *); +static vhd_context_t *vhd_cache_get_parent(vhd_context_t *); static inline int old_test_bit(volatile char *addr, int nr) @@ -251,8 +251,8 @@ vhd_validate_footer(vhd_footer_t *footer if (memcmp(footer->cookie, HD_COOKIE, csize) != 0 && memcmp(footer->cookie, VHD_POISON_COOKIE, csize) != 0) { char buf[9]; - strncpy(buf, footer->cookie, sizeof(buf)); - buf[sizeof(buf)-1]= ''\0''; + memcpy(buf, footer->cookie, 8); + buf[8] = ''\0''; VHDLOG("invalid footer cookie: %s\n", buf); return -EINVAL; } @@ -312,8 +312,8 @@ vhd_validate_header(vhd_header_t *header if (memcmp(header->cookie, DD_COOKIE, 8) != 0) { char buf[9]; - strncpy(buf, header->cookie, sizeof(buf)); - buf[sizeof(buf)-1]= ''\0''; + memcpy(buf, header->cookie, 8); + buf[8] = ''\0''; VHDLOG("invalid header cookie: %s\n", buf); return -EINVAL; } @@ -323,8 +323,8 @@ vhd_validate_header(vhd_header_t *header return -EINVAL; } - if (header->data_offset != 0xFFFFFFFFFFFFFFFF) { - VHDLOG("invalid header data_offset 0x%016"PRIx64"\n", + if (header->data_offset != 0xFFFFFFFFFFFFFFFFULL) { + VHDLOG("invalid header data_offset 0x%016" PRIx64 "\n", header->data_offset); return -EINVAL; } @@ -355,18 +355,22 @@ vhd_validate_bat(vhd_bat_t *bat) } uint32_t -vhd_checksum_batmap(vhd_batmap_t *batmap) +vhd_checksum_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) { - int i, n; + int i; char *blob; uint32_t checksum; + size_t map_size; blob = batmap->map; checksum = 0; - n = vhd_sectors_to_bytes(batmap->header.batmap_size); - - for (i = 0; i < n; i++) { + map_size + vhd_sectors_to_bytes(secs_round_up_no_zero + (ctx->footer. + curr_size >> (VHD_BLOCK_SHIFT + 3))); + + for (i = 0; i < map_size; i++) { if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1)) checksum += (uint32_t)blob[i]; else @@ -389,14 +393,14 @@ vhd_validate_batmap_header(vhd_batmap_t } int -vhd_validate_batmap(vhd_batmap_t *batmap) +vhd_validate_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) { uint32_t checksum; if (!batmap->map) return -EINVAL; - checksum = vhd_checksum_batmap(batmap); + checksum = vhd_checksum_batmap(ctx, batmap); if (checksum != batmap->header.checksum) return -EINVAL; @@ -404,9 +408,9 @@ vhd_validate_batmap(vhd_batmap_t *batmap } int -vhd_batmap_header_offset(vhd_context_t *ctx, off_t *_off) +vhd_batmap_header_offset(vhd_context_t *ctx, off64_t *_off) { - off_t off; + off64_t off; size_t bat; *_off = 0; @@ -593,11 +597,11 @@ vhd_bitmap_clear(vhd_context_t *ctx, cha * byte of the file which is not vhd metadata */ int -vhd_end_of_headers(vhd_context_t *ctx, off_t *end) +vhd_end_of_headers(vhd_context_t *ctx, off64_t *end) { int err, i, n; uint32_t bat_bytes; - off_t eom, bat_end; + off64_t eom, bat_end; vhd_parent_locator_t *loc; *end = 0; @@ -613,7 +617,7 @@ vhd_end_of_headers(vhd_context_t *ctx, o eom = MAX(eom, bat_end); if (vhd_has_batmap(ctx)) { - off_t hdr_end, hdr_secs, map_end, map_secs; + off64_t hdr_end, hdr_secs, map_end, map_secs; err = vhd_get_batmap(ctx); if (err) @@ -637,7 +641,7 @@ vhd_end_of_headers(vhd_context_t *ctx, o n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t); for (i = 0; i < n; i++) { - off_t loc_end; + off64_t loc_end; loc = &ctx->header.loc[i]; if (loc->code == PLAT_CODE_NONE) @@ -652,10 +656,10 @@ vhd_end_of_headers(vhd_context_t *ctx, o } int -vhd_end_of_data(vhd_context_t *ctx, off_t *end) +vhd_end_of_data(vhd_context_t *ctx, off64_t *end) { int i, err; - off_t max; + off64_t max; uint64_t blk; if (!vhd_type_dynamic(ctx)) { @@ -664,7 +668,7 @@ vhd_end_of_data(vhd_context_t *ctx, off_ return err; max = vhd_position(ctx); - if (max == (off_t)-1) + if (max == (off64_t) - 1) return -errno; *end = max - sizeof(vhd_footer_t); @@ -822,7 +826,7 @@ vhd_get_batmap(vhd_context_t *ctx) if (!vhd_has_batmap(ctx)) return -EINVAL; - if (!vhd_validate_batmap(&ctx->batmap)) + if (!vhd_validate_batmap(ctx, &ctx->batmap)) return 0; vhd_put_batmap(ctx); @@ -871,8 +875,8 @@ int vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer) { int err; - char *buf; - off_t eof; + void *buf; + off64_t eof; buf = NULL; @@ -881,7 +885,7 @@ vhd_read_short_footer(vhd_context_t *ctx goto out; eof = vhd_position(ctx); - if (eof == (off_t)-1) { + if (eof == (off64_t) - 1) { err = -errno; goto out; } @@ -890,7 +894,7 @@ vhd_read_short_footer(vhd_context_t *ctx if (err) goto out; - err = posix_memalign((void **)&buf, + err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); if (err) { buf = NULL; @@ -919,10 +923,10 @@ out: } int -vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off) +vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off) { + void *buf; int err; - char *buf; buf = NULL; @@ -930,7 +934,7 @@ vhd_read_footer_at(vhd_context_t *ctx, v if (err) goto out; - err = posix_memalign((void **)&buf, + err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); if (err) { buf = NULL; @@ -959,14 +963,14 @@ int vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer) { int err; - off_t off; + off64_t off; err = vhd_seek(ctx, 0, SEEK_END); if (err) return err; off = vhd_position(ctx); - if (off == (off_t)-1) + if (off == (off64_t) - 1) return -errno; err = vhd_read_footer_at(ctx, footer, off - 512); @@ -977,17 +981,22 @@ vhd_read_footer(vhd_context_t *ctx, vhd_ if (err != -EINVAL) return err; - if (ctx->oflags & VHD_OPEN_STRICT) - return -EINVAL; + /* + * Disable the enforcement of VHD_OPEN_STRICT until we figure out how + * to recover from crashes. Note that we never enforced it before + * anyways due to a bug (CA-28285) and everything was ok. + */ + /* if (ctx->oflags & VHD_OPEN_STRICT) + return -EINVAL; */ return vhd_read_footer_at(ctx, footer, 0); } int -vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off) +vhd_read_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off) { + void *buf; int err; - char *buf; buf = NULL; @@ -1000,7 +1009,7 @@ vhd_read_header_at(vhd_context_t *ctx, v if (err) goto out; - err = posix_memalign((void **)&buf, + err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(vhd_header_t)); if (err) { buf = NULL; @@ -1028,8 +1037,7 @@ out: int vhd_read_header(vhd_context_t *ctx, vhd_header_t *header) { - int err; - off_t off; + off64_t off; if (!vhd_type_dynamic(ctx)) { VHDLOG("%s is not dynamic!\n", ctx->file); @@ -1044,8 +1052,9 @@ int vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat) { int err; - char *buf; - off_t off; + void *buf; + off64_t off; + uint32_t vhd_blks; size_t size; buf = NULL; @@ -1056,9 +1065,14 @@ vhd_read_bat(vhd_context_t *ctx, vhd_bat } off = ctx->header.table_offset; - size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); - - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + /* The BAT size is stored in ctx->header.max_bat_size. However, we + * sometimes preallocate BAT + batmap for max VHD size, so only read in + * the BAT entries that are in use for curr_size */ + vhd_blks = ctx->footer.curr_size >> VHD_BLOCK_SHIFT; + ASSERT(ctx->header.max_bat_size >= vhd_blks); + size = vhd_bytes_padded(vhd_blks * sizeof(uint32_t)); + + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) { buf = NULL; err = -err; @@ -1074,7 +1088,7 @@ vhd_read_bat(vhd_context_t *ctx, vhd_bat goto fail; bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; - bat->entries = ctx->header.max_bat_size; + bat->entries = vhd_blks; bat->bat = (uint32_t *)buf; vhd_bat_in(bat); @@ -1092,8 +1106,8 @@ static int vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap) { int err; - char *buf; - off_t off; + void *buf; + off64_t off; size_t size; buf = NULL; @@ -1107,7 +1121,7 @@ vhd_read_batmap_header(vhd_context_t *ct goto fail; size = vhd_bytes_padded(sizeof(vhd_batmap_header_t)); - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) { buf = NULL; err = -err; @@ -1137,13 +1151,16 @@ static int vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap) { int err; - char *buf; - off_t off; + void *buf; + off64_t off; size_t map_size; - map_size = vhd_sectors_to_bytes(batmap->header.batmap_size); - - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size); + map_size = vhd_sectors_to_bytes(secs_round_up_no_zero + (ctx->footer. + curr_size >> (VHD_BLOCK_SHIFT + 3))); + ASSERT(vhd_sectors_to_bytes(batmap->header.batmap_size) >= map_size); + + err = posix_memalign(&buf, VHD_SECTOR_SIZE, map_size); if (err) { buf = NULL; err = -err; @@ -1191,7 +1208,7 @@ vhd_read_batmap(vhd_context_t *ctx, vhd_ if (err) return err; - err = vhd_validate_batmap(batmap); + err = vhd_validate_batmap(ctx, batmap); if (err) goto fail; @@ -1252,8 +1269,10 @@ vhd_test_file_fixed(const char *file, in int vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location) { + char *location, __location[PATH_MAX]; + char *cpath, __cpath[PATH_MAX]; + char *cdir, *path; int err; - char *location, *cpath, *cdir, *path; err = 0; path = NULL; @@ -1266,16 +1285,15 @@ vhd_find_parent(vhd_context_t *ctx, cons if (parent[0] == ''/'') { if (!access(parent, R_OK)) { - path = strdup(parent); - if (!path) - return -ENOMEM; - *_location = path; + *_location = strdup(parent); + if (!*_location) + return -errno; return 0; } } /* check parent path relative to child''s directory */ - cpath = realpath(ctx->file, NULL); + cpath = realpath(ctx->file, __cpath); if (!cpath) { err = -errno; goto out; @@ -1289,28 +1307,27 @@ vhd_find_parent(vhd_context_t *ctx, cons } if (!access(location, R_OK)) { - path = realpath(location, NULL); + path = realpath(location, __location); if (path) { - *_location = path; - return 0; + *_location = strdup(path); + if (*_location) + goto out; } } err = -errno; out: free(location); - free(cpath); return err; } -static int +int vhd_macx_encode_location(char *name, char **out, int *outlen) { iconv_t cd; int len, err; size_t ibl, obl; - char *uri, *uri_utf8, *uri_utf8p, *ret; - const char *urip; + char *uri, *urip, *uri_utf8, *uri_utf8p, *ret; err = 0; ret = NULL; @@ -1321,7 +1338,7 @@ vhd_macx_encode_location(char *name, cha ibl = len; obl = len; - urip = uri = malloc(ibl + 1); + uri = urip = malloc(ibl + 1); uri_utf8 = uri_utf8p = malloc(obl); if (!uri || !uri_utf8) @@ -1333,14 +1350,10 @@ vhd_macx_encode_location(char *name, cha goto out; } - snprintf(uri, ibl+1, "file://%s", name); - - if (iconv(cd, -#ifdef __linux__ - (char **) -#endif - &urip, &ibl, &uri_utf8p, &obl) == (size_t)-1 || - ibl || obl) { + sprintf(uri, "file://%s", name); + + if (iconv(cd, &urip, &ibl, &uri_utf8p, &obl) == (size_t) - 1 || + ibl || obl) { err = (errno ? -errno : -EIO); goto out; } @@ -1364,14 +1377,13 @@ vhd_macx_encode_location(char *name, cha return err; } -static int +int vhd_w2u_encode_location(char *name, char **out, int *outlen) { iconv_t cd; int len, err; size_t ibl, obl; - char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret; - const char *urip; + char *uri, *urip, *uri_utf16, *uri_utf16p, *tmp, *ret; err = 0; ret = NULL; @@ -1425,12 +1437,8 @@ vhd_w2u_encode_location(char *name, char goto out; } - if (iconv(cd, -#ifdef __linux__ - (char **) -#endif - &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 || - ibl || obl) { + if (iconv(cd, &urip, &ibl, &uri_utf16p, &obl) == (size_t) - 1 || + ibl || obl) { err = (errno ? -errno : -EIO); goto out; } @@ -1457,7 +1465,7 @@ vhd_w2u_encode_location(char *name, char } static char * -vhd_macx_decode_location(const char *in, char *out, int len) +vhd_macx_decode_location(char *in, char *out, int len) { iconv_t cd; char *name; @@ -1470,11 +1478,7 @@ vhd_macx_decode_location(const char *in, if (cd == (iconv_t)-1) return NULL; - if (iconv(cd, -#ifdef __linux__ - (char **) -#endif - &in, &ibl, &out, &obl) == (size_t)-1 || ibl) + if (iconv(cd, &in, &ibl, &out, &obl) == (size_t) - 1 || ibl) return NULL; iconv_close(cd); @@ -1489,7 +1493,7 @@ vhd_macx_decode_location(const char *in, } static char * -vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type) +vhd_w2u_decode_location(char *in, char *out, int len, char *utf_type) { iconv_t cd; char *name, *tmp; @@ -1502,11 +1506,7 @@ vhd_w2u_decode_location(const char *in, if (cd == (iconv_t)-1) return NULL; - if (iconv(cd, -#ifdef __linux__ - (char **) -#endif - &in, &ibl, &out, &obl) == (size_t)-1 || ibl) + if (iconv(cd, &in, &ibl, &out, &obl) == (size_t) - 1 || ibl) return NULL; iconv_close(cd); @@ -1545,7 +1545,7 @@ vhd_parent_locator_read(vhd_context_t *c vhd_parent_locator_t *loc, char **parent) { int err, size; - char *raw, *out, *name; + void *raw, *out, *name; raw = NULL; out = NULL; @@ -1577,7 +1577,7 @@ vhd_parent_locator_read(vhd_context_t *c goto out; } - err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size); + err = posix_memalign(&raw, VHD_SECTOR_SIZE, size); if (err) { raw = NULL; err = -err; @@ -1635,7 +1635,7 @@ vhd_parent_locator_get(vhd_context_t *ct char *name, *location; vhd_parent_locator_t *loc; - err = 0; + err = -EINVAL; *parent = NULL; if (ctx->footer.type != HD_TYPE_DIFF) @@ -1643,9 +1643,11 @@ vhd_parent_locator_get(vhd_context_t *ct n = vhd_parent_locator_count(ctx); for (i = 0; i < n; i++) { + int _err; + loc = ctx->header.loc + i; - err = vhd_parent_locator_read(ctx, loc, &name); - if (err) + _err = vhd_parent_locator_read(ctx, loc, &name); + if (_err) continue; err = vhd_find_parent(ctx, name, &location); @@ -1665,12 +1667,14 @@ vhd_parent_locator_get(vhd_context_t *ct int vhd_parent_locator_write_at(vhd_context_t *ctx, - const char *parent, off_t off, uint32_t code, + const char *parent, off64_t off, uint32_t code, size_t max_bytes, vhd_parent_locator_t *loc) { struct stat stats; int err, len, size; - char *absolute_path, *relative_path, *encoded, *block; + char *absolute_path, *relative_path, *encoded; + char __parent[PATH_MAX]; + void *block; memset(loc, 0, sizeof(vhd_parent_locator_t)); @@ -1693,7 +1697,7 @@ vhd_parent_locator_write_at(vhd_context_ return -EINVAL; } - absolute_path = realpath(parent, NULL); + absolute_path = realpath(parent, __parent); if (!absolute_path) { err = -errno; goto out; @@ -1742,7 +1746,7 @@ vhd_parent_locator_write_at(vhd_context_ goto out; } - err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size); + err = posix_memalign(&block, VHD_SECTOR_SIZE, size); if (err) { block = NULL; err = -err; @@ -1759,7 +1763,6 @@ vhd_parent_locator_write_at(vhd_context_ err = 0; out: - free(absolute_path); free(relative_path); free(encoded); free(block); @@ -1781,7 +1784,7 @@ out: } static int -vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off) +vhd_footer_offset_at_eof(vhd_context_t *ctx, off64_t *off) { int err; if ((err = vhd_seek(ctx, 0, SEEK_END))) @@ -1794,9 +1797,9 @@ int vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp) { int err; - char *buf; + void *buf; size_t size; - off_t off; + off64_t off; uint64_t blk; buf = NULL; @@ -1823,7 +1826,7 @@ vhd_read_bitmap(vhd_context_t *ctx, uint if (err) return err; - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) return -err; @@ -1843,10 +1846,10 @@ int vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp) { int err; - char *buf; + void *buf; size_t size; uint64_t blk; - off_t end, off; + off64_t end, off; buf = NULL; *bufp = NULL; @@ -1872,7 +1875,7 @@ vhd_read_block(vhd_context_t *ctx, uint3 if (err) return err; - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) { err = -err; goto fail; @@ -1900,20 +1903,21 @@ fail: } int -vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off) +vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off64_t off) { int err; + void *buf; vhd_footer_t *f; f = NULL; - err = posix_memalign((void **)&f, + err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); if (err) { - f = NULL; err = -err; goto out; } + f = buf; memcpy(f, footer, sizeof(vhd_footer_t)); f->checksum = vhd_checksum_footer(f); @@ -1942,7 +1946,7 @@ int vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer) { int err; - off_t off; + off64_t off; if (ctx->is_block) err = vhd_footer_offset_at_eof(ctx, &off); @@ -1955,6 +1959,12 @@ vhd_write_footer(vhd_context_t *ctx, vhd if (err) return err; + if (!ctx->is_block) { + err = ftruncate(ctx->fd, off + sizeof(vhd_footer_t)); + if (err) + return -errno; + } + if (!vhd_type_dynamic(ctx)) return 0; @@ -1962,10 +1972,11 @@ vhd_write_footer(vhd_context_t *ctx, vhd } int -vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off) +vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off64_t off) { int err; vhd_header_t *h; + void *buf; h = NULL; @@ -1974,13 +1985,13 @@ vhd_write_header_at(vhd_context_t *ctx, goto out; } - err = posix_memalign((void **)&h, + err = posix_memalign(&buf, VHD_SECTOR_SIZE, sizeof(vhd_header_t)); if (err) { - h = NULL; err = -err; goto out; } + h = buf; memcpy(h, header, sizeof(vhd_header_t)); @@ -2008,8 +2019,7 @@ out: int vhd_write_header(vhd_context_t *ctx, vhd_header_t *header) { - int err; - off_t off; + off64_t off; if (!vhd_type_dynamic(ctx)) return -EINVAL; @@ -2022,8 +2032,9 @@ int vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat) { int err; - off_t off; + off64_t off; vhd_bat_t b; + void *buf; size_t size; if (!vhd_type_dynamic(ctx)) @@ -2046,9 +2057,10 @@ vhd_write_bat(vhd_context_t *ctx, vhd_ba if (err) return err; - err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) return -err; + b.bat = buf; memcpy(b.bat, bat->bat, size); b.spb = bat->spb; @@ -2061,13 +2073,50 @@ vhd_write_bat(vhd_context_t *ctx, vhd_ba return err; } +static int +vhd_write_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + size_t size; + off64_t off; + void *buf = NULL; + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + goto out; + + size = vhd_bytes_padded(sizeof(*batmap)); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + goto out; + } + + vhd_batmap_header_out(batmap); + memset(buf, 0, size); + memcpy(buf, &batmap->header, sizeof(batmap->header)); + + err = vhd_write(ctx, buf, size); + + out: + if (err) + VHDLOG("%s: failed writing batmap: %d\n", ctx->file, err); + free(buf); + return err; +} + int vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) { int err; - off_t off; + off64_t off; vhd_batmap_t b; - char *buf, *map; + void *buf, *map; size_t size, map_size; buf = NULL; @@ -2081,19 +2130,22 @@ vhd_write_batmap(vhd_context_t *ctx, vhd b.header = batmap->header; b.map = batmap->map; - b.header.checksum = vhd_checksum_batmap(&b); - err = vhd_validate_batmap(&b); + b.header.checksum = vhd_checksum_batmap(ctx, &b); + err = vhd_validate_batmap(ctx, &b); if (err) goto out; off = b.header.batmap_offset; - map_size = vhd_sectors_to_bytes(b.header.batmap_size); + map_size = vhd_sectors_to_bytes(secs_round_up_no_zero + (ctx->footer. + curr_size >> (VHD_BLOCK_SHIFT + 3))); + ASSERT(vhd_sectors_to_bytes(b.header.batmap_size) >= map_size); err = vhd_seek(ctx, off, SEEK_SET); if (err) goto out; - err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size); + err = posix_memalign(&map, VHD_SECTOR_SIZE, map_size); if (err) { map = NULL; err = -err; @@ -2116,7 +2168,7 @@ vhd_write_batmap(vhd_context_t *ctx, vhd if (err) goto out; - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) { err = -err; buf = NULL; @@ -2141,9 +2193,9 @@ int vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap) { int err; - off_t off; + off64_t off; uint64_t blk; - size_t secs, size; + size_t size; if (!vhd_type_dynamic(ctx)) return -EINVAL; @@ -2180,7 +2232,7 @@ int vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data) { int err; - off_t off; + off64_t off; size_t size; uint64_t blk; @@ -2230,13 +2282,79 @@ namedup(char **dup, const char *name) return 0; } +#define vwrite (ssize_t (*)(int, void *, size_t))write +#define vpwrite (ssize_t (*)(int, void *, size_t, off_t))pwrite + +static ssize_t +vhd_atomic_pio(ssize_t(*f) (int, void *, size_t, off_t), + int fd, void *_s, size_t n, off_t off) +{ + char *s = _s; + size_t pos = 0; + ssize_t res; + struct stat st; + + memset(&st, 0, sizeof(st)); + + for (;;) { + res = (f) (fd, s + pos, n - pos, off + pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + else + return 0; + break; + case 0: + errno = EPIPE; + return pos; + } + + if (pos + res == n) + return n; + + if (!st.st_size) + if (fstat(fd, &st) == -1) + return -1; + + if (off + pos + res == st.st_size) + return pos + res; + + pos += (res & ~(VHD_SECTOR_SIZE - 1)); + } + + return -1; +} + +static ssize_t +vhd_atomic_io(ssize_t(*f) (int, void *, size_t), int fd, void *_s, + size_t n) +{ + off64_t off; + ssize_t res; + ssize_t(*pf) (int, void *, size_t, off_t); + + off = lseek64(fd, 0, SEEK_CUR); + if (off == (off_t) - 1) + return -1; + + pf = (f == read ? pread : vpwrite); + res = vhd_atomic_pio(pf, fd, _s, n, off); + + if (res > 0) + if (lseek64(fd, off + res, SEEK_SET) == (off64_t) - 1) + return -1; + + return res; +} + int -vhd_seek(vhd_context_t *ctx, off_t offset, int whence) +vhd_seek(vhd_context_t *ctx, off64_t offset, int whence) { - off_t off; - - off = lseek(ctx->fd, offset, whence); - if (off == (off_t)-1) { + off64_t off; + + off = lseek64(ctx->fd, offset, whence); + if (off == (off64_t)-1) { VHDLOG("%s: seek(0x%08"PRIx64", %d) failed: %d\n", ctx->file, offset, whence, -errno); return -errno; @@ -2245,10 +2363,10 @@ vhd_seek(vhd_context_t *ctx, off_t offse return 0; } -off_t +off64_t vhd_position(vhd_context_t *ctx) { - return lseek(ctx->fd, 0, SEEK_CUR); + return lseek64(ctx->fd, 0, SEEK_CUR); } int @@ -2258,7 +2376,7 @@ vhd_read(vhd_context_t *ctx, void *buf, errno = 0; - ret = read(ctx->fd, buf, size); + ret = vhd_atomic_io(read, ctx->fd, buf, size); if (ret == size) return 0; @@ -2275,7 +2393,7 @@ vhd_write(vhd_context_t *ctx, void *buf, errno = 0; - ret = write(ctx->fd, buf, size); + ret = vhd_atomic_io(vwrite, ctx->fd, buf, size); if (ret == size) return 0; @@ -2285,6 +2403,40 @@ vhd_write(vhd_context_t *ctx, void *buf, return (errno ? -errno : -EIO); } +static int +vhd_pread(vhd_context_t * ctx, void *buf, size_t size, off64_t offset) +{ + ssize_t ret; + + errno = 0; + + ret = vhd_atomic_pio(pread, ctx->fd, buf, size, offset); + if (ret == size) + return 0; + + VHDLOG("%s: pread of %zu returned %zd, errno: %d\n", + ctx->file, size, ret, -errno); + + return (errno ? -errno : -EIO); +} + +static int +vhd_pwrite(vhd_context_t * ctx, void *buf, size_t size, off64_t offset) +{ + ssize_t ret; + + errno = 0; + + ret = vhd_atomic_pio(vpwrite, ctx->fd, buf, size, offset); + if (ret == size) + return 0; + + VHDLOG("%s: pwrite of %zu returned %zd, errno: %d\n", + ctx->file, size, ret, -errno); + + return (errno ? -errno : -EIO); +} + int vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset) { @@ -2312,11 +2464,11 @@ int vhd_open_fast(vhd_context_t *ctx) { int err; - char *buf; + void *buf; size_t size; size = sizeof(vhd_footer_t) + sizeof(vhd_header_t); - err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + err = posix_memalign(&buf, VHD_SECTOR_SIZE, size); if (err) { VHDLOG("failed allocating %s: %d\n", ctx->file, -err); return -err; @@ -2360,12 +2512,14 @@ out: int vhd_open(vhd_context_t *ctx, const char *file, int flags) { - int err, oflags; + int i, err, oflags; if (flags & VHD_OPEN_STRICT) vhd_flag_clear(flags, VHD_OPEN_FAST); memset(ctx, 0, sizeof(vhd_context_t)); + vhd_cache_init(ctx); + ctx->fd = -1; ctx->oflags = flags; @@ -2373,7 +2527,9 @@ vhd_open(vhd_context_t *ctx, const char if (err) return err; - oflags = O_DIRECT | O_LARGEFILE; + oflags = O_LARGEFILE; + if (!(flags & VHD_OPEN_CACHED)) + oflags |= O_DIRECT; if (flags & VHD_OPEN_RDONLY) oflags |= O_RDONLY; if (flags & VHD_OPEN_RDWR) @@ -2408,7 +2564,13 @@ vhd_open(vhd_context_t *ctx, const char } if (vhd_type_dynamic(ctx)) { - err = vhd_read_header(ctx, &ctx->header); + for (i = 0; i < VHD_HEADER_MAX_RETRIES; i++) { + err = vhd_read_header(ctx, &ctx->header); + if (!err) + break; + VHDLOG("Error reading header, retry %d\n", i); + sleep(1); + } if (err) goto fail; @@ -2416,6 +2578,12 @@ vhd_open(vhd_context_t *ctx, const char ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3); } + err = vhd_cache_load(ctx); + if (err) { + VHDLOG("failed to load cache: %d\n", err); + goto fail; + } + return 0; fail: @@ -2429,8 +2597,14 @@ fail: void vhd_close(vhd_context_t *ctx) { + vhd_cache_unload(ctx); + if (ctx->file) + { + fsync(ctx->fd); close(ctx->fd); + } + free(ctx->file); free(ctx->bat.bat); free(ctx->batmap.map); @@ -2452,19 +2626,18 @@ vhd_initialize_footer(vhd_context_t *ctx ctx->footer.geometry = vhd_chs(size); ctx->footer.type = type; ctx->footer.saved = 0; - ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF; + ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFFULL; strcpy(ctx->footer.crtr_app, "tap"); - vhd_uuid_generate(&ctx->footer.uuid); + uuid_generate(ctx->footer.uuid); } -static int +int vhd_initialize_header_parent_name(vhd_context_t *ctx, const char *parent_path) { int err; iconv_t cd; size_t ibl, obl; - char *ppath, *dst; - const char *pname; + char *pname, *ppath, *dst; err = 0; pname = NULL; @@ -2498,11 +2671,7 @@ vhd_initialize_header_parent_name(vhd_co memset(dst, 0, obl); - if (iconv(cd, -#ifdef __linux__ - (char **) -#endif - &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl) + if (iconv(cd, &pname, &ibl, &dst, &obl) == (size_t) - 1 || ibl) err = (errno ? -errno : -EINVAL); out: @@ -2511,25 +2680,25 @@ out: return err; } -static off_t +static off64_t get_file_size(const char *name) { int fd; - off_t end; + off64_t end; fd = open(name, O_LARGEFILE | O_RDONLY); if (fd == -1) { VHDLOG("unable to open ''%s'': %d\n", name, errno); return -errno; } - end = lseek(fd, 0, SEEK_END); + end = lseek64(fd, 0, SEEK_END); close(fd); return end; } static int vhd_initialize_header(vhd_context_t *ctx, const char *parent_path, - uint64_t size, int raw) + uint64_t size, int raw, uint64_t *psize) { int err; struct stat stats; @@ -2560,20 +2729,26 @@ vhd_initialize_header(vhd_context_t *ctx if (raw) { ctx->header.prt_ts = vhd_time(stats.st_mtime); + *psize = get_file_size(parent_path); if (!size) - size = get_file_size(parent_path); - } - else { + size = *psize; + } else { err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY); if (err) return err; ctx->header.prt_ts = vhd_time(stats.st_mtime); - vhd_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid); + uuid_copy(ctx->header.prt_uuid, parent.footer.uuid); + *psize = parent.footer.curr_size; if (!size) - size = parent.footer.curr_size; + size = *psize; vhd_close(&parent); } + if (size < *psize) { + VHDLOG("snapshot size (%" PRIu64 ") < parent size (%" PRIu64 ")\n", + size, *psize); + return -EINVAL; + } ctx->footer.orig_size = size; ctx->footer.curr_size = size; ctx->footer.geometry = vhd_chs(size); @@ -2583,11 +2758,11 @@ vhd_initialize_header(vhd_context_t *ctx return vhd_initialize_header_parent_name(ctx, parent_path); } -static int +int vhd_write_parent_locators(vhd_context_t *ctx, const char *parent) { int i, err; - off_t off; + off64_t off; uint32_t code; code = PLAT_CODE_NONE; @@ -2631,8 +2806,9 @@ vhd_change_parent(vhd_context_t *child, char *ppath; struct stat stats; vhd_context_t parent; - - ppath = realpath(parent_path, NULL); + char __parent_path[PATH_MAX]; + + ppath = realpath(parent_path, __parent_path); if (!ppath) { VHDLOG("error resolving parent path %s for %s: %d\n", parent_path, child->file, errno); @@ -2651,7 +2827,7 @@ vhd_change_parent(vhd_context_t *child, } if (raw) { - vhd_uuid_clear(&child->header.prt_uuid); + uuid_clear(child->header.prt_uuid); } else { err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY); if (err) { @@ -2659,7 +2835,7 @@ vhd_change_parent(vhd_context_t *child, ppath, child->file, err); goto out; } - vhd_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid); + uuid_copy(child->header.prt_uuid, parent.footer.uuid); vhd_close(&parent); } @@ -2700,16 +2876,16 @@ vhd_change_parent(vhd_context_t *child, err = 0; out: - free(ppath); return err; } static int vhd_create_batmap(vhd_context_t *ctx) { - off_t off; + off64_t off; int err, map_bytes; vhd_batmap_header_t *header; + void *map; if (!vhd_type_dynamic(ctx)) return -EINVAL; @@ -2731,14 +2907,13 @@ vhd_create_batmap(vhd_context_t *ctx) map_bytes = vhd_sectors_to_bytes(header->batmap_size); - err = posix_memalign((void **)&ctx->batmap.map, + err = posix_memalign(&map, VHD_SECTOR_SIZE, map_bytes); - if (err) { - ctx->batmap.map = NULL; + if (err) return -err; - } - - memset(ctx->batmap.map, 0, map_bytes); + + memset(map, 0, map_bytes); + ctx->batmap.map = map; return vhd_write_batmap(ctx, &ctx->batmap); } @@ -2748,16 +2923,17 @@ vhd_create_bat(vhd_context_t *ctx) { int i, err; size_t size; + void *bat; if (!vhd_type_dynamic(ctx)) return -EINVAL; size = vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); - err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size); - if (err) { - ctx->bat.bat = NULL; + err = posix_memalign(&bat, VHD_SECTOR_SIZE, size); + if (err) return err; - } + + ctx->bat.bat = bat; memset(ctx->bat.bat, 0, size); for (i = 0; i < ctx->header.max_bat_size; i++) @@ -2787,7 +2963,7 @@ vhd_initialize_fixed_disk(vhd_context_t return err; buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ, - MAP_SHARED | MAP_ANON, -1, 0); + MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (buf == MAP_FAILED) return -errno; @@ -2805,7 +2981,7 @@ out: } int -vhd_get_phys_size(vhd_context_t *ctx, off_t *size) +vhd_get_phys_size(vhd_context_t *ctx, off64_t *size) { int err; @@ -2816,9 +2992,9 @@ vhd_get_phys_size(vhd_context_t *ctx, of } int -vhd_set_phys_size(vhd_context_t *ctx, off_t size) +vhd_set_phys_size(vhd_context_t *ctx, off64_t size) { - off_t phys_size; + off64_t phys_size; int err; err = vhd_get_phys_size(ctx, &phys_size); @@ -2835,15 +3011,40 @@ vhd_set_phys_size(vhd_context_t *ctx, of } static int -__vhd_create(const char *name, const char *parent, uint64_t bytes, int type, - vhd_flag_creat_t flags) +vhd_set_virt_size_no_write(vhd_context_t *ctx, uint64_t size) +{ + if ((size >> VHD_BLOCK_SHIFT) > ctx->header.max_bat_size) { + VHDLOG("not enough metadata space reserved for fast " + "resize (BAT size %u, need %" PRIu64 ")\n", + ctx->header.max_bat_size, size >> VHD_BLOCK_SHIFT); + return -EINVAL; + } + + /* update footer */ + ctx->footer.curr_size = size; + ctx->footer.geometry = vhd_chs(ctx->footer.curr_size); + ctx->footer.checksum = vhd_checksum_footer(&ctx->footer); + return 0; +} + +int vhd_set_virt_size(vhd_context_t * ctx, uint64_t size) { int err; - off_t off; + + err = vhd_set_virt_size_no_write(ctx, size); + if (err) + return err; + return vhd_write_footer(ctx, &ctx->footer); +} + +static int +__vhd_create(const char *name, const char *parent, uint64_t bytes, int type, + uint64_t mbytes, vhd_flag_creat_t flags) +{ + int err; + off64_t off; vhd_context_t ctx; - vhd_footer_t *footer; - vhd_header_t *header; - uint64_t size, blks; + uint64_t size, psize, blks; switch (type) { case HD_TYPE_DIFF: @@ -2859,10 +3060,19 @@ static int if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1) return -ENAMETOOLONG; + if (bytes && mbytes && mbytes < bytes) + return -EINVAL; + memset(&ctx, 0, sizeof(vhd_context_t)); - footer = &ctx.footer; - header = &ctx.header; + psize = 0; blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; + /* If mbytes is provided (virtual-size-for-metadata-preallocation), + * create the VHD of size mbytes, which will create the BAT & the + * batmap of the appropriate size. Once the BAT & batmap are + * initialized, reset the virtual size to the requested one. + */ + if (mbytes) + blks = (mbytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; size = blks << VHD_BLOCK_SHIFT; ctx.fd = open(name, O_WRONLY | O_CREAT | @@ -2888,15 +3098,7 @@ static int goto out; } else { int raw = vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW); - err = vhd_initialize_header(&ctx, parent, size, raw); - if (err) - goto out; - - err = vhd_write_footer_at(&ctx, &ctx.footer, 0); - if (err) - goto out; - - err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE); + err = vhd_initialize_header(&ctx, parent, size, raw, &psize); if (err) goto out; @@ -2913,8 +3115,28 @@ static int if (err) goto out; } - - /* write header again since it may have changed */ + } + + if (mbytes) { + /* set the virtual size to the requested size */ + if (bytes) { + blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; + size = blks << VHD_BLOCK_SHIFT; + + } else { + size = psize; + } + ctx.footer.orig_size = size; + err = vhd_set_virt_size_no_write(&ctx, size); + if (err) + goto out; + } + + if (type != HD_TYPE_FIXED) { + err = vhd_write_footer_at(&ctx, &ctx.footer, 0); + if (err) + goto out; + err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE); if (err) goto out; @@ -2925,7 +3147,7 @@ static int goto out; off = vhd_position(&ctx); - if (off == (off_t)-1) { + if (off == (off64_t)-1) { err = -errno; goto out; } @@ -2947,16 +3169,17 @@ out: } int -vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags) +vhd_create(const char *name, uint64_t bytes, int type, uint64_t mbytes, + vhd_flag_creat_t flags) { - return __vhd_create(name, NULL, bytes, type, flags); + return __vhd_create(name, NULL, bytes, type, mbytes, flags); } int vhd_snapshot(const char *name, uint64_t bytes, const char *parent, - vhd_flag_creat_t flags) + uint64_t mbytes, vhd_flag_creat_t flags) { - return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags); + return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, mbytes, flags); } static int @@ -3000,7 +3223,7 @@ static int __vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map, char *buf, uint64_t sector, uint32_t secs) { - off_t off; + off64_t off; uint32_t blk, sec; int err, cnt, map_off; char *bitmap, *data, *src; @@ -3056,9 +3279,9 @@ static int char *map, char *buf, uint64_t sec, uint32_t secs) { int fd, err; - off_t off; + off64_t off; uint64_t size; - char *data; + void *data; err = 0; errno = 0; @@ -3068,8 +3291,8 @@ static int return -errno; } - off = lseek(fd, vhd_sectors_to_bytes(sec), SEEK_SET); - if (off == (off_t)-1) { + off = lseek64(fd, vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off64_t)-1) { VHDLOG("%s: seek(0x%08"PRIx64") failed: %d\n", filename, vhd_sectors_to_bytes(sec), -errno); err = -errno; @@ -3077,7 +3300,7 @@ static int } size = vhd_sectors_to_bytes(secs); - err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size); + err = posix_memalign(&data, VHD_SECTOR_SIZE, size); if (err) goto close; @@ -3134,12 +3357,21 @@ static int } if (vhd->footer.type == HD_TYPE_DIFF) { + vhd_context_t *p; + p = vhd_cache_get_parent(vhd); + if (p) { + vhd = p; + err = vhd_get_bat(vhd); + if (err) + goto out; + continue; + } + err = vhd_parent_locator_get(vhd, &next); if (err) goto close; if (vhd_parent_raw(vhd)) { - err = __raw_read_link(next, map, buf, sec, - secs); + err = __raw_read_link(next, map, buf, sec, secs); goto close; } } else { @@ -3164,7 +3396,7 @@ static int } close: - if (vhd != ctx) + if (vhd != ctx && !vhd_flag_test(vhd->oflags, VHD_OPEN_CACHED)) vhd_close(vhd); out: free(map); @@ -3202,8 +3434,8 @@ static int { char *buf; size_t size; - off_t off, max; - int i, err, gap, spp; + off64_t off, max; + int err, gap, spp, secs; spp = getpagesize() >> VHD_SECTOR_SHIFT; @@ -3225,8 +3457,12 @@ static int if (err) return err; - size = vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap); - buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0); + secs = ctx->bm_secs + gap; + if (!vhd_flag_test(ctx->oflags, VHD_OPEN_IO_WRITE_SPARSE)) + secs += ctx->spb; + + size = vhd_sectors_to_bytes(secs); + buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (buf == MAP_FAILED) return -errno; @@ -3251,7 +3487,7 @@ static int char *buf, uint64_t sector, uint32_t secs) { char *map; - off_t off; + off64_t off; uint32_t blk, sec; int i, err, cnt, ret; @@ -3350,3 +3586,765 @@ vhd_io_write(vhd_context_t *ctx, char *b return __vhd_io_dynamic_write(ctx, buf, sec, secs); } + +static void vhd_cache_init(vhd_context_t * ctx __attribute__((unused))) +{ +} + +static int vhd_cache_enabled(vhd_context_t * ctx) +{ + return vhd_flag_test(ctx->oflags, VHD_OPEN_CACHED); +} + +static int vhd_cache_load(vhd_context_t * ctx) +{ + char *next; + int err, pflags; + vhd_context_t *vhd; + + err = 1; + pflags = ctx->oflags; + vhd = ctx; + next = NULL; + + vhd_flag_set(pflags, VHD_OPEN_RDONLY); + vhd_flag_clear(pflags, VHD_OPEN_CACHED); + + if (!vhd_cache_enabled(vhd)) + goto done; + + while (vhd->footer.type == HD_TYPE_DIFF) { + vhd_context_t *parent; + + parent = NULL; + + if (vhd_parent_raw(vhd)) + goto done; + + err = vhd_parent_locator_get(vhd, &next); + if (err) + goto out; + + parent = calloc(1, sizeof(*parent)); + if (!parent) + goto out; + + err = vhd_open(parent, next, pflags); + if (err) { + free(parent); + parent = NULL; + goto out; + } + + fcntl(parent->fd, F_SETFL, fcntl(parent->fd, F_GETFL) & ~O_DIRECT); + vhd_flag_set(parent->oflags, VHD_OPEN_CACHED); + vhd->parent = parent; + + free(next); + next = NULL; + vhd = parent; + } + + done: + err = 0; + out: + free(next); + if (err) + vhd_cache_unload(vhd); + + return err; +} + +static int vhd_cache_unload(vhd_context_t * ctx) +{ + vhd_context_t *vhd; + + if (!vhd_cache_enabled(ctx)) + goto out; + + vhd = ctx; + while ((vhd = vhd->parent)) { + vhd_close(vhd); + free(vhd); + } + ctx->parent = NULL; + + out: + return 0; +} + +static inline vhd_context_t *vhd_cache_get_parent(vhd_context_t * ctx) +{ + if (!vhd_cache_enabled(ctx)) + return NULL; + + return ctx->parent; +} + +typedef struct vhd_block_vector vhd_block_vector_t; +typedef struct vhd_block_vector_entry vhd_block_vector_entry_t; + +struct vhd_block_vector_entry { + uint64_t off; /* byte offset from block */ + uint32_t bytes; /* size in bytes */ + char *buf; /* destination buffer */ +}; + +struct vhd_block_vector { + uint32_t block; /* logical block in vhd */ + int entries; /* number of vector entries */ + vhd_block_vector_entry_t *array; /* vector list */ +}; + +/** + * @vec: block vector describing read + * + * @vec describes a list of byte-spans within a given block + * and a corresponding list of destination buffers. + */ +static int +vhd_block_vector_read(vhd_context_t * ctx, vhd_block_vector_t * vec) +{ + int err, i; + off64_t off; + uint32_t blk; + + err = vhd_get_bat(ctx); + if (err) + goto out; + + if (vec->block >= ctx->bat.entries) { + err = -ERANGE; + goto out; + } + + blk = ctx->bat.bat[vec->block]; + if (blk == DD_BLK_UNUSED) { + err = -EINVAL; + goto out; + } + + off = vhd_sectors_to_bytes(blk + ctx->bm_secs); + + for (i = 0; i < vec->entries; i++) { + vhd_block_vector_entry_t *v = vec->array + i; + err = vhd_pread(ctx, v->buf, v->bytes, off + v->off); + if (err) + goto out; + } + + out: + return err; +} + +/** + * @vec: block vector to initialize + * @block: vhd block number + * @map: optional bitmap of sectors to map (relative to beginning of block) + * @buf: destination buffer + * @blk_start: byte offset relative to beginning of block + * @blk_end: byte offset relative to beginning of block + * + * initializes @vec to describe a read into a contiguous buffer + * of potentially non-contiguous byte ranges in a given vhd block. + * only sectors with corresponding bits set in @map (if it is not NULL) + * will be mapped; bits corresponding to unmapped sectors will be cleared. + * first and last sector maps may be smaller than vhd sector size. + */ +static int +vhd_block_vector_init(vhd_context_t * ctx, + vhd_block_vector_t * vec, uint32_t block, char *map, + char *buf, uint64_t blk_start, uint64_t blk_end) +{ + int err, sec; + char *bitmap; + uint32_t first_sec, last_sec; + + bitmap = NULL; + memset(vec, 0, sizeof(*vec)); + + first_sec = blk_start >> VHD_SECTOR_SHIFT; + last_sec = secs_round_up_no_zero(blk_end); + + err = vhd_read_bitmap(ctx, block, &bitmap); + if (err) + goto out; + + vec->array = calloc(ctx->spb, sizeof(vhd_block_vector_entry_t)); + if (!vec->array) { + err = -ENOMEM; + goto out; + } + + for (sec = first_sec; sec < last_sec; sec++) { + uint32_t cnt; + vhd_block_vector_entry_t *v; + + cnt = VHD_SECTOR_SIZE - (blk_start & (VHD_SECTOR_SIZE - 1)); + if (cnt > blk_end - blk_start) + cnt = blk_end - blk_start; + + if (map && !test_bit(map, sec)) + goto next; + + if (vhd_bitmap_test(ctx, bitmap, sec)) { + if (vec->entries > 0) { + v = vec->array + vec->entries - 1; + if (v->off + v->bytes == blk_start) { + v->bytes += cnt; + goto next; + } + } + + v = vec->array + vec->entries; + v->off = blk_start; + v->bytes = cnt; + v->buf = buf; + + vec->entries++; + + } else if (map) { + clear_bit(map, sec); + } + + next: + blk_start += cnt; + buf += cnt; + } + + vec->block = block; + + out: + free(bitmap); + return err; +} + +#if 0 +/** + * @block: vhd block number + * @buf: buffer to place data in + * @size: number of bytes to read + * @start: byte offset into block from which to start reading + * @end: byte offset in block at which to stop reading + * + * reads data (if it exists) into @buf. partial reads may occur + * for the first and last sectors if @start and @end are not multiples + * of vhd sector size. + */ +static int +vhd_block_vector_read_allocated(vhd_context_t * ctx, uint32_t block, + char *buf, uint64_t start, uint64_t end) +{ + int err; + vhd_block_vector_t vec; + + vec.array = NULL; + + err = vhd_block_vector_init(ctx, &vec, block, NULL, buf, start, end); + if (err) + goto out; + + err = vhd_block_vector_read(ctx, &vec); + + out: + free(vec.array); + return err; +} +#endif + +/** + * @block: vhd block number + * @map: bitmap of sectors in block which should be read + * @buf: buffer to place data in + * @start: byte offset into block from which to start reading + * @end: byte offset in block at which to stop reading + * + * for every bit set in @map (corresponding to sectors in @block), + * reads data (if it exists) into @buf. if data does not exist, + * clears corresponding bit in @map. partial reads may occur + * for the first and last sectors if @start and @end are not multiples + * of vhd sector size. + */ +static int +vhd_block_vector_read_allocated_selective(vhd_context_t * ctx, + uint32_t block, char *map, + char *buf, uint64_t start, + uint64_t end) +{ + int err; + vhd_block_vector_t vec; + + vec.array = NULL; + + err = vhd_block_vector_init(ctx, &vec, block, map, buf, start, end); + if (err) + goto out; + + err = vhd_block_vector_read(ctx, &vec); + + out: + free(vec.array); + return err; +} + +/** + * @map: bitmap of sectors which have already been read + * @buf: destination buffer + * @size: size in bytes to read + * @off: byte offset in virtual disk to read + * + * reads @size bytes into @buf, starting at @off, skipping sectors + * which have corresponding bits set in @map + */ +static int +__vhd_io_dynamic_read_link_bytes(vhd_context_t * ctx, char *map, + char *buf, size_t size, uint64_t off) +{ + char *blkmap; + int i, err, map_off; + off64_t blk_off, blk_size; + uint32_t blk, bytes, first_sec, last_sec; + + blkmap = malloc((ctx->spb + 7) >> 3); + if (!blkmap) { + err = -ENOMEM; + goto out; + } + + map_off = 0; + blk_size = vhd_sectors_to_bytes(ctx->spb); + + do { + blk = off / blk_size; + blk_off = off % blk_size; + bytes = MIN(blk_size - blk_off, size); + + first_sec = blk_off >> VHD_SECTOR_SHIFT; + last_sec = secs_round_up_no_zero(blk_off + bytes); + + if (ctx->bat.bat[blk] == DD_BLK_UNUSED) + goto next; + + memset(blkmap, 0, (ctx->spb + 7) >> 3); + + for (i = 0; i < (last_sec - first_sec); i++) + if (!test_bit(map, map_off + i)) + set_bit(blkmap, first_sec + i); + + err = vhd_block_vector_read_allocated_selective(ctx, blk, + blkmap, buf, + blk_off, + blk_off + bytes); + if (err) + goto out; + + for (i = 0; i < (last_sec - first_sec); i++) + if (test_bit(blkmap, first_sec + i)) + set_bit(map, map_off + i); + + next: + size -= bytes; + off += bytes; + map_off += (last_sec - first_sec); + buf += bytes; + + } while (size); + + err = 0; + out: + free(blkmap); + return err; +} + +static int +__raw_read_link_bytes(const char *filename, + char *map, char *buf, size_t size, uint64_t off) +{ + int fd, err; + uint32_t i, first_sec, last_sec; + + fd = open(filename, O_RDONLY | O_LARGEFILE); + if (fd == -1) { + VHDLOG("%s: failed to open: %d\n", filename, -errno); + return -errno; + } + + first_sec = off >> VHD_SECTOR_SHIFT; + last_sec = secs_round_up_no_zero(off + size); + + for (i = first_sec; i < last_sec; i++) { + if (!test_bit(map, i - first_sec)) { + uint32_t secs = 0; + uint64_t coff, csize; + + while (i + secs < last_sec && + !test_bit(map, i + secs - first_sec)) + secs++; + + coff = vhd_sectors_to_bytes(i); + csize = vhd_sectors_to_bytes(secs); + + if (i == first_sec) + coff = off; + if (secs == last_sec - 1) + csize = (off + size) - coff; + + if (pread(fd, buf + coff - off, csize, coff) != csize) { + err = (errno ? -errno : -EIO); + goto close; + } + + i += secs - 1; + } + } + + err = 0; + + close: + close(fd); + return err; +} + +static int +__vhd_io_dynamic_read_bytes(vhd_context_t * ctx, + char *buf, size_t size, uint64_t off) +{ + int err; + char *next, *map; + vhd_context_t parent, *vhd; + uint32_t i, done, first_sec, last_sec; + + err = vhd_get_bat(ctx); + if (err) + return err; + + first_sec = off >> VHD_SECTOR_SHIFT; + last_sec = secs_round_up_no_zero(off + size); + + vhd = ctx; + next = NULL; + map = calloc(1, ((last_sec - first_sec) + 7) >> 3); + if (!map) { + err = -ENOMEM; + goto out; + } + + for (;;) { + err = __vhd_io_dynamic_read_link_bytes(vhd, map, buf, size, off); + if (err) + goto close; + + for (done = 0, i = 0; i < (last_sec - first_sec); i++) + if (test_bit(map, i)) + done++; + + if (done == last_sec - first_sec) { + err = 0; + goto close; + } + + if (vhd->footer.type == HD_TYPE_DIFF) { + vhd_context_t *p; + p = vhd_cache_get_parent(vhd); + if (p) { + vhd = p; + err = vhd_get_bat(vhd); + if (err) + goto out; + continue; + } + + err = vhd_parent_locator_get(vhd, &next); + if (err) + goto close; + + if (vhd_parent_raw(vhd)) { + err = __raw_read_link_bytes(next, map, buf, size, off); + goto close; + } + } else { + err = 0; + goto close; + } + + if (vhd != ctx) + vhd_close(vhd); + vhd = &parent; + + err = vhd_open(vhd, next, VHD_OPEN_RDONLY); + if (err) + goto out; + + err = vhd_get_bat(vhd); + if (err) + goto close; + + free(next); + next = NULL; + } + + close: + if (!err) { + /* + * clear any regions not present on disk + */ + for (i = first_sec; i < last_sec; i++) { + if (!test_bit(map, i - first_sec)) { + uint64_t coff = vhd_sectors_to_bytes(i); + uint32_t csize = VHD_SECTOR_SIZE; + + if (i == first_sec) + coff = off; + if (i == last_sec - 1) + csize = (off + size) - coff; + + memset(buf + coff - off, 0, csize); + } + } + } + + if (vhd != ctx && !vhd_flag_test(vhd->oflags, VHD_OPEN_CACHED)) + vhd_close(vhd); + out: + free(map); + free(next); + return err; +} + +int +vhd_io_read_bytes(vhd_context_t * ctx, void *buf, size_t size, + uint64_t off) +{ + if (off + size > ctx->footer.curr_size) + return -ERANGE; + + if (!vhd_type_dynamic(ctx)) + return vhd_pread(ctx, buf, size, off); + + return __vhd_io_dynamic_read_bytes(ctx, buf, size, off); +} + +static int +__vhd_io_dynamic_write_bytes_aligned(vhd_context_t * ctx, + char *buf, size_t size, uint64_t off) +{ + char *map; + int i, err, ret; + uint64_t blk_off, blk_size, blk_start; + uint32_t blk, bytes, first_sec, last_sec; + + if (off & (VHD_SECTOR_SIZE - 1) || size & (VHD_SECTOR_SIZE - 1)) + return -EINVAL; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (vhd_has_batmap(ctx)) { + err = vhd_get_batmap(ctx); + if (err) + return err; + } + + map = NULL; + blk_size = vhd_sectors_to_bytes(ctx->spb); + + do { + blk = off / blk_size; + blk_off = off % blk_size; + bytes = MIN(blk_size - blk_off, size); + + first_sec = blk_off >> VHD_SECTOR_SHIFT; + last_sec = secs_round_up_no_zero(blk_off + bytes); + + blk_start = ctx->bat.bat[blk]; + if (blk_start == DD_BLK_UNUSED) { + err = __vhd_io_allocate_block(ctx, blk); + if (err) + goto fail; + + blk_start = ctx->bat.bat[blk]; + } + + blk_start = vhd_sectors_to_bytes(blk_start + ctx->bm_secs); + + err = vhd_pwrite(ctx, buf, bytes, blk_start + blk_off); + if (err) + goto fail; + + if (vhd_has_batmap(ctx) && vhd_batmap_test(ctx, &ctx->batmap, blk)) + goto next; + + err = vhd_read_bitmap(ctx, blk, &map); + if (err) { + map = NULL; + goto fail; + } + + for (i = first_sec; i < last_sec; i++) + vhd_bitmap_set(ctx, map, i); + + err = vhd_write_bitmap(ctx, blk, map); + if (err) + goto fail; + + if (vhd_has_batmap(ctx)) { + for (i = 0; i < ctx->spb; i++) + if (!vhd_bitmap_test(ctx, map, i)) { + free(map); + map = NULL; + goto next; + } + + vhd_batmap_set(ctx, &ctx->batmap, blk); + err = vhd_write_batmap(ctx, &ctx->batmap); + if (err) + goto fail; + } + + free(map); + map = NULL; + + next: + size -= bytes; + off += bytes; + buf += bytes; + + } while (size); + + err = 0; + + out: + ret = vhd_write_footer(ctx, &ctx->footer); + return (err ? err : ret); + + fail: + free(map); + goto out; +} + +static int +__vhd_io_dynamic_write_bytes(vhd_context_t * ctx, + char *buf, size_t size, uint64_t off) +{ + int err; + char *tmp; + uint32_t first_sec, last_sec, first_sec_off, last_sec_off; + + err = 0; + tmp = NULL; + + first_sec = off >> VHD_SECTOR_SHIFT; + last_sec = secs_round_up_no_zero(off + size); + + first_sec_off = off & (VHD_SECTOR_SIZE - 1); + last_sec_off = (off + size) & (VHD_SECTOR_SIZE - 1); + + if (first_sec_off || last_sec_off) { + tmp = malloc(VHD_SECTOR_SIZE); + if (!tmp) { + err = -ENOMEM; + goto out; + } + + if (first_sec_off) { + uint32_t new = VHD_SECTOR_SIZE - first_sec_off; + if (new > size) + new = size; + + err = vhd_io_read_bytes(ctx, tmp, VHD_SECTOR_SIZE, + vhd_sectors_to_bytes(first_sec)); + if (err) + goto out; + + memcpy(tmp + first_sec_off, buf, new); + + err + __vhd_io_dynamic_write_bytes_aligned(ctx, tmp, + VHD_SECTOR_SIZE, + vhd_sectors_to_bytes + (first_sec)); + if (err) + goto out; + + buf += new; + off += new; + size -= new; + } + + if (last_sec_off && (last_sec - first_sec > 1 || !first_sec_off)) { + uint32_t new = last_sec_off; + + err = vhd_io_read_bytes(ctx, tmp, VHD_SECTOR_SIZE, + vhd_sectors_to_bytes(last_sec - 1)); + if (err) + goto out; + + memcpy(tmp, buf + size - new, new); + + err + __vhd_io_dynamic_write_bytes_aligned(ctx, tmp, + VHD_SECTOR_SIZE, + vhd_sectors_to_bytes + (last_sec - 1)); + if (err) + goto out; + + size -= new; + } + } + + if (size) + err = __vhd_io_dynamic_write_bytes_aligned(ctx, buf, size, off); + + out: + free(tmp); + return err; +} + +int +vhd_io_write_bytes(vhd_context_t * ctx, void *buf, size_t size, + uint64_t off) +{ + if (off + size > ctx->footer.curr_size) + return -ERANGE; + + if (!vhd_type_dynamic(ctx)) + return vhd_pwrite(ctx, buf, size, off); + + return __vhd_io_dynamic_write_bytes(ctx, buf, size, off); +} + +int vhd_marker(vhd_context_t * ctx, char *marker) +{ + int err; + vhd_batmap_t batmap; + + *marker = 0; + + if (!vhd_has_batmap(ctx)) + return -ENOSYS; + + err = vhd_read_batmap_header(ctx, &batmap); + if (err) + return err; + + *marker = batmap.header.marker; + return 0; +} + +int vhd_set_marker(vhd_context_t * ctx, char marker) +{ + int err; + vhd_batmap_t batmap; + + if (!vhd_has_batmap(ctx)) + return -ENOSYS; + + err = vhd_read_batmap_header(ctx, &batmap); + if (err) + return err; + + batmap.header.marker = marker; + return vhd_write_batmap_header(ctx, &batmap); +}
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 5 of 8 RESEND] blktap3/vhd: Introduce relative-path.c/relative-path.h
This patch copies from blktap2 functionality that returns a relative path from one path to another, with changes coming from blktap2.5. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap2/vhd/lib/relative-path.c b/tools/blktap3/vhd/lib/relative-path.c copy from tools/blktap2/vhd/lib/relative-path.c copy to tools/blktap3/vhd/lib/relative-path.c --- a/tools/blktap2/vhd/lib/relative-path.c +++ b/tools/blktap3/vhd/lib/relative-path.c @@ -1,4 +1,5 @@ /* Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,6 +29,7 @@ #include <errno.h> #include <stdlib.h> #include <string.h> +#include <limits.h> #include "relative-path.h" @@ -212,7 +214,8 @@ char * relative_path_to(char *from, char *to, int *err) { int from_nodes, common; - char *to_absolute, *from_absolute; + char *to_absolute, __to_absolute[PATH_MAX]; + char *from_absolute, __from_absolute[PATH_MAX]; char *up, *common_target_path, *relative_path; *err = 0; @@ -229,14 +232,14 @@ relative_path_to(char *from, char *to, i return NULL; } - to_absolute = realpath(to, NULL); + to_absolute = realpath(to, __to_absolute); if (!to_absolute) { EPRINTF("failed to get absolute path of %s\n", to); *err = -errno; goto out; } - from_absolute = realpath(from, NULL); + from_absolute = realpath(from, __from_absolute); if (!from_absolute) { EPRINTF("failed to get absolute path of %s\n", from); *err = -errno; @@ -292,8 +295,6 @@ relative_path_to(char *from, char *to, i out: sfree(up); - sfree(to_absolute); - sfree(from_absolute); return relative_path; } diff --git a/tools/blktap2/include/relative-path.h b/tools/blktap3/vhd/lib/relative-path.h copy from tools/blktap2/include/relative-path.h copy to tools/blktap3/vhd/lib/relative-path.h --- a/tools/blktap2/include/relative-path.h +++ b/tools/blktap3/vhd/lib/relative-path.h @@ -1,4 +1,5 @@ /* Copyright (c) 2008, XenSource Inc. + * Copyright (c) 2010, Citrix Systems, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 6 of 8 RESEND] blktap3/vhd: Introduce atomicio.[ch]
This patch imports file atomicio.[ch] from blktap2, which seems to contain wrappers around the read/write system calls. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap2/vhd/lib/atomicio.c b/tools/blktap3/vhd/lib/atomicio.c copy from tools/blktap2/vhd/lib/atomicio.c copy to tools/blktap3/vhd/lib/atomicio.c --- a/tools/blktap2/vhd/lib/atomicio.c +++ b/tools/blktap3/vhd/lib/atomicio.c @@ -40,7 +40,7 @@ atomicio(f, fd, _s, n) { char *s = _s; size_t pos = 0; - ssize_t res; + size_t res; while (n > pos) { res = (f) (fd, s + pos, n - pos); @@ -58,4 +58,3 @@ atomicio(f, fd, _s, n) } return (pos); } - diff --git a/tools/blktap2/include/atomicio.h b/tools/blktap3/vhd/lib/atomicio.h copy from tools/blktap2/include/atomicio.h copy to tools/blktap3/vhd/lib/atomicio.h
This patch imports from blktap2 the Makefile that compiles libvhd, with changes coming from blktap2.5. I have left out vhd-util related stuff as they are not necessary at this point, they will be introduced by a later patch. Also, libvhd is installed under a different name in order to avoid conflicts with the one used by blktap2. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap2/vhd/lib/Makefile b/tools/blktap3/vhd/lib/Makefile copy from tools/blktap2/vhd/lib/Makefile copy to tools/blktap3/vhd/lib/Makefile --- a/tools/blktap2/vhd/lib/Makefile +++ b/tools/blktap3/vhd/lib/Makefile @@ -3,21 +3,27 @@ BLKTAP_ROOT := ../.. include $(XEN_ROOT)/tools/Rules.mk LIBVHD-MAJOR = 1.0 -LIBVHD-MINOR = 0 +LIBVHD-MINOR = 3 LIBVHD-SONAME = libvhd.so.$(LIBVHD-MAJOR) -LVM-UTIL-OBJ := $(BLKTAP_ROOT)/lvm/lvm-util.o - LIBVHD-BUILD := libvhd.a INST-DIR = $(LIBDIR) -CFLAGS += -Werror -CFLAGS += -Wno-unused -CFLAGS += -I../../include -CFLAGS += -D_GNU_SOURCE -CFLAGS += -fPIC -CFLAGS += -g +override CFLAGS += \ + -I$(BLKTAP_ROOT)/include \ + -I$(BLKTAP_ROOT)/part \ + -D_GNU_SOURCE \ + -fPIC \ + $(CFLAGS_xeninclude) \ + -Wall \ + -Wextra \ + -Werror + +# FIXME cause trouble +override CFLAGS += \ + -Wno-sign-compare \ + -Wno-type-limits ifeq ($(CONFIG_Linux),y) LIBS := -luuid @@ -28,26 +34,12 @@ LIBS += -liconv endif LIB-SRCS := libvhd.c -LIB-SRCS += libvhd-journal.c -LIB-SRCS += vhd-util-coalesce.c -LIB-SRCS += vhd-util-create.c -LIB-SRCS += vhd-util-fill.c -LIB-SRCS += vhd-util-modify.c -LIB-SRCS += vhd-util-query.c -LIB-SRCS += vhd-util-read.c -LIB-SRCS += vhd-util-repair.c -LIB-SRCS += vhd-util-resize.c -LIB-SRCS += vhd-util-revert.c -LIB-SRCS += vhd-util-set-field.c -LIB-SRCS += vhd-util-snapshot.c -LIB-SRCS += vhd-util-scan.c -LIB-SRCS += vhd-util-check.c -LIB-SRCS += vhd-util-uuid.c +# TODO Not in Citrix blktap2, import it. +#LIB-SRCS += vhd-util-uuid.c LIB-SRCS += relative-path.c LIB-SRCS += atomicio.c LIB-OBJS = $(patsubst %.c,%.o,$(LIB-SRCS)) -LIB-OBJS += $(LVM-UTIL-OBJ) LIB-PICOBJS = $(patsubst %.o,%.opic,$(LIB-OBJS)) @@ -55,7 +47,10 @@ LIBVHD = libvhd.a libvhd.so.$( all: build -build: libvhd.a libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) +build: libvhd.a libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) libvhdio.so + +libvhdio.so: libvhdio.o + $(CC) -Wl,$(SONAME_LDFLAG) $(SHLIB_LDFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) libvhd.a: $(LIB-OBJS) $(AR) rc $@ $^ @@ -70,6 +65,7 @@ install: all $(INSTALL_DIR) -p $(DESTDIR)$(INST-DIR) $(INSTALL_DATA) libvhd.a $(DESTDIR)$(INST-DIR) $(INSTALL_PROG) libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) $(DESTDIR)$(INST-DIR) + $(INSTALL_PROG) libvhdio.so $(DESTDIR)$(INST-DIR) ln -sf libvhd.so.$(LIBVHD-MAJOR).$(LIBVHD-MINOR) $(DESTDIR)$(INST-DIR)/libvhd.so.$(LIBVHD-MAJOR) ln -sf libvhd.so.$(LIBVHD-MAJOR) $(DESTDIR)$(INST-DIR)/libvhd.so
Thanos Makatos
2013-Jul-15 11:43 UTC
[PATCH 8 of 8 RESEND] blktap3/vhd: Introduce Makefile to in the VHD module that calls the libvhd Makefile
This patch introduces the Makefile that builds the VHD module. Since the vhd/ directory is not yet populated, it simply calls the vhd/lib/ Makefile. This file is based on the existing blktap2 file. Signed-off-by: Thanos Makatos <thanos.makatos@citrix.com> diff --git a/tools/blktap2/vhd/Makefile b/tools/blktap3/vhd/Makefile copy from tools/blktap2/vhd/Makefile copy to tools/blktap3/vhd/Makefile --- a/tools/blktap2/vhd/Makefile +++ b/tools/blktap3/vhd/Makefile @@ -5,45 +5,17 @@ include $(XEN_ROOT)/tools/Rules.mk SUBDIRS-y : SUBDIRS-y += lib -IBIN = vhd-util vhd-update -INST_DIR = $(SBINDIR) +all: subdirs-all -CFLAGS += -Werror -CFLAGS += -Wno-unused -CFLAGS += -I../include -CFLAGS += -D_GNU_SOURCE - -ifeq ($(CONFIG_X86_64),y) -CFLAGS += -fPIC -endif - -ifeq ($(VHD_STATIC),y) -CFLAGS += -static -endif - -LIBS := -Llib -lvhd - -all: subdirs-all build - -build: $(IBIN) - -LIBS_DEPENDS := lib/libvhd.so lib/vhd.a -$(LIBS_DEPENDS):subdirs-all - -vhd-util: vhd-util.o $(LIBS_DEPENDS) - $(CC) $(LDFLAGS) -o vhd-util vhd-util.o $(LIBS) - -vhd-update: vhd-update.o $(LIBS_DEPENDS) - $(CC) $(LDFLAGS) -o vhd-update vhd-update.o $(LIBS) +LIBS_DEPENDS := lib/libvhd.so lib/libvhd.a +$(LIBS_DEPENDS): subdirs-all install: all $(MAKE) subdirs-install - $(INSTALL_DIR) -p $(DESTDIR)$(INST_DIR) - $(INSTALL_PROG) $(IBIN) $(DESTDIR)$(INST_DIR) clean: subdirs-clean - rm -rf *.o *~ $(DEPS) $(IBIN) -.PHONY: all build clean install vhd-util vhd-update +.PHONY: all clean install +# TODO Is this necessary? -include $(DEPS)