vfs interface

This commit is contained in:
2026-10-06 01:59:08 +02:00
parent f8eb4f4e54
commit 5d672d8132
+376 -31
View File
@@ -3,13 +3,35 @@
#include <zinnia/errno.h>
#include <zinnia/ident.h>
#include <zinnia/iovec.h>
#include <zinnia/mman.h>
#include <zinnia/mutex.h>
#include <zinnia/refcount.h>
#include <zinnia/spin.h>
#include <zinnia/tailq.h>
#include <zinnia/time.h>
#include <zinnia/types.h>
#include <zinnia/vec.h>
#include <zinnia/vma.h>
#include <zinnia/vmo.h>
#include <stddef.h>
#include <stdint.h>
#include <uapi/dirent.h>
#include <uapi/fcntl.h>
#include <uapi/limits.h>
#include <uapi/mount.h>
#include <uapi/stat.h>
#include <uapi/statvfs.h>
#include <uapi/time.h>
struct vfs_mount;
struct vfs_entry;
struct vfs_path;
struct vfs_node;
struct vfs_file;
struct vfs_super_block;
struct vfs_filesystem;
struct vfs_node_ops;
struct vfs_file_ops;
enum open_flags : uint32_t {
O_RDONLY = __O_RDONLY,
@@ -38,24 +60,30 @@ enum open_flags : uint32_t {
O_ACCMODE = __O_ACCMODE,
};
struct vfs_mount {
struct refcount rc;
// TODO
enum mount_flags : uint32_t {
MNT_RDONLY = __MNT_RDONLY,
MNT_NOSUID = __MNT_NOSUID,
MNT_NOEXEC = __MNT_NOEXEC,
MNT_RELATIME = __MNT_RELATIME,
MNT_NOATIME = __MNT_NOATIME,
MNT_REMOUNT = __MNT_REMOUNT,
MNT_FORCE = __MNT_FORCE,
};
struct vfs_entry {
struct refcount rc;
// TODO
struct exec_info {
struct vma* space;
struct vmo* file_obj;
const char** argv;
const char** envp;
};
struct vfs_path {
struct vfs_mount* mount;
struct vfs_entry* entry;
enum vfs_lookup_flags : uint32_t {
VFS_LOOKUP_FOLLOW_SYMLINKS = 1 << 0,
VFS_LOOKUP_MUST_EXIST = 1 << 1,
VFS_LOOKUP_MUST_NOT_EXIST = 1 << 2,
VFS_LOOKUP_REAL_ID = 1 << 3,
};
errno_t vfs_path_mount();
errno_t vfs_path_lookup(struct vfs_path);
enum vfs_node_type {
VNODE_REGULAR,
VNODE_DIRECTORY,
@@ -64,56 +92,373 @@ enum vfs_node_type {
VNODE_BLOCKDEV,
VNODE_CHARDEV,
VNODE_SOCKET,
VNODE_ANONYMOUS,
};
enum vfs_entry_state {
VFS_ENTRY_NOT_CACHED, // Entry hasn't been looked up yet.
VFS_ENTRY_PRESENT, // Entry is positive and contains a link to the inode.
VFS_ENTRY_NOT_PRESENT, // Entry is negative.
};
struct vfs_entry {
struct refcount rc;
// The name of this entry.
char* name;
size_t name_len;
struct mutex lock;
enum vfs_entry_state state;
// The inode this entry points to when present.
struct vfs_node* inode;
// The parent of this entry, weak reference.
struct vfs_entry* parent;
// All cached children of this entry.
tailq_head(struct vfs_entry) children;
tailq_link(struct vfs_entry) child_link;
// A list of mounts on this entry.
vec(struct vfs_mount*) mounts;
};
struct vfs_path {
struct vfs_mount* mount;
struct vfs_entry* entry;
};
struct vfs_path vfs_path_clone(struct vfs_path path);
void vfs_path_drop(struct vfs_path path);
struct vfs_mount {
struct refcount rc;
enum mount_flags flags;
struct vfs_entry* root;
struct vfs_path mount_point;
bool has_mount_point;
};
void vfs_mount_ref(struct vfs_mount* mount);
void vfs_mount_unref(struct vfs_mount* mount);
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);
struct vfs_super_block_ops {
errno_t (*sync)(struct vfs_super_block* sb);
errno_t (*statvfs)(struct vfs_super_block* sb, struct __statvfs* out);
void (*destroy)(struct vfs_super_block* sb);
};
struct vfs_super_block {
struct refcount rc;
const struct vfs_super_block_ops* ops;
};
// TODO: Maybe not like this
struct vfs_mknod_target {
enum vfs_node_type type;
const struct vfs_node_ops* ops;
void* priv;
};
struct vfs_file;
struct vfs_node;
struct vfs_node_ops {
errno_t (*sync)();
// Synchronizes the node metadata back to the underlying file system.
errno_t (*sync)(struct vfs_node* node);
// Destroys private state and drops references.
void (*destroy)(struct vfs_node* node);
// Operations that can be performed on an open file of this node.
const struct vfs_file_ops* file_ops;
union {
// VNODE_REGULAR
struct {
errno_t (*truncate)(struct vfs_node*, uint64_t new_length);
// Truncates the file down to `new_length` bytes.
errno_t (*truncate)(struct vfs_node* node, uint64_t new_length);
} regular;
// VNODE_SYMLINK
struct {
errno_t (*read_link)(struct vfs_node*, char* buf, size_t len, size_t* out_written);
// Reads the value of the symlink into `buf`.
errno_t (*read_link)(struct vfs_node* node, struct iovec_iter* iter, size_t* out_read);
} symlink;
// VNODE_DIR
struct {
errno_t (*lookup)(struct vfs_node*, struct vfs_path entry);
errno_t (*lookup)(struct vfs_node* node, struct vfs_path* child);
errno_t (*open)(
struct vfs_node*,
struct vfs_path entry,
struct vfs_node* node,
struct vfs_path* path,
enum open_flags flags,
const struct ident* identity,
struct vfs_file* out_file
struct vfs_file** out_file
);
errno_t (*create)(
struct vfs_node* node,
struct vfs_entry* entry,
mode_t mode,
const struct ident* identity
);
errno_t (*mkdir)(struct vfs_node* node, struct vfs_path* path, mode_t mode, const struct ident* identity);
errno_t (*symlink)(
struct vfs_node* node,
struct vfs_path* path,
const char* target,
size_t target_len,
const struct ident* identity
);
errno_t (*link)(
struct vfs_node* node,
struct vfs_path* path,
struct vfs_node* target,
const struct ident* identity
);
errno_t (*unlink)(struct vfs_node* node, struct vfs_path* path, const struct ident* identity);
errno_t (*rmdir)(struct vfs_node* node, struct vfs_path* path, const struct ident* identity);
errno_t (*rename)(
struct vfs_node* node,
struct vfs_path* path,
struct vfs_node* target,
struct vfs_path* target_path,
const struct ident* identity
);
errno_t (*mknod)(
struct vfs_node* node,
mode_t mode,
struct vfs_mknod_target* target,
const struct ident* identity,
struct vfs_node** out
);
errno_t (*get_dir_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
);
errno_t (*create)(struct vfs_node*, const struct ident* identity);
} dir;
};
};
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;
struct vfs_super_block* sb;
struct mutex lock;
void* priv;
ino_t id;
size_t size;
mode_t mode;
uid_t uid;
gid_t gid;
timespec_t atime;
timespec_t mtime;
timespec_t ctime;
};
struct vfs_file;
struct vfs_file_ops {
errno_t (*read)(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read);
errno_t (*write)(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written);
errno_t (*ioctl)(struct vfs_file* file, uintptr_t request, uintptr_t arg, uintptr_t* out_result);
errno_t (*sync)(struct vfs_file* file, bool data_only);
errno_t (*mmap)(
struct vfs_file* file,
struct vma* vma,
uintptr_t addr,
size_t len,
enum prot_flags prot,
enum map_flags flags,
off_t offset,
uintptr_t* out_addr
);
bool (*seekable)(struct vfs_file* file);
};
enum vfs_file_position {
VFS_POSITION_STREAM, // The file has no meaningful position.
VFS_POSITION_POSITION, // The file has a position, but accesses do not serialize the whole operation.
VFS_POSITION_ATOMIC, // Accesses through the shared file position are serialized.
};
enum vfs_seek_anchor {
VFS_SEEK_START,
VFS_SEEK_CURRENT,
VFS_SEEK_END,
};
// The kernel representation of an open file.
struct vfs_file {
struct refcount rc;
const struct vfs_file_ops* ops; // vtable for operations on this open file.
struct vfs_node* vnode; // The opened vnode.
const char* abs_path; // Absolute path resolved during `open()`.
struct spinlock lock;
const struct vfs_file_ops* ops;
struct vfs_node* vnode;
struct vfs_path path; // The cached entry pointing to this node.
bool has_path; // If this file has a path.
// Absolute path resolved during open.
char* absolute_path;
enum open_flags flags;
enum vfs_file_position position_kind;
off_t position;
struct mutex lock;
struct vfs_path path;
bool has_path;
};
errno_t vfs_file_read(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read);
errno_t vfs_file_write(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written);
// A file system driver.
struct vfs_filesystem {
const char* name;
errno_t (*mount)(const struct vfs_filesystem* fs, enum mount_flags flags, void* arg, struct vfs_mount** 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);
void vfs_entry_unref(struct vfs_entry* entry);
void vfs_file_ref(struct vfs_file* file);
void vfs_file_unref(struct vfs_file* file);
void vfs_super_block_ref(struct vfs_super_block* sb);
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();
// Resolves a `path` to a VFS path, relative to `root` and `cwd`.
// Returns the result in `out` if the lookup with the given flags was successful.
errno_t vfs_path_lookup(
struct vfs_path root,
struct vfs_path cwd,
const char* path,
const struct ident* identity,
enum vfs_lookup_flags flags,
struct vfs_path* out
);
errno_t vfs_path_lookup_parent(struct vfs_path path, struct vfs_path* out);
errno_t vfs_path_lookup_child(
struct vfs_path path,
struct vfs_path* root,
const char* name,
size_t name_len,
struct vfs_path* out
);
// Builds the absolute path of `path` from `root` and stores the result in `buf`.
// Returns the amount of actually written bytes in `out_len`.
errno_t vfs_path_absolute(struct vfs_path path, struct vfs_path* root, char* buf, size_t len, size_t* out_len);
// Creates a new directory at `path`, relative to `root` and `cwd` with `mode`.
errno_t vfs_mkdir(
struct vfs_path root,
struct vfs_path cwd,
const char* path,
mode_t mode,
const struct ident* identity
);
// Creates a new symbolic link at `path`, relative to `root` and `cwd`, pointing to `target`.
errno_t vfs_symlink(
struct vfs_path root,
struct vfs_path cwd,
const char* path,
const char* target,
const struct ident* identity
);
// Unlinks a file at `path`, relative to `root` and `cwd`.
errno_t vfs_unlink(struct vfs_path root, struct vfs_path cwd, const char* path, const struct ident* identity);
// Removes a directory at `path`, relative to `root` and `cwd`.
errno_t vfs_rmdir(struct vfs_path root, struct vfs_path cwd, const char* path, const struct ident* identity);
errno_t vfs_mknod(
struct vfs_path root,
struct vfs_path cwd,
const char* path,
mode_t mode,
struct vfs_mknod_target* target,
const struct ident* identity
);
// Checks if an access to a `node` with `flags` 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);
// Opens a node at `path`, relative to `root` and `cwd`.
// Returns the opened node as a file in `out`.
errno_t vfs_file_open(
struct vfs_path root,
struct vfs_path cwd,
const char* path,
enum open_flags flags,
mode_t mode,
const struct ident* identity,
struct vfs_file** 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, struct iovec_iter* buffer, ssize_t* out_read);
// Same as `vfs_file_read`, but from a given offset.
errno_t vfs_file_pread(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_read);
// Wites to a `file` from a `buffer` from the current offset.
// Returns the actual bytes written in `out_written`.
errno_t vfs_file_write(struct vfs_file* file, struct iovec_iter* buffer, ssize_t* out_written);
// Same as `vfs_file_write`, but from a given offset.
errno_t vfs_file_pwrite(struct vfs_file* file, struct iovec_iter* buffer, uint64_t offset, ssize_t* out_written);
// Performs a generic `request` on a `file`.
// Returns the result in `out_result`.
errno_t vfs_file_ioctl(struct vfs_file* file, uintptr_t request, uintptr_t arg, uintptr_t* out_result);
errno_t vfs_file_sync(struct vfs_file* file, bool data_only);
errno_t vfs_file_seek(struct vfs_file* file, enum vfs_seek_anchor anchor, off_t offset, off_t* out_position);
off_t vfs_file_tell(struct vfs_file* file);
// Maps `len` bytes of a `file` from `offset` into `vma` at `addr`.
// Returns the start of the mapped region in `out_addr`.
errno_t vfs_file_mmap(
struct vfs_file* file,
struct vma* vma,
uintptr_t addr,
size_t len,
enum prot_flags prot,
enum map_flags flags,
off_t offset,
uintptr_t* out_addr
);
// Directory listing.
errno_t vfs_get_dir_entries(
struct vfs_file* file,
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,
enum vfs_node_type type,
mode_t mode,
const struct ident* identity,
struct vfs_node** out
);