Skip to content

Ext4 for Windows - Release 10.2026

Latest

Choose a tag to compare

@wesmar wesmar released this 01 Oct 18:53

Native ext2, ext3 and ext4 read/write file-system driver for Windows 10 and Windows 11 x64.

Data safety: the extent tree and the directories rewritten (17 October update)

Please update: the previous release has the first three faults below.

  • Fixed: a file could show data it never wrote. A file grown into its own preallocation (allocation size set first, then the end of file) had those unwritten blocks marked written, and a cut inside an unwritten extent kept the part before it as written; both read what the disk held before - a deleted file's data - from Windows and from Linux.
  • Fixed: a Windows write into a hole of a sparse file that Linux made, in front of an extent node's first extent, counted the hole up to the second extent; the new extent overlapped the first (e2fsck: out of order extents, wrong i_blocks).
  • The extent tree is new code, written from the rules of Linux's fs/ext4/extents.c and checked after every change against a flat map of every block: 5.6 billion checks on x86 and x64. tests/extent-vm.ps1 shows all three faults on the previous release and none on this one, with files of 6 000 extents built and cut back.
  • Fixed: a name with a character beyond the BMP (emoji, rarer CJK) was written as CESU-8, which Linux does not read as that character, and Linux's own four-byte names came back cut to 16 bits. Names are UTF-8 both ways; names written the old way still open.
  • Fixed: a name of more than 255 bytes of UTF-8 - 255 UTF-16 characters can need 765 - overwrote the directory entry after it. Such a name is refused before anything changes.
  • Fixed: in a large directory a name made again after its removal read the whole directory (455 ms a rename in a 20 MB directory); a missed lookup of an emoji name in a casefolded directory read it too (7.8 ms, now 0.13).
  • Fixed: a sound directory was refused now and then as corrupt while the name cache was being trimmed.
  • Indexed directories are new code from the rules of fs/ext4/namei.c: the hashes equal debugfs on 200 000 names; 801 million model checks; five volumes with 145 000 names built by the driver, then checked, listed and changed by Linux's kernel and by the driver in turn.
  • Orphans a crash left - files deleted while open, truncations under way - are finished at mount, as Linux and e2fsck finish them. The journal replay is new code, equal to e2fsck's on 5 950 logs in real volumes.
  • A directory listing takes 410 ns per entry instead of 509 (NTFS 383 in the same guest): a profile put a third of its time in the name hash, the name cache's compare and the conversion of ASCII names.
  • Of the hand-written C tokens, 58.8 % lie in no run shared with an origin (41.6 % with identifiers ignored).

Faster listings and fsync, every crash state checked (16 October update)

  • A directory listing takes 460 ns per entry instead of 801 (NTFS about 400 in the same guest). A name is converted in one pass, not three; single-byte code pages read and write ASCII directly; an indexed listing keeps its names in chunks; the name cache's stripes are push locks.
  • An append with fsync takes 1.16 ms instead of 1.78 (NTFS about 1.09). The journal's tail is published only when the log needs the room, as jbd2 does: no superblock write and two device flushes per commit.
  • Fixed: the journal superblock could move the log's tail past checkpointed blocks before they were flushed. A disk persisting its cache out of order could keep the new tail and lose a directory block. Those writes are flushed first now.
  • The tables of every code page and of case folding live in a pageable section: 261 KB that no longer stay resident.
  • New MIT-licensed code: EUC-JP, written anew over code page 932; the red-black tree under the buffer heads, the run map and the htree reader; the NTSTATUS names, generated from the SDK's header. Of the hand-written C tokens, 47.1 % lie in no run shared with an origin (33.0 % with identifiers ignored).

Code pages and case folding, rebuilt under MIT

  • The 37 code page converters are generated from the published Microsoft, ISO 8859 and KOI8 mapping tables, with every difference the driver has always had kept as listed data. They are stored as 32-entry slices; the GBK and UHC extensions, which fill in Unicode order the code points their base sets leave free, are a population count on the Unicode side and the position of a set bit on the code side - no search. The CJK tables went from 277 KB to 201 KB; 18 KB of case maps the driver never read are gone.
  • KOI8-RU read every byte but the two it changes as Ў or ў; it now reads as KOI8-U except for those two. The Shift JIS encoder no longer writes a Latin-1 character's two bytes past a one-byte bound.
  • Casefolded directories fold names through tables generated from the Unicode Character Database 12.1 and a normalizer written anew from what Linux emits, including for invalid UTF-8, which Linux hashes too. Every one of the 1 112 064 code points has the leaf of Linux's trie; a name folds about 2.5 times faster. Strict mode refuses what Linux's utf8nlen() refuses: the previous check accepted stray continuation bytes.
  • Both are MIT-licensed new work; the driver as a whole remains GPL-2.0.
  • 3 500 lines of declarations nothing used are removed. ext4.sys is 600 KB, from 819 KB before the converters were rebuilt.

Compact character mappings and name lifetime

  • A failed filename conversion no longer returns a freed directory entry. Allocation and conversion failures release the entry once; the caller receives NULL.

Corruption handled as Linux handles it

  • Every metadata checksum is verified before use: superblock, group descriptors, block and inode bitmaps, inodes, extent nodes, directory leaves and htree nodes, xattr blocks, MMP and the journal.
  • Corruption found on disk is recorded in the superblock the way ext4_error records it - the error flag, the count, the first and the last place found - and the volume goes on or stops writing as its errors= setting asks. The record is written by a per-volume worker, never from inside paging I/O.
  • A group whose bitmap fails its check is left out of allocation. An inode whose blocks cannot all be released goes on the orphan list. A file whose inode cannot be read is listed by name and fails to open.
  • A superblock that fails its own checksum, which Linux refuses to mount, is mounted read-only and never written: the files can be copied off, and Windows does not offer to format the partition.

Security and correctness

  • Volume lock, unlock, dismount and extended DASD I/O are taken from volume handles only; through a file handle any user could lock or dismount a volume.
  • Reparse point, EA and mounted-volume requests refuse the control device and volume handles before their context is used. Setting and deleting a reparse point checks what NTFS checks and what Linux checks (mode bits, chattr +i/+a).
  • Set-information buffers are checked against each class's fixed size and a rename's or link's whole name, also for requests other drivers build.
  • A Linux fast symlink followed from Windows no longer has its / separators turned into \ in the inode itself; a symlinked directory lists the directory it points to.
  • Paths are built from the name tree when asked: an open file's path follows a renamed parent, and a directory watch survives the rename of its directory.
  • Copied folders stay writable: a folder's read-only attribute is no longer taken for a permission, and attributes set right after a create keep the directory bit.
  • HidingPrefix / HidingSuffix reach the volumes; a drive letter requested through IOCTL_APP_MOUNT_POINT is created on the letter asked for.
  • A removed directory's blocks are freed with lookups kept off, so a racing lookup no longer records corruption on a sound volume.

Waiting on events, not on time

  • Requests sent to the disk are waited for until they complete, as NTFS and FAT wait; a 30-second cancel used to report a write that completed as failed.
  • The journal polls no more. Handles, commits and the stop wait for events set under the journal lock; the journal thread wakes at the commit interval - counted from a transaction's first record, as jbd2 counts it - at the idle checkpoint, or on a request. A commit used to abort the journal after 30 seconds of waiting for handles, which could turn a volume on a slow disk read-only.

Code

  • The large dispatch routines run as short steps over explicit request state; driver load and unload are one step per resource, each undone by its counterpart.
  • Builds are reproducible: the same source and toolset give the same ext4.sys and ext4ctl.exe byte for byte. The final Release image was rebuilt from the published source and compared before packaging.

Validation

The packaged image passed the full Windows/Linux matrix under Driver Verifier: 20 sections, including folder copies, parallel I/O, stop/load cycles, hot-plug, LUKS/LVM, Linux feature checks with casefolded directories, clean e2fsck and content verification; it passed the five-volume directory test and the 38-code-page filename test as well. The image before the listing speed-up - the same extent and directory code - also passed, under Driver Verifier, the 219 e2fsprogs malformed-media fixtures (196 walked), the planted-corruption checks, the dedicated ext2/ext3 media checks and 18 orphan volumes, with no crash, hang or harness error.

Crash states: a test build records every write and flush the driver sends, and every state a power cut can leave - each prefix of the record, and each loss of one write between two flushes - is judged by e2fsck after its journal replay. On the new extent and directory code a directory grown to 2 000 names gave 5 004 states, none rejected; the driver's own journal replay of 40 of them left exactly the names and bytes e2fsck's replay gives. The new journal replay also passed the 120 states taken from the earlier runs - ext3, 1 KiB blocks, 300 create/rename/delete rounds - and a new 3 384-state churn run.

The extent, directory and name-set models passed 5.6 billion, 801 million and 60 billion checks on each of x86 and x64, and once more under AddressSanitizer and UndefinedBehaviorSanitizer; the journal replay model, 70 888 generated logs. The code page converters passed 126,567,306 equivalence checks against the converters they replace, and 38 code pages passed a Windows filename test with 1206 names and unrepresentable-name rejection on this image. Case folding passed 4,501,783,352 checks against the Linux trie. Release and Debug builds pass /W4 /WX without warnings, and the Release image rebuilt from the published source is identical byte for byte.

Limits

Damage found on disk is refused, recorded and contained, never repaired: that remains e2fsck's work. A volume with an external journal is mounted read-only. Keep backups of important data.

Package and loading

ext4.7z contains ext4.sys and ext4ctl.exe, without a password. Microsoft production signing is pending; use KVC or DrvLoader on a machine you administer.

copy /y ext4.sys "%SystemRoot%\System32\drivers\ext4.sys"
kvc driver load ext4
sc query ext4

Before replacing the image, run sc stop ext4 and wait for STOPPED. Copy the replacement and load it again. The README documents kvc driver reload ext4, DrvLoader and the separate kvc install ext4 boot-loader path.