Skip to content

Releases: alegauss/freewilly

FreeWilly 1.0.16

Choose a tag to compare

@alegauss alegauss released this 15 Oct 21:37

What's Changed

  • chore(deps): Bump the nuget group with 2 updates by @dependabot[bot] in #20
  • chore(deps): bump the site group in /site with 6 updates by @dependabot[bot] in #19

Full Changelog: v1.0.15...v1.0.16

FreeWilly 1.0.15

Choose a tag to compare

@alegauss alegauss released this 16 Sep 11:26

An engine whose filesystem was damaged badly enough to stop it booting could not be checked,
because the check began by asking that engine a question. The Engine page's filesystem check now
works on that machine, and it repaired the one this was measured on.

The check refused on the disk it exists for

When the engine exited with
Wsl/Service/CreateInstance/E_UNEXPECTED. The kernel log said why: as the distribution mounted,
ext4 found a bad checksum on one block bitmap, aborted the journal and remounted read-only, and
WSL's init could not start on that root.

EXT4-fs error (device sdd): ext4_validate_block_bitmap:421: comm LaunchDistro: bg 410: bad block bitmap checksum
Aborting journal on device sdd-8.
EXT4-fs (sdd): Remounting filesystem read-only
WSL (1) ERROR: I/O error @main.cpp:2396 (MountInit)

That is the damage the filesystem check exists to find, and in 1.0.14 it stopped at its first step:

[ok  ]  stop the engine         no host was running, so nothing will put the engine back
[FAIL]  note the disk           freewilly could not say which filesystem is its root, so nothing here can tell its disk from the others attached. Run the sequence by hand

The check runs e2fsck from a temporary rescue distribution against the engine's disk, and it
picks that disk out by the UUID of its filesystem, because device names move between boots: the
same disk was /dev/sdd in one run and /dev/sde in the next. It learned the UUID by asking the
engine's distribution, and a distribution that cannot boot cannot answer.

The UUID is read out of the virtual disk

When the distribution does not answer, the check reads the UUID from ext4.vhdx on the Windows
side
, following the VHDX block allocation table to the ext4 superblock. The read works while WSL
has the file open.

How the disk is picked does not change. The UUID is still matched against blkid inside the
rescue, so a bad read ends in "no attached disk carries" and never in e2fsck running against some
other distribution's disk.

The disk is attached by a launch that fails

A UUID is no use until the disk is attached, and WSL attaches a distribution's disk only when
something launches that distribution. Measured with another distribution holding the virtual
machine up: terminating the engine's stopped distribution exits 0 and attaches nothing, while a
launch that fails with E_UNEXPECTED leaves the disk attached and unmounted, and it stays attached
through the terminate.

So the check now launches the distribution once, after the rescue is holding the machine and
before the terminate.
A launch that boots after all is taken down by that same terminate, so
e2fsck never meets a mounted root. The step appears in the transcript as attach the disk.

A refusal quotes WSL

Where neither the distribution nor its disk can name the filesystem, the refusal now carries what
wsl.exe said, rather than stopping at "could not say".

What it did on the machine it was measured on

The read-only check found errors confined to block group 410: 192 blocks the bitmap marked as used
that nothing used, free-block counts that no longer added up, and a bitmap whose checksum did not
match. Passes 1 to 4, which cover inodes, directories and link counts, came through clean. The
repair corrected all three:

[ok  ]  attach the disk         freewilly would not start, which still attaches its disk: ...
[ok  ]  take the engine down    freewilly terminated, so its root is unmounted
[ok  ]  find the disk           9cb04147-49e5-4515-8e3c-0ecdca70eb3f is attached as /dev/sdd
[ok  ]  repair                  errors were found and corrected

The distribution booted afterwards with its root mounted read-write and ext4 recording no errors.

Install

Download FreeWilly-Setup-1.0.15.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

An engine that will not start can be checked from the Engine page, or with FreeWilly.exe --fsck
at a prompt. The repair writes to the disk holding every image and volume, so it is offered only
after a check has shown what it found, and at a prompt it is FreeWilly.exe --fsck --repair.

