diff --git a/extracters/libvhd/include/blk_uuid.h b/extracters/libvhd/include/blk_uuid.h new file mode 100644 index 0000000..e176892 --- /dev/null +++ b/extracters/libvhd/include/blk_uuid.h @@ -0,0 +1,200 @@ +/* Copyright (c) 2008, XenSource 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. +*/ +/* Modified March 2016 Timothe Litt to work under windows. +* Copyright (C) 2016 Timothe Litt +* Modifications subject to the same license terms as above, +* substituting "Timothe Litt" for "XenSource Inc." +*/ + +#ifndef __BLKTAP2_UUID_H__ +#define __BLKTAP2_UUID_H__ + +#if defined(__linux__) + +#include + +typedef struct { + uuid_t uuid; +} blk_uuid_t; + +static inline int blk_uuid_is_nil(blk_uuid_t *uuid) +{ + return uuid_is_null(uuid->uuid); +} + +static inline void blk_uuid_generate(blk_uuid_t *uuid) +{ + uuid_generate(uuid->uuid); +} + +static inline void blk_uuid_to_string(blk_uuid_t *uuid, char *out, size_t size) +{ + (void) size; + uuid_unparse(uuid->uuid, out); +} + +static inline void blk_uuid_from_string(blk_uuid_t *uuid, const char *in) +{ + uuid_parse(in, uuid->uuid); +} + +static inline void blk_uuid_copy(blk_uuid_t *dst, blk_uuid_t *src) +{ + uuid_copy(dst->uuid, src->uuid); +} + +static inline void blk_uuid_clear(blk_uuid_t *uuid) +{ + uuid_clear(uuid->uuid); +} + +static inline int blk_uuid_compare(blk_uuid_t *uuid1, blk_uuid_t *uuid2) +{ + return uuid_compare(uuid1->uuid, uuid2->uuid); +} + +#elif defined(__NetBSD__) + +#include +#include +#include + +typedef uuid_t blk_uuid_t; + +static inline int blk_uuid_is_nil(blk_uuid_t *uuid) +{ + uint32_t status; + return uuid_is_nil((uuid_t *)uuid, &status); +} + +static inline void blk_uuid_generate(blk_uuid_t *uuid) +{ + uint32_t status; + uuid_create((uuid_t *)uuid, &status); +} + +static inline void blk_uuid_to_string(blk_uuid_t *uuid, char *out, size_t size) +{ + uint32_t status; + char *_out = NULL; + uuid_to_string((uuid_t *)uuid, &_out, &status); + strlcpy(out, _out, size); + free(_out); +} + +static inline void blk_uuid_from_string(blk_uuid_t *uuid, const char *in) +{ + uint32_t status; + uuid_from_string(in, (uuid_t *)uuid, &status); +} + +static inline void blk_uuid_copy(blk_uuid_t *dst, blk_uuid_t *src) +{ + memcpy((uuid_t *)dst, (uuid_t *)src, sizeof(uuid_t)); +} + +static inline void blk_uuid_clear(blk_uuid_t *uuid) +{ + memset((uuid_t *)uuid, 0, sizeof(uuid_t)); +} + +static inline int blk_uuid_compare(blk_uuid_t *uuid1, blk_uuid_t *uuid2) +{ + uint32_t status; + return uuid_compare((uuid_t *)uuid1, (uuid_t *)uuid2, &status); +} + +#elif defined(_WIN32) +#include +typedef UUID blk_uuid_t; + +static size_t strlcpy( char *dst, const char *src, size_t size ) { + size_t srclen; + + size--; + srclen = strlen( src ); + + if( srclen > size ) + srclen = size; + + memcpy( dst, src, srclen ); + dst[srclen] = '\0'; + + return (srclen); +} +static inline int blk_uuid_is_nil( blk_uuid_t *uuid ) +{ + RPC_STATUS status; + return UuidIsNil( (uuid_t *)uuid, &status ); +} + +static inline void blk_uuid_generate( blk_uuid_t *uuid ) +{ + RPC_STATUS status; + status = UuidCreate( (uuid_t *)uuid ); + if( status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY ) + return; + abort(); +} + +static inline void blk_uuid_to_string( blk_uuid_t *uuid, char *out, size_t size ) +{ + RPC_CSTR _out = NULL; + if( UuidToString( (uuid_t *)uuid, &_out ) != RPC_S_OK ) + return; + strlcpy( out, (const char *)_out, size ); + RpcStringFree( &_out ); + return; +} + +static inline void blk_uuid_from_string( blk_uuid_t *uuid, const char *in ) +{ + UuidFromString( (RPC_CSTR)in, (uuid_t *)uuid ); +} + +static inline void blk_uuid_copy( blk_uuid_t *dst, blk_uuid_t *src ) +{ + memcpy( (uuid_t *)dst, (uuid_t *)src, sizeof( uuid_t ) ); +} + +static inline void blk_uuid_clear( blk_uuid_t *uuid ) +{ + memset( (uuid_t *)uuid, 0, sizeof( uuid_t ) ); +} + +static inline int blk_uuid_compare( blk_uuid_t *uuid1, blk_uuid_t *uuid2 ) +{ + RPC_STATUS status; + return UuidCompare( (uuid_t *)uuid1, (uuid_t *)uuid2, &status ); +} +#else + +#error "Please update blk_uuid.h for your OS" + +#endif + +#endif /* __BLKTAP2_UUID_H__ */ diff --git a/extracters/libvhd/include/libvhd.h b/extracters/libvhd/include/libvhd.h new file mode 100644 index 0000000..6f607f5 --- /dev/null +++ b/extracters/libvhd/include/libvhd.h @@ -0,0 +1,363 @@ +/* Copyright (c) 2008, XenSource 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. +*/ +/* Modified March 2016 Timothe Litt to work under windows. +* Copyright (C) 2016 Timothe Litt +* Modifications subject to the same license terms as above, +* substituting "Timothe Litt" for "XenSource Inc." +*/ + +#ifndef _VHD_LIB_H_ +#define _VHD_LIB_H_ + +#define _CRT_SECURE_NO_WARNINGS 1 + +#include +#if defined(__linux__) +#include +#include +#elif defined(__NetBSD__) +#include +#include +#elif defined(_WIN32) +#undef BYTE_ORDER +#undef LITTLE_ENDIAN +#define BYTE_ORDER 1234 +#define LITTLE_ENDIAN 1234 +#define bswap_16(val) _byteswap_ushort(val) +#define bswap_32(val) _byteswap_ulong(val) +#define bswap_64(val) _byteswap_uint64(val) +#endif + +#include "blk_uuid.h" +#include "vhd.h" + +#ifndef O_LARGEFILE +#define O_LARGEFILE 0 +#endif + +#if defined(_WIN32) && defined(LIBVHD_DLL) +#ifdef LIBVHD_EXPORTS +#define LIBVHD_API __declspec(dllexport) +#else +#define LIBVHD_API __declspec(dllimport) +#endif +#else +#define LIBVHD_API +#endif + +#if BYTE_ORDER == LITTLE_ENDIAN +#if defined(__linux__) || defined(_WIN32) + #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 VHD_MAX_NAME_LEN 1024 + +#define VHD_BLOCK_SHIFT 21 +#define VHD_BLOCK_SIZE (1ULL << VHD_BLOCK_SHIFT) + +#define UTF_16 "UTF-16" +#define UTF_16LE "UTF-16LE" +#define UTF_16BE "UTF-16BE" + +#define VHD_OPEN_RDONLY 0x00001 +#define VHD_OPEN_RDWR 0x00002 +#define VHD_OPEN_FAST 0x00004 +#define VHD_OPEN_STRICT 0x00008 +#define VHD_OPEN_IGNORE_DISABLED 0x00010 + +#define VHD_FLAG_CREAT_PARENT_RAW 0x00001 + +#define vhd_flag_set(word, flag) ((word) |= (flag)) +#define vhd_flag_clear(word, flag) ((word) &= ~(flag)) +#define vhd_flag_test(word, flag) ((word) & (flag)) + + +#ifndef _WIN32x +#define ENABLE_FAILURE_TESTING 1 +#endif +#define FAIL_REPARENT_BEGIN 0 +#define FAIL_REPARENT_LOCATOR 1 +#define FAIL_REPARENT_END 2 +#define FAIL_RESIZE_BEGIN 3 +#define FAIL_RESIZE_DATA_MOVED 4 +#define FAIL_RESIZE_METADATA_MOVED 5 +#define FAIL_RESIZE_END 6 +#define NUM_FAIL_TESTS 7 + +#ifdef ENABLE_FAILURE_TESTING +#define TEST_FAIL_AT(point) \ + if (TEST_FAIL[point]) { \ + printf("Failing at %s\n", ENV_VAR_FAIL[point]); exit(EINVAL); } +#ifdef _WIN32 +#define TEST_FAIL_EXTERN_VARS \ +LIBVHD_API extern const char* ENV_VAR_FAIL[]; \ +LIBVHD_API extern int TEST_FAIL[]; +#else +#define TEST_FAIL_EXTERN_VARS \ +extern const char* ENV_VAR_FAIL[]; \ +extern int TEST_FAIL[]; +#endif +#else +#define TEST_FAIL_AT(point) +#define TEST_FAIL_EXTERN_VARS +#endif /* ENABLE_FAILURE_TESTING */ + + +static const char VHD_POISON_COOKIE[] = "v_poison"; + +typedef struct hd_ftr vhd_footer_t; +typedef struct dd_hdr vhd_header_t; +typedef struct vhd_bat vhd_bat_t; +typedef struct vhd_batmap vhd_batmap_t; +typedef struct dd_batmap_hdr vhd_batmap_header_t; +typedef struct prt_loc vhd_parent_locator_t; +typedef struct vhd_context vhd_context_t; +typedef uint32_t vhd_flag_creat_t; + +struct vhd_bat { + uint32_t spb; + uint32_t entries; + uint32_t *bat; +}; + +struct vhd_batmap { + vhd_batmap_header_t header; + char *map; +}; + +struct vhd_context { + int fd; + char *file; + int oflags; + int is_block; + + uint32_t spb; + uint32_t bm_secs; + + vhd_header_t header; + vhd_footer_t footer; + vhd_bat_t bat; + vhd_batmap_t batmap; +}; + +static inline uint32_t +secs_round_up(uint64_t bytes) +{ + return (uint32_t)((bytes + (VHD_SECTOR_SIZE - 1)) >> VHD_SECTOR_SHIFT); +} + +static inline uint32_t +secs_round_up_no_zero(uint64_t bytes) +{ + uint32_t result; + + result = secs_round_up(bytes); + return ( result? result : 1); +} + +static inline uint64_t +vhd_sectors_to_bytes(uint64_t sectors) +{ + return sectors << VHD_SECTOR_SHIFT; +} + +static inline uint64_t +vhd_bytes_padded(uint64_t bytes) +{ + return vhd_sectors_to_bytes(secs_round_up_no_zero(bytes)); +} + +static inline int +vhd_type_dynamic(vhd_context_t *ctx) +{ + return (ctx->footer.type == HD_TYPE_DYNAMIC || + ctx->footer.type == HD_TYPE_DIFF); +} + +static inline int +vhd_creator_tapdisk(vhd_context_t *ctx) +{ + return !strncmp(ctx->footer.crtr_app, "tap", 3); +} + +static inline int +vhd_disabled(vhd_context_t *ctx) +{ + return (!memcmp(ctx->footer.cookie, + VHD_POISON_COOKIE, sizeof(ctx->footer.cookie))); +} + +static inline size_t +vhd_parent_locator_size(vhd_parent_locator_t *loc) +{ + /* + * MICROSOFT_COMPAT + * data_space *should* be in sectors, + * but sometimes we find it in bytes + */ + if (loc->data_space < 512) + return (size_t)vhd_sectors_to_bytes(loc->data_space); + else if (loc->data_space % 512 == 0) + return loc->data_space; + else + return 0; +} + +static inline int +vhd_parent_raw(vhd_context_t *ctx) +{ + return blk_uuid_is_nil(&ctx->header.prt_uuid); +} + +LIBVHD_API void libvhd_set_log_level(int); + +LIBVHD_API int vhd_test_file_fixed(const char *, int *); + +LIBVHD_API uint32_t vhd_time(time_t time); +LIBVHD_API size_t vhd_time_to_string(uint32_t timestamp, char *target); +LIBVHD_API uint32_t vhd_chs(uint64_t size); + +LIBVHD_API uint32_t vhd_checksum_footer(vhd_footer_t *); +LIBVHD_API uint32_t vhd_checksum_header(vhd_header_t *); +LIBVHD_API uint32_t vhd_checksum_batmap(vhd_batmap_t *); + +LIBVHD_API void vhd_footer_in(vhd_footer_t *); +LIBVHD_API void vhd_footer_out(vhd_footer_t *); +LIBVHD_API void vhd_header_in(vhd_header_t *); +LIBVHD_API void vhd_header_out(vhd_header_t *); +LIBVHD_API void vhd_bat_in(vhd_bat_t *); +LIBVHD_API void vhd_bat_out(vhd_bat_t *); +LIBVHD_API void vhd_batmap_header_in(vhd_batmap_t *); +LIBVHD_API void vhd_batmap_header_out(vhd_batmap_t *); + +LIBVHD_API int vhd_validate_footer(vhd_footer_t *footer); +LIBVHD_API int vhd_validate_header(vhd_header_t *header); +LIBVHD_API int vhd_validate_batmap_header(vhd_batmap_t *batmap); +LIBVHD_API int vhd_validate_batmap(vhd_batmap_t *batmap); +LIBVHD_API int vhd_validate_platform_code(uint32_t code); + +LIBVHD_API int vhd_open(vhd_context_t *, const char *file, int flags); +LIBVHD_API void vhd_close(vhd_context_t *); +LIBVHD_API int vhd_create(const char *name, uint64_t bytes, int type, 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 */ +LIBVHD_API int vhd_snapshot(const char *snapshot, uint64_t bytes, const char *parent, + vhd_flag_creat_t); + +LIBVHD_API int vhd_hidden(vhd_context_t *, int *); +LIBVHD_API int vhd_chain_depth(vhd_context_t *, int *); + +LIBVHD_API off_t vhd_position(vhd_context_t *); +LIBVHD_API int vhd_seek(vhd_context_t *, off_t, int); +LIBVHD_API int vhd_read(vhd_context_t *, void *, size_t); +LIBVHD_API int vhd_write(vhd_context_t *, void *, size_t); + +LIBVHD_API int vhd_offset(vhd_context_t *, uint32_t, uint32_t *); + +LIBVHD_API int vhd_end_of_headers(vhd_context_t *ctx, off_t *off); +LIBVHD_API int vhd_end_of_data(vhd_context_t *ctx, off_t *off); +LIBVHD_API int vhd_batmap_header_offset(vhd_context_t *ctx, off_t *off); + +LIBVHD_API int vhd_get_header(vhd_context_t *); +LIBVHD_API int vhd_get_footer(vhd_context_t *); +LIBVHD_API int vhd_get_bat(vhd_context_t *); +LIBVHD_API int vhd_get_batmap(vhd_context_t *); + +LIBVHD_API void vhd_put_header(vhd_context_t *); +LIBVHD_API void vhd_put_footer(vhd_context_t *); +LIBVHD_API void vhd_put_bat(vhd_context_t *); +LIBVHD_API void vhd_put_batmap(vhd_context_t *); + +LIBVHD_API int vhd_has_batmap(vhd_context_t *); +LIBVHD_API int vhd_batmap_test(vhd_context_t *, vhd_batmap_t *, uint32_t); +LIBVHD_API void vhd_batmap_set(vhd_context_t *, vhd_batmap_t *, uint32_t); +LIBVHD_API void vhd_batmap_clear(vhd_context_t *, vhd_batmap_t *, uint32_t); + +LIBVHD_API int vhd_get_phys_size(vhd_context_t *, off_t *); +LIBVHD_API int vhd_set_phys_size(vhd_context_t *, off_t); + +LIBVHD_API int vhd_bitmap_test(vhd_context_t *, char *, uint32_t); +LIBVHD_API void vhd_bitmap_set(vhd_context_t *, char *, uint32_t); +LIBVHD_API void vhd_bitmap_clear(vhd_context_t *, char *, uint32_t); + +LIBVHD_API int vhd_parent_locator_count(vhd_context_t *); +LIBVHD_API int vhd_parent_locator_get(vhd_context_t *, char **); +LIBVHD_API int vhd_parent_locator_read(vhd_context_t *, vhd_parent_locator_t *, char **); +LIBVHD_API int vhd_find_parent(vhd_context_t *, const char *, char **); +LIBVHD_API int vhd_parent_locator_write_at(vhd_context_t *, const char *, + off_t, uint32_t, size_t, + vhd_parent_locator_t *); + +LIBVHD_API int vhd_header_decode_parent(vhd_context_t *, vhd_header_t *, char **); +LIBVHD_API int vhd_change_parent(vhd_context_t *, char *parent_path, int raw); + +LIBVHD_API int vhd_read_footer(vhd_context_t *, vhd_footer_t *); +LIBVHD_API int vhd_read_footer_at(vhd_context_t *, vhd_footer_t *, off_t); +LIBVHD_API int vhd_read_footer_strict(vhd_context_t *, vhd_footer_t *); +LIBVHD_API int vhd_read_header(vhd_context_t *, vhd_header_t *); +LIBVHD_API int vhd_read_header_at(vhd_context_t *, vhd_header_t *, off_t); +LIBVHD_API int vhd_read_bat(vhd_context_t *, vhd_bat_t *); +LIBVHD_API int vhd_read_batmap(vhd_context_t *, vhd_batmap_t *); +LIBVHD_API int vhd_read_bitmap(vhd_context_t *, uint32_t block, char **bufp); +LIBVHD_API int vhd_read_block(vhd_context_t *, uint32_t block, char **bufp); + +LIBVHD_API int vhd_write_footer(vhd_context_t *, vhd_footer_t *); +LIBVHD_API int vhd_write_footer_at(vhd_context_t *, vhd_footer_t *, off_t); +LIBVHD_API int vhd_write_header(vhd_context_t *, vhd_header_t *); +LIBVHD_API int vhd_write_header_at(vhd_context_t *, vhd_header_t *, off_t); +LIBVHD_API int vhd_write_bat(vhd_context_t *, vhd_bat_t *); +LIBVHD_API int vhd_write_batmap(vhd_context_t *, vhd_batmap_t *); +LIBVHD_API int vhd_write_bitmap(vhd_context_t *, uint32_t block, char *bitmap); +LIBVHD_API int vhd_write_block(vhd_context_t *, uint32_t block, char *data); + +LIBVHD_API int vhd_io_read(vhd_context_t *, char *, uint64_t, uint32_t); +LIBVHD_API int vhd_io_write(vhd_context_t *, char *, uint64_t, uint32_t); + +#endif diff --git a/extracters/libvhd/include/relative-path.h b/extracters/libvhd/include/relative-path.h new file mode 100644 index 0000000..18ed134 --- /dev/null +++ b/extracters/libvhd/include/relative-path.h @@ -0,0 +1,59 @@ +/* Copyright (c) 2008, XenSource 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. +*/ +/* Modified March 2016 Timothe Litt to work under windows. +* Copyright (C) 2016 Timothe Litt +* Modifications subject to the same license terms as above, +* substituting "Timothe Litt" for "XenSource Inc." +*/ + +#ifndef _RELATIVE_PATH_H_ +#define _RELATIVE_PATH_H_ + +#define MAX_NAME_LEN 1000 + +#if defined(_WIN32) && defined(LIBVHD_DLL) +#ifdef LIBVHD_EXPORTS +#define LIBVHD_API __declspec(dllexport) +#else +#define LIBVHD_API __declspec(dllimport) +#endif +#else +#define LIBVHD_API +#endif + +/* + * returns a relative path from @src to @dest + * result should be freed + */ +LIBVHD_API char *relative_path_to(char *src, char *dest, int *err); + +#ifdef _WIN32 +LIBVHD_API char *realpath( const char *path, char *resolved ); +LIBVHD_API int asprintf( char **result, const char *fmt, ... ); +#endif + +#endif diff --git a/extracters/libvhd/include/vhd.h b/extracters/libvhd/include/vhd.h new file mode 100644 index 0000000..bc52c57 --- /dev/null +++ b/extracters/libvhd/include/vhd.h @@ -0,0 +1,228 @@ +/* Copyright (c) 2008, XenSource 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. +*/ +/* Modified March 2016 Timothe Litt to work under windows. +* Copyright (C) 2016 Timothe Litt +* Modifications subject to the same license terms as above, +* substituting "Timothe Litt" for "XenSource Inc." +*/ +#ifndef __VHD_H__ +#define __VHD_H__ + +#include + +typedef uint32_t u32; +typedef uint64_t u64; + +/* #define DEBUG 1 */ + +/* ---------------------------------------------------------------------- */ +/* General definitions. */ +/* ---------------------------------------------------------------------- */ + +#define VHD_SECTOR_SIZE 512 +#define VHD_SECTOR_SHIFT 9 + +/* ---------------------------------------------------------------------- */ +/* This is the generic disk footer, used by all disks. */ +/* ---------------------------------------------------------------------- */ + +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. */ + blk_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 */ +}; + +/* VHD cookie string. */ +static const char HD_COOKIE[9] = "conectix"; + +/* Feature fields in hd_ftr */ +#define HD_NO_FEATURES 0x00000000 +#define HD_TEMPORARY 0x00000001 /* disk can be deleted on shutdown */ +#define HD_RESERVED 0x00000002 /* NOTE: must always be set */ + +/* Version field in hd_ftr */ +#define HD_FF_VERSION 0x00010000 + +/* Known creator OS type fields in hd_ftr.crtr_os */ +#define HD_CR_OS_WINDOWS 0x5769326B /* (Wi2k) */ +#define HD_CR_OS_MACINTOSH 0x4D616320 /* (Mac ) */ +#define HD_CR_OS_UNIX 0x556E6978 /* (Unix) */ +#define HD_CR_OS_VMS 0x4F564D53 /* (OVMS) */ +/* + * version 0.1: little endian bitmaps + * version 1.1: big endian bitmaps; batmap + * version 1.2: libvhd + * version 1.3: batmap version bump to 1.2 + */ +#define VHD_VERSION(major, minor) (((major) << 16) | ((minor) & 0x0000FFFF)) +#define VHD_CURRENT_VERSION VHD_VERSION(1, 3) + +/* Disk geometry accessor macros. */ +/* Geometry is a triple of (cylinders (2 bytes), tracks (1 byte), and + * secotrs-per-track (1 byte)) + */ +#define GEOM_GET_CYLS(_g) (((_g) >> 16) & 0xffff) +#define GEOM_GET_HEADS(_g) (((_g) >> 8) & 0xff) +#define GEOM_GET_SPT(_g) ((_g) & 0xff) + +#define GEOM_ENCODE(_c, _h, _s) (((_c) << 16) | ((_h) << 8) | (_s)) + +/* type field in hd_ftr */ +#define HD_TYPE_NONE 0 +#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 */ +#ifdef __GNUC__ +__attribute__((unused)) +#endif +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 Codes */ +#define PLAT_CODE_NONE 0x0 +#define PLAT_CODE_WI2R 0x57693272 /* deprecated */ +#define PLAT_CODE_WI2K 0x5769326B /* deprecated */ +#define PLAT_CODE_W2RU 0x57327275 /* Windows relative path (UTF-16) */ +#define PLAT_CODE_W2KU 0x57326B75 /* Windows absolute path (UTF-16) */ +#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. */ +/* ---------------------------------------------------------------------- */ + +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. */ + blk_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. */ +}; + +/* VHD cookie string. */ +static const char DD_COOKIE[9] = "cxsparse"; + +/* Version field in hd_ftr */ +#define DD_VERSION 0x00010000 + +/* Default blocksize is 2 meg. */ +#define DD_BLOCKSIZE_DEFAULT 0x00200000 + +#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 */ +}; + +static const char VHD_BATMAP_COOKIE[9] = "tdbatmap"; + +/* + * 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: + * + * +-------------------------------------------------+ + * | Mirror image of HD footer (hd_ftr) (512 bytes) | + * +-------------------------------------------------+ + * | Sparse drive header (dd_hdr) (1024 bytes) | + * +-------------------------------------------------+ + * | BAT (Block allocation table) | + * | - Array of absolute sector offsets into the | + * | file (u32). | + * | - Rounded up to a sector boundary. | + * | - Unused entries are marked as 0xFFFFFFFF | + * | - max entries in dd_hdr->max_bat_size | + * +-------------------------------------------------+ + * | Data Block 0 | + * | Bitmap (padded to 512 byte sector boundary) | + * | - each bit indicates whether the associated | + * | sector within this block is used. | + * | Data | + * | - power-of-two multiple of sectors. | + * | - default 2MB (4096 * 512) | + * | - Any entries with zero in bitmap should be | + * | zero on disk | + * +-------------------------------------------------+ + * | Data Block 1 | + * +-------------------------------------------------+ + * | ... | + * +-------------------------------------------------+ + * | Data Block n | + * +-------------------------------------------------+ + * | HD Footer (511 bytes) | + * +-------------------------------------------------+ + */ + +#endif diff --git a/extracters/libvhd/libvhd.c b/extracters/libvhd/libvhd.c new file mode 100644 index 0000000..b8c90d5 --- /dev/null +++ b/extracters/libvhd/libvhd.c @@ -0,0 +1,4190 @@ +/* Copyright (c) 2008, XenSource 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. +*/ +/* Modified March 2016 Timothe Litt to work under windows. +* Copyright (C) 2016 Timothe Litt +* Modifications subject to the same license terms as above, +* substituting "Timothe Litt" for "XenSource Inc." +*/ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#include +#define strdup _strdup +#define open _open +#ifndef O_BINARY +#define O_BINARY _O_BINARY +#endif +#define close _close +#define unlink _unlink +#define lseek _lseeki64 +#define read _read +#define write _write +#else +#include +#include +#define _aligned_free free +#include +#include +#include +#ifndef O_BINARY +#define O_BINARY 0 +#endif +#endif +#include +#include +#include +#include +#include "libvhd.h" +#include "relative-path.h" + +#ifndef O_DIRECT +#define O_DIRECT 0 +#endif + +static int libvhd_dbg = 0; + +/* Extension to Xen. Interactive utilities really shouldn't send + * errors to syslog. Set log_level to -1 for them to go to stderr. + * Existing callers will still go to syslog, as they all seem to use (1). + * + * On systems without syslog, stderr is the only choice. + */ + +void +libvhd_set_log_level(int level) +{ + if (level) + libvhd_dbg = level; +} + +#define FN __func__ + +#define VHDLOG(a) vhd_log_error a + +static void vhd_log_error( const char *func, const char *fmt, ... ) +#ifdef __GNUC__ + __attribute__((format(printf,2,3))); +#else + ; +#endif + +#ifndef LIBVHD_HAS_SYSLOG +#ifdef _WIN32 +#define LIBVHD_HAS_SYSLOG 0 +#else +#define LIBVHD_HAS_SYSLOG 1 +#endif +#endif + +static void vhd_log_error( const char *func, const char *fmt, ... ) +{ + char *buf, nilbuf; + size_t ilen, len; + va_list ap; + + if( !libvhd_dbg ) + return; + + ilen = sizeof( "libvhd::%s: " ) + strlen( func ) -2; + va_start(ap, fmt ); + len = vsnprintf( &nilbuf, 1, fmt, ap ); + va_end( ap ); + + if( (buf = malloc( ilen + len + 1 )) == NULL ) { + if( !LIBVHD_HAS_SYSLOG || libvhd_dbg < 0 ) + fprintf( stderr, "libvhd::%s: Out of memory for %s\n", func, fmt ); +#if LIBVHD_HAS_SYSLOG + if( libvhd_dbg > 0 ) + syslog( LOG_INFO, "libvhd::%s:: Out of memory", func ); +#endif + return; + } + va_start(ap, fmt); + (void) snprintf( buf, ilen, "libvhd::%s: ", func ); + (void) vsnprintf( buf + ilen -1, len+1, fmt, ap ); + va_end( ap ); + len += ilen -1; + if( buf[ len -1 ] != '\n' ) + buf[len++] = '\n'; + buf[len] = '\0'; + if( !LIBVHD_HAS_SYSLOG || libvhd_dbg < 0 ) + fputs( buf, stderr ); +#if LIBVHD_HAS_SYSLOG + if( libvhd_dbg > 0 ) + syslog(LOG_INFO, buf); +#endif + free( buf ); + + return; + } + +#ifdef _WIN32 +LIBVHD_API +char *dirname( char *path ) { + static char buf[MAX_PATH + 1]; + char *p, *d; + if( path == NULL || !*path ) + return "."; + strlcpy( buf, path, sizeof( buf ) ); + for( d = buf; *d && *d != ':' && *d != '/'; d++ ) + ; + if( !*d ) + return "."; + if( *d == ':' ) { + d++; + if( !strcmp( d, "." ) ) + return buf; + } + for( p = d + strlen( d ) - 1; p > d; p-- ) + if( *p != '/' ) + break; + if( p <= d ) { + *d++ = '/'; + *d = '\0'; + return buf; + } + *p = '\0'; + if( (p = strrchr( d, '/' )) == NULL ) { + *d++ = '.'; + *d = '\0'; + return buf; +} + if( p == d ) { + *p++ = '/'; + *p = '\0'; + } else + *p = '\0'; + return buf; +} +LIBVHD_API +char *basename( char *path ) { + static char buf[MAX_PATH + 1]; + char *p, *d; + if( path == NULL || !*path ) + return "."; + strlcpy( buf, path, sizeof( buf ) ); + for( d = buf; *d && *d != ':' && *d != '/'; d++ ) + ; + if( !*d ) + return buf; + if( *d == ':' ) { + d++; + if( !strcmp( d, "." ) ) + return buf; + } + for( p = d + strlen( d ) - 1; p > d; p-- ) + if( *p == '/' ) + *p = '\0'; + else + break; + if( p <= d ) { + if( !*++d ) + return "/"; + return d; + } + if( (p = strrchr( d, '/' )) == NULL ) { + return d; + } + return ++p; +} + +/* Partial simulation of iconv + * The usage here is restricted, always is just + * one string, so we don't have to worry about + * state. We do handle conversions among ASCII, + * UTF-8 and UTF-16. ASCII is defined as Latin-1. + */ + +typedef void *iconv_t; + +enum iconvcs { + icc_ascii, + icc_utf16, + icc_utf16be, + icc_utf16le, + icc_utf8 +}; +typedef struct iconvin { + enum iconvcs toset; + enum iconvcs fromset; +} iconvin_t; + +static int iconv_close( iconv_t cd ) { + if( cd != (iconv_t)-1 ) + free( cd ); + return 0; +} + +static enum iconvcs iconvcs( const char *setname ) { + if( !strcmp( setname, "ASCII" ) ) + return icc_ascii; + if( !strcmp( setname, "UTF-16" ) ) + return icc_utf16; + if( !strcmp( setname, "UTF-16BE" )) + return icc_utf16be; + if( !strcmp( setname, "UTF-16LE" )) + return icc_utf16le; + if( !strcmp( setname, "UTF-8" )) + return icc_utf8; + abort(); +} + +static iconv_t iconv_open( const char *toset, const char *fromset ) { + iconvin_t *ict; + + if( (ict = (iconvin_t *)malloc( sizeof( iconvin_t ) )) == NULL ) + return (iconv_t)-1; + + ict->toset = iconvcs( toset ); + ict->fromset = iconvcs( fromset ); + + return (iconv_t)ict; +} + +static size_t iconv( iconv_t cd, + const char **inbuf, size_t *inbytesleft, + char **outbuf, size_t *outbytesleft ) { + iconvin_t *ict; + size_t nonrev = 0; + const char *ip; + char *op; + enum iconvcs ics; + + ict = (iconvin_t *)cd; + + if( (inbuf == NULL || *inbuf == NULL) && (outbuf == NULL || *outbuf == NULL) ) + return 0; /* reset */ + + if( (inbuf == NULL || *inbuf == NULL) && (outbuf == NULL && *outbuf == NULL) ) + abort(); /* output shift */ + + if( inbuf == NULL || *inbuf == NULL || outbuf == NULL || *outbuf == NULL ) + abort(); /* undefined */ + + ip = *inbuf; + op = *outbuf; + + ics = ict->fromset; + + while( *inbytesleft && *outbytesleft ) { + size_t c, c2; + + switch( ics ) { + case icc_ascii: + c = *ip++; + --*inbytesleft; + break; + case icc_utf8: + c = *ip++; + --*inbytesleft; + if( !(c & 0x80) ) + break; + if( !*inbytesleft ) { + *inbuf = ip - 1; + *inbytesleft = 1; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + } + c2 = *ip++; + --*inbytesleft; + if( (c & 0xe0) == 0xc0 ) { + c = ((c & 0x1f) << 6) | (c2 & 0x3f); + break; + } + if( !*inbytesleft ) { + *inbuf = ip - 2; + *inbytesleft = 2; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + } + if( (c & 0xf0) == 0xe0 ) { + c = ((c & 0xf) << 12) | ((c2 & 0x3f) << 6) | (*ip++ & 0x3f); + --*inbytesleft; + break; + } + if( *inbytesleft < 2 ) { + *inbuf = ip - 2; + *inbytesleft += 2; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + } + if( (c & 0xf8) == 0xf0 ) { + c = ((c & 0x7) << 18) | ((c2 & 0x3f) << 12) | ((*ip++ & 0x3f) << 6); + c |= *ip++ & 0x3f; + *inbytesleft -= 2; + break; + } + *inbuf = ip-2; + *inbytesleft += 2; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + case icc_utf16: + if( *inbytesleft < 2 ) { + *inbuf = ip; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + } + *inbytesleft -= 2; + ip += 2; + if( ip[-2] == 0xfe && ip[-1] == 0xff ) { + ics = icc_utf16be; + continue; + } + if( ip[-2] == 0xff && ip[-1] == 0xfe ) { + ics = icc_utf16le; + continue; + } + c = (ip[-2] << 8) | ip[-1]; + if( c < 0xd800 || c >= 0xe000 ) + break; + if( *inbytesleft < 2 ) { + *inbuf = ip - 2; + *outbuf = op; + *inbytesleft += 2; + errno = EILSEQ; + return (size_t)-1; + } + *inbytesleft -= 2; + ip += 2; + c2 = (ip[-2] << 8) | ip[-1]; + if( c2 < 0xd800 || c2 >= 0xe000 ) { + *inbuf = ip - 4; + *outbuf = op; + *inbytesleft += 4; + errno = EILSEQ; + return (size_t)-1; + } + c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); + break; + case icc_utf16be: + if( *inbytesleft < 2 ) { + *inbuf = ip; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + } + *inbytesleft -= 2; + ip += 2; + c = (ip[-2] << 8) | ip[-1]; + if( c < 0xd800 || c >= 0xe000 ) + break; + if( *inbytesleft < 2 ) { + *inbuf = ip - 2; + *outbuf = op; + *inbytesleft += 2; + errno = EILSEQ; + return (size_t)-1; + } + *inbytesleft -= 2; + ip += 2; + c2 = (ip[-2] << 8) | ip[-1]; + if( c2 < 0xd800 || c2 >= 0xe000 ) { + *inbuf = ip - 4; + *outbuf = op; + *inbytesleft += 4; + errno = EILSEQ; + return (size_t)-1; + } + c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); + break; + case icc_utf16le: + if( *inbytesleft < 2 ) { + *inbuf = ip; + *outbuf = op; + errno = EINVAL; + return (size_t)-1; + } + *inbytesleft -= 2; + ip += 2; + c = (ip[-1] << 8) | ip[-2]; + if( c < 0xd800 || c >= 0xe000 ) + break; + if( *inbytesleft < 2 ) { + *inbuf = ip - 2; + *outbuf = op; + *inbytesleft += 2; + errno = EILSEQ; + return (size_t)-1; + } + *inbytesleft -= 2; + ip += 2; + c2 = (ip[-1] << 8) | ip[-2]; + if( c2 < 0xd800 || c2 >= 0xe000 ) { + *inbuf = ip - 4; + *outbuf = op; + *inbytesleft += 4; + errno = EILSEQ; + return (size_t)-1; + } + c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00); + break; + default: + abort(); + } + + switch( ict->toset ) { + case icc_ascii: + *op++ = c & 0xff; + --*outbytesleft; + if( c > 0xff ) + nonrev++; + continue; + case icc_utf8: + if( c <= 0x7F ) { + *op++ = c & 0x7f; + --*outbytesleft; + continue; + } + if( c <= 0x7ff ) { + if( *outbytesleft < 2 ) + break; + *op++ = (((c >> 6) & 0x1f) | 0xc0) & 0xff; + *op++ = ((c & 0x3f) | 0xc0) & 0xff; + *outbytesleft -= 2; + continue; + } + if( c <= 0xffff ) { + if( *outbytesleft < 3 ) + break; + *op++ = (((c >> 12) & 0xf) | 0xe0) & 0xff; + *op++ = (((c >> 6) & 0x3f) | 0xc0) & 0xff; + *op++ = ((c & 0x3f) | 0xc0) & 0xff; + *outbytesleft -= 3; + continue; + } + if( c <= 0x1fffff ) { + if( *outbytesleft < 4 ) + break; + *op++ = (((c >> 18) & 0x7) | 0xf0) & 0xff; + *op++ = (((c >> 12) & 0x3f) | 0xc0) & 0xff; + *op++ = (((c >> 6) & 0x3f) | 0xc0) & 0xff; + *op++ = ((c & 0x3f) | 0xc0) & 0xff; + *outbytesleft -= 4; + continue; + } + break; + case icc_utf16: + if( *outbytesleft < 2 ) + break; + if( c < 0x10000 ) { + *op++ = ( c >> 8 ) & 0xff; + *op++ = c & 0xff; + *outbytesleft -= 2; + continue; + } + if( *outbytesleft < 4 ) + break; + c2 = 0xDC00 + (c & 0x3FF); + c = (0xD800 - (0x10000 >> 10)) + (c >> 10); + op[0] = (c >> 8) & 0xff; + op[1] = c & 0xff; + op[2] = (c2 >> 8) & 0xff; + op[3] = c2 & 0xff; + op += 4; + *outbytesleft -=4; + continue; + case icc_utf16be: + if( *outbytesleft < 2 ) + break; + if( c < 0x10000 ) { + *op++ = ( c >> 8 ) & 0xff; + *op++ = c & 0xff; + *outbytesleft -= 2; + continue; + } + if( *outbytesleft < 4 ) + break; + c2 = 0xDC00 + (c & 0x3FF); + c = (0xD800 - (0x10000 >> 10)) + (c >> 10); + op[0] = (c >> 8) & 0xff; + op[1] = c & 0xff; + op[2] = (c2 >> 8) & 0xff; + op[3] = c2 & 0xff; + op += 4; + *outbytesleft -=4; + continue; + case icc_utf16le: + if( *outbytesleft < 2 ) + break; + if( c < 0x10000 ) { + *op++ = c & 0xff; + *op++ = (c >> 8) & 0xff; + *outbytesleft -= 2; + continue; + } + if( *outbytesleft < 4 ) + break; + c2 = 0xDC00 + (c & 0x3FF); + c = (0xD800 - (0x10000 >> 10)) + (c >> 10); + op[1] = (c >> 8) & 0xff; + op[0] = c & 0xff; + op[3] = (c2 >> 8) & 0xff; + op[2] = c2 & 0xff; + op += 4; + *outbytesleft -=4; + continue; + default: + abort(); + } + errno = E2BIG; + return (size_t)-1; + } + *inbuf = ip; + *outbuf = op; + + return nonrev; +} +#endif + +#define BIT_MASK 0x80 + +#ifdef ENABLE_FAILURE_TESTING +LIBVHD_API +const char* ENV_VAR_FAIL[NUM_FAIL_TESTS] = { + "VHD_UTIL_TEST_FAIL_REPARENT_BEGIN", + "VHD_UTIL_TEST_FAIL_REPARENT_LOCATOR", + "VHD_UTIL_TEST_FAIL_REPARENT_END", + "VHD_UTIL_TEST_FAIL_RESIZE_BEGIN", + "VHD_UTIL_TEST_FAIL_RESIZE_DATA_MOVED", + "VHD_UTIL_TEST_FAIL_RESIZE_METADATA_MOVED", + "VHD_UTIL_TEST_FAIL_RESIZE_END" +}; +LIBVHD_API +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 inline int +old_test_bit(volatile char *addr, int nr) +{ + return (((uint32_t *)addr)[nr >> 5] >> (nr & 31)) & 1; +} + +static inline void +old_set_bit(volatile char *addr, int nr) +{ + ((uint32_t *)addr)[nr >> 5] |= (1 << (nr & 31)); +} + +static inline void +old_clear_bit(volatile char *addr, int nr) +{ + ((uint32_t *)addr)[nr >> 5] &= ~(1 << (nr & 31)); +} + +void +vhd_footer_in(vhd_footer_t *footer) +{ + BE32_IN(&footer->features); + BE32_IN(&footer->ff_version); + BE64_IN(&footer->data_offset); + BE32_IN(&footer->timestamp); + BE32_IN(&footer->crtr_ver); + BE32_IN(&footer->crtr_os); + BE64_IN(&footer->orig_size); + BE64_IN(&footer->curr_size); + BE32_IN(&footer->geometry); + BE32_IN(&footer->type); + BE32_IN(&footer->checksum); +} + +void +vhd_footer_out(vhd_footer_t *footer) +{ + BE32_OUT(&footer->features); + BE32_OUT(&footer->ff_version); + BE64_OUT(&footer->data_offset); + BE32_OUT(&footer->timestamp); + BE32_OUT(&footer->crtr_ver); + BE32_OUT(&footer->crtr_os); + BE64_OUT(&footer->orig_size); + BE64_OUT(&footer->curr_size); + BE32_OUT(&footer->geometry); + BE32_OUT(&footer->type); + BE32_OUT(&footer->checksum); +} + +void +vhd_header_in(vhd_header_t *header) +{ + int i, n; + + BE64_IN(&header->data_offset); + BE64_IN(&header->table_offset); + BE32_IN(&header->hdr_ver); + BE32_IN(&header->max_bat_size); + BE32_IN(&header->block_size); + BE32_IN(&header->checksum); + BE32_IN(&header->prt_ts); + + n = sizeof(header->loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n; i++) { + BE32_IN(&header->loc[i].code); + BE32_IN(&header->loc[i].data_space); + BE32_IN(&header->loc[i].data_len); + BE64_IN(&header->loc[i].data_offset); + } +} + +void +vhd_header_out(vhd_header_t *header) +{ + int i, n; + + BE64_OUT(&header->data_offset); + BE64_OUT(&header->table_offset); + BE32_OUT(&header->hdr_ver); + BE32_OUT(&header->max_bat_size); + BE32_OUT(&header->block_size); + BE32_OUT(&header->checksum); + BE32_OUT(&header->prt_ts); + + n = sizeof(header->loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n; i++) { + BE32_OUT(&header->loc[i].code); + BE32_OUT(&header->loc[i].data_space); + BE32_OUT(&header->loc[i].data_len); + BE64_OUT(&header->loc[i].data_offset); + } +} + +void +vhd_batmap_header_in(vhd_batmap_t *batmap) +{ + BE64_IN(&batmap->header.batmap_offset); + BE32_IN(&batmap->header.batmap_size); + BE32_IN(&batmap->header.batmap_version); + BE32_IN(&batmap->header.checksum); +} + +void +vhd_batmap_header_out(vhd_batmap_t *batmap) +{ + BE64_OUT(&batmap->header.batmap_offset); + BE32_OUT(&batmap->header.batmap_size); + BE32_OUT(&batmap->header.batmap_version); + BE32_OUT(&batmap->header.checksum); +} + +void +vhd_bat_in(vhd_bat_t *bat) +{ + unsigned int i; + + for (i = 0; i < bat->entries; i++) + BE32_IN(&bat->bat[i]); +} + +void +vhd_bat_out(vhd_bat_t *bat) +{ + unsigned int i; + + for (i = 0; i < bat->entries; i++) + BE32_OUT(&bat->bat[i]); +} + +uint32_t +vhd_checksum_footer(vhd_footer_t *footer) +{ + size_t i; + unsigned char *blob; + uint32_t checksum, tmp; + + checksum = 0; + tmp = footer->checksum; + footer->checksum = 0; + + blob = (unsigned char *)footer; + for (i = 0; i < sizeof(vhd_footer_t); i++) + checksum += (uint32_t)blob[i]; + + footer->checksum = tmp; + return ~checksum; +} + +int +vhd_validate_footer(vhd_footer_t *footer) +{ + int csize; + uint32_t checksum; + + csize = sizeof(footer->cookie); + 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'; + VHDLOG((FN,"invalid footer cookie: %s\n", buf)); + return -EINVAL; + } + + checksum = vhd_checksum_footer(footer); + if (checksum != footer->checksum) { + /* + * early td-util did not re-calculate + * checksum when marking vhds 'hidden' + */ + if (footer->hidden && + !strncmp(footer->crtr_app, "tap", 3) && + (footer->crtr_ver == VHD_VERSION(0, 1) || + footer->crtr_ver == VHD_VERSION(1, 1))) { + char tmp = footer->hidden; + footer->hidden = 0; + checksum = vhd_checksum_footer(footer); + footer->hidden = tmp; + + if (checksum == footer->checksum) + return 0; + } + + VHDLOG((FN,"invalid footer checksum: " + "footer = 0x%08x, calculated = 0x%08x\n", + footer->checksum, checksum)); + return -EINVAL; + } + + return 0; +} + +uint32_t +vhd_checksum_header(vhd_header_t *header) +{ + size_t i; + unsigned char *blob; + uint32_t checksum, tmp; + + checksum = 0; + tmp = header->checksum; + header->checksum = 0; + + blob = (unsigned char *)header; + for (i = 0; i < sizeof(vhd_header_t); i++) + checksum += (uint32_t)blob[i]; + + header->checksum = tmp; + return ~checksum; +} + +int +vhd_validate_header(vhd_header_t *header) +{ + int i, n; + uint32_t checksum; + + if (memcmp(header->cookie, DD_COOKIE, 8) != 0) { + char buf[9]; + strncpy(buf, header->cookie, sizeof(buf)); + buf[sizeof(buf)-1]= '\0'; + VHDLOG((FN,"invalid header cookie: %s\n", buf)); + return -EINVAL; + } + + if (header->hdr_ver != 0x00010000) { + VHDLOG((FN,"invalid header version 0x%08x\n", header->hdr_ver)); + return -EINVAL; + } + + if (header->data_offset != 0xFFFFFFFFFFFFFFFF) { + VHDLOG((FN,"invalid header data_offset 0x%016"PRIx64"\n", + header->data_offset)); + return -EINVAL; + } + + n = sizeof(header->loc) / sizeof(vhd_parent_locator_t); + for (i = 0; i < n; i++) + if (vhd_validate_platform_code(header->loc[i].code)) + return -EINVAL; + + checksum = vhd_checksum_header(header); + if (checksum != header->checksum) { + VHDLOG((FN,"invalid header checksum: " + "header = 0x%08x, calculated = 0x%08x\n", + header->checksum, checksum)); + return -EINVAL; + } + + return 0; +} + +static inline int +vhd_validate_bat(vhd_bat_t *bat) +{ + if (!bat->bat) + return -EINVAL; + + return 0; +} + +uint32_t +vhd_checksum_batmap(vhd_batmap_t *batmap) +{ + u32 i, n; + char *blob; + uint32_t checksum; + + blob = batmap->map; + checksum = 0; + + n = (u32) vhd_sectors_to_bytes(batmap->header.batmap_size); + + for (i = 0; i < n; i++) { + if (batmap->header.batmap_version == VHD_BATMAP_VERSION(1, 1)) + checksum += (uint32_t)blob[i]; + else + checksum += (uint32_t)(unsigned char)blob[i]; + } + + return ~checksum; +} + +int +vhd_validate_batmap_header(vhd_batmap_t *batmap) +{ + if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, 8)) + return -EINVAL; + + if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION) + return -EINVAL; + + return 0; +} + +int +vhd_validate_batmap(vhd_batmap_t *batmap) +{ + uint32_t checksum; + + if (!batmap->map) + return -EINVAL; + + checksum = vhd_checksum_batmap(batmap); + if (checksum != batmap->header.checksum) + return -EINVAL; + + return 0; +} + +int +vhd_batmap_header_offset(vhd_context_t *ctx, off_t *_off) +{ + off_t off; + size_t bat; + + *_off = 0; + + off = (off_t)ctx->header.table_offset; + bat = ctx->header.max_bat_size * sizeof(uint32_t); + off += (off_t)vhd_bytes_padded(bat); + + *_off = off; + return 0; +} + +int +vhd_validate_platform_code(uint32_t code) +{ + switch (code) { + case PLAT_CODE_NONE: + case PLAT_CODE_WI2R: + case PLAT_CODE_WI2K: + case PLAT_CODE_W2RU: + case PLAT_CODE_W2KU: + case PLAT_CODE_MAC: + case PLAT_CODE_MACX: + return 0; + default: + VHDLOG((FN,"invalid parent locator code %u\n", code)); + return -EINVAL; + } +} + +int +vhd_parent_locator_count(vhd_context_t *ctx) +{ + return (sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t)); +} + +int +vhd_hidden(vhd_context_t *ctx, int *hidden) +{ + int err; + + *hidden = 0; + + if (vhd_type_dynamic(ctx) && vhd_creator_tapdisk(ctx) && + (ctx->footer.crtr_ver == VHD_VERSION(0, 1) || + ctx->footer.crtr_ver == VHD_VERSION(1, 1))) { + vhd_footer_t copy; + + err = vhd_read_footer_at(ctx, ©, 0); + if (err) { + VHDLOG((FN,"error reading backup footer of %s: %d\n", + ctx->file, err)); + return err; + } + *hidden = copy.hidden; + } else + *hidden = ctx->footer.hidden; + + return 0; +} + +int +vhd_chain_depth(vhd_context_t *ctx, int *depth) +{ + char *file; + int err, cnt; + vhd_context_t vhd, *cur; + + err = 0; + cnt = 0; + *depth = 0; + file = NULL; + cur = ctx; + + for (;;) { + cnt++; + + if (cur->footer.type != HD_TYPE_DIFF) + break; + + if (vhd_parent_raw(cur)) { + cnt++; + break; + } + + err = vhd_parent_locator_get(cur, &file); + if (err) { + file = NULL; + break; + } + + if (cur != ctx) { + vhd_close(cur); + cur = NULL; + } + + err = vhd_open(&vhd, file, VHD_OPEN_RDONLY); + if (err) + break; + + cur = &vhd; + free(file); + file = NULL; + } + + free(file); + if (cur && cur != ctx) + vhd_close(cur); + + if (!err) + *depth = cnt; + + return err; +} + +int +vhd_batmap_test(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block) +{ + if (!vhd_has_batmap(ctx) || !batmap->map) + return 0; + + if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3))) + return 0; + + return test_bit(batmap->map, block); +} + +void +vhd_batmap_set(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block) +{ + if (!vhd_has_batmap(ctx) || !batmap->map) + return; + + if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3))) + return; + + set_bit(batmap->map, block); +} + +void +vhd_batmap_clear(vhd_context_t *ctx, vhd_batmap_t *batmap, uint32_t block) +{ + if (!vhd_has_batmap(ctx) || !batmap->map) + return; + + if (block >= (batmap->header.batmap_size << (VHD_SECTOR_SHIFT + 3))) + return; + + clear_bit(batmap->map, block); +} + +int +vhd_bitmap_test(vhd_context_t *ctx, char *map, uint32_t block) +{ + if (vhd_creator_tapdisk(ctx) && + ctx->footer.crtr_ver == 0x00000001) + return old_test_bit(map, block); + + return test_bit(map, block); +} + +void +vhd_bitmap_set(vhd_context_t *ctx, char *map, uint32_t block) +{ + if( vhd_creator_tapdisk( ctx ) && + ctx->footer.crtr_ver == 0x00000001 ) { + old_set_bit( map, block ); + return; + } + + set_bit(map, block); + return; +} + +void +vhd_bitmap_clear(vhd_context_t *ctx, char *map, uint32_t block) +{ + if( vhd_creator_tapdisk( ctx ) && + ctx->footer.crtr_ver == 0x00000001 ) { + old_clear_bit( map, block ); + return; + } + + clear_bit(map, block); + return; +} + +/* + * returns absolute offset of the first + * byte of the file which is not vhd metadata + */ +int +vhd_end_of_headers(vhd_context_t *ctx, off_t *end) +{ + int err, i, n; + uint32_t bat_bytes; + off_t eom, bat_end; + vhd_parent_locator_t *loc; + + *end = 0; + + if (!vhd_type_dynamic(ctx)) + return 0; + + eom = (off_t)(ctx->footer.data_offset + sizeof(vhd_header_t)); + + bat_bytes = (uint32_t)vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); + bat_end = (off_t)(ctx->header.table_offset + bat_bytes); + + eom = MAX(eom, bat_end); + + if (vhd_has_batmap(ctx)) { + off_t hdr_end, hdr_secs, map_end, map_secs; + + err = vhd_get_batmap(ctx); + if (err) + return err; + + hdr_secs = secs_round_up_no_zero(sizeof(vhd_batmap_header_t)); + err = vhd_batmap_header_offset(ctx, &hdr_end); + if (err) + return err; + + hdr_end += (off_t)vhd_sectors_to_bytes(hdr_secs); + eom = MAX(eom, hdr_end); + + map_secs = ctx->batmap.header.batmap_size; + map_end = (off_t)(ctx->batmap.header.batmap_offset + + vhd_sectors_to_bytes(map_secs)); + eom = MAX(eom, map_end); + } + + /* parent locators */ + n = sizeof(ctx->header.loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n; i++) { + off_t loc_end; + + loc = &ctx->header.loc[i]; + if (loc->code == PLAT_CODE_NONE) + continue; + + loc_end = (off_t)(loc->data_offset + vhd_parent_locator_size(loc)); + eom = MAX(eom, loc_end); + } + + *end = eom; + return 0; +} + +int +vhd_end_of_data(vhd_context_t *ctx, off_t *end) +{ + unsigned int i; + int err; + off_t max; + uint64_t blk; + + if (!vhd_type_dynamic(ctx)) { + err = vhd_seek(ctx, 0, SEEK_END); + if (err) + return err; + + max = vhd_position(ctx); + if (max == (off_t)-1) + return -errno; + + *end = max - sizeof(vhd_footer_t); + return 0; + } + + err = vhd_end_of_headers(ctx, &max); + if (err) + return err; + + err = vhd_get_bat(ctx); + if (err) + return err; + + max >>= VHD_SECTOR_SHIFT; + + for (i = 0; i < ctx->bat.entries; i++) { + blk = ctx->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + blk += ctx->spb + ctx->bm_secs; + max = MAX((off_t)blk, max); + } + } + + *end = (off_t)vhd_sectors_to_bytes(max); + return 0; +} + +uint32_t +vhd_time(time_t time) +{ + struct tm tm; + time_t micro_epoch; + + memset(&tm, 0, sizeof(struct tm)); + tm.tm_year = 100; + tm.tm_mon = 0; + tm.tm_mday = 1; + micro_epoch = mktime(&tm); + + return (uint32_t)(time - micro_epoch); +} + +/* + * Stringify the VHD timestamp for printing. + * As with ctime_r, target must be >=26 bytes. + */ +size_t +vhd_time_to_string(uint32_t timestamp, char *target) +{ + char *cr; + struct tm tm; + time_t t1, t2; +#ifdef _WIN32 + __time32_t t3; +#endif + memset(&tm, 0, sizeof(struct tm)); + + /* VHD uses an epoch of 12:00AM, Jan 1, 2000. */ + /* Need to adjust this to the expected epoch of 1970. */ + tm.tm_year = 100; + tm.tm_mon = 0; + tm.tm_mday = 1; + + t1 = mktime(&tm); + t2 = t1 + (time_t)timestamp; +#ifdef _WIN32 + t3 = (__time32_t)t2; + _ctime32_s( target, 26, &t3 ); +#else + ctime_r(&t2, target); +#endif + /* handle mad ctime_r newline appending. */ + if ((cr = strchr(target, '\n')) != NULL) + *cr = '\0'; + + return (strlen(target)); +} + +/* + * nabbed from vhd specs. + */ +uint32_t +vhd_chs(uint64_t size) +{ + uint32_t secs, cylinders, heads, spt, cth; + + secs = secs_round_up_no_zero(size); + + if (secs > 65535 * 16 * 255) + secs = 65535 * 16 * 255; + + if (secs >= 65535 * 16 * 63) { + spt = 255; + cth = secs / spt; + heads = 16; + } else { + spt = 17; + cth = secs / spt; + heads = (cth + 1023) / 1024; + + if (heads < 4) + heads = 4; + + if (cth >= (heads * 1024) || heads > 16) { + spt = 31; + cth = secs / spt; + heads = 16; + } + + if (cth >= heads * 1024) { + spt = 63; + cth = secs / spt; + heads = 16; + } + } + + cylinders = cth / heads; + + return GEOM_ENCODE(cylinders, heads, spt); +} + +int +vhd_get_footer(vhd_context_t *ctx) +{ + if (!vhd_validate_footer(&ctx->footer)) + return 0; + + return vhd_read_footer(ctx, &ctx->footer); +} + +int +vhd_get_header(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + if (!vhd_validate_header(&ctx->header)) + return 0; + + return vhd_read_header(ctx, &ctx->header); +} + +int +vhd_get_bat(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + if (!vhd_validate_bat(&ctx->bat)) + return 0; + + vhd_put_bat(ctx); + return vhd_read_bat(ctx, &ctx->bat); +} + +int +vhd_get_batmap(vhd_context_t *ctx) +{ + if (!vhd_has_batmap(ctx)) + return -EINVAL; + + if (!vhd_validate_batmap(&ctx->batmap)) + return 0; + + vhd_put_batmap(ctx); + return vhd_read_batmap(ctx, &ctx->batmap); +} + +void +vhd_put_footer(vhd_context_t *ctx) +{ + memset(&ctx->footer, 0, sizeof(vhd_footer_t)); +} + +void +vhd_put_header(vhd_context_t *ctx) +{ + memset(&ctx->header, 0, sizeof(vhd_header_t)); +} + +void +vhd_put_bat(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return; + + _aligned_free(ctx->bat.bat); + memset(&ctx->bat, 0, sizeof(vhd_bat_t)); +} + +void +vhd_put_batmap(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return; + + if (!vhd_has_batmap(ctx)) + return; + + _aligned_free(ctx->batmap.map); + memset(&ctx->batmap, 0, sizeof(vhd_batmap_t)); +} + +/* + * look for 511 byte footer at end of file + */ +int +vhd_read_short_footer(vhd_context_t *ctx, vhd_footer_t *footer) +{ + int err; + char *buf; + off_t eof; + + buf = NULL; + + err = vhd_seek(ctx, 0, SEEK_END); + if (err) + goto out; + + eof = vhd_position(ctx); + if (eof == (off_t)-1) { + err = -errno; + goto out; + } + + err = vhd_seek(ctx, eof - 511, SEEK_SET); + if (err) + goto out; + +#ifdef _WIN32 + buf = _aligned_malloc( sizeof(vhd_footer_t), VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); + if (err) { + buf = NULL; + err = -err; + goto out; + } +#endif + + memset(buf, 0, sizeof(vhd_footer_t)); + + /* + * expecting short read here + */ + vhd_read(ctx, buf, sizeof(vhd_footer_t)); + + memcpy(footer, buf, sizeof(vhd_footer_t)); + + vhd_footer_in(footer); + err = vhd_validate_footer(footer); + +out: + if (err) + VHDLOG((FN,"%s: failed reading short footer: %d\n", + ctx->file, err)); + _aligned_free(buf); + return err; +} + +int +vhd_read_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off) +{ + int err; + char *buf; + + buf = NULL; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + +#ifdef _WIN32 + buf = _aligned_malloc( sizeof(vhd_footer_t), VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); + if (err) { + buf = NULL; + err = -err; + goto out; + } +#endif + + err = vhd_read(ctx, buf, sizeof(vhd_footer_t)); + if (err) + goto out; + + memcpy(footer, buf, sizeof(vhd_footer_t)); + + vhd_footer_in(footer); + err = vhd_validate_footer(footer); + +out: + if (err) + VHDLOG((FN,"%s: reading footer at 0x%08"PRIx64" failed: %d\n", + ctx->file, off, err)); + _aligned_free(buf); + return err; +} + +int +vhd_read_footer(vhd_context_t *ctx, vhd_footer_t *footer) +{ + int err; + off_t off; + + err = vhd_seek(ctx, 0, SEEK_END); + if (err) + return err; + + off = vhd_position(ctx); + if (off == (off_t)-1) + return -errno; + + err = vhd_read_footer_at(ctx, footer, off - 512); + if (err != -EINVAL) + return err; + + err = vhd_read_short_footer(ctx, footer); + if (err != -EINVAL) + return err; + + 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) +{ + int err; + char *buf; + + buf = NULL; + + if (!vhd_type_dynamic(ctx)) { + err = -EINVAL; + goto out; + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + +#ifdef _WIN32 + buf = _aligned_malloc( sizeof(vhd_header_t), VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, sizeof(vhd_header_t)); + if (err) { + buf = NULL; + err = -err; + goto out; + } +#endif + + err = vhd_read(ctx, buf, sizeof(vhd_header_t)); + if (err) + goto out; + + memcpy(header, buf, sizeof(vhd_header_t)); + + vhd_header_in(header); + err = vhd_validate_header(header); + +out: + if (err) + VHDLOG((FN,"%s: reading header at 0x%08"PRIx64" failed: %d\n", + ctx->file, off, err)); + _aligned_free(buf); + return err; +} + +int +vhd_read_header(vhd_context_t *ctx, vhd_header_t *header) +{ + off_t off; + + if (!vhd_type_dynamic(ctx)) { + VHDLOG((FN,"%s is not dynamic!\n", ctx->file)); + return -EINVAL; + } + + off = (off_t)ctx->footer.data_offset; + return vhd_read_header_at(ctx, header, off); +} + +int +vhd_read_bat(vhd_context_t *ctx, vhd_bat_t *bat) +{ + int err; + char *buf; + off_t off; + size_t size; + + buf = NULL; + + if (!vhd_type_dynamic(ctx)) { + err = -EINVAL; + goto fail; + } + + off = (off_t)ctx->header.table_offset; + size = (size_t)vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); + +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto fail; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + buf = NULL; + err = -err; + goto fail; + } +#endif + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + bat->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + bat->entries = ctx->header.max_bat_size; + bat->bat = (uint32_t *)buf; + + vhd_bat_in(bat); + + return 0; + +fail: + _aligned_free(buf); + memset(bat, 0, sizeof(vhd_bat_t)); + VHDLOG((FN,"%s: failed to read bat: %d\n", ctx->file, err)); + return err; +} + +static int +vhd_read_batmap_header(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + char *buf; + off_t off; + size_t size; + + buf = NULL; + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + goto fail; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + size = (size_t)vhd_bytes_padded(sizeof(vhd_batmap_header_t)); +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto fail; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + buf = NULL; + err = -err; + goto fail; + } +#endif + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + memcpy(&batmap->header, buf, sizeof(vhd_batmap_header_t)); + _aligned_free(buf); + buf = NULL; + + vhd_batmap_header_in(batmap); + + return 0; + +fail: + _aligned_free(buf); + memset(&batmap->header, 0, sizeof(vhd_batmap_header_t)); + VHDLOG((FN,"%s: failed to read batmap header: %d\n", ctx->file, err)); + return err; +} + +static int +vhd_read_batmap_map(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + char *buf; + off_t off; + size_t map_size; + + map_size = (size_t)vhd_sectors_to_bytes(batmap->header.batmap_size); + +#ifdef _WIN32 + buf = _aligned_malloc( map_size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto fail; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, map_size); + if (err) { + buf = NULL; + err = -err; + goto fail; + } +#endif + + off = (off_t)batmap->header.batmap_offset; + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + err = vhd_read(ctx, buf, map_size); + if (err) + goto fail; + + batmap->map = buf; + return 0; + +fail: + _aligned_free(buf); + batmap->map = NULL; + VHDLOG((FN,"%s: failed to read batmap: %d\n", ctx->file, err)); + return err; +} + +int +vhd_read_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + + if (!vhd_has_batmap(ctx)) + return -EINVAL; + + memset(batmap, 0, sizeof(vhd_batmap_t)); + + err = vhd_read_batmap_header(ctx, batmap); + if (err) + return err; + + err = vhd_validate_batmap_header(batmap); + if (err) + return err; + + err = vhd_read_batmap_map(ctx, batmap); + if (err) + return err; + + err = vhd_validate_batmap(batmap); + if (err) + goto fail; + + return 0; + +fail: + _aligned_free(batmap->map); + memset(batmap, 0, sizeof(vhd_batmap_t)); + return err; +} + +int +vhd_has_batmap(vhd_context_t *ctx) +{ + if (!vhd_type_dynamic(ctx)) + return 0; + + if (!vhd_creator_tapdisk(ctx)) + return 0; + + if (ctx->footer.crtr_ver <= VHD_VERSION(0, 1)) + return 0; + + if (ctx->footer.crtr_ver >= VHD_VERSION(1, 2)) + return 1; + + /* + * VHDs of version 1.1 probably have a batmap, but may not + * if they were updated from version 0.1 via vhd-update. + */ + if (!vhd_validate_batmap_header(&ctx->batmap)) + return 1; + + if (vhd_read_batmap_header(ctx, &ctx->batmap)) + return 0; + + return (!vhd_validate_batmap_header(&ctx->batmap)); +} + +/* + * Is this a block device (with a fixed size)? This affects whether the file + * can be truncated and where the footer is written for VHDs. + */ +int +vhd_test_file_fixed(const char *file, int *is_block) +{ + int err; + struct stat stats; + + err = stat(file, &stats); + if (err == -1) + return -errno; +#ifdef _WIN32 + *is_block = 0; /* Look at filename for initial \\?? */ +#else + *is_block = !!(S_ISBLK(stats.st_mode)); +#endif + return err; +} + +int +vhd_find_parent(vhd_context_t *ctx, const char *parent, char **_location) +{ + int err, isabs = 0, isrd = 0; + char *location, *cpath, *cdir, *path; + + err = 0; + path = NULL; + cpath = NULL; + location = NULL; + *_location = NULL; + + if (!parent) + return -EINVAL; + +#ifdef _WIN32 + if( parent[0] == '/' ) + isabs = 1; + else { + const char *p; + for( p = parent; *p && *p != '/' && *p != ':'; p++ ) + ; + isabs = ( *p == ':' ); + } + + if( isabs ) { + if( (isrd = _open( parent, _O_RDONLY | _O_BINARY )) == -1 ) + goto errout; + else { + _close( isrd ); + isrd = 1; + } + } +#else + if (parent[0] == '/') { + isabs = 1; + isrd = !access( parent, R_OK ); + } +#endif + if( isabs ) { + if( isrd ) { + path = strdup( parent ); + if( !path ) + return -ENOMEM; + *_location = path; + return 0; + } + goto errout; + } + + /* check parent path relative to child's directory */ + cpath = realpath(ctx->file, NULL); + if (!cpath) + goto errout; +#ifdef _WIN32 + cdir = dirname( cpath + 1 ); +#else + cdir = dirname( cpath ); +#endif + if (asprintf(&location, "%s/%s", cdir, parent) == -1) { + location = NULL; + goto errout; + } +#ifdef _WIN32 + if( (isrd = _open( location, _O_RDONLY | _O_BINARY )) == -1 ) + isrd = 0; + else { + _close( isrd ); + isrd = 1; + } +#else + isrd = !access( location, R_OK ); +#endif + if( isrd ) { + path = realpath( location, NULL ); + if( path ) { +#ifdef _WIN32 + *_location = strdup( path + 1 ); + free( path ); +#else + *_location = path; +#endif + free(cpath); + free(location); + return 0; + } + } + +errout: + err = -errno; + free(location); + free(cpath); + return err; +} + +static int +vhd_macx_encode_location(char *name, char **out, size_t *outlen) +{ + iconv_t cd; + int err; + size_t len; + size_t ibl, obl; + char *uri, *uri_utf8, *uri_utf8p, *ret; + const char *urip; + + err = 0; + ret = NULL; + *out = NULL; + *outlen = 0; + len = strlen(name) + strlen("file://"); + + ibl = len; + obl = len * 2; + + urip = uri = malloc(ibl + 1); + uri_utf8 = uri_utf8p = malloc(obl); + + if (!uri || !uri_utf8) + return -ENOMEM; + + cd = iconv_open("UTF-8", "ASCII"); + if (cd == (iconv_t)-1) { + err = -errno; + 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) { + err = (errno ? -errno : -EIO); + goto out; + } + len = (size_t)(uri_utf8p - uri_utf8); + ret = malloc(len); + if (!ret) { + err = -ENOMEM; + goto out; + } + + memcpy(ret, uri_utf8, len); + *outlen = len; + *out = ret; + + out: + free(uri); + free(uri_utf8); + if (cd != (iconv_t)-1) + iconv_close(cd); + + return err; +} + +static int +vhd_w2u_encode_location(char *name, char **out, size_t *outlen) +{ + int len, err; + size_t ibl, obl; + char *uri, *uri_utf16, *uri_utf16p, *tmp, *ret; + const char *urip; + iconv_t cd; + + cd = (iconv_t)-1; + + err = 0; + ret = NULL; + *out = NULL; + *outlen = 0; + + /* + * MICROSOFT_COMPAT + * relative paths must start with ".\" + */ + if (name[0] != '/') { + tmp = strstr(name, "./"); + if (tmp == name) + tmp += strlen("./"); + else + tmp = name; + + err = asprintf(&uri, ".\\%s", tmp); + } else +#ifdef _WIN32 + err = asprintf( &uri, "%s", name + 1 ); +#else + err = asprintf( &uri, "%s", name ); +#endif + + if (err == -1) + return -ENOMEM; + + tmp = uri; + while (*tmp != '\0') { + if (*tmp == '/') + *tmp = '\\'; + tmp++; + } + + len = strlen(uri); + ibl = len; + obl = len * 4; + urip = uri; + + uri_utf16 = uri_utf16p = malloc(obl); + if (!uri_utf16) { + err = -ENOMEM; + goto out; + } + + /* + * MICROSOFT_COMPAT + * little endian unicode here + */ + cd = iconv_open("UTF-16LE", "ASCII"); + if (cd == (iconv_t)-1) { + err = -errno; + goto out; + } + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &urip, &ibl, &uri_utf16p, &obl) == (size_t)-1 || + ibl) { + err = (errno ? -errno : -EIO); + goto out; + } + + len = (size_t)(uri_utf16p - uri_utf16); + ret = malloc(len); + if (!ret) { + err = -ENOMEM; + goto out; + } + + memcpy(ret, uri_utf16, len); + *outlen = len; + *out = ret; + err = 0; + + out: + free(uri); + free(uri_utf16); + if (cd != (iconv_t)-1) + iconv_close(cd); + + return err; +} + +static char * +vhd_macx_decode_location(const char *in, char *out, int len) +{ + iconv_t cd; + char *name; + size_t ibl, obl; + + name = out; + ibl = obl = len; + + cd = iconv_open("ASCII", "UTF-8"); + if (cd == (iconv_t)-1) + return NULL; + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &in, &ibl, &out, &obl) == (size_t)-1 || ibl) + return NULL; + + iconv_close(cd); + *out = '\0'; + + if (strstr(name, "file://") != name) + return NULL; + + name += strlen("file://"); + + return strdup(name); +} + +static char * +vhd_w2u_decode_location(const char *in, char *out, int len, char *utf_type) +{ + iconv_t cd; + char *name, *tmp; + size_t ibl, obl; + + tmp = name = out; + ibl = obl = len; + + cd = iconv_open("ASCII", utf_type); + if (cd == (iconv_t)-1) + return NULL; + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &in, &ibl, &out, &obl) == (size_t)-1 || ibl) { + iconv_close(cd); + return NULL; + } + + iconv_close(cd); + *out = '\0'; + + /* TODO: spaces */ + while (tmp != out) { + if (*tmp == '\\') + *tmp = '/'; + tmp++; + } + + if (strstr(name, "C:") == name || strstr(name, "c:") == name) + name += strlen("c:"); + return strdup(name); +} + +int +vhd_header_decode_parent(vhd_context_t *ctx, vhd_header_t *header, char **buf) +{ + char *code, out[512+1]; + + if (vhd_creator_tapdisk(ctx) && + ctx->footer.crtr_ver == VHD_VERSION(0, 1)) + code = UTF_16; + else + code = UTF_16BE; + + *buf = vhd_w2u_decode_location(header->prt_name, out, 512, code); + return (*buf == NULL ? -EINVAL : 0); +} + +int +vhd_parent_locator_read(vhd_context_t *ctx, + vhd_parent_locator_t *loc, char **parent) +{ + int err, size; + char *raw, *out, *name; + + raw = NULL; + out = NULL; + name = NULL; + *parent = NULL; + + if (ctx->footer.type != HD_TYPE_DIFF) { + err = -EINVAL; + goto out; + } + + switch (loc->code) { + case PLAT_CODE_MACX: + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + break; + default: + err = -EINVAL; + goto out; + } + + err = vhd_seek(ctx, (off_t)loc->data_offset, SEEK_SET); + if (err) + goto out; + + size = vhd_parent_locator_size(loc); + if (size <= 0) { + err = -EINVAL; + goto out; + } + +#ifdef _WIN32 + raw = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( raw == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&raw, VHD_SECTOR_SIZE, size); + if (err) { + raw = NULL; + err = -err; + goto out; + } +#endif + + err = vhd_read(ctx, raw, size); + if (err) + goto out; + + out = malloc(loc->data_len + 1); + if (!out) { + err = -ENOMEM; + goto out; + } + + switch (loc->code) { + case PLAT_CODE_MACX: + name = vhd_macx_decode_location(raw, out, loc->data_len); + break; + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + name = vhd_w2u_decode_location(raw, out, + loc->data_len, UTF_16LE); + break; + } + + if (!name) { + err = -EINVAL; + goto out; + } + + err = 0; + *parent = name; + +out: + _aligned_free(raw); + free(out); + + if (err) { + VHDLOG((FN,"%s: error reading parent locator: %d\n", + ctx->file, err)); + VHDLOG((FN,"%s: locator: code %u, space 0x%x, len 0x%x, " + "off 0x%"PRIx64"\n", ctx->file, loc->code, loc->data_space, + loc->data_len, loc->data_offset)); + } + + return err; +} + +int +vhd_parent_locator_get(vhd_context_t *ctx, char **parent) +{ + int i, n, err; + char *name, *location; + vhd_parent_locator_t *loc; + + err = 0; + *parent = NULL; + + if (ctx->footer.type != HD_TYPE_DIFF) + return -EINVAL; + + n = vhd_parent_locator_count(ctx); + for (i = 0; i < n; i++) { + loc = ctx->header.loc + i; + err = vhd_parent_locator_read(ctx, loc, &name); + if (err) + continue; + + err = vhd_find_parent(ctx, name, &location); + if (err) + VHDLOG((FN,"%s: couldn't find parent %s (%d)\n", + ctx->file, name, err)); + free(name); + + if (!err) { + *parent = location; + return 0; + } + } + + return err; +} + +int +vhd_parent_locator_write_at(vhd_context_t *ctx, + const char *parent, off_t off, uint32_t code, + size_t max_bytes, vhd_parent_locator_t *loc) +{ + struct stat stats; + int err; + size_t size, len; + char *absolute_path, *relative_path, *encoded, *block; + + memset(loc, 0, sizeof(vhd_parent_locator_t)); + + if (ctx->footer.type != HD_TYPE_DIFF) + return -EINVAL; + + absolute_path = NULL; + relative_path = NULL; + encoded = NULL; + block = NULL; + size = 0; + len = 0; + + switch (code) { + case PLAT_CODE_MACX: + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + break; + default: + return -EINVAL; + } + + absolute_path = realpath(parent, NULL); + if (!absolute_path) { + err = -errno; + goto out; + } + + #ifdef _WIN32 + err = stat( absolute_path+1, &stats ); +#else + err = stat( absolute_path, &stats ); +#endif + if (err) { + err = -errno; + goto out; + } + +#ifdef _WIN32 + /* Look at filename for initial \\ to replace _S_IFBLK?? */ + if (!(stats.st_mode & _S_IFREG) || (stats.st_mode & _S_IFDIR)) { + err = -EINVAL; + goto out; + } +#else + if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) { + err = -EINVAL; + goto out; + } +#endif + + relative_path = relative_path_to(ctx->file, absolute_path, &err); + if (!relative_path || err) { + err = (err ? err : -EINVAL); + goto out; + } + + switch (code) { + case PLAT_CODE_MACX: + err = vhd_macx_encode_location(relative_path, &encoded, &len); + break; + case PLAT_CODE_W2KU: + err = vhd_w2u_encode_location( absolute_path, &encoded, &len ); + break; + case PLAT_CODE_W2RU: + err = vhd_w2u_encode_location(relative_path, &encoded, &len); + break; + default: + err = -EINVAL; + } + + if (err) + goto out; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + size = (size_t)vhd_bytes_padded(len); + + if (max_bytes && size > max_bytes) { + err = -ENAMETOOLONG; + goto out; + } + +#ifdef _WIN32 + block = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( block == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&block, VHD_SECTOR_SIZE, size); + if (err) { + block = NULL; + err = -err; + goto out; + } +#endif + + memset(block, 0, size); + memcpy(block, encoded, len); + + err = vhd_write(ctx, block, size); + if (err) + goto out; + + err = 0; + +out: + free(absolute_path); + free(relative_path); + free(encoded); + _aligned_free(block); + + if (!err) { + loc->res = 0; + loc->code = code; + loc->data_len = len; + /* + * write number of bytes ('size') instead of number of sectors + * into loc->data_space to be compatible with MSFT, even though + * this goes against the specs + */ + loc->data_space = size; + loc->data_offset = off; + } + + return err; +} + +static int +vhd_footer_offset_at_eof(vhd_context_t *ctx, off_t *off) +{ + int err; + if ((err = vhd_seek(ctx, 0, SEEK_END))) + return errno; + *off = vhd_position(ctx) - sizeof(vhd_footer_t); + return 0; +} + +int +vhd_read_bitmap(vhd_context_t *ctx, uint32_t block, char **bufp) +{ + int err; + char *buf; + size_t size; + off_t off; + uint64_t blk; + + buf = NULL; + *bufp = NULL; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = (off_t)vhd_sectors_to_bytes(blk); + size = (size_t)vhd_bytes_padded(ctx->spb >> 3); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) + return -errno; +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; +#endif + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + *bufp = buf; + return 0; + +fail: + _aligned_free(buf); + return err; +} + +int +vhd_read_block(vhd_context_t *ctx, uint32_t block, char **bufp) +{ + int err; + char *buf; + size_t size; + uint64_t blk; + off_t end, off; + + buf = NULL; + *bufp = NULL; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = (off_t)vhd_sectors_to_bytes(blk + ctx->bm_secs); + size = (size_t)vhd_sectors_to_bytes(ctx->spb); + + err = vhd_footer_offset_at_eof(ctx, &end); + if (err) + return err; + +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto fail; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + goto fail; + } +#endif + + if (end < off + (off_t)ctx->header.block_size) { + size = end - off; + memset(buf + size, 0, ctx->header.block_size - size); + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto fail; + + err = vhd_read(ctx, buf, size); + if (err) + goto fail; + + *bufp = buf; + return 0; + +fail: + _aligned_free(buf); + return err; +} + +int +vhd_write_footer_at(vhd_context_t *ctx, vhd_footer_t *footer, off_t off) +{ + int err; + vhd_footer_t *f; + + f = NULL; + +#ifdef _WIN32 + f = _aligned_malloc( sizeof(vhd_footer_t), VHD_SECTOR_SIZE ); + if( f == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&f, + VHD_SECTOR_SIZE, sizeof(vhd_footer_t)); + if (err) { + f = NULL; + err = -err; + goto out; + } +#endif + + memcpy(f, footer, sizeof(vhd_footer_t)); + f->checksum = vhd_checksum_footer(f); + + err = vhd_validate_footer(f); + if (err) + goto out; + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + vhd_footer_out(f); + + err = vhd_write(ctx, f, sizeof(vhd_footer_t)); + +out: + if (err) + VHDLOG((FN,"%s: failed writing footer at 0x%08"PRIx64": %d\n", + ctx->file, off, err)); + _aligned_free(f); + return err; +} + +int +vhd_write_footer(vhd_context_t *ctx, vhd_footer_t *footer) +{ + int err; + off_t off; + + if (ctx->is_block) + err = vhd_footer_offset_at_eof(ctx, &off); + else + err = vhd_end_of_data(ctx, &off); + if (err) + return err; + + err = vhd_write_footer_at(ctx, footer, off); + if (err) + return err; + + if (!vhd_type_dynamic(ctx)) + return 0; + + return vhd_write_footer_at(ctx, footer, 0); +} + +int +vhd_write_header_at(vhd_context_t *ctx, vhd_header_t *header, off_t off) +{ + int err; + vhd_header_t *h; + + h = NULL; + + if (!vhd_type_dynamic(ctx)) { + err = -EINVAL; + goto out; + } + +#ifdef _WIN32 + h = _aligned_malloc( sizeof(vhd_header_t), VHD_SECTOR_SIZE ); + if( h == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&h, + VHD_SECTOR_SIZE, sizeof(vhd_header_t)); + if (err) { + h = NULL; + err = -err; + goto out; + } +#endif + + memcpy(h, header, sizeof(vhd_header_t)); + + h->checksum = vhd_checksum_header(h); + err = vhd_validate_header(h); + if (err) + goto out; + + vhd_header_out(h); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(ctx, h, sizeof(vhd_header_t)); + +out: + if (err) + VHDLOG((FN,"%s: failed writing header at 0x%08"PRIx64": %d\n", + ctx->file, off, err)); + _aligned_free(h); + return err; +} + +int +vhd_write_header(vhd_context_t *ctx, vhd_header_t *header) +{ + off_t off; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + off = (off_t)ctx->footer.data_offset; + return vhd_write_header_at(ctx, header, off); +} + +int +vhd_write_bat(vhd_context_t *ctx, vhd_bat_t *bat) +{ + int err; + off_t off; + vhd_bat_t b; + size_t size; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_validate_bat(&ctx->bat); + if (err) + return err; + + err = vhd_validate_bat(bat); + if (err) + return err; + + memset(&b, 0, sizeof(vhd_bat_t)); + + off = (off_t)ctx->header.table_offset; + size = (uint32_t)vhd_bytes_padded(bat->entries * sizeof(uint32_t)); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + +#ifdef _WIN32 + b.bat = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( b.bat == NULL ) + return -errno; +#else + err = posix_memalign((void **)&b.bat, VHD_SECTOR_SIZE, size); + if (err) + return -err; +#endif + + memcpy(b.bat, bat->bat, size); + b.spb = bat->spb; + b.entries = bat->entries; + vhd_bat_out(&b); + + err = vhd_write(ctx, b.bat, size); + _aligned_free(b.bat); + + return err; +} + +int +vhd_write_batmap(vhd_context_t *ctx, vhd_batmap_t *batmap) +{ + int err; + off_t off; + vhd_batmap_t b; + char *buf, *map; + size_t size, map_size; + + buf = NULL; + map = NULL; + + if (!vhd_has_batmap(ctx)) { + err = -EINVAL; + goto out; + } + + b.header = batmap->header; + b.map = batmap->map; + + b.header.checksum = vhd_checksum_batmap(&b); + err = vhd_validate_batmap(&b); + if (err) + goto out; + + off = (off_t)b.header.batmap_offset; + map_size = (size_t)vhd_sectors_to_bytes(b.header.batmap_size); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + +#ifdef _WIN32 + map = _aligned_malloc( map_size, VHD_SECTOR_SIZE ); + if( map == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&map, VHD_SECTOR_SIZE, map_size); + if (err) { + map = NULL; + err = -err; + goto out; + } +#endif + + memcpy(map, b.map, map_size); + + err = vhd_write(ctx, map, map_size); + if (err) + goto out; + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + goto out; + + size = (size_t)vhd_bytes_padded(sizeof(vhd_batmap_header_t)); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + goto out; + +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + buf = NULL; + goto out; + } +#endif + + vhd_batmap_header_out(&b); + memset(buf, 0, size); + memcpy(buf, &b.header, sizeof(vhd_batmap_header_t)); + + err = vhd_write(ctx, buf, size); + +out: + if (err) + VHDLOG((FN,"%s: failed writing batmap: %d\n", ctx->file, err)); + _aligned_free(buf); + _aligned_free(map); + return 0; +} + +int +vhd_write_bitmap(vhd_context_t *ctx, uint32_t block, char *bitmap) +{ + int err; + off_t off; + uint64_t blk; + size_t size; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_validate_bat(&ctx->bat); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + if ((unsigned long)bitmap & (VHD_SECTOR_SIZE - 1)) + return -EINVAL; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = (off_t)vhd_sectors_to_bytes(blk); + size = (size_t)vhd_sectors_to_bytes(ctx->bm_secs); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + err = vhd_write(ctx, bitmap, size); + if (err) + return err; + + return 0; +} + +int +vhd_write_block(vhd_context_t *ctx, uint32_t block, char *data) +{ + int err; + off_t off; + size_t size; + uint64_t blk; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + err = vhd_validate_bat(&ctx->bat); + if (err) + return err; + + if (block >= ctx->bat.entries) + return -ERANGE; + + if ((unsigned long)data & ~(VHD_SECTOR_SIZE -1)) + return -EINVAL; + + blk = ctx->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return -EINVAL; + + off = (off_t)vhd_sectors_to_bytes(blk + ctx->bm_secs); + size = (size_t)vhd_sectors_to_bytes(ctx->spb); + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + err = vhd_write(ctx, data, size); + if (err) + return err; + + return 0; +} + +static inline int +namedup(char **dup, const char *name) +{ + *dup = NULL; + + if (strnlen(name, MAX_NAME_LEN) >= MAX_NAME_LEN) + return -ENAMETOOLONG; + + *dup = strdup(name); + if (*dup == NULL) + return -ENOMEM; + + return 0; +} + +int +vhd_seek(vhd_context_t *ctx, off_t offset, int whence) +{ + off_t off; + + off = (off_t)lseek(ctx->fd, offset, whence); + if (off == (off_t)-1) { + VHDLOG((FN,"%s: seek(0x%08"PRIx64", %d) failed: %d\n", + ctx->file, offset, whence, -errno)); + return -errno; + } + + return 0; +} + +off_t +vhd_position(vhd_context_t *ctx) +{ + return (off_t)lseek(ctx->fd, 0, SEEK_CUR); +} + +int +vhd_read(vhd_context_t *ctx, void *buf, size_t size) +{ + size_t ret; + + errno = 0; + + ret = read(ctx->fd, buf, size); + if (ret == size) + return 0; + + VHDLOG((FN,"%s: read of %" PRIuMAX " returned %" PRIdMAX ", errno: %d\n", + ctx->file, (uintmax_t)size, (intmax_t)ret, -errno)); + + return (errno ? -errno : -EIO); +} + +int +vhd_write(vhd_context_t *ctx, void *buf, size_t size) +{ + size_t ret; + + errno = 0; + + ret = write(ctx->fd, buf, size); + if (ret == size) + return 0; + + VHDLOG((FN,"%s: write of %" PRIuMAX " returned %" PRIdMAX ", errno: %d\n", + ctx->file, (uintmax_t)size, (intmax_t)ret, -errno)); + + return (errno ? -errno : -EIO); +} + +int +vhd_offset(vhd_context_t *ctx, uint32_t sector, uint32_t *offset) +{ + int err; + uint32_t block; + + if (!vhd_type_dynamic(ctx)) + return sector; + + err = vhd_get_bat(ctx); + if (err) + return err; + + block = sector / ctx->spb; + if (ctx->bat.bat[block] == DD_BLK_UNUSED) + *offset = DD_BLK_UNUSED; + else + *offset = ctx->bat.bat[block] + + ctx->bm_secs + (sector % ctx->spb); + + return 0; +} + +int +vhd_open_fast(vhd_context_t *ctx) +{ + int err; + char *buf; + size_t size; + + size = sizeof(vhd_footer_t) + sizeof(vhd_header_t); +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + VHDLOG((FN,"failed allocating %s: %d\n", ctx->file, -errno)); + return -errno; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + VHDLOG((FN,"failed allocating %s: %d\n", ctx->file, -err)); + return -err; + } +#endif + + err = vhd_read(ctx, buf, size); + if (err) { + VHDLOG((FN,"failed reading %s: %d\n", ctx->file, err)); + goto out; + } + + memcpy(&ctx->footer, buf, sizeof(vhd_footer_t)); + vhd_footer_in(&ctx->footer); + err = vhd_validate_footer(&ctx->footer); + if (err) + goto out; + + if (vhd_type_dynamic(ctx)) { + if (ctx->footer.data_offset != sizeof(vhd_footer_t)) + err = vhd_read_header(ctx, &ctx->header); + else { + memcpy(&ctx->header, + buf + sizeof(vhd_footer_t), + sizeof(vhd_header_t)); + vhd_header_in(&ctx->header); + err = vhd_validate_header(&ctx->header); + } + + if (err) + goto out; + + ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3); + } + +out: + _aligned_free(buf); + return err; +} + +int +vhd_open(vhd_context_t *ctx, const char *file, int flags) +{ + int err, oflags; + + if (flags & VHD_OPEN_STRICT) + vhd_flag_clear(flags, VHD_OPEN_FAST); + + memset(ctx, 0, sizeof(vhd_context_t)); + ctx->fd = -1; + ctx->oflags = flags; + + err = namedup(&ctx->file, file); + if (err) + return err; + + oflags = O_DIRECT | O_LARGEFILE | O_BINARY; + if (flags & VHD_OPEN_RDONLY) + oflags |= O_RDONLY; + if (flags & VHD_OPEN_RDWR) + oflags |= O_RDWR; + + ctx->fd = open(ctx->file, oflags, 0644); + if (ctx->fd == -1) { + err = -errno; + VHDLOG((FN,"failed to open %s: %d\n", ctx->file, err)); + goto fail; + } + + err = vhd_test_file_fixed(ctx->file, &ctx->is_block); + if (err) + goto fail; + + if (flags & VHD_OPEN_FAST) { + err = vhd_open_fast(ctx); + if (err) + goto fail; + + return 0; + } + + err = vhd_read_footer(ctx, &ctx->footer); + if (err) + goto fail; + + if (!(flags & VHD_OPEN_IGNORE_DISABLED) && vhd_disabled(ctx)) { + err = -EINVAL; + goto fail; + } + + if (vhd_type_dynamic(ctx)) { + err = vhd_read_header(ctx, &ctx->header); + if (err) + goto fail; + + ctx->spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + ctx->bm_secs = secs_round_up_no_zero(ctx->spb >> 3); + } + + return 0; + +fail: + if (ctx->fd != -1) + close(ctx->fd); + free(ctx->file); + memset(ctx, 0, sizeof(vhd_context_t)); + return err; +} + +void +vhd_close(vhd_context_t *ctx) +{ + if (ctx->file) + close(ctx->fd); + free(ctx->file); + _aligned_free(ctx->bat.bat); + _aligned_free(ctx->batmap.map); + memset(ctx, 0, sizeof(vhd_context_t)); +} + +static inline void +vhd_initialize_footer(vhd_context_t *ctx, int type, uint64_t size) +{ + memset(&ctx->footer, 0, sizeof(vhd_footer_t)); + memcpy(ctx->footer.cookie, HD_COOKIE, sizeof(ctx->footer.cookie)); + ctx->footer.features = HD_RESERVED; + ctx->footer.ff_version = HD_FF_VERSION; + ctx->footer.timestamp = vhd_time(time(NULL)); + ctx->footer.crtr_ver = VHD_CURRENT_VERSION; + #ifdef _WIN32 + ctx->footer.crtr_os = HD_CR_OS_WINDOWS; +#elif __APPLE__ + ctx->footer.crtr_os = HD_CR_OS_MACINTOSH; +#elif VMS + ctx->footer.crtr_os = HD_CR_OS_VMS; +#else + ctx->footer.crtr_os = HD_CR_OS_UNIX; +#endif + ctx->footer.orig_size = size; + ctx->footer.curr_size = size; + ctx->footer.geometry = vhd_chs(size); + ctx->footer.type = type; + ctx->footer.saved = 0; + ctx->footer.data_offset = 0xFFFFFFFFFFFFFFFF; + strcpy(ctx->footer.crtr_app, "tap"); + blk_uuid_generate(&ctx->footer.uuid); +} + +static 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; + + err = 0; + pname = NULL; + ppath = NULL; + + /* + * MICROSOFT_COMPAT + * big endian unicode here + */ + cd = iconv_open(UTF_16BE, "ASCII"); + if (cd == (iconv_t)-1) { + err = -errno; + goto out; + } + + ppath = strdup(parent_path); + if (!ppath) { + err = -ENOMEM; + goto out; + } + + pname = basename(ppath); + if (!strcmp(pname, "")) { + err = -EINVAL; + goto out; + } + + ibl = strlen(pname); + obl = sizeof(ctx->header.prt_name); + dst = ctx->header.prt_name; + + memset(dst, 0, obl); + + if (iconv(cd, +#ifdef __linux__ + (char **) +#endif + &pname, &ibl, &dst, &obl) == (size_t)-1 || ibl) + err = (errno ? -errno : -EINVAL); + +out: + iconv_close(cd); + free(ppath); + return err; +} + +static off_t +get_file_size(const char *name) +{ + int fd; + off_t end; + + fd = open(name, O_LARGEFILE | O_RDONLY | O_BINARY); + if (fd == -1) { + VHDLOG((FN,"unable to open '%s': %d\n", name, errno)); + return -errno; + } + end = (off_t)lseek(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) +{ + int err; + struct stat stats; + vhd_context_t parent; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + memset(&ctx->header, 0, sizeof(vhd_header_t)); + memcpy(ctx->header.cookie, DD_COOKIE, sizeof(ctx->header.cookie)); + ctx->header.data_offset = (uint64_t)-1; + ctx->header.table_offset = VHD_SECTOR_SIZE * 3; /* 1 ftr + 2 hdr */ + ctx->header.hdr_ver = DD_VERSION; + ctx->header.block_size = VHD_BLOCK_SIZE; + ctx->header.prt_ts = 0; + ctx->header.res1 = 0; + ctx->header.max_bat_size = (u32)((ctx->footer.curr_size + + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT); + + ctx->footer.data_offset = VHD_SECTOR_SIZE; + + if (ctx->footer.type == HD_TYPE_DYNAMIC) + return 0; + + err = stat(parent_path, &stats); + if (err == -1) + return -errno; + + if (raw) { + ctx->header.prt_ts = vhd_time(stats.st_mtime); + if (!size) + size = get_file_size(parent_path); + } + else { + err = vhd_open(&parent, parent_path, VHD_OPEN_RDONLY); + if (err) + return err; + + ctx->header.prt_ts = vhd_time(stats.st_mtime); + blk_uuid_copy(&ctx->header.prt_uuid, &parent.footer.uuid); + if (!size) + size = parent.footer.curr_size; + vhd_close(&parent); + } + ctx->footer.orig_size = size; + ctx->footer.curr_size = size; + ctx->footer.geometry = vhd_chs(size); + ctx->header.max_bat_size = (u32)( + (size + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT); + + return vhd_initialize_header_parent_name(ctx, parent_path); +} + +static int +vhd_write_parent_locators(vhd_context_t *ctx, const char *parent) +{ + int i, err; + off_t off; + uint32_t code; + + code = PLAT_CODE_NONE; + + if (ctx->footer.type != HD_TYPE_DIFF) + return -EINVAL; + + off = (off_t)(ctx->batmap.header.batmap_offset + + vhd_sectors_to_bytes(ctx->batmap.header.batmap_size)); + if (off & (VHD_SECTOR_SIZE - 1)) + off = (off_t)vhd_bytes_padded(off); + + for (i = 0; i < 3; i++) { + switch (i) { + case 0: + code = PLAT_CODE_MACX; + break; + case 1: + code = PLAT_CODE_W2KU; + break; + case 2: + code = PLAT_CODE_W2RU; + break; + } + + err = vhd_parent_locator_write_at(ctx, parent, off, code, + 0, ctx->header.loc + i); + if (err) + return err; + + off += vhd_parent_locator_size(ctx->header.loc + i); + } + + return 0; +} + +int +vhd_change_parent(vhd_context_t *child, char *parent_path, int flags) +{ + int i, err; + char *ppath; + struct stat stats; + vhd_context_t parent; + + ppath = realpath(parent_path, NULL); + if (!ppath) { + VHDLOG((FN,"error resolving parent path %s for %s: %d\n", + parent_path, child->file, errno)); + return -errno; + } +#ifdef _WIN32 + ppath++; +#endif + err = stat(ppath, &stats); + if (err == -1) { + err = -errno; + goto out; + } + +#ifdef _WIN32 + /* Look at filename for initial \\ to replace _S_IFBLK?? */ + if (!(stats.st_mode & _S_IFREG) || (stats.st_mode & _S_IFDIR)) { + err = -EINVAL; + goto out; + } +#else + if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) { + err = -EINVAL; + goto out; + } +#endif + + if (flags & 1) { + blk_uuid_clear(&child->header.prt_uuid); + } else { + err = vhd_open(&parent, ppath, VHD_OPEN_RDONLY); + if (err) { + VHDLOG((FN,"error opening parent %s for %s: %d\n", + ppath, child->file, err)); + goto out; + } + /* Extension */ + if( flags & 2 ) { + if( blk_uuid_compare(&child->header.prt_uuid, + &parent.footer.uuid) != 0 ) { + VHDLOG((FN,"new parent %s for %s: parent UUID doesn't match child\n", + ppath, child->file)); + vhd_close(&parent); + err = -EINVAL; + goto out; + } + } else + blk_uuid_copy(&child->header.prt_uuid, &parent.footer.uuid); + vhd_close(&parent); + } + + vhd_initialize_header_parent_name(child, ppath); + child->header.prt_ts = vhd_time(stats.st_mtime); + + for (i = 0; i < vhd_parent_locator_count(child); i++) { + vhd_parent_locator_t *loc = child->header.loc + i; + size_t max = vhd_parent_locator_size(loc); + + switch (loc->code) { + case PLAT_CODE_MACX: + case PLAT_CODE_W2KU: + case PLAT_CODE_W2RU: + break; + default: + continue; + } + + err = vhd_parent_locator_write_at(child, ppath, + ((off_t)loc->data_offset), + loc->code, max, loc); + if (err) { + VHDLOG((FN,"error writing parent locator %d for %s: %d\n", + i, child->file, err)); + goto out; + } + } + + TEST_FAIL_AT(FAIL_REPARENT_LOCATOR); + + err = vhd_write_header(child, &child->header); + if (err) { + VHDLOG((FN,"error writing header for %s: %d\n", child->file, err)); + goto out; + } + + err = 0; + +out: +#ifdef _WIN32 + free(ppath -1); +#else + free(ppath); +#endif + return err; +} + +static int +vhd_create_batmap(vhd_context_t *ctx) +{ + off_t off; + int err; + size_t map_bytes; + vhd_batmap_header_t *header; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + map_bytes = (ctx->header.max_bat_size + 7) >> 3; + header = &ctx->batmap.header; + + memset(header, 0, sizeof(vhd_batmap_header_t)); + memcpy(header->cookie, VHD_BATMAP_COOKIE, sizeof(header->cookie)); + + err = vhd_batmap_header_offset(ctx, &off); + if (err) + return err; + + header->batmap_offset = off + + vhd_bytes_padded(sizeof(vhd_batmap_header_t)); + header->batmap_size = secs_round_up_no_zero(map_bytes); + header->batmap_version = VHD_BATMAP_CURRENT_VERSION; + + map_bytes = (size_t)vhd_sectors_to_bytes(header->batmap_size); + +#ifdef _WIN32 + ctx->batmap.map = _aligned_malloc( map_bytes, VHD_SECTOR_SIZE ); + if( ctx->batmap.map == NULL ) { + return -errno; + } +#else + err = posix_memalign((void **)&ctx->batmap.map, + VHD_SECTOR_SIZE, map_bytes); + if (err) { + ctx->batmap.map = NULL; + return -err; + } +#endif + + memset(ctx->batmap.map, 0, map_bytes); + + return vhd_write_batmap(ctx, &ctx->batmap); +} + +static int +vhd_create_bat(vhd_context_t *ctx) +{ + int err; + size_t i, size; + + if (!vhd_type_dynamic(ctx)) + return -EINVAL; + + size = (size_t)vhd_bytes_padded(ctx->header.max_bat_size * sizeof(uint32_t)); +#ifdef _WIN32 + ctx->bat.bat = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( ctx->bat.bat == NULL ) { + return -errno; + } +#else + err = posix_memalign((void **)&ctx->bat.bat, VHD_SECTOR_SIZE, size); + if (err) { + ctx->bat.bat = NULL; + return -err; + } +#endif + + memset(ctx->bat.bat, 0, size); + for (i = 0; i < ctx->header.max_bat_size; i++) + ctx->bat.bat[i] = DD_BLK_UNUSED; + + err = vhd_seek(ctx, (off_t)ctx->header.table_offset, SEEK_SET); + if (err) + return err; + + ctx->bat.entries = ctx->header.max_bat_size; + ctx->bat.spb = ctx->header.block_size >> VHD_SECTOR_SHIFT; + + return vhd_write_bat(ctx, &ctx->bat); +} + +static int +vhd_initialize_fixed_disk(vhd_context_t *ctx) +{ + char *buf; + size_t i; + int err; + + if (ctx->footer.type != HD_TYPE_FIXED) + return -EINVAL; + + err = vhd_seek(ctx, 0, SEEK_SET); + if (err) + return err; + +#ifdef _WIN32 + buf = VirtualAlloc( NULL, VHD_BLOCK_SIZE, MEM_COMMIT, PAGE_READONLY ); + if( buf == NULL ) + return -ENOMEM; +#else + buf = mmap(0, VHD_BLOCK_SIZE, PROT_READ, + MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) + return -errno; +#endif + for (i = 0; i < ctx->footer.curr_size >> VHD_BLOCK_SHIFT; i++) { + err = vhd_write(ctx, buf, VHD_BLOCK_SIZE); + if (err) + goto out; + } + + err = 0; + +out: +#ifdef _WIN32 + VirtualFree( buf, 0, MEM_RELEASE ); +#else + munmap(buf, VHD_BLOCK_SIZE); +#endif + return err; +} + +int +vhd_get_phys_size(vhd_context_t *ctx, off_t *size) +{ + int err; + + if ((err = vhd_end_of_data(ctx, size))) + return err; + *size += sizeof(vhd_footer_t); + return 0; +} + +int +vhd_set_phys_size(vhd_context_t *ctx, off_t size) +{ + off_t phys_size; + int err; + + err = vhd_get_phys_size(ctx, &phys_size); + if (err) + return err; + if (size < phys_size) { + /* would result in data loss */ + VHDLOG((FN,"ERROR: new size (%"PRIu64") < phys size (%"PRIu64")\n", + size, phys_size)); + return -EINVAL; + } + return vhd_write_footer_at(ctx, &ctx->footer, + size - sizeof(vhd_footer_t)); +} + +static int +__vhd_create(const char *name, const char *parent, uint64_t bytes, int type, + vhd_flag_creat_t flags) +{ + int err; + off_t off; + vhd_context_t ctx; + uint64_t size, blks; + + switch (type) { + case HD_TYPE_DIFF: + if (!parent) + return -EINVAL; + case HD_TYPE_FIXED: + case HD_TYPE_DYNAMIC: + break; + default: + return -EINVAL; + } + + if (strnlen(name, VHD_MAX_NAME_LEN - 1) == VHD_MAX_NAME_LEN - 1) + return -ENAMETOOLONG; + + memset(&ctx, 0, sizeof(vhd_context_t)); + blks = (bytes + VHD_BLOCK_SIZE - 1) >> VHD_BLOCK_SHIFT; + size = blks << VHD_BLOCK_SHIFT; + + ctx.fd = open(name, O_WRONLY | O_CREAT | O_BINARY | + O_TRUNC | O_LARGEFILE | O_DIRECT, 0644); + if (ctx.fd == -1) + return -errno; + + ctx.file = strdup(name); + if (!ctx.file) { + err = -ENOMEM; + goto out; + } + + err = vhd_test_file_fixed(ctx.file, &ctx.is_block); + if (err) + goto out; + + vhd_initialize_footer(&ctx, type, size); + + if (type == HD_TYPE_FIXED) { + err = vhd_initialize_fixed_disk(&ctx); + if (err) + 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); + if (err) + goto out; + + err = vhd_create_batmap(&ctx); + if (err) + goto out; + + err = vhd_create_bat(&ctx); + if (err) + goto out; + + if (type == HD_TYPE_DIFF) { + err = vhd_write_parent_locators(&ctx, parent); + if (err) + goto out; + } + + /* write header again since it may have changed */ + err = vhd_write_header_at(&ctx, &ctx.header, VHD_SECTOR_SIZE); + if (err) + goto out; + } + + err = vhd_seek(&ctx, 0, SEEK_END); + if (err) + goto out; + + off = vhd_position(&ctx); + if (off == (off_t)-1) { + err = -errno; + goto out; + } + + if (ctx.is_block) + off -= sizeof(vhd_footer_t); + + err = vhd_write_footer_at(&ctx, &ctx.footer, off); + if (err) + goto out; + + err = 0; + +out: + vhd_close(&ctx); + if (err && !ctx.is_block) + unlink(name); + return err; +} + +int +vhd_create(const char *name, uint64_t bytes, int type, vhd_flag_creat_t flags) +{ + return __vhd_create(name, NULL, bytes, type, flags); +} + +int +vhd_snapshot(const char *name, uint64_t bytes, const char *parent, + vhd_flag_creat_t flags) +{ + return __vhd_create(name, parent, bytes, HD_TYPE_DIFF, flags); +} + +static int +__vhd_io_fixed_read(vhd_context_t *ctx, + char *buf, uint64_t sec, uint32_t secs) +{ + int err; + + err = vhd_seek(ctx, (off_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (err) + return err; + + return vhd_read(ctx, buf, (off_t)vhd_sectors_to_bytes(secs)); +} + +static void +__vhd_io_dynamic_copy_data(vhd_context_t *ctx, + char *map, int map_off, + char *bitmap, int bitmap_off, + char *dst, char *src, int secs) +{ + int i; + + for (i = 0; i < secs; i++) { + if (test_bit(map, map_off + i)) + goto next; + + if (ctx && !vhd_bitmap_test(ctx, bitmap, bitmap_off + i)) + goto next; + + memcpy(dst, src, VHD_SECTOR_SIZE); + set_bit(map, map_off + i); + + next: + src += VHD_SECTOR_SIZE; + dst += VHD_SECTOR_SIZE; + } +} + +static int +__vhd_io_dynamic_read_link(vhd_context_t *ctx, char *map, + char *buf, uint64_t sector, uint32_t secs) +{ + off_t off; + uint32_t blk, sec; + int err, cnt, map_off; + char *bitmap, *data, *src; + + map_off = 0; + + do { + blk = (uint32_t)(sector / ctx->spb); + sec = sector % ctx->spb; + off = ctx->bat.bat[blk]; + data = NULL; + bitmap = NULL; + + if (off == DD_BLK_UNUSED) { + cnt = MIN(secs, ctx->spb); + goto next; + } + + err = vhd_read_bitmap(ctx, blk, &bitmap); + if (err) + return err; + + err = vhd_read_block(ctx, blk, &data); + if (err) { + _aligned_free(bitmap); + return err; + } + + cnt = MIN(secs, ctx->spb - sec); + src = data + vhd_sectors_to_bytes(sec); + + __vhd_io_dynamic_copy_data(ctx, + map, map_off, + bitmap, sec, + buf, src, cnt); + + next: + _aligned_free(data); + _aligned_free(bitmap); + + secs -= cnt; + sector += cnt; + map_off += cnt; + buf += vhd_sectors_to_bytes(cnt); + + } while (secs); + + return 0; +} + +static int +__raw_read_link(char *filename, + char *map, char *buf, uint64_t sec, uint32_t secs) +{ + int fd, err; + off_t off; + uint64_t size; + char *data; + + err = 0; + errno = 0; + fd = open(filename, O_RDONLY | O_DIRECT | O_LARGEFILE | O_BINARY); + if (fd == -1) { + VHDLOG((FN,"%s: failed to open: %d\n", filename, -errno)); + return -errno; + } + + off = (off_t)lseek(fd, (off_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off_t)-1) { + VHDLOG((FN,"%s: seek(0x%08"PRIx64") failed: %d\n", + filename, vhd_sectors_to_bytes(sec), -errno)); + err = -errno; + goto close; + } + + size = vhd_sectors_to_bytes(secs); +#ifdef _WIN32 + data = _aligned_malloc( (size_t)size, VHD_SECTOR_SIZE ); + if( data == NULL ) { + err = -errno; + goto close; + } +#else + err = posix_memalign((void **)&data, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + goto close; + } +#endif + +#ifdef _WIN32 + { + char *dp; + dp = data; + + while( size != 0 ) { + uint32_t n; + + if( size > UINT32_MAX ) + n = (uint32_t)((((UINT64)UINT32_MAX) + 1) / 2); + else + n = (uint32_t)size; + + err = read( fd, dp, n ); + if( err != n ) { + VHDLOG((FN, "%s: reading of %"PRIu64" returned %d, errno: %d\n", + filename, n, err, -errno )); + _aligned_free( data ); + err = errno ? -errno : -EIO; + goto close; + } + size -= n; + dp += n; + } + } +#else + err = read( fd, data, size ); + if( (size_t)err != size ) { + VHDLOG((FN, "%s: reading of %"PRIu64" returned %d, errno: %d\n", + filename, size, err, -errno )); + _aligned_free( data ); + err = errno ? -errno : -EIO; + goto close; + } +#endif + __vhd_io_dynamic_copy_data(NULL, map, 0, NULL, 0, buf, data, secs); + _aligned_free(data); + err = 0; + +close: + close(fd); + return err; +} + +static int +__vhd_io_dynamic_read(vhd_context_t *ctx, + char *buf, uint64_t sec, uint32_t secs) +{ + int err; + uint32_t i, done; + char *map, *next; + vhd_context_t parent, *vhd; + + err = vhd_get_bat(ctx); + if (err) + return err; + + vhd = ctx; + next = NULL; + map = calloc(1, secs << (VHD_SECTOR_SHIFT - 3)); + if (!map) + return -ENOMEM; + + memset(buf, 0, (size_t)vhd_sectors_to_bytes(secs)); + + for (;;) { + err = __vhd_io_dynamic_read_link(vhd, map, buf, sec, secs); + if (err) + goto close; + + for (done = 0, i = 0; i < secs; i++) + if (test_bit(map, i)) + done++; + + if (done == secs) { + err = 0; + goto close; + } + + if (vhd->footer.type == HD_TYPE_DIFF) { + 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); + 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 (vhd != ctx) + vhd_close(vhd); +out: + free(map); + free(next); + return err; +} + +int +vhd_io_read(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs) +{ + if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size) + return -ERANGE; + + if (!vhd_type_dynamic(ctx)) + return __vhd_io_fixed_read(ctx, buf, sec, secs); + + return __vhd_io_dynamic_read(ctx, buf, sec, secs); +} + +static int +__vhd_io_fixed_write(vhd_context_t *ctx, + char *buf, uint64_t sec, uint32_t secs) +{ + int err; + + err = vhd_seek(ctx, (off_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (err) + return err; + + return vhd_write(ctx, buf, (size_t)vhd_sectors_to_bytes(secs)); +} + +static int +__vhd_io_allocate_block(vhd_context_t *ctx, uint32_t block) +{ + char *buf; + size_t size; + off_t off, max; + int err, gap; + long spp; +#ifdef _WIN32 + SYSTEM_INFO inf; + + GetSystemInfo( &inf ); + spp = inf.dwPageSize >> VHD_SECTOR_SHIFT; +#elif defined( _SC_PAGESIZE) + spp = sysconf( _SC_PAGESIZE ) >> VHD_SECTOR_SHIFT; +#else + spp = getpagesize() >> VHD_SECTOR_SHIFT; +#endif + if( spp == 0 ) + abort(); + + err = vhd_end_of_data(ctx, &max); + if (err) + return err; + + gap = 0; + off = max; + max >>= VHD_SECTOR_SHIFT; + + /* data region of segment should begin on page boundary */ + if ((max + ctx->bm_secs) % spp) { + gap = (spp - ((max + ctx->bm_secs) % spp)); + max += gap; + } + + err = vhd_seek(ctx, off, SEEK_SET); + if (err) + return err; + + size = (size_t)vhd_sectors_to_bytes(ctx->spb + ctx->bm_secs + gap); +#ifdef _WIN32 + buf = VirtualAlloc( NULL, size, MEM_COMMIT, PAGE_READONLY ); + if( buf == NULL ) + return -ENOMEM; +#else + buf = mmap(0, size, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0); + if (buf == MAP_FAILED) + return -errno; +#endif + + err = vhd_write(ctx, buf, size); + if (err) + goto out; + + ctx->bat.bat[block] = max; + err = vhd_write_bat(ctx, &ctx->bat); + if (err) + goto out; + + err = 0; + +out: +#ifdef _WIN32 + VirtualFree( buf, 0, MEM_RELEASE ); +#else + munmap(buf, size); +#endif + return err; +} + +static int +__vhd_io_dynamic_write(vhd_context_t *ctx, + char *buf, uint64_t sector, uint32_t secs) +{ + char *map; + off_t off; + uint32_t blk, sec; + int err, ret; + size_t i, cnt; + + if (vhd_sectors_to_bytes(sector + secs) > ctx->footer.curr_size) + return -ERANGE; + + err = vhd_get_bat(ctx); + if (err) + return err; + + if (vhd_has_batmap(ctx)) { + err = vhd_get_batmap(ctx); + if (err) + return err; + } + + do { + blk = (uint32_t)(sector / ctx->spb); + sec = sector % ctx->spb; + + off = ctx->bat.bat[blk]; + if (off == DD_BLK_UNUSED) { + err = __vhd_io_allocate_block(ctx, blk); + if (err) + return err; + + off = ctx->bat.bat[blk]; + } + + off += ctx->bm_secs + sec; + err = vhd_seek(ctx, (off_t)vhd_sectors_to_bytes(off), SEEK_SET); + if (err) + return err; + + cnt = MIN(secs, ctx->spb - sec); + err = vhd_write(ctx, buf, (size_t)vhd_sectors_to_bytes(cnt)); + if (err) + return err; + + if (vhd_has_batmap(ctx) && + vhd_batmap_test(ctx, &ctx->batmap, blk)) + goto next; + + err = vhd_read_bitmap(ctx, blk, &map); + if (err) + return err; + + for (i = 0; i < cnt; i++) + vhd_bitmap_set(ctx, map, sec + 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)) { + _aligned_free(map); + goto next; + } + + vhd_batmap_set(ctx, &ctx->batmap, blk); + err = vhd_write_batmap(ctx, &ctx->batmap); + if (err) + goto fail; + } + + _aligned_free(map); + map = NULL; + + next: + secs -= cnt; + sector += cnt; + buf += vhd_sectors_to_bytes(cnt); + } while (secs); + + err = 0; + +out: + ret = vhd_write_footer(ctx, &ctx->footer); + return (err ? err : ret); + +fail: + _aligned_free(map); + goto out; +} + +int +vhd_io_write(vhd_context_t *ctx, char *buf, uint64_t sec, uint32_t secs) +{ + if (vhd_sectors_to_bytes(sec + secs) > ctx->footer.curr_size) + return -ERANGE; + + if (!vhd_type_dynamic(ctx)) + return __vhd_io_fixed_write(ctx, buf, sec, secs); + + return __vhd_io_dynamic_write(ctx, buf, sec, secs); +} diff --git a/extracters/libvhd/libvhd.sln b/extracters/libvhd/libvhd.sln new file mode 100644 index 0000000..f5b982f --- /dev/null +++ b/extracters/libvhd/libvhd.sln @@ -0,0 +1,41 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvhd", "libvhd.vcxproj", "{695E504C-32F4-43AF-8AD6-761DE66D0EAB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vhd-util", "..\vhd-util\vhd-util.vcxproj", "{CC5FB437-0C01-4ECF-A75F-10C37B607AC6}" + ProjectSection(ProjectDependencies) = postProject + {695E504C-32F4-43AF-8AD6-761DE66D0EAB} = {695E504C-32F4-43AF-8AD6-761DE66D0EAB} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Debug|x64.ActiveCfg = Debug|x64 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Debug|x64.Build.0 = Debug|x64 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Debug|x86.ActiveCfg = Debug|Win32 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Debug|x86.Build.0 = Debug|Win32 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Release|x64.ActiveCfg = Release|x64 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Release|x64.Build.0 = Release|x64 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Release|x86.ActiveCfg = Release|Win32 + {695E504C-32F4-43AF-8AD6-761DE66D0EAB}.Release|x86.Build.0 = Release|Win32 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Debug|x64.ActiveCfg = Debug|x64 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Debug|x64.Build.0 = Debug|x64 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Debug|x86.ActiveCfg = Debug|Win32 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Debug|x86.Build.0 = Debug|Win32 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Release|x64.ActiveCfg = Release|x64 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Release|x64.Build.0 = Release|x64 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Release|x86.ActiveCfg = Release|Win32 + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extracters/libvhd/libvhd.vcxproj b/extracters/libvhd/libvhd.vcxproj new file mode 100644 index 0000000..4840070 --- /dev/null +++ b/extracters/libvhd/libvhd.vcxproj @@ -0,0 +1,125 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {695E504C-32F4-43AF-8AD6-761DE66D0EAB} + Win32Proj + 8.1 + + + + StaticLibrary + true + v140 + + + StaticLibrary + false + v140 + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + + + + + + + + + true + + + true + + + + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + Disabled + include;%(AdditionalIncludeDirectories) + + + MachineX86 + true + Console + Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + false + true + + + + + + + + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + MultiThreaded + Level3 + ProgramDatabase + include;%(AdditionalIncludeDirectories) + + + MachineX86 + true + Console + true + true + Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extracters/libvhd/libvhd.vcxproj.filters b/extracters/libvhd/libvhd.vcxproj.filters new file mode 100644 index 0000000..f7c7898 --- /dev/null +++ b/extracters/libvhd/libvhd.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/extracters/libvhd/relative-path.c b/extracters/libvhd/relative-path.c new file mode 100644 index 0000000..cc328b3 --- /dev/null +++ b/extracters/libvhd/relative-path.c @@ -0,0 +1,442 @@ +/* Copyright (c) 2008, XenSource 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. +*/ +/* Modified March 2016 Timothe Litt to work under windows. + * Copyright (C) 2016 Timothe Litt + * Modifications subject to the same license terms as above, + * substituting "Timothe Litt" for "XenSource Inc." + */ + +#ifdef _WIN32 +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#include +#include +#include +#include +#include + +#include "relative-path.h" + +#ifdef _WIN32 +#include +#include +#include +#define strdup _strdup +#else +#include +#endif + +#define DELIMITER '/' + +#define FN __func__ + +#define EPRINTF(a) rpath_log_error a + +static void rpath_log_error( const char *func, const char *fmt, ... ) +#ifdef __GNUC__ + __attribute__((format(printf,2,3))); +#else + ; +#endif + +#ifndef LIBVHD_HAS_SYSLOG +#ifdef _WIN32 +#define LIBVHD_HAS_SYSLOG 0 +#else +#define LIBVHD_HAS_SYSLOG 1 +#endif +#endif + +#define sfree(ptr) \ +do { \ + free(ptr); \ + ptr = NULL; \ +} while (0) + +#ifdef _WIN32 +LIBVHD_API char *realpath( const char *path, char *resolved ) { + char *p; + DWORD len; + + p = resolved; + if( resolved == NULL ) { + resolved = malloc( 1+ MAX_PATH + 1 ); + if( resolved == NULL ) { + return NULL; + } + } + if( (len = GetFullPathName( path[0] == '/'? path+1:path, MAX_PATH + 1, resolved, NULL )) == 0 ) { + if( p == NULL ) + free( resolved ); + return NULL; + } + if( len > MAX_PATH ) { + if( p != NULL ) + return NULL; + if( (p = realloc( resolved, len )) == NULL ) { + free( resolved ); + return NULL; + } + resolved = p; + len = GetFullPathName( path, MAX_PATH + 1, resolved, NULL ); + if( len > MAX_PATH || len == 0 ) { + free( resolved ); + return NULL; + } + } + + for( p = resolved; *p; p++ ) { + if( *p == '\\') + *p = '/'; + } + for( p = resolved; isalpha( *p ); p++ ) + ; + if( *p == ':' ) { /* Hide drive under '/' for callers. */ + memmove( resolved + 1, resolved, strlen( resolved ) +1 ); + resolved[0] = '/'; /* Callers must skip '/' when touching filesystem. */ + } + return resolved; +} +LIBVHD_API int asprintf( char **result, const char *fmt, ... ) { + int len; + va_list ap; + char *np; + + /* It would be better to va_copy() the list and do a prescan, + * but va_copy apparently has issues with versions of MS C + * still in the field. This approach is ugly and wasteful, but + * should work. (A KB just isn't what it used to be...) + */ + if( (*result = malloc( 1 * 100 * 1000 + 1 )) == NULL ) + return -1; + va_start( ap, fmt ); + len = vsprintf( *result, fmt, ap ); + np = realloc( *result, len + 1 ); + if( np != NULL ) + *result = np; + return len; +} +#endif + +/* + * count number of tokens between DELIMITER characters + */ + +int count_nodes(char *path) +{ + int i; + char *tmp; + + if (!path) + return 0; + + for (i = 0, tmp = path; *tmp != '\0'; tmp++) + if (*tmp == DELIMITER) + i++; + + return i; +} + +/* + * return copy of next node in @path, or NULL + * @path is moved to the end of the next node + * @err is set to -errno on failure + * copy should be freed + */ +static char * +next_node(char **path, int *err) +{ + int ret; + char *tmp, *start; + + if (!path || !*path) { + *err = -EINVAL; + return NULL; + } + + *err = 0; + start = *path; + + for (tmp = *path; *tmp != '\0'; tmp++) + if (*tmp == DELIMITER) { + int size; + char *node; + + size = tmp - start + 1; + node = malloc(size); + if (!node) { + *err = -ENOMEM; + return NULL; + } + + ret = snprintf(node, size, "%s", start); + if (ret < 0) { + free(node); + *err = -EINVAL; + return NULL; + } + + *path = tmp; + return node; + } + + return NULL; +} + +/* + * count number of nodes in common betwee @to and @from + * returns number of common nodes, or -errno on failure + */ +static int +count_common_nodes(char *to, char *from) +{ + int err, common; + char *to_node, *from_node; + + if (!to || !from) + return -EINVAL; + + err = 0; + common = 0; + to_node = NULL; + from_node = NULL; + + do { + to_node = next_node(&to, &err); + if (err || !to_node) + break; + + from_node = next_node(&from, &err); + if (err || !from_node) + break; + + if (strncmp(to_node, from_node, MAX_NAME_LEN)) + break; + + ++to; + ++from; + ++common; + sfree(to_node); + sfree(from_node); + + } while (1); + + sfree(to_node); + sfree(from_node); + + if (err) + return err; + + return common; +} + +/* + * construct path of @count '../', './' if @count is zero, or NULL on error + * result should be freed + */ +static char * +up_nodes(int count) +{ + char *path, *tmp; + int i, ret, len, size; + + if (!count) + return strdup("./"); + + len = strlen("../"); + size = len * count; + if (size >= MAX_NAME_LEN) + return NULL; + + path = malloc(size + 1); + if (!path) + return NULL; + + tmp = path; + for (i = 0; i < count; i++) { + ret = sprintf(tmp, "../"); + if (ret < 0 || ret != len) { + free(path); + return NULL; + } + tmp += ret; + } + + return path; +} + +/* + * return pointer to @offset'th node of path or NULL on error + */ +static char * +node_offset(char *from, int offset) +{ + char *path; + + if (!from || !offset) + return NULL; + + for (path = from; *path != '\0'; path++) { + if (*path == DELIMITER) + if (--offset == 0) + return path + 1; + } + + return NULL; +} + +/* + * return a relative path from @from to @to + * result should be freed + */ +LIBVHD_API +char * +relative_path_to(char *from, char *to, int *err) +{ + int from_nodes, common; + char *to_absolute, *from_absolute; + char *up, *common_target_path, *relative_path; + + *err = 0; + up = NULL; + to_absolute = NULL; + from_absolute = NULL; + relative_path = NULL; + + if (strnlen(to, MAX_NAME_LEN) == MAX_NAME_LEN || + strnlen(from, MAX_NAME_LEN) == MAX_NAME_LEN) { + EPRINTF((FN,"invalid input; max path length is %d\n", + MAX_NAME_LEN)); + *err = -ENAMETOOLONG; + return NULL; + } + + to_absolute = realpath(to, NULL); + if (!to_absolute) { + EPRINTF((FN,"failed to get absolute path of %s\n", to)); + *err = -errno; + goto out; + } + + from_absolute = realpath(from, NULL); + if (!from_absolute) { + EPRINTF((FN,"failed to get absolute path of %s\n", from)); + *err = -errno; + goto out; + } + + if (strnlen(to_absolute, MAX_NAME_LEN) == MAX_NAME_LEN || + strnlen(from_absolute, MAX_NAME_LEN) == MAX_NAME_LEN) { + EPRINTF((FN,"invalid input; max path length is %d\n", + MAX_NAME_LEN)); + *err = -ENAMETOOLONG; + goto out; + } + + /* count nodes in source path */ + from_nodes = count_nodes(from_absolute); + + /* count nodes in common */ + common = count_common_nodes(to_absolute + 1, from_absolute + 1); + if (common < 0) { + EPRINTF((FN,"failed to count common nodes of %s and %s: %d\n", + to_absolute, from_absolute, common)); + *err = common; + goto out; + } + + /* move up to common node */ + up = up_nodes(from_nodes - common - 1); + if (!up) { + EPRINTF((FN,"failed to allocate relative path for %s: %d\n", + from_absolute, -ENOMEM)); + *err = -ENOMEM; + goto out; + } + + /* get path from common node to target */ + common_target_path = node_offset(to_absolute, common + 1); + if (!common_target_path) { + EPRINTF((FN,"failed to find common target path to %s: %d\n", + to_absolute, -EINVAL)); + *err = -EINVAL; + goto out; + } + + /* get relative path */ + if (asprintf(&relative_path, "%s%s", up, common_target_path) == -1) { + EPRINTF((FN,"failed to construct final path %s%s: %d\n", + up, common_target_path, -ENOMEM)); + relative_path = NULL; + *err = -ENOMEM; + goto out; + } + +out: + sfree(up); + sfree(to_absolute); + sfree(from_absolute); + + return relative_path; +} +static void rpath_log_error( const char *func, const char *fmt, ... ) +{ + char *buf, nilbuf; + size_t ilen, len; + va_list ap; + + ilen = sizeof( "tap-err:%s: " ) + strlen( func ) -2; + va_start(ap, fmt ); + len = vsnprintf( &nilbuf, 1, fmt, ap ); + va_end( ap ); + + if( (buf = malloc( ilen + len + 1 )) == NULL ) { +#if LIBVHD_HAS_SYSLOG + syslog( LOG_INFO, "tap-err:%s: Out of memory", func ); +#else + fprintf( stderr, "tap-err%s: Out of memory for %s\n", func, fmt ); +#endif + return; + } + va_start(ap, fmt); + (void) snprintf( buf, ilen, "tap-err:%s: ", func ); + (void) vsnprintf( buf + ilen -1, len+1, fmt, ap ); + va_end( ap ); + len += ilen -1; + if( buf[ len -1 ] != '\n' ) + buf[len++] = '\n'; + buf[len] = '\0'; +#if LIBVHD_HAS_SYSLOG + syslog(LOG_INFO, buf); +#else + fputs( buf, stderr ); +#endif + free( buf ); + + return; + } diff --git a/extracters/vhd-util/COPYING b/extracters/vhd-util/COPYING new file mode 100644 index 0000000..673becb --- /dev/null +++ b/extracters/vhd-util/COPYING @@ -0,0 +1,33 @@ +* Copyright (c) 2008, XenSource 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. +*/ + +The Windows version uses a stripped version of the GNU C library's getopt, +and hence is covered by the GPL. + +The modifications made by Timothe Litt are Copyright (C) 2016 by Timothe Litt. +They may be used under the same terms as the XenSource license above (substituting "Timothe Litt" for "Xensource Inc" as appropriate. + diff --git a/extracters/vhd-util/INSTALL b/extracters/vhd-util/INSTALL new file mode 100644 index 0000000..d60771c --- /dev/null +++ b/extracters/vhd-util/INSTALL @@ -0,0 +1,31 @@ +This is a port of the Xen vhd-util tool and the libvhd library to windows. + +It still works (with minor enhancements) on Unix. + +The distribution kit includes prebuilt executables for Linux and Windows. + +There are two components: + libvhd - library for VHD file access + vhd-util - tool for managing VHD files. + +Each has a directory, which should be at the same level. + +On Unix: + cd vhd-util && make && make install + + cd vhd-util && make PREFIX=/usr && make PREFIX=/usr + To place somewhere else. + + This creates a sharable library (libvhd) and the vhd-util command. + +On Windows: + Builds under microsoft Visual Studio (2015), free edition. + + Open libvhd/libvhd.sln and build it. + +On Windows, a DLL is NOT created as the API doesn't support it. +(The DLL allocates memory that the client must free, but doesn't + provide routines to do this. On windows, the pools are different.) + +Licensing is described in COPYING. + diff --git a/extracters/vhd-util/Makefile b/extracters/vhd-util/Makefile new file mode 100644 index 0000000..2e2bd1e --- /dev/null +++ b/extracters/vhd-util/Makefile @@ -0,0 +1,115 @@ +# -*- Makefile -*- + +# Builds vhd-util and the libvhd shared library. + +PREFIX = /usr/local +LIBDIR = $(PREFIX)/lib +BINDIR = $(PREFIX)/bin + +CPPFLAGS = -D_GNU_SOURCE -I include -I $(LIBVHD)/include +CFLAGS = -O4 +LDFLAGS = -L. -L $(LIBVHD) +LDLIBS = -lvhd-util -luuid -lvhd + +LDCONFIG_D = /etc/ld.so.conf.d/vhd-util + +LIBVHD = ../libvhd + +SRCS = atomicio.c \ +libvhd-journal.c \ +vhd-util-check.c \ +vhd-util-coalesce.c \ +vhd-util-create.c \ +vhd-util-fill.c \ +lvm-util.c \ +vhd-util-modify.c \ +vhd-util-query.c \ +vhd-util-read.c \ +vhd-util-repair.c \ +vhd-util-resize.c \ +vhd-util-revert.c \ +vhd-util-scan.c \ +vhd-util-set-field.c \ +vhd-util-snapshot.c + +OBJS = $(SRCS:.c=.o) + +LIBVHDSO = libvhd.so +LIBVHDSONAME = $(LIBVHDSO).2 +LIBVHDSOFILE = $(LIBVHDSONAME).1.0 + +all: vhd-util $(LIBVHD)/$(LIBVHDSO) + +# The utility + +libvhd-util.a : $(OBJS) + $(AR) -rs libvhd-util.a $(OBJS) + +vhd-util : vhd-util.o libvhd-util.a $(LIBVHD)/$(LIBVHDSO) + $(CC) $(LDFLAGS) -Wl,-rpath,$(LIBDIR) -o vhd-util vhd-util.o $(LDLIBS) + +# The sharable library + +$(LIBVHD)/$(LIBVHDSOFILE) : $(LIBVHD)/libvhd.c $(LIBVHD)/relative-path.c + $(CC) -shared -o $(LIBVHD)/$(LIBVHDSOFILE) -Wl,-soname,$(LIBVHDSONAME) $(CPPFLAGS) $(CFLAGS) -fPIC $(LIBVHD)/libvhd.c $(LIBVHD)/relative-path.c -luuid + +# The symlinks in the local directory necessary for linking + +$(LIBVHD)/$(LIBVHDSO) : $(LIBVHD)/$(LIBVHDSOFILE) + ldconfig -n $(LIBVHD) + ln -sf $(LIBVHD)/$(LIBVHDSOFILE) $(LIBVHD)/$(LIBVHDSO) + +# Install (note that to run the utility before installation, you must set +# LD_LOAD_LIBRARY to $(VLDLIB) + +install: all + cp -p $(LIBVHD)/$(LIBVHDSOFILE) $(LIBDIR)/ + cp -p vhd-util $(BINDIR)/ + echo "$(LIBDIR)" >$(LDCONFIG_D) + ldconfig + ln -sf $(LIBVHDSONAME) $(LIBDIR)/$(LIBVHDSO) + +# Un-install + +uninstall: + rm -f $(BINDIR)/vhd-util $(LIBDIR)/$(LIBVHDSOFILE) $(LIBDIR)/$(LIBVHDSONAME) $(LIBDIR)/$(LIBVHDSO) + ldconfig + rm -f $(LDCONFIG_D) + +# Clean working area + +clean: tidy + rm -f vhd-util vhd-util.exe $(LIBVHD)/$(LIBVHDSOFILE) $(LIBVHD)/$(LIBVHDSONAME) $(LIBVHD)/$(LIBVHDSO) + +# Clean intermediate files only, leaving outputs + +tidy: + rm -f $(OBJS) libvhd-util.a vhd-util.o + +KITFILES = \ +vhd-util/README \ +vhd-util/INSTALL \ +vhd-util/COPYING \ +vhd-util/*.c \ +vhd-util/include/*.h \ +vhd-util/vhd-util \ +vhd-util/vhd-util.exe \ +vhd-util/Makefile \ +vhd-util/vhd-util.vcxproj \ +vhd-util/vhd-util.vcxproj.filters \ +\ +libvhd/*.c \ +libvhd/libvhd.so* \ +libvhd/libvhd.sln \ +libvhd/libvhd.vcxproj \ +libvhd/libvhd.vcxproj.filters \ +libvhd/include/*.h \ + +KITEXES = \ +vhd-util/vhd-util \ +vhd-util/vhd-util.exe \ +libvhd/libvhd.so* + +kit: all + cd .. && chown -h $(USER).$(USER) $(KITFILES) && chmod -x $(KITFILES) && chmod +x $(KITEXES) && tar -czf vhd-tools.tgz $(KITFILES) + diff --git a/extracters/vhd-util/README b/extracters/vhd-util/README new file mode 100644 index 0000000..13e5f54 --- /dev/null +++ b/extracters/vhd-util/README @@ -0,0 +1,229 @@ +vhd-util is originally an unsupported tool provided with the Xen emulator +family. It was not designed as a user tool, but it does have some +functions that can be used without internals knowledge. + +It is a "sharp" tool - you can do irreversible damage with it. +Use at your own risk. + +This version has been modified to: + + o Work on Windows as well as Unix + o Resolve a number of compiler-detected issues + o Identify the creator OS + o Fix some bugs. + o The 'scan' command, which operates on LVM volumes, was + not ported to Windows. All other commands (should) work. + o An error message is generated when a command fails. + o Some safeguards have been added to the 'modify' command. + o read -P summarizes file and its parents. + +The tool is undocumentd by Xen, except for a few cryptic comments +in the README that assume the Xen environment. + +The following should be more useful as a stand-alone tool. + +This document is the result of reverse-engineering the vhd-util code, +and may contain errors. As with the tool, use with caution and at +your own risk. + +vhd-util is a command-line utility with the following general syntax: + + vhd-util COMMAND OPTIONS + +Most commands produce a cryptic usage with the -h option. +The target of a command is specified with the (required) -n "option". + +Commands tend to fail when they don't make sense - e.g. asking about +difference disk data structures on a fixed-size disk. + +Some terminology: + Sector - Address on virtual disk. Always 512 bytes. + Block - Allocation unit in sparse disks. Default 4096 sectors + (2 MB). + Fixed disk - Virtual disk of fixed size = maximum size + small header. + Dynamic disk - (Sparse disk) Virtual disk that grows as data is + written. Simulator sees it as fixed (max) size. + Diff disk - (Snapshot) Dynamic disk that contains (only) changes + to a parent Dynamic (or Diff) disk. Used for + backups, or to experiment with easy rollback. + BAT - "Block allocation table": map of virtual blocks to + physical sectors in a dynamic/diff disk. + Bitmap - Bits associated with each Block indicating which + sectors within the block have been written. + +vhd-util create + Create a new disk image file. + + -n filename File to create + -s size Specify file size (MB) + -r Create a fixed size image file (default is sparse) + +vhd-util snapshot + Create a new snapshot (difference image) of a sparse file. + Any subsequent modification to the parent file invalidates + the snapshot. The parent filename is stored in the snapshot. + Snapshots can be chained to arbitrary depth, at perfomance cost. + + -n filename File to create + -p filename Filename of parent disk + -l limit Maximum depth of snapshot chain + -m Parent disk is raw (physical). Not supported on windows. + +vhd-util check + Check metadata of VHD file for consistency and reports any errors + detected. + + -n filename File to check + -p Check parents as well + -i Ignore missing primary footer (repair can fix) + +vhd-util read + Read and format the metadata of a sparse (dynamic) disk. + + -n filename File to query + -p Print formatted header and footer. + -P Print formatted summary of file and its parents + + The following options are used for debugging. + + -t sector# Translate logical sector to VHD location + -b block# Print BAT directory for block + -m block# Print BINARY bitmap. (Pipe to a hex editor or file) + -i sec# Print block/sector address and written status + -a Print BINARY BAT map entries + -j block# Print allocation status of block + -d block# Print BINARY content of block data + -c # Repeat command # times for consecutivie blocks/sectors. + -r sector# Print BINARY content of sector(s) + -x Print (some) values in hex. + +vhd-util query + Print key fields from the metadata. Mostly unlabeled, suitable for + consumption by a script. + + -n filename File to query + -v Print virtual size (MB) + -p Print filename of parent + -s Print physical size (bytes) Includes metadata. + -f Print Xen's "hidden" state field. (Non-standard) + -d Print chain depth (# files from -n to ultimate parent) + +vhd-util coalesce + Merges a snapshot (differencing) disk into its parent. + + -n filename File to merge (parent is found automagically) + +vhd-util repair + Attempt to repair a damaged volume. + Copies redundant copy of metadata to correct place at the + end of the file. N.B. OS level data may still be corrupt... + + -n filename File to repair + +vhd-util resize + Change the size of an image file. Can expand, or if + unwritten space at end, shrink. N.B. Does NOT tell the + OS filesystem, nor respect it. Restricted to files created + by Xen (including vhd-util). + + Shrink currently aborts with "partially implemented" for + dynamic disks... + + -n filename File to resize + -j filename Journal file (Must not exist,deleted at end.) + -s size (MB) New size + +vhd-util fill + Reads and rewrites (with the same data) every block of the disk. + Effect is to allocate zero blocks for every page of a dynamic + (sparse) disk. + + -n filename File to process + +vhd-util modify + Modify file metadata + + -n filename File to modify + -P filename (Extension) change parent only after verifying + that the UUID of the parent matches the old one. + Safer alternative for case where parent has been + renamed or moved. + + The following **dangerous** options are used for debugging. + + -p filename Change parent to this file. Actually changing + to a different file will corrupt data. Think + at least three times before using! + -s byteaddress Move the file footer. Can be used for changing + physical size of file, but it's tricky. Xen " + Change the size of the file containing the VHD + image. This does NOT affect the VHD disk capacity, + only the physical size of the file containing + the VHD. Naturally, it is not possible to set the + file size to be less than what the VHD utilizes. + The operation doesn't actually change the file + size, but it writes the footer in the right + location such that resizing the file (manually, as + a separate step) will produce the correct results. + + If the new file size is greater than the current + file size, the file must first be expanded and + then altered with this operation. + + If the new size is smaller than the current size, + the VHD must first be altered with this operation + and then the file must be shrunk. + + Failing to resize the file will result in a + corrupted VHD." + -P is an extension to the Xen tool. + For protection against typos, -p must be specified twice. + +vhd-util revert + Applies a journal file to recover from a failed operation. + Resize can fail with VHD in an inconsistent state; the journal file + records the changes made so that they can (hopefully) be reversed. + + A VHD file in this state has a special 'poisoned' cookie to prevent + it from being accessed normally. + + -n filename The file to be recovered + -j filename The journal file (specified with -j on the resize) + +vhd-util set + Set the (Xen-specifix) hidden flag. + + -n filename File to modify + -f hidden Only "hidden" supported. + -v value Decimal value for field. + +vhd-util scan + Scan LVM volumes for VHD files and report capacity, size and parent. + Unix only. + + -m filter Select files to consider (match name) + -f Do a 'fast' scan + -c Continue on errors + -l volume Scan LVM volume + -p Pretty-print results + -a Scan parents + -v Verbose + +For example, to create a sparse image - + vhd-util create -n MyVHDFile -s 1024 + +This creates a sparse 1GB file named "MyVHDFile" that can be mounted +and populated with data. + +One can also base the image on an existing file - + vhd-util snapshot -n MyVHDFile -p SomeFile + +This creates a sparse VHD file named "MyVHDFile" using "SomeFile" +as a parent image. Copy-on-write semantics ensure that writes will be +stored in "MyVHDFile" while reads will be directed to the most +recently written version of the data, either in "MyVHDFile" or +"SomeFile" as is appropriate. + +This document is Copyright (C) 2016 Timothe Litt. + +See the COPYING file for license. diff --git a/extracters/vhd-util/atomicio.c b/extracters/vhd-util/atomicio.c new file mode 100644 index 0000000..95bd754 --- /dev/null +++ b/extracters/vhd-util/atomicio.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2005 Anil Madhavapeddy. All rights reserved. + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 +#include +#include +#include "atomicio.h" + +/* + * ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t +atomicio(f, fd, _s, n) + ssize_t (*f) (int, void *, size_t); + int fd; + void *_s; + size_t n; +{ + char *s = _s; + size_t pos = 0; + ssize_t res; + + while (n > pos) { + res = (f) (fd, s + pos, n - pos); + switch (res) { + case -1: + if (errno == EINTR || errno == EAGAIN) + continue; + return 0; + case 0: + errno = EPIPE; + return pos; + default: + pos += (size_t)res; + } + } + return (pos); +} + diff --git a/extracters/vhd-util/gnugetopt.c b/extracters/vhd-util/gnugetopt.c new file mode 100644 index 0000000..402be3e --- /dev/null +++ b/extracters/vhd-util/gnugetopt.c @@ -0,0 +1,506 @@ +/* This is GNU's getopt.c */ + +char *optarg; + +int optind = 1; + +int __getopt_initialized; + +static char *nextchar; + +int opterr = 1; + +int optopt = '?'; + +static enum +{ + REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER +} ordering; + +static char *posixly_correct = 0; +#include +#include +#include +#include + +#define _(x) x + +static char * +my_index (str, chr) + const char *str; + int chr; +{ + while (*str) + { + if (*str == chr) + return (char *) str; + str++; + } + return 0; +} + +static int first_nonopt; +static int last_nonopt; + +# define SWAP_FLAGS(ch1, ch2) + +#if defined __STDC__ && __STDC__ +static void exchange (char **); +#endif + +static void +exchange (argv) + char **argv; +{ + int bottom = first_nonopt; + int middle = last_nonopt; + int top = optind; + char *tem; + + while (top > middle && middle > bottom) + { + if (top - middle > middle - bottom) + { + int len = middle - bottom; + register int i; + + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); + } + top -= len; + } + else + { + int len = top - middle; + register int i; + + for (i = 0; i < len; i++) + { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + SWAP_FLAGS (bottom + i, middle + i); + } + bottom += len; + } + } + + first_nonopt += (optind - last_nonopt); + last_nonopt = optind; +} + +#if defined __STDC__ && __STDC__ +static const char *_getopt_initialize (int, char *const *, const char *); +#endif +static const char * +_getopt_initialize (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + first_nonopt = last_nonopt = optind; + + nextchar = NULL; + + if (optstring[0] == '-') + { + ordering = RETURN_IN_ORDER; + ++optstring; + } + else if (optstring[0] == '+') + { + ordering = REQUIRE_ORDER; + ++optstring; + } + else if (posixly_correct != NULL) + ordering = REQUIRE_ORDER; + else + ordering = PERMUTE; + + return optstring; +} + +int +_getopt_internal (argc, argv, optstring, longopts, longind, long_only) + int argc; + char *const *argv; + const char *optstring; + const struct option *longopts; + int *longind; + int long_only; +{ + int print_errors = opterr; + if (optstring[0] == ':') + print_errors = 0; + + optarg = NULL; + + if (optind == 0 || !__getopt_initialized) + { + if (optind == 0) + optind = 1; /* Don't scan ARGV[0], the program name. */ + optstring = _getopt_initialize (argc, argv, optstring); + __getopt_initialized = 1; + } + +# define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') + + if (nextchar == NULL || *nextchar == '\0') + { + if (last_nonopt > optind) + last_nonopt = optind; + if (first_nonopt > optind) + first_nonopt = optind; + + if (ordering == PERMUTE) + { + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (last_nonopt != optind) + first_nonopt = optind; + + while (optind < argc && NONOPTION_P) + optind++; + last_nonopt = optind; + } + + + if (optind != argc && !strcmp (argv[optind], "--")) + { + optind++; + + if (first_nonopt != last_nonopt && last_nonopt != optind) + exchange ((char **) argv); + else if (first_nonopt == last_nonopt) + first_nonopt = optind; + last_nonopt = argc; + + optind = argc; + } + + if (optind == argc) + { + if (first_nonopt != last_nonopt) + optind = first_nonopt; + return -1; + } + + if (NONOPTION_P) + { + if (ordering == REQUIRE_ORDER) + return -1; + optarg = argv[optind++]; + return 1; + } + + nextchar = (argv[optind] + 1 + + (longopts != NULL && argv[optind][1] == '-')); + } + + + if (longopts != NULL + && (argv[optind][1] == '-' + || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) + == (unsigned int) strlen (p->name)) + { + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + pfound = p; + indfound = option_index; + } + else + ambig = 1; + } + + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `%s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + optopt = 0; + return '?'; + } + + if (pfound != NULL) + { + option_index = indfound; + optind++; + if (*nameend) + { + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + { + if (argv[optind - 1][1] == '-') + fprintf (stderr, + _("%s: option `--%s' doesn't allow an argument\n"), + argv[0], pfound->name); + else + fprintf (stderr, + _("%s: option `%c%s' doesn't allow an argument\n"), + argv[0], argv[optind - 1][0], pfound->name); + } + + nextchar += strlen (nextchar); + + optopt = pfound->val; + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + + if (!long_only || argv[optind][1] == '-' + || my_index (optstring, *nextchar) == NULL) + { + if (print_errors) + { + if (argv[optind][1] == '-') + fprintf (stderr, _("%s: unrecognized option `--%s'\n"), + argv[0], nextchar); + else + /* +option or -option */ + fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), + argv[0], argv[optind][0], nextchar); + } + nextchar = (char *) ""; + optind++; + optopt = 0; + return '?'; + } + } + { + char c = *nextchar++; + char *temp = my_index (optstring, c); + + if (*nextchar == '\0') + ++optind; + + if (temp == NULL || c == ':') + { + if (print_errors) + { + if (posixly_correct) + fprintf (stderr, _("%s: illegal option -- %c\n"), + argv[0], c); + else + fprintf (stderr, _("%s: invalid option -- %c\n"), + argv[0], c); + } + optopt = c; + return '?'; + } + if (temp[0] == 'W' && temp[1] == ';') + { + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = 0; + int option_index; + + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + fprintf (stderr, _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + return c; + } + else + optarg = argv[optind++]; + + for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp (p->name, nextchar, nameend - nextchar)) + { + if ((unsigned int) (nameend - nextchar) == strlen (p->name)) + { + pfound = p; + indfound = option_index; + exact = 1; + break; + } + else if (pfound == NULL) + { + pfound = p; + indfound = option_index; + } + else + ambig = 1; + } + if (ambig && !exact) + { + if (print_errors) + fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), + argv[0], argv[optind]); + nextchar += strlen (nextchar); + optind++; + return '?'; + } + if (pfound != NULL) + { + option_index = indfound; + if (*nameend) + { + if (pfound->has_arg) + optarg = nameend + 1; + else + { + if (print_errors) + fprintf (stderr, _("\ +%s: option `-W %s' doesn't allow an argument\n"), + argv[0], pfound->name); + + nextchar += strlen (nextchar); + return '?'; + } + } + else if (pfound->has_arg == 1) + { + if (optind < argc) + optarg = argv[optind++]; + else + { + if (print_errors) + fprintf (stderr, + _("%s: option `%s' requires an argument\n"), + argv[0], argv[optind - 1]); + nextchar += strlen (nextchar); + return optstring[0] == ':' ? ':' : '?'; + } + } + nextchar += strlen (nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) + { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + nextchar = NULL; + return 'W'; /* Let the application handle it. */ + } + if (temp[1] == ':') + { + if (temp[2] == ':') + { + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else + optarg = NULL; + nextchar = NULL; + } + else + { + if (*nextchar != '\0') + { + optarg = nextchar; + optind++; + } + else if (optind == argc) + { + if (print_errors) + { + fprintf (stderr, + _("%s: option requires an argument -- %c\n"), + argv[0], c); + } + optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } + else + optarg = argv[optind++]; + nextchar = NULL; + } + } + return c; + } +} + +int +getopt (argc, argv, optstring) + int argc; + char *const *argv; + const char *optstring; +{ + return _getopt_internal (argc, argv, optstring, + (const struct option *) 0, + (int *) 0, + 0); +} + diff --git a/extracters/vhd-util/include/atomicio.h b/extracters/vhd-util/include/atomicio.h new file mode 100644 index 0000000..7eccf20 --- /dev/null +++ b/extracters/vhd-util/include/atomicio.h @@ -0,0 +1,33 @@ +/* $OpenBSD: atomicio.h,v 1.6 2005/05/24 17:32:43 avsm Exp $ */ + +/* + * Copyright (c) 1995,1999 Theo de Raadt. All rights reserved. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Ensure all of data on socket comes through. f==read || f==vwrite + */ +size_t atomicio(ssize_t (*)(int, void *, size_t), int, void *, size_t); + +#define vwrite (ssize_t (*)(int, void *, size_t))write diff --git a/extracters/vhd-util/include/libvhd-journal.h b/extracters/vhd-util/include/libvhd-journal.h new file mode 100644 index 0000000..7585252 --- /dev/null +++ b/extracters/vhd-util/include/libvhd-journal.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2008, XenSource 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 _VHD_JOURNAL_H_ +#define _VHD_JOURNAL_H_ + +#include + +#include "libvhd.h" + +#define VHD_JOURNAL_METADATA 0x01 +#define VHD_JOURNAL_DATA 0x02 + +#define VHD_JOURNAL_HEADER_COOKIE "vjournal" +#define VHD_JOURNAL_ENTRY_COOKIE 0xaaaa12344321aaaa + +typedef struct vhd_journal_header { + char cookie[8]; + blk_uuid_t uuid; + uint64_t vhd_footer_offset; + uint32_t journal_data_entries; + uint32_t journal_metadata_entries; + uint64_t journal_data_offset; + uint64_t journal_metadata_offset; + uint64_t journal_eof; + char pad[448]; +} vhd_journal_header_t; + +typedef struct vhd_journal { + char *jname; + int jfd; + int is_block; /* is jfd a block device */ + vhd_journal_header_t header; + vhd_context_t vhd; +} vhd_journal_t; + +int vhd_journal_create(vhd_journal_t *, const char *file, const char *jfile); +int vhd_journal_open(vhd_journal_t *, const char *file, const char *jfile); +int vhd_journal_add_block(vhd_journal_t *, uint32_t block, char mode); +int vhd_journal_commit(vhd_journal_t *); +int vhd_journal_revert(vhd_journal_t *); +int vhd_journal_close(vhd_journal_t *); +int vhd_journal_remove(vhd_journal_t *); + +#endif diff --git a/extracters/vhd-util/include/list.h b/extracters/vhd-util/include/list.h new file mode 100644 index 0000000..2a5f855 --- /dev/null +++ b/extracters/vhd-util/include/list.h @@ -0,0 +1,124 @@ +/* + * list.h + * + * This is a subset of linux's list.h intended to be used in user-space. + * XXX The namespace conflicts with NetBSD's + * + */ + +#ifndef __LIST_H__ +#define __LIST_H__ + +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +struct list_head { + struct list_head *next, *prev; +}; + +/* XXX workaround for conflicts. The list API should use its own + * namespace prefix, i.e. BLK_ + */ +#ifdef LIST_HEAD_INIT +#undef LIST_HEAD_INIT +#endif +#ifndef LIST_HEAD +#undef LIST_HEAD +#endif + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +static inline void INIT_LIST_HEAD(struct list_head *list) +{ + list->next = list; + list->prev = list; +} + +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +static inline int list_is_last(const struct list_head *list, + const struct list_head *head) +{ + return list->next == head; +} + +static inline void __list_splice(struct list_head *list, + struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; +} + +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); +} + +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif /* __LIST_H__ */ diff --git a/extracters/vhd-util/include/lvm-util.h b/extracters/vhd-util/include/lvm-util.h new file mode 100644 index 0000000..95f3320 --- /dev/null +++ b/extracters/vhd-util/include/lvm-util.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2008, XenSource 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 _LVM_UTIL_H_ +#define _LVM_UTIL_H_ + +#include + +#define MAX_NAME_SIZE 256 + +#define LVM_SEG_TYPE_LINEAR 1 +#define LVM_SEG_TYPE_UNKNOWN 2 + +struct lv_segment { + uint8_t type; + char device[MAX_NAME_SIZE]; + uint64_t pe_start; + uint64_t pe_size; +}; + +struct lv { + char name[MAX_NAME_SIZE]; + uint64_t size; + uint32_t segments; + struct lv_segment first_segment; +}; + +struct pv { + char name[MAX_NAME_SIZE]; + uint64_t start; +}; + +struct vg { + char name[MAX_NAME_SIZE]; + uint64_t extent_size; + + int pv_cnt; + struct pv *pvs; + + int lv_cnt; + struct lv *lvs; +}; + +int lvm_scan_vg(const char *vg_name, struct vg *vg); +void lvm_free_vg(struct vg *vg); + +#endif diff --git a/extracters/vhd-util/include/vhd-util-win32.h b/extracters/vhd-util/include/vhd-util-win32.h new file mode 100644 index 0000000..6c6b574 --- /dev/null +++ b/extracters/vhd-util/include/vhd-util-win32.h @@ -0,0 +1,73 @@ +/* + * Compatibility file for Windows. + * Compiler-forced #include at line 1 of every source. + */ + +#include +#include +#include +#include +#include +#include +#if defined(_MSC_VER) +#include +typedef SSIZE_T ssize_t; +#endif +#include +#include "relative-path.h" +#define O_DIRECT 0 + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 + +#define open _open +#define close _close + +static inline int ftruncate(int fd, __int64 length) +{ + HANDLE fh = (HANDLE)_get_osfhandle(fd); + + if (!fh || _lseeki64(fd, length, SEEK_SET)) + return -1; + return SetEndOfFile(fh) ? 0 : -1; +} + +#define fdatasync fsync + +static inline int +fsync (int fd) +{ + HANDLE h = (HANDLE) _get_osfhandle (fd); + DWORD err; + + if (h == INVALID_HANDLE_VALUE) + { + errno = EBADF; + return -1; + } + + if (!FlushFileBuffers (h)) + { + err = GetLastError (); + switch (err) + { + case ERROR_ACCESS_DENIED: + return 0; + + case ERROR_INVALID_HANDLE: + errno = EINVAL; + break; + + default: + errno = EIO; + } + return -1; + } + + return 0; +} + +/* Emulated in libvhd */ + +char *basename( char *path ); diff --git a/extracters/vhd-util/include/vhd-util.h b/extracters/vhd-util/include/vhd-util.h new file mode 100644 index 0000000..11f077e --- /dev/null +++ b/extracters/vhd-util/include/vhd-util.h @@ -0,0 +1,44 @@ +/* Copyright (c) 2008, XenSource 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 _VHD_UTIL_H_ +#define _VHD_UTIL_H_ + +int vhd_util_create(int argc, char **argv); +int vhd_util_snapshot(int argc, char **argv); +int vhd_util_query(int argc, char **argv); +int vhd_util_read(int argc, char **argv); +int vhd_util_set_field(int argc, char **argv); +int vhd_util_repair(int argc, char **argv); +int vhd_util_fill(int argc, char **argv); +int vhd_util_resize(int argc, char **argv); +int vhd_util_coalesce(int argc, char **argv); +int vhd_util_modify(int argc, char **argv); +int vhd_util_scan(int argc, char **argv); +int vhd_util_check(int argc, char **argv); +int vhd_util_revert(int argc, char **argv); + +#endif diff --git a/extracters/vhd-util/include/wingetopt.h b/extracters/vhd-util/include/wingetopt.h new file mode 100644 index 0000000..8c32d0b --- /dev/null +++ b/extracters/vhd-util/include/wingetopt.h @@ -0,0 +1,88 @@ +/* Declarations for getopt. + Copyright (C) 1989,90,91,92,93,94,96,97,98 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ + +#ifndef _GETOPT_H + +#ifndef __need_getopt +# define _GETOPT_H 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +extern char *optarg; + +extern int optind; + +extern int opterr; + +extern int optopt; + +#ifndef __need_getopt + +struct option +{ +# if defined __STDC__ && __STDC__ + const char *name; +# else + char *name; +# endif + int has_arg; + int *flag; + int val; +}; + +# define no_argument 0 +# define required_argument 1 +# define optional_argument 2 +#endif /* need getopt */ + + +#if defined __STDC__ && __STDC__ +extern int getopt (); + +# ifndef __need_getopt +extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, + const struct option *__longopts, int *__longind); +extern int getopt_long_only (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind); + +extern int _getopt_internal (int __argc, char *const *__argv, + const char *__shortopts, + const struct option *__longopts, int *__longind, + int __long_only); +# endif +#else /* not __STDC__ */ +extern int getopt (); +# ifndef __need_getopt +extern int getopt_long (); +extern int getopt_long_only (); + +extern int _getopt_internal (); +# endif +#endif /* __STDC__ */ + +#ifdef __cplusplus +} +#endif +#undef __need_getopt + +#endif /* getopt.h */ diff --git a/extracters/vhd-util/libvhd-journal.c b/extracters/vhd-util/libvhd-journal.c new file mode 100644 index 0000000..bc97b13 --- /dev/null +++ b/extracters/vhd-util/libvhd-journal.c @@ -0,0 +1,1618 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#define O_BINARY 0 +#endif +#include + +#include "atomicio.h" +#include "libvhd-journal.h" + +#define VHD_JOURNAL_ENTRY_TYPE_FOOTER_P 1 +#define VHD_JOURNAL_ENTRY_TYPE_FOOTER_C 2 +#define VHD_JOURNAL_ENTRY_TYPE_HEADER 3 +#define VHD_JOURNAL_ENTRY_TYPE_LOCATOR 4 +#define VHD_JOURNAL_ENTRY_TYPE_BAT 5 +#define VHD_JOURNAL_ENTRY_TYPE_BATMAP_H 6 +#define VHD_JOURNAL_ENTRY_TYPE_BATMAP_M 7 +#define VHD_JOURNAL_ENTRY_TYPE_DATA 8 + +typedef struct vhd_journal_entry { + uint64_t cookie; + uint32_t type; + uint32_t size; + uint64_t offset; + uint32_t checksum; +} vhd_journal_entry_t; + +static inline int +vhd_journal_seek(vhd_journal_t *j, off_t offset, int whence) +{ + off_t off; + + off = lseek(j->jfd, offset, whence); + if (off == (off_t)-1) + return -errno; + + return 0; +} + +static inline off_t +vhd_journal_position(vhd_journal_t *j) +{ + return lseek(j->jfd, 0, SEEK_CUR); +} + +static inline int +vhd_journal_read(vhd_journal_t *j, void *buf, size_t size) +{ + ssize_t ret; + + errno = 0; + + ret = atomicio(read, j->jfd, buf, size); + if (ret != size) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline int +vhd_journal_write(vhd_journal_t *j, void *buf, size_t size) +{ + ssize_t ret; + + errno = 0; + + ret = atomicio(vwrite, j->jfd, buf, size); + if (ret != size) + return (errno ? -errno : -EIO); + + return 0; +} + +static inline int +vhd_journal_truncate(vhd_journal_t *j, off_t length) +{ + int err; + + err = ftruncate(j->jfd, length); + if (err == -1) + return -errno; + + return 0; +} + +static inline int +vhd_journal_sync(vhd_journal_t *j) +{ + int err; + + err = fdatasync(j->jfd); + if (err) + return -errno; + + return 0; +} + +static inline void +vhd_journal_header_in(vhd_journal_header_t *header) +{ + BE64_IN(&header->vhd_footer_offset); + BE32_IN(&header->journal_data_entries); + BE32_IN(&header->journal_metadata_entries); + BE64_IN(&header->journal_data_offset); + BE64_IN(&header->journal_metadata_offset); +} + +static inline void +vhd_journal_header_out(vhd_journal_header_t *header) +{ + BE64_OUT(&header->vhd_footer_offset); + BE32_OUT(&header->journal_data_entries); + BE32_OUT(&header->journal_metadata_entries); + BE64_OUT(&header->journal_data_offset); + BE64_OUT(&header->journal_metadata_offset); +} + +static int +vhd_journal_validate_header(vhd_journal_t *j, vhd_journal_header_t *header) +{ + int err; + off_t eof; + + if (memcmp(header->cookie, + VHD_JOURNAL_HEADER_COOKIE, sizeof(header->cookie))) + return -EINVAL; + + err = vhd_journal_seek(j, (off_t)j->header.journal_eof, SEEK_SET); + if (err) + return err; + + eof = vhd_journal_position(j); + if (eof == (off_t)-1) + return -errno; + + if (j->header.journal_data_offset > j->header.journal_eof) + return -EINVAL; + + if (j->header.journal_metadata_offset > j->header.journal_eof) + return -EINVAL; + + return 0; +} + +static int +vhd_journal_read_journal_header(vhd_journal_t *j, vhd_journal_header_t *header) +{ + int err; + size_t size; + + size = sizeof(vhd_journal_header_t); + err = vhd_journal_seek(j, 0, SEEK_SET); + if (err) + return err; + + err = vhd_journal_read(j, header, size); + if (err) + return err; + + vhd_journal_header_in(header); + + return vhd_journal_validate_header(j, header); +} + +static int +vhd_journal_write_header(vhd_journal_t *j, vhd_journal_header_t *header) +{ + int err; + size_t size; + vhd_journal_header_t h; + + memcpy(&h, header, sizeof(vhd_journal_header_t)); + + err = vhd_journal_validate_header(j, &h); + if (err) + return err; + + vhd_journal_header_out(&h); + size = sizeof(vhd_journal_header_t); + + err = vhd_journal_seek(j, 0, SEEK_SET); + if (err) + return err; + + err = vhd_journal_write(j, &h, size); + if (err) + return err; + + return 0; +} + +static int +vhd_journal_add_journal_header(vhd_journal_t *j) +{ + int err; + off_t off; + vhd_context_t *vhd; + + vhd = &j->vhd; + memset(&j->header, 0, sizeof(vhd_journal_header_t)); + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) + return err; + + off = vhd_position(vhd); + if (off == (off_t)-1) + return -errno; + + err = vhd_get_footer(vhd); + if (err) + return err; + + blk_uuid_copy(&j->header.uuid, &vhd->footer.uuid); + memcpy(j->header.cookie, + VHD_JOURNAL_HEADER_COOKIE, sizeof(j->header.cookie)); + j->header.vhd_footer_offset = off - sizeof(vhd_footer_t); + j->header.journal_eof = sizeof(vhd_journal_header_t); + + return vhd_journal_write_header(j, &j->header); +} + +static void +vhd_journal_entry_in(vhd_journal_entry_t *entry) +{ + BE32_IN(&entry->type); + BE32_IN(&entry->size); + BE64_IN(&entry->offset); + BE64_IN(&entry->cookie); + BE32_IN(&entry->checksum); +} + +static void +vhd_journal_entry_out(vhd_journal_entry_t *entry) +{ + BE32_OUT(&entry->type); + BE32_OUT(&entry->size); + BE64_OUT(&entry->offset); + BE64_OUT(&entry->cookie); + BE32_OUT(&entry->checksum); +} + +static uint32_t +vhd_journal_checksum_entry(vhd_journal_entry_t *entry, char *buf, size_t size) +{ + size_t i; + unsigned char *blob; + uint32_t checksum, tmp; + + checksum = 0; + tmp = entry->checksum; + entry->checksum = 0; + + blob = (unsigned char *)entry; + for (i = 0; i < sizeof(vhd_journal_entry_t); i++) + checksum += blob[i]; + + blob = (unsigned char *)buf; + for (i = 0; i < size; i++) + checksum += blob[i]; + + entry->checksum = tmp; + return ~checksum; +} + +static int +vhd_journal_validate_entry(vhd_journal_entry_t *entry) +{ + if (entry->size == 0) + return -EINVAL; + + if (entry->size & (VHD_SECTOR_SIZE - 1)) + return -EINVAL; + + if (entry->cookie != VHD_JOURNAL_ENTRY_COOKIE) + return -EINVAL; + + return 0; +} + +static int +vhd_journal_read_entry(vhd_journal_t *j, vhd_journal_entry_t *entry) +{ + int err; + + err = vhd_journal_read(j, entry, sizeof(vhd_journal_entry_t)); + if (err) + return err; + + vhd_journal_entry_in(entry); + return vhd_journal_validate_entry(entry); +} + +static int +vhd_journal_write_entry(vhd_journal_t *j, vhd_journal_entry_t *entry) +{ + int err; + vhd_journal_entry_t e; + + err = vhd_journal_validate_entry(entry); + if (err) + return err; + + memcpy(&e, entry, sizeof(vhd_journal_entry_t)); + vhd_journal_entry_out(&e); + + err = vhd_journal_write(j, &e, sizeof(vhd_journal_entry_t)); + if (err) + err; + + return 0; +} + +static int +vhd_journal_validate_entry_data(vhd_journal_entry_t *entry, char *buf) +{ + int err; + uint32_t checksum; + + err = 0; + checksum = vhd_journal_checksum_entry(entry, buf, entry->size); + + if (checksum != entry->checksum) + return -EINVAL; + + return err; +} + +static int +vhd_journal_update(vhd_journal_t *j, off_t offset, + char *buf, size_t size, uint32_t type) +{ + int err; + uint64_t *off, off_bak; + uint32_t *entries; + vhd_journal_entry_t entry; + + entry.type = type; + entry.size = size; + entry.offset = offset; + entry.cookie = VHD_JOURNAL_ENTRY_COOKIE; + entry.checksum = vhd_journal_checksum_entry(&entry, buf, size); + + err = vhd_journal_seek(j, (off_t)j->header.journal_eof, SEEK_SET); + if (err) + return err; + + err = vhd_journal_write_entry(j, &entry); + if (err) + goto fail; + + err = vhd_journal_write(j, buf, size); + if (err) + goto fail; + + if (type == VHD_JOURNAL_ENTRY_TYPE_DATA) { + off = &j->header.journal_data_offset; + entries = &j->header.journal_data_entries; + } else { + off = &j->header.journal_metadata_offset; + entries = &j->header.journal_metadata_entries; + } + + off_bak = *off; + if (!(*entries)++) + *off = j->header.journal_eof; + j->header.journal_eof += (size + sizeof(vhd_journal_entry_t)); + + err = vhd_journal_write_header(j, &j->header); + if (err) { + if (!--(*entries)) + *off = off_bak; + j->header.journal_eof -= (size + sizeof(vhd_journal_entry_t)); + goto fail; + } + + return 0; + +fail: + if (!j->is_block) + vhd_journal_truncate(j, (off_t)j->header.journal_eof); + return err; +} + +static int +vhd_journal_add_footer(vhd_journal_t *j) +{ + int err; + off_t off; + vhd_context_t *vhd; + vhd_footer_t footer; + + vhd = &j->vhd; + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) + return err; + + off = vhd_position(vhd); + if (off == (off_t)-1) + return -errno; + + err = vhd_read_footer_at(vhd, &footer, off - sizeof(vhd_footer_t)); + if (err) + return err; + + vhd_footer_out(&footer); + err = vhd_journal_update(j, off - sizeof(vhd_footer_t), + (char *)&footer, + sizeof(vhd_footer_t), + VHD_JOURNAL_ENTRY_TYPE_FOOTER_P); + if (err) + return err; + + if (!vhd_type_dynamic(vhd)) + return 0; + + err = vhd_read_footer_at(vhd, &footer, 0); + if (err) + return err; + + vhd_footer_out(&footer); + err = vhd_journal_update(j, 0, + (char *)&footer, + sizeof(vhd_footer_t), + VHD_JOURNAL_ENTRY_TYPE_FOOTER_C); + + return err; +} + +static int +vhd_journal_add_header(vhd_journal_t *j) +{ + int err; + off_t off; + vhd_context_t *vhd; + vhd_header_t header; + + vhd = &j->vhd; + + err = vhd_read_header(vhd, &header); + if (err) + return err; + + off = (off_t)vhd->footer.data_offset; + + vhd_header_out(&header); + err = vhd_journal_update(j, off, + (char *)&header, + sizeof(vhd_header_t), + VHD_JOURNAL_ENTRY_TYPE_HEADER); + + return err; +} + +static int +vhd_journal_add_locators(vhd_journal_t *j) +{ + int i, n, err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_header(vhd); + if (err) + return err; + + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + for (i = 0; i < n; i++) { + char *buf; + off_t off; + size_t size; + vhd_parent_locator_t *loc; + + loc = vhd->header.loc + i; + err = vhd_validate_platform_code(loc->code); + if (err) + return err; + + if (loc->code == PLAT_CODE_NONE) + continue; + + off = (off_t)loc->data_offset; + size = vhd_parent_locator_size(loc); + +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) + return -errno; +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) + return -err; +#endif + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto end; + + err = vhd_read(vhd, buf, size); + if (err) + goto end; + + err = vhd_journal_update(j, off, buf, size, + VHD_JOURNAL_ENTRY_TYPE_LOCATOR); + if (err) + goto end; + + err = 0; + + end: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + if (err) + break; + } + + return err; +} + +static int +vhd_journal_add_bat(vhd_journal_t *j) +{ + int err; + off_t off; + size_t size; + vhd_bat_t bat; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_header(vhd); + if (err) + return err; + + err = vhd_read_bat(vhd, &bat); + if (err) + return err; + + off = (off_t)vhd->header.table_offset; + size = (size_t)vhd_bytes_padded(bat.entries * sizeof(uint32_t)); + + vhd_bat_out(&bat); + err = vhd_journal_update(j, off, (char *)bat.bat, size, + VHD_JOURNAL_ENTRY_TYPE_BAT); + +#ifdef _WIN32 + _aligned_free( bat.bat ); +#else + free( bat.bat ); +#endif + return err; +} + +static int +vhd_journal_add_batmap(vhd_journal_t *j) +{ + int err; + off_t off; + size_t size; + vhd_context_t *vhd; + vhd_batmap_t batmap; + + vhd = &j->vhd; + + err = vhd_batmap_header_offset(vhd, &off); + if (err) + return err; + + err = vhd_read_batmap(vhd, &batmap); + if (err) + return err; + + size = (size_t)vhd_bytes_padded(sizeof(struct dd_batmap_hdr)); + + vhd_batmap_header_out(&batmap); + err = vhd_journal_update(j, off, (char *)&batmap.header, size, + VHD_JOURNAL_ENTRY_TYPE_BATMAP_H); + if (err) + goto out; + + vhd_batmap_header_in(&batmap); + off = (off_t)batmap.header.batmap_offset; + size = (size_t)vhd_sectors_to_bytes(batmap.header.batmap_size); + + err = vhd_journal_update(j, off, batmap.map, size, + VHD_JOURNAL_ENTRY_TYPE_BATMAP_M); + +out: +#ifdef _WIN32 + _aligned_free( batmap.map ); +#else + free( batmap.map ); +#endif + return err; +} + +static int +vhd_journal_add_metadata(vhd_journal_t *j) +{ + int err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_journal_add_footer(j); + if (err) + return err; + + if (!vhd_type_dynamic(vhd)) + return 0; + + err = vhd_journal_add_header(j); + if (err) + return err; + + err = vhd_journal_add_locators(j); + if (err) + return err; + + err = vhd_journal_add_bat(j); + if (err) + return err; + + if (vhd_has_batmap(vhd)) { + err = vhd_journal_add_batmap(j); + if (err) + return err; + } + + j->header.journal_data_offset = j->header.journal_eof; + return vhd_journal_write_header(j, &j->header); +} + +static int +__vhd_journal_read_footer(vhd_journal_t *j, + vhd_footer_t *footer, uint32_t type) +{ + int err; + vhd_journal_entry_t entry; + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != type) + return -EINVAL; + + if (entry.size != sizeof(vhd_footer_t)) + return -EINVAL; + + err = vhd_journal_read(j, footer, entry.size); + if (err) + return err; + + vhd_footer_in(footer); + return vhd_validate_footer(footer); +} + +static int +vhd_journal_read_footer(vhd_journal_t *j, vhd_footer_t *footer) +{ + return __vhd_journal_read_footer(j, footer, + VHD_JOURNAL_ENTRY_TYPE_FOOTER_P); +} + +static int +vhd_journal_read_footer_copy(vhd_journal_t *j, vhd_footer_t *footer) +{ + return __vhd_journal_read_footer(j, footer, + VHD_JOURNAL_ENTRY_TYPE_FOOTER_C); +} + +static int +vhd_journal_read_header(vhd_journal_t *j, vhd_header_t *header) +{ + int err; + vhd_journal_entry_t entry; + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_HEADER) + return -EINVAL; + + if (entry.size != sizeof(vhd_header_t)) + return -EINVAL; + + err = vhd_journal_read(j, header, entry.size); + if (err) + return err; + + vhd_header_in(header); + return vhd_validate_header(header); +} + +static int +vhd_journal_read_locators(vhd_journal_t *j, char ***locators, int *locs) +{ + int err, n, _locs; + char **_locators, *buf; + off_t pos; + vhd_journal_entry_t entry; + + _locs = 0; + *locs = 0; + *locators = NULL; + + n = sizeof(j->vhd.header.loc) / sizeof(vhd_parent_locator_t); + _locators = calloc(n, sizeof(char *)); + if (!_locators) + return -ENOMEM; + + for( ;;) { + buf = NULL; + + pos = vhd_journal_position( j ); + err = vhd_journal_read_entry( j, &entry ); + if( err ) + goto fail; + + if( entry.type != VHD_JOURNAL_ENTRY_TYPE_LOCATOR ) { + err = vhd_journal_seek( j, pos, SEEK_SET ); + if( err ) + goto fail; + break; + } + + if( _locs >= n ) { + err = -EINVAL; + goto fail; + } + +#ifdef _WIN32 + buf = _aligned_malloc( entry.size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto fail; + } +#else + err = posix_memalign((void **)&buf, + VHD_SECTOR_SIZE, entry.size); + if (err) { + err = -err; + buf = NULL; + goto fail; + } +#endif + err = vhd_journal_read(j, buf, entry.size); + if (err) + goto fail; + + _locators[_locs++] = buf; + err = 0; + } + + + *locs = _locs; + *locators = _locators; + + return 0; + +fail: + if (_locators) { + for (n = 0; n < _locs; n++) +#ifdef _WIN32 + _aligned_free( _locators[n] ); +#else + free( _locators[n] ); +#endif + free(_locators); + } + return err; +} + +static int +vhd_journal_read_bat(vhd_journal_t *j, vhd_bat_t *bat) +{ + int err; + size_t size; + vhd_context_t *vhd; + vhd_journal_entry_t entry; + + vhd = &j->vhd; + + size = (size_t)vhd_bytes_padded(vhd->header.max_bat_size * sizeof(uint32_t)); + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BAT) + return -EINVAL; + + if (entry.size != size) + return -EINVAL; + + if (entry.offset != vhd->header.table_offset) + return -EINVAL; + +#ifdef _WIN32 + bat->bat = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( bat->bat == NULL ) { + return -errno; + } +#else + err = posix_memalign((void **)&bat->bat, VHD_SECTOR_SIZE, size); + if (err) + return -err; +#endif + + err = vhd_journal_read(j, bat->bat, entry.size); + if (err) + goto fail; + + bat->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT; + bat->entries = vhd->header.max_bat_size; + vhd_bat_in(bat); + + return 0; + +fail: +#ifdef _WIN32 + _aligned_free( bat->bat ); +#else + free( bat->bat ); +#endif + bat->bat = NULL; + return err; +} + +static int +vhd_journal_read_batmap_header(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + int err; + char *buf; + size_t size; + vhd_journal_entry_t entry; + + size = (size_t)vhd_bytes_padded(sizeof(struct dd_batmap_hdr)); + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_H) + return -EINVAL; + + if (entry.size != size) + return -EINVAL; + +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) + return errno; +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) + return err; +#endif + + err = vhd_journal_read(j, buf, entry.size); + if (err) { +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + return err; + } + + memcpy(&batmap->header, buf, sizeof(batmap->header)); + + vhd_batmap_header_in(batmap); + return vhd_validate_batmap_header(batmap); +} + +static int +vhd_journal_read_batmap_map(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + int err; + vhd_journal_entry_t entry; + + err = vhd_journal_read_entry(j, &entry); + if (err) + return err; + + if (entry.type != VHD_JOURNAL_ENTRY_TYPE_BATMAP_M) + return -EINVAL; + + if (entry.size != vhd_sectors_to_bytes(batmap->header.batmap_size)) + return -EINVAL; + + if (entry.offset != batmap->header.batmap_offset) + return -EINVAL; + +#ifdef _WIN32 + batmap->map = _aligned_malloc( entry.size, VHD_SECTOR_SIZE ); + if( batmap->map == NULL ) + return -errno; +#else + err = posix_memalign((void **)&batmap->map, + VHD_SECTOR_SIZE, entry.size); + if (err) + return -err; +#endif + + err = vhd_journal_read(j, batmap->map, entry.size); + if (err) { +#ifdef _WIN32 + _aligned_free( batmap->map ); +#else + free( batmap->map ); + batmap->map = NULL; +#endif + return err; + } + + return 0; +} + +static int +vhd_journal_read_batmap(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + int err; + + err = vhd_journal_read_batmap_header(j, batmap); + if (err) + return err; + + err = vhd_journal_read_batmap_map(j, batmap); + if (err) + return err; + + err = vhd_validate_batmap(batmap); + if (err) { +#ifdef _WIN32 + _aligned_free( batmap->map ); +#else + free( batmap->map ); +#endif + batmap->map = NULL; + return err; + } + + return 0; +} + +static int +vhd_journal_restore_footer(vhd_journal_t *j, vhd_footer_t *footer) +{ + return vhd_write_footer_at(&j->vhd, footer, + (off_t)j->header.vhd_footer_offset); +} + +static int +vhd_journal_restore_footer_copy(vhd_journal_t *j, vhd_footer_t *footer) +{ + return vhd_write_footer_at(&j->vhd, footer, 0); +} + +static int +vhd_journal_restore_header(vhd_journal_t *j, vhd_header_t *header) +{ + off_t off; + vhd_context_t *vhd; + + vhd = &j->vhd; + off = (off_t)vhd->footer.data_offset; + + return vhd_write_header_at(&j->vhd, header, off); +} + +static int +vhd_journal_restore_locators(vhd_journal_t *j, char **locators, int locs) +{ + size_t size; + vhd_context_t *vhd; + int i, n, lidx, err; + vhd_parent_locator_t *loc; + + lidx = 0; + vhd = &j->vhd; + + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + + for (i = 0; i < n && lidx < locs; i++) { + loc = vhd->header.loc + i; + if (loc->code == PLAT_CODE_NONE) + continue; + + err = vhd_seek(vhd, (off_t)loc->data_offset, SEEK_SET); + if (err) + return err; + + size = vhd_parent_locator_size(loc); + err = vhd_write(vhd, locators[lidx++], size); + if (err) + return err; + } + + return 0; +} + +static int +vhd_journal_restore_bat(vhd_journal_t *j, vhd_bat_t *bat) +{ + return vhd_write_bat(&j->vhd, bat); +} + +static int +vhd_journal_restore_batmap(vhd_journal_t *j, vhd_batmap_t *batmap) +{ + return vhd_write_batmap(&j->vhd, batmap); +} + +static int +vhd_journal_restore_metadata(vhd_journal_t *j) +{ + off_t off; + char **locators; + vhd_footer_t copy; + vhd_context_t *vhd; + int i, locs, hlocs, err; + + vhd = &j->vhd; + locs = 0; + hlocs = 0; + locators = NULL; + + err = vhd_journal_seek(j, sizeof(vhd_journal_header_t), SEEK_SET); + if (err) + return err; + + err = vhd_journal_read_footer(j, &vhd->footer); + if (err) + return err; + + if (!vhd_type_dynamic(vhd)) + goto restore; + + err = vhd_journal_read_footer_copy(j, ©); + if (err) + return err; + + err = vhd_journal_read_header(j, &vhd->header); + if (err) + return err; + + for (hlocs = 0, i = 0; i < vhd_parent_locator_count(vhd); i++) { + if (vhd_validate_platform_code(vhd->header.loc[i].code)) + return err; + + if (vhd->header.loc[i].code != PLAT_CODE_NONE) + hlocs++; + } + + if (hlocs) { + err = vhd_journal_read_locators(j, &locators, &locs); + if (err) + return err; + + if (hlocs != locs) { + err = -EINVAL; + goto out; + } + } + + err = vhd_journal_read_bat(j, &vhd->bat); + if (err) + goto out; + + if (vhd_has_batmap(vhd)) { + err = vhd_journal_read_batmap(j, &vhd->batmap); + if (err) + goto out; + } + +restore: + off = vhd_journal_position(j); + if (off == (off_t)-1) + return -errno; + + if (j->header.journal_data_offset != off) + return -EINVAL; + + err = vhd_journal_restore_footer(j, &vhd->footer); + if (err) + goto out; + + if (!vhd_type_dynamic(vhd)) + goto out; + + err = vhd_journal_restore_footer_copy(j, ©); + if (err) + goto out; + + err = vhd_journal_restore_header(j, &vhd->header); + if (err) + goto out; + + if (locs) { + err = vhd_journal_restore_locators(j, locators, locs); + if (err) + goto out; + } + + err = vhd_journal_restore_bat(j, &vhd->bat); + if (err) + goto out; + + if (vhd_has_batmap(vhd)) { + err = vhd_journal_restore_batmap(j, &vhd->batmap); + if (err) + goto out; + } + + err = 0; + +out: + if (locators) { + for (i = 0; i < locs; i++) +#ifdef _WIN32 + _aligned_free( locators[i] ); +#else + free( locators[i] ); +#endif + free(locators); + } + + if (!err && !vhd->is_block) + err = ftruncate(vhd->fd, + j->header.vhd_footer_offset + + sizeof(vhd_footer_t)); + + return err; +} + +static int +vhd_journal_disable_vhd(vhd_journal_t *j) +{ + int err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_footer(vhd); + if (err) + return err; + + memcpy(&vhd->footer.cookie, + VHD_POISON_COOKIE, sizeof(vhd->footer.cookie)); + vhd->footer.checksum = vhd_checksum_footer(&vhd->footer); + + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + return 0; +} + +static int +vhd_journal_enable_vhd(vhd_journal_t *j) +{ + int err; + vhd_context_t *vhd; + + vhd = &j->vhd; + + err = vhd_get_footer(vhd); + if (err) + return err; + + if (!vhd_disabled(vhd)) + return 0; + + memcpy(&vhd->footer.cookie, HD_COOKIE, sizeof(vhd->footer.cookie)); + vhd->footer.checksum = vhd_checksum_footer(&vhd->footer); + + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + return 0; +} + +int +vhd_journal_close(vhd_journal_t *j) +{ + if (j->jfd) + close(j->jfd); + + vhd_close(&j->vhd); + free(j->jname); + + return 0; +} + +int +vhd_journal_remove(vhd_journal_t *j) +{ + int err; + + err = vhd_journal_enable_vhd(j); + if (err) + return err; + + if (j->jfd) { + close(j->jfd); + if (!j->is_block) + unlink(j->jname); + } + + vhd_close(&j->vhd); + free(j->jname); + + return 0; +} + +int +vhd_journal_open(vhd_journal_t *j, const char *file, const char *jfile) +{ + int err; + vhd_context_t *vhd; + + memset(j, 0, sizeof(vhd_journal_t)); + + j->jfd = -1; + vhd = &j->vhd; + + j->jname = strdup(jfile); + if (j->jname == NULL) + return -ENOMEM; + + j->jfd = open(j->jname, O_LARGEFILE | O_RDWR | O_BINARY); + if (j->jfd == -1) { + err = -errno; + goto fail; + } + + err = vhd_test_file_fixed(j->jname, &j->is_block); + if (err) + goto fail; + + vhd->fd = open(file, O_LARGEFILE | O_RDWR | O_DIRECT | O_BINARY ); + if (vhd->fd == -1) { + err = -errno; + goto fail; + } + + err = vhd_test_file_fixed(file, &vhd->is_block); + if (err) + goto fail; + + err = vhd_journal_read_journal_header(j, &j->header); + if (err) + goto fail; + + err = vhd_journal_restore_metadata(j); + if (err) + goto fail; + + close(vhd->fd); + free(vhd->bat.bat); + free(vhd->batmap.map); + + err = vhd_open(vhd, file, VHD_OPEN_RDWR); + if (err) + goto fail; + + err = vhd_get_bat(vhd); + if (err) + goto fail; + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) + goto fail; + } + + err = vhd_journal_disable_vhd(j); + if (err) + goto fail; + + return 0; + +fail: + vhd_journal_close(j); + return err; +} + +int +vhd_journal_create(vhd_journal_t *j, const char *file, const char *jfile) +{ + int err; + + memset(j, 0, sizeof(vhd_journal_t)); + j->jfd = -1; + + j->jname = strdup(jfile); + if (j->jname == NULL) { + err = -ENOMEM; + goto fail1; + } + +#ifdef _WIN32 + j->is_block = 0; + j->jfd = open(j->jname, + O_EXCL | O_CREAT | O_TRUNC | O_LARGEFILE | O_RDWR | O_BINARY, 0644); +#else + if (access(j->jname, F_OK) == 0) { + err = vhd_test_file_fixed(j->jname, &j->is_block); + if (err) + goto fail1; + + if (!j->is_block) { + err = -EEXIST; + goto fail1; + } + } + if (j->is_block) + j->jfd = open(j->jname, O_LARGEFILE | O_RDWR | O_BINARY, 0644); + else + j->jfd = open(j->jname, + O_CREAT | O_TRUNC | O_LARGEFILE | O_RDWR | O_BINARY, 0644); +#endif + if (j->jfd == -1) { + err = -errno; + goto fail1; + } + + err = vhd_open(&j->vhd, file, VHD_OPEN_RDWR | VHD_OPEN_STRICT); + if (err) + goto fail1; + + err = vhd_get_bat(&j->vhd); + if (err) + goto fail2; + + if (vhd_has_batmap(&j->vhd)) { + err = vhd_get_batmap(&j->vhd); + if (err) + goto fail2; + } + + err = vhd_journal_add_journal_header(j); + if (err) + goto fail2; + + err = vhd_journal_add_metadata(j); + if (err) + goto fail2; + + err = vhd_journal_disable_vhd(j); + if (err) + goto fail2; + + err = vhd_journal_sync(j); + if (err) + goto fail2; + + return 0; + +fail1: + if (j->jfd != -1) { + close(j->jfd); + if (!j->is_block) + unlink(j->jname); + } + free(j->jname); + memset(j, 0, sizeof(vhd_journal_t)); + + return err; + +fail2: + vhd_journal_remove(j); + return err; +} + +int +vhd_journal_add_block(vhd_journal_t *j, uint32_t block, char mode) +{ + int err; + char *buf; + off_t off; + size_t size; + uint64_t blk; + vhd_context_t *vhd; + + buf = NULL; + vhd = &j->vhd; + + if (!vhd_type_dynamic(vhd)) + return -EINVAL; + + err = vhd_get_bat(vhd); + if (err) + return err; + + if (block >= vhd->bat.entries) + return -ERANGE; + + blk = vhd->bat.bat[block]; + if (blk == DD_BLK_UNUSED) + return 0; + + off = (off_t)vhd_sectors_to_bytes(blk); + + if (mode & VHD_JOURNAL_METADATA) { + size = (size_t)vhd_sectors_to_bytes(vhd->bm_secs); + + err = vhd_read_bitmap(vhd, block, &buf); + if (err) + return err; + + err = vhd_journal_update(j, off, buf, size, + VHD_JOURNAL_ENTRY_TYPE_DATA); + + free(buf); + + if (err) + return err; + } + + if (mode & VHD_JOURNAL_DATA) { + off += (off_t)vhd_sectors_to_bytes(vhd->bm_secs); + size = (size_t)vhd_sectors_to_bytes(vhd->spb); + + err = vhd_read_block(vhd, block, &buf); + if (err) + return err; + + err = vhd_journal_update(j, off, buf, size, + VHD_JOURNAL_ENTRY_TYPE_DATA); + free(buf); + + if (err) + return err; + } + + return vhd_journal_sync(j); +} + +/* + * commit indicates the transaction completed + * successfully and we can remove the undo log + */ +int +vhd_journal_commit(vhd_journal_t *j) +{ + int err; + + j->header.journal_data_entries = 0; + j->header.journal_metadata_entries = 0; + j->header.journal_data_offset = 0; + j->header.journal_metadata_offset = 0; + + err = vhd_journal_write_header(j, &j->header); + if (err) + return err; + + if (!j->is_block) + err = vhd_journal_truncate(j, sizeof(vhd_journal_header_t)); + if (err) + return -errno; + + return 0; +} + +/* + * revert indicates the transaction failed + * and we should revert any changes via the undo log + */ +int +vhd_journal_revert(vhd_journal_t *j) +{ + size_t i; + int err; + char *buf, *file; + vhd_context_t *vhd; + vhd_journal_entry_t entry; + + err = 0; + vhd = &j->vhd; + buf = NULL; + + file = strdup(vhd->file); + if (!file) + return -ENOMEM; + + vhd_close(&j->vhd); + j->vhd.fd = open(file, O_RDWR | O_DIRECT | O_LARGEFILE | O_BINARY ); + if (j->vhd.fd == -1) { + free(file); + return -errno; + } + + err = vhd_test_file_fixed(file, &vhd->is_block); + if (err) { + free(file); + return err; + } + + err = vhd_journal_restore_metadata(j); + if (err) { + free(file); + return err; + } + + close(vhd->fd); + free(vhd->bat.bat); + free(vhd->batmap.map); + + err = vhd_open(vhd, file, VHD_OPEN_RDWR); + free(file); + if (err) + return err; + + err = vhd_journal_seek(j, (off_t)j->header.journal_data_offset, SEEK_SET); + if (err) + return err; + + for( i = 0; i < j->header.journal_data_entries; i++ ) { + err = vhd_journal_read_entry( j, &entry ); + if( err ) + goto end; + +#ifdef _WIN32 + buf = _aligned_malloc( entry.size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto end; + } +#else + err = posix_memalign( (void **)&buf, + VHD_SECTOR_SIZE, entry.size ); + if( err ) { + err = -err; + buf = NULL; + goto end; + } +#endif + + err = vhd_journal_read( j, buf, entry.size ); + if( err ) + goto end; + + err = vhd_journal_validate_entry_data( &entry, buf ); + if( err ) + goto end; + + err = vhd_seek( vhd, (off_t)entry.offset, SEEK_SET ); + if( err ) + goto end; + + err = vhd_write( vhd, buf, entry.size ); + if( err ) + goto end; + + err = 0; + + end: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + buf = NULL; + + if( err ) + break; + } + + if( err ) + return err; + + if (!vhd->is_block) { + err = ftruncate(vhd->fd, j->header.vhd_footer_offset + + sizeof(vhd_footer_t)); + if (err) + return -errno; + } + + return vhd_journal_sync(j); +} diff --git a/extracters/vhd-util/lvm-util.c b/extracters/vhd-util/lvm-util.c new file mode 100644 index 0000000..3fc5877 --- /dev/null +++ b/extracters/vhd-util/lvm-util.c @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2008, XenSource 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 +#include +#include +#include + +#include "lvm-util.h" + +#define _NAME "%255s" +static char line[1024]; + +static inline int +lvm_read_line(FILE *scan) +{ + memset(line, 0, sizeof(line)); + return (fscanf(scan, "%1023[^\n]", line) != 1); +} + +static inline int +lvm_next_line(FILE *scan) +{ + return (fscanf(scan, "%1023[\n]", line) != 1); +} + +static int +lvm_copy_name(char *dst, const char *src, size_t size) +{ + if (strnlen(src, size) == size) + return -ENAMETOOLONG; + + strcpy(dst, src); + return 0; +} + +static int +lvm_parse_pv(struct vg *vg, const char *name, int pvs, uint64_t start) +{ + int i, err; + struct pv *pv; + + pv = NULL; + + if (!vg->pvs) { + vg->pvs = calloc(pvs, sizeof(struct pv)); + if (!vg->pvs) + return -ENOMEM; + } + + for (i = 0; i < pvs; i++) { + pv = vg->pvs + i; + + if (!pv->name[0]) + break; + + if (!strcmp(pv->name, name)) + return -EEXIST; + } + + if (!pv) + return -ENOENT; + + if (i == pvs) + return -ENOMEM; + + err = lvm_copy_name(pv->name, name, sizeof(pv->name) - 1); + if (err) + return err; + + pv->start = start; + return 0; +} + +static int +lvm_open_vg(const char *vgname, struct vg *vg) +{ + FILE *scan; + int i, err, pvs, lvs; + char *cmd, pvname[256]; + uint64_t size, pv_start; + + memset(vg, 0, sizeof(*vg)); + + err = asprintf(&cmd, "/usr/sbin/vgs %s --noheadings --nosuffix --units=b " + "--options=vg_name,vg_extent_size,lv_count,pv_count," + "pv_name,pe_start --unbuffered 2> /dev/null", vgname); + if (err == -1) + return -ENOMEM; + + errno = 0; + scan = popen(cmd, "r"); + if (!scan) { + err = (errno ? -errno : ENOMEM); + goto out; + } + + for (;;) { + if (lvm_read_line(scan)) + break; + + err = -EINVAL; + if (sscanf(line, _NAME" %"SCNu64" %d %d "_NAME" %"SCNu64, + vg->name, &size, &lvs, &pvs, pvname, &pv_start) != 6) + goto out; + + if (strcmp(vg->name, vgname)) + goto out; + + err = lvm_parse_pv(vg, pvname, pvs, pv_start); + if (err) + goto out; + + if (lvm_next_line(scan)) + break; + } + + err = -EINVAL; + if (strcmp(vg->name, vgname)) + goto out; + + for (i = 0; i < pvs; i++) + if (!vg->pvs[i].name[0]) + goto out; + + err = -ENOMEM; + vg->lvs = calloc(lvs, sizeof(struct lv)); + if (!vg->lvs) + goto out; + + err = 0; + vg->lv_cnt = lvs; + vg->pv_cnt = pvs; + vg->extent_size = size; + +out: + if (scan) + pclose(scan); + if (err) + lvm_free_vg(vg); + free(cmd); + return err; +} + +static int +lvm_parse_lv_devices(struct vg *vg, struct lv_segment *seg, char *devices) +{ + size_t i; + uint64_t start, pe_start; + + for (i = 0; i < strlen(devices); i++) + if (strchr(",()", devices[i])) + devices[i] = ' '; + + if (sscanf(devices, _NAME" %"SCNu64, seg->device, &start) != 2) + return -EINVAL; + + pe_start = -1; + for (i = 0; i < (size_t)vg->pv_cnt; i++) + if (!strcmp(vg->pvs[i].name, seg->device)) { + pe_start = vg->pvs[i].start; + break; + } + + if (pe_start == -1) + return -EINVAL; + + seg->pe_start = (start * vg->extent_size) + pe_start; + return 0; +} + +static int +lvm_scan_lvs(struct vg *vg) +{ + char *cmd; + FILE *scan; + int i, err; + + err = asprintf(&cmd, "/usr/sbin/lvs %s --noheadings --nosuffix --units=b " + "--options=lv_name,lv_size,segtype,seg_count,seg_start," + "seg_size,devices --unbuffered 2> /dev/null", vg->name); + if (err == -1) + return -ENOMEM; + + errno = 0; + scan = popen(cmd, "r"); + if (!scan) { + err = (errno ? -errno : -ENOMEM); + goto out; + } + + for (i = 0;;) { + int segs; + struct lv *lv; + struct lv_segment seg; + uint64_t size, seg_start; + char type[32], name[256], devices[1024]; + + if (i >= vg->lv_cnt) + break; + + if (lvm_read_line(scan)) { + vg->lv_cnt = i; + break; + } + + err = -EINVAL; + lv = vg->lvs + i; + + if (sscanf(line, _NAME" %"SCNu64" %31s %u %"SCNu64" %"SCNu64" %1023s", + name, &size, type, &segs, &seg_start, + &seg.pe_size, devices) != 7) + goto out; + + if (seg_start) + goto next; + + if (!strcmp(type, "linear")) + seg.type = LVM_SEG_TYPE_LINEAR; + else + seg.type = LVM_SEG_TYPE_UNKNOWN; + + if (lvm_parse_lv_devices(vg, &seg, devices)) + goto out; + + i++; + lv->size = size; + lv->segments = segs; + lv->first_segment = seg; + + err = lvm_copy_name(lv->name, name, sizeof(lv->name) - 1); + if (err) + goto out; + err = -EINVAL; + + next: + if (lvm_next_line(scan)) + goto out; + } + + err = 0; + +out: + if (scan) + pclose(scan); + free(cmd); + return err; +} + +void +lvm_free_vg(struct vg *vg) +{ + free(vg->lvs); + free(vg->pvs); + memset(vg, 0, sizeof(*vg)); +} + +int +lvm_scan_vg(const char *vg_name, struct vg *vg) +{ + int err; + + memset(vg, 0, sizeof(*vg)); + + err = lvm_open_vg(vg_name, vg); + if (err) + return err; + + err = lvm_scan_lvs(vg); + if (err) { + lvm_free_vg(vg); + return err; + } + + return 0; +} + +#ifdef LVM_UTIL +static int +usage(void) +{ + printf("usage: lvm-util \n"); + exit(EINVAL); +} + +int +main(int argc, char **argv) +{ + int i, err; + struct vg vg; + struct pv *pv; + struct lv *lv; + struct lv_segment *seg; + + if (argc != 2) + usage(); + + err = lvm_scan_vg(argv[1], &vg); + if (err) { + printf("scan failed: %d\n", err); + return (err >= 0 ? err : -err); + } + + + printf("vg %s: extent_size: %"PRIu64", pvs: %d, lvs: %d\n", + vg.name, vg.extent_size, vg.pv_cnt, vg.lv_cnt); + + for (i = 0; i < vg.pv_cnt; i++) { + pv = vg.pvs + i; + printf("pv %s: start %"PRIu64"\n", pv->name, pv->start); + } + + for (i = 0; i < vg.lv_cnt; i++) { + lv = vg.lvs + i; + seg = &lv->first_segment; + printf("lv %s: size: %"PRIu64", segments: %u, type: %u, " + "dev: %s, pe_start: %"PRIu64", pe_size: %"PRIu64"\n", + lv->name, lv->size, lv->segments, seg->type, + seg->device, seg->pe_start, seg->pe_size); + } + + lvm_free_vg(&vg); + return 0; +} +#endif diff --git a/extracters/vhd-util/vhd-util b/extracters/vhd-util/vhd-util new file mode 100755 index 0000000..cae7412 Binary files /dev/null and b/extracters/vhd-util/vhd-util differ diff --git a/extracters/vhd-util/vhd-util-check.c b/extracters/vhd-util/vhd-util-check.c new file mode 100644 index 0000000..e424201 --- /dev/null +++ b/extracters/vhd-util/vhd-util-check.c @@ -0,0 +1,1024 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#define O_BINARY 0 +#endif +#include +#include + +#include "libvhd.h" +#include "vhd-util.h" + +/* allow the VHD timestamp to be at most this many seconds into the future to + * account for time skew with NFS servers + */ +#define TIMESTAMP_MAX_SLACK 1800 + +static int +vhd_util_check_zeros(void *buf, size_t size) +{ + size_t i; + char *p; + + p = buf; + for (i = 0; i < size; i++) + if (p[i]) + return i; + + return 0; +} + +static int +vhd_util_check_footer_opened(vhd_footer_t *footer) +{ + int i, n; + uint32_t *buf; + + buf = (uint32_t *)footer; + n = sizeof(*footer) / sizeof(uint32_t); + + for (i = 0; i < n; i++) + if (buf[i] != 0xc7c7c7c7) + return 0; + + return 1; +} + +static char * +vhd_util_check_validate_footer(vhd_footer_t *footer) +{ + int size; + uint32_t checksum, now; + + size = sizeof(footer->cookie); + if (memcmp(footer->cookie, HD_COOKIE, size)) + return "invalid cookie"; + + checksum = vhd_checksum_footer(footer); + if (checksum != footer->checksum) { + if (footer->hidden && + !strncmp(footer->crtr_app, "tap", 3) && + (footer->crtr_ver == VHD_VERSION(0, 1) || + footer->crtr_ver == VHD_VERSION(1, 1))) { + char tmp = footer->hidden; + footer->hidden = 0; + checksum = vhd_checksum_footer(footer); + footer->hidden = tmp; + + if (checksum == footer->checksum) + goto ok; + } + + return "invalid checksum"; + } + +ok: + if (!(footer->features & HD_RESERVED)) + return "invalid 'reserved' feature"; + + if (footer->features & ~(HD_TEMPORARY | HD_RESERVED)) + return "invalid extra features"; + + if (footer->ff_version != HD_FF_VERSION) + return "invalid file format version"; + + if (footer->type != HD_TYPE_DYNAMIC && + footer->type != HD_TYPE_DIFF && + footer->data_offset != ~(0ULL)) + return "invalid data offset"; + + now = vhd_time(time(NULL)); + if (footer->timestamp > now + TIMESTAMP_MAX_SLACK) + return "creation time in future"; + + if (!strncmp(footer->crtr_app, "tap", 3) && + footer->crtr_ver > VHD_CURRENT_VERSION) + return "unsupported tap creator version"; + + if (vhd_chs(footer->curr_size) < footer->geometry) + return "geometry too large"; + + if (footer->type != HD_TYPE_FIXED && + footer->type != HD_TYPE_DYNAMIC && + footer->type != HD_TYPE_DIFF) + return "invalid type"; + + if (footer->saved && footer->saved != 1) + return "invalid 'saved' state"; + + if (footer->hidden && footer->hidden != 1) + return "invalid 'hidden' state"; + + if (vhd_util_check_zeros(footer->reserved, + sizeof(footer->reserved))) + return "invalid 'reserved' bits"; + + return NULL; +} + +static char * +vhd_util_check_validate_header(int fd, vhd_header_t *header) +{ + off_t eof; + int i, cnt, size; + uint32_t checksum; + + size = sizeof(header->cookie); + if (memcmp(header->cookie, DD_COOKIE, size)) + return "invalid cookie"; + + checksum = vhd_checksum_header(header); + if (checksum != header->checksum) + return "invalid checksum"; + + if (header->hdr_ver != 0x00010000) + return "invalid header version"; + + if (header->data_offset != ~(0ULL)) + return "invalid data offset"; + + eof = lseek(fd, 0, SEEK_END); + if (eof == (off_t)-1) + return "error finding eof"; + + if (header->table_offset <= 0 || + header->table_offset % 512 || + (header->table_offset + + (header->max_bat_size * sizeof(uint32_t)) > + eof - sizeof(vhd_footer_t))) + return "invalid table offset"; + + for (cnt = 0, i = 0; i < sizeof(header->block_size) * 8; i++) + if ((header->block_size >> i) & 1) + cnt++; + + if (cnt != 1) + return "invalid block size"; + + if (header->res1) + return "invalid reserved bits"; + + if (vhd_util_check_zeros(header->res2, sizeof(header->res2))) + return "invalid reserved bits"; + + return NULL; +} + +static char * +vhd_util_check_validate_differencing_header(vhd_context_t *vhd) +{ + vhd_header_t *header; + + header = &vhd->header; + + if (vhd->footer.type == HD_TYPE_DIFF) { + char *parent; + uint32_t now; + + now = vhd_time(time(NULL)); + if (header->prt_ts > now + TIMESTAMP_MAX_SLACK) + return "parent creation time in future"; + + if (vhd_header_decode_parent(vhd, header, &parent)) + return "invalid parent name"; + + free(parent); + } else { + if (vhd_util_check_zeros(header->prt_name, + sizeof(header->prt_name))) + return "invalid non-null parent name"; + + if (vhd_util_check_zeros(header->loc, sizeof(header->loc))) + return "invalid non-null parent locators"; + + if (!blk_uuid_is_nil(&header->prt_uuid)) + return "invalid non-null parent uuid"; + + if (header->prt_ts) + return "invalid non-zero parent timestamp"; + } + + return NULL; +} + +static char * +vhd_util_check_validate_batmap(vhd_context_t *vhd, vhd_batmap_t *batmap) +{ + int size; + off_t eof; + uint32_t checksum; + + size = sizeof(batmap->header.cookie); + if (memcmp(batmap->header.cookie, VHD_BATMAP_COOKIE, size)) + return "invalid cookie"; + + if (batmap->header.batmap_version > VHD_BATMAP_CURRENT_VERSION) + return "unsupported batmap version"; + + checksum = vhd_checksum_batmap(batmap); + if (checksum != batmap->header.checksum) + return "invalid checksum"; + + if (!batmap->header.batmap_size) + return "invalid size zero"; + + eof = lseek(vhd->fd, 0, SEEK_END); + if (eof == (off_t)-1) + return "error finding eof"; + + if (!batmap->header.batmap_offset || + batmap->header.batmap_offset % 512) + return "invalid batmap offset"; + + if ((batmap->header.batmap_offset + + vhd_sectors_to_bytes(batmap->header.batmap_size)) > + eof - sizeof(vhd_footer_t)) + return "invalid batmap size"; + + return NULL; +} + +static char * +vhd_util_check_validate_parent_locator(vhd_context_t *vhd, + vhd_parent_locator_t *loc) +{ + off_t eof; + + if (vhd_validate_platform_code(loc->code)) + return "invalid platform code"; + + if (loc->code == PLAT_CODE_NONE) { + if (vhd_util_check_zeros(loc, sizeof(*loc))) + return "non-zero locator"; + + return NULL; + } + + if (!loc->data_offset) + return "invalid data offset"; + + if (!loc->data_space) + return "invalid data space"; + + if (!loc->data_len) + return "invalid data length"; + + eof = lseek(vhd->fd, 0, SEEK_END); + if (eof == (off_t)-1) + return "error finding eof"; + + if (loc->data_offset + vhd_parent_locator_size(loc) > + eof - sizeof(vhd_footer_t)) + return "invalid size"; + + if (loc->res) + return "invalid reserved bits"; + + return NULL; +} + +static const char * +vhd_util_check_validate_parent(vhd_context_t *vhd, const char *ppath) +{ + const char *msg; + vhd_context_t parent; + + msg = NULL; + + if (vhd_parent_raw(vhd)) + return msg; + + if (vhd_open(&parent, ppath, + VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED)) + return "error opening parent"; + + if (blk_uuid_compare(&vhd->header.prt_uuid, &parent.footer.uuid)) { + msg = "invalid parent uuid"; + goto out; + } + +out: + vhd_close(&parent); + return msg; +} + +static int +vhd_util_check_footer(int fd, vhd_footer_t *footer, int ignore) +{ + size_t size; + int err, opened; + char *msg, *buf; + off_t eof, off; + vhd_footer_t primary, backup; + + memset(&primary, 0, sizeof(primary)); + memset(&backup, 0, sizeof(backup)); + +#ifdef _WIN32 + buf = _aligned_malloc( sizeof( primary ), VHD_SECTOR_SIZE ); + if( buf == NULL ) { + printf( "error allocating buffer: %d\n", -errno ); + return -errno; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(primary)); + if (err) { + printf("error allocating buffer: %d\n", err); + return -err; + } +#endif + + memset(buf, 0, sizeof(primary)); + + eof = lseek(fd, 0, SEEK_END); + if (eof == (off_t)-1) { + err = -errno; + printf("error calculating end of file: %d\n", err); + goto out; + } + + size = ((eof % 512) ? 511 : 512); + eof = lseek(fd, eof - size, SEEK_SET); + if (eof == (off_t)-1) { + err = -errno; + printf("error calculating end of file: %d\n", err); + goto out; + } + + err = read(fd, buf, 512); + if (err != size) { + err = (errno ? -errno : -EIO); + printf("error reading primary footer: %d\n", err); + goto out; + } + + memcpy(&primary, buf, sizeof(primary)); + opened = vhd_util_check_footer_opened(&primary); + vhd_footer_in(&primary); + + msg = vhd_util_check_validate_footer(&primary); + if (msg) { + if (opened && ignore) + goto check_backup; + + err = -EINVAL; + printf("primary footer invalid: %s\n", msg); + goto out; + } + + if (primary.type == HD_TYPE_FIXED) { + err = 0; + goto out; + } + +check_backup: + off = lseek(fd, 0, SEEK_SET); + if (off == (off_t)-1) { + err = -errno; + printf("error seeking to backup footer: %d\n", err); + goto out; + } + + size = 512; + memset(buf, 0, sizeof(primary)); + + err = read(fd, buf, size); + if (err != size) { + err = (errno ? -errno : -EIO); + printf("error reading backup footer: %d\n", err); + goto out; + } + + memcpy(&backup, buf, sizeof(backup)); + vhd_footer_in(&backup); + + msg = vhd_util_check_validate_footer(&backup); + if (msg) { + err = -EINVAL; + printf("backup footer invalid: %s\n", msg); + goto out; + } + + if (memcmp(&primary, &backup, sizeof(primary))) { + if (opened && ignore) { + memcpy(&primary, &backup, sizeof(primary)); + goto ok; + } + + if (backup.hidden && + !strncmp(backup.crtr_app, "tap", 3) && + (backup.crtr_ver == VHD_VERSION(0, 1) || + backup.crtr_ver == VHD_VERSION(1, 1))) { + char cmp, tmp = backup.hidden; + backup.hidden = 0; + cmp = memcmp(&primary, &backup, sizeof(primary)); + backup.hidden = tmp; + if (!cmp) + goto ok; + } + + err = -EINVAL; + printf("primary and backup footers do not match\n"); + goto out; + } + +ok: + err = 0; + memcpy(footer, &primary, sizeof(primary)); + +out: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + return err; +} + +static int +vhd_util_check_header(int fd, vhd_footer_t *footer) +{ + int err; + off_t off; + char *msg, *buf; + vhd_header_t header; + +#ifdef _WIN32 + buf = _aligned_malloc( sizeof( header ), VHD_SECTOR_SIZE ); + if( buf == NULL ) { + printf( "error allocating header: %d\n", -errno ); + return -errno; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, sizeof(header)); + if (err) { + printf("error allocating header: %d\n", err); + return err; + } +#endif + + off = (off_t)footer->data_offset; + off = lseek(fd, off, SEEK_SET); + if (off == (off_t)-1) { + err = -errno; + printf("error seeking to header: %d\n", err); + goto out; + } + + err = read(fd, buf, sizeof(header)); + if (err != sizeof(header)) { + err = (errno ? -errno : -EIO); + printf("error reading header: %d\n", err); + goto out; + } + + memcpy(&header, buf, sizeof(header)); + vhd_header_in(&header); + + msg = vhd_util_check_validate_header(fd, &header); + if (msg) { + err = -EINVAL; + printf("header is invalid: %s\n", msg); + goto out; + } + + err = 0; + +out: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + return err; +} + +static int +vhd_util_check_differencing_header(vhd_context_t *vhd) +{ + char *msg; + + msg = vhd_util_check_validate_differencing_header(vhd); + if (msg) { + printf("differencing header is invalid: %s\n", msg); + return -EINVAL; + } + + return 0; +} + +static int +vhd_util_check_bat(vhd_context_t *vhd) +{ + off_t eof, eoh; + size_t i, j, block_size; + int err; + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) { + printf("error calculating eof: %d\n", err); + return err; + } + + eof = vhd_position(vhd); + if (eof == (off_t)-1) { + printf("error calculating eof: %d\n", -errno); + return -errno; + } + + /* adjust eof for vhds with short footers */ + if (eof % 512) { + if (eof % 512 != 511) { + printf("invalid file size: 0x%"PRIx64"\n", (uintmax_t)eof); + return -EINVAL; + } + + eof++; + } + + err = vhd_get_bat(vhd); + if (err) { + printf("error reading bat: %d\n", err); + return err; + } + + err = vhd_end_of_headers(vhd, &eoh); + if (err) { + printf("error calculating end of metadata: %d\n", err); + return err; + } + + eof -= sizeof(vhd_footer_t); + eof >>= VHD_SECTOR_SHIFT; + eoh >>= VHD_SECTOR_SHIFT; + block_size = vhd->spb + vhd->bm_secs; + + for (i = 0; i < vhd->header.max_bat_size; i++) { + uint32_t off = vhd->bat.bat[i]; + if (off == DD_BLK_UNUSED) + continue; + + if ((off_t)off < eoh) { + printf("block %d (offset 0x%x) clobbers headers\n", + i, off); + return -EINVAL; + } + + if ((off_t)(off + block_size) > eof) { + printf("block %d (offset 0x%x) clobbers footer\n", + i, off); + return -EINVAL; + } + + for (j = 0; j < vhd->header.max_bat_size; j++) { + uint32_t joff = vhd->bat.bat[j]; + + if (i == j) + continue; + + if (joff == DD_BLK_UNUSED) + continue; + + if (off == joff) + err = -EINVAL; + + if (off > joff && off < joff + block_size) + err = -EINVAL; + + if (off + block_size > joff && + off + block_size < joff + block_size) + err = -EINVAL; + + if (err) { + printf("block %d (offset 0x%x) clobbers " + "block %d (offset 0x%x)\n", + i, off, j, joff); + return err; + } + } + } + + return 0; +} + +static int +vhd_util_check_batmap(vhd_context_t *vhd) +{ + char *msg; + size_t i; + int err; + + err = vhd_get_bat(vhd); + if (err) { + printf("error reading bat: %d\n", err); + return err; + } + + err = vhd_get_batmap(vhd); + if (err) { + printf("error reading batmap: %d\n", err); + return err; + } + + msg = vhd_util_check_validate_batmap(vhd, &vhd->batmap); + if (msg) { + printf("batmap is invalid: %s\n", msg); + return -EINVAL; + } + + for (i = 0; i < vhd->header.max_bat_size; i++) { + if (!vhd_batmap_test(vhd, &vhd->batmap, i)) + continue; + + if (vhd->bat.bat[i] == DD_BLK_UNUSED) { + printf("batmap shows unallocated block %d full\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int +vhd_util_check_parent_locators(vhd_context_t *vhd) +{ + int i, n, err; + vhd_parent_locator_t *loc; + char *file, *ppath, *location, *pname; + const char *msg; + int mac, macx, w2ku, w2ru, wi2r, wi2k, found; + + mac = 0; + macx = 0; + w2ku = 0; + w2ru = 0; + wi2r = 0; + wi2k = 0; + found = 0; + pname = NULL; + ppath = NULL; + location = NULL; + + err = vhd_header_decode_parent(vhd, &vhd->header, &pname); + if (err) { + printf("error decoding parent name: %d\n", err); + return err; + } + + n = sizeof(vhd->header.loc) / sizeof(vhd->header.loc[0]); + for (i = 0; i < n; i++) { + ppath = NULL; + location = NULL; + loc = vhd->header.loc + i; + + msg = vhd_util_check_validate_parent_locator(vhd, loc); + if (msg) { + err = -EINVAL; + printf("invalid parent locator %d: %s\n", i, msg); + goto out; + } + + if (loc->code == PLAT_CODE_NONE) + continue; + + switch (loc->code) { + case PLAT_CODE_MACX: + if (macx++) + goto dup; + break; + + case PLAT_CODE_MAC: + if (mac++) + goto dup; + break; + + case PLAT_CODE_W2KU: + if (w2ku++) + goto dup; + break; + + case PLAT_CODE_W2RU: + if (w2ru++) + goto dup; + break; + + case PLAT_CODE_WI2R: + if (wi2r++) + goto dup; + break; + + case PLAT_CODE_WI2K: + if (wi2k++) + goto dup; + break; + + default: + err = -EINVAL; + printf("invalid platform code for locator %d\n", i); + goto out; + } + + if (loc->code != PLAT_CODE_MACX && + loc->code != PLAT_CODE_W2RU && + loc->code != PLAT_CODE_W2KU) + continue; + + err = vhd_parent_locator_read(vhd, loc, &ppath); + if (err) { + printf("error reading parent locator %d: %d\n", i, err); + goto out; + } + + file = basename(ppath); + if (strcmp(pname, file)) { + err = -EINVAL; + printf("parent locator %d name (%s) does not match " + "header name (%s)\n", i, file, pname); + goto out; + } + + err = vhd_find_parent(vhd, ppath, &location); + if (err) { + printf("error resolving %s: %d\n", ppath, err); + goto out; + } + +#ifdef _WIN32 + if( (err = _open( location, _O_RDONLY | _O_BINARY )) != -1 ) { + _close( err ); + err = 0; + } else + err = ENOENT; +#else + err = access(location, R_OK); +#endif + if (err && loc->code == PLAT_CODE_MACX) { + err = -errno; + printf("parent locator %d points to missing file %s " + "(resolved to %s)\n", i, ppath, location); + goto out; + } + + msg = vhd_util_check_validate_parent(vhd, location); + if (msg) { + err = -EINVAL; + printf("invalid parent %s: %s\n", location, msg); + goto out; + } + + found++; + free(ppath); + free(location); + ppath = NULL; + location = NULL; + + continue; + + dup: + printf("duplicate platform code in locator %d: 0x%x\n", + i, loc->code); + err = -EINVAL; + goto out; + } + + if (!found) { + err = -EINVAL; + printf("could not find parent %s\n", pname); + goto out; + } + + err = 0; + +out: + free(pname); + free(ppath); + free(location); + return err; +} + +static void +vhd_util_dump_headers(const char *name) +{ + char *argv[] = { "read", "-p", "-n", (char *)name }; + int argc = sizeof(argv) / sizeof(argv[0]); + + printf("%s appears invalid; dumping metadata\n", name); + vhd_util_read(argc, argv); +} + +static int +vhd_util_check_vhd(const char *name, int ignore) +{ + int fd, err; + vhd_context_t vhd; + struct stat stats; + vhd_footer_t footer; + + fd = -1; + memset(&vhd, 0, sizeof(vhd)); + memset(&footer, 0, sizeof(footer)); + + err = stat(name, &stats); + if (err == -1) { + printf("cannot stat %s: %d\n", name, errno); + return -errno; + } + +#ifdef _WIN32 + /* Look at filename for initial \\ to replace _S_IFBLK?? */ + if( !(stats.st_mode & _S_IFREG) || (stats.st_mode & _S_IFDIR) ) { + printf( "%s is not a regular file or block device\n", name ); + return -EINVAL; + } +#else + if (!S_ISREG(stats.st_mode) && !S_ISBLK(stats.st_mode)) { + printf("%s is not a regular file or block device\n", name); + return -EINVAL; + } +#endif + + fd = open(name, O_RDONLY | O_DIRECT | O_LARGEFILE | O_BINARY ); + if (fd == -1) { + printf("error opening %s\n", name); + return -errno; + } + + err = vhd_util_check_footer(fd, &footer, ignore); + if (err) + goto out; + + if (footer.type != HD_TYPE_DYNAMIC && footer.type != HD_TYPE_DIFF) + goto out; + + err = vhd_util_check_header(fd, &footer); + if (err) + goto out; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) + goto out; + + err = vhd_util_check_differencing_header(&vhd); + if (err) + goto out; + + err = vhd_util_check_bat(&vhd); + if (err) + goto out; + + if (vhd_has_batmap(&vhd)) { + err = vhd_util_check_batmap(&vhd); + if (err) + goto out; + } + + if (vhd.footer.type == HD_TYPE_DIFF) { + err = vhd_util_check_parent_locators(&vhd); + if (err) + goto out; + } + + err = 0; + printf("%s is valid\n", name); + +out: + if (err) + vhd_util_dump_headers(name); + if (fd != -1) + close(fd); + vhd_close(&vhd); + return err; +} + +static int +vhd_util_check_parents(const char *name, int ignore) +{ + int err; + vhd_context_t vhd; + char *cur, *parent; + + cur = (char *)name; + + for (;;) { + err = vhd_open(&vhd, cur, + VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) + goto out; + + if (vhd.footer.type != HD_TYPE_DIFF || vhd_parent_raw(&vhd)) { + vhd_close(&vhd); + goto out; + } + + err = vhd_parent_locator_get(&vhd, &parent); + vhd_close(&vhd); + + if (err) { + printf("error getting parent: %d\n", err); + goto out; + } + + if (cur != name) + free(cur); + cur = parent; + + err = vhd_util_check_vhd(cur, ignore); + if (err) + goto out; + } + +out: + if (err) + printf("error checking parents: %d\n", err); + if (cur != name) + free(cur); + return err; +} + +int +vhd_util_check(int argc, char **argv) +{ + char *name; + int c, err, ignore, parents; + + if (!argc || !argv) { + err = -EINVAL; + goto usage; + } + + ignore = 0; + parents = 0; + name = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "n:iph")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'i': + ignore = 1; + break; + case 'p': + parents = 1; + break; + case 'h': + err = 0; + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!name || optind != argc) { + err = -EINVAL; + goto usage; + } + + err = vhd_util_check_vhd(name, ignore); + if (err) + goto out; + + if (parents) + err = vhd_util_check_parents(name, ignore); + +out: + return err; + +usage: + printf("options: -n [-i ignore missing primary footers] " + "[-p check parents] [-h help]\n"); + return err; +} diff --git a/extracters/vhd-util/vhd-util-coalesce.c b/extracters/vhd-util/vhd-util-coalesce.c new file mode 100644 index 0000000..dad6e03 --- /dev/null +++ b/extracters/vhd-util/vhd-util-coalesce.c @@ -0,0 +1,232 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#define O_BINARY 0 +#endif + +#include "libvhd.h" + +static int +__raw_io_write(int fd, char* buf, uint64_t sec, uint32_t secs) +{ + off_t off; + size_t ret; + + errno = 0; + off = lseek(fd, (off_t)vhd_sectors_to_bytes(sec), SEEK_SET); + if (off == (off_t)-1) { + printf("raw parent: seek(0x%08"PRIx64") failed: %d\n", + vhd_sectors_to_bytes(sec), -errno); + return -errno; + } + + ret = write(fd, buf, (size_t)vhd_sectors_to_bytes(secs)); + if (ret == vhd_sectors_to_bytes(secs)) + return 0; + + printf("raw parent: write of 0x%"PRIx64" returned %zd, errno: %d\n", + vhd_sectors_to_bytes(secs), ret, -errno); + return (errno ? -errno : -EIO); +} + +/* + * Use 'parent' if the parent is VHD, and 'parent_fd' if the parent is raw + */ +static int +vhd_util_coalesce_block(vhd_context_t *vhd, vhd_context_t *parent, + int parent_fd, uint64_t block) +{ + size_t i; + int err; + char *buf, *map; + uint64_t sec, secs; + + buf = NULL; + map = NULL; + sec = block * vhd->spb; + + if (vhd->bat.bat[block] == DD_BLK_UNUSED) + return 0; + +#ifdef _WIN32 + buf = _aligned_malloc( vhd->header.block_size, 4096 ); + if( buf == NULL ) + return -errno; +#else + err = posix_memalign( (void **)&buf, 4096, vhd->header.block_size ); + if( err ) + return -err; +#endif + + err = vhd_io_read(vhd, buf, sec, vhd->spb); + if (err) + goto done; + + if (vhd_has_batmap(vhd) && vhd_batmap_test(vhd, &vhd->batmap, (uint32_t)block)) { + if (parent->file) + err = vhd_io_write(parent, buf, sec, vhd->spb); + else + err = __raw_io_write(parent_fd, buf, sec, vhd->spb); + goto done; + } + + err = vhd_read_bitmap(vhd, (uint32_t)block, &map); + if (err) + goto done; + + for (i = 0; i < vhd->spb; i++) { + if (!vhd_bitmap_test(vhd, map, i)) + continue; + + for (secs = 0; i + secs < vhd->spb; secs++) + if (!vhd_bitmap_test(vhd, map, i + (uint32_t)secs)) + break; + + if (parent->file) + err = vhd_io_write(parent, + buf + vhd_sectors_to_bytes(i), + sec + i, (uint32_t)secs); + else + err = __raw_io_write(parent_fd, + buf + vhd_sectors_to_bytes(i), + sec + i, (uint32_t)secs); + if (err) + goto done; + + i += (uint32_t)secs; + } + + err = 0; + +done: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + free(map); + return err; +} + +int +vhd_util_coalesce(int argc, char **argv) +{ + int err, c; + uint64_t i; + char *name, *pname; + vhd_context_t vhd, parent; + int parent_fd = -1; + + name = NULL; + pname = NULL; + parent.file = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + err = vhd_parent_locator_get(&vhd, &pname); + if (err) { + printf("error finding %s parent: %d\n", name, err); + vhd_close(&vhd); + return err; + } + + if (vhd_parent_raw(&vhd)) { + parent_fd = open(pname, O_RDWR | O_DIRECT | O_LARGEFILE | O_BINARY, 0644); + if (parent_fd == -1) { + err = -errno; + printf("failed to open parent %s: %d\n", pname, err); + vhd_close(&vhd); + return err; + } + } else { + err = vhd_open(&parent, pname, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", pname, err); + free(pname); + vhd_close(&vhd); + return err; + } + } + + err = vhd_get_bat(&vhd); + if (err) + goto done; + + if (vhd_has_batmap(&vhd)) { + err = vhd_get_batmap(&vhd); + if (err) + goto done; + } + + for (i = 0; i < vhd.bat.entries; i++) { + err = vhd_util_coalesce_block(&vhd, &parent, parent_fd, i); + if (err) + goto done; + } + + err = 0; + + done: + free(pname); + vhd_close(&vhd); + if (parent.file) + vhd_close(&parent); + else + close(parent_fd); + return err; + +usage: + printf("options: <-n name> [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-create.c b/extracters/vhd-util/vhd-util-create.c new file mode 100644 index 0000000..9e3a991 --- /dev/null +++ b/extracters/vhd-util/vhd-util-create.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +int +vhd_util_create(int argc, char **argv) +{ + char *name; + uint64_t size; + int c, sparse, err; + vhd_flag_creat_t flags; + + err = -EINVAL; + size = 0; + sparse = 1; + name = NULL; + flags = 0; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:s:rh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 's': + err = 0; + size = strtoull(optarg, NULL, 10); + break; + case 'r': + sparse = 0; + break; + case 'h': + default: + goto usage; + } + } + + if (err || !name || optind != argc) + goto usage; + + return vhd_create(name, size << 20, + (sparse ? HD_TYPE_DYNAMIC : HD_TYPE_FIXED), + flags); + +usage: + printf("options: <-n name> <-s size (MB)> [-r reserve] [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-fill.c b/extracters/vhd-util/vhd-util-fill.c new file mode 100644 index 0000000..79f6228 --- /dev/null +++ b/extracters/vhd-util/vhd-util-fill.c @@ -0,0 +1,119 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +int +vhd_util_fill( int argc, char **argv ) +{ + int err, c; + char *buf, *name; + vhd_context_t vhd; + uint64_t i, sec, secs; + + buf = NULL; + name = NULL; + + if( !argc || !argv ) + goto usage; + + optind = 0; + while( (c = getopt( argc, argv, "n:h" )) != -1 ) { + switch( c ) { + case 'n': + name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if( !name || optind != argc ) + goto usage; + + err = vhd_open( &vhd, name, VHD_OPEN_RDWR ); + if( err ) { + printf( "error opening %s: %d\n", name, err ); + return err; + } + + err = vhd_get_bat( &vhd ); + if( err ) + goto done; + +#ifdef _WIN32 + buf = _aligned_malloc( vhd.header.block_size, 4096 ); + if( buf == NULL ) { + err = -errno; + goto done; + } +#else + err = posix_memalign((void **)&buf, 4096, vhd.header.block_size); + if (err) { + err = -err; + goto done; + } +#endif + + sec = 0; + secs = vhd.header.block_size >> VHD_SECTOR_SHIFT; + + for (i = 0; i < vhd.header.max_bat_size; i++) { + err = vhd_io_read(&vhd, buf, sec, (uint32_t)secs); + if (err) + goto done; + + err = vhd_io_write(&vhd, buf, sec, (uint32_t)secs); + if (err) + goto done; + + sec += secs; + } + + err = 0; + + done: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-modify.c b/extracters/vhd-util/vhd-util-modify.c new file mode 100644 index 0000000..a73c75d --- /dev/null +++ b/extracters/vhd-util/vhd-util-modify.c @@ -0,0 +1,144 @@ +/* Copyright (c) 2008, XenSource 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. + * + * Altering operations: + * + * 1. Change the parent pointer to another file. + * 2. Change the size of the file containing the VHD image. This does NOT + * affect the VHD disk capacity, only the physical size of the file containing + * the VHD. Naturally, it is not possible to set the file size to be less than + * the what VHD utilizes. + * The operation doesn't actually change the file size, but it writes the + * footer in the right location such that resizing the file (manually, as a + * separate step) will produce the correct results. If the new file size is + * greater than the current file size, the file must first be expanded and then + * altered with this operation. If the new size is smaller than the current + * size, the VHD must first be altered with this operation and then the file + * must be shrunk. Failing to resize the file will result in a corrupted VHD. + */ + +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +TEST_FAIL_EXTERN_VARS; + +int +vhd_util_modify(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + int err, c, size, parent, parent_flags; + off_t newsize = 0; + char *newparent = NULL; + + name = NULL; + size = 0; + parent = 0; + parent_flags = 0; + + optind = 0; + while ((c = getopt(argc, argv, "n:s:p:P:mh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 's': + size = 1; + errno = 0; + newsize = (off_t)strtoll(optarg, NULL, 10); + if (errno) { + fprintf(stderr, "Invalid size '%s'\n", optarg); + goto usage; + } + break; + case 'p': + if( parent && strcmp( newparent, optarg ) ) { + fprintf(stderr, "Parent name doesn't match\n"); + goto usage; + } + ++parent; + newparent = optarg; + break; + case 'P': + parent = 2; + parent_flags |= 2; + newparent = optarg; + break; + case 'm': + parent_flags |= 1; + break; + + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc || (parent && parent <2)) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + if (size) { + err = vhd_set_phys_size(&vhd, newsize); + if (err) + printf("failed to set physical size to %"PRIu64":" + " %d\n", (uintmax_t)newsize, err); + } + + if (parent) { + TEST_FAIL_AT(FAIL_REPARENT_BEGIN); + err = vhd_change_parent(&vhd, newparent, parent_flags); + if (err) { + printf("failed to set parent to '%s': %d\n", + newparent, err); + goto done; + } + TEST_FAIL_AT(FAIL_REPARENT_END); + } + +done: + vhd_close(&vhd); + return err; + +usage: + printf("*** Dangerous operations, use with care ***\n"); + printf("options: <-n name> [-p NEW_PARENT set parent [-m raw]] " + "[-P change PARENT with uuid check] " + "[-s NEW_SIZE set size] [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-query.c b/extracters/vhd-util/vhd-util-query.c new file mode 100644 index 0000000..d1312cf --- /dev/null +++ b/extracters/vhd-util/vhd-util-query.c @@ -0,0 +1,161 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +int +vhd_util_query(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + off_t currsize; + int ret, err, c, size, physize, parent, fields, depth; + + name = NULL; + size = 0; + physize = 0; + parent = 0; + fields = 0; + depth = 0; + + if (!argc || !argv) { + err = -EINVAL; + goto usage; + } + + optind = 0; + while ((c = getopt(argc, argv, "n:vspfdh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'v': + size = 1; + break; + case 's': + physize = 1; + break; + case 'p': + parent = 1; + break; + case 'f': + fields = 1; + break; + case 'd': + depth = 1; + break; + case 'h': + err = 0; + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!name || optind != argc) { + err = -EINVAL; + goto usage; + } + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + if (size) + printf("%"PRIu64"\n", vhd.footer.curr_size >> 20); + + if (physize) { + err = vhd_get_phys_size(&vhd, &currsize); + if (err) + printf("failed to get physical size: %d\n", err); + else + printf("%"PRIu64"\n", (uintmax_t)currsize); + } + + if (parent) { + ret = 0; + + if (vhd.footer.type != HD_TYPE_DIFF) + printf("%s has no parent\n", name); + else { + char *pname; + + ret = vhd_parent_locator_get(&vhd, &pname); + if (ret) + printf("query failed\n"); + else { + printf("%s\n", pname); + free(pname); + } + } + + err = (err ? err : ret); + } + + if (fields) { + int hidden; + + ret = vhd_hidden(&vhd, &hidden); + if (ret) + printf("error checking 'hidden' field: %d\n", ret); + else + printf("hidden: %d\n", hidden); + + err = (err ? err : ret); + } + + if (depth) { + int length; + + ret = vhd_chain_depth(&vhd, &length); + if (ret) + printf("error checking chain depth: %d\n", ret); + else + printf("chain depth: %d\n", length); + + err = (err ? err : ret); + } + + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> [-v print virtual size (in MB)] " + "[-s print physical utilization (bytes)] [-p print parent] " + "[-f print fields] [-d print chain depth] [-h help]\n"); + return err; +} diff --git a/extracters/vhd-util/vhd-util-read.c b/extracters/vhd-util/vhd-util-read.c new file mode 100644 index 0000000..b2638fd --- /dev/null +++ b/extracters/vhd-util/vhd-util-read.c @@ -0,0 +1,876 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#define _O_BINARY 0 +#endif +#include + +#include "libvhd.h" +#include "vhd-util.h" + +#define nsize 15 +static char nbuf[nsize]; + +static inline char * +__xconv(uint64_t num) +{ + snprintf(nbuf, nsize, "%#" PRIx64 , num); + return nbuf; +} + +static inline char * +__dconv(uint64_t num) +{ + snprintf(nbuf, nsize, "%" PRIu64, num); + return nbuf; +} + +#define conv(hex, num) \ + (hex ? __xconv((uint64_t)num) : __dconv((uint64_t)num)) + +static void +vhd_print_header(vhd_context_t *vhd, vhd_header_t *h, int hex) +{ + int err; + uint32_t cksm; + char uuid[39], time_str[26], cookie[9], *name; + + printf("VHD Header Summary:\n-------------------\n"); + + snprintf(cookie, sizeof(cookie), "%s", h->cookie); + printf("Cookie : %s\n", cookie); + + printf("Data offset (unusd) : %s\n", conv(hex, h->data_offset)); + printf("Table offset : %s\n", conv(hex, h->table_offset)); + printf("Header version : 0x%08x\n", h->hdr_ver); + printf("Max BAT size : %s\n", conv(hex, h->max_bat_size)); + printf("Block size : %s ", conv(hex, h->block_size)); + printf("(%s MB)\n", conv(hex, h->block_size >> 20)); + + err = vhd_header_decode_parent(vhd, h, &name); + printf("Parent name : %s\n", + (err ? "failed to read name" : name)); + free(name); + + blk_uuid_to_string(&h->prt_uuid, uuid, sizeof(uuid)); + printf("Parent UUID : %s\n", uuid); + + vhd_time_to_string(h->prt_ts, time_str); + printf("Parent timestamp : %s\n", time_str); + + cksm = vhd_checksum_header(h); + printf("Checksum : 0x%x|0x%x (%s)\n", h->checksum, cksm, + h->checksum == cksm ? "Good!" : "Bad!"); + printf("\n"); +} + +static void +vhd_print_footer(vhd_footer_t *f, int hex) +{ + uint64_t c, h, s; + uint32_t ff_maj, ff_min, cr_maj, cr_min, cksm; + char time_str[26], creator[5], uuid[39], cookie[9]; + + printf("VHD Footer Summary:\n-------------------\n"); + + snprintf(cookie, sizeof(cookie), "%s", f->cookie); + printf("Cookie : %s\n", cookie); + + printf("Features : (0x%08x) %s%s\n", f->features, + (f->features & HD_TEMPORARY) ? "" : "", + (f->features & HD_RESERVED) ? "" : ""); + + ff_maj = f->ff_version >> 16; + ff_min = f->ff_version & 0xffff; + printf("File format version : Major: %d, Minor: %d\n", + ff_maj, ff_min); + + printf("Data offset : %s\n", conv(hex, f->data_offset)); + + vhd_time_to_string(f->timestamp, time_str); + printf("Timestamp : %s\n", time_str); + + memcpy(creator, f->crtr_app, 4); + creator[4] = '\0'; + printf("Creator Application : '%s'\n", creator); + + cr_maj = f->crtr_ver >> 16; + cr_min = f->crtr_ver & 0xffff; + printf("Creator version : Major: %d, Minor: %d\n", + cr_maj, cr_min); + + printf("Creator OS : %s\n", + ((f->crtr_os == HD_CR_OS_WINDOWS) ? "Windows" : + ((f->crtr_os == HD_CR_OS_MACINTOSH) ? "Macintosh" : + ((f->crtr_os == HD_CR_OS_UNIX) ? "Unix" : + ((f->crtr_os == HD_CR_OS_VMS) ? "VMS" : + "Unknown!"))))); + + printf("Original disk size : %s MB ", conv(hex, f->orig_size >> 20)); + printf("(%s Bytes)\n", conv(hex, f->orig_size)); + + printf("Current disk size : %s MB ", conv(hex, f->curr_size >> 20)); + printf("(%s Bytes)\n", conv(hex, f->curr_size)); + + c = f->geometry >> 16; + h = (f->geometry & 0x0000FF00) >> 8; + s = f->geometry & 0x000000FF; + printf("Geometry : Cyl: %s, ", conv(hex, c)); + printf("Hds: %s, ", conv(hex, h)); + printf("Sctrs: %s\n", conv(hex, s)); + printf(" : = %s MB ", conv(hex, (c * h * s) >> 11)); + printf("(%s Bytes)\n", conv(hex, c * h * s << 9)); + + printf("Disk type : %s\n", + f->type <= HD_TYPE_MAX ? + HD_TYPE_STR[f->type] : "Unknown"); + + cksm = vhd_checksum_footer(f); + printf("Checksum : 0x%x|0x%x (%s)\n", f->checksum, cksm, + f->checksum == cksm ? "Good!" : "Bad!"); + + blk_uuid_to_string(&f->uuid, uuid, sizeof(uuid)); + printf("UUID : %s\n", uuid); + + printf("Saved state : %s\n", f->saved == 0 ? "No" : "Yes"); + printf("Hidden : %d\n", f->hidden); + printf("\n"); +} + +static inline char * +code_name(uint32_t code) +{ + switch(code) { + case PLAT_CODE_NONE: + return "PLAT_CODE_NONE"; + case PLAT_CODE_WI2R: + return "PLAT_CODE_WI2R"; + case PLAT_CODE_WI2K: + return "PLAT_CODE_WI2K"; + case PLAT_CODE_W2RU: + return "PLAT_CODE_W2RU"; + case PLAT_CODE_W2KU: + return "PLAT_CODE_W2KU"; + case PLAT_CODE_MAC: + return "PLAT_CODE_MAC"; + case PLAT_CODE_MACX: + return "PLAT_CODE_MACX"; + default: + return "UNKOWN"; + } +} + +static void +vhd_print_parent(vhd_context_t *vhd, vhd_parent_locator_t *loc) +{ + int err; + char *buf; + + err = vhd_parent_locator_read(vhd, loc, &buf); + if (err) { + printf("failed to read parent name\n"); + return; + } + + printf(" decoded name : %s\n", buf); + free( buf ); +} + +static void +vhd_print_parent_locators(vhd_context_t *vhd, int hex) +{ + int i, n; + vhd_parent_locator_t *loc; + + printf("VHD Parent Locators:\n--------------------\n"); + + n = sizeof(vhd->header.loc) / sizeof(struct prt_loc); + for (i = 0; i < n; i++) { + loc = &vhd->header.loc[i]; + + if (loc->code == PLAT_CODE_NONE) + continue; + + printf("locator: : %d\n", i); + printf(" code : %s\n", + code_name(loc->code)); + printf(" data_space : %s\n", + conv(hex, loc->data_space)); + printf(" data_length : %s\n", + conv(hex, loc->data_len)); + printf(" data_offset : %s\n", + conv(hex, loc->data_offset)); + vhd_print_parent(vhd, loc); + printf("\n"); + } +} + +static void +vhd_print_batmap_header(vhd_batmap_t *batmap, int hex) +{ + uint32_t cksm; + + printf("VHD Batmap Summary:\n-------------------\n"); + printf("Batmap offset : %s\n", + conv(hex, batmap->header.batmap_offset)); + printf("Batmap size (secs) : %s\n", + conv(hex, batmap->header.batmap_size)); + printf("Batmap version : 0x%08x\n", + batmap->header.batmap_version); + + cksm = vhd_checksum_batmap(batmap); + printf("Checksum : 0x%x|0x%x (%s)\n", + batmap->header.checksum, cksm, + (batmap->header.checksum == cksm ? "Good!" : "Bad!")); + printf("\n"); +} + +static inline int +check_block_range(vhd_context_t *vhd, uint64_t block, int hex) +{ + if (block > vhd->header.max_bat_size) { + fprintf(stderr, "block %s past end of file\n", + conv(hex, block)); + return -ERANGE; + } + + return 0; +} + +static int +vhd_print_headers(vhd_context_t *vhd, int hex) +{ + int err; + + vhd_print_footer(&vhd->footer, hex); + + if (vhd_type_dynamic(vhd)) { + vhd_print_header(vhd, &vhd->header, hex); + + if (vhd->footer.type == HD_TYPE_DIFF) + vhd_print_parent_locators(vhd, hex); + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) { + printf("failed to get batmap header\n"); + return err; + } + + vhd_print_batmap_header(&vhd->batmap, hex); + } + } + + return 0; +} + +static int +vhd_dump_headers(const char *name, int hex) +{ + vhd_context_t vhd; + + libvhd_set_log_level(1); + memset(&vhd, 0, sizeof(vhd)); + + printf("\n%s appears invalid; dumping headers\n\n", name); + + vhd.fd = open(name, O_DIRECT | O_LARGEFILE | O_RDONLY | _O_BINARY); + if (vhd.fd == -1) + return -errno; + + vhd.file = strdup(name); + + vhd_read_footer(&vhd, &vhd.footer); + vhd_read_header(&vhd, &vhd.header); + + vhd_print_footer(&vhd.footer, hex); + vhd_print_header(&vhd, &vhd.header, hex); + + close(vhd.fd); + free(vhd.file); + + return 0; +} + +static int +vhd_print_logical_to_physical(vhd_context_t *vhd, + uint64_t sector, int count, int hex) +{ + int i; + uint32_t blk, lsec; + uint64_t cur, offset; + + if (vhd_sectors_to_bytes(sector + count) > vhd->footer.curr_size) { + fprintf(stderr, "sector %s past end of file\n", + conv(hex, sector + count)); + return -ERANGE; + } + + for (i = 0; i < count; i++) { + cur = sector + i; + blk = (uint32_t)(cur / vhd->spb); + lsec = cur % vhd->spb; + offset = vhd->bat.bat[blk]; + + if (offset != DD_BLK_UNUSED) { + offset += lsec + 1; + offset = vhd_sectors_to_bytes(offset); + } + + printf("logical sector %s: ", conv(hex, cur)); + printf("block number: %s, ", conv(hex, blk)); + printf("sector offset: %s, ", conv(hex, lsec)); + printf("file offset: %s\n", (offset == DD_BLK_UNUSED ? + "not allocated" : conv(hex, offset))); + } + + return 0; +} + +static int +vhd_print_bat(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + int i; + uint64_t cur, offset; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + for (i = 0; i < count; i++) { + cur = block + i; + offset = vhd->bat.bat[cur]; + + printf("block: %s: ", conv(hex, cur)); + printf("offset: %s\n", + (offset == DD_BLK_UNUSED ? "not allocated" : + conv(hex, vhd_sectors_to_bytes(offset)))); + } + + return 0; +} + +static inline void +write_full(int fd, void* buf, size_t count) +{ + ssize_t num_written = 0; + if (!buf) return; + + + while(count > 0) { + + num_written = write(fd, buf, count); + if (num_written == -1) { + if (errno == EINTR) + continue; + else + return; + } + + count -= num_written; + buf = ((char *)buf) + num_written; + } +} + +static int +vhd_print_bitmap(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + char *buf; + int i, err; + uint64_t cur; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + for (i = 0; i < count; i++) { + cur = block + i; + + if (vhd->bat.bat[cur] == DD_BLK_UNUSED) { + printf("block %s not allocated\n", conv(hex, cur)); + continue; + } + + err = vhd_read_bitmap(vhd, (uint32_t)cur, &buf); + if (err) + goto out; + + write_full(STDOUT_FILENO, buf, + (size_t)vhd_sectors_to_bytes(vhd->bm_secs)); +#ifdef _WIN32 + _aligned_free(buf); +#else + free(buf); +#endif + } + + err = 0; +out: + return err; +} + +static int +vhd_test_bitmap(vhd_context_t *vhd, uint64_t sector, int count, int hex) +{ + char *buf; + uint64_t cur; + int i, err, bit; + uint32_t blk, bm_blk, sec; + + if (vhd_sectors_to_bytes(sector + count) > vhd->footer.curr_size) { + printf("sector %s past end of file\n", conv(hex, sector)); + return -ERANGE; + } + + bm_blk = -1; + buf = NULL; + + for (i = 0; i < count; i++) { + cur = sector + i; + blk = (uint32_t)(cur / vhd->spb); + sec = cur % vhd->spb; + + if (blk != bm_blk) { + bm_blk = blk; + free(buf); + buf = NULL; + + if (vhd->bat.bat[blk] != DD_BLK_UNUSED) { + err = vhd_read_bitmap(vhd, blk, &buf); + if (err) + goto out; + } + } + + if (vhd->bat.bat[blk] == DD_BLK_UNUSED) + bit = 0; + else + bit = vhd_bitmap_test(vhd, buf, blk); + + printf("block %s: ", conv(hex, blk)); + printf("sec: %s: %d\n", conv(hex, sec), bit); + } + + err = 0; + out: +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + return err; +} + +static int +vhd_print_batmap(vhd_context_t *vhd) +{ + int err; + size_t size; + + err = vhd_get_batmap(vhd); + if (err) { + printf("failed to read batmap: %d\n", err); + return err; + } + + size = (uint32_t)vhd_sectors_to_bytes(vhd->batmap.header.batmap_size); + write_full(STDOUT_FILENO, vhd->batmap.map, size); + + return 0; +} + +static int +vhd_test_batmap(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + int i, err; + uint64_t cur; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + err = vhd_get_batmap(vhd); + if (err) { + fprintf(stderr, "failed to get batmap\n"); + return err; + } + + for (i = 0; i < count; i++) { + cur = block + i; + fprintf(stderr, "batmap for block %s: %d\n", conv(hex, cur), + vhd_batmap_test(vhd, &vhd->batmap, (uint32_t)cur)); + } + + return 0; +} + +static int +vhd_print_data(vhd_context_t *vhd, uint64_t block, int count, int hex) +{ + char *buf; + int i, err; + uint64_t cur; + + err = 0; + + if (check_block_range(vhd, block + count, hex)) + return -ERANGE; + + for (i = 0; i < count; i++) { + cur = block + i; + + if (vhd->bat.bat[cur] == DD_BLK_UNUSED) { + printf("block %s not allocated\n", conv(hex, cur)); + continue; + } + + err = vhd_read_block(vhd, (uint32_t)cur, &buf); + if (err) + break; + + write_full(STDOUT_FILENO, buf, vhd->header.block_size); + free(buf); + } + + return err; +} + +static int +vhd_read_data(vhd_context_t *vhd, uint64_t sec, int count, int hex) +{ + char *buf; + uint64_t cur; + int err; + size_t max, secs; + + if (vhd_sectors_to_bytes(sec + count) > vhd->footer.curr_size) + return -ERANGE; + + max = MIN((size_t)vhd_sectors_to_bytes(count), VHD_BLOCK_SIZE); +#ifdef _WIN32 + buf = _aligned_malloc( max, VHD_SECTOR_SIZE ); + if( buf == NULL ) + return -errno; +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, max); + if (err) + return -err; +#endif + + cur = sec; + while (count) { + secs = MIN((max >> VHD_SECTOR_SHIFT), (size_t)count); + err = vhd_io_read(vhd, buf, cur, secs); + if (err) + break; + + write_full(STDOUT_FILENO, buf, (size_t)vhd_sectors_to_bytes(secs)); + + cur += secs; + count -= secs; + } + +#ifdef _WIN32 + _aligned_free( buf ); +#else + free( buf ); +#endif + return err; +} + +static void vhd_print_brief(vhd_context_t *cvhd, char *name) { + int err; + size_t i, n, level = 0; + vhd_parent_locator_t *loc; + vhd_context_t *vhd; + char uuid[39], time_str[26], *pname; + + if( !vhd_type_dynamic(cvhd) ) { + printf( "%s has no parent\n", + cvhd->footer.type <= HD_TYPE_MAX? + HD_TYPE_STR[cvhd->footer.type] : + "Unknown disk type" ); + return; + } + + name = strdup( name ); + if( name == NULL ) + return; + + vhd = malloc( sizeof( vhd_context_t ) ); + if( vhd == NULL ) + goto out; + + memcpy( vhd, cvhd, sizeof( vhd_context_t ) ); + + pname = NULL; + level = 0; + + while( 1 ) { + printf( "%s\n", name ); + free( name ); + name = NULL; + + printf(" Disk type : %s\n", + vhd->footer.type <= HD_TYPE_MAX ? + HD_TYPE_STR[vhd->footer.type] : "Unknown"); + + if( vhd->footer.type != HD_TYPE_DIFF ) { + blk_uuid_to_string(&vhd->footer.uuid, uuid, sizeof(uuid)); + printf(" UUID : %s\n", uuid); + vhd_time_to_string(vhd->footer.timestamp, time_str); + printf(" Timestamp : %s\n", time_str); + break; + } + err = vhd_header_decode_parent(vhd, &vhd->header, &name); + printf(" Parent name : %s\n", + (err ? "failed to read name" : name)); + free(name); + name = NULL; + + blk_uuid_to_string(&vhd->header.prt_uuid, uuid, sizeof(uuid)); + printf(" Parent UUID : %s\n", uuid); + + vhd_time_to_string(vhd->header.prt_ts, time_str); + printf(" Parent timestamp : %s\n", time_str); + + n = sizeof(vhd->header.loc) / sizeof(struct prt_loc); + for (i = 0; i < n; i++) { + loc = &vhd->header.loc[i]; + + if (loc->code == PLAT_CODE_NONE) + continue; + err = vhd_parent_locator_read( vhd, loc, &name ); + if( err ) { + printf( "Failed to read parent locator %u", i ); + break; + } + + printf(" Parent location %4d: %s\n", i, name ); + + if( pname == NULL ) { + err = vhd_find_parent( vhd, name, &pname ); + } + free( name ); + name = NULL; + } + if( pname == NULL ) + break; + printf( "\nParent is " ); + name = pname; + pname = NULL; + if( level != 0 ) + vhd_close(vhd); + + err = vhd_open(vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) { + printf("unable to open: %d\n", err); + break; + } + ++level; + } + + out: + if( level > 0 ) + vhd_close( vhd ); + free( vhd ); + free( pname ); + free( name ); + + return; +} + +int +vhd_util_read(int argc, char **argv) +{ + char *name; + vhd_context_t vhd; + int c, err, headers, brief, hex; + uint64_t bat, bitmap, tbitmap, batmap, tbatmap, data, lsec, count, read; + + err = 0; + hex = 0; + headers = 0; + brief = 0; + count = 1; + bat = -1; + bitmap = -1; + tbitmap = -1; + batmap = -1; + tbatmap = -1; + data = -1; + lsec = -1; + read = -1; + name = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:pPt:b:m:i:aj:d:c:r:xh")) != -1) { + switch(c) { + case 'n': + name = optarg; + break; + case 'p': + headers = 1; + break; + case 'P': + brief = 1; + break; + case 't': + lsec = strtoul(optarg, NULL, 10); + break; + case 'b': + bat = strtoull(optarg, NULL, 10); + break; + case 'm': + bitmap = strtoull(optarg, NULL, 10); + break; + case 'i': + tbitmap = strtoul(optarg, NULL, 10); + break; + case 'a': + batmap = 1; + break; + case 'j': + tbatmap = strtoull(optarg, NULL, 10); + break; + case 'd': + data = strtoull(optarg, NULL, 10); + break; + case 'r': + read = strtoull(optarg, NULL, 10); + break; + case 'c': + count = strtoul(optarg, NULL, 10); + break; + case 'x': + hex = 1; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED); + if (err) { + printf("Failed to open %s: %d\n", name, err); + vhd_dump_headers(name, hex); + return err; + } + + err = vhd_get_bat(&vhd); + if (err) { + printf("Failed to get bat for %s: %d\n", name, err); + goto out; + } + + if (headers) + vhd_print_headers(&vhd, hex); + + if (brief) + vhd_print_brief(&vhd, name); + + if (lsec != -1) { + err = vhd_print_logical_to_physical(&vhd, lsec, (int)count, hex); + if (err) + goto out; + } + + if (bat != -1) { + err = vhd_print_bat(&vhd, bat, (int)count, hex); + if (err) + goto out; + } + + if (bitmap != -1) { + err = vhd_print_bitmap(&vhd, bitmap, (int)count, hex); + if (err) + goto out; + } + + if (tbitmap != -1) { + err = vhd_test_bitmap(&vhd, tbitmap, (int)count, hex); + if (err) + goto out; + } + + if (batmap != -1) { + err = vhd_print_batmap(&vhd); + if (err) + goto out; + } + + if (tbatmap != -1) { + err = vhd_test_batmap(&vhd, tbatmap, (int)count, hex); + if (err) + goto out; + } + + if (data != -1) { + err = vhd_print_data(&vhd, data, (int)count, hex); + if (err) + goto out; + } + + if (read != -1) { + err = vhd_read_data(&vhd, read, (int)count, hex); + if (err) + goto out; + } + + err = 0; + + out: + vhd_close(&vhd); + return err; + + usage: + printf("options:\n" + "-h help\n" + "-n name\n" + "-p print VHD headers\n" + "-P print parent summary\n" + "-t sec translate logical sector to VHD location\n" + "-b blk print bat entry\n" + "-m blk print bitmap\n" + "-i sec test bitmap for logical sector\n" + "-a print batmap\n" + "-j blk test batmap for block\n" + "-d blk print data\n" + "-c num num units\n" + "-r sec read num sectors at sec\n" + "-x print in hex\n"); + return EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-repair.c b/extracters/vhd-util/vhd-util-repair.c new file mode 100644 index 0000000..b062030 --- /dev/null +++ b/extracters/vhd-util/vhd-util-repair.c @@ -0,0 +1,86 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +int +vhd_util_repair(int argc, char **argv) +{ + char *name; + int err, c; + off_t eof; + vhd_context_t vhd; + + name = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || optind != argc) + goto usage; + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + err = vhd_end_of_data(&vhd, &eof); + if (err) { + printf("error finding end of data: %d\n", err); + goto done; + } + + err = vhd_write_footer_at(&vhd, &vhd.footer, eof); + + done: + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-resize.c b/extracters/vhd-util/vhd-util-resize.c new file mode 100644 index 0000000..84562fa --- /dev/null +++ b/extracters/vhd-util/vhd-util-resize.c @@ -0,0 +1,1199 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#include +#include +#endif +#include +#include + +#include "libvhd-journal.h" + +#ifdef _WIN32 +#define EPRINTF(x) printf x +#else +#define EPRINTF(x) EPRINTFu x +#if 1 +#define DFPRINTF(_f, _a...) fprintf(stdout, _f, ##_a) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif + +#define EPRINTFu(_f, _a...) \ + do { \ + syslog(LOG_INFO, "%s: " _f, __func__, ##_a); \ + DFPRINTF(_f, _a); \ + } while (0) +#endif + +typedef struct vhd_block { + uint32_t block; + uint32_t offset; +} vhd_block_t; + +TEST_FAIL_EXTERN_VARS; + +static inline uint32_t +secs_to_blocks_down(vhd_context_t *vhd, uint64_t secs) +{ + return (uint32_t)(secs / vhd->spb); +} + +static uint32_t +secs_to_blocks_up(vhd_context_t *vhd, uint64_t secs) +{ + uint32_t blocks; + + blocks = (uint32_t)(secs / vhd->spb); + if (secs % vhd->spb) + blocks++; + + return blocks; +} + +static int +vhd_fixed_shrink(vhd_journal_t *journal, uint64_t secs) +{ + int err; + uint64_t new_eof; + vhd_context_t *vhd; + + vhd = &journal->vhd; + + new_eof = vhd->footer.curr_size - vhd_sectors_to_bytes(secs); + if (new_eof <= sizeof(vhd_footer_t)) + return -EINVAL; + + err = ftruncate(vhd->fd, new_eof); + if (err) + return errno; + + vhd->footer.curr_size = new_eof; + return vhd_write_footer(vhd, &vhd->footer); +} + +static int +vhd_write_zeros(vhd_journal_t *journal, off_t off, uint64_t size) +{ + int err; + char *buf; + vhd_context_t *vhd; + uint64_t bytes, map; + + vhd = &journal->vhd; + map = MIN(size, VHD_BLOCK_SIZE); + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + return err; + +#ifdef _WIN32 + buf = VirtualAlloc( NULL, (SIZE_T)map, MEM_COMMIT, PAGE_READONLY ); + if( buf == NULL ) + return -ENOMEM; +#else + buf = mmap( 0, map, PROT_READ, MAP_SHARED | MAP_ANON, -1, 0 ); + if( buf == MAP_FAILED ) + return -errno; +#endif + + do { + bytes = MIN(size, map); + + err = vhd_write(vhd, buf, (size_t)bytes); + if (err) + break; + + size -= bytes; + } while (size); + +#ifdef _WIN32 + VirtualFree( buf, 0, MEM_RELEASE ); +#else + munmap( buf, map ); +#endif + + return err; +} + +static int +vhd_fixed_grow(vhd_journal_t *journal, uint64_t secs) +{ + int err; + vhd_context_t *vhd; + uint64_t size, eof, new_eof; + + size = vhd_sectors_to_bytes(secs); + vhd = &journal->vhd; + + err = vhd_seek(vhd, 0, SEEK_END); + if (err) + goto out; + + eof = vhd_position(vhd); + if (eof == (off_t)-1) { + err = -errno; + goto out; + } + + err = vhd_write_zeros(journal, (off_t)(eof - sizeof(vhd_footer_t)), size); + if (err) + goto out; + + new_eof = eof + size; + err = vhd_seek(vhd, (off_t)new_eof, SEEK_SET); + if (err) + goto out; + + vhd->footer.curr_size += size; + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + goto out; + + err = 0; + +out: + return err; +} + +static int +vhd_fixed_resize(vhd_journal_t *journal, uint64_t size) +{ + int err; + vhd_context_t *vhd; + uint64_t cur_secs, new_secs; + + vhd = &journal->vhd; + cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT; + new_secs = size << (20 - VHD_SECTOR_SHIFT); + + if (cur_secs == new_secs) + return 0; + else if (cur_secs > new_secs) + err = vhd_fixed_shrink(journal, cur_secs - new_secs); + else + err = vhd_fixed_grow(journal, new_secs - cur_secs); + + return err; +} + +static inline void +swap(vhd_block_t *list, int a, int b) +{ + vhd_block_t tmp; + + tmp = list[a]; + list[a] = list[b]; + list[b] = tmp; +} + +static int +partition(vhd_block_t *list, int left, int right, int pidx) +{ + int i, sidx; + long long pval; + + sidx = left; + pval = list[pidx].offset; + swap(list, pidx, right); + + for (i = left; i < right; i++) + if (list[i].offset >= pval) { + swap(list, sidx, i); + ++sidx; + } + + swap(list, right, sidx); + return sidx; +} + +static void +quicksort(vhd_block_t *list, int left, int right) +{ + int pidx, new_pidx; + + if (right < left) + return; + + pidx = left; + new_pidx = partition(list, left, right, pidx); + quicksort(list, left, new_pidx - 1); + quicksort(list, new_pidx + 1, right); +} + +static int +vhd_move_block(vhd_journal_t *journal, uint32_t src, off_t offset) +{ + int err; + char *buf; + size_t size; + vhd_context_t *vhd; + off_t off, src_off; + + buf = NULL; + vhd = &journal->vhd; + off = offset; + size = (size_t)vhd_sectors_to_bytes(vhd->bm_secs); + src_off = vhd->bat.bat[src]; + + if (src_off == DD_BLK_UNUSED) + return -EINVAL; + src_off = (off_t)vhd_sectors_to_bytes(src_off); + + err = vhd_journal_add_block(journal, src, + VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA); + if (err) + goto out; + + err = vhd_read_bitmap(vhd, src, &buf); + if (err) + goto out; + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(vhd, buf, size); + if (err) + goto out; + + free(buf); + buf = NULL; + off += size; + size = (size_t)vhd_sectors_to_bytes(vhd->spb); + + err = vhd_read_block(vhd, src, &buf); + if (err) + goto out; + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(vhd, buf, size); + if (err) + goto out; + + vhd->bat.bat[src] = offset >> VHD_SECTOR_SHIFT; + + err = vhd_write_zeros(journal, src_off, + vhd_sectors_to_bytes(vhd->bm_secs + vhd->spb)); + +out: + free(buf); + return err; +} + +static int +vhd_clobber_block(vhd_journal_t *journal, uint32_t src, uint32_t dest) +{ + int err; + off_t off; + vhd_context_t *vhd; + + vhd = &journal->vhd; + off = (off_t)vhd_sectors_to_bytes(vhd->bat.bat[dest]); + + err = vhd_journal_add_block(journal, dest, + VHD_JOURNAL_DATA | VHD_JOURNAL_METADATA); + if (err) + return err; + + err = vhd_move_block(journal, src, off); + if (err) + return err; + + vhd->bat.bat[dest] = DD_BLK_UNUSED; + + return 0; +} + +/* + * remove a list of blocks from the vhd file + * if a block to be removed: + * - resides at the end of the file: simply clear its bat entry + * - resides elsewhere: move the last block in the file into its position + * and update the bat to reflect this + */ +static int +vhd_defrag_shrink(vhd_journal_t *journal, + vhd_block_t *original_free_list, int free_cnt) +{ + vhd_context_t *vhd; + size_t i, free_idx; + int j, err; + vhd_block_t *blocks, *free_list; + + err = 0; + blocks = NULL; + free_list = NULL; + vhd = &journal->vhd; + + blocks = malloc(vhd->bat.entries * sizeof(vhd_block_t)); + if (!blocks) { + err = -ENOMEM; + goto out; + } + + free_list = malloc(free_cnt * sizeof(vhd_block_t)); + if (!free_list) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < vhd->bat.entries; i++) { + blocks[i].block = i; + blocks[i].offset = vhd->bat.bat[i]; + } + + memcpy(free_list, original_free_list, + free_cnt * sizeof(vhd_block_t)); + + /* sort both the to-free list and the bat list + * in order of descending file offset */ + quicksort(free_list, 0, free_cnt - 1); + quicksort(blocks, 0, vhd->bat.entries - 1); + + for (i = 0, free_idx = 0; + i < vhd->bat.entries && free_idx < (size_t)free_cnt; i++) { + vhd_block_t *b = blocks + i; + + if (b->offset == DD_BLK_UNUSED) + continue; + + for (j = free_idx; j < free_cnt; j++) + if (b->block == free_list[j].block) { + /* the last block in the file is in the list of + * blocks to remove; no need to shuffle the + * data -- just clear the bat entry */ + vhd->bat.bat[free_list[j].block] = DD_BLK_UNUSED; + free_idx++; + continue; + } + + err = vhd_clobber_block(journal, b->block, + free_list[free_idx++].block); + if (err) + goto out; + } + + /* clear any bat entries for blocks we did not shuffle */ + for (i = free_idx; i < (size_t)free_cnt; i++) + vhd->bat.bat[free_list[i].block] = DD_BLK_UNUSED; + +out: + free(blocks); + free(free_list); + + return err; +} + +static int +vhd_clear_bat_entries(vhd_journal_t *journal, uint32_t entries) +{ + size_t i; + int err; + vhd_context_t *vhd; + off_t orig_map_off, new_map_off; + uint32_t orig_entries, new_entries; + + vhd = &journal->vhd; + orig_entries = vhd->header.max_bat_size; + new_entries = orig_entries - entries; + + if (vhd_has_batmap(vhd)) { + err = vhd_batmap_header_offset(vhd, &orig_map_off); + if (err) + return err; + } + + /* update header */ + vhd->header.max_bat_size = new_entries; + err = vhd_write_header(vhd, &vhd->header); + if (err) + return err; + + /* update footer */ + vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size; + vhd->footer.geometry = vhd_chs(vhd->footer.curr_size); + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + /* update bat -- we don't reclaim space, just clear entries */ + for (i = new_entries; i < orig_entries; i++) + vhd->bat.bat[i] = 0; + + err = vhd_write_bat(vhd, &vhd->bat); + if (err) + return err; + + /* update this after write_bat so the end of the bat is zeored */ + vhd->bat.entries = new_entries; + + if (!vhd_has_batmap(vhd)) + return 0; + + /* zero out old batmap header if new header has moved */ + err = vhd_batmap_header_offset(vhd, &new_map_off); + if (err) + return err; + + if (orig_map_off != new_map_off) { + size_t size; + + size = (size_t)vhd_bytes_padded(sizeof(struct dd_batmap_hdr)); + + err = vhd_write_zeros(journal, orig_map_off, size); + if (err) + return err; + } + + /* update batmap -- clear entries for freed blocks */ + for (i = new_entries; i < orig_entries; i++) + vhd_batmap_clear(vhd, &vhd->batmap, i); + + err = vhd_write_batmap(vhd, &vhd->batmap); + if (err) + return err; + + return 0; +} + +static int +vhd_dynamic_shrink(vhd_journal_t *journal, uint64_t secs) +{ + off_t eof; + uint32_t blocks; + vhd_context_t *vhd; + size_t i, j; + int err, free_cnt; + struct vhd_block *free_list; + + printf("dynamic shrink not fully implemented\n"); + return -ENOSYS; + + eof = 0; + free_cnt = 0; + free_list = NULL; + vhd = &journal->vhd; + + blocks = secs_to_blocks_down(vhd, secs); + if (blocks == 0) + return 0; + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) + return err; + } + + free_list = malloc(blocks * sizeof(struct vhd_block)); + if (!free_list) + return -ENOMEM; + + for (i = vhd->bat.entries - 1, j = 0; i >= 0 && j < blocks; i--, j++) { + uint32_t blk = vhd->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + free_list[free_cnt].block = i; + free_list[free_cnt].offset = blk; + free_cnt++; + } + } + + if (free_cnt) { + err = vhd_defrag_shrink(journal, free_list, free_cnt); + if (err) + goto out; + } + + err = vhd_clear_bat_entries(journal, blocks); + if (err) + goto out; + + /* remove data beyond footer */ + err = vhd_end_of_data(vhd, &eof); + if (err) + goto out; + + err = ftruncate(vhd->fd, eof + sizeof(vhd_footer_t)); + if (err) { + err = -errno; + goto out; + } + + err = 0; + +out: + free(free_list); + return err; +} + +static inline void +vhd_first_data_block(vhd_context_t *vhd, vhd_block_t *block) +{ + size_t i; + uint32_t blk; + + memset(block, 0, sizeof(vhd_block_t)); + + for (i = 0; i < vhd->bat.entries; i++) { + blk = vhd->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + if (!block->offset || blk < block->offset) { + block->block = i; + block->offset = blk; + } + } + } +} + +static inline uint32_t +vhd_next_block_offset(vhd_context_t *vhd) +{ + size_t i; + uint32_t blk, end, spp, next; +#ifdef _WIN32 + SYSTEM_INFO inf; + + GetSystemInfo( &inf ); + spp = inf.dwPageSize >> VHD_SECTOR_SHIFT; +#elif defined( _SC_PAGESIZE) + spp = sysconf( _SC_PAGESIZE ) >> VHD_SECTOR_SHIFT; +#else + spp = getpagesize() >> VHD_SECTOR_SHIFT; +#endif + next = 0; + + for (i = 0; i < vhd->bat.entries; i++) { + blk = vhd->bat.bat[i]; + + if (blk != DD_BLK_UNUSED) { + end = blk + vhd->spb + vhd->bm_secs; + next = MAX(next, end); + } + } + + return next; +} + +static inline int +in_range(off_t off, off_t start, off_t size) +{ + return (start < off && start + size > off); +} + +#define SKIP_HEADER 0x01 +#define SKIP_BAT 0x02 +#define SKIP_BATMAP 0x04 +#define SKIP_PLOC 0x08 +#define SKIP_DATA 0x10 + +static inline int +skip_check(int mode, int type) +{ + return mode & type; +} + +static int +vhd_check_for_clobber(vhd_context_t *vhd, off_t off, int mode) +{ + size_t i, n; + char *msg; + size_t size; + vhd_block_t fb; + vhd_parent_locator_t *loc; + + msg = NULL; + + if (!vhd_type_dynamic(vhd)) + return 0; + + if (off < VHD_SECTOR_SIZE) { + msg = "backup footer"; + goto fail; + } + + if (!skip_check(mode, SKIP_HEADER)) + if (in_range(off, + (off_t)vhd->footer.data_offset, sizeof(vhd_header_t))) { + msg = "header"; + goto fail; + } + + if (!skip_check(mode, SKIP_BAT)) + if (in_range(off, (off_t)vhd->header.table_offset, + (off_t)vhd_bytes_padded(vhd->header.max_bat_size * + sizeof(uint32_t)))) { + msg = "bat"; + goto fail; + } + + if (!skip_check(mode, SKIP_BATMAP)) + if (vhd_has_batmap(vhd) && + in_range(off, (off_t)vhd->batmap.header.batmap_offset, + (off_t)vhd_bytes_padded(vhd->batmap.header.batmap_size))) { + msg = "batmap"; + goto fail; + } + + if (!skip_check(mode, SKIP_PLOC)) { + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + for (i = 0; i < n; i++) { + loc = vhd->header.loc + i; + if (loc->code == PLAT_CODE_NONE) + continue; + + size = vhd_parent_locator_size(loc); + if (in_range(off, (off_t)loc->data_offset, size)) { + msg = "parent locator"; + goto fail; + } + } + } + + if (!skip_check(mode, SKIP_DATA)) { + vhd_first_data_block(vhd, &fb); + if (fb.offset && in_range(off, + (off_t)vhd_sectors_to_bytes(fb.offset), + VHD_BLOCK_SIZE)) { + msg = "data block"; + goto fail; + } + } + + return 0; + +fail: + EPRINTF(("write to 0x%08"PRIx64" would clobber %s\n", (uintmax_t)off, msg)); + return -EINVAL; +} + +/* + * take any metadata after the bat (@eob) and shift it + */ +static int +vhd_shift_metadata(vhd_journal_t *journal, off_t eob, + size_t bat_needed, size_t map_needed) +{ + int i, n, err; + vhd_context_t *vhd; + size_t size_needed; + char *buf, **locators; + vhd_parent_locator_t *loc; + + vhd = &journal->vhd; + size_needed = bat_needed + map_needed; + + n = sizeof(vhd->header.loc) / sizeof(vhd_parent_locator_t); + + locators = calloc(n, sizeof(char *)); + if (!locators) + return -ENOMEM; + + for (i = 0; i < n; i++) { + size_t size; + + loc = vhd->header.loc + i; + if (loc->code == PLAT_CODE_NONE) + continue; + + if (loc->data_offset < eob) + continue; + + size = vhd_parent_locator_size(loc); +#ifdef _WIN32 + buf = _aligned_malloc( size, VHD_SECTOR_SIZE ); + if( buf == NULL ) { + err = -errno; + goto out; + } +#else + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + err = -err; + buf = NULL; + goto out; + } +#endif + + err = vhd_seek(vhd, (off_t)loc->data_offset, SEEK_SET); + if (err) + goto out; + + err = vhd_read(vhd, buf, size); + if (err) + goto out; + + locators[i] = buf; + } + + for (i = 0; i < n; i++) { + off_t off; + size_t size; + + if (!locators[i]) + continue; + + loc = vhd->header.loc + i; + off = (off_t)loc->data_offset + size_needed; + size = vhd_parent_locator_size(loc); + + if (vhd_check_for_clobber(vhd, off + size, SKIP_PLOC)) { + EPRINTF(("%s: shifting locator %d would clobber data\n", + vhd->file, i)); + return -EINVAL; + } + + err = vhd_seek(vhd, off, SEEK_SET); + if (err) + goto out; + + err = vhd_write(vhd, locators[i], size); + if (err) + goto out; + + free(locators[i]); + locators[i] = NULL; + loc->data_offset = off; + + /* write the new header after writing the new bat */ + } + + if (vhd_has_batmap(vhd) && vhd->batmap.header.batmap_offset > eob) { + vhd->batmap.header.batmap_offset += bat_needed; + + /* write the new batmap after writing the new bat */ + } + + err = 0; + +out: + for (i = 0; i < n; i++) +#ifdef _WIN32 + _aligned_free( locators[i] ); +#else + free( locators[i] ); +#endif + free(locators); + + return err; +} + +static int +vhd_add_bat_entries(vhd_journal_t *journal, int entries) +{ + size_t i; + int err; + off_t off; + vhd_bat_t new_bat; + vhd_context_t *vhd; + uint32_t new_entries; + vhd_batmap_t new_batmap; + uint64_t bat_size, new_bat_size, map_size, new_map_size; + + vhd = &journal->vhd; + new_entries = vhd->header.max_bat_size + entries; + + bat_size = vhd_bytes_padded(vhd->header.max_bat_size * + sizeof(uint32_t)); + new_bat_size = vhd_bytes_padded(new_entries * sizeof(uint32_t)); + + map_size = vhd_bytes_padded((vhd->header.max_bat_size + 7) >> 3); + new_map_size = vhd_bytes_padded((new_entries + 7) >> 3); + + off = (off_t)(vhd->header.table_offset + new_bat_size); + if (vhd_check_for_clobber(vhd, off, SKIP_BAT | SKIP_BATMAP)) { + EPRINTF(("%s: writing new bat of 0x%"PRIx64" bytes " + "at 0x%08"PRIx64" would clobber data\n", + vhd->file, new_bat_size, vhd->header.table_offset)); + return -EINVAL; + } + + if (vhd_has_batmap(vhd)) { + off = (off_t)(vhd->batmap.header.batmap_offset + new_map_size); + if (vhd_check_for_clobber(vhd, off, 0)) { + EPRINTF(("%s: writing new batmap of 0x%"PRIx64" bytes" + " at 0x%08"PRIx64" would clobber data\n", vhd->file, + new_map_size, vhd->batmap.header.batmap_offset)); + return -EINVAL; + } + } + + /* update header */ + vhd->header.max_bat_size = new_entries; + err = vhd_write_header(vhd, &vhd->header); + if (err) + return err; + + /* update footer */ + vhd->footer.curr_size = (uint64_t)new_entries * vhd->header.block_size; + vhd->footer.geometry = vhd_chs(vhd->footer.curr_size); + vhd->footer.checksum = vhd_checksum_footer(&vhd->footer); + err = vhd_write_footer(vhd, &vhd->footer); + if (err) + return err; + + /* allocate new bat */ +#ifdef _WIN32 + new_bat.bat = _aligned_malloc( (size_t)new_bat_size, VHD_SECTOR_SIZE ); + if( new_bat.bat == NULL ) + return -errno; +#else + err = posix_memalign((void **)&new_bat.bat, VHD_SECTOR_SIZE, new_bat_size); + if (err) + return -err; +#endif + + new_bat.spb = vhd->bat.spb; + new_bat.entries = new_entries; + memcpy(new_bat.bat, vhd->bat.bat, (size_t)bat_size); + for (i = vhd->bat.entries; i < new_entries; i++) + new_bat.bat[i] = DD_BLK_UNUSED; + + /* write new bat */ + err = vhd_write_bat(vhd, &new_bat); + if (err) { +#ifdef _WIN32 + _aligned_free( new_bat.bat ); +#else + free(new_bat.bat); +#endif + return err; + } + + /* update in-memory bat */ +#ifdef _WIN32 + _aligned_free( vhd->bat.bat ); +#else + free(vhd->bat.bat); +#endif + vhd->bat = new_bat; + + if (!vhd_has_batmap(vhd)) + return 0; + + /* allocate new batmap */ +#ifdef _WIN32 + new_batmap.map = _aligned_malloc( (size_t)new_map_size, VHD_SECTOR_SIZE ); + if( new_batmap.map == NULL ) + return -errno; +#else + err = posix_memalign((void **)&new_batmap.map, + VHD_SECTOR_SIZE, new_map_size); + if (err) + return err; +#endif + new_batmap.header = vhd->batmap.header; + new_batmap.header.batmap_size = (u32)secs_round_up_no_zero(new_map_size); + memcpy(new_batmap.map, vhd->batmap.map, (size_t)map_size); + memset(new_batmap.map + map_size, 0, (size_t)(new_map_size - map_size)); + + /* write new batmap */ + err = vhd_write_batmap(vhd, &new_batmap); + if (err) { +#ifdef _WIN32 + _aligned_free( new_batmap.map ); +#else + free( new_batmap.map ); +#endif + return err; + } + + /* update in-memory batmap */ +#ifdef _WIN32 + _aligned_free( vhd->batmap.map ); +#else + free( vhd->batmap.map ); +#endif + vhd->batmap = new_batmap; + + return 0; +} + +static int +vhd_dynamic_grow(vhd_journal_t *journal, uint64_t secs) +{ + int err; + off_t eob, eom; + vhd_context_t *vhd; + vhd_block_t first_block; + uint64_t blocks, size_needed; + uint64_t bat_needed, bat_size, bat_avail, bat_bytes, bat_secs; + uint64_t map_needed, map_size, map_avail, map_bytes, map_secs; + + vhd = &journal->vhd; + + size_needed = 0; + bat_needed = 0; + map_needed = 0; + + /* number of vhd blocks to add */ + blocks = secs_to_blocks_up(vhd, secs); + + /* size in bytes needed for new bat entries */ + bat_needed = blocks * sizeof(uint32_t); + map_needed = (blocks >> 3) + 1; + + /* available bytes in current bat */ + bat_bytes = vhd->header.max_bat_size * sizeof(uint32_t); + bat_secs = secs_round_up_no_zero(bat_bytes); + bat_size = vhd_sectors_to_bytes(bat_secs); + bat_avail = bat_size - bat_bytes; + + if (vhd_has_batmap(vhd)) { + /* avaliable bytes in current batmap */ + map_bytes = (vhd->header.max_bat_size + 7) >> 3; + map_secs = vhd->batmap.header.batmap_size; + map_size = vhd_sectors_to_bytes(map_secs); + map_avail = map_size - map_bytes; + } else { + map_needed = 0; + map_avail = 0; + } + + /* we have enough space already; just extend the bat */ + if (bat_needed <= bat_avail && map_needed <= map_avail) + goto add_entries; + + /* we need to add new sectors to the bat */ + if (bat_needed > bat_avail) { + bat_needed -= bat_avail; + bat_needed = vhd_bytes_padded(bat_needed); + } else + bat_needed = 0; + + /* we need to add new sectors to the batmap */ + if (map_needed > map_avail) { + map_needed -= map_avail; + map_needed = vhd_bytes_padded(map_needed); + } else + map_needed = 0; + + /* how many additional bytes do we need? */ + size_needed = bat_needed + map_needed; + + /* calculate space between end of headers and beginning of data */ + err = vhd_end_of_headers(vhd, &eom); + if (err) + return err; + + eob = (off_t)(vhd->header.table_offset + vhd_sectors_to_bytes(bat_secs)); + vhd_first_data_block(vhd, &first_block); + + /* no blocks allocated; just shift post-bat metadata */ + if (!first_block.offset) + goto shift_metadata; + + /* + * not enough space -- + * move vhd data blocks to the end of the file to make room + */ + do { + off_t new_off, bm_size, gap_size; + + new_off = (off_t)vhd_sectors_to_bytes(vhd_next_block_offset(vhd)); + + /* data region of segment should begin on page boundary */ + bm_size = (off_t)vhd_sectors_to_bytes(vhd->bm_secs); + if ((new_off + bm_size) % 4096) { + gap_size = 4096 - ((new_off + bm_size) % 4096); + + err = vhd_write_zeros(journal, new_off, gap_size); + if (err) + return err; + + new_off += gap_size; + } + + err = vhd_move_block(journal, first_block.block, new_off); + if (err) + return err; + + vhd_first_data_block(vhd, &first_block); + + } while (eom + size_needed >= vhd_sectors_to_bytes(first_block.offset)); + + TEST_FAIL_AT(FAIL_RESIZE_DATA_MOVED); + +shift_metadata: + /* shift any metadata after the bat to make room for new bat sectors */ + err = vhd_shift_metadata(journal, eob, (size_t)bat_needed, (size_t)map_needed); + if (err) + return err; + + TEST_FAIL_AT(FAIL_RESIZE_METADATA_MOVED); + +add_entries: + return vhd_add_bat_entries(journal, (int)blocks); +} + +static int +vhd_dynamic_resize(vhd_journal_t *journal, uint64_t size) +{ + int err; + vhd_context_t *vhd; + uint64_t cur_secs, new_secs; + + vhd = &journal->vhd; + cur_secs = vhd->footer.curr_size >> VHD_SECTOR_SHIFT; + new_secs = size << (20 - VHD_SECTOR_SHIFT); + + if (cur_secs == new_secs) + return 0; + + err = vhd_get_header(vhd); + if (err) + return err; + + err = vhd_get_bat(vhd); + if (err) + return err; + + if (vhd_has_batmap(vhd)) { + err = vhd_get_batmap(vhd); + if (err) + return err; + } + + if (cur_secs > new_secs) + err = vhd_dynamic_shrink(journal, cur_secs - new_secs); + else + err = vhd_dynamic_grow(journal, new_secs - cur_secs); + + return err; +} + +static int +vhd_util_resize_check_creator(const char *name) +{ + int err; + vhd_context_t vhd; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY | VHD_OPEN_STRICT); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + if (!vhd_creator_tapdisk(&vhd)) { + printf("%s not created by xen; resize not supported\n", name); + err = -EINVAL; + } + + vhd_close(&vhd); + return err; +} + +int +vhd_util_resize(int argc, char **argv) +{ + char *name, *jname; + uint64_t size; + int c, err, jerr; + vhd_journal_t journal; + vhd_context_t *vhd; + + err = -EINVAL; + size = 0; + name = NULL; + jname = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "n:j:s:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'j': + jname = optarg; + break; + case 's': + err = 0; + size = strtoull(optarg, NULL, 10); + break; + case 'h': + default: + goto usage; + } + } + + if (err || !name || !jname || argc != optind) + goto usage; + + err = vhd_util_resize_check_creator(name); + if (err) + return err; + + libvhd_set_log_level(1); + err = vhd_journal_create(&journal, name, jname); + if (err) { + printf("creating journal failed: %d\n", err); + return err; + } + + vhd = &journal.vhd; + + err = vhd_get_footer(vhd); + if (err) + goto out; + + TEST_FAIL_AT(FAIL_RESIZE_BEGIN); + + if (vhd_type_dynamic(vhd)) + err = vhd_dynamic_resize(&journal, size); + else + err = vhd_fixed_resize(&journal, size); + + TEST_FAIL_AT(FAIL_RESIZE_END); + +out: + if (err) { + printf("resize failed: %d\n", err); + jerr = vhd_journal_revert(&journal); + } else + jerr = vhd_journal_commit(&journal); + + if (jerr) { + printf("closing journal failed: %d\n", jerr); + vhd_journal_close(&journal); + } else + vhd_journal_remove(&journal); + + return (err ? err : jerr); + +usage: + printf("options: <-n name> <-j journal> <-s size (in MB)> [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-revert.c b/extracters/vhd-util/vhd-util-revert.c new file mode 100644 index 0000000..6add4a1 --- /dev/null +++ b/extracters/vhd-util/vhd-util-revert.c @@ -0,0 +1,108 @@ +/* Copyright (c) 2008, XenSource 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. + * + * Altering operations: + * + * 1. Change the parent pointer to another file. + * 2. Change the size of the file containing the VHD image. This does NOT + * affect the VHD disk capacity, only the physical size of the file containing + * the VHD. Naturally, it is not possible to set the file size to be less than + * the what VHD utilizes. + * The operation doesn't actually change the file size, but it writes the + * footer in the right location such that resizing the file (manually, as a + * separate step) will produce the correct results. If the new file size is + * greater than the current file size, the file must first be expanded and then + * altered with this operation. If the new size is smaller than the current + * size, the VHD must first be altered with this operation and then the file + * must be shrunk. Failing to resize the file will result in a corrupted VHD. +*/ + +#include +/*#include */ +#include +/*#include */ +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" +#include "libvhd-journal.h" + +int +vhd_util_revert(int argc, char **argv) +{ + char *name, *jname; + vhd_journal_t journal; + int c, err; + + name = NULL; + jname = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "n:j:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'j': + jname = optarg; + break; + case 'h': + default: + goto usage; + } + } + + if (!name || !jname || argc != optind) + goto usage; + + libvhd_set_log_level(1); + err = vhd_journal_open(&journal, name, jname); + if (err) { + printf("opening journal failed: %d\n", err); + return err; + } + + err = vhd_journal_revert(&journal); + if (err) { + printf("reverting journal failed: %d\n", err); + vhd_journal_close(&journal); + return err; + } + + err = vhd_journal_remove(&journal); + if (err) { + printf("removing journal failed: %d\n", err); + vhd_journal_close(&journal); + return err; + } + + return 0; + +usage: + printf("options: <-n name> <-j journal> [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-scan.c b/extracters/vhd-util/vhd-util-scan.c new file mode 100644 index 0000000..363ad65 --- /dev/null +++ b/extracters/vhd-util/vhd-util-scan.c @@ -0,0 +1,1319 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include +#include /* for basename() */ +#include + +#include "list.h" +#include "libvhd.h" +#include "lvm-util.h" + +#define VHD_SCAN_FAST 0x01 +#define VHD_SCAN_PRETTY 0x02 +#define VHD_SCAN_VOLUME 0x04 +#define VHD_SCAN_NOFAIL 0x08 +#define VHD_SCAN_VERBOSE 0x10 +#define VHD_SCAN_PARENTS 0x20 + +#define VHD_TYPE_RAW_FILE 0x01 +#define VHD_TYPE_VHD_FILE 0x02 +#define VHD_TYPE_RAW_VOLUME 0x04 +#define VHD_TYPE_VHD_VOLUME 0x08 + +static inline int +target_volume(uint8_t type) +{ + return (type == VHD_TYPE_RAW_VOLUME || type == VHD_TYPE_VHD_VOLUME); +} + +static inline int +target_vhd(uint8_t type) +{ + return (type == VHD_TYPE_VHD_FILE || type == VHD_TYPE_VHD_VOLUME); +} + +struct target { + char name[VHD_MAX_NAME_LEN]; + char device[VHD_MAX_NAME_LEN]; + uint64_t size; + uint64_t start; + uint64_t end; + uint8_t type; +}; + +struct iterator { + int cur; + int cur_size; + int max_size; + struct target *targets; +}; + +struct vhd_image { + char *name; + char *parent; + uint64_t capacity; + off_t size; + uint8_t hidden; + int error; + char *message; + + struct target *target; + + struct list_head sibling; + struct list_head children; + struct vhd_image *parent_image; +}; + +struct vhd_scan { + int cur; + int size; + + int lists_cur; + int lists_size; + + struct vhd_image **images; + struct vhd_image **lists; +}; + +static int flags; +static struct vg vg; +static struct vhd_scan scan; + +static int +vhd_util_scan_pretty_allocate_list(int cnt) +{ + int i; + struct vhd_image *list; + + memset(&scan, 0, sizeof(scan)); + + scan.lists_cur = 1; + scan.lists_size = 10; + + scan.lists = calloc(scan.lists_size, sizeof(struct vhd_image *)); + if (!scan.lists) + goto fail; + + scan.lists[0] = calloc(cnt, sizeof(struct vhd_image)); + if (!scan.lists[0]) + goto fail; + + scan.images = calloc(cnt, sizeof(struct vhd_image *)); + if (!scan.images) + goto fail; + + for (i = 0; i < cnt; i++) + scan.images[i] = scan.lists[0] + i; + + scan.cur = 0; + scan.size = cnt; + + return 0; + +fail: + if (scan.lists) { + free(scan.lists[0]); + free(scan.lists); + } + + free(scan.images); + memset(&scan, 0, sizeof(scan)); + return -ENOMEM; +} + +static void +vhd_util_scan_pretty_free_list(void) +{ + int i; + + if (scan.lists) { + for (i = 0; i < scan.lists_cur; i++) + free(scan.lists[i]); + free(scan.lists); + } + + free(scan.images); + memset(&scan, 0, sizeof(scan)); +} + +static int +vhd_util_scan_pretty_add_image(struct vhd_image *image) +{ + int i; + struct vhd_image *img; + + for (i = 0; i < scan.cur; i++) { + img = scan.images[i]; + if (!strcmp(img->name, image->name)) + return 0; + } + + if (scan.cur >= scan.size) { + struct vhd_image *new, **list; + + if (scan.lists_cur >= scan.lists_size) { + list = realloc(scan.lists, scan.lists_size * 2 * + sizeof(struct vhd_image *)); + if (!list) + return -ENOMEM; + + scan.lists_size *= 2; + scan.lists = list; + } + + new = calloc(scan.size, sizeof(struct vhd_image)); + if (!new) + return -ENOMEM; + + scan.lists[scan.lists_cur++] = new; + scan.size *= 2; + + list = realloc(scan.images, scan.size * + sizeof(struct vhd_image *)); + if (!list) + return -ENOMEM; + + scan.images = list; + for (i = 0; i + scan.cur < scan.size; i++) + scan.images[i + scan.cur] = new + i; + } + + img = scan.images[scan.cur]; + INIT_LIST_HEAD(&img->sibling); + INIT_LIST_HEAD(&img->children); + + img->capacity = image->capacity; + img->size = image->size; + img->hidden = image->hidden; + img->error = image->error; + img->message = image->message; + + img->name = strdup(image->name); + if (!img->name) + goto fail; + + if (image->parent) { + img->parent = strdup(image->parent); + if (!img->parent) + goto fail; + } + + scan.cur++; + return 0; + +fail: + free(img->name); + free(img->parent); + memset(img, 0, sizeof(*img)); + return -ENOMEM; +} + +static int +vhd_util_scan_pretty_image_compare(const void *lhs, const void *rhs) +{ + struct vhd_image *l, *r; + + l = *(struct vhd_image **)lhs; + r = *(struct vhd_image **)rhs; + + return strcmp(l->name, r->name); +} + +static void +vhd_util_scan_print_image_indent(struct vhd_image *image, int tab) +{ + char *pad, *name, *pmsg, *parent; + + pad = (tab ? " " : ""); + name = image->name; + parent = (image->parent ? : "none"); + + if ((flags & VHD_SCAN_PRETTY) && image->parent && !image->parent_image) + pmsg = " (not found in scan)"; + else + pmsg = ""; + + if (!(flags & VHD_SCAN_VERBOSE)) { + name = basename(image->name); + if (image->parent) + parent = basename(image->parent); + } + + if (image->error) + printf("%*svhd=%s scan-error=%d error-message='%s'\n", + tab, pad, image->name, image->error, image->message); + else + printf("%*svhd=%s capacity=%"PRIu64" size=%"PRIu64" hidden=%u " + "parent=%s%s\n", tab, pad, name, image->capacity, + image->size, image->hidden, parent, pmsg); +} + +static void +vhd_util_scan_pretty_print_tree(struct vhd_image *image, int depth) +{ + struct vhd_image *img, *tmp; + + vhd_util_scan_print_image_indent(image, depth * 3); + + list_for_each_entry_safe(img, tmp, &image->children, sibling) + if (!img->hidden) + vhd_util_scan_pretty_print_tree(img, depth + 1); + + list_for_each_entry_safe(img, tmp, &image->children, sibling) + if (img->hidden) + vhd_util_scan_pretty_print_tree(img, depth + 1); + + free(image->name); + free(image->parent); + + image->name = NULL; + image->parent = NULL; +} + +static void +vhd_util_scan_pretty_print_images(void) +{ + int i; + struct vhd_image *image, **parentp, *parent, *keyp, key; + + qsort(scan.images, scan.cur, sizeof(scan.images[0]), + vhd_util_scan_pretty_image_compare); + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (!image->parent) { + image->parent_image = NULL; + continue; + } + + memset(&key, 0, sizeof(key)); + key.name = image->parent; + keyp = &key; + + parentp = bsearch(&keyp, scan.images, scan.cur, + sizeof(scan.images[0]), + vhd_util_scan_pretty_image_compare); + if (!parentp) { + image->parent_image = NULL; + continue; + } + + parent = *parentp; + image->parent_image = parent; + list_add_tail(&image->sibling, &parent->children); + } + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (image->parent_image || !image->hidden) + continue; + + vhd_util_scan_pretty_print_tree(image, 0); + } + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (!image->name || image->parent_image) + continue; + + vhd_util_scan_pretty_print_tree(image, 0); + } + + for (i = 0; i < scan.cur; i++) { + image = scan.images[i]; + + if (!image->name) + continue; + + vhd_util_scan_pretty_print_tree(image, 0); + } +} + +static void +vhd_util_scan_print_image(struct vhd_image *image) +{ + int err; + + if (!image->error && (flags & VHD_SCAN_PRETTY)) { + err = vhd_util_scan_pretty_add_image(image); + if (!err) + return; + + if (!image->error) { + image->error = err; + image->message = "allocating memory"; + } + } + + vhd_util_scan_print_image_indent(image, 0); +} + +static int +vhd_util_scan_error(const char *file, int err) +{ + struct vhd_image image; + + memset(&image, 0, sizeof(image)); + image.name = (char *)file; + image.error = err; + image.message = "failure scanning target"; + + vhd_util_scan_print_image(&image); + + /* + if (flags & VHD_SCAN_NOFAIL) + return 0; + */ + + return err; +} + +static vhd_parent_locator_t * +vhd_util_scan_get_parent_locator(vhd_context_t *vhd) +{ + int i; + vhd_parent_locator_t *loc; + + loc = NULL; + + for (i = 0; i < 8; i++) { + if (vhd->header.loc[i].code == PLAT_CODE_MACX) { + loc = vhd->header.loc + i; + break; + } + + if (vhd->header.loc[i].code == PLAT_CODE_W2RU) + loc = vhd->header.loc + i; + + if (!loc && vhd->header.loc[i].code != PLAT_CODE_NONE) + loc = vhd->header.loc + i; + } + + return loc; +} + +static inline int +copy_name(char *dst, const char *src) +{ + if (snprintf(dst, VHD_MAX_NAME_LEN, "%s", src) < VHD_MAX_NAME_LEN) + return 0; + + return -ENAMETOOLONG; +} + +/* + * LVHD stores realpath(parent) in parent locators, so + * /dev// becomes /dev/mapper/- + */ +static int +vhd_util_scan_extract_volume_name(char *dst, const char *src) +{ + int err; + char copy[VHD_MAX_NAME_LEN], *name, *s, *c; + + name = strrchr(src, '/'); + if (!name) + name = (char *)src; + + /* convert single dashes to slashes, double dashes to single dashes */ + for (c = copy, s = name; *s != '\0'; s++, c++) { + if (*s == '-') { + if (s[1] != '-') + *c = '/'; + else { + s++; + *c = '-'; + } + } else + *c = *s; + } + + *c = '\0'; + c = strrchr(copy, '/'); + if (c == name) { + /* unrecognized format */ + strcpy(dst, src); + return -EINVAL; + } + + strcpy(dst, ++c); + return 0; +} + +static int +vhd_util_scan_get_volume_parent(vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + char name[VHD_MAX_NAME_LEN]; + vhd_parent_locator_t *loc, copy; + + if (flags & VHD_SCAN_FAST) { + err = vhd_header_decode_parent(vhd, + &vhd->header, &image->parent); + if (!err) + goto found; + } + + loc = vhd_util_scan_get_parent_locator(vhd); + if (!loc) + return -EINVAL; + + copy = *loc; + copy.data_offset += image->target->start; + err = vhd_parent_locator_read(vhd, ©, &image->parent); + if (err) + return err; + +found: + err = vhd_util_scan_extract_volume_name(name, image->parent); + if (!err) + return copy_name(image->parent, name); + + return 0; +} + +static int +vhd_util_scan_get_parent(vhd_context_t *vhd, struct vhd_image *image) +{ + int i, err; + vhd_parent_locator_t *loc; + + if (!target_vhd(image->target->type)) { + image->parent = NULL; + return 0; + } + + loc = NULL; + + if (target_volume(image->target->type)) + return vhd_util_scan_get_volume_parent(vhd, image); + + if (flags & VHD_SCAN_FAST) { + err = vhd_header_decode_parent(vhd, + &vhd->header, &image->parent); + if (!err) + return 0; + } else { + /* + * vhd_parent_locator_get checks for the existence of the + * parent file. if this call succeeds, all is well; if not, + * we'll try to return whatever string we have before failing + * outright. + */ + err = vhd_parent_locator_get(vhd, &image->parent); + if (!err) + return 0; + } + + loc = vhd_util_scan_get_parent_locator(vhd); + if (!loc) + return -EINVAL; + + return vhd_parent_locator_read(vhd, loc, &image->parent); +} + +static int +vhd_util_scan_get_hidden(vhd_context_t *vhd, struct vhd_image *image) +{ + int err, hidden; + + err = 0; + hidden = 0; + + if (target_vhd(image->target->type)) + err = vhd_hidden(vhd, &hidden); + else + hidden = 1; + + if (err) + return err; + + image->hidden = hidden; + return 0; +} + +static int +vhd_util_scan_get_size(vhd_context_t *vhd, struct vhd_image *image) +{ + image->size = image->target->size; + + if (target_vhd(image->target->type)) + image->capacity = vhd->footer.curr_size; + else + image->capacity = image->size; + + return 0; +} + +static int +vhd_util_scan_open_file(vhd_context_t *vhd, struct vhd_image *image) +{ + int err, vhd_flags; + + if (!target_vhd(image->target->type)) + return 0; + + vhd_flags = VHD_OPEN_RDONLY | VHD_OPEN_IGNORE_DISABLED; + if (flags & VHD_SCAN_FAST) + vhd_flags |= VHD_OPEN_FAST; + + err = vhd_open(vhd, image->name, vhd_flags); + if (err) { + vhd->file = NULL; + image->message = "opening file"; + image->error = err; + return image->error; + } + + return 0; +} + +static int +vhd_util_scan_read_volume_headers(vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + char *buf; + size_t size; + struct target *target; + + buf = NULL; + target = image->target; + size = sizeof(vhd_footer_t) + sizeof(vhd_header_t); + + err = posix_memalign((void **)&buf, VHD_SECTOR_SIZE, size); + if (err) { + buf = NULL; + image->message = "allocating image"; + image->error = -err; + goto out; + } + + err = vhd_seek(vhd, target->start, SEEK_SET); + if (err) { + image->message = "seeking to headers"; + image->error = err; + goto out; + } + + err = vhd_read(vhd, buf, size); + if (err) { + image->message = "reading headers"; + image->error = err; + goto out; + } + + memcpy(&vhd->footer, buf, sizeof(vhd_footer_t)); + vhd_footer_in(&vhd->footer); + err = vhd_validate_footer(&vhd->footer); + if (err) { + image->message = "invalid footer"; + image->error = err; + goto out; + } + + /* lvhd vhds should always be dynamic */ + if (vhd_type_dynamic(vhd)) { + if (vhd->footer.data_offset != sizeof(vhd_footer_t)) + err = vhd_read_header_at(vhd, &vhd->header, + vhd->footer.data_offset + + target->start); + else { + memcpy(&vhd->header, + buf + sizeof(vhd_footer_t), + sizeof(vhd_header_t)); + vhd_header_in(&vhd->header); + err = vhd_validate_header(&vhd->header); + } + + if (err) { + image->message = "reading header"; + image->error = err; + goto out; + } + + vhd->spb = vhd->header.block_size >> VHD_SECTOR_SHIFT; + vhd->bm_secs = secs_round_up_no_zero(vhd->spb >> 3); + } + +out: + free(buf); + return image->error; +} + +static int +vhd_util_scan_open_volume(vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + struct target *target; + + target = image->target; + memset(vhd, 0, sizeof(*vhd)); + vhd->oflags = VHD_OPEN_RDONLY | VHD_OPEN_FAST; + + if (target->end - target->start < 4096) { + image->message = "device too small"; + image->error = -EINVAL; + return image->error; + } + + vhd->file = strdup(image->name); + if (!vhd->file) { + image->message = "allocating device"; + image->error = -ENOMEM; + return image->error; + } + + vhd->fd = open(target->device, O_RDONLY | O_DIRECT | O_LARGEFILE); + if (vhd->fd == -1) { + free(vhd->file); + vhd->file = NULL; + + image->message = "opening device"; + image->error = -errno; + return image->error; + } + + if (target_vhd(target->type)) + return vhd_util_scan_read_volume_headers(vhd, image); + + return 0; +} + +static int +vhd_util_scan_open(vhd_context_t *vhd, struct vhd_image *image) +{ + struct target *target; + + target = image->target; + + if (target_volume(image->target->type) || !(flags & VHD_SCAN_PRETTY)) + image->name = target->name; + else { + image->name = realpath(target->name, NULL); + if (!image->name) { + image->name = target->name; + image->message = "resolving name"; + image->error = -errno; + return image->error; + } + } + + if (target_volume(target->type)) + return vhd_util_scan_open_volume(vhd, image); + else + return vhd_util_scan_open_file(vhd, image); +} + +static int +vhd_util_scan_init_file_target(struct target *target, + const char *file, uint8_t type) +{ + int err; + struct stat stats; + + err = stat(file, &stats); + if (err == -1) + return -errno; + + err = copy_name(target->name, file); + if (err) + return err; + + err = copy_name(target->device, file); + if (err) + return err; + + target->type = type; + target->start = 0; + target->size = stats.st_size; + target->end = stats.st_size; + + return 0; +} + +static int +vhd_util_scan_init_volume_target(struct target *target, + struct lv *lv, uint8_t type) +{ + int err; + + if (lv->first_segment.type != LVM_SEG_TYPE_LINEAR) + return -ENOSYS; + + err = copy_name(target->name, lv->name); + if (err) + return err; + + err = copy_name(target->device, lv->first_segment.device); + if (err) + return err; + + target->type = type; + target->size = lv->size; + target->start = lv->first_segment.pe_start; + target->end = target->start + lv->first_segment.pe_size; + + return 0; +} + +static int +iterator_init(struct iterator *itr, int cnt, struct target *targets) +{ + memset(itr, 0, sizeof(*itr)); + + itr->targets = malloc(sizeof(struct target) * cnt); + if (!itr->targets) + return -ENOMEM; + + memcpy(itr->targets, targets, sizeof(struct target) * cnt); + + itr->cur = 0; + itr->cur_size = cnt; + itr->max_size = cnt; + + return 0; +} + +static struct target * +iterator_next(struct iterator *itr) +{ + if (itr->cur == itr->cur_size) + return NULL; + + return itr->targets + itr->cur++; +} + +static int +iterator_add_file(struct iterator *itr, + struct target *target, const char *parent, uint8_t type) +{ + int i; + struct target *t; + char *lname, *rname; + + for (i = 0; i < itr->cur_size; i++) { + t = itr->targets + i; + lname = basename((char *)t->name); + rname = basename((char *)parent); + + if (!strcmp(lname, rname)) + return -EEXIST; + } + + return vhd_util_scan_init_file_target(target, parent, type); +} + +static int +iterator_add_volume(struct iterator *itr, + struct target *target, const char *parent, uint8_t type) +{ + int i, err; + struct lv *lv; + + lv = NULL; + err = -ENOENT; + + for (i = 0; i < itr->cur_size; i++) + if (!strcmp(parent, itr->targets[i].name)) + return -EEXIST; + + for (i = 0; i < vg.lv_cnt; i++) { + err = fnmatch(parent, vg.lvs[i].name, FNM_PATHNAME); + if (err != FNM_NOMATCH) { + lv = vg.lvs + i; + break; + } + } + + if (err && err != FNM_PATHNAME) + return err; + + if (!lv) + return -ENOENT; + + return vhd_util_scan_init_volume_target(target, lv, type); +} + +static int +iterator_add(struct iterator *itr, const char *parent, uint8_t type) +{ + int err; + struct target *target; + + if (itr->cur_size == itr->max_size) { + struct target *new; + + new = realloc(itr->targets, + sizeof(struct target) * + itr->max_size * 2); + if (!new) + return -ENOMEM; + + itr->max_size *= 2; + itr->targets = new; + } + + target = itr->targets + itr->cur_size; + + if (target_volume(type)) + err = iterator_add_volume(itr, target, parent, type); + else + err = iterator_add_file(itr, target, parent, type); + + if (err) + memset(target, 0, sizeof(*target)); + else + itr->cur_size++; + + return (err == -EEXIST ? 0 : err); +} + +static void +iterator_free(struct iterator *itr) +{ + free(itr->targets); + memset(itr, 0, sizeof(*itr)); +} + +static void +vhd_util_scan_add_parent(struct iterator *itr, + vhd_context_t *vhd, struct vhd_image *image) +{ + int err; + uint8_t type; + + if (vhd_parent_raw(vhd)) + type = target_volume(image->target->type) ? + VHD_TYPE_RAW_VOLUME : VHD_TYPE_RAW_FILE; + else + type = target_volume(image->target->type) ? + VHD_TYPE_VHD_VOLUME : VHD_TYPE_VHD_FILE; + + err = iterator_add(itr, image->parent, type); + if (err) + vhd_util_scan_error(image->parent, err); +} + +static int +vhd_util_scan_targets(int cnt, struct target *targets) +{ + int ret, err; + vhd_context_t vhd; + struct iterator itr; + struct target *target; + struct vhd_image image; + + ret = 0; + err = 0; + + err = iterator_init(&itr, cnt, targets); + if (err) + return err; + + while ((target = iterator_next(&itr))) { + memset(&vhd, 0, sizeof(vhd)); + memset(&image, 0, sizeof(image)); + + image.target = target; + + err = vhd_util_scan_open(&vhd, &image); + if (err) { + ret = -EAGAIN; + goto end; + } + + err = vhd_util_scan_get_size(&vhd, &image); + if (err) { + ret = -EAGAIN; + image.message = "getting physical size"; + image.error = err; + goto end; + } + + err = vhd_util_scan_get_hidden(&vhd, &image); + if (err) { + ret = -EAGAIN; + image.message = "checking 'hidden' field"; + image.error = err; + goto end; + } + + if (vhd.footer.type == HD_TYPE_DIFF) { + err = vhd_util_scan_get_parent(&vhd, &image); + if (err) { + ret = -EAGAIN; + image.message = "getting parent"; + image.error = err; + goto end; + } + } + + end: + vhd_util_scan_print_image(&image); + + if (flags & VHD_SCAN_PARENTS && image.parent) + vhd_util_scan_add_parent(&itr, &vhd, &image); + + if (vhd.file) + vhd_close(&vhd); + if (image.name != target->name) + free(image.name); + free(image.parent); + + if (err && !(flags & VHD_SCAN_NOFAIL)) + break; + } + + iterator_free(&itr); + + if (flags & VHD_SCAN_NOFAIL) + return ret; + + return err; +} + +static int +vhd_util_scan_targets_pretty(int cnt, struct target *targets) +{ + int err; + + err = vhd_util_scan_pretty_allocate_list(cnt); + if (err) { + printf("scan failed: no memory\n"); + return -ENOMEM; + } + + err = vhd_util_scan_targets(cnt, targets); + + vhd_util_scan_pretty_print_images(); + vhd_util_scan_pretty_free_list(); + + return ((flags & VHD_SCAN_NOFAIL) ? 0 : err); +} + +static int +vhd_util_scan_find_file_targets(int cnt, char **names, + const char *filter, + struct target **_targets, int *_total) +{ + glob_t g; + struct target *targets; + int i, globs, err, total; + + total = cnt; + globs = 0; + *_total = 0; + *_targets = NULL; + + memset(&g, 0, sizeof(g)); + + if (filter) { + int gflags = ((flags & VHD_SCAN_FAST) ? GLOB_NOSORT : 0); + + errno = 0; + err = glob(filter, gflags, vhd_util_scan_error, &g); + + switch (err) { + case GLOB_NOSPACE: + err = -ENOMEM; + break; + case GLOB_ABORTED: + err = -EIO; + break; + case GLOB_NOMATCH: + err = -errno; + break; + } + + if (err) { + vhd_util_scan_error(filter, err); + return err; + } + + globs = g.gl_pathc; + total += globs; + } + + targets = calloc(total, sizeof(struct target)); + if (!targets) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < g.gl_pathc; i++) { + err = vhd_util_scan_init_file_target(targets + i, + g.gl_pathv[i], + VHD_TYPE_VHD_FILE); + if (err) { + vhd_util_scan_error(g.gl_pathv[i], err); + if (!(flags & VHD_SCAN_NOFAIL)) + goto out; + } + } + + for (i = 0; i + globs < total; i++) { + err = vhd_util_scan_init_file_target(targets + i + globs, + names[i], + VHD_TYPE_VHD_FILE); + if (err) { + vhd_util_scan_error(names[i], err); + if (!(flags & VHD_SCAN_NOFAIL)) + goto out; + } + } + + err = 0; + *_total = total; + *_targets = targets; + +out: + if (err) + free(targets); + if (filter) + globfree(&g); + + return err; +} + +static inline void +swap_volume(struct lv *lvs, int dst, int src) +{ + struct lv copy, *ldst, *lsrc; + + if (dst == src) + return; + + lsrc = lvs + src; + ldst = lvs + dst; + + memcpy(©, ldst, sizeof(copy)); + memcpy(ldst, lsrc, sizeof(*ldst)); + memcpy(lsrc, ©, sizeof(copy)); +} + +static int +vhd_util_scan_sort_volumes(struct lv *lvs, int cnt, + const char *filter, int *_matches) +{ + struct lv *lv; + int i, err, matches; + + matches = 0; + *_matches = 0; + + if (!filter) + return 0; + + for (i = 0; i < cnt; i++) { + lv = lvs + i; + + err = fnmatch(filter, lv->name, FNM_PATHNAME); + if (err) { + if (err != FNM_NOMATCH) { + vhd_util_scan_error(lv->name, err); + if (!(flags & VHD_SCAN_NOFAIL)) + return err; + } + + continue; + } + + swap_volume(lvs, matches++, i); + } + + *_matches = matches; + return 0; +} + +static int +vhd_util_scan_find_volume_targets(int cnt, char **names, + const char *volume, const char *filter, + struct target **_targets, int *_total) +{ + struct target *targets; + int i, err, total, matches; + + *_total = 0; + *_targets = NULL; + targets = NULL; + + err = lvm_scan_vg(volume, &vg); + if (err) + return err; + + err = vhd_util_scan_sort_volumes(vg.lvs, vg.lv_cnt, + filter, &matches); + if (err) + goto out; + + total = matches; + for (i = 0; i < cnt; i++) { + err = vhd_util_scan_sort_volumes(vg.lvs + total, + vg.lv_cnt - total, + names[i], &matches); + if (err) + goto out; + + total += matches; + } + + targets = calloc(total, sizeof(struct target)); + if (!targets) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < total; i++) { + err = vhd_util_scan_init_volume_target(targets + i, + vg.lvs + i, + VHD_TYPE_VHD_VOLUME); + if (err) { + vhd_util_scan_error(vg.lvs[i].name, err); + if (!(flags & VHD_SCAN_NOFAIL)) + goto out; + } + } + + err = 0; + *_total = total; + *_targets = targets; + +out: + if (err) + free(targets); + return err; +} + +static int +vhd_util_scan_find_targets(int cnt, char **names, + const char *volume, const char *filter, + struct target **targets, int *total) +{ + if (flags & VHD_SCAN_VOLUME) + return vhd_util_scan_find_volume_targets(cnt, names, + volume, filter, + targets, total); + return vhd_util_scan_find_file_targets(cnt, names, + filter, targets, total); +} + +int +vhd_util_scan(int argc, char **argv) +{ + int c, ret, err, cnt; + char *filter, *volume; + struct target *targets; + + cnt = 0; + ret = 0; + err = 0; + flags = 0; + filter = NULL; + volume = NULL; + targets = NULL; + + optind = 0; + while ((c = getopt(argc, argv, "m:fcl:pavh")) != -1) { + switch (c) { + case 'm': + filter = optarg; + break; + case 'f': + flags |= VHD_SCAN_FAST; + break; + case 'c': + flags |= VHD_SCAN_NOFAIL; + break; + case 'l': + volume = optarg; + flags |= VHD_SCAN_VOLUME; + break; + case 'p': + flags |= VHD_SCAN_PRETTY; + break; + case 'a': + flags |= VHD_SCAN_PARENTS; + break; + case 'v': + flags |= VHD_SCAN_VERBOSE; + break; + case 'h': + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!filter && argc - optind == 0) { + err = -EINVAL; + goto usage; + } + + if (flags & VHD_SCAN_PRETTY) + flags &= ~VHD_SCAN_FAST; + + err = vhd_util_scan_find_targets(argc - optind, argv + optind, + volume, filter, &targets, &cnt); + if (err) { + printf("scan failed: %d\n", err); + return err; + } + + if (!cnt) + return 0; + + if (flags & VHD_SCAN_PRETTY) + err = vhd_util_scan_targets_pretty(cnt, targets); + else + err = vhd_util_scan_targets(cnt, targets); + + free(targets); + lvm_free_vg(&vg); + + return ((flags & VHD_SCAN_NOFAIL) ? 0 : err); + +usage: + printf("usage: [OPTIONS] FILES\n" + "options: [-m match filter] [-f fast] [-c continue on failure] " + "[-l LVM volume] [-p pretty print] [-a scan parents] " + "[-v verbose] [-h help]\n"); + return err; +} diff --git a/extracters/vhd-util/vhd-util-set-field.c b/extracters/vhd-util/vhd-util-set-field.c new file mode 100644 index 0000000..efcc5ed --- /dev/null +++ b/extracters/vhd-util/vhd-util-set-field.c @@ -0,0 +1,106 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +int +vhd_util_set_field(int argc, char **argv) +{ + long value; + int err, c; + vhd_context_t vhd; + char *name, *field; + + err = -EINVAL; + value = 0; + name = NULL; + field = NULL; + + if (!argc || !argv) + goto usage; + + optind = 0; + while ((c = getopt(argc, argv, "n:f:v:h")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'f': + field = optarg; + break; + case 'v': + err = 0; + value = strtol(optarg, NULL, 10); + break; + case 'h': + default: + goto usage; + } + } + + if (!name || !field || optind != argc || err) + goto usage; + + if (strnlen(field, 25) >= 25) { + printf("invalid field\n"); + goto usage; + } + + if (strcmp(field, "hidden")) { + printf("invalid field %s\n", field); + goto usage; + } + + if (value < 0 || value > 255) { + printf("invalid value %ld\n", value); + goto usage; + } + + err = vhd_open(&vhd, name, VHD_OPEN_RDWR); + if (err) { + printf("error opening %s: %d\n", name, err); + return err; + } + + vhd.footer.hidden = (char)value; + + err = vhd_write_footer(&vhd, &vhd.footer); + + vhd_close(&vhd); + return err; + +usage: + printf("options: <-n name> <-f field> <-v value> [-h help]\n"); + return -EINVAL; +} diff --git a/extracters/vhd-util/vhd-util-snapshot.c b/extracters/vhd-util/vhd-util-snapshot.c new file mode 100644 index 0000000..c4b396a --- /dev/null +++ b/extracters/vhd-util/vhd-util-snapshot.c @@ -0,0 +1,225 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include +#ifndef _WIN32 +#include +#endif + +#include "libvhd.h" + +static int +vhd_util_find_snapshot_target(const char *name, char **result, int *parent_raw) +{ + size_t i; + int err; + char *target; + vhd_context_t vhd; + + *parent_raw = 0; + *result = NULL; + + target = strdup(name); + if (!target) + return -ENOMEM; + + for (;;) { + err = vhd_open(&vhd, target, VHD_OPEN_RDONLY); + if (err) + return err; + + if (vhd.footer.type != HD_TYPE_DIFF) + goto out; + + err = vhd_get_bat(&vhd); + if (err) + goto out; + + for (i = 0; i < vhd.bat.entries; i++) + if (vhd.bat.bat[i] != DD_BLK_UNUSED) + goto out; + + free(target); + err = vhd_parent_locator_get(&vhd, &target); + if (err) + goto out; + + if (vhd_parent_raw(&vhd)) { + *parent_raw = 1; + goto out; + } + + vhd_close(&vhd); + } + +out: + vhd_close(&vhd); + if (err) + free(target); + else + *result = target; + + return err; +} + +static int +vhd_util_check_depth(const char *name, int *depth) +{ + int err; + vhd_context_t vhd; + + err = vhd_open(&vhd, name, VHD_OPEN_RDONLY); + if (err) + return err; + + err = vhd_chain_depth(&vhd, depth); + vhd_close(&vhd); + + return err; +} + +int +vhd_util_snapshot(int argc, char **argv) +{ + vhd_flag_creat_t flags; + int c, err, prt_raw, limit; + char *name, *pname, *ppath, *backing; + uint64_t size; + vhd_context_t vhd; + + name = NULL; + pname = NULL; + ppath = NULL; + backing = NULL; + size = 0; + flags = 0; + limit = 0; + + if (!argc || !argv) { + err = -EINVAL; + goto usage; + } + + optind = 0; + while ((c = getopt(argc, argv, "n:p:l:mh")) != -1) { + switch (c) { + case 'n': + name = optarg; + break; + case 'p': + pname = optarg; + break; + case 'l': + limit = strtol(optarg, NULL, 10); + break; + case 'm': + vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW); + break; + case 'h': + err = 0; + goto usage; + default: + err = -EINVAL; + goto usage; + } + } + + if (!name || !pname || optind != argc) { + err = -EINVAL; + goto usage; + } + + ppath = realpath(pname, NULL); + if (!ppath) + return -errno; +#ifdef _WIN32 + ppath++; +#endif + if (vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) { + backing = strdup(ppath); + if (!backing) { + err = -ENOMEM; + goto out; + } + } else { + err = vhd_util_find_snapshot_target(ppath, &backing, &prt_raw); + if (err) { + backing = NULL; + goto out; + } + + /* + * if the sizes of the parent chain are non-uniform, we need to + * pick the right size: that of the supplied parent + */ + if (strcmp(ppath, backing)) { + err = vhd_open(&vhd, ppath, VHD_OPEN_RDONLY); + if (err) + goto out; + size = vhd.footer.curr_size; + vhd_close(&vhd); + } + + if (prt_raw) + vhd_flag_set(flags, VHD_FLAG_CREAT_PARENT_RAW); + } + + if (limit && !vhd_flag_test(flags, VHD_FLAG_CREAT_PARENT_RAW)) { + int depth; + + err = vhd_util_check_depth(backing, &depth); + if (err) + printf("error checking snapshot depth: %d\n", err); + else if (depth + 1 > limit) { + err = -ENOSPC; + printf("snapshot depth exceeded: " + "current depth: %d, limit: %d\n", depth, limit); + } + + if (err) + goto out; + } + + err = vhd_snapshot(name, size, pname, flags); + +out: +#ifdef _WIN32 + free( ppath - 1 ); +#else + free(ppath); +#endif + free(backing); + + return err; + +usage: + printf("options: <-n name> <-p parent name> [-l snapshot depth limit]" + " [-m parent_is_raw] [-h help]\n"); + return err; +} diff --git a/extracters/vhd-util/vhd-util.c b/extracters/vhd-util/vhd-util.c new file mode 100644 index 0000000..aa12e7a --- /dev/null +++ b/extracters/vhd-util/vhd-util.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2008, XenSource 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 +#include +#include +#include + +#include "libvhd.h" +#include "vhd-util.h" + +#ifndef _WIN32 +#if 1 +#define DFPRINTF(_f, _a...) fprintf(stdout, _f , ##_a) +#else +#define DFPRINTF(_f, _a...) ((void)0) +#endif +#endif + +typedef int (*vhd_util_func_t) (int, char **); + +struct command { + char *name; + vhd_util_func_t func; +}; + +struct command commands[] = { + { .name = "create", .func = vhd_util_create }, + { .name = "snapshot", .func = vhd_util_snapshot }, + { .name = "query", .func = vhd_util_query }, + { .name = "read", .func = vhd_util_read }, + { .name = "set", .func = vhd_util_set_field }, + { .name = "repair", .func = vhd_util_repair }, + { .name = "resize", .func = vhd_util_resize }, + { .name = "fill", .func = vhd_util_fill }, + { .name = "coalesce", .func = vhd_util_coalesce }, + { .name = "modify", .func = vhd_util_modify }, +#ifndef _WIN32 + { .name = "scan", .func = vhd_util_scan }, +#endif + {.name = "check", .func = vhd_util_check }, + { .name = "revert", .func = vhd_util_revert }, +}; + +#define print_commands() \ + do { \ + int i, n; \ + n = sizeof(commands) / sizeof(struct command); \ + printf("COMMAND := { "); \ + printf("%s", commands[0].name); \ + for (i = 1; i < n; i++) \ + printf(" | %s", commands[i].name); \ + printf(" }\n"); \ + } while (0) + +TEST_FAIL_EXTERN_VARS; + +void +help(void) +{ + printf("usage: vhd-util COMMAND [OPTIONS]\n"); + print_commands(); + exit(0); +} + +struct command * +get_command(char *command) +{ + int i, n; + + if (strnlen(command, 25) >= 25) + return NULL; + + n = sizeof(commands) / sizeof (struct command); + + for (i = 0; i < n; i++) + if (!strcmp(command, commands[i].name)) + return &commands[i]; + + return NULL; +} + +int +main(int argc, char *argv[]) +{ + char **cargv; + struct command *cmd; + int cargc, i, cnt, ret; + +#ifdef CORE_DUMP + #include + struct rlimit rlim; + rlim.rlim_cur = RLIM_INFINITY; + rlim.rlim_max = RLIM_INFINITY; + if (setrlimit(RLIMIT_CORE, &rlim) < 0) + fprintf(stderr, "setrlimit failed: %d\n", errno); +#endif + + ret = 0; + + libvhd_set_log_level(-1); + if (argc < 2) + help(); + + cargc = argc - 1; + cmd = get_command(argv[1]); + if (!cmd) { + fprintf(stderr, "invalid COMMAND %s\n", argv[1]); + help(); + } + + cargv = malloc(sizeof(char *) * cargc); + if (!cargv) + exit(ENOMEM); + + cnt = 1; + cargv[0] = cmd->name; + for (i = 1; i < cargc; i++) { + char *arg = argv[i + (argc - cargc)]; + + if (!strcmp(arg, "--debug")) { + libvhd_set_log_level(1); + continue; + } + + cargv[cnt++] = arg; + } + +#ifdef ENABLE_FAILURE_TESTING + for (i = 0; i < NUM_FAIL_TESTS; i++) { + TEST_FAIL[i] = 0; + if (getenv(ENV_VAR_FAIL[i])) + TEST_FAIL[i] = 1; + } +#endif /* ENABLE_FAILURE_TESTING */ + + ret = cmd->func(cnt, cargv); + + free(cargv); + + if( ret ) + printf( "Failed: (%d): %s\n", ret, strerror( (ret >0)? ret: -ret ) ); + + return (ret >= 0 ? ret : -ret); +} diff --git a/extracters/vhd-util/vhd-util.exe b/extracters/vhd-util/vhd-util.exe new file mode 100755 index 0000000..c7452d7 Binary files /dev/null and b/extracters/vhd-util/vhd-util.exe differ diff --git a/extracters/vhd-util/vhd-util.vcxproj b/extracters/vhd-util/vhd-util.vcxproj new file mode 100644 index 0000000..ad50e8b --- /dev/null +++ b/extracters/vhd-util/vhd-util.vcxproj @@ -0,0 +1,145 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {CC5FB437-0C01-4ECF-A75F-10C37B607AC6} + Win32Proj + 8.1 + + + + Application + true + v140 + + + Application + false + v140 + + + Application + true + v140 + + + Application + false + v140 + + + + + + + + + + + + + + + + + + + + + true + $(ProjectDir)$(Configuration)\ + + + false + $(ProjectDir)$(Configuration)\ + + + + _CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDebug + Level3 + ProgramDatabase + Disabled + include;../libvhd/include;%(AdditionalIncludeDirectories) + vhd-util-win32.h + false + false + + + MachineX86 + true + Console + ../libvhd/Debug/libvhd.lib;Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + _CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE + MultiThreaded + Level3 + ProgramDatabase + include;../libvhd/include;%(AdditionalIncludeDirectories) + false + vhd-util-win32.h + + + MachineX86 + true + Console + true + true + ../libvhd/Release/libvhd.lib;Rpcrt4.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + copy $(TargetPath) $(TargetDir)..\ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/extracters/vhd-util/vhd-util.vcxproj.filters b/extracters/vhd-util/vhd-util.vcxproj.filters new file mode 100644 index 0000000..03dde85 --- /dev/null +++ b/extracters/vhd-util/vhd-util.vcxproj.filters @@ -0,0 +1,93 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file