File device

This is the specification for the file device of the Bedrock computer system.

This document is aimed at people who are implementing the Bedrock system from scratch. For people who are learning about or writing program for the Bedrock system, the file device manual will generally be more useful.

Concepts

Filesystem entry

A filesystem entry is an object in the filesystem, and is either a file or a directory. A file is a named block of zero or more bytes. A directory is a named list of zero or more child entries. An entry must be addressable by at least one valid filesystem path, otherwise it will be ignored by the file device. The maximum size of a file or directory is determined by the implementation.

Filesystem path

A filesystem path is a UTF-8 encoded string representing the address of a filesystem entry. An empty string represents no path.

A path separator is a single forward-slash character 0x2F. A path component is a string representing the name of a filesystem entry. A path component must be at least one character long, and cannot contain the forward-slash character 0x2F or any character from 0x00 to 0x1F.

A path must begin with a path separator, followed by zero or more alternating path components and path separators. A path containing a path segment cannot end with a path separator. A path cannot exceed 255 bytes in length. If a path does not meet all of these requirements, it is invalid.

Current entry

The current entry is the filesystem entry that is currently open. Initially no entry will be open.

When the current entry is closed, the path buffers associated with the open, action, path, and child path ports are cleared, the entry address is set to zero, and the selected child is deselected.

Path buffer

A path buffer is a 256 byte block of writeable memory with an associated pointer. The initial value of each byte is zero. The initial value of the pointer is zero.

Reading a byte from a path buffer will return the byte referenced by the pointer. Writing a byte to a path buffer will write that byte to the address referenced by the pointer. After reading or writing, the pointer will be incremented by 1, wrapping to zero after 255.

To clear a path buffer, the pointer and every byte will be set to zero. To populate a path buffer with a filesystem path, the buffer will be cleared, then every byte of the path will be written to the buffer, and then the pointer will be set to zero.

To read a filesystem path from a path buffer, the buffer contents up to the first zero byte will be read as a path. If the final byte of the buffer is not zero, the path will be invalid. If the first byte of the buffer is zero, the buffer is empty.

This device contains four path buffers: one for each of the open, action, path, and child path ports.

Selected child

The selected child is a child entry of the current entry. Initially there will be no child selected. A child will only be selected when the entry address port is written to while a directory is open.

The sort order of the children in a directory is determined by the implementation, but must be stable. Each child is addressed by its position in the sort order. Any child that does not have a valid path will be excluded from the sort order, and will not be selectable.

Entry address

The entry address is a 32-bit value that represents the address of a byte or a child entry of the current entry. The initial value of the entry address is zero.

Error flag

The error flag is a flag that indicates that an error has occurred. It is cleared when read. The initial state of this flag is unset.

Ports

Port Name Description Read Write
0x90 Open Open a filesystem entry.
0x91 Action Perform a filesystem action.
0x92 Head Read and write to an open file.
0x93 aliased aliased
0x94 Path Path of the current entry.
0x95 Type Type of the current entry.
0x96 Child path Path of the selected child entry.
0x97 Child type Type of the selected child entry.
0x98 Address Pointer into the current entry.
0x99 continued continued
0x9A continued continued
0x9B continued continued
0x9C Length Length of the current entry.
0x9D continued continued
0x9E continued continued
0x9F continued continued

Open

This port is associated with a path buffer.

Reading from this port will return 0xFF if an entry is currently open, or 0x00 otherwise.

Writing a non-zero byte to this port will write that byte to the associated path buffer. Writing a zero byte to this port will read a filesystem path from the associated path buffer, then will close the current entry, and then will attempt to open the entry addressed by the path if the buffer was not empty, setting the error flag on error.

Action

This port is associated with a path buffer.

Reading from this port will return 0xFF if the error flag is set, or 0x00 otherwise. The error flag is then cleared.

Writing a non-zero byte to this port will write that byte to the associated path buffer. Writing a zero byte to this port will read a filesystem path from the associated path buffer, then will attempt to perform a filesystem action, and then will close the current entry.

The source path is the path of the current entry, and is empty if no entry is open. The destination path is the path that was read, and is empty if the buffer was empty. If the destination path is invalid or addresses an existing entry, no action will be performed and the error flag will be set.

Source Destination Action
No action.
Create empty file at destination.
Delete source.
Move source to destination.

If only the source path is empty, an empty file will be created at the destination path, with parent directories being created as needed. The error flag will be set if the file or any directories could not be created.

If only the destination path is empty, the entry at the source path will be deleted. The error flag will be set if the entry could not be deleted and still exists.

If neither path is empty, the entry at the source path will be moved to the destination path, with parent directories being created as needed. The error flag will be set if the entry could not be moved or if any directories could not be created.

Reading from this port while the current entry is a file will return the byte referenced by the entry address, and then will increment the entry address by 1. If the file could not be read or if the entry address points past the end of the file, the error flag will be set and the value zero will be returned. Overflowing the entry address will cause undefined behaviour.