FreeWilly 1.0.14

Choose a tag to compare

@alegauss alegauss released this 05 Sep 01:29

Shutting the machine down with FreeWilly running left the engine's ext4 filesystem torn off
mid-write, on three evenings running, and none of the three left a journal that said why.

Windows was refusing to start wsl.exe, and the refusal was a modal box

Between 29 and 31 August 2026 Windows logged the same application popup at every shutdown:
wsl.exe failing with 0xC0000142, STATUS_DLL_INIT_FAILED, in the same second the engine host and
the tray both took SessionEnding. It is not a WSL fault and not a fault in how this tool spells the
command. Adobe's LogTransport2.exe took the same code in the same second on 30 August, so the
condition belongs to the session: under Fast Startup, which is on by default, Windows logs the user
off before it hibernates, and process creation in that session stops working part way through.

What made that worse than a failed launch is that the popup is a hard error. The child sat on a
modal box nobody could click, so the runner waited out its whole budget on a process that had
already failed. Every failing teardown in the journal ends at exactly the four seconds Windows
allows; the one that worked, on 30 August at 10:08, finished in two.

FreeWilly now sets SEM_FAILCRITICALERRORS in its entry point, before anything can reach for
wsl.exe, and every child inherits it. The launch still fails, because nothing here can make
Windows start a process it has decided not to start. It fails immediately and silently instead,
which returns the budget to the steps that can still run and takes the dialog off the screen.

A hurried stop unmounts before it does anything else

The stop did four things in order: drop the relay, ask whether the distribution is running, send
dockerd a SIGTERM and wait for it, and only then run wsl --terminate. Three of those four reach
for wsl.exe, and the terminate, which is the step that unmounts ext4, was last. With four seconds
to spend and one launch able to consume all of them, the sessions of 29, 30 and 31 August all end
at "still tearing down after 4s" with no "terminated freewilly" line behind them.

Containers stopping themselves is worth having, but not ahead of the unmount, because a container
reaped by WSL2 recovers on the next start and a root filesystem torn off mid-write may not. A
hurried stop now terminates first
and does the rest with whatever budget is left. A patient stop
keeps the order it had, where the SIGTERM has twenty seconds and nothing is racing it.

A quiet pipe is not a stopped distribution

The tray runs a backstop for the case where the engine host does not handle the teardown, and it
decided the host had by pinging the engine and finding nothing there. Dropping the relay is the
first thing the stop does, so the pipe goes quiet at the start of a teardown rather than at the
end. On 31 August the tray wrote "the engine host took it down" at 21:51:46, and two seconds later
the host itself wrote "still tearing down after 4s". Both lines are about the same teardown and the
second one is the true one.

The backstop now asks whether the distribution is registered and running, which stays true
right up to the terminate. That question needs wsl.exe, the launch that may not work, so a failed
launch reads as unknown and terminates anyway: terminating a distribution that is already down
costs one wasted call, and not terminating one that is up costs the ext4.

Nothing in a hurried teardown outlasts the budget

Two waits were longer than the whole shutdown. Every wsl.exe call carried the fifteen seconds
written for a preflight at a prompt, and the relay's dispose joined its accept thread for five
seconds. Both now take two seconds when the stop is hurried, half of the four the session has, so
one call that hangs gives up in time for the steps behind it rather than spending the shutdown
alone.

The journal says how far a teardown got

The stop collected its steps and turned them into one line when it returned, so a teardown Windows
killed part way through left no trace that it had run at all. Read the next morning, the last three
shutdowns said only that the teardown had not finished, which the four-second line above them had
already said. Finding out which step it died in took the Windows System log, which is the wrong
place to have to go.

  • A hurried stop writes each step as it finishes, flushed there and then, so what got done
    survives the kill. The patient path keeps its single aggregate line.
  • The exit codes a session ending produces are named. 0xC0000142 reads as Windows refusing to
    initialise the process and 0x40010004 as Windows killing it, rather than arriving as 1073807364.
    The rest keep their number and gain the hex, because a reader who has to convert one by hand
    before they can search for it will not.
  • Each teardown line carries the process that wrote it, so the host's stop and the tray's
    backstop are told apart in a file they both append to.
  • A wsl call that exited non-zero and said nothing names its exit code, in all six places that
    used to spell their own version of that message.
  • A terminate that finds nothing registered says so, so a logoff on a machine that never
    provisioned an engine stops writing a failure about one.

