From 5d672d813280bd696c4062db226bd1c2bcecdff1 Mon Sep 17 00:00:00 2001 From: Marvin Friedrich Date: Tue, 6 Oct 2026 01:59:08 +0200 Subject: [PATCH] vfs interface --- include/zinnia/vfs.h | 407 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 376 insertions(+), 31 deletions(-) diff --git a/include/zinnia/vfs.h b/include/zinnia/vfs.h index cd82666..5165730 100644 --- a/include/zinnia/vfs.h +++ b/include/zinnia/vfs.h @@ -3,13 +3,35 @@ #include #include #include +#include +#include #include #include +#include +#include #include +#include +#include +#include #include #include +#include #include +#include +#include #include +#include +#include + +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 +);