std1.0.0-beta-7 SafeC docs

Source fs/tmpfs.sc

View in the repository

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

namespace std {

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

inline void TmpfsCtx::init() {
    safe_memset_obj(self, 0);
    // Create root inode at index 0.
    self.inodes[0].used   = 1;
    self.inodes[0].type   = VFS_TYPE_DIR;
    self.inodes[0].parent = (unsigned long)0;
    self.inodes[0].size   = (unsigned long)0;
    unsafe {
        self.inodes[0].name[0] = '/';
        self.inodes[0].name[1] = (char)0;
    }
    self.inode_count = 1;
    self.data_used   = (unsigned long)0;
}

// ── Helpers ───────────────────────────────────────────────────────────────────

// Tokenise a path and resolve down from root; returns inode index or -1.
static int tmpfs_resolve_(struct TmpfsCtx* tc, const char* path) {
    unsafe { if (path[0] == '/' && path[1] == (char)0) { return 0; } }
    // Walk components.
    unsigned long cur = (unsigned long)0;
    unsigned long i   = (unsigned long)0;
    unsafe { while (path[i] == '/') { i = i + (unsigned long)1; } }
    unsafe {
    while (path[i] != (char)0) {
        // Extract component.
        char comp[VFS_MAX_NAME];
        unsigned long clen = (unsigned long)0;
        {
            while (path[i] != (char)0 && path[i] != '/' &&
                   clen < (unsigned long)VFS_MAX_NAME - (unsigned long)1) {
                comp[clen] = path[i];
                clen = clen + (unsigned long)1;
                i = i + (unsigned long)1;
            }
            comp[clen] = (char)0;
            while (path[i] == '/') { i = i + (unsigned long)1; }
        }
        // Find child with this name under `cur`.
        int found = -1;
        int j = 0;
        while (j < TMPFS_MAX_FILES) {
            if (tc->inodes[j].used != 0 &&
                (unsigned long)tc->inodes[j].parent == cur) {
                int eq = 0;
                eq = (strcmp(tc->inodes[j].name, (const char*)comp) == 0);
                if (eq != 0) { found = j; break; }
            }
            j = j + 1;
        }
        if (found < 0) { return -1; }
        cur = (unsigned long)found;
    }
    }
    return (int)cur;
}

// Allocate a fresh inode; returns index or -1.
static int tmpfs_alloc_inode_(struct TmpfsCtx* tc) {
    int i = 0;
    while (i < TMPFS_MAX_FILES) {
        unsafe { if (tc->inodes[i].used == 0) { return i; } }
        i = i + 1;
    }
    return -1;
}

// Split path into parent path + leaf component.
static void tmpfs_split_path_(const char* path,
                               char* parent_out, char* leaf_out) {
    unsafe {
        unsigned long len = strlen(path);
        // Find last slash.
        long last = -1;
        long k = (long)len - 1L;
        while (k >= 0L) {
            if (path[k] == '/') { last = k; break; }
            k = k - 1L;
        }
        if (last <= 0L) {
            parent_out[0] = '/'; parent_out[1] = (char)0;
            strncpy(leaf_out, path + (last + 1L), (unsigned long)VFS_MAX_NAME - (unsigned long)1);
            leaf_out[VFS_MAX_NAME - 1] = (char)0;
        } else {
            strncpy(parent_out, path, (unsigned long)last);
            parent_out[last] = (char)0;
            strncpy(leaf_out, path + last + 1L,
                    (unsigned long)VFS_MAX_NAME - (unsigned long)1);
            leaf_out[VFS_MAX_NAME - 1] = (char)0;
        }
    }
}

// ── FileSystem methods (see vfs.h) ────────────────────────────────────────────

int TmpfsCtx::open(const &char path_ref, int flags, &VfsNode node_out) {
    const char* path = path_ref;
    struct TmpfsCtx* tc;
    unsafe { tc = (struct TmpfsCtx*)self; }
    int idx = tmpfs_resolve_(tc, path);
    if (idx < 0) {
        // Create if VFS_O_CREATE.
        if ((flags & VFS_O_CREATE) == 0) { return -1; }
        char parent[VFS_MAX_PATH];
        char leaf[VFS_MAX_NAME];
        tmpfs_split_path_(path, (char*)parent, (char*)leaf);
        int pidx = tmpfs_resolve_(tc, (const char*)parent);
        if (pidx < 0) { return -1; }
        int new_idx = tmpfs_alloc_inode_(tc);
        if (new_idx < 0) { return -1; }
        unsafe {
            tc->inodes[new_idx].used     = 1;
            tc->inodes[new_idx].type     = VFS_TYPE_FILE;
            tc->inodes[new_idx].parent   = (unsigned long)pidx;
            tc->inodes[new_idx].size     = (unsigned long)0;
            tc->inodes[new_idx].data_off = tc->data_used;
            strncpy(tc->inodes[new_idx].name, (const char*)leaf,
                    (unsigned long)VFS_MAX_NAME - (unsigned long)1);
            tc->inodes[new_idx].name[VFS_MAX_NAME - 1] = (char)0;
            tc->inode_count = tc->inode_count + 1;
        }
        idx = new_idx;
    }
    unsafe {
        unsigned long nlen = strlen(tc->inodes[idx].name);
        unsigned long k = (unsigned long)0;
        while (k < nlen && k < (unsigned long)VFS_MAX_NAME - (unsigned long)1) {
            node_out.name[k] = tc->inodes[idx].name[k];
            k = k + (unsigned long)1;
        }
        node_out.name[k] = (char)0;
        node_out.type  = tc->inodes[idx].type;
        node_out.size  = tc->inodes[idx].size;
    }
    node_out.inode = (unsigned long)idx;
    return 0;
}

int TmpfsCtx::unlink(const &char path_ref) {
    const char* path = path_ref;
    struct TmpfsCtx* tc;
    unsafe { tc = (struct TmpfsCtx*)self; }
    int idx = tmpfs_resolve_(tc, path);
    if (idx < 0) { return -1; }
    unsafe {
        tc->inodes[idx].used = 0;
        tc->inode_count = tc->inode_count - 1;
    }
    return 0;
}

int TmpfsCtx::mkdir(const &char path_ref) {
    const char* path = path_ref;
    struct TmpfsCtx* tc;
    unsafe { tc = (struct TmpfsCtx*)self; }
    char parent[VFS_MAX_PATH];
    char leaf[VFS_MAX_NAME];
    tmpfs_split_path_(path, (char*)parent, (char*)leaf);
    int pidx = tmpfs_resolve_(tc, (const char*)parent);
    if (pidx < 0) { return -1; }
    int new_idx = tmpfs_alloc_inode_(tc);
    if (new_idx < 0) { return -1; }
    unsafe {
        tc->inodes[new_idx].used   = 1;
        tc->inodes[new_idx].type   = VFS_TYPE_DIR;
        tc->inodes[new_idx].parent = (unsigned long)pidx;
        tc->inodes[new_idx].size   = (unsigned long)0;
        strncpy(tc->inodes[new_idx].name, (const char*)leaf,
                (unsigned long)VFS_MAX_NAME - (unsigned long)1);
        tc->inodes[new_idx].name[VFS_MAX_NAME - 1] = (char)0;
        tc->inode_count = tc->inode_count + 1;
    }
    return 0;
}

unsigned long TmpfsCtx::read(unsigned long inode, unsigned long offset, []unsigned char buf_s) {
    unsigned long len = buf_s.len;
    unsigned char* buf;
    unsafe { buf = (unsigned char*)buf_s.ptr; }
    struct TmpfsCtx* tc;
    unsafe { tc = (struct TmpfsCtx*)self; }
    int idx = (int)inode;
    if (idx < 0 || idx >= TMPFS_MAX_FILES) { return (unsigned long)0; }
    unsigned long fsize;
    unsafe {
        if (tc->inodes[idx].used == 0 || tc->inodes[idx].type != VFS_TYPE_FILE) {
            return (unsigned long)0;
        }
        fsize = tc->inodes[idx].size;
    }
    if (offset >= fsize) { return (unsigned long)0; }
    unsigned long avail = fsize - offset;
    if (avail > len) { avail = len; }
    unsafe {
        unsigned long doff = tc->inodes[idx].data_off + offset;
        unsigned long k = (unsigned long)0;
        while (k < avail) {
            buf[k] = tc->data[doff + k];
            k = k + (unsigned long)1;
        }
    }
    return avail;
}

unsigned long TmpfsCtx::write(unsigned long inode, unsigned long offset, []const unsigned char buf_s) {
    unsigned long len = buf_s.len;
    const unsigned char* buf;
    unsafe { buf = (const unsigned char*)buf_s.ptr; }
    struct TmpfsCtx* tc;
    unsafe { tc = (struct TmpfsCtx*)self; }
    int idx = (int)inode;
    if (idx < 0 || idx >= TMPFS_MAX_FILES) { return (unsigned long)0; }
    unsigned long doff;
    unsigned long end;
    unsafe {
        if (tc->inodes[idx].used == 0 || tc->inodes[idx].type != VFS_TYPE_FILE) {
            return (unsigned long)0;
        }
        doff = tc->inodes[idx].data_off + offset;
        end  = doff + len;
        if (end > (unsigned long)TMPFS_MAX_DATA) {
            len = (unsigned long)TMPFS_MAX_DATA - doff;
        }
        unsigned long k = (unsigned long)0;
        while (k < len) {
            tc->data[doff + k] = buf[k];
            k = k + (unsigned long)1;
        }
        unsigned long new_size = offset + len;
        if (new_size > tc->inodes[idx].size) { tc->inodes[idx].size = new_size; }
        if (end > tc->data_used) { tc->data_used = end; }
    }
    return len;
}

int TmpfsCtx::readdir_raw_(unsigned long inode, fn void(&VfsNode, ?&Erased) cb, ?&Erased user) {
    struct TmpfsCtx* tc;
    unsafe { tc = (struct TmpfsCtx*)self; }
    int count = 0;
    int j = 0;
    while (j < TMPFS_MAX_FILES) {
        int matched;
        unsafe {
            matched = (tc->inodes[j].used != 0 &&
                     (unsigned long)tc->inodes[j].parent == inode &&
                     j != (int)inode) ? 1 : 0;
        }
        if (matched != 0) {
            struct VfsNode node;
            unsafe {
                unsigned long nlen = strlen(tc->inodes[j].name);
                unsigned long k = (unsigned long)0;
                while (k < nlen && k < (unsigned long)VFS_MAX_NAME - (unsigned long)1) {
                    node.name[k] = tc->inodes[j].name[k];
                    k = k + (unsigned long)1;
                }
                node.name[k] = (char)0;
                node.type  = tc->inodes[j].type;
                node.size  = tc->inodes[j].size;
                node.inode = (unsigned long)j;
                node.fs_ctx = null;
                cb(&node, user);
            }
            count = count + 1;
        }
        j = j + 1;
    }
    return count;
}

} // namespace std