Skip to content

Block Device Layer

opencode-agent[bot] edited this page May 10, 2026 · 2 revisions

Block Device Layer

The block I/O layer provides byte-level read/write/flush operations on storage devices, with support for partition mapping, sector alignment, and filesystem probing.

Overview

The block device layer sits between physical storage drivers (IDE, SCSI, USB, RAMdisk) and higher-level consumers (filesystems, the VFS layer). It defines a hierarchy of APIs that add increasingly specific capabilities:

  1. BlockDeviceAPI — base contract for raw block I/O
  2. PartitionableBlockDeviceAPI — adds partition table access and sector size queries
  3. FSBlockDeviceAPI — for devices that may contain a filesystem

Wrapper classes inject cross-cutting concerns (alignment, offset-mapping) without altering the underlying driver. This design isolates physical device specifics from higher-level code.

Key Components

Class / File Role
fs/src/driver/org/jnode/driver/block/BlockDeviceAPI.java Base interface: read, write, flush, getLength
fs/src/driver/org/jnode/driver/block/PartitionableBlockDeviceAPI.java Adds getSectorSize, getPartitionTable
fs/src/driver/org/jnode/driver/block/FSBlockDeviceAPI.java Adds getSectorSize, getPartitionTableEntry
fs/src/driver/org/jnode/driver/block/BlockAlignmentSupport.java Wraps a BlockDeviceAPI, enforcing sector-aligned reads/writes
fs/src/driver/org/jnode/driver/block/FSBlockAlignmentSupport.java Wraps an FSBlockDeviceAPI, adding alignment
fs/src/driver/org/jnode/driver/block/PartitionableBlockAlignmentSupport.java Wraps a PartitionableBlockDeviceAPI, adding alignment
fs/src/driver/org/jnode/driver/block/MappedBlockDeviceSupport.java Virtual sub-device: maps an offset/length region of a parent device
fs/src/driver/org/jnode/driver/block/MappedFSBlockDeviceSupport.java FSBlockDeviceAPI-aware version of mapped support
fs/src/driver/org/jnode/driver/block/BlockDeviceAPIHelper.java Static helpers: checkBounds, checkAlignment
fs/src/driver/org/jnode/driver/block/FileDevice.java FSBlockDeviceAPI backed by a host file (for testing)
fs/src/driver/org/jnode/driver/block/ByteArrayDevice.java BlockDeviceAPI backed by an in-memory byte array
fs/src/driver/org/jnode/driver/block/JarFileDevice.java Read-only FSBlockDeviceAPI backed by a JAR
fs/src/driver/org/jnode/driver/block/Geometry.java CHS geometry descriptor (cylinders/heads/sectors)
fs/src/driver/org/jnode/driver/block/CHS.java Cylinder-head-sector value holder
fs/src/driver/org/jnode/driver/block/ide/, scsi/, floppy/, usb/, ramdisk/ Physical device driver implementations

How It Works

API Hierarchy

The three interfaces form a linear hierarchy:

DeviceAPI
    └── BlockDeviceAPI
            ├── PartitionableBlockDeviceAPI<PTE>
            └── FSBlockDeviceAPI

BlockDeviceAPI is the contract every block device must implement:

public interface BlockDeviceAPI extends DeviceAPI {
    long getLength() throws IOException;
    void read(long devOffset, ByteBuffer dest) throws IOException;
    void write(long devOffset, ByteBuffer src) throws IOException;
    void flush() throws IOException;
}

PartitionableBlockDeviceAPI extends this for devices with MBR/GPT partition tables:

public interface PartitionableBlockDeviceAPI<PTE extends PartitionTableEntry>
    extends BlockDeviceAPI {
    int getSectorSize() throws IOException;
    PartitionTable<PTE> getPartitionTable() throws IOException;
}

FSBlockDeviceAPI extends BlockDeviceAPI for devices that may hold a filesystem. Unlike PartitionableBlockDeviceAPI, it is used by virtual partition devices (created via MappedFSBlockDeviceSupport):