Writing a byte to this port while the current entry is a file will write that byte to the address referenced by the entry address, and then will increment the entry address by 1. If the entry address points past the end of the file, the file will first be zero-padded to a length one greater than the entry address. If the file could not be resized or written to, the error flag will be set. Overflowing the entry address will cause undefined behaviour.

If the current entry is a directory or if no entry is open, reading from this port will return zero and writing to this port will have no effect.

Path

This port is associated with a path buffer. When an entry is opened, the buffer is populated with the entry path. When an entry is closed, the buffer is cleared.

Reading from this port will read and return a byte from the associated path buffer.

Writing a non-zero value to this port will set the pointer of the associated path buffer to the address following the final forward-slash byte 0x2F, or otherwise to zero. Writing a zero value to this port will set the pointer to zero.

Type

Reading from this port will return 0xFF if the current entry is a directory, or 0x00 otherwise.

Writing to this port while an entry is open will close that entry and then will attempt to open the parent directory of the entry. If the entry has no parent or if that directory cannot be opened, the original entry will be opened and the error flag will be set.

Writing to this port while no entry is open will attempt to open the default directory, setting the error flag on error. The path of the default directory is implementation defined.

Child path

This port is associated with a path buffer. When a child is selected, the buffer is populated with the child path. When a child is deselected, the buffer is cleared.

Reading from this port will read and return a byte from the associated path buffer.

Writing a non-zero value to this port will set the pointer of the associated path buffer to the address following the final forward-slash byte 0x2F, or otherwise to zero. Writing a zero value to this port will set the pointer to zero.

Child type

Reading from this port will return 0xFF if the selected child is a directory, or 0x00 otherwise.

Writing to this port while a child entry is selected will close the current entry and then will attempt to open the child entry. If the child cannot be opened, the original entry will be opened and the error flag will be set.

Address

Reading from this port group will perform an atomic read, returning the entry address.

Writing to this port group will perform an atomic write, setting the entry address to the value written on commit. If the current entry is a directory and the value written references the address of a child entry, that entry will become the selected child, otherwise there will be no selected child.

Length

Reading from this port group will perform an atomic read, returning the length of the current entry. Reading while no entry is open will return zero. Reading while a file is open will return the length of the file in bytes. Reading while a directory is open will return the number of children in the directory. The value read will saturate at 0xFFFFFFFF.

Writing to this port group will perform an atomic write. If the current entry is a file, the file will be resized on commit. The file will be truncated or zero-padded to match the value written, setting the error flag on error.

Implementation

Alternate path separators

Some filesystems use an alternative path separator that isn’t the forward-slash character 0x2F. One example is Windows, which uses the back-slash character 0x5C. On these systems, each occurrence of the host path separator should be replaced by a forward-slash. If a path already contains a forward-slash that isn’t a path separator, that path will be invalid.

Default directory

The default directory should be the directory that the Bedrock program was launched from (known as the current working directory). If this isn’t available, the default directory should be the home directory of the user. If this isn’t available, the default directory should be the root directory of the filesystems.

Current entry missing

If the current entry is moved or deleted by an external process, the entry should remain open, but reading from, writing to, resizing, and moving the entry should fail.

Virtual root

Some filesystems are represented as multiple disconnected directory trees. One example is Windows, which treats each mounted drive as a separate filesystem. To allow this device to navigate across disconnected trees, a virtual root directory should be implemented at the root path /. This virtual root would act as the parent directory of each disconnected tree, with each tree represented as a child directory of the root.

Hidden entries

Some filesystems contain files that are normally hidden and ignored by users. One example is Windows, which contains large numbers of irrelevant system files scattered around regular user files. If a file or directory would normally be invisible to a user, it should be hidden from hierarchical directory navigation.

On Windows, entries with the attributes FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or FILE_ATTRIBUTE_HIDDEN should be hidden. On Linux, entries that begin with a leading period should be hidden. There should be some way to temporarily reveal these entries.

Sort order

Children of a directory should be sorted with directories above files, and then in ascending character order. Additionally, sorting should be case-insensitive, with upper-case characters sorted before lower-case.

Alternate encodings

If the filesystem does not encode file paths as UTF-8 strings, this device must perform translation between the original encoding and UTF-8. If a file path cannot be losslessly encoded as UTF-8, the file path is invalid.

Special components

A Bedrock path cannot contain any special path components provided by the filesystem that do not reference the direct child of a directory, such as the relative path components . and ... Any path containing a special path component is invalid.

A symbolic link is treated as the entry that it links to. Moving and deleting an entry represented by a symbolic link will affect the link object itself, not the linked file. The path of a symbolic link is the path of the link object. The parent directory of a symbolic link is the parent directory of the link object.

Safety

Access to the filesystem could allow a malicious program to read and modify sensitive data, fill up the filesystem with junk data, or delete important system files.

To mitigate this, a sandboxed root that points to an empty directory could be provided for untrusted programs, and traversal of symbolic links could be disallowed. Because special components are not allowed in paths, untrusted programs will be unable to break free from the sandbox.

Wake

This device will never send a wake request to the system device.