File device

This specification describes the file device of the Bedrock computer system.

The file device provides access to a heirarchical file system.

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, otherwise it will return zero.

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, otherwise it will return zero. 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. The behaviour when the entry address overflows is undefined.

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. The behaviour when the entry address overflows is undefined.

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 move the pointer of the associated path buffer to the final path component. 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, otherwise it will return zero.

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 move the pointer of the associated path buffer to the final path component. 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, otherwise it will return zero.

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 entry length.

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

Data structures

Filesystem entry

A filesystem entry is an object in the filesystem, either a file or a directory. A file represents a block of zero or more bytes. A directory represents a list of zero or more child entries. Each entry is addressable by at least one filesystem path. The maximum size of a file or directory is implementation defined.

Current entry

The current entry is the filesystem entry that is currently open. At most one entry can be open at a time. There will initially be no entry open.

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

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 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 these requirements, it is invalid.

A path separator is a single forward-slash character 0x2F. A path separator cannot directly follow another path separator.

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 null character 0x00 or the forward-slash character 0x2F.

Path buffer

A path buffer is a 256 byte block of writeable memory with an associated pointer. The initial value of each byte of the buffer 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, and then will increment the pointer by 1, wrapping to zero after 255.

Writing a byte to a path buffer will write that byte to the buffer address referenced by the pointer, and then will increment the pointer by 1, wrapping to zero after 255.

To move the pointer to the final path component, the pointer will be set to the address of the byte following the final forward-slash byte 0x2F, or to zero if no such byte exists.

To clear a path buffer, the pointer and every byte of the buffer 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. 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 path will be empty.

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

Selected child

The selected child is a child entry of the current entry that is currently selected. There will initially 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 implementation defined, 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 addressable.

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.

Entry length

The entry length is a 32-bit value that represents the length of the current entry.

If the current entry is a file, the entry length is the length of the file in bytes. If the length of the file exceeds 4,294,967,295 bytes, the value 0xFFFFFFFF will be returned.

If the current entry is a directory, the entry length is the number of child entries inside the directory. If the number of child entries exceeds 4,294,967,295 entries, the value 0xFFFFFFFF will be returned.

If no entry is open, the entry length will be 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 cleared.

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, also called 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.