Install

Download FreeWilly-Setup-1.0.14.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.13

Choose a tag to compare

@alegauss alegauss released this 31 Aug 22:39

Installing 1.0.12 over a running 1.0.11 left the engine down again, for the same reason as the
release before it and in a shape that release's own fix could not see.

A start no longer races the install that is replacing the engine

1.0.12 taught a start to launch the daemon again where the shell had exited 126, which is what a
shell says when it cannot execute a file another process holds open for writing. That covered
dockerd and nothing else.

Measured on 31 August 2026, upgrading 1.0.11 to 1.0.12: the shell executed dockerd perfectly,
dockerd then forked containerd against a file the installer still held, and the whole thing
came back as an ordinary exit 1. The retry never fired. The journal read:

the daemon exited while starting: wsl.exe exited 1, and /var/log/dockerd.log ends:
failed to start containerd: fork/exec /usr/local/bin/containerd: text file busy

The timestamps name the cause. The failure is stamped 15:44:32, and provision.log, which is
written when provisioning finishes, is stamped 15:44:34: the unpack was still running when the
start asked for a daemon. The engine tarball holds eight binaries and containerd is the third of
them, so four seconds in, dockerd had not been reached yet and the copy on disk was the previous
version's, intact and executable. That is why one ran and the other did not.

The provisioner now says when it is writing, and a start waits for it. The lock is held across
the distribution import and the engine unpack, and released the moment the last write lands. A
start waits up to twenty seconds for it, out of the budget it was already given rather than on top
of it, and the journal carries the wait only where there was one.

A start also relaunches on what the daemon's own log says, and not only on the exit code. Any
binary the daemon forks being busy now reads as the same condition, matched whichever way it is
spelled: the shell writes Text file busy and Go's os/exec writes it lowercased. The bound of
three attempts is unchanged, because that log is appended to across every launch a distribution has
ever had, and a stale line must not be able to retry forever.

Neither half replaces the other. Three pauses of 700ms cannot outlast an unpack of 85 MB, so the
retry alone would not have saved either failure that day; and a start cannot know what else on the
machine might hold a file, so the lock alone would cover nothing but this product's own installer.

Install

Download FreeWilly-Setup-1.0.13.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.12

Choose a tag to compare

@github-actions github-actions released this 31 Aug 16:07

The first full release since 1.0.10. Everything in the 1.0.11 pre-release is here, and the two
defects that pre-release turned up are fixed.

A container that succeeded is no longer reported as a failure

docker compose run, docker start -a and docker exec all returned 1 over a container that had
exited 0. The output arrived in full and only the ending was wrong, which is what made this look
like the container's own failure rather than something on the Windows side.

The pipe was the difference. Over the distribution's unix socket the same daemon and the same
container answered 0, so what changed the result was the relay tearing an upgraded connection down
with DisconnectNamedPipe. That call discards whatever the pipe still holds and answers the
client's next read with "no process is on the other end of the pipe". A response carrying a
Content-Length had been read whole long before that, so the error landed after the client already
had its answer and nothing ever noticed. On a hijacked stream the end of the stream is the result,
so every client that reads to the end reported a failure instead.

The teardown now drains and closes, and never disconnects. The drain returns once the client has
taken every byte, and closing the handle after it ends the pipe with ERROR_BROKEN_PIPE, which is
the ending every client reads as a stream that finished.

An upgrade no longer needs a second start

