VFS: mounts KFS, shell functions
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 3 Jun 2010 00:48:56 +0000 (17:48 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:47 +0000 (17:35 -0700)
Added the appserver file items to the vfs's struct file, until we can
port the appserver or whatever over to use the vfs layer (if ever).

So far, it just inits the VFS and mounts a useless KFS.

Documentation/vfs.txt [new file with mode: 0644]
kern/arch/i686/pmap.c
kern/include/frontend.h
kern/include/kfs.h
kern/include/page_alloc.h
kern/include/vfs.h
kern/src/Makefrag
kern/src/init.c
kern/src/kfs.c
kern/src/manager.c
kern/src/vfs.c [new file with mode: 0644]

diff --git a/Documentation/vfs.txt b/Documentation/vfs.txt
new file mode 100644 (file)
index 0000000..705253d
--- /dev/null
@@ -0,0 +1,89 @@
+Unorganized Notes on the Virtual File System
+-------------------------------------
+Our VFS is built similarly to Linux's, mostly because I'd like to have somewhat
+easy integration with ext2 (or at the very least have our own FS that can
+integrate into Linux easily).
+
+There are four main objects in a filesystem: superblock, inode, dentry, and a
+file.  
+       - Superblock: Specific instance of a mounted filesystem.  All
+         synchronization is done with the one spinlock.
+       - Inode: represents a specific file
+       - Dentry: in memory object, corresponding to an element of a path.  E.g. /,
+         usr, bin, and vim are all dentries.  All have inodes.  Vim happens to be a
+         file instead of a directory.
+       - File: represents a file opened by a process.
+
+So far, dentries are the most complicated, so here are some notes about them.
+These are probably self-consistent, but may be a bit old (or just wrong) because
+I wrote them as I put the VFS together:
+
+A dentry is just a connection between a part of a path and an inode.  We just
+want to walk from dentry to dentry, asking each to find the next part in the
+path.  When you hit a dentry that's not the end of the desired path, you ask its
+inode for the next dentry by using it's operation (lookup).
+
+lookup() takes the inode of the directory (or 0 for the root) and a dentry
+(already allocated, with the d_name and i_name filled in), and it will find the
+correct inode for the given dentry, as well as filling out any specific FS
+things in the dentry (like d_ops).  It will return 0 on failure, or the dentry
+pointer passed in on success.  Somehow the nameidata bit will be used too.  This
+will probably change a bit...  Note that lookup() needs to read the actual
+directory file and also lookup the inode off the disc, which means it will
+block.
+
+When the next dentry is a mountpoint, (e.g. /mnt/cdrom), when you ask mnt for
+cdrom, lookup() will parse it's file (a directory) and see 'cdrom' there as a child
+entry.  Then it will return cdrom's dentry, like always.
+
+But since cdrom was a mountpoint, (which you can see from the d_mount_point),
+you need to walk through the structures a bit to get the dentry of the FS rooted
+at that mountpoint to keep walking.  The VFS can handle that, so lookup()
+doesn't need to worry about it.
+
+Why are there two dentries associated with a vfsmount?  Normally, a dentry for a
+directory points to an inode that contains its members.  When a FS is mounted
+over it, we don't want to destroy that.  Instead, we mark it as a mountpoint,
+and when following, we walk into the next FS.  The mountpoint is the dentry in
+the parent mount (using the parent mount's FS superblock), and the root is the
+dentry of the mounted FS, using the FS superblock of the mounted FS.
+
+Sometimes lookup() will not be called at all.  We want to look for present
+dentries first (ones that exist in memory already, including all of those with a
+refcnt).  So we can look in the cache (currently just an SLIST, but will
+eventually be a hash table).  When hitting in the cache (hashed by dentry name,
+and probably the parent dir), we need to verify that the dentry's parent is our
+own (handled by the VFS code).  All vfsmount dentries will be in the cache,
+since they all have a refcnt (the vfsmount struct points to them).
+
+Dentries for / and pwd and whatnot have a refcnt (pointed to by fs_struct,
+vfsmounts, etc).  Anything with a pointer to it has a refcnt, so it never goes
+away.  So if we have an inode in memory, it's entire dentry path is cached,
+(just follow the parent pointers back).  Note that when a dentry's refcnt hits
+0, we do *not* deallocate (like we do with many other structures).  It will just
+be on the LRU list in the dcache system.  Likewise, every dentry points to its
+inode, which pins that inode in memory.
+
+Other refcnts: just about everything has a refcnt, and we need to be careful
+about when we want to use them and dealloc.  Some things would be a pain in the
+ass, like with the super_block refcnt.  Every dentry has a ref to the SB, but
+changing that ref every time we add or remove a dentry will probably be an
+unnecessary penalty if we can ensure all dentries of that FS are gone before
+removing the superblock through another mechanism.  We'll see.  Mostly, this
+just means we need to think about what we really want from a refcnt, and whether
+or not we want the kref / process style refcnting.
+
+Mounting:
+
+When you mount, you need to read in the super block and connect the relevant
+data structures together.  The SB is connected to the vfsmount, which is
+connected to the dentry of the mount point and the dentry of the root of the FS.
+This means when mounting the FS, we need to create the dentry for "/", which
+means we also need the inode, which needs to be read_inode()'d in.  Actually, we
+might not need to read the inode in right away - we might be able to get away
+with reading them in on-demand.
+
+All of this means that for every mount point, the SB, vfsmount, dentry, and
+inode are in memory.  Due to the way dentries link, every dentry and inode back
+up to the real root are all in memory too.  Having a mount point is like having
+a process working in that directory - the chain back up is pinned.
index 4e8decf..0f711d3 100644 (file)
@@ -279,9 +279,9 @@ vm_init(void)
         * PSE status: 
         * - can walk and set up boot_map_segments with jumbos but can't
         *   insert yet.  need to look at the page_dir and friends.
-        * - anything related to a single struct Page still can't handle 
+        * - anything related to a single struct page still can't handle 
         *   jumbos.  will need to think about and adjust Page functions
-        * - do we want to store info like this in the struct Page?  or just check
+        * - do we want to store info like this in the struct page?  or just check
         *   by walking the PTE
         * - when we alloc a page, and we want it to be 4MB, we'll need
         *   to have contiguous memory, etc
index 2cec60b..2026bc3 100644 (file)
@@ -6,15 +6,9 @@
 #ifdef ROS_KERNEL
 
 #include <env.h>
+#include <vfs.h>
 #include <process.h>
 
-// for now, this is where struct file lives
-struct file {
-       int fd; // all it contains is an appserver fd (for pid 0, aka kernel)
-       int refcnt;
-       spinlock_t lock;
-};
-
 // Default APPSERVER_ETH_TYPE if not defined externally
 #ifndef APPSERVER_ETH_TYPE
 #define APPSERVER_ETH_TYPE 0x8888
index 6da1a51..2accb89 100644 (file)
@@ -1,5 +1,4 @@
-/*
- * Copyright (c) 2009 The Regents of the University of California
+/* Copyright (c) 2009, 2010 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
  *
@@ -9,15 +8,18 @@
  * linked at the end of the kernel.  Extremely rudimentary.
  * Also allows for process creation from file (can consider moving this).
  *
- * Add the files you want in KFS in kfs.c.
- */
+ * Add the files you want in KFS in kfs.c. */
 
 #ifndef ROS_KERN_KFS_H
 #define ROS_KERN_KFS_H
 
 #include <ros/common.h>
+#include <vfs.h>
 #include <process.h>
 
+/* Every FS must extern it's type, and be included in vfs_init() */
+extern struct fs_type kfs_fs_type;
+
 struct kfs_entry {
        char (NT name)[256];
        uint8_t *COUNT(size) start;
index 81ab395..e6d7abe 100644 (file)
 #include <process.h>
 
 /****************** Page Structures *********************/
-struct Page;
+struct page;
 typedef size_t ppn_t;
-typedef struct Page page_t;
-typedef LIST_HEAD(PageList, Page) page_list_t;
-typedef LIST_ENTRY(Page) page_list_entry_t;
+typedef struct page page_t;
+typedef LIST_HEAD(PageList, page) page_list_t;
+typedef LIST_ENTRY(page) page_list_entry_t;
 
 /* TODO: this struct is not protected from concurrent operations in any
  * function.  We may want a lock, but a better thing would be a good use of
  * reference counting and atomic operations. */
-struct Page {
+struct page {
        page_list_entry_t LCKD(&colored_page_free_list_lock)page_link;
     size_t page_ref;
 };
index 34d99b6..678143b 100644 (file)
@@ -1,14 +1,14 @@
 /* Barret Rhoden <brho@cs.berkeley.edu>
  *
- * VFS, based on the Linux VFS as described in LKD 2nd Ed (Robert Love), which
- * was probably written by Linus.  A lot of it was changed (reduced) to handle
- * what ROS will need, at least initially.  Hopefully it'll be similar enough
- * to interface with ext2 and other Linux FSs.
+ * VFS, based on the Linux VFS as described in LKD 2nd Ed (Robert Love) and in
+ * UTLK (Bovet/Cesati) , which was probably written by Linus.  A lot of it was
+ * changed (reduced) to handle what ROS will need, at least initially.
+ * Hopefully it'll be similar enough to interface with ext2 and other Linux
+ * FSs.
  *
  * struct qstr came directly from Linux.
  * Lawyers can sort out the copyrights and whatnot with these interfaces and
- * structures.
- */
+ * structures. */
 
 #ifndef ROS_KERN_VFS_H
 #define ROS_KERN_VFS_H
@@ -18,6 +18,7 @@
 #include <arch/bitmask.h>
 #include <atomic.h>
 #include <timing.h>
+#include <page_alloc.h>
 
 // TODO: temp typedefs, etc.  remove when we support this stuff.
 typedef int dev_t;
@@ -38,18 +39,18 @@ struct inode;
 struct inode_operations;
 struct file;
 struct file_operations;
-struct file_system_type;
+struct fs_type;
 struct vm_area_struct;
 struct vfsmount;
 
 /* part of the kernel interface, ripped from man pages, ought to work. */
 // TODO: eventually move this to ros/fs.h or something.
-#define MAX_FILENAME 255
+#define MAX_FILENAME_SZ 255
 struct dirent { // or maybe something else to not conflict with userspace
        ino_t          d_ino;       /* inode number */
        off_t          d_off;       /* offset to the next dirent */
        unsigned short d_reclen;    /* length of this record */
-       char           d_name[MAX_FILENAME + 1]; /* filename */
+       char           d_name[MAX_FILENAME_SZ + 1]; /* filename */
 };
 
 struct iovec {
@@ -67,28 +68,42 @@ TAILQ_HEAD(file_tailq, file);
 TAILQ_HEAD(io_wb_tailq, io_writeback);
 TAILQ_HEAD(event_poll_tailq, event_poll);
 TAILQ_HEAD(vfsmount_tailq, vfsmount);
+TAILQ_HEAD(fs_type_tailq, fs_type);
 
 /* Linux's quickstring - saves recomputing the hash and length. */
 struct qstr {
     unsigned int hash;
     unsigned int len;
-    const unsigned char *name;
+    const char *name;
 };
 
+/* Helpful structure to pass around during lookup operations.  At each point,
+ * it tracks the the answer, the name of the previous, how deep the symlink
+ * following has gone, and the symlink pathnames.  *dentry and *mnt up the
+ * refcnt of those objects too, so whoever receives this will need to decref.
+ * We'll see how this works out... */
+#define MAX_SYMLINK_DEPTH 6 // arbitrary.
+struct nameidata {
+       struct dentry                           *dentry;                /* dentry of the obj */
+       struct vfsmount                         *mnt;                   /* its mount pt */
+       struct qstr                                     last;                   /* last component in search */
+       int                                                     flags;                  /* lookup flags */
+       int                                                     last_type;              /* type fo last component */
+       unsigned int                            depth;                  /* search's symlink depth */
+       char                                            *saved_names[MAX_SYMLINK_DEPTH];
+       int                                                     intent;                 /* access type for the file */
+};
 
 /* Superblock: Specific instance of a mounted filesystem.  All synchronization
  * is done with the one spinlock. */
 
-extern struct sb_tailq super_blocks;                   /* list of all sbs */
-extern spinlock_t super_blocks_lock;
-
 struct super_block {
-       TAILQ_ENTRY(superblock)         s_list;                 /* list of all sbs */
+       TAILQ_ENTRY(super_block)        s_list;                 /* list of all sbs */
        dev_t                                           s_dev;                  /* id */
        unsigned long                           s_blocksize;
        bool                                            s_dirty;
        unsigned long long                      s_maxbytes;             /* max file size */
-       struct file_system_type         *s_type;
+       struct fs_type                          *s_type;
        struct super_operations         *s_op;
        unsigned long                           s_flags;
        unsigned long                           s_magic;
@@ -96,12 +111,13 @@ struct super_block {
        spinlock_t                                      s_lock;                 /* used for all sync */
        atomic_t                                        s_refcnt;
        bool                                            s_syncing;              /* currently syncing metadata */
+       struct inode_tailq                      s_inodes;               /* all inodes */
        struct inode_tailq                      s_dirty_i;              /* dirty inodes */
        struct io_wb_tailq                      s_io_wb;                /* writebacks */
        struct dentry_slist                     s_anon_d;               /* anonymous dentries */
        struct file_tailq                       s_files;                /* assigned files */
        struct block_device                     *s_bdev;
-       TAILQ_ENTRY(superblock)         s_instances;    /* list of sbs of this fs type*/
+       TAILQ_ENTRY(super_block)        s_instances;    /* list of sbs of this fs type*/
        char                                            s_name[32];
        void                                            *s_fs_info;
 };
@@ -111,22 +127,22 @@ struct super_operations {
        void (*destroy_inode) (struct inode *);         /* dealloc.  might need more */
        void (*read_inode) (struct inode *);
        void (*dirty_inode) (struct inode *);
-       void (*write_inode) (struct inode *, int);
+       void (*write_inode) (struct inode *, bool);
+       void (*put_inode) (struct inode *);                     /* when decreffed */
+       void (*drop_inode) (struct inode *);            /* when about to destroy */
        void (*delete_inode) (struct inode *);          /* deleted from disk */
        void (*put_super) (struct super_block *);       /* releases sb */
        void (*write_super) (struct super_block *);     /* sync with sb on disk */
-       int (*sync_fs) (struct super_block *, int);
-       int (*remount_fs) (struct super_block *, int *, char *);
+       int (*sync_fs) (struct super_block *, bool);
+       int (*remount_fs) (struct super_block *, int, char *);
        void (*umount_begin) (struct super_block *);/* called by NFS */
 };
 
-/* this will create/init a SB for a FS */
-void alloc_super(void);
-
 /* Inode: represents a specific file */
 struct inode {
        SLIST_ENTRY(inode)                      i_hash;                 /* inclusion in a hash table */
-       TAILQ_ENTRY(inode)                      i_list;                 /* all inodes in the FS */
+       TAILQ_ENTRY(inode)                      i_sb_list;              /* all inodes in the FS */
+       TAILQ_ENTRY(inode)                      i_list;                 /* describes state (dirty) */
        struct dentry_tailq                     i_dentry;               /* all dentries pointing here*/
        unsigned long                           i_ino;
        atomic_t                                        i_refcnt;
@@ -142,7 +158,7 @@ struct inode {
        unsigned long                           i_blksize;
        unsigned long                           i_blocks;               /* filesize in blocks */
        spinlock_t                                      i_lock;
-       struct inode_operations         *i_ops;
+       struct inode_operations         *i_op;
        struct file_operations          *i_fop;
        struct super_block                      *i_sb;
        //struct address_space          *i_mapping;             /* linux mapping structs */
@@ -161,8 +177,9 @@ struct inode {
 };
 
 struct inode_operations {
-       int (*create) (struct inode *, struct dentry *, int);
-       struct dentry *(*lookup) (struct inode *, struct dentry *);
+       int (*create) (struct inode *, struct dentry *, int, struct nameidata *);
+       struct dentry *(*lookup) (struct inode *, struct dentry *,
+                                 struct nameidata *);
        int (*link) (struct dentry *, struct inode *, struct dentry *);
        int (*unlink) (struct inode *, struct dentry *);
        int (*symlink) (struct inode *, struct dentry *, const char *);
@@ -171,16 +188,13 @@ struct inode_operations {
        int (*mknod) (struct inode *, struct dentry *, int, dev_t);
        int (*rename) (struct inode *, struct dentry *,
                       struct inode *, struct dentry *);
-       int (*readlink) (struct dentry *, char *, int);
+       int (*readlink) (struct dentry *, char *, size_t);
+       int (*follow_link) (struct dentry *, struct nameidata *);
+       int (*put_link) (struct dentry *, struct nameidata *);
        void (*truncate) (struct inode *);                      /* set i_size before calling */
-       int (*permission) (struct inode *, int);
+       int (*permission) (struct inode *, int, struct nameidata *);
 };
 
-// TODO: might want a static dentry for /, though processes can get to their
-// root via their fs_struct or even the default namespace.
-// TODO: should have a dentry_htable or something.  we have the structs built
-// in to the dentry right now (linux style).  Need some form of locking too
-
 #define DNAME_INLINE_LEN 32
 /* Dentry: in memory object, corresponding to an element of a path.  E.g. /,
  * usr, bin, and vim are all dentries.  All have inodes.  Vim happens to be a
@@ -191,32 +205,34 @@ struct inode_operations {
  * Unused and negatives go in the LRU list. */
 struct dentry {
        atomic_t                                        d_refcnt;               /* don't discard when 0 */
-       unsigned long                           d_vfs_flags;    /* dentry cache flags */
+       unsigned long                           d_flags;                /* dentry cache flags */
        spinlock_t                                      d_lock;
        struct inode                            *d_inode;
        TAILQ_ENTRY(dentry)                     d_lru;                  /* unused list */
-       struct dentry_tailq                     d_child;
+       TAILQ_ENTRY(dentry)                     d_child;                /* linkage for i_dentry */
        struct dentry_tailq                     d_subdirs;
+       TAILQ_ENTRY(dentry)                     d_subdirs_link;
        unsigned long                           d_time;                 /* revalidate time (jiffies)*/
        struct dentry_operations        *d_op;
        struct super_block                      *d_sb;
-       unsigned int                            d_flags;
-       bool                                            d_mount_point;
-       void                                            *d_fs_info;
+       bool                                            d_mount_point;  /* is an FS mounted over here */
+       struct vfsmount                         *d_mounted_fs;  /* fs mounted here */
        struct dentry                           *d_parent;
        struct qstr                                     d_name;                 /* pts to iname and holds hash*/
        SLIST_ENTRY(dentry)                     d_hash;                 /* link for the dcache */
-       struct dentry_slist                     *d_bucket;              /* hash bucket of this dentry */
-       unsigned char                           d_iname[DNAME_INLINE_LEN];
+       struct dentry_slist                     d_bucket;               /* hash bucket of this dentry */
+       char                                            d_iname[DNAME_INLINE_LEN];
+       void                                            *d_fs_info;
 };
 
 /* not sure yet if we want to call delete when refcnt == 0 (move it to LRU) or
  * when its time to remove it from the dcache. */
 struct dentry_operations {
-       int (*d_revalidate) (struct dentry *, int);     /* usually a noop */
+       int (*d_revalidate) (struct dentry *, struct nameidata *);
        int (*d_hash) (struct dentry *, struct qstr *);
        int (*d_compare) (struct dentry *, struct qstr *, struct qstr *);
        int (*d_delete) (struct dentry *);
+       int (*d_release) (struct dentry *);
        void (*d_iput) (struct dentry *, struct inode *);
 };
 
@@ -237,10 +253,14 @@ struct file {
        spinlock_t                                      f_ep_lock;
        void                                            *f_fs_info;             /* tty driver hook */
        //struct address_space          *f_mapping;             /* page cache mapping */
+
+       /* Ghetto appserver support */
+       int fd; // all it contains is an appserver fd (for pid 0, aka kernel)
+       int refcnt;
+       spinlock_t lock;
 };
 
 struct file_operations {
-       struct module *owner;
        off_t (*llseek) (struct file *, off_t, int);
        ssize_t (*read) (struct file *, char *, size_t, off_t *);
        ssize_t (*write) (struct file *, const char *, size_t, off_t *);
@@ -255,27 +275,28 @@ struct file_operations {
                          off_t *);
        ssize_t (*writev) (struct file *, const struct iovec *, unsigned long,
                          off_t *);
+       ssize_t (*sendpage) (struct file *, struct page *, int, size_t, off_t, int);
        int (*check_flags) (int flags);                         /* most FS's ignore this */
 };
 
 /* FS structs.  One of these per FS (e.g., ext2) */
-struct file_system_type {
+struct fs_type {
        const char                                      *name;
        int                                                     fs_flags;
-       struct superblock                       *(*get_sb) (struct file_system_type *, int,
-                                               char *, void *);
+       struct super_block                      *(*get_sb) (struct fs_type *, int,
+                                               char *, struct vfsmount *);
        void                                            (*kill_sb) (struct super_block *);
-       struct file_system_type         *next;                  /* next FS */
+       TAILQ_ENTRY(fs_type)            list;
        struct sb_tailq                         fs_supers;              /* all of this FS's sbs */
 };
 
 /* A mount point: more focused on the mounting, and solely in memory, compared
  * to the SB which is focused on FS definitions (and exists on disc). */
 struct vfsmount {
-       TAILQ_ENTRY(vfsmount)           mnt_list;               /* might want a hash instead */
+       TAILQ_ENTRY(vfsmount)           mnt_list;
        struct vfsmount                         *mnt_parent;
-       struct dentry                           *mnt_mountpoint;/* do we need both of them? */
-       struct dentry                           *mnt_root;              /* do we need both of them? */
+       struct dentry                           *mnt_mountpoint;/* parent dentry where mnted */
+       struct dentry                           *mnt_root;              /* dentry of root of this fs */
        struct super_block                      *mnt_sb;
        struct vfsmount_tailq           mnt_child_mounts;
        TAILQ_ENTRY(vfsmount)           mnt_child_link;
@@ -323,7 +344,7 @@ struct fs_struct {
        struct dentry                           *pwd;
 };
 
-/* Each process can have it's own (eventually), but default to the same NS */
+/* Each process can have its own (eventually), but default to the same NS */
 struct namespace {
        atomic_t                                        refcnt;
        spinlock_t                                      lock;
@@ -331,6 +352,28 @@ struct namespace {
        struct vfsmount_tailq           vfsmounts;      /* all vfsmounts in this ns */
 };
 
+/* Global Structs */
+extern struct sb_tailq super_blocks;                   /* list of all sbs */
+extern spinlock_t super_blocks_lock;
+extern struct fs_type_tailq file_systems;              /* lock this if it's dynamic */
 extern struct namespace default_ns;
+// TODO: should have a dentry_htable or something.  we have the structs built
+// in to the dentry right now (linux style).
+extern struct dentry_slist dcache;
+extern spinlock_t dcache_lock;
+
+/* Slab caches for common objects */
+extern struct kmem_cache *dentry_kcache;
+extern struct kmem_cache *inode_kcache;
+extern struct kmem_cache *file_kcache;
+
+/* VFS Functions */
+void vfs_init(void);
+void qstr_builder(struct dentry *dentry, char *l_name);
+struct super_block *get_sb(void);
+void init_sb(struct super_block *sb, struct vfsmount *vmnt,
+             struct dentry_operations *d_op, unsigned long root_ino);
+struct dentry *get_dentry(struct super_block *sb);
+void dcache_put(struct dentry *dentry);
 
 #endif /* ROS_KERN_VFS_H */
index 4f98b04..822a531 100644 (file)
@@ -39,6 +39,7 @@ KERN_SRCFILES := $(KERN_ARCH_SRCFILES) \
                  $(KERN_SRC_DIR)/slab.c \
                  $(KERN_SRC_DIR)/elf.c \
                  $(KERN_SRC_DIR)/frontend.c \
+                 $(KERN_SRC_DIR)/vfs.c \
                  $(KERN_SRC_DIR)/testing.c
 
 # Only build files if they exist.
index eb55bcb..48d8ab6 100644 (file)
@@ -78,7 +78,7 @@ void kernel_init(multiboot_info_t *mboot_info)
        mmap_init();
        file_init();
        page_check();
-
+       vfs_init();
        idt_init();
        kernel_msg_init();
        sysenter_init();
index 93e6a64..2e340af 100644 (file)
@@ -1,8 +1,10 @@
-/*
- * Copyright (c) 2009 The Regents of the University of California
+/* Copyright (c) 2009, 2010 The Regents of the University of California
  * Barret Rhoden <brho@cs.berkeley.edu>
  * See LICENSE for details.
- */
+ *
+ * Implementation of the KFS file system.  It is a RAM based, read-only FS
+ * consisting of files that are added to the kernel binary image.  Might turn
+ * this into a read/write FS with directories someday. */
 
 #ifdef __SHARC__
 #pragma nosharc
 #pragma nodeputy
 #endif
 
+#include <vfs.h>
 #include <kfs.h>
+#include <slab.h>
+#include <kmalloc.h>
 #include <string.h>
 #include <stdio.h>
 #include <assert.h>
 #include <error.h>
 
+#define KFS_MAX_FILE_SIZE 1024*1024*128
+#define KFS_MAGIC 0xdead0001
+
+/* VFS required Functions */
+/* These structs are declared again and initialized farther down */
+struct super_operations kfs_s_op;
+struct inode_operations kfs_i_op;
+struct dentry_operations kfs_d_op;
+struct file_operations kfs_f_op;
+
+/* Creates the SB (normally would read in from disc and create).  Ups the refcnt
+ * for whoever consumes this.  Returns 0 on failure.
+ * TODO: consider pulling out more of the FS-independent stuff, if possible.
+ * There are only two things, but the pain in the ass is that you'd need to read
+ * the disc to get that first inode, and it's a FS-specific thing. */
+struct super_block *kfs_get_sb(struct fs_type *fs, int flags,
+                               char *dev_name, struct vfsmount *vmnt)
+{
+       /* Ought to check that dev_name has our FS on it.  in this case, it's
+        * irrelevant. */
+       //if (something_bad)
+       //      return 0;
+
+       /* Build and init the SB.  No need to read off disc. */
+       struct super_block *sb = get_sb();
+       sb->s_dev = 0;
+       sb->s_blocksize = 4096; /* or 512.  haven't thought this through */
+       sb->s_maxbytes = KFS_MAX_FILE_SIZE;
+       sb->s_type = &kfs_fs_type;
+       sb->s_op = &kfs_s_op;
+       sb->s_flags = flags;
+       sb->s_magic = KFS_MAGIC;
+       sb->s_mount = vmnt;
+       sb->s_syncing = FALSE;
+       sb->s_bdev = 0;
+       strlcpy(sb->s_name, "KFS", 32);
+       //sb->s_fs_info = 0; /* nothing to override with for KFS */
+       /* Final stages of initializing the sb, mostly FS-independent */
+       init_sb(sb, vmnt, &kfs_d_op, 1); /* 1 is the KFS root ino (inode number) */
+       printk("KFS superblock loaded\n");
+       return sb;
+}
+
+void kfs_kill_sb(struct super_block *sb)
+{
+       panic("Killing KFS is not supported!");
+}
+
+/* Every FS must have a static FS Type, with which the VFS code can bootstrap */
+struct fs_type kfs_fs_type = {"KFS", 0, kfs_get_sb, kfs_kill_sb, {0, 0},
+               TAILQ_HEAD_INITIALIZER(kfs_fs_type.fs_supers)};
+
+/* Super Operations */
+
+/* creates and initializes a new inode.  generic fields are filled in.  specific
+ * fields are filled in in read_inode() based on what's on the disk for a given
+ * i_no.  i_no is set by the caller. */
+struct inode *kfs_alloc_inode(struct super_block *sb)
+{
+       /* arguably, we can avoid some of this init by using the slab/cache */
+       struct inode *inode = kmem_cache_alloc(inode_kcache, 0);
+       memset(inode, 0, sizeof(struct inode));
+       TAILQ_INSERT_HEAD(&sb->s_inodes, inode, i_sb_list);
+       TAILQ_INIT(&inode->i_dentry);
+       inode->i_ino = 0;                                       /* set by caller later */
+       atomic_set(&inode->i_refcnt, 1);
+       inode->i_blksize = 4096;                        /* keep in sync with get_sb() */
+       spinlock_init(&inode->i_lock);
+       inode->i_op = &kfs_i_op;
+       inode->i_fop = &kfs_f_op;
+       inode->i_sb = sb;
+       inode->i_state = 0;                                     /* need real states, want I_NEW */
+       inode->dirtied_when = 0;
+       atomic_set(&inode->i_writecount, 0);
+       inode->i_fs_info = 0;
+       return inode;
+       /* caller sets i_ino, i_list set when applicable */
+}
+
+/* deallocs and cleans up after an inode. */
+void kfs_destroy_inode(struct inode *inode)
+{
+       kmem_cache_free(inode_kcache, inode);
+}
+
+/* reads the inode data on disk specified by inode->i_ino into the inode.
+ * basically, it's a "make this inode the one for i_ino (i number)" */
+void kfs_read_inode(struct inode *inode)
+{
+       /* TODO: what does it mean to ask for an inode->i_ino that doesn't exist?
+        *      possibly a bug, since these inos come from directories */
+       if (inode->i_ino == 1) {
+               inode->i_mode = 0x777;                  /* TODO: use something appropriate */
+               inode->i_nlink = 1;                             /* assuming only one hardlink */
+               inode->i_uid = 0;
+               inode->i_gid = 0;
+               inode->i_rdev = 0;
+               inode->i_size = 0;                              /* make sense for KFS? */
+               inode->i_atime.tv_sec = 0;
+               inode->i_atime.tv_nsec = 0;
+               inode->i_mtime.tv_sec = 0;
+               inode->i_mtime.tv_nsec = 0;
+               inode->i_ctime.tv_sec = 0;
+               inode->i_ctime.tv_nsec = 0;
+               inode->i_blksize = 0;
+               inode->i_blocks = 0;
+               inode->i_bdev = 0;                              /* assuming blockdev? */
+               inode->dirtied_when = 0;
+               inode->i_flags = 0;
+               inode->i_socket = FALSE;
+       } else {
+               panic("Not implemented");
+       }
+       /* TODO: unused: inode->i_hash add to hash (saves on disc reading)
+        * i_mapping, i_data, when they mean something */
+}
+
+/* called when an inode in memory is modified (journalling FS's care) */
+void kfs_dirty_inode(struct inode *inode)
+{      // KFS doesn't care
+}
+
+/* write the inode to disk (specifically, to inode inode->i_ino), synchronously
+ * if we're asked to wait */
+void kfs_write_inode(struct inode *inode, bool wait)
+{      // KFS doesn't care
+}
+
+/* called when an inode is decref'd, to do any FS specific work */
+void kfs_put_inode(struct inode *inode)
+{      // KFS doesn't care
+}
+
+/* called when an inode is about to be destroyed.  the generic version ought to
+ * remove every reference to the inode from the VFS, and if the inode isn't in
+ * any directory, calls delete_inode */
+void kfs_drop_inode(struct inode *inode)
+{ // TODO: should call a generic one instead.  or at least do something...
+       // remove from lists
+}
+
+/* delete the inode from disk (all data) and deallocs the in memory inode */
+void kfs_delete_inode(struct inode *inode)
+{
+       // would remove from "disk" here
+       kfs_destroy_inode(inode);
+}
+
+/* unmount and release the super block */
+void kfs_put_super(struct super_block *sb)
+{
+       panic("Shazbot! KFS can't be unmounted yet!");
+}
+
+/* updates the on-disk SB with the in-memory SB */
+void kfs_write_super(struct super_block *sb)
+{      // KFS doesn't care
+}
+
+/* syncs FS metadata with the disc, synchronously if we're waiting.  this info
+ * also includes anything pointed to by s_fs_info. */
+int kfs_sync_fs(struct super_block *sb, bool wait)
+{
+       return 0;
+}
+
+/* remount the FS with the new flags */
+int kfs_remount_fs(struct super_block *sb, int flags, char *data)
+{
+       warn("KFS will not remount.");
+       return -1; // can't remount
+}
+
+/* interrupts a mount operation - used by NFS and friends */
+void kfs_umount_begin(struct super_block *sb)
+{
+       panic("Cannot abort a KFS mount, and why would you?");
+}
+
+/* inode_operations */
+
+/* Create a new disk inode in dir associated with dentry, with the given mode.
+ * called when creating or opening a regular file  (TODO probably not open) */
+int kfs_create(struct inode *dir, struct dentry *dentry, int mode,
+               struct nameidata *nd)
+{
+       // TODO: how exactly does this work when we open a file?  and what about
+       // when we need an inode to fill in a dentry structure?
+       // seems like we can use this to fill it in, instead of having the inode
+       // filled in at dentry creation
+       //      - diff between dentry creation for an existing path and a new one (open
+       //      vs create)
+       //      - this might just be for a brand new one (find a free inode, give it to
+       //      me, etc)
+       //
+       //      note it is the i_ino that uniquely identifies a file in the system.
+       //              there's a diff between creating an inode (even for an in-use ino)
+       //              and then filling it in, and vs creating a brand new one
+       //
+       //dentry with d_inode == NULL -> ENOENT (negative entry?)
+       //      linux now has a nameidata for this
+       //
+       //presence of a lookup op (in linux) shows it is a directory... np
+       //same with symlink
+       //their link depth is tied to the task_struct (for max, not for one path)
+       return -1;
+}
+
+/* Searches the directory for the filename in the dentry, filling in the dentry
+ * with the FS specific info of this file */
+struct dentry *kfs_lookup(struct inode *dir, struct dentry *dentry,
+                          struct nameidata *nd)
+{
+       // TODO: does this mean the inode too?
+       // what the hell are we returning?  the passed in dentry?  an error code?
+       // this will have to read the directory, then find the ino, then creates a
+       // dentry for it
+       //
+       // linux now has a nameidata for this
+       return 0;
+}
+
+/* Hard link to old_dentry in directory dir with a name specified by new_dentry.
+ * TODO: should this also make the dentry linkage, or just discard everything?*/
+int kfs_link(struct dentry *old_dentry, struct inode *dir,
+             struct dentry *new_dentry)
+{
+       return -1;
+}
+
+/* Removes the link from the dentry in the directory */
+int kfs_unlink(struct inode *dir, struct dentry *dentry)
+{
+       return -1;
+}
+
+/* Creates a new inode for a symlink named symname in dir, and links to dentry.
+ * */
+int kfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+       return -1;
+}
+
+/* Creates a new inode for a directory associated with dentry in dir with the
+ * given mode */
+int kfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       return -1;
+}
+
+/* Removes from dir the directory specified by the name in dentry. */
+// TODO: note this isn't necessarily the same dentry, just using it for the
+// naming (which seems to be a common way of doing things, like in lookup() -
+// can work either way.
+int kfs_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       return -1;
+}
+
+/* Used to make a generic file, based on the type and the major/minor numbers
+ * (in rdev), with the given mode.  As with others, this creates a new disk
+ * inode for the file */
+int kfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+{
+       return -1;
+}
+
+/* Moves old_dentry from old_dir to new_dentry in new_dir */
+int kfs_rename(struct inode *old_dir, struct dentry *old_dentry,
+               struct inode *new_dir, struct dentry *new_dentry)
+{
+       return -1;
+}
+
+/* Copies to the userspace buffer the file pathname corresponding to the symlink
+ * specified by dentry. */
+int kfs_readlink(struct dentry *dentry, char *buffer, size_t buflen)
+{
+       return -1;
+}
+
+/* Translates the symlink specified by sym and puts the result in nd. */
+int kfs_follow_link(struct dentry *sym, struct nameidata *nd)
+{
+       return -1;
+}
+
+/* Cleans up after follow_link (decrefs the nameidata business) */
+int kfs_put_link(struct dentry *sym, struct nameidata *nd)
+{
+       return -1;
+}
+
+/* Modifies the size of the file of inode to whatever its i_size is set to */
+void kfs_truncate(struct inode *inode)
+{
+}
+
+/* Checks whether the the access mode is allowed for the file belonging to the
+ * inode.  Implies that the permissions are on the file, and not the hardlink */
+int kfs_permission(struct inode *inode, int mode, struct nameidata *nd)
+{
+       return -1;
+}
+
+
+/* dentry_operations */
+/* Determines if the dentry is still valid before using it to translate a path.
+ * Network FS's need to deal with this. */
+int kfs_d_revalidate(struct dentry *dir, struct nameidata *nd)
+{ // default, nothing
+       return -1;
+}
+
+/* Produces the hash to lookup this dentry from the dcache */
+int kfs_d_hash(struct dentry *dentry, struct qstr *name)
+{
+       return -1;
+}
+
+/* Compares name1 and name2.  name1 should be a member of dir. */
+int kfs_d_compare(struct dentry *dir, struct qstr *name1, struct qstr *name2)
+{ // default, string comp (case sensitive)
+       return -1;
+}
+
+/* Called when the last ref is deleted (refcnt == 0) */
+int kfs_d_delete(struct dentry *dentry)
+{ // default, nothin
+       return -1;
+}
+
+/* Called when it's about to be slab-freed */
+int kfs_d_release(struct dentry *dentry)
+{ // default, nothin
+       return -1;
+}
+
+/* Called when the dentry loses it's inode (becomes "negative") */
+void kfs_d_iput(struct dentry *dentry, struct inode *inode)
+{ // default, call i_put to release the inode object
+}
+
+
+/* file_operations */
+
+/* Updates the file pointer */
+off_t kfs_llseek(struct file *file, off_t offset, int whence)
+{
+       return -1;
+}
+
+/* Read cound bytes from the file into buf, starting at *offset, which is increased
+ * accordingly */
+ssize_t kfs_read(struct file *file, char *buf, size_t count, off_t *offset)
+{
+       return -1;
+}
+
+/* Writes count bytes from buf to the file, starting at *offset, which is
+ * increased accordingly */
+ssize_t kfs_write(struct file *file, const char *buf, size_t count,
+                  off_t *offset)
+{
+       return -1;
+}
+
+/* Fills in the next directory entry (dirent), starting with d_off */
+int kfs_readdir(struct file *dir, struct dirent *dirent)
+{
+       return -1;
+}
+
+/* Memory maps the file into the virtual memory area */
+int kfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       return -1;
+}
+
+/* Opens the file specified by the inode, linking it with file */
+int kfs_open(struct inode *inode, struct file *file)
+{
+       return -1;
+}
+
+/* Called when a file descriptor is closed. */
+int kfs_flush(struct file *file)
+{
+       return -1;
+}
+
+/* Called when the file refcnt == 0 */
+int kfs_release(struct inode *inode, struct file *file)
+{
+       return -1;
+}
+
+/* Flushes the file's dirty contents to disc */
+int kfs_fsync(struct file *file, struct dentry *dentry, int datasync)
+{
+       return -1;
+}
+
+/* Traditionally, sleeps until there is file activity.  We probably won't
+ * support this, or we'll handle it differently. */
+unsigned int kfs_poll(struct file *file, struct poll_table_struct *poll_table)
+{
+       return -1;
+}
+
+/* Reads count bytes from a file, starting from (and modifiying) offset, and
+ * putting the bytes into buffers described by vector */
+ssize_t kfs_readv(struct file *file, const struct iovec *vector,
+                  unsigned long count, off_t *offset)
+{
+       return -1;
+}
+
+/* Writes count bytes to a file, starting from (and modifiying) offset, and
+ * taking the bytes from buffers described by vector */
+ssize_t kfs_writev(struct file *file, const struct iovec *vector,
+                  unsigned long count, off_t *offset)
+{
+       return -1;
+}
+
+/* Write the contents of file to the page.  Will sort the params later */
+ssize_t kfs_sendpage(struct file *file, struct page *page, int offset,
+                     size_t size, off_t pos, int more)
+{
+       return -1;
+}
+
+/* Checks random FS flags.  Used by NFS. */
+int kfs_check_flags(int flags)
+{ // default, nothing
+       return -1;
+}
+
+/* Redeclaration and initialization of the FS ops structures */
+struct super_operations kfs_s_op = {
+       kfs_alloc_inode,
+       kfs_destroy_inode,
+       kfs_read_inode,
+       kfs_dirty_inode,
+       kfs_write_inode,
+       kfs_put_inode,
+       kfs_drop_inode,
+       kfs_delete_inode,
+       kfs_put_super,
+       kfs_write_super,
+       kfs_sync_fs,
+       kfs_remount_fs,
+       kfs_umount_begin,
+};
+
+struct inode_operations kfs_i_op = {
+       kfs_create,
+       kfs_lookup,
+       kfs_link,
+       kfs_unlink,
+       kfs_symlink,
+       kfs_mkdir,
+       kfs_rmdir,
+       kfs_mknod,
+       kfs_rename,
+       kfs_readlink,
+       kfs_follow_link,
+       kfs_put_link,
+       kfs_truncate,
+       kfs_permission,
+};
+
+struct dentry_operations kfs_d_op = {
+       kfs_d_revalidate,
+       kfs_d_hash,
+       kfs_d_compare,
+       kfs_d_delete,
+       kfs_d_release,
+       kfs_d_iput,
+};
+
+struct file_operations kfs_f_op = {
+       kfs_llseek,
+       kfs_read,
+       kfs_write,
+       kfs_readdir,
+       kfs_mmap,
+       kfs_open,
+       kfs_flush,
+       kfs_release,
+       kfs_fsync,
+       kfs_poll,
+       kfs_readv,
+       kfs_writev,
+       kfs_sendpage,
+       kfs_check_flags,
+};
+
+/* KFS Specific Internal Functions */
+
 /* For obj files compiled with the kernel */
 #define DECL_PROG(x) \
     extern uint8_t (COUNT(sizeof(size_t)) _binary_obj_tests_##x##_size)[],\
index 1fd9f47..2cfeea5 100644 (file)
@@ -100,6 +100,7 @@ void manager_brho(void)
 
        switch (progress++) {
                case 0:
+                       monitor(0);
                        /* 124 is half of the available boxboro colors (with the kernel
                         * getting 8) */
                        //quick_proc_color_run("msr_dumb_while", p, 124);
diff --git a/kern/src/vfs.c b/kern/src/vfs.c
new file mode 100644 (file)
index 0000000..d12eab5
--- /dev/null
@@ -0,0 +1,212 @@
+/* Copyright (c) 2009, 2010 The Regents of the University of California
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * Default implementations and global values for the VFS. */
+
+#include <vfs.h> // keep this first
+#include <sys/queue.h>
+#include <assert.h>
+#include <stdio.h>
+#include <atomic.h>
+#include <slab.h>
+#include <kmalloc.h>
+#include <kfs.h>
+
+struct sb_tailq super_blocks = TAILQ_HEAD_INITIALIZER(super_blocks);
+spinlock_t super_blocks_lock = SPINLOCK_INITIALIZER;
+struct fs_type_tailq file_systems = TAILQ_HEAD_INITIALIZER(file_systems);
+struct namespace default_ns;
+// TODO: temp dcache, holds all dentries ever for now
+struct dentry_slist dcache = SLIST_HEAD_INITIALIZER(dcache);
+spinlock_t dcache_lock = SPINLOCK_INITIALIZER;
+
+struct kmem_cache *dentry_kcache; // not to be confused with the dcache
+struct kmem_cache *inode_kcache;
+struct kmem_cache *file_kcache;
+
+/* Mounts fs from dev_name at mnt_pt in namespace ns.  There could be no mnt_pt,
+ * such as with the root of (the default) namespace.  Not sure how it would work
+ * with multiple namespaces on the same FS yet.  Note if you mount the same FS
+ * multiple times, you only have one FS still (and one SB).  If we ever support
+ * that... */
+struct vfsmount *mount_fs(struct fs_type *fs, char *dev_name,
+                          struct dentry *mnt_pt, int flags,
+                          struct namespace *ns)
+{
+       struct super_block *sb;
+       struct vfsmount *vmnt = kmalloc(sizeof(struct vfsmount), 0);
+
+       /* Build the vfsmount, if there is no mnt_pt, mnt is the root vfsmount (for now).
+        * fields related to the actual FS, like the sb and the mnt_root are set in
+        * the fs-specific get_sb() call. */
+       if (!mnt_pt) {
+               vmnt->mnt_parent = NULL;
+               vmnt->mnt_mountpoint = NULL;
+       } else { /* common case, but won't be tested til we try to mount another FS */
+               mnt_pt->d_mount_point = TRUE;
+               mnt_pt->d_mounted_fs = vmnt;
+               atomic_inc(&vmnt->mnt_refcnt); /* held by mnt_pt */
+               vmnt->mnt_parent = mnt_pt->d_sb->s_mount;
+               vmnt->mnt_mountpoint = mnt_pt;
+       }
+       TAILQ_INIT(&vmnt->mnt_child_mounts);
+       vmnt->mnt_flags = flags;
+       vmnt->mnt_devname = dev_name;
+       vmnt->mnt_namespace = ns;
+       atomic_inc(&ns->refcnt); /* held by vmnt */
+       atomic_set(&vmnt->mnt_refcnt, 1); /* for the ref in the NS tailq below */
+
+       /* Read in / create the SB */
+       sb = fs->get_sb(fs, flags, dev_name, vmnt);
+       if (!sb)
+               panic("You're FS sucks");
+
+       /* TODO: consider moving this into get_sb or something, in case the SB
+        * already exists (mounting again) (if we support that) */
+       spin_lock(&super_blocks_lock);
+       TAILQ_INSERT_TAIL(&super_blocks, sb, s_list); /* storing a ref here... */
+       spin_unlock(&super_blocks_lock);
+
+       /* Update holding NS */
+       spin_lock(&ns->lock);
+       TAILQ_INSERT_TAIL(&ns->vfsmounts, vmnt, mnt_list);
+       spin_unlock(&ns->lock);
+       /* note to self: so, right after this point, the NS points to the root FS
+        * mount (we return the mnt, which gets assigned), the root mnt has a dentry
+        * for /, backed by an inode, with a SB prepped and in memory. */
+       return vmnt;
+}
+
+void vfs_init(void)
+{
+       struct fs_type *fs;
+
+       dentry_kcache = kmem_cache_create("dentry", sizeof(struct dentry),
+                                         __alignof__(struct dentry), 0, 0, 0);
+       inode_kcache = kmem_cache_create("inode", sizeof(struct inode),
+                                        __alignof__(struct inode), 0, 0, 0);
+       file_kcache = kmem_cache_create("file", sizeof(struct file),
+                                       __alignof__(struct file), 0, 0, 0);
+
+       atomic_set(&default_ns.refcnt, 1); // default NS never dies, +1 to exist
+       spinlock_init(&default_ns.lock);
+       default_ns.root = NULL;
+       TAILQ_INIT(&default_ns.vfsmounts);
+
+       /* build list of all FS's in the system.  put yours here.  if this is ever
+        * done on the fly, we'll need to lock. */
+       TAILQ_INSERT_TAIL(&file_systems, &kfs_fs_type, list);
+       TAILQ_FOREACH(fs, &file_systems, list)
+               printk("Supports the %s Filesystem\n", fs->name);
+
+       /* mounting KFS at the root (/), pending root= parameters */
+       // TODO: linux creates a temp root_fs, then mounts the real root onto that
+       default_ns.root = mount_fs(&kfs_fs_type, "RAM", NULL, 0, &default_ns);
+
+       printk("vfs_init() completed\n");
+       /*
+       put structs and friends in struct proc, and init in proc init
+       */
+       // LOOKUP: follow_mount, follow_link, etc
+       // pains in the ass for having .. or . in the middle of the path
+}
+
+/* Builds / populates the qstr of a dentry based on its d_iname.  If there is an
+ * l_name, (long), it will use that instead of the inline name.  This will
+ * probably change a bit. */
+void qstr_builder(struct dentry *dentry, char *l_name)
+{
+       dentry->d_name.name = l_name ? l_name : dentry->d_iname;
+       // TODO: pending what we actually do in d_hash
+       //dentry->d_name.hash = dentry->d_op->d_hash(dentry, &dentry->d_name); 
+       dentry->d_name.hash = 0xcafebabe;
+       dentry->d_name.len = strnlen(dentry->d_name.name, MAX_FILENAME_SZ);
+}
+
+/* Helper to alloc and initialize a generic superblock.  This handles all the
+ * VFS related things, like lists.  Each FS will need to handle its own things
+ * in it's *_get_sb(), usually involving reading off the disc. */
+struct super_block *get_sb(void)
+{
+       struct super_block *sb = kmalloc(sizeof(struct super_block), 0);
+       sb->s_dirty = FALSE;
+       spinlock_init(&sb->s_lock);
+       atomic_set(&sb->s_refcnt, 1); // for the ref passed out
+       TAILQ_INIT(&sb->s_inodes);
+       TAILQ_INIT(&sb->s_dirty_i);
+       TAILQ_INIT(&sb->s_io_wb);
+       SLIST_INIT(&sb->s_anon_d);
+       TAILQ_INIT(&sb->s_files);
+       sb->s_fs_info = 0; // can override somewhere else
+       return sb;
+}
+
+/* Final stages of initializing a super block, including creating and linking
+ * the root dentry, root inode, vmnt, and sb.  The d_op and root_ino are
+ * FS-specific, but otherwise it's FS-independent, tricky, and not worth having
+ * around multiple times.
+ *
+ * Not the world's best interface, so it's subject to change. */
+void init_sb(struct super_block *sb, struct vfsmount *vmnt,
+             struct dentry_operations *d_op, unsigned long root_ino)
+{
+       /* Build and init the first dentry / inode */
+       struct dentry *d_root = get_dentry(sb); /* ref held by vfsmount's mnt_root*/
+       strlcpy(d_root->d_iname, "/", MAX_FILENAME_SZ); /* probably right */
+       qstr_builder(d_root, 0);                                /* sets d_name */
+       /* a lot of here on down is normally done in lookup() */
+       d_root->d_op = d_op;
+       struct inode *inode = sb->s_op->alloc_inode(sb);
+       if (!inode)
+               panic("This FS sucks!");
+       d_root->d_inode = inode;
+       TAILQ_INSERT_TAIL(&inode->i_dentry, d_root, d_child);
+       atomic_inc(&d_root->d_refcnt);                  /* held by the inode */
+       inode->i_ino = root_ino;
+       /* TODO: add the inode to the appropriate list (off i_list) */
+       /* TODO: do we need to read in the inode?  can we do this on demand? */
+       /* if this FS is already mounted, we'll need to do something different. */
+       sb->s_op->read_inode(inode);
+       /* Link the dentry and SB to the VFS mount */
+       vmnt->mnt_root = d_root;                                /* refcnt'd above */
+       vmnt->mnt_sb = sb;
+       /* If there is no mount point, there is no parent.  This is true only for
+        * the rootfs. */
+       if (vmnt->mnt_mountpoint) {
+               d_root->d_parent = vmnt->mnt_mountpoint;        /* dentry of the root */
+               atomic_inc(&vmnt->mnt_mountpoint->d_refcnt);/* held by d_root */
+       }
+       /* insert the dentry into the dentry cache.  when's the earliest we can?
+        * when's the earliest we should?  what about concurrent accesses to the
+        * same dentry?  should be locking the dentry... */
+       dcache_put(d_root);
+}
+
+/* Helper to alloc and initialize a generic dentry */
+struct dentry *get_dentry(struct super_block *sb)
+{
+       struct dentry *dentry = kmem_cache_alloc(dentry_kcache, 0);
+       //memset(dentry, 0, sizeof(struct dentry));
+       atomic_set(&dentry->d_refcnt, 1); // this ref is returned
+       spinlock_init(&dentry->d_lock);
+       TAILQ_INIT(&dentry->d_subdirs);
+       dentry->d_time = 0;
+       dentry->d_sb = sb; // storing a ref here...
+       dentry->d_mount_point = FALSE;
+       dentry->d_mounted_fs = 0;
+       dentry->d_parent = 0;
+       dentry->d_flags = 0; // related to its dcache state
+       dentry->d_fs_info = 0;
+       SLIST_INIT(&dentry->d_bucket);
+       return dentry;
+}
+
+/* Adds a dentry to the dcache. */
+void dcache_put(struct dentry *dentry)
+{
+       // TODO: prob should do something with the dentry flags
+       spin_lock(&dcache_lock);
+       SLIST_INSERT_HEAD(&dcache, dentry, d_hash);
+       spin_unlock(&dcache_lock);
+}