Skip to content

Commit

Permalink
Added wiping free space capability
Browse files Browse the repository at this point in the history
- new Dirtable.wipefreespace member to zero unallocated clusters
- new wipe tool
- ability to optimize virtual disk containers combining wipe with imgclone tool
- fixed a small help-bug in mkvdisk.py
  • Loading branch information
maxpat78 committed Oct 17, 2023
1 parent 38cdddb commit de1a611
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 5 deletions.
13 changes: 12 additions & 1 deletion FATtools/FAT.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,6 @@ def free(self, start, runs=None):
if self.last <= next <= self.last+7: break



class Chain(object):
"Opens a cluster chain or run like a plain file"
def __init__ (self, boot, fat, cluster, size=0, nofat=0, end=0):
Expand Down Expand Up @@ -1392,6 +1391,18 @@ def getdiskspace(self):
free_bytes = self.fat.free_clusters * self.boot.cluster
return (self.fat.free_clusters, free_bytes)

def wipefreespace(self):
"Zeroes free clusters"
buf = (4<<20) * b'\x00'
fourmegs = (4<<20)//self.boot.cluster
for start, length in self.fat.free_clusters_map.items():
if DEBUG&4: log("Wiping %d clusters from cluster #%d", length, start)
self.boot.stream.seek(self.boot.cl2offset(start))
while length:
q = min(length, fourmegs)
self.boot.stream.write(buf[:q*self.boot.cluster])
length -= q

def open(self, name):
"Opens the chain corresponding to an existing file name"
self._checkopen()
Expand Down
12 changes: 12 additions & 0 deletions FATtools/exFAT.py
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,18 @@ def getdiskspace(self):
free_bytes = self.boot.bitmap.free_clusters * self.boot.cluster
return (self.boot.bitmap.free_clusters, free_bytes)

def wipefreespace(self):
"Zeroes free clusters"
buf = (4<<20) * b'\x00'
fourmegs = (4<<20)//self.boot.cluster
for start, length in self.boot.bitmap.free_clusters_map.items():
if DEBUG&4: log("Wiping %d clusters from cluster #%d", length, start)
self.boot.stream.seek(self.boot.cl2offset(start))
while length:
q = min(length, fourmegs)
self.boot.stream.write(buf[:q*self.boot.cluster])
length -= q

def open(self, name):
"Opens the slot corresponding to an existing file name"
self._checkopen()
Expand Down
2 changes: 1 addition & 1 deletion FATtools/scripts/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


def main():
scripts=["cat","cp","imgclone","ls","mkfat","mkvdisk","rm","reordergui"]
scripts=["cat","cp","imgclone","ls","mkfat","mkvdisk","rm","reordergui","wipe"]
help_s="Usage: fattools " + ''.join( ['%s|'%s for s in scripts])[:-1]

if len(sys.argv) < 2:
Expand Down
1 change: 0 additions & 1 deletion FATtools/scripts/mkvdisk.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def call(args):

if not args.image_size:
print("mkvdisk error: you must specify a virtual disk image size!")
par.print_help()
sys.exit(1)

u = args.image_size[-1].lower()
Expand Down
40 changes: 40 additions & 0 deletions FATtools/scripts/wipe.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
import sys, os, argparse, logging
from FATtools import Volume

DEBUG = 0
from FATtools.debug import log

#~ logging.basicConfig(level=logging.DEBUG, filename='wipe.log', filemode='w')

class Arguments:
pass


def wipe(img):
v = Volume.vopen(img,'r+b')
if not hasattr(v, 'wipefreespace'):
print("Couldn't open a FAT/exFAT filesystem inside '%s'"%img)
sys.exit(1)
print('Wiping %d free clusters (%d bytes) . . .' % v.getdiskspace())
v.wipefreespace()
print('Done.')

def create_parser(parser_create_fn=argparse.ArgumentParser,parser_create_args=None):
help_s = """
fattools wipe IMAGE_FILE
"""
par = parser_create_fn(*parser_create_args,usage=help_s,
formatter_class=argparse.RawDescriptionHelpFormatter,
description="Wipes the free space in an (ex)FAT formatted disk, zeroing all free clusters.",
epilog="Combined with imgclone tool, it permits to optimize a virtual disk image size.\n")
par.add_argument('image_file', nargs=1)
return par

def call(args):
wipe(args.image_file[0])

if __name__ == '__main__':
par=create_parser()
args = par.parse_args()
call(args)
2 changes: 1 addition & 1 deletion FATtools/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.0.28'
__version__ = '1.0.29'
8 changes: 7 additions & 1 deletion README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Following features are implemented (mostly in Python, with a few ctypes calls to
- MBR and GPT partitions handling
- Long File Name and Unicode support
- tools to open, create, rename, list and delete files and directories, and to partition disks
- facilities to sort, clean and shrink directory tables
- facilities to sort, clean and shrink directory tables and to wipe (zero) free space
- file fragmentation calculator
- mkfat tool to properly (partition and) apply a FAT12/16/32 or exFAT filesystem to a block device (file or disk) and let CHKDSK be happy with it (included exFAT compressed Up-Case table generator)

Expand Down Expand Up @@ -57,10 +57,16 @@ fattools mkfat -t exfat -p gpt image.vhdx
fattools mkvdisk -b image.vdi delta.vdi
```

- to wipe free space in an (ex)FAT formatted disk, zeroing all free clusters:
```
fattools wipe image.vhd
```

- to convert a RAW disk image into a Dynamic VHD (so implicitly virtualizing zeroed data blocks):
```
fattools imgclone image.raw image.vhd
```
Please note that resulting image size can be reduced if the free space in image.raw has been wiped (zeroed) before.

- to list contents in a disk image, copy items to/from it, display and erase them:
```
Expand Down

0 comments on commit de1a611

Please sign in to comment.