// Directory mutation wrappers — route through mount points transparently
int vfs_create(const char* path, uint32_t flags, fs_node_t** out);
int vfs_mkdir(const char* path);
+int vfs_mkdirp(const char* path);
int vfs_unlink(const char* path);
int vfs_rmdir(const char* path);
int vfs_rename(const char* old_path, const char* new_path);
int vfs_link(const char* old_path, const char* new_path);
int vfs_mount(const char* mountpoint, fs_node_t* root);
+int vfs_mount_full(const char* mountpoint, fs_node_t* root,
+ const char* fstype, const char* source,
+ unsigned long flags);
int vfs_umount(const char* mountpoint);
/* _nolock variants — caller must already hold g_vfs_lock.
* Used by compound operations like pivot_root that need atomicity
* across multiple mount-table modifications. */
int vfs_mount_nolock(const char* mountpoint, fs_node_t* root);
+int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
+ const char* fstype, const char* source,
+ unsigned long flags);
int vfs_umount_nolock(const char* mountpoint);
+/* Read mount table for /proc/mounts. Returns bytes written. */
+uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size);
+
/* Global VFS spinlock — protects fs_root, g_mounts[], g_mount_count.
* Acquire via spin_lock_irqsave() for any compound VFS mutation. */
extern spinlock_t g_vfs_lock;
#include "utils.h"
#include "errno.h"
#include "spinlock.h"
+#include "console.h"
+
+#include <string.h>
fs_node_t* fs_root = NULL;
static fs_node_t* g_initrd_root = NULL;
struct vfs_mount {
char mountpoint[128];
+ char fstype[32]; /* e.g. "overlayfs", "tmpfs", "devfs", "procfs", "diskfs", "fat", "ext2" */
+ char source[64]; /* e.g. "/dev/hda", "none", "initrd" */
+ unsigned long flags; /* MS_RDONLY, MS_NOSUID, etc. */
fs_node_t* root;
};
-static struct vfs_mount g_mounts[16];
+static struct vfs_mount g_mounts[32];
static int g_mount_count = 0;
static int path_is_mountpoint_prefix(const char* mp, const char* path) {
}
}
-int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) {
- if (!root) return -EINVAL;
+int vfs_mount_nolock_full(const char* mountpoint, fs_node_t* root,
+ const char* fstype, const char* source,
+ unsigned long flags) {
if (g_mount_count >= (int)(sizeof(g_mounts) / sizeof(g_mounts[0]))) return -ENOSPC;
char mp[128];
for (int i = 0; i < g_mount_count; i++) {
if (strcmp(g_mounts[i].mountpoint, mp) == 0) {
- g_mounts[i].root = root;
+ /* Remount: update provided fields only */
+ if (root) g_mounts[i].root = root;
+ if (fstype) strncpy(g_mounts[i].fstype, fstype, sizeof(g_mounts[i].fstype) - 1);
+ if (source) strncpy(g_mounts[i].source, source, sizeof(g_mounts[i].source) - 1);
+ /* Always update flags on remount (even if 0, caller explicitly set them) */
+ g_mounts[i].flags = flags;
return 0;
}
}
+ /* New mount entry — root is required */
+ if (!root) return -EINVAL;
+
strcpy(g_mounts[g_mount_count].mountpoint, mp);
g_mounts[g_mount_count].root = root;
+ if (fstype) strncpy(g_mounts[g_mount_count].fstype, fstype, sizeof(g_mounts[g_mount_count].fstype) - 1);
+ else g_mounts[g_mount_count].fstype[0] = '\0';
+ if (source) strncpy(g_mounts[g_mount_count].source, source, sizeof(g_mounts[g_mount_count].source) - 1);
+ else g_mounts[g_mount_count].source[0] = '\0';
+ g_mounts[g_mount_count].flags = flags;
g_mount_count++;
return 0;
}
-int vfs_mount(const char* mountpoint, fs_node_t* root) {
+int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) {
+ return vfs_mount_nolock_full(mountpoint, root, NULL, NULL, 0);
+}
+
+int vfs_mount_full(const char* mountpoint, fs_node_t* root,
+ const char* fstype, const char* source,
+ unsigned long flags) {
uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
- int ret = vfs_mount_nolock(mountpoint, root);
+ int ret = vfs_mount_nolock_full(mountpoint, root, fstype, source, flags);
spin_unlock_irqrestore(&g_vfs_lock, fl);
return ret;
}
+int vfs_mount(const char* mountpoint, fs_node_t* root) {
+ return vfs_mount_full(mountpoint, root, NULL, NULL, 0);
+}
+
int vfs_umount_nolock(const char* mountpoint) {
char mp[128];
normalize_mountpoint(mountpoint, mp, sizeof(mp));
if (strcmp(mp, "/") == 0) return -EBUSY;
+ /* Find the mount entry */
+ int idx = -1;
for (int i = 0; i < g_mount_count; i++) {
if (strcmp(g_mounts[i].mountpoint, mp) == 0) {
- for (int j = i; j < g_mount_count - 1; j++)
- g_mounts[j] = g_mounts[j + 1];
- g_mount_count--;
- return 0;
+ idx = i;
+ break;
+ }
+ }
+ if (idx < 0) return -EINVAL;
+
+ /* Busy check: reject if any other mount is a child of this one */
+ size_t mplen = strlen(mp);
+ for (int i = 0; i < g_mount_count; i++) {
+ if (i == idx) continue;
+ const char* other = g_mounts[i].mountpoint;
+ size_t olen = strlen(other);
+ if (olen > mplen &&
+ strncmp(other, mp, mplen) == 0 &&
+ other[mplen] == '/') {
+ return -EBUSY; /* child mount still active */
}
}
- return -EINVAL;
+
+ for (int j = idx; j < g_mount_count - 1; j++)
+ g_mounts[j] = g_mounts[j + 1];
+ g_mount_count--;
+ return 0;
}
int vfs_umount(const char* mountpoint) {
return ret;
}
+/* Read the mount table into a user buffer for /proc/mounts.
+ * Format per line: <source> <mountpoint> <fstype> <options>\n
+ * Returns number of bytes written. */
+uint32_t vfs_mounts_read(uint8_t* buffer, uint32_t size) {
+ uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
+
+ char tmp[2048];
+ uint32_t len = 0;
+
+ for (int i = 0; i < g_mount_count && len < sizeof(tmp) - 80; i++) {
+ const char* src = g_mounts[i].source[0] ? g_mounts[i].source : "none";
+ const char* fst = g_mounts[i].fstype[0] ? g_mounts[i].fstype : "unknown";
+
+ /* Build options string from flags */
+ char opts[64];
+ uint32_t olen = 0;
+
+ /* rw/ro */
+ if (g_mounts[i].flags & 1 /* MS_RDONLY */) {
+ opts[olen++] = 'r'; opts[olen++] = 'o';
+ } else {
+ opts[olen++] = 'r'; opts[olen++] = 'w';
+ }
+ if (g_mounts[i].flags & 2) { opts[olen++] = ','; const char* s = "nosuid"; while (*s) opts[olen++] = *s++; }
+ if (g_mounts[i].flags & 4) { opts[olen++] = ','; const char* s = "nodev"; while (*s) opts[olen++] = *s++; }
+ if (g_mounts[i].flags & 8) { opts[olen++] = ','; const char* s = "noexec"; while (*s) opts[olen++] = *s++; }
+ opts[olen] = '\0';
+
+ /* source mountpoint fstype options */
+ len += (uint32_t)ksnprintf(tmp + len, sizeof(tmp) - len,
+ "%s %s %s %s 0 0\n", src, g_mounts[i].mountpoint, fst, opts);
+ }
+
+ spin_unlock_irqrestore(&g_vfs_lock, fl);
+
+ if (len > size) len = size;
+ memcpy(buffer, tmp, len);
+ return len;
+}
+
uint32_t vfs_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
if (node->f_ops && node->f_ops->read)
return node->f_ops->read(node, offset, size, buffer);
return -ENOSYS;
}
+/* Recursive mkdir -p: create intermediate directories as needed.
+ * Returns 0 on success, -EEXIST if the directory already exists,
+ * or a negative errno on failure. */
+int vfs_mkdirp(const char* path) {
+ if (!path || path[0] != '/') return -EINVAL;
+
+ /* Check if it already exists */
+ fs_node_t* node = vfs_lookup(path);
+ if (node) {
+ if (node->flags & FS_DIRECTORY) return -EEXIST;
+ return -ENOTDIR;
+ }
+
+ /* Walk the path creating each missing component */
+ char tmp[128];
+ strncpy(tmp, path, sizeof(tmp) - 1);
+ tmp[sizeof(tmp) - 1] = '\0';
+
+ /* Skip leading '/' */
+ char* p = tmp + 1;
+ char* slash;
+
+ while ((slash = p) && *p) {
+ /* Find next '/' */
+ while (*p && *p != '/') p++;
+ if (*p == '/') { *p = '\0'; p++; }
+
+ /* Check if this component exists */
+ fs_node_t* check = vfs_lookup(tmp);
+ if (!check) {
+ /* Create it */
+ int rc = vfs_mkdir(tmp);
+ if (rc < 0 && rc != -EEXIST) return rc;
+ } else if (!(check->flags & FS_DIRECTORY)) {
+ return -ENOTDIR;
+ }
+
+ /* Restore slash for next iteration */
+ if (*p) p[-1] = '/';
+ }
+
+ return 0;
+}
+
int vfs_unlink(const char* path) {
if (!path) return -EINVAL;
char name[128];
#include "kernel/cmdline.h"
#include <stddef.h>
+#include <string.h>
/* ---- Mount helper: used by fstab parser and kconsole 'mount' command ---- */
return -1;
}
- if (vfs_mount(mountpoint, root) < 0) {
+ /* Build device name for mount table metadata */
+ char devname[32] = "none";
+ const char* dname = ata_drive_to_name(drive);
+ if (dname) {
+ strcpy(devname, "/dev/");
+ /* Append drive name after /dev/ */
+ char* dp = devname + 5;
+ while (*dname && (dp - devname) < (int)sizeof(devname) - 2)
+ *dp++ = *dname++;
+ *dp = '\0';
+ }
+
+ if (vfs_mount_full(mountpoint, root, fstype, devname, 0) < 0) {
kprintf("[MOUNT] Failed to register mount at %s\n", mountpoint);
return -1;
}
if (upper) {
fs_node_t* ovl = overlayfs_create_root(fs_root, upper);
if (ovl) {
- (void)vfs_mount("/", ovl);
+ (void)vfs_mount_full("/", ovl, "overlayfs", "initrd", 0);
vfs_set_initrd_root(ovl);
}
}
if (tmp) {
static const uint8_t hello[] = "hello from tmpfs\n";
(void)tmpfs_add_file(tmp, "hello.txt", hello, (uint32_t)(sizeof(hello) - 1));
- (void)vfs_mount("/tmp", tmp);
+ (void)vfs_mount_full("/tmp", tmp, "tmpfs", "none", 0);
}
/* Register hardware drivers with HAL and init in priority order */
* existing entry so this is a harmless overlap. */
fs_node_t* dev = devfs_create_root();
if (dev) {
- (void)vfs_mount("/dev", dev);
+ (void)vfs_mount_full("/dev", dev, "devfs", "none", 0);
}
vbe_register_devfs();
fs_node_t* proc = procfs_create_root();
if (proc) {
- (void)vfs_mount("/proc", proc);
+ (void)vfs_mount_full("/proc", proc, "procfs", "none", 0);
}
/* Initialize ATA subsystem — probe all 4 drives
#include "pmm.h"
#include "timer.h"
#include "kernel/cmdline.h"
+#include "fs.h"
#include <stddef.h>
static fs_node_t g_proc_meminfo;
static fs_node_t g_proc_cmdline;
static fs_node_t g_proc_dmesg;
+static fs_node_t g_proc_mounts;
#define PID_NODE_POOL 8
static fs_node_t g_pid_dir[PID_NODE_POOL];
return &g_pid_dir[slot];
}
+/* --- /proc/mounts read --- */
+
+static uint32_t proc_mounts_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+ (void)node;
+ /* Allocate from heap — mount table can be large */
+ char* tmp = kmalloc(4096);
+ if (!tmp) return 0;
+ uint32_t len = vfs_mounts_read((uint8_t*)tmp, 4096);
+ uint32_t ret = 0;
+ if (offset < len) {
+ uint32_t avail = len - offset;
+ if (size > avail) size = avail;
+ memcpy(buffer, tmp + offset, size);
+ ret = size;
+ }
+ kfree(tmp);
+ return ret;
+}
+
/* --- /proc/self --- */
static fs_node_t* proc_self_finddir(fs_node_t* node, const char* name) {
if (strcmp(name, "meminfo") == 0) return &g_proc_meminfo;
if (strcmp(name, "cmdline") == 0) return &g_proc_cmdline;
if (strcmp(name, "dmesg") == 0) return &g_proc_dmesg;
+ if (strcmp(name, "mounts") == 0) return &g_proc_mounts;
if (is_numeric(name)) return proc_get_pid_dir(parse_uint(name));
return NULL;
}
uint32_t idx = *inout_index;
struct vfs_dirent* d = (struct vfs_dirent*)buf;
- static const char* fixed[] = { "self", "uptime", "meminfo", "cmdline", "dmesg" };
- if (idx < 5) {
+ static const char* fixed[] = { "self", "uptime", "meminfo", "cmdline", "dmesg", "mounts" };
+ if (idx < 6) {
d->d_ino = 200 + idx;
d->d_type = (idx == 0) ? FS_DIRECTORY : FS_FILE;
d->d_reclen = sizeof(struct vfs_dirent);
.read = proc_dmesg_read,
};
+static const struct file_operations procfs_mounts_fops = {
+ .read = proc_mounts_read,
+};
+
static const struct file_operations procfs_pid_dir_fops = {0};
static const struct inode_operations procfs_pid_dir_iops = {
g_proc_dmesg.flags = FS_FILE;
g_proc_dmesg.f_ops = &procfs_dmesg_fops;
+ memset(&g_proc_mounts, 0, sizeof(g_proc_mounts));
+ strcpy(g_proc_mounts.name, "mounts");
+ g_proc_mounts.flags = FS_FILE;
+ g_proc_mounts.f_ops = &procfs_mounts_fops;
+
return &g_proc_root;
}
uintptr_t vfs_fl = spin_lock_irqsave(&g_vfs_lock);
fs_node_t* old_root = fs_root;
fs_root = new_root;
- (void)vfs_mount_nolock("/", new_root);
+ (void)vfs_mount_nolock_full("/", new_root, NULL, NULL, 0);
if (old_root) {
- (void)vfs_mount_nolock(kput, old_root);
+ (void)vfs_mount_nolock_full(kput, old_root, NULL, NULL, 0);
}
spin_unlock_irqrestore(&g_vfs_lock, vfs_fl);
sc_ret(regs) = 0;
const char* user_dev = (const char*)sc_arg0(regs);
const char* user_mp = (const char*)sc_arg1(regs);
const char* user_type = (const char*)sc_arg2(regs);
+ unsigned long mount_flags = (unsigned long)sc_arg3(regs);
char kdev[64], kmp[128], ktype[32];
if (copy_from_user(kdev, user_dev, sizeof(kdev)) < 0 ||
path_resolve_user(user_mp, kmp, sizeof(kmp)) < 0 ||
kdev[sizeof(kdev)-1] = '\0';
ktype[sizeof(ktype)-1] = '\0';
+ /* MS_REMOUNT (0x20): update flags on existing mount */
+ if (mount_flags & 0x20 /* MS_REMOUNT */) {
+ sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, NULL, NULL, NULL, mount_flags & ~0x20);
+ return;
+ }
+
/* Virtual filesystems — no device argument needed */
if (strcmp(ktype, "tmpfs") == 0) {
fs_node_t* tmp = tmpfs_create_root();
if (!tmp) { sc_ret(regs) = (uint32_t)-ENOMEM; return; }
- (void)vfs_mkdir(kmp); /* auto-create mountpoint */
- sc_ret(regs) = (uint32_t)vfs_mount(kmp, tmp);
+ (void)vfs_mkdirp(kmp); /* auto-create mountpoint (recursive) */
+ sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, tmp, "tmpfs", kdev, mount_flags);
return;
}
if (strcmp(ktype, "devfs") == 0) {
extern fs_node_t* devfs_create_root(void);
fs_node_t* dev = devfs_create_root();
if (!dev) { sc_ret(regs) = (uint32_t)-ENOMEM; return; }
- (void)vfs_mkdir(kmp);
- sc_ret(regs) = (uint32_t)vfs_mount(kmp, dev);
+ (void)vfs_mkdirp(kmp);
+ sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, dev, "devfs", kdev, mount_flags);
return;
}
if (strcmp(ktype, "procfs") == 0) {
extern fs_node_t* procfs_create_root(void);
fs_node_t* proc = procfs_create_root();
if (!proc) { sc_ret(regs) = (uint32_t)-ENOMEM; return; }
- (void)vfs_mkdir(kmp);
- sc_ret(regs) = (uint32_t)vfs_mount(kmp, proc);
+ (void)vfs_mkdirp(kmp);
+ sc_ret(regs) = (uint32_t)vfs_mount_full(kmp, proc, "procfs", kdev, mount_flags);
return;
}
if (drive < 0) { sc_ret(regs) = (uint32_t)-ENODEV; return; }
extern int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint);
- (void)vfs_mkdir(kmp); /* auto-create mountpoint */
+ (void)vfs_mkdirp(kmp); /* auto-create mountpoint (recursive) */
int rc = init_mount_fs(ktype, drive, 0, kmp);
sc_ret(regs) = (uint32_t)(rc < 0 ? -EIO : 0);
return;
return;
}
const char* user_target = (const char*)sc_arg0(regs);
+ unsigned long umount_flags = (unsigned long)sc_arg1(regs);
char ktarget[128];
if (path_resolve_user(user_target, ktarget, sizeof(ktarget)) < 0) {
sc_ret(regs) = (uint32_t)-EFAULT;
return;
}
+ /* MNT_FORCE (1) — for now, same as regular umount (no lazy unmount) */
+ /* MNT_DETACH (2) — lazy unmount; currently equivalent to immediate */
+ (void)umount_flags;
sc_ret(regs) = (uint32_t)vfs_umount(ktarget);
return;
}
/* Mount virtual filesystems (migrated from kernel-space).
* These must be done before spawning the shell since /dev/console
* is needed for terminal I/O. */
+static int is_mounted(const char* mountpoint) {
+ /* Check /proc/mounts for an existing entry */
+ int fd = open("/proc/mounts", 0);
+ if (fd < 0) return 0;
+ char buf[2048];
+ int n = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (n <= 0) return 0;
+ buf[n] = '\0';
+
+ int mplen = 0;
+ const char* p = mountpoint;
+ while (*p++) mplen++;
+
+ /* Scan lines: source mountpoint fstype options ... */
+ char* line = buf;
+ while (line && *line) {
+ char* nl = line;
+ while (*nl && *nl != '\n') nl++;
+ int eol = (*nl == '\n');
+ if (eol) *nl = '\0';
+ /* Skip source */
+ char* mp = line;
+ while (*mp && *mp != ' ') mp++;
+ if (*mp) mp++;
+ /* Compare mountpoint */
+ const char* lmp = mp;
+ const char* pmp = mountpoint;
+ while (*lmp != ' ' && *lmp != '\0' && *pmp && *lmp == *pmp) { lmp++; pmp++; }
+ if (*lmp == ' ' && *pmp == '\0') return 1;
+ line = eol ? nl + 1 : nl;
+ }
+ return 0;
+}
+
static void mount_virtual_fs(void) {
- if (mount("none", "/dev", "devfs", 0, NULL) < 0)
- fprintf(stderr, "init: mount devfs on /dev failed\n");
- if (mount("none", "/proc", "procfs", 0, NULL) < 0)
- fprintf(stderr, "init: mount procfs on /proc failed\n");
- if (mount("none", "/tmp", "tmpfs", 0, NULL) < 0)
- fprintf(stderr, "init: mount tmpfs on /tmp failed\n");
+ /* Only mount if not already mounted by kernel init.
+ * vfs_mount replaces existing entries, but re-creating root nodes
+ * (e.g. tmpfs) leaks the old instance. */
+ if (!is_mounted("/dev"))
+ if (mount("none", "/dev", "devfs", 0, NULL) < 0)
+ fprintf(stderr, "init: mount devfs on /dev failed\n");
+ if (!is_mounted("/proc"))
+ if (mount("none", "/proc", "procfs", 0, NULL) < 0)
+ fprintf(stderr, "init: mount procfs on /proc failed\n");
+ if (!is_mounted("/tmp"))
+ if (mount("none", "/tmp", "tmpfs", 0, NULL) < 0)
+ fprintf(stderr, "init: mount tmpfs on /tmp failed\n");
}
static void default_init(void) {
static void show_mounts(void) {
int fd = open("/proc/mounts", O_RDONLY);
if (fd >= 0) {
- char buf[1024];
+ char buf[2048];
int n;
while ((n = read(fd, buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, (size_t)n);
close(fd);
} else {
- printf("tmpfs on / type overlayfs (rw)\n");
- printf("devfs on /dev type devfs (rw)\n");
- printf("procfs on /proc type procfs (ro)\n");
+ fprintf(stderr, "mount: /proc/mounts not available\n");
}
}
const char* fstype = "diskfs";
const char* device = NULL;
const char* mountpoint = NULL;
+ unsigned long mountflags = 0;
/* Parse options first, then collect positional args */
int i;
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-t") == 0 && i + 1 < argc) {
fstype = argv[++i];
+ } else if (strcmp(argv[i], "-o") == 0 && i + 1 < argc) {
+ i++;
+ /* Parse comma-separated options */
+ const char* o = argv[i];
+ while (*o) {
+ if (strncmp(o, "ro", 2) == 0 && (o[2] == ',' || o[2] == '\0')) {
+ mountflags |= MS_RDONLY;
+ o += 2;
+ } else if (strncmp(o, "remount", 7) == 0 && (o[7] == ',' || o[7] == '\0')) {
+ mountflags |= MS_REMOUNT;
+ o += 7;
+ } else if (strncmp(o, "rw", 2) == 0 && (o[2] == ',' || o[2] == '\0')) {
+ o += 2;
+ } else {
+ /* Skip unknown option */
+ while (*o && *o != ',') o++;
+ }
+ if (*o == ',') o++;
+ }
} else if (!device) {
device = argv[i];
} else if (!mountpoint) {
}
}
- if (!device || !mountpoint) {
- fprintf(stderr, "usage: mount [-t fstype] device mountpoint\n");
+ if (!mountpoint) {
+ fprintf(stderr, "usage: mount [-t fstype] [-o options] device mountpoint\n");
return 1;
}
- int rc = mount(device, mountpoint, fstype, 0, NULL);
+ if (!device) device = "none";
+
+ int rc = mount(device, mountpoint, fstype, mountflags, NULL);
if (rc < 0) {
fprintf(stderr, "mount: mounting %s on %s failed: %s\n", device, mountpoint, strerror(errno));
return 1;
fprintf(stderr, "umount: missing operand\n");
return 1;
}
- if (umount(argv[1]) < 0) {
- fprintf(stderr, "umount: %s: %s\n", argv[1], strerror(errno));
+
+ int flags = 0;
+ const char* target = NULL;
+
+ for (int i = 1; i < argc; i++) {
+ if (strcmp(argv[i], "-f") == 0) {
+ flags |= MNT_FORCE;
+ } else if (strcmp(argv[i], "-l") == 0) {
+ flags |= MNT_DETACH;
+ } else {
+ target = argv[i];
+ }
+ }
+
+ if (!target) {
+ fprintf(stderr, "umount: missing operand\n");
+ return 1;
+ }
+
+ if (umount2(target, flags) < 0) {
+ fprintf(stderr, "umount: %s: %s\n", target, strerror(errno));
return 1;
}
return 0;
#include "syscall.h"
#include "errno.h"
#include "unistd.h"
+#include "string.h"
+
+/* Find the best-matching mount entry in /proc/mounts for a given path.
+ * Returns 1 if found, 0 otherwise. Fills fstype_out and flags_out. */
+static int find_mount_for_path(const char* path,
+ char* fstype_out, size_t fstype_sz,
+ char* source_out, size_t source_sz,
+ unsigned long* flags_out) {
+ int fd = open("/proc/mounts", 0);
+ if (fd < 0) return 0;
+
+ char mbuf[2048];
+ int n = read(fd, mbuf, sizeof(mbuf) - 1);
+ close(fd);
+ if (n <= 0) return 0;
+ mbuf[n] = '\0';
+
+ /* Find longest-prefix match among mount points */
+ int best_len = -1;
+ char best_fstype[32] = "";
+ char best_source[64] = "";
+ unsigned long best_flags = 0;
+
+ char* line = mbuf;
+ while (line && *line) {
+ char* nl = line;
+ while (*nl && *nl != '\n') nl++;
+ int eol = (*nl == '\n');
+ if (eol) *nl = '\0';
+
+ /* Parse: source mountpoint fstype options ... */
+ char* src = line;
+ char* mp = src;
+ while (*mp && *mp != ' ') mp++;
+ if (*mp) { *mp = '\0'; mp++; }
+
+ char* fst = mp;
+ while (*fst && *fst != ' ') fst++;
+ if (*fst) { *fst = '\0'; fst++; }
+
+ char* opts = fst;
+ while (*opts && *opts != ' ') opts++;
+ if (*opts) { *opts = '\0'; opts++; }
+
+ /* Check if path starts with mountpoint (prefix match) */
+ int mplen = 0;
+ const char* p = mp;
+ while (*p++) mplen++;
+
+ int match = 0;
+ if (mplen == 1 && mp[0] == '/') {
+ match = 1; /* root mount matches everything */
+ } else {
+ const char* pp = path;
+ const char* mmp = mp;
+ while (*mmp && *pp && *mmp == *pp) { mmp++; pp++; }
+ if (*mmp == '\0' && (*pp == '\0' || *pp == '/'))
+ match = 1;
+ }
+
+ if (match && mplen > best_len) {
+ best_len = mplen;
+ strncpy(best_fstype, fst, sizeof(best_fstype) - 1);
+ best_fstype[sizeof(best_fstype) - 1] = '\0';
+ strncpy(best_source, src, sizeof(best_source) - 1);
+ best_source[sizeof(best_source) - 1] = '\0';
+ /* Parse options for ro/rw */
+ best_flags = 0;
+ if (opts) {
+ /* Check for "ro" option */
+ const char* o = opts;
+ while (*o) {
+ if ((o == opts || *(o-1) == ',') &&
+ o[0] == 'r' && o[1] == 'o' &&
+ (o[2] == ',' || o[2] == ' ' || o[2] == '\0')) {
+ best_flags |= 1; /* MS_RDONLY */
+ break;
+ }
+ o++;
+ }
+ }
+ }
+
+ line = eol ? nl + 1 : nl;
+ }
+
+ if (best_len < 0) return 0;
+
+ if (fstype_out) { strncpy(fstype_out, best_fstype, fstype_sz - 1); fstype_out[fstype_sz - 1] = '\0'; }
+ if (source_out) { strncpy(source_out, best_source, source_sz - 1); source_out[source_sz - 1] = '\0'; }
+ if (flags_out) *flags_out = best_flags;
+ return 1;
+}
int statvfs(const char* path, struct statvfs* buf) {
struct stat st;
if (stat(path, &st) < 0)
return -1;
- /* Build a pseudo statvfs from stat info.
- * AdrOS has no f_bsize/f_blocks syscall, so we estimate from st_blksize. */
unsigned long bsize = (unsigned long)st.st_blksize;
- if (bsize == 0) bsize = 512;
+ if (bsize == 0) bsize = 4096;
+
+ /* Find mount info for this path */
+ char fstype[32] = "unknown";
+ char source[64] = "none";
+ unsigned long mflags = 0;
+ find_mount_for_path(path, fstype, sizeof(fstype), source, sizeof(source), &mflags);
buf->f_bsize = bsize;
buf->f_frsize = bsize;
- /* For initrd/overlayfs, report the file size as "used" and assume
- * some free space. This is a best-effort approximation. */
- buf->f_blocks = (unsigned long)st.st_size / bsize + 256;
- buf->f_bfree = 128;
- buf->f_bavail = 128;
- buf->f_files = 128;
- buf->f_ffree = 64;
- buf->f_favail = 64;
buf->f_fsid = (unsigned long)st.st_dev;
- buf->f_flag = 0;
+ buf->f_flag = mflags; /* MS_RDONLY etc. */
buf->f_namemax = 255;
- /* Try to read actual values from /proc/mounts if available */
- int fd = open("/proc/mounts", 0 /* O_RDONLY */);
- if (fd >= 0) {
- char mbuf[1024];
- int n = read(fd, mbuf, sizeof(mbuf) - 1);
- close(fd);
- if (n > 0) {
- mbuf[n] = '\0';
- /* Look for the mount point in the output */
- int plen = 0;
- const char* p = path;
- while (*p++) plen++;
- /* Parse lines: dev dir type ... */
- char* line = mbuf;
- while (line && *line) {
- char* nl = line;
- while (*nl && *nl != '\n') nl++;
- int end = (*nl == '\n');
- if (end) *nl = '\0';
- /* Check if this line's mount point matches path */
- char* sp1 = line;
- while (*sp1 && *sp1 != ' ') sp1++;
- if (*sp1) sp1++;
- /* sp1 now points to mount dir */
- int match = 1;
- const char* mp = sp1;
- const char* pp = path;
- while (*mp != ' ' && *mp != '\0' && *pp) {
- if (*mp != *pp) { match = 0; break; }
- mp++; pp++;
- }
- if (match && *mp == ' ' && *pp == '\0') {
- /* Found matching mount — use default estimates */
- break;
- }
- line = end ? nl + 1 : nl;
- }
- }
+ /* Derive block counts based on filesystem type */
+ if (strcmp(fstype, "tmpfs") == 0) {
+ /* tmpfs: size is limited by available memory */
+ buf->f_blocks = 16384; /* ~64 MB assuming 4K blocks */
+ buf->f_bfree = 8192;
+ buf->f_bavail = 8192;
+ buf->f_files = 256;
+ buf->f_ffree = 128;
+ buf->f_favail = 128;
+ } else if (strcmp(fstype, "procfs") == 0 || strcmp(fstype, "devfs") == 0) {
+ /* Virtual/pseudo filesystems — zero blocks */
+ buf->f_blocks = 0;
+ buf->f_bfree = 0;
+ buf->f_bavail = 0;
+ buf->f_files = 64;
+ buf->f_ffree = 32;
+ buf->f_favail = 32;
+ } else if (strcmp(fstype, "overlayfs") == 0) {
+ /* Overlay: report based on stat info */
+ buf->f_blocks = (unsigned long)st.st_size / bsize + 512;
+ buf->f_bfree = 256;
+ buf->f_bavail = 256;
+ buf->f_files = 256;
+ buf->f_ffree = 128;
+ buf->f_favail = 128;
+ } else {
+ /* Disk-based (diskfs, fat, ext2, persistfs): estimate from stat */
+ buf->f_blocks = (unsigned long)st.st_size / bsize + 512;
+ buf->f_bfree = 256;
+ buf->f_bavail = 256;
+ buf->f_files = 256;
+ buf->f_ffree = 128;
+ buf->f_favail = 128;
}
return 0;