Skip to main content

Filesystem information

Windows 93 uses IndexedDB to store files. There exist two buckets, each with one object store called store. fileindex is first initialized as a dump of /files.cbor. fs is all .desktop files, etc. Each file is given a unique identifier, and is stored as a File object.

Creating a driver

Driver architecture

The runtime selects a storage backend from mount points and delegates file operations to a driver.

Core flow:

  1. FileSystem resolves a path to a mounted driver name in 42/api/fs.js#L76.
  2. Driver is loaded lazily through 42/api/fs/getDriverLazy.js#L5.
  3. Files are represented by inodes in FileIndex.
  4. Inode format for stored files is [id, mask, times] (written in 42/api/fs/class/BrowserDriver.js#L196).
  5. mask identifies which backend owns that file. Cross-driver delegation is handled in 42/api/fs/class/BrowserDriver.js#L141 and 42/api/fs/class/BrowserDriver.js#L247.

mask

A mask is a numeric backend identifier stored in each inode.

Mask mapping is defined in 42/api/fs/FileIndex.js#L7.

Current builtins:

  • 0x10 -> memory
  • 0x12 -> localstorage
  • 0x13 -> indexeddb
  • 0x14 -> opfs

A new driver should have a unique mask value. Do not follow the sequential order above.

Simple implementation

The simplest path is to extend BrowserDriver in 42/api/fs/class/BrowserDriver.js.

tip

Don't extend Driver.js

You must provide:

  • mask (number)
  • store object with storage primitives compatible with BrowserDriver

Expected store methods:

  • has(id) -> boolean | Promise<boolean>
  • get(id) -> Blob | File | undefined | Promise<...>
  • set(id, data) -> void | Promise<void> where data is usually File/Blob
  • delete(id) -> void | Promise<void>

BrowserDriver handles:

  • inode metadata updates (b/a/c/m times)
  • symlink and directory checks
  • cross-driver delegation by mask
  • stream wrappers (sink/source)

Example

Create a file under 42/api/fs/driver, named using the <name>Driver.js convention.

The lazy loader imports by name pattern in 42/api/fs/getDriverLazy.js#L13, so naming must match.

Example skeleton:

import { BrowserDriver } from "../class/BrowserDriver.js"

class MyBackendDriver extends BrowserDriver {
mask = 0x19

async init() {
// optional: feature detection or fallback
// if unsupported: return this.getDriver("indexeddb")

this.store = {
has: async (id) => {
// return true/false
},
get: async (id) => {
// return File/Blob or undefined
},
set: async (id, data) => {
// persist data
},
delete: async (id) => {
// remove entry
},
}

return super.init()
}
}

export const driver = (...args) => new MyBackendDriver(...args).init()

Driver Registration

Add your new mask entry in 42/api/fs/FileIndex.js#L7.

Example:

0x15: "mybackend",

Rules:

  • mask must be unique
  • name should match your filename prefix (mybackendDriver.js)

Eager loading vs lazy loading

Eager loading imports all drivers up front - basically, faster first-use access at the cost of higher startup time/memory. Lazy loading loads driver code only when a mounted path first needs it.

If code paths use eager loading via 42/api/fs/getDriver.js, also:

  1. import the driver module
  2. add it to modules

If your runtime always uses lazy loading, this is optional, but keeping both loaders aligned would be recommended.

Mounting

Mount points are configured in 42/api/fs.js#L15.

Default root mount currently points to IndexedDB in 42/api/fs.js#L17.

Options:

  • global mount: set places: { "/": "mybackend" } in fs.js
  • scoped mount: call fs.mount("/some/path", "mybackend")

Longest matching mount path wins (see sort logic in 42/api/fs.js#L104).

For browser-dependent APIs, implement feature detection in init() and fallback to IndexedDB when unavailable.

Reference behavior in 42/api/fs/driver/opfsDriver.js#L8.

misc info

Your backend must follow these expectations:

  • IDs are opaque and generated by uid() in BrowserDriver.
  • Data written through write() is a File object
  • get() should return an object compatible with Blob APIs (arrayBuffer(), stream()).
  • Missing entries should return undefined where possible. DO NOT THROW!!
  • delete() should be able to tolerate missing keys.

If things go wrong, here's where you messed up probably:

  • mask not registered in 42/api/fs/FileIndex.js#L7
  • returning non-Blob-compatible objects from store.get
  • forgetting return super.init() after setting up store

Refer to these files

  • 42/api/fs.js
  • 42/api/fs/class/BrowserDriver.js
  • 42/api/fs/getDriverLazy.js
  • 42/api/fs/getDriver.js
  • 42/api/fs/FileIndex.js
  • 42/api/fs/driver/indexeddbDriver.js
  • 42/api/fs/driver/opfsDriver.js
  • 42/api/fs/driver/localstorageDriver.js
  • 42/api/fs/driver/memoryDriver.js