Skip to content

Commit

Permalink
Supports merging a Differencing VHD
Browse files Browse the repository at this point in the history
  • Loading branch information
maxpat78 committed Oct 20, 2023
1 parent ca55eb7 commit 9de7678
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
2 changes: 1 addition & 1 deletion FATtools/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.0.31'
__version__ = '1.0.32'
49 changes: 43 additions & 6 deletions FATtools/vhdutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,10 @@
by the BAT and 512 MiB by bitmap sectors.
In fact, Windows 11 refuses to mount a VHD >2040 GiB.
A BAT index of 0xFFFFFFFF signals a zeroed block not allocated physically:
such value is kept until a block is written with all zeros, too. However,
since in a Differencing VHD the full chain must be checked, zeroing a block
already present in an ancestor requires physically allocating and zeroing it
in the descendant.
A BAT index of 0xFFFFFFFF signals a zeroed block (Dynamic VHD) or a block not
allocated on a given child (Differencing VHD): in the latter case, zeroing a
block alredy present in an ancestor requires allocating a new zeroed block
in the child image.
PLEASE NOTE THAT ALL NUMBERS ARE IN BIG ENDIAN FORMAT! """
import io, struct, uuid, zlib, ctypes, time, os, math
Expand Down Expand Up @@ -456,6 +455,8 @@ def tell(self):

def close(self):
self.stream.close()
if self.Parent:
self.Parent.close()

def has_block(self, i):
"Returns True if the caller or some ascendant has got allocated a block"
Expand Down Expand Up @@ -647,7 +648,43 @@ def copysect(vpos, sec):
self.stream.seek(bmp.i*512)
self.stream.write(bmp.bmp)


def merge(self):
"""Merges a Differencing VHD with its parent and erase the image on success.
Returns None if unsupported image, or a tuple (sectors_merged, blocks_merged)."""
if not self.Parent: return None
i = 0
tot_blocks=0
tot_sectors=0
while i < self.bat.size:
# check block presence
blkoff = self.bat[i]
if blkoff == 0xFFFFFFFF:
i += 1
continue
self.Parent.close()
self.Parent = Image(self.Parent.name, "r+b") # reopen Parent in RW mode
# load and scan the block bitmap
j = 0
self.stream.seek(blkoff*512)
bmp = BlockBitmap(self.stream.read(self.bitmap_size), i)
copied=0
while j < self.bitmap_size*8:
if bmp.isset(j):
# read the sector
self.stream.seek(blkoff*512 + self.bitmap_size + j*512)
s = self.stream.read(512)
# seek absolute position in parent and copy
self.Parent.seek(i*self.block + j*512)
self.Parent.write(s)
tot_sectors+=1
copied=1
j += 1
if copied: tot_blocks+=1
i += 1
if DEBUG&16: log("%s: merged %d sectors in %d blocks",self.name,tot_sectors,tot_blocks)
self.close()
os.remove(self.name)
return (tot_sectors, tot_blocks)

def mk_chs(size):
"Given a disk size, computates and returns as a string the pseudo CHS for VHD Footer"
Expand Down

0 comments on commit 9de7678

Please sign in to comment.