+void
+fsm_itbegin(struct fs_iter* iterator);
+
+bool
+fsm_itnext(struct fs_iter* iterator);
+
+static inline void
+fsm_itend(struct fs_iter* iterator)
+{
+ iterator->fs = NULL;
+}
+
+void
+vfs_vncache_init(struct vncache* cache);
+
+void
+vfs_vncache_free(struct vncache* cache);
+
+void
+vfs_vncache_add(struct vncache* cache, size_t key, struct hlist_node* node);
+
+#define vncache_lock_read(cache) rwlock_begin_read(&(cache)->lock);
+#define vncache_unlock_read(cache) rwlock_end_read(&(cache)->lock);
+
+#define vncache_lock_write(cache) rwlock_begin_write(&(cache)->lock);
+#define vncache_unlock_write(cache) rwlock_end_write(&(cache)->lock);
+
+void
+vfs_init();
+
+void
+vfs_export_attributes();
+
+struct v_dnode*
+vfs_dcache_lookup(struct v_dnode* parent, struct hstr* str);
+
+void
+vfs_dcache_add(struct v_dnode* parent, struct v_dnode* dnode);
+
+void
+vfs_dcache_rehash(struct v_dnode* new_parent, struct v_dnode* dnode);
+
+void
+vfs_dcache_remove(struct v_dnode* dnode);
+
+int
+vfs_walk(struct v_dnode* start,
+ const char* path,
+ struct v_dnode** dentry,
+ struct hstr* component,
+ int walk_options);
+
+int
+vfs_walk_proc(const char* path,
+ struct v_dnode** dentry,
+ struct hstr* component,
+ int options);
+
+int
+vfs_walkat(int fd, const char* path, int at_opts,
+ struct v_dnode** dnode_out);
+
+int
+vfs_mount(const char* target,
+ const char* fs_name,
+ struct device* device,
+ int options);
+
+int
+vfs_unmount(const char* target);
+
+int
+vfs_mount_at(const char* fs_name,
+ struct device* device,
+ struct v_dnode* mnt_point,
+ int options);
+
+int
+vfs_unmount_at(struct v_dnode* mnt_point);
+
+int
+vfs_mkdir(const char* path, struct v_dnode** dentry);
+
+int
+vfs_open(struct v_dnode* dnode, struct v_file** file);
+
+int
+vfs_pclose(struct v_file* file, pid_t pid);
+
+int
+vfs_close(struct v_file* file);
+
+void
+vfs_free_fd(struct v_fd* fd);
+
+int
+vfs_fsync(struct v_file* file);
+
+void
+vfs_assign_inode(struct v_dnode* assign_to, struct v_inode* inode);
+
+struct v_superblock*
+vfs_sb_alloc();
+
+void
+vfs_sb_unref(struct v_superblock* sb);
+
+void
+vfs_sb_ref(struct v_superblock* sb);
+
+#define vfs_assign_sb(sb_accessor, sb) \
+ ({ \
+ if (likely(sb_accessor)) { \
+ vfs_sb_unref(sb_accessor); \
+ } \
+ vfs_sb_ref(((sb_accessor) = (sb))); \
+ })
+
+static inline void
+vfs_i_assign_sb(struct v_inode* inode, struct v_superblock* sb)
+{
+ vfs_assign_sb(inode->sb, sb);
+}
+
+static inline void
+vfs_d_assign_sb(struct v_dnode* dnode, struct v_superblock* sb)
+{
+ vfs_assign_sb(dnode->super_block, sb);
+}
+
+static inline void
+vfs_d_assign_vmnt(struct v_dnode* dnode, struct v_mount* vmnt)
+{
+ if (dnode->mnt) {
+ assert_msg(dnode->mnt->mnt_point != dnode,
+ "vmnt must be detached first");
+ }
+
+ dnode->mnt = vmnt;
+
+ if (likely(vmnt))
+ vfs_d_assign_sb(dnode, vmnt->super_block);
+}
+
+static inline void
+vfs_vmnt_assign_sb(struct v_mount* vmnt, struct v_superblock* sb)
+{
+ vfs_assign_sb(vmnt->super_block, sb);
+}
+
+struct v_dnode*
+vfs_d_alloc(struct v_dnode* parent, struct hstr* name);
+
+void
+vfs_d_free(struct v_dnode* dnode);
+
+struct v_inode*
+vfs_i_find(struct v_superblock* sb, u32_t i_id);
+
+void
+vfs_i_addhash(struct v_inode* inode);
+
+struct v_inode*
+vfs_i_alloc(struct v_superblock* sb);
+
+void
+vfs_i_free(struct v_inode* inode);
+
+int
+vfs_dup_fd(struct v_fd* old, struct v_fd** new);
+
+int
+vfs_getfd(int fd, struct v_fd** fd_s);
+
+int
+vfs_get_dtype(int itype);
+
+int
+vfs_get_path(struct v_dnode* dnode, char* buf, size_t size, int depth);
+
+void
+pcache_init(struct pcache* pcache);
+
+int
+pcache_write(struct v_inode* inode, void* data, u32_t len, u32_t fpos);
+
+int
+pcache_read(struct v_inode* inode, void* data, u32_t len, u32_t fpos);
+
+void
+pcache_release(struct pcache* pcache);
+
+int
+pcache_commit(struct v_inode* inode, struct pcache_pg* page);
+
+void
+pcache_commit_all(struct v_inode* inode);
+
+void
+pcache_invalidate(struct pcache* pcache, struct pcache_pg* page);
+
+/**
+ * @brief 将挂载点标记为繁忙
+ *
+ * @param mnt
+ */
+void
+mnt_mkbusy(struct v_mount* mnt);
+
+/**
+ * @brief 将挂载点标记为清闲
+ *
+ * @param mnt
+ */
+void
+mnt_chillax(struct v_mount* mnt);
+
+int
+vfs_mount_root(const char* fs_name, struct device* device);
+
+static inline bool
+mnt_check_busy(struct v_mount* mnt)
+{
+ return mnt->busy_counter > 1;
+}
+
+static inline void
+vfs_ref_dnode(struct v_dnode* dnode)
+{
+ dnode->ref_count++;
+
+ if (likely(dnode->mnt)) {
+ mnt_mkbusy(dnode->mnt);
+ }
+}
+
+static inline void
+vfs_unref_dnode(struct v_dnode* dnode)
+{
+ dnode->ref_count--;
+
+ if (likely(dnode->mnt)) {
+ mnt_chillax(dnode->mnt);
+ }
+}
+
+static inline void
+vfs_ref_file(struct v_file* file)
+{
+ file->ref_count++;
+}
+
+static inline void
+vfs_unref_file(struct v_file* file)
+{
+ file->ref_count--;
+}
+
+static inline bool
+vfs_check_duped_file(struct v_file* file)
+{
+ return file->ref_count > 1;
+}
+
+int
+vfs_check_writable(struct v_dnode* dnode);
+
+int
+default_file_read(struct v_inode* inode, void* buffer, size_t len, size_t fpos);
+
+int
+default_file_write(struct v_inode* inode,
+ void* buffer,
+ size_t len,
+ size_t fpos);
+
+int
+default_file_read_page(struct v_inode* inode, void* buffer, size_t fpos);
+
+int
+default_file_write_page(struct v_inode* inode, void* buffer, size_t fpos);
+
+int
+default_file_readdir(struct v_file* file, struct dir_context* dctx);
+
+int
+default_inode_dirlookup(struct v_inode* this, struct v_dnode* dnode);
+
+int
+default_inode_rename(struct v_inode* from_inode,
+ struct v_dnode* from_dnode,
+ struct v_dnode* to_dnode);
+
+int
+default_file_close(struct v_file* file);
+
+int
+default_file_seek(struct v_file* file, size_t offset);
+
+int
+default_inode_open(struct v_inode* this, struct v_file* file);
+
+int
+default_inode_rmdir(struct v_inode* this, struct v_dnode* dir);
+
+int
+default_inode_mkdir(struct v_inode* this, struct v_dnode* dir);
+
+struct v_xattr_entry*
+xattr_new(struct hstr* name);
+
+struct v_xattr_entry*
+xattr_getcache(struct v_inode* inode, struct hstr* name);
+
+void
+xattr_addcache(struct v_inode* inode, struct v_xattr_entry* xattr);
+
+
+/* --- fdtable --- */
+
+struct v_fdtable*
+fdtable_create();
+
+void
+fdtable_copy(struct v_fdtable* dest, struct v_fdtable* src);
+
+void
+fdtable_free(struct v_fdtable* table);
+
+
+/* --- misc stuff --- */
+
+#define check_itype(to_check, itype) \
+ (((to_check) & (itype)) == (itype))
+
+/**
+ * @brief Check if node represent a regular file (nothing but a file)
+ *
+ * @param inode
+ * @return true
+ * @return false
+ */
+static inline bool
+check_regfile_node(struct v_inode* inode)
+{
+ return inode->itype == VFS_IFFILE;
+}
+
+/**
+ * @brief Check if node represent a file.
+ * This is basically everything within file system (dir, dev, etc.)
+ *
+ * @param inode
+ * @return true
+ * @return false
+ */
+static inline bool
+check_file_node(struct v_inode* inode)
+{
+ return check_itype(inode->itype, VFS_IFFILE);
+}
+
+static inline bool
+check_directory_node(struct v_inode* inode)
+{
+ return check_itype(inode->itype, VFS_IFDIR);
+}
+
+static inline bool
+check_device_node(struct v_inode* inode)
+{
+ return check_itype(inode->itype, VFS_IFDEV);
+}
+
+static inline bool
+check_seqdev_node(struct v_inode* inode)
+{
+ return check_device_node(inode);
+}
+
+static inline bool
+check_voldev_node(struct v_inode* inode)
+{
+ return check_itype(inode->itype, VFS_IFVOLDEV);
+}
+
+static inline bool
+check_symlink_node(struct v_inode* inode)
+{
+ return check_itype(inode->itype, VFS_IFSYMLINK);
+}
+
+static inline bool
+check_allow_ops(struct v_inode* inode, unsigned int perm)
+{
+ return fsacl_allow_ops(perm, inode->acl, inode->uid, inode->gid);
+}
+
+static inline bool
+check_allow_read(struct v_inode* inode)
+{
+ return check_allow_ops(inode, FSACL_aR);
+}
+
+static inline bool
+check_allow_write(struct v_inode* inode)
+{
+ return check_allow_ops(inode, FSACL_aW);
+}
+
+static inline bool
+check_allow_execute(struct v_inode* inode)
+{
+ return check_allow_ops(inode, FSACL_aX);
+}
+