Installing 1.0.11 over a running 1.0.10 left the engine down. The journal said wsl.exe exited 126 without a word, and the cause was that the installer was still writing the engine into the
distribution when the new tray asked for a start: a shell cannot execute a file another process
holds open for writing, and it exits 126 saying so. Nothing tried again, so the engine stayed down
until it was started by hand.

A start now launches the daemon again where the launcher could not execute it, up to three
times, pausing between. The condition clears in well under a second, so the whole event becomes
invisible. Where it took a relaunch the line says so, because a retry that hides a failure should
not also hide itself.

A launch that failed quietly now says why

The same event proved a sentence wrong. The host reported the launcher as exiting "without a word"
while the words were sitting in /var/log/dockerd.log, which is where the launch command's own
redirection puts them, so the one failure that is always silent was also the one the host declined
to name a file for.

A silent launcher now has that log's last line read and quoted:

the daemon exited while starting: wsl.exe exited 126, and /var/log/dockerd.log ends:
/bin/sh: exec: line 0: /usr/local/bin/dockerd: Text file busy

A launcher that named its own cause is still not sent to the log, because handing a reader both a
cause and a file sends them to the file.

Nothing this product starts holds the directory it was launched from

A process holds a lock on its working directory for as long as it lives, and this product starts
children that live a long time: the engine host, the daemon's launcher, a VM hold, a terminal you
are about to work in. Given no directory of its own, each one held whichever directory its caller
happened to be in. For the engine host that is the install's own, so an update or an uninstall could
fail to replace a file, and the failure said nothing about a child process.

Every one of them now names its own. The single exception is the docker on your PATH, which has to
inherit yours: docker build ., -f compose.yaml and -v .\data:/data all resolve against the
directory you typed them in.

The relay says what it is still holding

The sentence the journal quotes when the engine has gone quiet gained a clause naming how many
channels to the daemon the relay has open, and it is silent at zero. A quiet engine holding forty
channels is this process; a quiet engine holding none is the daemon. Those two read identically
before, and every one of those channels is a wsl.exe attached to the daemon's socket.

What the suite drives now

The first defect on this page shipped with the suite green, because every check the relay had sent a
request carrying a Content-Length, which is the one shape it spared. So there is now a live drill:
it serves a relay built from the working tree, points a real docker at it, and asserts the exit
code each client reports across compose run, start -a, exec, attach and logs --follow,
including a container that fails. It also asserts that a client walking away from a follow takes its
channel with it rather than leaving one attached for the life of the host.

Install

Download FreeWilly-Setup-1.0.12.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.11

FreeWilly 1.0.11 Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 31 Aug 15:01

A container that succeeded is no longer reported as a failure

docker compose run, docker start -a and docker exec all returned 1 over a container that had
exited 0. The output arrived in full and only the ending was wrong, which is what made this look
like the container's own failure rather than something on the Windows side.

The pipe was the difference. Over the distribution's unix socket the same daemon and the same
container answered 0, so what changed the result was the relay tearing an upgraded connection down
with DisconnectNamedPipe. That call discards whatever the pipe still holds and answers the
client's next read with "no process is on the other end of the pipe". A response carrying a
Content-Length had been read whole long before that, so the error landed after the client already
had its answer and nothing ever noticed. On a hijacked stream the end of the stream is the result,
so every client that reads to the end reported a failure instead.

The teardown now drains and closes, and never disconnects. The drain returns once the client has
taken every byte, and closing the handle after it ends the pipe with ERROR_BROKEN_PIPE, which is
the ending every client reads as a stream that finished.

Nothing this product starts holds the directory it was launched from

A process holds a lock on its working directory for as long as it lives, and this product starts
children that live a long time: the engine host, the daemon's launcher, a VM hold, a terminal you
are about to work in. Given no directory of its own, each one held whichever directory its caller
happened to be in. For the engine host that is the install's own, so an update or an uninstall could
fail to replace a file, and the failure said nothing about a child process.

Every one of them now names its own. The single exception is the docker on your PATH, which has to
inherit yours: docker build ., -f compose.yaml and -v .\data:/data all resolve against the
directory you typed them in.

The relay says what it is still holding

