std1.0.0-beta-7 SafeC docs

Source fs/vfs.sc

View in the repository

// SafeC Standard Library — VFS Implementation
#pragma once
#include <std/stddef.h>
#include <std/mem.h>
#include <std/fs/vfs.h>

namespace std {

// Definitions of the globals vfs.h declares extern.
struct Vfs vfs_root;


extern int   strcmp(const char* a, const char* b);
extern size_t strlen(const char* s);
extern char* strncpy(char* d, const char* s, size_t n);


inline struct VfsOps vfs_ops_none_() {
    struct VfsOps ops;
    safe_memset_obj(&ops, 0);
    return ops;
}

inline void vfs_init() {
    safe_memset_obj(&vfs_root, 0);
    vfs_root.mount_count = 0;
}

// ── VfsNode forwarding methods ────────────────────────────────────────────────

inline unsigned long VfsNode::read(unsigned long offset, []unsigned char buf) {
    match (self.fs_ctx) {
        case some(m): {
            match (m.ops.read) {
                case some(f): {
                    match (m.ctx) { case some(c): return f(c, self.inode, offset, buf); case null: { } }
                }
                case null: { }
            }
        }
        case null: { }
    }
    return (unsigned long)0;
}

inline unsigned long VfsNode::write(unsigned long offset, []const unsigned char buf) {
    match (self.fs_ctx) {
        case some(m): {
            match (m.ops.write) {
                case some(f): {
                    match (m.ctx) { case some(c): return f(c, self.inode, offset, buf); case null: { } }
                }
                case null: { }
            }
        }
        case null: { }
    }
    return (unsigned long)0;
}

// Hands each entry to the caller's callback with fs_ctx set to the mount,
// so the entry can be read or listed like a node from Vfs::open.
struct VfsListing_ {
    fn void(&VfsNode, ?&Erased) cb;     // the caller's callback
    ?&Erased user;
    ?&VfsMount mount;
};

static void vfs_listing_entry_(&VfsNode node, ?&VfsListing_ lp) {
    match (lp) {
        case some(l): {
            node.fs_ctx = l.mount;
            l.cb(node, l.user);
        }
        case null: { }
    }
}

inline int VfsNode::readdir_raw_(fn void(&VfsNode, ?&Erased) cb, ?&Erased user) {
    match (self.fs_ctx) {
        case some(m): {
            match (m.ops.readdir) {
                case some(f): {
                    match (m.ctx) {
                        case some(c): {
                            struct VfsListing_ l;
                            l.cb = cb;
                            l.user = user;
                            l.mount = m;
                            fn void(&VfsNode, ?&VfsListing_) entry = vfs_listing_entry_;
                            unsafe {
                                return f(c, self.inode, (fn void(&VfsNode, ?&Erased))entry, (?&Erased)&l);
                            }
                        }
                        case null: { }
                    }
                }
                case null: { }
            }
        }
        case null: { }
    }
    return -1;
}

// ── Vfs methods ───────────────────────────────────────────────────────────────

// Find the mount whose mountpoint is longest prefix of `path`.
static struct VfsMount* vfs_find_mount_(struct Vfs* v, const char* path) {
    struct VfsMount* best;
    unsafe { best = (struct VfsMount*)0; }
    unsigned long best_len = (unsigned long)0;
    int i = 0;
    unsafe {
    while (i < v->mount_count) {
        struct VfsMount* m = (struct VfsMount*)&v->mounts[i];
        if (m->active != 0) {
            unsigned long mlen = strlen(m->mountpoint);
            int matches = 0;
            {
                // strncmp equivalent
                int j = 0;
                matches = 1;
                while ((unsigned long)j < mlen) {
                    if (m->mountpoint[j] != path[j]) { matches = 0; break; }
                    j = j + 1;
                }
            }
            if (matches != 0 && mlen > best_len) {
                best = m;
                best_len = mlen;
            }
        }
        i = i + 1;
    }
    }
    return best;
}

// Strip the mountpoint prefix from path to get the relative path.
static const char* vfs_rel_path_(const char* path, const char* mountpoint) {
    unsigned long mlen;
    unsafe { mlen = strlen(mountpoint); }
    unsafe {
        const char* rel = path + mlen;
        if (rel[0] == (char)0) { return (const char*)"/"; }
        return rel;
    }
    return path;
}

int Vfs::mount_raw_(const &char mountpoint, struct VfsOps ops, &Erased ctx) {
    if (self.mount_count >= VFS_MAX_MOUNTS) { return -1; }
    int i = 0;
    while (i < VFS_MAX_MOUNTS) {
        if (self.mounts[i].active == 0) {
            unsafe {
                strncpy((char*)self.mounts[i].mountpoint, mountpoint,
                        (unsigned long)VFS_MAX_NAME - (unsigned long)1);
                self.mounts[i].mountpoint[VFS_MAX_NAME - 1] = (char)0;
                self.mounts[i].ops = ops;
            }
            self.mounts[i].ctx    = ctx;
            self.mounts[i].active = 1;
            self.mount_count      = self.mount_count + 1;
            return 0;
        }
        i = i + 1;
    }
    return -1;
}

inline int Vfs::unmount(const &char mountpoint) {
    int i = 0;
    while (i < VFS_MAX_MOUNTS) {
        if (self.mounts[i].active != 0) {
            int eq = 0;
            unsafe { eq = (strcmp(self.mounts[i].mountpoint, mountpoint) == 0); }
            if (eq != 0) {
                self.mounts[i].active = 0;
                self.mount_count = self.mount_count - 1;
                return 0;
            }
        }
        i = i + 1;
    }
    return -1;
}

inline int Vfs::open(const &char path, int flags, &VfsNode node_out) {
    struct VfsMount* m;
    unsafe { m = vfs_find_mount_((struct Vfs*)self, path); }
    if (m == (struct VfsMount*)0) { return -1; }
    int rc = -1;
    unsafe {
        const char* rel = vfs_rel_path_(path, (const char*)m->mountpoint);
        match (m->ops.open) {
            case some(f): {
                match (m->ctx) { case some(c): rc = f(c, rel, flags, node_out); case null: { } }
            }
            case null: return -1;
        }
    }
    if (rc == 0) {
        unsafe { node_out.fs_ctx = (?&VfsMount)m; }
    }
    return rc;
}

inline int Vfs::unlink(const &char path) {
    struct VfsMount* m;
    unsafe { m = vfs_find_mount_((struct Vfs*)self, path); }
    if (m == (struct VfsMount*)0) { return -1; }
    unsafe {
        const char* rel = vfs_rel_path_(path, (const char*)m->mountpoint);
        match (m->ops.unlink) {
            case some(f): {
                match (m->ctx) { case some(c): return f(c, rel); case null: { } }
            }
            case null: { }
        }
    }
    return -1;
}

inline int Vfs::mkdir(const &char path) {
    struct VfsMount* m;
    unsafe { m = vfs_find_mount_((struct Vfs*)self, path); }
    if (m == (struct VfsMount*)0) { return -1; }
    unsafe {
        const char* rel = vfs_rel_path_(path, (const char*)m->mountpoint);
        match (m->ops.mkdir) {
            case some(f): {
                match (m->ctx) { case some(c): return f(c, rel); case null: { } }
            }
            case null: { }
        }
    }
    return -1;
}

} // namespace std