fs: Initial VFS port

This commit is contained in:
2026-10-25 18:35:03 +01:00
parent efe1feb852
commit d14f895288
18 changed files with 3296 additions and 23 deletions
+53
View File
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: ISC
#pragma once
#include <zinnia/errno.h>
#include <zinnia/tailq.h>
#include <stddef.h>
#include <stdint.h>
struct process;
struct vfs_file;
struct vma;
struct vmo;
// Information passed to `exec_format.load`.
struct exec_info {
// An address space for the new process.
struct vma* space;
// The excutable to load.
struct vmo* file_obj;
// Path passed to execve.
const char* exec_path;
// Arguments.
const char** argv;
// Environment variables.
const char** envp;
size_t depth;
};
// An executable format.
struct exec_format {
const char* name;
// Identifies whether a file is a valid executable of this format.
bool (*identify)(struct vmo* obj);
// Loads the executable and returns a new initial thread.
// The implementation should not modify `proc` at all.
errno_t (*load)(struct process* proc, struct exec_info* info, uintptr_t* out_ip, uintptr_t* out_sp);
tailq_link(struct exec_format) link;
};
// Attempts to identify the format of this executable file.
struct exec_format* exec_identify(struct vmo* obj);
// Installs a new executable format.
void exec_register(struct exec_format* format);
errno_t exec_open(struct process* proc, const char* path, struct vmo** out);
errno_t exec_file_object(struct process* proc, struct vfs_file* file, struct vmo** out);
+2
View File
@@ -15,3 +15,5 @@ struct ident {
const struct ident* ident_kernel(void);
bool ident_in_group(const struct ident* identity, gid_t gid, bool use_real);
bool ident_is_privileged(const struct ident* identity, bool use_real);
+14
View File
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: ISC
#pragma once
#include <zinnia/errno.h>
#include <zinnia/ident.h>
#include <zinnia/vfs.h>
errno_t pipe_create(
enum open_flags flags,
const struct ident* identity,
struct vfs_file** out_read,
struct vfs_file** out_write
);
+131 -19
View File
@@ -4,9 +4,11 @@
#include <zinnia/errno.h>
#include <zinnia/ident.h>
#include <zinnia/initgraph.h>
#include <zinnia/iovec.h>
#include <zinnia/mman.h>
#include <zinnia/mutex.h>
#include <zinnia/poll.h>
#include <zinnia/refcount.h>
#include <zinnia/spin.h>
#include <zinnia/tailq.h>
@@ -34,6 +36,7 @@ struct vfs_super_block;
struct vfs_filesystem;
struct vfs_node_ops;
struct vfs_file_ops;
struct pipe;
enum open_flags : uint32_t {
O_RDONLY = __O_RDONLY,
@@ -62,6 +65,13 @@ enum open_flags : uint32_t {
O_ACCMODE = __O_ACCMODE,
};
enum access_flags : uint32_t {
F_OK = __F_OK,
X_OK = __X_OK,
W_OK = __W_OK,
R_OK = __R_OK,
};
enum mount_flags : uint32_t {
MNT_RDONLY = __MNT_RDONLY,
MNT_NOSUID = __MNT_NOSUID,
@@ -72,13 +82,6 @@ enum mount_flags : uint32_t {
MNT_FORCE = __MNT_FORCE,
};
struct exec_info {
struct vma* space;
struct vmo* file_obj;
const char** argv;
const char** envp;
};
enum vfs_lookup_flags : uint32_t {
VFS_LOOKUP_FOLLOW_SYMLINKS = 1 << 0,
VFS_LOOKUP_MUST_EXIST = 1 << 1,
@@ -103,6 +106,7 @@ enum vfs_entry_state {
VFS_ENTRY_NOT_PRESENT, // Entry is negative.
};
// A namecache entry.
struct vfs_entry {
struct refcount rc;
// The name of this entry.
@@ -111,12 +115,12 @@ struct vfs_entry {
mutex_t lock;
enum vfs_entry_state state;
// The inode this entry points to when present.
// The inode this entry points to when present. Holds a reference.
struct vfs_node* inode;
// The parent of this entry, weak reference.
// The parent of this entry, strong reference. `nullptr` for file system roots.
struct vfs_entry* parent;
// All cached children of this entry.
// All cached children of this entry. Each child in this list holds a reference.
tailq_head(struct vfs_entry) children;
tailq_link(struct vfs_entry) child_link;
@@ -132,6 +136,22 @@ struct vfs_path {
struct vfs_path vfs_path_clone(struct vfs_path path);
void vfs_path_drop(struct vfs_path path);
// Allocates a new, uncached entry with the given `name`. `parent` gets referenced if non-null.
errno_t vfs_entry_new(const char* name, size_t name_len, struct vfs_entry* parent, struct vfs_entry** out);
// Makes `entry` positive and points it to `node`. Takes a new reference to `node`.
void vfs_entry_set_inode(struct vfs_entry* entry, struct vfs_node* node);
// Returns a new reference to the inode of `entry`, or `nullptr` if the entry is negative.
struct vfs_node* vfs_entry_get_inode(struct vfs_entry* entry);
// Makes `entry` negative and drops its reference to the inode.
void vfs_entry_set_negative(struct vfs_entry* entry);
void vfs_entry_replace_children(struct vfs_entry* dst, struct vfs_entry* src);
void vfs_entry_prune(struct vfs_entry* entry);
// A mount point.
struct vfs_mount {
struct refcount rc;
enum mount_flags flags;
@@ -142,9 +162,18 @@ struct vfs_mount {
void vfs_mount_ref(struct vfs_mount* mount);
void vfs_mount_unref(struct vfs_mount* mount);
// Creates a unique instance of a mount point.
errno_t vfs_mount(const char* fs_name, enum mount_flags flags, void* arg, struct vfs_mount** out);
errno_t vfs_unmount(struct vfs_mount* mount);
// Allocates a new mount with `root` as its root entry. Takes a new reference to `root`.
errno_t vfs_mount_new(enum mount_flags flags, struct vfs_entry* root, struct vfs_mount** out);
// Attaches `mount` on top of the entry referenced by `at`.
errno_t vfs_path_mount(struct vfs_path at, struct vfs_mount* mount);
errno_t vfs_path_unmount(struct vfs_path path);
struct vfs_super_block_ops {
errno_t (*sync)(struct vfs_super_block* sb);
errno_t (*statvfs)(struct vfs_super_block* sb, struct __statvfs* out);
@@ -154,6 +183,7 @@ struct vfs_super_block_ops {
struct vfs_super_block {
struct refcount rc;
const struct vfs_super_block_ops* ops;
enum mount_flags flags;
};
// TODO: Maybe not like this
@@ -161,6 +191,7 @@ struct vfs_mknod_target {
enum vfs_node_type type;
const struct vfs_node_ops* ops;
void* priv;
dev_t rdev;
};
struct vfs_node_ops {
@@ -176,6 +207,8 @@ struct vfs_node_ops {
struct {
// Truncates the file down to `new_length` bytes.
errno_t (*truncate)(struct vfs_node* node, uint64_t new_length);
// Returns a new reference to a memory object holding the file contents.
errno_t (*get_vmo)(struct vfs_node* node, struct vmo** out);
} regular;
// VNODE_SYMLINK
@@ -184,6 +217,11 @@ struct vfs_node_ops {
errno_t (*read_link)(struct vfs_node* node, iovec_iter_t* iter, size_t* out_read);
} symlink;
// VNODE_CHARDEV or VNODE_BLOCKDEV
struct {
errno_t (*open)(struct vfs_node* node, struct vfs_file* file);
} device;
// VNODE_DIR
struct {
errno_t (*lookup)(struct vfs_node* node, struct vfs_path* child);
@@ -225,10 +263,10 @@ struct vfs_node_ops {
);
errno_t (*mknod)(
struct vfs_node* node,
struct vfs_path* path,
mode_t mode,
struct vfs_mknod_target* target,
const struct ident* identity,
struct vfs_node** out
const struct ident* identity
);
errno_t (*get_dir_entries)(
struct vfs_node* node,
@@ -246,7 +284,6 @@ struct vfs_node_ops {
struct vfs_node {
struct refcount rc;
enum vfs_node_type type;
bool is_seekable;
const struct vfs_node_ops* ops;
const struct vfs_file_ops* file_ops;
@@ -254,9 +291,13 @@ struct vfs_node {
mutex_t lock;
void* priv;
struct pipe* pipe;
ino_t id;
dev_t rdev;
size_t size;
blkcnt_t blocks;
nlink_t nlink;
mode_t mode;
uid_t uid;
gid_t gid;
@@ -280,7 +321,8 @@ struct vfs_file_ops {
off_t offset,
uintptr_t* out_addr
);
bool (*seekable)(struct vfs_file* file);
enum poll_flags (*poll)(struct vfs_file* file, enum poll_flags events, struct poll_table* table);
void (*release)(struct vfs_file* file);
};
enum vfs_file_position {
@@ -303,7 +345,7 @@ struct vfs_file {
// Absolute path resolved during open.
char* absolute_path;
enum open_flags flags;
_Atomic(enum open_flags) flags;
enum vfs_file_position position_kind;
off_t position;
@@ -311,6 +353,9 @@ struct vfs_file {
struct vfs_path path;
bool has_path;
// Driver data.
void* priv;
};
// A file system driver.
@@ -319,6 +364,16 @@ struct vfs_filesystem {
errno_t (*mount)(const struct vfs_filesystem* fs, enum mount_flags flags, void* arg, struct vfs_mount** out);
};
// Allocates a new inode in `sb` with `type`, `ops` and permission bits `mode`.
// The inode takes a reference to `sb` if it's not `nullptr`.
errno_t vfs_node_new(
struct vfs_super_block* sb,
enum vfs_node_type type,
const struct vfs_node_ops* ops,
mode_t mode,
struct vfs_node** out
);
void vfs_node_ref(struct vfs_node* node);
void vfs_node_unref(struct vfs_node* node);
void vfs_entry_ref(struct vfs_entry* entry);
@@ -331,8 +386,6 @@ void vfs_super_block_unref(struct vfs_super_block* sb);
// Gets the kernel path representation of the absolute root.
struct vfs_path vfs_get_root();
void vfs_init();
errno_t vfs_register_filesystem(const struct vfs_filesystem* fs);
errno_t vfs_sync_all();
@@ -392,12 +445,46 @@ errno_t vfs_mknod(
const char* path,
mode_t mode,
struct vfs_mknod_target* target,
const struct ident* identity,
struct vfs_node** out
);
errno_t vfs_rename(
struct vfs_path root,
struct vfs_path old_cwd,
const char* old_path,
struct vfs_path new_cwd,
const char* new_path,
const struct ident* identity
);
// Checks if an access to a `node` with `flags` using `identity` is allowed.
errno_t vfs_link(
struct vfs_path root,
struct vfs_path old_cwd,
const char* old_path,
struct vfs_path new_cwd,
const char* new_path,
bool follow_symlinks,
const struct ident* identity
);
void vfs_stat(struct vfs_node* node, struct __stat* out);
errno_t vfs_statvfs(struct vfs_node* node, struct __statvfs* out);
errno_t vfs_chmod(struct vfs_node* node, mode_t mode, const struct ident* identity);
errno_t vfs_chown(struct vfs_node* node, uid_t uid, gid_t gid, const struct ident* identity);
errno_t vfs_utimens(struct vfs_node* node, const timespec_t* times, const struct ident* identity);
errno_t vfs_node_check_exec(struct vfs_node* node, const struct ident* identity);
errno_t vfs_truncate(struct vfs_node* node, uint64_t length);
errno_t vfs_read_link(struct vfs_node* node, iovec_iter_t* iter, size_t* out_read);
// Checks if an access to a `node` with `access` using `identity` is allowed.
// Returns 0 if the access is allowed.
errno_t vfs_node_try_access(struct vfs_node* node, const struct ident* identity, enum open_flags flags, bool use_real);
errno_t vfs_node_try_access(
struct vfs_node* node,
const struct ident* identity,
enum access_flags access,
bool use_real
);
// Opens a node at `path`, relative to `root` and `cwd`.
// Returns the opened node as a file in `out`.
@@ -411,6 +498,16 @@ errno_t vfs_file_open(
struct vfs_file** out
);
// Creates a new open file backed by `node`, which must not be `nullptr`.
// If `path` is not `nullptr`, the file remembers it and takes a reference to it.
errno_t vfs_file_new(struct vfs_node* node, const struct vfs_path* path, enum open_flags flags, struct vfs_file** out);
// Changes the file's position mode to a stream.
void vfs_stream_open(struct vfs_file* file);
// Gets a new reference to a memory object holding the contents of `file`.
errno_t vfs_file_get_object(struct vfs_file* file, struct vmo** out);
// Reads from a `file` into a `buffer` from the current offset.
// Returns the actual bytes read in `out_read`.
errno_t vfs_file_read(struct vfs_file* file, iovec_iter_t* buffer, ssize_t* out_read);
@@ -447,6 +544,8 @@ errno_t vfs_file_mmap(
uintptr_t* out_addr
);
enum poll_flags vfs_poll(struct vfs_file* file, enum poll_flags events, struct poll_table* table);
// Directory listing.
errno_t vfs_get_dir_entries(
struct vfs_file* file,
@@ -456,6 +555,17 @@ errno_t vfs_get_dir_entries(
size_t* out_count
);
// Generic directory listing implementation which walks the cached children of `entry`.
errno_t vfs_dir_get_cached_entries(
struct vfs_node* node,
struct vfs_entry* entry,
off_t offset,
struct __dirent* buffer,
size_t buffer_len,
const struct ident* identity,
size_t* out_count
);
// Creates an inode belonging to the anonymous pseudo file system.
errno_t vfs_create_anon_inode(
const struct vfs_file_ops* file_ops,
@@ -464,3 +574,5 @@ errno_t vfs_create_anon_inode(
const struct ident* identity,
struct vfs_node** out
);
INIT_EXTERN(vfs_stage);