The sentence the journal quotes when the engine has gone quiet gained a clause naming how many
channels to the daemon the relay has open, and it is silent at zero. A quiet engine holding forty
channels is this process; a quiet engine holding none is the daemon. Those two read identically
before, and every one of those channels is a wsl.exe attached to the daemon's socket.

What the suite drives now

The defect at the top of this page shipped with the suite green, because every check the relay had
sent a request carrying a Content-Length, which is the one shape it spared. So there is now a live
drill: it serves a relay built from the working tree, points a real docker at it, and asserts the
exit code each client reports across compose run, start -a, exec, attach and logs --follow, including a container that fails. It also asserts that a client walking away from a
follow takes its channel with it rather than leaving one attached for the life of the host.

Install

Download FreeWilly-Setup-1.0.11.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.10

Choose a tag to compare

@alegauss alegauss released this 31 Aug 13:09

Watching a run, without leaving the read surface

freewilly read logs <name> --follow closes the last hole in the read surface. Every diagnostic on
this machine was already a freewilly read except one: a database seed or a migration that prints
for two minutes still sent a session to docker compose logs -f, and a session that has dropped to
the docker CLI once tends to stay there for the next check too.

--until returns the moment the line arrives. read logs shop-db-1 --follow --until "database system is ready" is what read verify --wait already was for a port, applied to whatever the
container prints. It exits 0 when the line lands and 1 when it does not, so it works as a gate.

It is never open-ended. Three things bound it: the pattern, --timeout 90s, and the token
budget, so a container printing faster than the ceiling allows ends the follow rather than the
session's context. Following starts from now, because the run you want to watch is the one you are
about to make; --since <cursor> replays what came before.

Nothing prints until it returns. The reader is an agent, which sees stdout once the process has
ended, so a live scroll buys it nothing and --level, --dedup, the budget and the cursor all still
apply to the whole payload. Ctrl+C is a normal ending and keeps what was read.

A follow says which ending stopped it

A pattern that never arrives exits 1, and your next move differs by reason, so the last line names
which reason it was:

until   "seed complete" did not arrive in 90s
until   "seed complete" did not arrive, and the log ended
until   "seed complete" did not arrive, and the budget filled first: raise --budget, or --out to a file
until   "seed complete" had not arrived when this was stopped
until   "seed complete" did not arrive, and the container is gone

Only the first names a duration, because it is the only one that waited one.

A follow survives compose up. A log stream belongs to one container id and a recreate makes a
new one, so the stream ends while the service is running, printing, and about to print the line you
asked for. Where you addressed a role, meaning svc:<project>/<service> or a name, which compose
reuses, the follow crosses to the replacement and marks the seam in the payload:

O  migrating
--- svc:shop/api was replaced: aaaaaaaaaaaa -> bbbbbbbbbbbb ---
O  listening on :8080

It crosses once, because a service in a crash loop is recreated over and over. An id prefix names
one container and no other container is it, so there the follow ends.

Proving a path is gone

read verify --request answered one question: does this path answer. A 404 was therefore always a
failure, so the proof that a retired page had gone was the run that printed red, and the session had
to hold in its head that this particular red was the green one.

--expect turns the row into "this path answered what I named". read verify shop-web-1 --request /retired --expect 404 exits 0 and prints [ok], and --expect 404,410 takes either. A
missed expectation prints the status that arrived instead. --request with no --expect still means
2xx or 3xx, so nothing already written changes, and --wait now waits for a removal to take effect
as readily as for an arrival.

Fixes

A refusal names the value that would have worked. --request healthz used to answer "healthz is
not a path: it begins with a slash", stating the false half as a fact about what you typed.

Two help rows fit the terminal. read context and do reclaim ran past the width, and a folded
summary puts its tail in column 0, where a verb name goes.

A long follow costs what it reads. Matching a pattern across frame boundaries re-split the whole
buffer on every chunk, which is quadratic once --out lifts the token ceiling on purpose.

Install

Download FreeWilly-Setup-1.0.10.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.9

Choose a tag to compare

