Skip to content
Jay Logue edited this page Oct 13, 2022 · 3 revisions

retro-fuse implements a standard FUSE (Filesystem in Userspace) application. It uses the high-level API exposed by the libfuse library to service file I/O requests from the host kernel, translating those requests into the appropriate operations on the underlying ancient Unix filesystem.

The design of retro-fuse is novel in that it incorporates original Unix source code as the means to access ancient filesystems. The ancient code has been "lightly modernized" to be palatable to current compilers and adapted to run in userspace, but otherwise remains unchanged. This approach (made possible by Caldera’s generous open source license for ancient Unix) allows retro-fuse to support both reading and writing of ancient filesystems, while ensuring the historical fidelity of on-disk structures.

retro-fuse provides support for a broad array of modern filesystem operations on top of the basic capabilities of the original code. This includes support for modern rename(2) semantics, filesystem statistics gathering (e.g. via statfs(2) / df(1)) and symlinks (where appropriate). The code also provides a C API with semantics that match modern Unix syscalls, making it possible to incorporate ancient filesystem access directly into other projects.

Architecture

retro fuse architecture

retro-fuse is organized as a set of layers which handle I/O operations at different levels of abstraction. With the exception of the top-most and bottom-most layers (libfuse and dsk), a distinct version of each layer exists for each of the supported filesystems.

At the top of the stack is the libfuse library provided by the FUSE project. This module receives file I/O operations from the host kernel and dispatches them into the retro-fuse FUSE application layer.

The FUSE application layer is responsible for receiving callbacks from libfuse and making the appropriate calls into the filesystem API layer to perform the requested operation. In many cases these calls are one-to-one with the operation being performed (e.g. an "open file" request from libfuse ends up calling an open() function in the filesystem API). However the FUSE application layer is also responsible for conveying any context information that may influence the operation, such as the real and effective uids/gids of the process making the request. The application layer also handles application initialization and termination, parsing command line options, and performing administrative tasks such as initializing a new filesystem.

The Filesystem API layer provides an API for accessing files on ancient filesystems which closely mimics the modern Unix filesystem API. The notible exception from traditional Unix APIs is that errors are returned as return values rather than via errno. The code in the API layer maps the modern API operations onto the apppropriate function calls in the ancient Unix kernel layer. In some ways, this code is very similar to ancient C library code that performed kernel system calls, just without the syscall mechanism.

The Ancient Unix kernel layer contains the filesystem implementation and supporting source code from the original version of Unix. Modifications to the original kernel code are purposefully minimal, and consist mostly of syntactical and type compatibility changes. Additionally, a series of #defines and selective hand editing is used to add a prefix for functions and global variables (e.g. "v6_"), so as to avoid conflicts with similarly named modern constructs.

The ancient kernel layer relies on a Unix adapter sublayer, which provides replacements for various ancient Unix functions that either require significantly different behavior in the retro-fuse context, or were originally written in assembly. The signatures of these functions are preserved in order to mimimize changes to the original kernel source.

Finally, the dsk layer provides a simple abstraction of a virtual block-oriented disk device which the upper layers use to implement block I/O. This layer supports filesystems contained in image files as well as on host block devices (e.g. a MicroSD card).

Source Organization

ancient-src/v6/*, ancient-src/v7/*, …​

Ancient Unix kernel — Lightly modernized copy of ancient Unix source code.

src/v6adapt.[ch], src/v7adapt.[ch], …​

Unix adapter — Replacements for select ancient Unix functions.

src/v6fs.[ch], src/v7fs.[ch], …​

Filesystem API layer — Modern style API for accessing files in ancient filesystems.

src/fusecommon.c, src/v6fuse.c, src/v7fuse.c, …​

FUSE app — Main program implementing the FUSE filesystem handler.

src/dsk.[ch]

dsk layer — Virtual block-oriented disk device.

Test Code Organization

test/retro-fuse-test.py

test driver — Main script for testing filesystem handler programs. Automatically selects and invokes the appropriate tests based on the type of filesystem handler specified.

test/V6Tests.py, test/V7Tests.py, …​

filesystem-specific tests — Filesystem functionality tests tailored to each filesystem type.

test/*.py

test framework code — Various python modules implementing a reusable framework for testing ancient filesystems.

test/system-images/*

test system images — Prebuilt ancient Unix system disk images for testing filesystem integrity.

test/ancient-cksum/*.c

ancient cksum code — Implementations of POSIX-compliant cksum command for use on ancient Unix systems.

Clone this wiki locally