Install from PyPI using pip install FATtools
(easier) or downloading the source code (or the released packages) from here.
Born to re-sort directory entries in a FAT32 root table to cope with some hardware MP3 players' limits, it now provides full read/write support in Python 3 (both 32- and 64-bit) for FAT12/16/32 and exFAT filesystems, for hacking and recovering purposes.
Moreover:
Following features are implemented (mostly in Python, with a few ctypes calls to handle Win32 disks natively; compatibility with Linux is not regularly tested):
Obviously, since a filesystem is an extremely complex and delicate matter, and big bugs may lay around, you'll USE IT TOTALLY AT YOUR OWN RISK! But it seems quite stable and useable, now.
The most fragile area (and, thus, subject to bugs) was the caching mechanism, that operates in different ways:
Actually, the I/O speed is closer to system's one.
Code is GPLed (look at GPL.TXT).
[1] VHDX Log support is actually limited to replaying capability.
[2] Actually, to say, one can partition with GPT an 8 TB VHDX with 4K sectors and format with FAT32 and happily use it under Windows 11. However, Windows 11 CHKDSK reports no more than 4 TB bytes (while it counts clusters correctly). Also, FORMAT itself can't apply such legitimate FAT32 format to an 8 TB disk.
The package installs a fattools
script, you can use this to perform simple command line operations.
fattools mkvdisk -s 8T --large-sectors image.vhdx
fattools mkfat -t exfat -p gpt image.vhdx
fattools mkvdisk -b image.vdi delta.vdi
fattools wipe image.vhd
fattools imgclone image.raw image.vhd
Please note that resulting image size can get reduced if: 1) volume(s) is/are defragmented; 2) directory tables are cleaned and shrunk; 3) the free space has been wiped (zeroed) before.
fattools imgclone \.PhysicalDrive2 image.vhd
fattools ls image1.vhd/py* image2.vdi/py*
fattools cp C:Python39Libsite-packages image.vhd/Python39/Lib
fattools cp image.vhd/Python39 C:ProgramData
fattools cat image.vhd/readme.txt
fattools rm image.vhd/Python39
# -*- coding: cp1252 -*-
from FATtools.Volume import *
disk = vopen('MyDiskImage.img', 'r+b', 'disk')
from FATtools import partutils
gpt = partutils.partition(disk)
from FATtools import mkfat, Volume
part = Volume.vopen('MyDiskImage.img', 'r+b', 'partition0')
mkfat.exfat_mkfs(part, part.size)
fattools reordergui
# -*- coding: cp1252 -*-
from FATtools.Volume import *
# Assuming we have DirA, DirB, DirC in this disk order into X:
root = vopen('X:', 'r+b')
new_order = '''DirB
DirC
DirA'''
root._sortby.fix = new_order.split('n') # uses built-in directory sort algorithm
root.sort(root._sortby) # user-defined order, in _sortby.fix list
root.sort() # default ordering (alphabetical)
# -*- coding: cp1252 -*-
from FATtools.Volume import vopen, vclose
from FATtools.mkfat import exfat_mkfs
from os.path import join
import os
real_fat_fs = 'F:' # replace with mount point of your file system
# Open and format with FATtools
fs = vopen(real_fat_fs, 'r+b',what='disk')
exfat_mkfs(fs, fs.size)
vclose(fs)
# Write some files with Python and list them
T = ('c','a','b','d')
for t in T:
open(join(real_fat_fs, t+'.txt'), 'w').write('This is a sample "%s.txt" file.'%t)
print(os.listdir(real_fat_fs+'/'))
# Open again, and sort root with FATtools
fs = vopen(real_fat_fs, 'r+b')
fs.sort()
vclose(fs)
# Check new table order with Python
print(os.listdir(real_fat_fs+'/'))
# -*- coding: cp1252 -*-
from FATtools.Volume import vopen, vclose
from FATtools.mkfat import exfat_mkfs
from FATtools.partutils import partition
# Open & create GPT partition
o = vopen('\\.\PhysicalDrive1', 'r+b',what='disk')
print('Partitioning...')
partition(o, 'mbr')
vclose(o)
# Reopen and format with EXFAT
o = vopen('\\.\PhysicalDrive1', 'r+b',what='partition0')
print('Formatting...')
exfat_mkfs(o, o.size)
vclose(o) # auto-close partition AND disk
# Reopen FS and write
print('Writing...')
o = vopen('\\.\PhysicalDrive1', 'r+b')
# Write some files with FATtools and sort them
T = ('c','a','b','d')
for t in T:
f = o.create(t+'.txt')
f.write(b'This is a sample "%s.txt" file.'%bytes(t,'ascii'))
f.close()
o.sort()
vclose(o)
Please look inside 'samples' directory for more usage samples.