# How LiteBox Manages File Descriptors Across Linux and Windows Platforms

> Discover how LiteBox manages file descriptors across Linux and Windows. Learn about its unified abstraction using FilesState for consistent syscalls on diverse platforms.

- Repository: [Microsoft/litebox](https://github.com/microsoft/litebox)
- Tags: how-to-guide
- Published: 2026-02-19

---

**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:

```rust
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`](https://github.com/microsoft/litebox/blob/main/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`](https://github.com/microsoft/litebox/blob/main/litebox_shim_linux/src/syscalls/file.rs) demonstrates the standard open workflow:

```rust
// 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:

```rust
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`](https://github.com/microsoft/litebox/blob/main/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`](https://github.com/microsoft/litebox/blob/main/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.