Repository navigation
Releases
LintabOS 0.3.16
LintabOS 0.3.16 (pre-release)
The last three bugs in one chain, all found live on the same real Duet 3, in the space of about an hour: LinWinMod
"Open Windows files" had never actually worked on this kernel, for three separate, compounding reasons.
1. ntfsresize used a bare subprocess.run, bypassing the PATH fix 0.3.5 made elsewhere
lintab.bitlocker.probe() - the health check LinWinMod, WinTermMod and the read-write "Windows files" toggle all
run before touching a drive - called ntfsresize directly, never through disks.resolve_executable(). On a plain
desktop session whose PATH doesn't include /usr/sbin (where ntfsresize actually lives), that's a bare
FileNotFoundError - the real, literal cause of "can't find ntfsresize." The PATH fix 0.3.5 made for sfdisk
elsewhere was never applied to this file. Fixed: probe() now resolves ntfsresize the same way.
2. The ntfs3 kernel module doesn't exist on this kernel at all
With ntfsresize now actually found and run, the next real error was ERROR(13): ... Permission denied -
itself just a sign the unprivileged check structurally can't read the raw device (expected; see #3 below) - which
led to testing the full mount path directly, and finding: modprobe ntfs3 reports "Module ntfs3 not found in
directory /lib/modules/7.1.13+deb13-amd64". Debian's own kernel package does not build or ship the newer in-kernel
NTFS driver at all. lintab.winfiles.mount_fstype() has always requested exactly that nonexistent driver
(mount -t ntfs3), so every attempt to mount the Windows drive through the fstab entry this project manages has
always failed with mount: unknown filesystem type 'ntfs3' - regardless of anything else being fixed.
Confirmed live: mount -t ntfs (not ntfs3) dispatches through the standard /sbin/mount.ntfs -> mount.ntfs-3g
helper convention to this project's own existing ntfs-3g dependency - no kernel module involved at all. Fixed:
mount_fstype() now requests ntfs. Confirmed on the real tablet: the fstab entry now reads ntfs instead of
ntfs3, and mount /media/windows actually succeeds, read-write, listing the real Windows C: drive.
3. The unprivileged pre-flight health check could never pass, and treated that as a hard block
Even with ntfsresize found, the health check in bitlocker.probe() needs to open the raw partition device to
read NTFS's own dirty/hibernation flags - exactly the same raw-device permission wall read_disk() hit in 0.3.15,
just for a different tool. Unprivileged, this check can never succeed - not because of anything wrong with the
volume, but because it was never able to look. All three callers (LinWinMod, WinTermMod, the read-write "Windows
files" CLI flag) treated any not info.healthy result as a reason to refuse outright, including this permanently
unanswerable case - so even once #1 and #2 above were fixed, the pre-flight check would have still blocked every
single unprivileged attempt, forever.
Fixed: probe() now returns a distinct kind="unreadable" specifically for "couldn't even open the device" (as
opposed to "dirty"/"hibernated"/a genuine "other" read failure, which still correctly block). All three
callers now treat "unreadable" as inconclusive rather than as a refusal, and fall through to the real,
authoritative check: the privileged mount attempt that follows, exactly the way a healthy result already did.
Net result
Confirmed live, end to end, on the real tablet: lintab-windows-files enable --read-write now writes a working
fstab entry, and mount /media/windows succeeds read-write through ntfs-3g. LinWinMod's "Open Windows files"
should now work for the first time on this kernel.
Files: lintabos-core_0.3.16_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.15
LintabOS 0.3.15 (pre-release)
The actual bug behind "the picker just isn't there" in LinWinMod - the deepest one yet in this run of real-hardware
fixes, and a real gap between this project's stated design intent and what the code actually did.
What was found
LinWinMod's disk scan is deliberately unprivileged - "it's read-only, so it shouldn't need a password just to
open the app." But disks.read_disk() has always read the partition table via sfdisk -J, which opens the raw
block device directly. On a real Duet 3, /dev/nvme0n1 is root:disk, mode 660 - a plain desktop user isn't in
the disk group (correctly: that group grants raw read+write to the whole disk, bypassing every filesystem
permission, including on Windows' own SAM hive). So every unprivileged call to read_disk() silently got zero
partitions back - not an error, just an empty list, because the existing code already treated an unreadable
partition table as "blank disk, no partition table yet." Both the automatic Windows-partition search and the
manual "pick any NTFS partition" picker depend on this, so both silently found nothing: no error message, no
partition list, nothing - "the picker just isn't there."
What this is not: adding the user to the disk group. That was floated and set aside - it grants raw
read+write to the entire disk indefinitely, which is a far bigger, standing privilege escalation than this needed,
and specifically defeats LinWinMod's own SAM/SECURITY read refusal by making it trivial to read those bytes
directly off the device instead.
The actual fix: lsblk already reads partition type GUIDs, UUIDs, GPT partition names, filesystem type and
label entirely through sysfs and the udev database - confirmed live, genuinely unprivileged, no elevation at all.
read_disk() now falls back to lsblk's own columns only when sfdisk cannot open the device at all. The
existing sfdisk-based path - what every privileged caller (the partitioner, the installer, Remove LintabOS)
already uses - is completely unchanged, specifically because lsblk's udev-sourced columns can lag right after a
partition table is freshly written (confirmed live: a udev race, not a timing bug in this fix - a brand new
partition can show null for its type for a moment even after the kernel already knows about it), which matters
to a tool that just wrote the table and immediately wants to read it back, but never to LinWinMod's scan, since
by the time anyone looks at an already-installed tablet, the partitions have existed since installation and udev
settled long ago. The one thing the fallback can't provide is each partition's start offset - deliberately: a
wrong sector/byte conversion there could silently miscalculate free space on a 4Kn drive, and nothing unprivileged
actually needs it.
Confirmed live end to end: list_microsoft_data_partitions() now returns both Windows partitions with zero
elevation, which means LinWinMod's manual picker - the actual UI the user was missing - now appears and is
populated correctly. (Automatic detection alone still won't complete unprivileged, since confirming a partition is
really Windows means mounting it to look for \Windows\System32 - a separate, correctly-still-privileged step
this was never trying to remove. The manual picker existed specifically to be the fallback for exactly that case,
and now it's the fallback that actually works.)
Files: lintabos-core_0.3.15_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.14
LintabOS 0.3.14 (pre-release)
Another real-hardware bug, found and fixed live against the same Duet 3: LinWinMod's "pick any NTFS partition by
hand" picker wasn't actually showing every NTFS partition.
What was found
WINRE_DRV, the tablet's Windows Recovery Environment partition, is genuinely NTFS-formatted - confirmed live
(fstype=ntfs) - but it carries the Windows Recovery Environment GPT type (de94bba4-...), not the Microsoft
basic data type (ebd0a0a2-...) that list_microsoft_data_partitions() matched on. The picker's whole point is
to find NTFS partitions automatic detection misses, by GPT type rather than fragile content-probing - but it was
only checking for one of the GPT types a real NTFS partition can carry, so WinRE silently never appeared in it.
Fixed: list_microsoft_data_partitions() now also matches the Windows Recovery Environment GPT type. Confirmed
live: WINRE_DRV now appears in the picker's results on the real tablet.
Also in this release
- The BCD write's power gate (
lintab.bcdbackup) no longer requires AC power to be connected - only the 40%
battery minimum remains. Being tethered to a charger just to use the tool was more friction than the marginal
protection it added once the battery threshold already covers the case that matters (a power loss mid-write).
Files: lintabos-core_0.3.14_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.13
LintabOS 0.3.13 (pre-release)
The real bug behind "Describe current boot entry" reporting no Windows boot manager at all, found and fixed the
same day as 0.3.12, by testing live against a real Duet 3.
What was found
On any real, already-installed LintabOS system, the EFI System Partition is always already mounted at
/boot/efi - LintabOS's own GRUB lives there too, on the same shared ESP as Windows. disks.esp_has_windows()
and disks.esp_free_bytes() always tried to mount the ESP fresh at a new temporary path regardless, which the
kernel refuses outright for an already-mounted device whose read-only/read-write state would change ("Can't
mount, would change RO state"). The result: both functions silently reported "no Windows boot manager here" /
"0 bytes free" on every real installed system - not because anything was wrong with the ESP, but because the
mount attempt itself was doomed from the start. This is shared, pre-existing code (installer/lintab/disks.py),
also used by the dual-boot partitioner and Remove LintabOS - on a real install, Remove LintabOS's own
esp_has_windows safety check would have hit the same failure (refusing to run, not doing anything unsafe, but
refusing for the wrong reason).
Fixed with a new disks.mount_point_of() helper: both functions, and lintab.bcdbackup's own ESP mounting, now
check /proc/mounts for an existing mount first and reuse it as-is - never mounting (or unmounting) something
the rest of the system still needs.
What this unblocks
With both this and 0.3.12's battery-detection fix in place, "Describe current boot entry" now actually works
on a real installed system. First real result, from the same Duet 3: recoverysequence is present and points at
a real GUID - recovery does look properly configured - and, as expected, onetimeadvancedoptions isn't set yet,
since neither previous write attempt ever actually reached the BCD (both were blocked by the two bugs above, not
by anything wrong with the write itself or the underlying mechanism). The path is now actually clear to test the
write for real.
Also fixed in passing: describe_default_entry was mislabeling one element ("23000003") with a friendly name
that's only correct when it belongs to {bootmgr} itself - the real OS loader entry this function actually
inspects carries its own, different, not-yet-identified element under the same numeric ID. Removed the
mislabel rather than show a confident wrong name.
Files: lintabos-core_0.3.13_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.12
LintabOS 0.3.12 (pre-release)
A real-hardware bug found within hours of 0.3.11 shipping, fixed the same day, plus a new read-only diagnostic
for the experimental BCD write tool.
What was found
0.3.11's power gate (AC power + 40% battery minimum, before the experimental BCD write is attempted) was reading
the wrong battery on a real Duet 3. The tablet exposes two "Battery"-typed entries under
/sys/class/power_supply/:
BATX capacity: 72 (the tablet's real battery)
hid-0018:4858:121A.0001-battery-2 capacity: 0 (a connected stylus/keyboard case's own battery, over HID)
battery_percent() returned whichever one directory listing happened to return first - on this tablet, the HID
peripheral, reporting 0%, which made the power gate refuse to even attempt the write regardless of the tablet's
actual charge. hid-<bus>:<vendor>:<product>...-battery-N is the Linux kernel's own, documented naming convention
for HID report-battery devices, so lintab.bcdbackup now skips any power supply named with that prefix when
looking for the AC adapter or the device's own battery - a real, general fix, not a one-off for this tablet.
New: read-only boot-entry inspection
Because of the above, it's still not actually confirmed whether the onetimeadvancedoptions write works as
intended on a real installed system - the write was being refused before it ever ran. Rather than guess again,
"Windows Boot Configuration (Experimental)" gains a third action, "Describe current boot entry": mounts the
Windows ESP read-only and lists every element on the default boot entry, with friendly names where known
(onetimeadvancedoptions, recoverysequence, ...). This is how to check whether a real Windows installation's
boot entry actually has recoverysequence and recovery enabled at all - without needing bcdedit or even to boot
into Windows; it works from LintabOS alone. Never writes anything.
Files: lintabos-core_0.3.12_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.11
LintabOS 0.3.11 (pre-release)
A new, clearly-labeled experimental tool: "Windows Boot Configuration (Experimental)", for the one thing
0.3.10 established the existing Recovery button doesn't actually do - open Windows' real Troubleshoot/Advanced
options menu. Not wired into the existing Recovery button, not enabled by default behavior, not run unattended.
What it does
Windows' own Shift+Restart / shutdown /r /o sets a flag called onetimeadvancedoptions inside Windows' Boot
Configuration Data (BCD) - a different, more direct mechanism than the UEFI OsIndications bit 0.3.9/0.3.10 use,
and the one that actually opens the Troubleshoot menu. Writing that flag means editing the BCD, which is itself a
binary registry hive - something LintabOS has never done before (see docs/LEGAL.md, LinWinMod's read-only
design). This release adds that write, but only ever behind a real safety net, every time it runs:
- Power gate. Refuses to start unless connected to AC power and at least 40% charged. The actual write takes
milliseconds, too fast to meaningfully catch a power-unplug event mid-write and abort it - so this is a
pre-flight check, not a live interrupt. The ESP is FAT32, not journaled, and a write torn by power loss can
corrupt more than just the one file. - Automatic backup. The live BCD is always copied to
/var/lib/lintab/bcd-backup/BCD.bakbefore anything is
written. - Fresh-handle verification. Immediately after writing, the BCD is re-opened in a brand new hivex handle (not
the one that just wrote it) and checked - not just trusting thatcommit()returning without an exception means
the result is correct. - Automatic restore on any failure. If that verification fails, for any reason, the backup is restored before
control ever returns to the user. - Manual restore, independent of any write. The same tool can just restore the last backup on its own.
What is, and isn't, confirmed
The write path itself (hivex add-element-then-commit) was verified during development, round-tripped through a
second, independent tool (hivexsh), against real BCD samples extracted from a Windows installation ISO. That
testing is not shipped in this repo: the actual test fixture (tests/fixtures/synthetic-bcd-sample/fake-bcd)
is a synthetic, BCD-shaped hive built from tests/fixtures/sample.hive (hivex's own upstream open-source test
fixture) plus invented data - not extracted from any real Windows install, so nothing Microsoft-copyrighted ships
in this public repo. See scripts/gen-synthetic-bcd-fixture.py.
What is not yet confirmed: that writing this on an installed Windows system, then rebooting, actually opens
the Troubleshoot menu on real hardware, or that the write handles whatever structural differences a real
installed-system BCD may have (more boot entries, prior edits, a different Windows version) from the setup-media
samples it was validated against during development. That is exactly why this ships as a separate, clearly-labeled
"(Experimental)" tool - "Windows Boot Configuration (Experimental)" in the app grid - rather than replacing the
existing Recovery button.
Files: lintabos-0.3.11-amd64.iso, lintabos-core_0.3.11_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.9
LintabOS 0.3.9 (pre-release)
Two new things: more ways into Windows' own repair tools, and a terminal where the Files app alone isn't enough.
Restart into Windows: Recovery and Safe Mode
"Restart into Windows" now opens a small chooser instead of rebooting immediately: a normal restart (unchanged),
Restart into Windows Recovery, and Restart into Windows Safe Mode.
Recovery sets the UEFI OsIndications variable's documented "start OS recovery" bit before rebooting — a real,
specification-defined mechanism, and exactly as safe as the already-established "reboot to firmware setup" feature
(the same variable, a different bit). Whether Windows' own boot manager actually honors this bit was not
confirmed despite real research effort — if it doesn't, the tablet simply boots into Windows normally. The real,
manual way in (Settings → Recovery → Advanced startup, or holding Shift while choosing Restart) is shown as part of
the choice itself, not hidden as a silent fallback for if the automatic attempt does nothing.
Safe Mode makes the same attempt, since Safe Mode lives inside Windows' own recovery menu rather than being a
separate boot choice. Reaching it directly would need editing Windows' Boot Configuration Data — a binary registry
hive LintabOS does not write to anywhere, for the same reason LinWinMod's registry browser is read-only: a wrong
edit could leave Windows unable to boot at all, and that risk can't be ruled out without a real Windows BCD store
to test against.
WinTermMod
A terminal, positioned at your mounted Windows drive — the same Fast-Startup/BitLocker safety checks as LinWinMod's
Files tab — for find, grep, bulk renames and other command-line work against Windows files directly.
It is not Windows cmd.exe. A native Windows program can't run outside Windows at all, and running one against
the real Windows system drive through a compatibility layer would be, in effect, a way to act on Windows without
logging into it — the same risk LinWinMod's registry browser was built to avoid. What you get is an ordinary Linux
shell, just already in the right place.
What was checked
32 new tests: the UEFI variable read/write logic (including that it never raises when unsupported, and never loses
a bit that was already set), that a failed grub-reboot can never still trigger an actual reboot, and every
WinTermMod failure path (no partition, unclean drive, refused mount, no terminal installed) reporting a message
instead of silently doing nothing. One of those tests caught a real bug in the test setup itself (a Python
default-argument late-binding mistake) before it could hide a future regression.
Files: lintabos-0.3.9-amd64.iso, lintabos-core_0.3.9_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.6
LintabOS 0.3.6 (pre-release)
A real user's tablet had no partition LinWinMod could recognise automatically.
The gap
disks.find_windows() (what windows_partitions() relies on) needs a \Windows\System32 folder found via a real
mount, or to be the biggest Microsoft-data partition on a disk whose ESP has a Windows boot manager. An unusual
layout, or a secondary NTFS data partition without a detectable System32, satisfies neither — and so never showed
up in LinWinMod at all, with no way to tell it "no, use this one."
The fix
LinWinMod's Files tab now also lists every NTFS partition on the tablet (list_ntfs_partitions()), in a
dropdown with a "Use this partition" button — regardless of whether it looks like "the" Windows install. The same
Fast-Startup/dirty safety check (lintab-windows-files enable --read-write's own gate) still applies before
anything is mounted read-write, and switching which partition is set up no longer leaves a stale mount of the
previous one behind.
What was checked
A new unit test confirms the filter logic (NTFS kept, BitLocker and other filesystems excluded) against a synthetic
disk; two real loop-device tests build an actual NTFS partition with no \Windows\System32 folder, confirm
automatic detection genuinely misses it, and confirm lintab-windows-files enable --read-write --partition PATH
correctly sets up that exact partition with its real blkid UUID. All 20 loop-device tests (dual-boot, shrink,
Remove LintabOS, and the two new ones) pass; the new window layout was verified by rendering it headlessly.
Files: lintabos-0.3.6-amd64.iso, lintabos-core_0.3.6_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.5
LintabOS 0.3.5 (pre-release)
Bug fix, and a direct result of 0.3.4 working as intended: a user's tablet reported "sfdisk doesn't exist or it can't
find it" — the real error 0.3.4's fix surfaced instead of LinWinMod silently hanging.
The cause
sfdisk, blkid, mount, and the rest of the partitioning tools live in /sbin or /usr/sbin. sudo and pkexec
add those directories to PATH for anything that elevates to root first — which is every other place in LintabOS that
touches disks: the installer, Remove LintabOS. LinWinMod's disk scan is the one place that deliberately runs
unprivileged (it's read-only, so it shouldn't need a password just to open the app), and a plain desktop session's
PATH often does not include /sbin or /usr/sbin at all. sfdisk really was installed; subprocess.run(["sfdisk", ...])
just couldn't find it by name.
The fix
disks.run() — the one helper every partitioning module (disks.py, plan.py, and now winfiles.py's blkid lookup)
already goes through — now resolves a tool's full path before running it: the inherited PATH first, then the usual
sbin directories as a fallback. Verified by reproducing the exact failure (a PATH with no sbin directories at all) in
the builder container and confirming sfdisk now resolves to /usr/sbin/sfdisk and runs. All 18 real loop-device
partitioner tests (dual-boot, shrink, Remove LintabOS) still pass against real sfdisk/ntfsresize, and 7 new tests
cover the resolution logic itself.
Files: lintabos-0.3.5-amd64.iso, lintabos-core_0.3.5_all.deb + .minisig, SHA256SUMS.
LintabOS 0.3.4
LintabOS 0.3.4 (pre-release)
Bug fix: a real user reported LinWinMod stuck at "Looking for Windows..." forever.
The cause
FilesPage._scan() (and three similar background workers in the same window — _open, _checked, and the Registry
page's _scan_hives) ran their disk/hive discovery in a background thread with no exception handling. If that call
raised — disk enumeration has already failed with a real DiskError in this project's own test logs
(lsblk: /dev/vda: not a block device) — the thread died silently before reaching the GLib.idle_add call that updates
the label, leaving the page stuck on its initial "Looking for Windows…" / "Checking the drive…" / "Opening…" text forever.
Every other scanning window in this codebase (e.g. "Remove LintabOS") already guards against exactly this; this one didn't.
The fix
All four background workers in LinWinMod now catch their own exceptions and show a plain-words message instead of
hanging. A new structural test parses winmod_gui.py's own source and checks that every function calling
GLib.idle_add is wrapped in a try/except, so this bug class is caught if it's ever reintroduced.
Files: lintabos-0.3.4-amd64.iso, lintabos-core_0.3.4_all.deb + .minisig, SHA256SUMS.