dcmfree

A small Windows utility to reclaim disk space from orphan HCS container layers, built as an excuse to use the windows-rs crate.

I wrote dcmfree to clear some disk space that seemingly there was no other way of removing. It gave me the opportunity to build with the Windows native APIs via the windows crate.

The space in question was in C:\ProgramData\Microsoft\Windows\Containers\Layers. This directory accumulates layer folders left behind by modern Docker (in Windows-containers mode), Windows Sandbox sessions, and Hyper-V isolated containers — all of which route their storage through the Host Compute Service. Each layer folder is full of NTFS reparse points and hardlinks. del and Remove-Item refuse to remove them, even from an elevated shell, because traversing the reparse points and hardlinks requires SeBackupPrivilege and SeRestorePrivilege, and the supported way to actually delete a layer is the HCS HcsDestroyLayer API rather than the normal file APIs.

The existing tools I could find for this — docker-ci-zap and friends — target the legacy C:\ProgramData\Docker\windowsfilter store, not the HCS-managed path that modern Docker, Sandbox, and Hyper-V actually use.

What it does

dcmfree is a single binary that is both a native Win32 GUI and a CLI:

  • Enumerates every layer under the HCS layers directory and reports its size, file count, age, and whether it is still referenced by an active compute system.
  • Cross-checks against docker images / docker inspect to skip layers Docker reports as in use.
  • Enables SeBackupPrivilege and SeRestorePrivilege on its own token, then calls HcsDestroyLayer per orphan layer.
  • Has a --dry-run, a --min-age filter (default 1 day), and an explicit confirm before anything destructive.

The GUI shows a ListView of layers with multi-select, runs the scan and destroy work on a worker thread, and keeps the message loop responsive so the Cancel button actually cancels.

The dcmfree GUI: a native Win32 window titled 'dcmfree - container layer cleanup' with Refresh, Select orphans, Clear selection and Active HCS systems buttons, a summary line reading 'Layers: 3  Orphans: 3 (156 B)  On disk: 156 B  Active HCS: 0  Selected: 0 (0 B)', and a ListView of three orphan layers showing size, file count, age, created and modified timestamps, status and ID columns.

The GUI listing three orphan layers under the HCS layers directory, each reported with its size, age, status, and ID.

What I learned from windows-rs

The windows crate exposes Win32 as typed Rust bindings generated from the official Windows metadata. A few things stood out:

  • HRESULT results auto-convert into windows::core::Result, so the FFI surface ends up looking like ordinary Rust error handling.
  • Handles like HCS_OPERATION and HLOCAL are proper newtypes, not *mut c_void, which makes it much harder to mix them up.
  • PCWSTR / PWSTR make NUL termination a type invariant. You still have to own the underlying UTF-16 buffer for the duration of the call, but the type signature is honest about what it wants.
  • Module organisation does not always follow the C headers. Microsoft documents HcsDestroyLayer as living in ComputeStorage.dll, so I went looking for a HostComputeStorage module in windows 0.60 — there isn’t one. The function is there, just bundled into windows::Win32::System::HostComputeSystem alongside everything else with an Hcs prefix.

Every Win32 handle is RAII-wrapped (OwnedToken, HcsOperation, OwnedComputeSystem, OwnedLocalAlloc), so a panic mid-destroy still closes everything. Every unsafe block has a // SAFETY: comment. It is a small program but it gave me a reason to be careful about what the operating system actually owns and what Rust does.

The source and prebuilt binaries are on GitHub. MIT licensed.