@alegauss alegauss released this 30 Aug 22:10

Compact reaches the end now

1.0.8 shipped the Compact button with a wall at the end of it. Windows has disabled the sparse
hand-back that wsl --manage <distro> --set-sparse true needs, so the button dropped the build
cache, trimmed the filesystem, and then explained a refusal instead of giving anything back.

This release gives it the route Windows leaves open. Where the hand-back is refused, the panel now
offers a compaction that runs diskpart under a single administrator prompt. It names the four
commands before it asks for the rights, the tray itself stays unelevated so only diskpart is raised,
and declining costs you nothing.

Shutting WSL down is part of the work. diskpart cannot open a virtual disk the WSL2 machine is
still holding, and terminating the distribution does not release it. The run shuts WSL down, waits
for Windows to let go of the file, and names every distribution that will stop before it starts. On
the first disk it ran against, 21.4 GB came back.

A run you can watch

Steps appear as they land. The panel used to say it was compacting and then say nothing for
minutes, so a working run looked exactly like a hung one. Both the ordinary and the elevated route
now draw each step as it finishes.

The longest step has a number. diskpart writes its own percentages into a log while it works,
and the panel reads them out of the file it is still writing. The match is on the number, so a
machine running in another language shows the same climb.

A failure names the step that decided the run. A compaction whose prune failed reported the
prune as its outcome, which hid the withdrawn hand-back underneath it.

Fixes

The prune inside a compaction gets ten minutes. It ran under the twenty seconds written for
asking a stopped engine whether it is there, so the fuller the disk the surer that step failed. The
window's own prune buttons had inherited the same budget, where a big machine got a timeout instead
of a reclaim.

An install over a running tray stops it first. It used to leave that to Restart Manager, which
can close neither a tray nor the engine it detached.

The site fits a phone. The nav measured 625px at a 390px viewport; every route now measures zero
sideways overflow from 320px up.

For diagnosing this tool

--compact runs the compaction from a terminal, through the same seam the button reaches. Add
--as-administrator and it takes the elevated route, prompt and WSL shutdown included.
--compact-drill rehearses the whole thing on a scratch disk grown and emptied on purpose, and it
takes the same flag, so the elevated route can be answered without going near your engine's disk.

--drive-window --compact works the compaction through the real window and prints what it says.
--drive-window on its own used to never return when no window was open, which is the machine it
was written to serve; it now takes about 2.4 seconds.

Install

Download FreeWilly-Setup-1.0.9.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.8

Choose a tag to compare

@github-actions github-actions released this 30 Aug 13:06

The filesystem check is finished, and Compact is waiting on Windows

1.0.6 added the check and the repair. 1.0.7 fixed the way its rescue was torn down. This release is
the rest of it: the check asks before it interrupts anything, stops reporting its own stop as a
failure, puts Docker back when it finishes, and needs no network after the first run.

Beside it is a new Compact button that Windows currently refuses. That is explained below rather
than left for you to find.

The check

It asks before it interrupts anything. Docker stops for the duration, and the dialog says so
before you agree rather than in a sentence beside the button. It starts Docker again at the end.

A stop you asked for is no longer announced as an outage. Running freewilly --fsck or
--stop from a terminal left the tray watching an engine that had gone away, and fifteen seconds
later announcing a failure about a stop you had just typed. Verbs now tell the tray what the window
already told it. The verb still leaves the engine where you asked, and names the command that starts
it again.

Only the first check needs a network. The rescue distribution used to fetch e2fsprogs every
time, which is a download at the moment something is already wrong. It now keeps the prepared
filesystem beside the install, about 11 MB, and imports that instead. A check works on a machine
whose network is part of what is broken. The dialog names that first download instead of blaming the
size of your disk for the wait.

What e2fsck printed is in the order it printed it. Its version banner used to land under the
summary line it belongs above, because the two output streams were appended rather than interleaved.

A clean verdict no longer contradicts the transcript beneath it. e2fsck can exit zero having
printed complaints, and the panel said "Nothing needed mending" directly over them.

