commit b2f00d7ab64d1058f3f5be593563d096b4e0d710
parent 307334353f0b9af87e4cad88f1289addcb3553be
Author: Drew DeVault <sir@cmpwn.com>
Date: Wed, 24 Feb 2021 11:02:44 -0500
fs: new module
Diffstat:
A | fs/fs.ha | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | fs/types.ha | | | 127 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | fs/util.ha | | | 35 | +++++++++++++++++++++++++++++++++++ |
3 files changed, 213 insertions(+), 0 deletions(-)
diff --git a/fs/fs.ha b/fs/fs.ha
@@ -0,0 +1,51 @@
+use io;
+
+// Closes a filesystem. The fs cannot be used after this function is called.
+export fn close(fs: *fs) void = {
+ match (fs.close) {
+ null => void,
+ f: *closefunc => f(fs),
+ };
+};
+
+// Opens a file.
+export fn open(fs: *fs, path: path, mode: io::mode) (*io::stream | error) = {
+ return match (fs.open) {
+ null => io::unsupported,
+ f: *openfunc => f(fs, path, mode),
+ };
+};
+
+// Creates a new file.
+export fn create(fs: *fs, path: path, mode: io::mode) (*io::stream | error) = {
+ return match (fs.create) {
+ null => io::unsupported,
+ f: *createfunc => f(fs, path, mode),
+ };
+};
+
+// Returns an iterator for a path, which yields the contents of a directory.
+// Pass empty string to yield from the root.
+export fn iter(fs: *fs, path: path) (*iterator | error) = {
+ return match (fs.iter) {
+ null => io::unsupported,
+ f: *iterfunc => f(fs, path),
+ };
+};
+
+// Obtains information about a file or directory. If the target is a symlink,
+// information is returned about the link, not its target.
+export fn stat(fs: *fs, path: path) (filestat | error) = {
+ return match (fs.stat) {
+ null => io::unsupported,
+ f: *statfunc => f(fs, path),
+ };
+};
+
+// Opens a new filesystem for a subdirectory.
+export fn subdir(fs: *fs, path: path) (*fs | error) = {
+ return match (fs.subdir) {
+ null => io::unsupported,
+ f: *subdirfunc => f(fs, path),
+ };
+};
diff --git a/fs/types.ha b/fs/types.ha
@@ -0,0 +1,127 @@
+use io;
+
+// A path or path component.
+export type path = (str | []u8);
+
+// An entry was requested which does not exist.
+export type noentry = void!;
+
+// The user does not have permission to use this resource.
+export type noaccess = void!;
+
+// All possible fs error types.
+export type error = (noentry | noaccess | io::error)!;
+
+// File mode information. These bits do not necessarily reflect the underlying
+// operating system's mode representation, though they were chosen to be
+// consistent with typical Unix file permissions. All implementations shall
+// support at least OWNER_RW, DIR, and REG.
+export type mode = enum uint {
+ // Read, write, and execute permissions for the file owner
+ OWNER_RWX = 0o700,
+ // Read and write permissions for the file owner
+ OWNER_RW = 0o500,
+ // Read permissions for the file owner
+ OWNER_R = 0o400,
+ // Write permissions for the file owner
+ OWNER_W = 0o200,
+ // Execute permissions for the file owner
+ OWNER_X = 0o100,
+
+ // Read, write, and execute permissions for group members
+ GROUP_RWX = 0o070,
+ // Read and write permissions for group members
+ GROUP_RW = 0o050,
+ // Read permissions for group members
+ GROUP_R = 0o040,
+ // Write permissions for group members
+ GROUP_W = 0o020,
+ // Execute permissions for group members
+ GROUP_X = 0o010,
+
+ // Read, write, and execute permissions for other users
+ OTHER_RWX = 0o007,
+ // Read and write permissions for other users
+ OTHER_RW = 0o005,
+ // Read permissions for other users
+ OTHER_R = 0o004,
+ // Write permissions for other users
+ OTHER_W = 0o002,
+ // Execute permissions for other users
+ OTHER_X = 0o001,
+
+ // Entry has the set-uid bit set
+ SETUID = 0o4000,
+ // Entry has the set-gid bit set
+ SETGID = 0o2000,
+ // Entry has the sticky bit set
+ STICKY = 0o1000,
+
+ // Entry is a FIFO (named pipe)
+ FIFO = 0o010000,
+ // Entry is a directory
+ DIR = 0o040000,
+ // Entry is a character device
+ CHR = 0o020000,
+ // Entry is a block device
+ BLK = 0o060000,
+ // Entry is a regular file
+ REG = 0o100000,
+ // Entry is a symbolic link
+ LINK = 0o120000,
+ // Entry is a Unix socket
+ SOCK = 0o140000,
+};
+
+// A mask defining what items are populated in the stat structure.
+export type stat_mask = enum uint {
+ UID = 1 << 0,
+ GID = 1 << 1,
+ SIZE = 1 << 2,
+};
+
+// Information about a file or directory. The mask field defines what other
+// fields are set; mode is always set.
+export type filestat = struct {
+ mask: stat_mask,
+ mode: mode,
+ uid: uint,
+ gid: uint,
+ sz: size,
+ // TODO: atime et al
+};
+
+export type closefunc = fn(fs: *fs) void;
+export type openfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error);
+export type createfunc = fn(fs: *fs, path: path, mode: io::mode) (*io::stream | error);
+export type iterfunc = fn(fs: *fs, path: path) (*iterator | error);
+export type statfunc = fn(fs: *fs, path: path) (filestat | error);
+export type subdirfunc = fn(fs: *fs, path: path) (*fs | error);
+
+// An abstract implementation of a filesystem.
+export type fs = struct {
+ // Frees resources associated with this filesystem.
+ close: nullable *closefunc,
+
+ // Opens a file.
+ open: nullable *openfunc,
+
+ // Creates a new file.
+ create: nullable *createfunc,
+
+ // Returns an iterator for a path, which yields the contents of a
+ // directory. Pass empty string to yield from the root.
+ iter: nullable *iterfunc,
+
+ // Obtains information about a file or directory. If the target is a
+ // symlink, information is returned about the link, not its target.
+ stat: nullable *statfunc,
+
+ // Opens a new filesystem for a subdirectory.
+ subdir: nullable *subdirfunc,
+};
+
+// TODO
+export type iterator = struct {
+ placeholder: int,
+};
diff --git a/fs/util.ha b/fs/util.ha
@@ -0,0 +1,35 @@
+use io;
+
+// Returns a human-friendly representation of an error.
+export fn errstr(err: error) const str = match (err) {
+ noentry => "File or directory not found",
+ noaccess => "Permission denied",
+ err: io::error => io::errstr(err),
+};
+
+// Converts a mode into a Unix-like mode string (e.g. "-rw-r--r--")
+export fn modestr(mode: mode) const str = {
+ // TODO: blocked on bufio::fixed
+ abort();
+};
+
+// Returns true if this item is a regular file.
+export fn is_file(mode: mode) bool = mode & mode::REG == mode::REG;
+
+// Returns true if this item is a FIFO (named pipe).
+export fn is_fifo(mode: mode) bool = mode & mode::FIFO == mode::FIFO;
+
+// Returns true if this item is a directory.
+export fn is_dir(mode: mode) bool = mode & mode::DIR == mode::DIR;
+
+// Returns true if this item is a character device.
+export fn is_chdev(mode: mode) bool = mode & mode::CHR == mode::CHR;
+
+// Returns true if this item is a block device.
+export fn is_blockdev(mode: mode) bool = mode & mode::BLK == mode::BLK;
+
+// Returns true if this item is a symbolic link.
+export fn is_link(mode: mode) bool = mode & mode::LINK == mode::LINK;
+
+// Returns true if this item is a Unix socket.
+export fn is_socket(mode: mode) bool = mode & mode::SOCK == mode::SOCK;