#include <stdint.h>
#include <stddef.h>
+#include "spinlock.h"
#define FS_FILE 0x01
#define FS_DIRECTORY 0x02
int vfs_mount(const char* mountpoint, fs_node_t* root);
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_umount_nolock(const char* mountpoint);
+
+/* 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;
+
// Global root of the filesystem
extern fs_node_t* fs_root;
#include "utils.h"
#include "errno.h"
+#include "spinlock.h"
fs_node_t* fs_root = NULL;
static fs_node_t* g_initrd_root = NULL;
+/* Global VFS spinlock — protects fs_root, g_mounts[], and g_mount_count.
+ * Must be held across any compound operation (e.g. pivot_root) that
+ * modifies more than one of these fields. */
+spinlock_t g_vfs_lock;
+
struct vfs_mount {
char mountpoint[128];
fs_node_t* root;
}
}
-int vfs_mount(const char* mountpoint, fs_node_t* root) {
+int vfs_mount_nolock(const char* mountpoint, fs_node_t* root) {
if (!root) return -EINVAL;
if (g_mount_count >= (int)(sizeof(g_mounts) / sizeof(g_mounts[0]))) return -ENOSPC;
return 0;
}
-int vfs_umount(const char* mountpoint) {
+int vfs_mount(const char* mountpoint, fs_node_t* root) {
+ uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
+ int ret = vfs_mount_nolock(mountpoint, root);
+ spin_unlock_irqrestore(&g_vfs_lock, fl);
+ return ret;
+}
+
+int vfs_umount_nolock(const char* mountpoint) {
char mp[128];
normalize_mountpoint(mountpoint, mp, sizeof(mp));
return -EINVAL;
}
+int vfs_umount(const char* mountpoint) {
+ uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
+ int ret = vfs_umount_nolock(mountpoint);
+ spin_unlock_irqrestore(&g_vfs_lock, fl);
+ return ret;
+}
+
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);
}
static fs_node_t* vfs_lookup_depth(const char* path, int depth) {
- if (!path || !fs_root) return NULL;
+ if (!path) return NULL;
if (depth > 8) return NULL;
+ /* Snapshot mount-table state under the VFS lock so that concurrent
+ * mount/umount/pivot_root on another CPU cannot corrupt our view. */
+ uintptr_t fl = spin_lock_irqsave(&g_vfs_lock);
+
+ if (!fs_root) { spin_unlock_irqrestore(&g_vfs_lock, fl); return NULL; }
+
fs_node_t* base = fs_root;
const char* rel = path;
size_t best_len = 0;
}
}
+ spin_unlock_irqrestore(&g_vfs_lock, fl);
+
if (!rel) return NULL;
while (*rel == '/') rel++;
if (*rel == 0) return base;
sc_ret(regs) = (uint32_t)-EINVAL;
return;
}
+ /* Hold g_vfs_lock across the compound mutation so that
+ * concurrent vfs_lookup() on other CPUs never sees an
+ * inconsistent (fs_root, mount-table) pair. */
+ uintptr_t vfs_fl = spin_lock_irqsave(&g_vfs_lock);
fs_node_t* old_root = fs_root;
fs_root = new_root;
- (void)vfs_mount("/", new_root);
+ (void)vfs_mount_nolock("/", new_root);
if (old_root) {
- (void)vfs_mount(kput, old_root);
+ (void)vfs_mount_nolock(kput, old_root);
}
+ spin_unlock_irqrestore(&g_vfs_lock, vfs_fl);
sc_ret(regs) = 0;
return;
}
act.sa_mask = 0;
act.sa_flags = 0;
- struct sigaction oldact = {{0}};
+ struct sigaction oldact = {0};
struct sigaction* oldp = old_out ? &oldact : 0;
int r = sys_sigaction2(sig, &act, oldp);
(uint32_t)(sizeof("[test] execveat OK\n") - 1));
}
- // I17: pivot_root — mount tmpfs + pivot_root + verify (isolated fork)
+ // I17: pivot_root — mount tmpfs + pivot_root + undo (isolated fork)
{
int pid = sys_fork();
if (pid < 0) {
sys_exit(1);
}
+ /* Undo pivot_root to restore global VFS state.
+ * Without per-process mount namespaces, pivot_root modifies
+ * the global fs_root and "/" mount entry. After the child
+ * exits the parent (and the shell) would see an empty tmpfs
+ * as "/", breaking all command lookups via access()/vfs_lookup().
+ * Pivot back so the overlayfs root is restored. */
+ (void)sys_mkdir("/undo_dir");
+ if (sys_pivot_root("/tmp/pivot_old", "/undo_dir") < 0) {
+ sys_write(1, "[test] pivot_root undo failed\n",
+ (uint32_t)(sizeof("[test] pivot_root undo failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_umount2("/undo_dir");
+ (void)sys_umount2("/tmp/pivot_old");
+ (void)sys_umount2("/tmp/pivot_new");
+
+ /* Verify that the original mount points are accessible
+ * after undoing pivot_root. */
+ {
+ int vfd = sys_open("/dev/null", 0);
+ if (vfd < 0) {
+ sys_write(1, "[test] pivot_root undo verify /dev failed\n",
+ (uint32_t)(sizeof("[test] pivot_root undo verify /dev failed\n") - 1));
+ sys_exit(1);
+ }
+ (void)sys_close(vfd);
+ }
+
sys_exit(0);
}
int st = 0;
}
char* strrchr(const char* s, int c) {
+ if (!s) return (void*)0;
const char* last = (void*)0;
while (*s) {
if (*s == (char)c) last = s;