fs: Initial VFS port
This commit is contained in:
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user