Compact, and why it does nothing yet

The Engine page has a Compact button beside Check filesystem. It drops the build cache, trims
the filesystem, and hands the freed blocks back to Windows with
wsl --manage <distro> --set-sparse true.

Windows currently refuses that last step. Sparse VHD support is disabled over possible data
corruption, and the only way past it is --allow-unsafe, which this will not pass on a disk holding
every image and volume you have. So the button says Windows has turned this off, in those words,
rather than handing you WSL's error text. Once a machine has refused, the plan says so before it
costs you an interruption instead of after one, and asks whether to try anyway: the flag is disabled
rather than removed, and may come back.

The panel now reads the disk from both sides, because a virtual disk handed back to Windows becomes
a sparse file and keeps its length. What the file says it is and what the volume is charging for are
different numbers, and only the second one moves when a compaction works.

Fixes

The preflight stopped calling your own engine a rival. With FreeWilly running, the Container
engine row went red saying an unidentified engine held \\.\pipe\docker_engine and telling you to
uninstall it first. --provision refused with it, so a repair install on a working machine was
blocked by the thing it was repairing.

An uninstall takes the rescue image with it, and an Alpine version bump no longer leaves the old
one on disk.

An interrupted check no longer blocks the next one. A run killed part-way through left its
temporary distribution registered, and the next run stopped on ERROR_ALREADY_EXISTS. A run that
meets a leftover of its own now clears it and says so.

For diagnosing this tool

Three verbs, each saying what it touches. --fsck-drill rehearses the repair on a scratch ext4
image dirtied on purpose. --compact-drill rehearses the compaction on a scratch disk grown and
emptied on purpose. Neither goes near your engine or its disk. --drive-window works the real window
through UI Automation and prints what it says; with --check it drives the real filesystem check,
which does stop Docker.

Install

Download FreeWilly-Setup-1.0.8.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.

FreeWilly 1.0.7

Choose a tag to compare

@alegauss alegauss released this 29 Aug 18:53

The filesystem check could leave WSL unable to start anything

1.0.6 added a check and a repair for the engine's own filesystem. Running either of them could put
the whole WSL installation into a state where nothing starts, including distributions this product
has never touched. This release is that fix, and nothing else.

Upgrade before running the check again. If you are on 1.0.6, do not press Check filesystem
on the Engine page and do not run freewilly --fsck until you have installed this.

What went wrong

The check runs e2fsck from a temporary distribution called freewilly-rescue, because a root
filesystem cannot check itself. That rescue holds the virtual machine open with a running process,
which is the part that keeps the engine's disk attached after its distribution is terminated, and
the teardown disposes that hold before unregistering the rescue.

Disposing it kills the Windows side of the process and not the process inside the distribution, so
WSL still counts the rescue as running. Unregistering a running distribution is not refused. It is
accepted, the distribution moves to state 4, uninstalling, and the service then blocks on something
that never stops.

Everything WSL is asked to do afterwards queues behind that. wsl --list and wsl --shutdown stop
returning, and starting the engine fails as the daemon exited while starting: wsl.exe exited 1 without a word, which is the launcher being unable to reach a service that is no longer answering.
Nothing is damaged: the engine's own distribution stays registered and its disk stays intact. It
simply cannot be started, and neither can anything else, until the WSL service is restarted with
administrator rights.

The rescue is now terminated before it is unregistered. A terminate stops what is inside and
returns, and unregistering a stopped distribution is the case WSL handles.

If a machine is already stuck

The upgrade cannot clear a rescue that is already wedged, because every WSL call is queued behind
it. From an elevated PowerShell:

Stop-Service WSLService -Force   # if this hangs: taskkill /F /IM wslservice.exe
Start-Service WSLService
wsl --unregister freewilly-rescue

That is what recovered the machine this was found on. The engine's distribution, its images and its
volumes are untouched by all of it.

Install

Download FreeWilly-Setup-1.0.7.exe and check it against SHA256SUMS.txt. Upgrading keeps the
distribution and everything in it.