How LiteBox Manages File Descriptors Across Linux and Windows Platforms

LiteBox implements a unified file descriptor abstraction through a centralized FilesState structure that maps guest-facing FDs to host-specific raw handles, enabling consistent syscall semantics across Linux kernel shims, Linux userland, and Windows userland platforms.

LiteBox, Microsoft's lightweight container runtime, solves the cross-platform file descriptor problem by decoupling guest-visible FD numbers from host-specific handle representations. The architecture centers on the FilesState struct in the microsoft/litebox repository, which uses lock-protected maps to maintain the relationship between portable Descriptor enums and platform-specific raw integers. This design ensures that operations like CLONE_FILES, fcntl flag manipulation, and resource cleanup behave identically whether the underlying host uses Linux file descriptors or Windows handles.

Core Architecture: The FilesState Abstraction

At the heart of LiteBox's FD management lies the FilesState<FS> struct, defined in the Linux shim layer. This generic structure maintains two synchronized storage mechanisms protected by read-write locks:

pub struct FilesState<FS: ShimFS> {
    pub file_descriptors: litebox::sync::RwLock<Platform, Descriptors<FS>>,
    pub raw_descriptor_store: litebox::sync::RwLock<Platform, litebox::fd::RawDescriptorStorage>,
}

The file_descriptors field maps guest-facing file descriptor numbers (u32 values) to Descriptor<FS> enum variants that describe the resource type. The raw_descriptor_store maintains a separate mapping from these internal descriptors to host-specific raw integers, such as Linux file descriptors or Windows HANDLE values. This separation allows the shim to present a consistent POSIX-like interface to the guest while translating operations to platform-native APIs.

The Descriptor Enum and Raw Storage

The Descriptor enum in litebox_shim_linux/src/lib.rs abstracts all supported resource types behind a unified interface:

  • LiteBoxRawFd(RawFd) – Regular files, sockets, and pipes backed by standard file descriptors
  • Eventfd { file, close_on_exec } – Linux event file descriptors
  • Epoll { file, close_on_exec } – Linux epoll instances
  • Unix { file, close_on_exec } – Unix domain sockets

When a task opens a file via sys_open, the shim obtains a LiteBox internal FD from the filesystem implementation, converts it to a raw integer using fd_into_raw_integer(), and wraps it in the appropriate Descriptor variant before insertion into the guest-visible table. This process isolates the guest from host-specific handle details while preserving the ability to perform native I/O operations.

Platform-Specific Implementations

Linux Shim and Userland

On Linux platforms, both the kernel shim (litebox_shim_linux) and userland platform (litebox_platform_linux_userland) share the same FilesState implementation. The userland platform reuses the shim's descriptor logic by providing a ShimFS implementation that forwards to the host kernel.

The syscall implementation in litebox_shim_linux/src/syscalls/file.rs demonstrates the standard open workflow:

// Inside Task::sys_open
let file = self.global.fs.open(path, flags - OFlags::CLOEXEC, mode)?;
let raw_fd = files.raw_descriptor_store.write().fd_into_raw_integer(file);
let guest_fd = files.file_descriptors
    .write()
    .insert(self, Descriptor::LiteBoxRawFd(raw_fd))?;
Ok(guest_fd)

When closing descriptors, sys_close acquires a write lock on the descriptor table, removes the entry, and invokes do_close() to release both the raw descriptor and underlying resource. This ensures atomic cleanup across shared descriptor tables when CLONE_FILES is enabled.

Windows Userland

The Windows userland platform (litebox_platform_windows_userland) adapts the same abstraction to Windows handles. Rather than using native Windows file descriptor tables, LiteBox stores Windows HANDLE values as raw integers within the shared RawDescriptorStorage. The Descriptor::LiteBoxRawFd variant wraps these handles, allowing the shim's existing code paths to function unmodified.

Unsupported descriptor types on Windows—such as Eventfd and Epoll—return ENOSYS or EINVAL from their respective syscall handlers. The platform implements a PunchthroughProvider for privileged operations like SetFsBase, but FD-related operations flow through the standard FilesState interface, ensuring that FD_CLOEXEC and other flags are managed consistently with the Linux implementation.

FD Lifecycle and Concurrency

LiteBox handles descriptor duplication and inheritance through the FilesState::Clone implementation. When a task is created with the CLONE_FILES flag, the new task receives a shallow copy of the parent's FilesState, causing both tasks to share the same underlying RwLock instances. Consequently, closing a descriptor in one task immediately invalidates it for the other, matching Linux kernel semantics.

Flag management operates through the global descriptor table accessed via descriptor_table_mut(). The sys_fcntl handler updates flags like FD_CLOEXEC using helper methods on the Descriptor enum:

let desc = file_table.get_fd(fd).ok_or(Errno::EBADF)?;
desc.set_file_descriptor_flags(&self.global, &files, flags)?;

This approach centralizes flag storage while allowing platform-specific validation of supported operations.

Summary

  • Unified abstraction: The FilesState struct provides a single interface for FD management across Linux kernel shims, Linux userland, and Windows userland platforms.
  • Type safety: The Descriptor enum abstracts platform-specific resources (epoll, eventfd, Windows handles) behind a consistent API.
  • Raw descriptor mapping: RawDescriptorStorage maintains the mapping between LiteBox-internal representations and host-native handles (Linux FDs or Windows HANDLEs).
  • POSIX compatibility: CLONE_FILES inheritance, FD_CLOEXEC semantics, and atomic close operations work identically across all supported platforms.
  • Platform limits: Windows implementations return ENOSYS for Linux-specific descriptor types like Eventfd and Epoll.

Frequently Asked Questions

How does LiteBox handle CLONE_FILES across different platforms?

When a LiteBox task creates a new task with the CLONE_FILES flag, the FilesState structure is shallow-copied, causing both tasks to share the same RwLock-protected descriptor tables. This implementation in litebox_shim_linux/src/lib.rs ensures that file descriptor modifications in one task are immediately visible to the other, matching Linux kernel semantics on both Linux and Windows hosts.

What happens to file descriptors when a LiteBox task closes them?

The sys_close implementation in litebox_shim_linux/src/syscalls/file.rs acquires a write lock on file_descriptors, removes the Descriptor entry, and calls do_close() to release the underlying resource. This atomically removes the mapping from both the guest-visible table and the raw_descriptor_store, ensuring the host handle is properly closed regardless of platform.

Does LiteBox support epoll and eventfd on Windows?

No. While the Descriptor enum defines variants for Epoll and Eventfd, the Windows userland platform does not implement backing support for these Linux-specific mechanisms. Syscalls attempting to create or manipulate these descriptor types return ENOSYS or EINVAL, as the Windows platform only supports LiteBoxRawFd variants backed by Windows file handles.

How are file descriptor flags like FD_CLOEXEC managed in LiteBox?

LiteBox stores descriptor flags in a global descriptor table accessed via litebox::litebox::descriptor_table_mut(). The sys_fcntl handler retrieves the Descriptor from FilesState and invokes platform-specific helpers like set_file_descriptor_flags() to update the flag state. This ensures that O_CLOEXEC, O_NONBLOCK, and other flags persist across operations while remaining synchronized with the underlying host handle's state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →