{"id":18742758,"url":"https://github.com/maxpat78/fattools","last_synced_at":"2025-07-05T18:34:30.540Z","repository":{"id":44902740,"uuid":"185855792","full_name":"maxpat78/FATtools","owner":"maxpat78","description":"Facilities to access (ex)FAT filesystems and disk images with Python 3","archived":false,"fork":false,"pushed_at":"2024-08-30T07:40:23.000Z","size":557,"stargazers_count":41,"open_issues_count":0,"forks_count":4,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-12-13T09:10:08.638Z","etag":null,"topics":["disk-image","exfat","fat","fatfs","filesystem","format","gpt","mbr","partition","utilities","vdi","vhd","vhdx","vmdk"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maxpat78.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-09T18:59:20.000Z","updated_at":"2024-12-02T14:55:43.000Z","dependencies_parsed_at":"2024-04-23T12:36:32.088Z","dependency_job_id":"9dc300a8-80d1-479d-beca-4a109e6df9c3","html_url":"https://github.com/maxpat78/FATtools","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxpat78%2FFATtools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxpat78%2FFATtools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxpat78%2FFATtools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxpat78%2FFATtools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxpat78","download_url":"https://codeload.github.com/maxpat78/FATtools/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241103912,"owners_count":19910336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["disk-image","exfat","fat","fatfs","filesystem","format","gpt","mbr","partition","utilities","vdi","vhd","vhdx","vmdk"],"created_at":"2024-11-07T16:09:11.311Z","updated_at":"2025-07-05T18:34:30.527Z","avatar_url":"https://github.com/maxpat78.png","language":"Python","readme":"FATtools\n========\n\nInstall from PyPI using `pip install FATtools` [3] (easier) or downloading the source code (or the released packages) from here.\n\nBorn 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.\n\n*From version 1.1.0, a beta, basic, read-only support for NTFS volumes is included.*\n*It works in most circumstances and integrates well with `fattools` script (ls, cp and cat work).*\n\nMoreover:\n- it is able to open disk partitioned with both MBR or GPT and to help in partitioning (universal MBR boot code included)\n- it can transparently create, read and write Dynamic and Differencing VHD, VHDX[1], VDI and VMDK disk images\n- it can convert disk images between different virtual formats and clone disks\n- it can handle RAW disk images and BytesIO \"RamDisk\" images, also.\n- it supports 4K sectors\n- it can handle large clusters (128K, 256K) with FAT formats[2]\n- it is able to merge Differencing VHDs\n\nFollowing features are implemented (mostly in Python, with a few ctypes calls to handle disks natively; compatibility with Linux and macOS is not regularly tested):\n- sector aligned read/writes with both file images and real disks\n- sector based caching mechanism (for both reading and writing) to speed-up FAT and directory table operations\n- run length encoded map (with tuples and dictionaries) of free clusters, free directory slots, allocated cluster chains\n- transparent reading and writing of FAT12/16/32 and exFAT filesystems with FS boot-sector auto recognizer\n- MBR and GPT partitions handling\n- Long File Name and Unicode support\n- tools to open, create, rename, list and delete files and directories, and to partition disks\n- facilities to sort, clean and shrink directory tables and to wipe (zero) free space\n- file fragmentation calculator\n- 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)\n\n*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!*   \nBut it seems quite stable and useable, now.\n\nThe most fragile area (and, thus, subject to bugs) was the caching mechanism, that operates in different ways:\n- intercepting small I/O (\u003c= 512 bytes), which is cached in a small circular buffer. Bigger I/O bypasses the cache; when the cache is full, all dirty sectors are committed to disk and the cache buffer is zeroed. Sectors and buffers are paired with Python builtin dictionaries: this permits a good (from a Pythonic perspective) I/O speed during FAT and directory tables access;\n- maintaining a dictionary of pre-decoded FAT indexes, to improve the speed of repetitive access to cluster chains;\n- maintaining a dictionary of short and long names (paired with their respective directory slots) for each directory table, to speed up searches and updates in directory tables;\n- maintaining a RLE map of free clusters, free directory slots and allocated cluster chains, to dramatically improve speed of allocation and file access. \n\n*Actually, the I/O speed is closer to system's one.*\n\nCode is GPLed (look at GPL.TXT).\n  \n  \n[1] VHDX Log support is actually limited to replaying capability.\n  \n[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.\n\n[3] In Linux and macOS a `venv` is the preferred way.\n\n\n\n# At a glance\n\nThe package installs a `fattools` script, you can use this to perform simple command line operations.\n\n**NOTE: administrator (root, superuser) rights are _always_ required to access raw disk devices!**\n\n- to create a dynamic 8TB VHDX disk image with a single GPT partition and format it with exFAT:\n```\nfattools mkvdisk -s 8T --large-sectors image.vhdx\nfattools mkfat -t exfat -p gpt image.vhdx\n```\n\n- to create a differencing VDI disk image:\n```\nfattools mkvdisk -b image.vdi delta.vdi\n```\n\n- to wipe free space in an (ex)FAT formatted disk, zeroing all free clusters:\n```\nfattools wipe image.vhd\n```\n\n- to convert a RAW disk image into a Dynamic VHD (so implicitly virtualizing zeroed data blocks):\n```\nfattools imgclone image.raw image.vhd\n```\nPlease 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.\n\n- to capture a physical drive (disk block device) to a Dynamic VHD:   \n`fattools imgclone \\\\.\\PhysicalDrive2 image.vhd` (Windows)   \n`fattools imgclone /dev/sdb image.vhd` (Linux)   \n`fattools imgclone /dev/disk0 image.vhd` (macOS X, disk has first to be dismounted with `diskutil unmount`)\n\n- to list contents in a disk or disk image, copy items to/from it, display and erase them:\n```\nfattools ls \\\\.\\PhysicalDrive2/py*\nfattools ls image1.vhd/py* image2.vdi/py*\nfattools cp C:\\Python39\\Lib\\site-packages image.vhd/Python39/Lib\nfattools cp image.vhd/Python39 C:\\ProgramData\nfattools cat image.vhd/readme.txt\nfattools rm image.vhd/Python39\n```\n\n- to open an existing plain or VHD disk image, or real disk:\n```\n# -*- coding: cp1252 -*-\nfrom FATtools.Volume import *\ndisk = vopen('MyDiskImage.img', 'r+b', 'disk')\n```\n\n- to make a single GPT partition from all disk space:\n```\nfrom FATtools import partutils\ngpt = partutils.partition(disk)\n```\n\n- to format such partition with the exFAT file system:\n```\nfrom FATtools import mkfat, Volume\npart = Volume.vopen('MyDiskImage.img', 'r+b', 'partition0')\nmkfat.exfat_mkfs(part, part.size)\n```\n\n- to order items inside directory tables easily, with GUI and drag support (please note: in Linux, this requires Python `tkinter` module previously installed by `sudo apt-get install python3-tk`):\n```\nfattools reordergui\n```\n\n- to order root directory table in USB drive X (scripting):\n```\n# -*- coding: cp1252 -*-\nfrom FATtools.Volume import *\n\n# Assuming we have DirA, DirB, DirC in this disk order into X:\nroot = vopen('X:', 'r+b')\n\nnew_order = '''DirB\nDirC\nDirA'''\n\nroot._sortby.fix = new_order.split('\\n') # uses built-in directory sort algorithm\nroot.sort(root._sortby) # user-defined order, in _sortby.fix list\nroot.sort() # default ordering (alphabetical)\n```\n\n- mixed access with Python and FATtools from the same script:\n```\n# -*- coding: cp1252 -*-\nfrom FATtools.Volume import vopen, vclose\nfrom FATtools.mkfat import exfat_mkfs\nfrom os.path import join\nimport os\n\nreal_fat_fs = 'F:' # replace with mount point of your file system\n\n# Open and format with FATtools\nfs = vopen(real_fat_fs, 'r+b',what='disk')\nexfat_mkfs(fs, fs.size)\nvclose(fs)\n\n# Write some files with Python and list them\nT = ('c','a','b','d')\nfor t in T:\n   open(join(real_fat_fs, t+'.txt'), 'w').write('This is a sample \"%s.txt\" file.'%t)\n\nprint(os.listdir(real_fat_fs+'/'))\n\n# Open again, and sort root with FATtools\nfs = vopen(real_fat_fs, 'r+b')\nfs.sort()\nvclose(fs)\n\n# Check new table order with Python\nprint(os.listdir(real_fat_fs+'/'))\n```\n\n- (almost) same as above:\n```\n# -*- coding: cp1252 -*-\nfrom FATtools.Volume import vopen, vclose\nfrom FATtools.mkfat import exfat_mkfs\nfrom FATtools.partutils import partition\n\n# Open \u0026 create GPT partition\no = vopen('\\\\\\\\.\\\\PhysicalDrive1', 'r+b',what='disk')\nprint('Partitioning...')\npartition(o, 'mbr')\nvclose(o)\n\n# Reopen and format with EXFAT\no = vopen('\\\\\\\\.\\\\PhysicalDrive1', 'r+b',what='partition0')\nprint('Formatting...')\nexfat_mkfs(o, o.size)\nvclose(o) # auto-close partition AND disk\n\n# Reopen FS and write\nprint('Writing...')\no = vopen('\\\\\\\\.\\\\PhysicalDrive1', 'r+b')\n\n# Write some files with FATtools and sort them\nT = ('c','a','b','d')\nfor t in T:\n   f = o.create(t+'.txt')\n   f.write(b'This is a sample \"%s.txt\" file.'%bytes(t,'ascii'))\n   f.close()\no.sort()\nvclose(o)\n```\n\nPlease look inside 'samples' directory for more usage samples.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxpat78%2Ffattools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxpat78%2Ffattools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxpat78%2Ffattools/lists"}