public interface FSBlockDeviceAPI extends BlockDeviceAPI {
    int getSectorSize() throws IOException;
    PartitionTableEntry getPartitionTableEntry();
}

Partition Handling

Physical devices (IDE disk, SCSI disk) implement PartitionableBlockDeviceAPI. The getPartitionTable() method reads the MBR/GPT from the device and returns a parsed PartitionTable. Each PartitionTableEntry contains the start LBA, length, and type of a partition.

From each entry, JNode creates a virtual child device using MappedFSBlockDeviceSupport:

// Inside PartitionManager or DeviceManager:
for (PartitionTableEntry entry : partitionTable.getEntries()) {
    MappedFSBlockDeviceSupport partDevice =
        new MappedFSBlockDeviceSupport(parentDevice, entry.getStartLBA() * sectorSize,
            entry.getLength());
}

This virtual device presents an FSBlockDeviceAPI to upper layers. The offset is pre-adjusted so callers see the partition's logical address space starting at zero. Reads/writes are transparently redirected to the parent device at offset + devOffset.

Alignment Support

Physical devices often require I/O requests aligned to the sector size (typically 512 or 4096 bytes). The BlockAlignmentSupport wrapper handles this by decomposing unaligned requests into a combination of HEAD, BODY, and TAIL operations:

  • HEAD — partial block before the first aligned boundary
  • BODY — fully aligned blocks in the middle
  • TAIL — partial block after the last aligned boundary

The read and write paths detect four buffer states:

  • EMPTY — zero-length request, no I/O
  • ALIGNED — offset and length are multiples of alignment, direct pass-through
  • CONTAINED — request fits within a single aligned block, read-modify-write
  • CROSSED — spans multiple aligned blocks, HEAD + BODY + TAIL

This minimizes buffer copies: at most 2 auxiliary reads/writes (HEAD + TAIL) are needed; the BODY passes directly to the underlying device.

Block Drivers → Filesystem Relationship

The relationship is layered:

Physical Driver (IDE, SCSI)
  └── implements PartitionableBlockDeviceAPI
        └── BlockAlignmentSupport (wraps, adds alignment)
              └── MappedFSBlockDeviceSupport (creates partition view)
                    └── registers FSBlockDeviceAPI
                          └── FileSystemType.probe(device)
                                └── creates FileSystem

The filesystem driver (e.g., FAT, ext2, ISO9660) calls FileSystemType.create(device, readOnly), passing a device that exposes FSBlockDeviceAPI. The FileSystemType implementation probes the device by reading its superblock and checking known magic bytes.

Concrete FSBlockDeviceAPI implementations:

  • FileDevice — backed by a host OS file, used in boot image and testing
  • JarFileDevice — backed by a JAR entry (read-only)
  • MappedFSBlockDeviceSupport — virtual device for partitions

Concrete BlockDeviceAPI implementations:

  • ByteArrayDevice — in-memory block device, used in tests
  • Physical drivers in ide/, scsi/, usb/, ramdisk/, floppy/

Gotchas

  • FSBlockDeviceAPI vs PartitionableBlockDeviceAPI: These are parallel hierarchies, not substitutes. Physical partitioned devices implement PartitionableBlockDeviceAPI; their virtual partition children implement FSBlockDeviceAPI. A filesystem driver should expect FSBlockDeviceAPI, not PartitionableBlockDeviceAPI.
  • Offset translation: When reading from a MappedBlockDeviceSupport, the passed offset is relative to the partition start, not the start of the whole device. This is handled by the mapping class itself, delegating offset + devOffset to the parent.
  • Alignment is a wrapper, not automatic: Drivers do not automatically enforce sector alignment. Code that needs alignment must explicitly wrap with BlockAlignmentSupport or FSBlockAlignmentSupport.
  • ByteArrayDevice read is non-seekable: It copies bytes directly from the array using System.arraycopy with a cast to int offset. Works for small in-memory devices but cannot address byte ranges beyond Integer.MAX_VALUE.
  • Partition table parsing: getPartitionTable() can return null if no valid partition table is found (e.g., unformatted disk or newer GPT not yet supported).

Related Pages

Clone this wiki locally