本工程主要由vhd2img.cpp layout.h組成,C代碼實現。
vhd2img.cpp:
// vhd2img.cpp : 定義控制台應用程序的入口點。 //by www.frombyte.cn zhangyu //北亞數據恢復中心(www.sjhf.net)張宇 2012/1/6 發表於51cto #include "stdafx.h" #include <windows.h> #include "layout.h" #define VHD_OPENED 1 #define VHD_UNOPEN 0 #define BIT_MASK 0x80 static inline int test_bit (byte *addr, u32 nr) { return ((addr[nr >> 3] << (nr & 7)) & BIT_MASK) != 0; }; static inline void set_bit (byte *addr, u32 nr) { addr[nr >> 3] |= (BIT_MASK >> (nr & 7)); } HANDLE hIn,hOut; struct vhd_info { u64 uSize; u32 *bat; byte *batmap; u64 uBlkSize; u64 uBatNum; u32 spb; //secters per block u32 log2spb; u32 vhd_status; bool is_have_batmap; }; int open_vhd(TCHAR *name,vhd_info &vhd) { hd_ftr vhd_head; hIn = CreateFile(name,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); if(hIn == INVALID_HANDLE_VALUE) { _tprintf(_T("ERROR%d:source VHD file open error!\n"),GetLastError()); return -1; } DWORD bRead; ReadFile(hIn,(char*)&vhd_head,sizeof(hd_ftr),&bRead,NULL); if(bRead != sizeof(hd_ftr)) return -2; if(strncmp(vhd_head.cookie,HD_COOKIE,8) != 0) { _tprintf(_T("the source file is not a valid VHD format!\n")); return -3; } if( (LE32(vhd_head.type) != HD_TYPE_DYNAMIC) && (LE32(vhd_head.type) != HD_TYPE_DIFF) ) return -4; dd_hdr vhd_sparce; ReadFile(hIn,(char*)&vhd_sparce,sizeof(dd_hdr),&bRead,NULL); if(bRead != sizeof(dd_hdr)) return -5; if(strncmp(vhd_sparce.cookie,DD_COOKIE,8) != 0) return -6; vhd.uBlkSize = LE32(vhd_sparce.block_size); vhd.uBatNum = LE32(vhd_sparce.max_bat_size); vhd.bat = new u32 [vhd.uBatNum]; LARGE_INTEGER liPoi,liNew; liPoi.QuadPart = LE64(vhd_sparce.table_offset); SetFilePointerEx(hIn,liPoi,&liNew,FILE_BEGIN); ReadFile(hIn,vhd.bat,vhd.uBatNum * sizeof(u32),&bRead,NULL); if(bRead != vhd.uBatNum * sizeof(u32)) return -7; dd_batmap_hdr batmap_head; ReadFile(hIn,(char*)&batmap_head,sizeof(dd_batmap_hdr),&bRead,NULL); if(bRead != sizeof(dd_batmap_hdr)) return -8; if(strncmp(batmap_head.cookie,VHD_BATMAP_COOKIE,8) != 0) vhd.is_have_batmap = FALSE; else vhd.is_have_batmap = TRUE; vhd.spb = vhd.uBlkSize >> VHD_SECTOR_SHIFT; vhd.vhd_status = VHD_OPENED; return 1; } int read_vhd(vhd_info &vhd,byte* buf,u32 blkno) { byte spb_bitmap[VHD_SECTOR_SIZE]; DWORD bRead; if(vhd.bat[blkno] == 0xFFFFFFFF) return 2; byte *tbuf = new byte [vhd.uBlkSize]; LARGE_INTEGER liPoi,liNew; liPoi.QuadPart = (u64)(LE32(vhd.bat[blkno])) << VHD_SECTOR_SHIFT; SetFilePointerEx(hIn,liPoi,&liNew,FILE_BEGIN); ReadFile(hIn,spb_bitmap,VHD_SECTOR_SIZE,&bRead,NULL); if(bRead != VHD_SECTOR_SIZE) { delete [] tbuf; return -2; } ReadFile(hIn,tbuf,vhd.uBlkSize,&bRead,NULL); if(bRead != vhd.uBlkSize) { delete [] tbuf; return -3; } for(u32 i=0;i<vhd.spb;i++) { if(test_bit(spb_bitmap,i)) //位為1,表示磁盤上有數據,需要拷貝 { memcpy(buf + i * VHD_SECTOR_SIZE,tbuf + i*VHD_SECTOR_SIZE,VHD_SECTOR_SIZE); } } delete [] tbuf; return 1; } int _tmain(int argc, _TCHAR* argv[]) { if(argc != 3) { _tprintf(_T("vhd2img.exe <input vhd name> <output file/disk name>\n")); _tprintf(_T("eg. vhd2img.exe my.vhd d:\\my.img \n")); _tprintf(_T(" vhd2img.exe my.vhd \"\\\\.\\physicaldrive1\" (write to hardisk 1)\n")); return 0; } vhd_info vhd; //打開輸入vhd if(open_vhd(argv[1],vhd) != 1) return -1; //生成目標文件 HANDLE hOut = CreateFile(argv[2],GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,0,NULL); if(hOut == INVALID_HANDLE_VALUE) { hOut = CreateFile(argv[2],GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_ALWAYS,0,NULL); if(hOut == INVALID_HANDLE_VALUE) { _tprintf(_T("ERROR%d:the dest disk/file open error!\n"),GetLastError()); return -1; } } //u32 nBlkSize = vhd.uBlkSize; byte* buf = new byte [vhd.uBlkSize]; u32 nBitmapSize = (vhd.spb >> 3 ); byte* bitmap = new byte [nBitmapSize]; DWORD bWrite; LARGE_INTEGER liPos,liNew; for(int i=0;i<vhd.uBatNum;i++) { memset(buf,0,vhd.uBlkSize); memset(bitmap,0,nBitmapSize); if(read_vhd(vhd,buf,i) != 1) //讀錯誤或屬於稀疏空間 continue; liPos.QuadPart = (u64)i * (u64)vhd.uBlkSize; SetFilePointerEx(hOut,liPos,&liNew,FILE_BEGIN); WriteFile(hOut,buf,vhd.uBlkSize,&bWrite,NULL); if(bWrite != vhd.uBlkSize) { _tprintf(_T("ERROR%d:#%dblk (2MB) write error!\n\n"),GetLastError(),i); return -1; } } liPos.QuadPart = (u64)vhd.uBatNum * (u64)vhd.uBlkSize; SetFilePointerEx(hOut,liPos,&liNew,FILE_BEGIN); SetEndOfFile(hOut); //釋放 delete [] buf; delete [] bitmap; CloseHandle(hIn); CloseHandle(hOut); return 1; }
layout.h:
/* 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. */ typedef UINT32 u32; typedef UINT64 u64; typedef unsigned char vhd_uuid_t[16]; #ifndef __VHD_H__ #define __VHD_H__ #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. */ vhd_uuid_t uuid; /* Unique disk ID, used for naming parents */ char saved; /* one-bit -- is this disk/VM in a saved state? */ char hidden; /* tapdisk-specific field: is this vdi hidden? */ char reserved[426]; /* padding */ }; /* 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 ) */ /* * 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 */ 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. */ vhd_uuid_t prt_uuid; /* ID of the parent disk. */ u32 prt_ts; /* Modification time of the parent disk */ u32 res1; /* Reserved. */ char prt_name[512]; /* Parent unicode name. */ struct prt_loc loc[8]; /* Parent locator entries. */ char res2[256]; /* Reserved. */ }; /* 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) | * +-------------------------------------------------+ */ #define LE32(x) (( (u32)x >>24) ^ ((u32)x <<8 >>24 <<8) ^ ((u32)x <<16 >>24 <<16) ^ ((u32)x <<24)) #define LE64(x) (((u64)LE32((u32)x)) <<32 ) ^ ( LE32((u32)((u64)x >>32)) ) #endif