x86: sends the EOI later in the IRQ path
[akaros.git] / kern / src / vfs.c
index 3dcb178..7b4c298 100644 (file)
@@ -40,9 +40,9 @@ struct vfsmount *__mount_fs(struct fs_type *fs, char *dev_name,
 
        /* this first ref is stored in the NS tailq below */
        kref_init(&vmnt->mnt_kref, fake_release, 1);
-       /* 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. */
+       /* 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;
@@ -99,7 +99,7 @@ void vfs_init(void)
        /* 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);
-#ifdef __CONFIG_EXT2FS__
+#ifdef CONFIG_EXT2FS
        TAILQ_INSERT_TAIL(&file_systems, &ext2_fs_type, list);
 #endif
        TAILQ_FOREACH(fs, &file_systems, list)
@@ -139,6 +139,10 @@ static struct dentry *do_lookup(struct dentry *parent, char *name)
 {
        struct dentry *result, *query;
        query = get_dentry(parent->d_sb, parent, name);
+       if (!query) {
+               warn("OOM in do_lookup(), probably wasn't expected\n");
+               return 0;
+       }
        result = dcache_get(parent->d_sb, query); 
        if (result) {
                __dentry_free(query);
@@ -243,7 +247,7 @@ static int follow_symlink(struct nameidata *nd)
                return 0;
        if (nd->depth > MAX_SYMLINK_DEPTH)
                return -ELOOP;
-       printd("Following symlink for dentry %08p %s\n", nd->dentry,
+       printd("Following symlink for dentry %p %s\n", nd->dentry,
               nd->dentry->d_name.name);
        nd->depth++;
        symname = nd->dentry->d_inode->i_op->readlink(nd->dentry);
@@ -571,6 +575,8 @@ void init_sb(struct super_block *sb, struct vfsmount *vmnt,
         * by vfsmount's mnt_root.  The parent is dealt with later. */
        struct dentry *d_root = get_dentry(sb, 0,  "/");        /* probably right */
 
+       if (!d_root)
+               panic("OOM!  init_sb() can't fail yet!");
        /* a lot of here on down is normally done in lookup() or create, since
         * get_dentry isn't a fully usable dentry.  The two FS-specific settings are
         * normally inherited from a parent within the same FS in get_dentry, but we
@@ -623,8 +629,10 @@ struct dentry *get_dentry(struct super_block *sb, struct dentry *parent,
        struct dentry *dentry = kmem_cache_alloc(dentry_kcache, 0);
        char *l_name = 0;
 
-       if (!dentry)
+       if (!dentry) {
+               set_errno(ENOMEM);
                return 0;
+       }
        //memset(dentry, 0, sizeof(struct dentry));
        kref_init(&dentry->d_kref, dentry_release, 1);  /* this ref is returned */
        spinlock_init(&dentry->d_lock);
@@ -668,7 +676,7 @@ void dentry_release(struct kref *kref)
 {
        struct dentry *dentry = container_of(kref, struct dentry, d_kref);
 
-       printd("'Releasing' dentry %08p: %s\n", dentry, dentry->d_name.name);
+       printd("'Releasing' dentry %p: %s\n", dentry, dentry->d_name.name);
        /* DYING dentries (recently unlinked / rmdir'd) just get freed */
        if (dentry->d_flags & DENTRY_DYING) {
                __dentry_free(dentry);
@@ -707,7 +715,7 @@ void dentry_release(struct kref *kref)
 void __dentry_free(struct dentry *dentry)
 {
        if (dentry->d_inode)
-               printk("Freeing dentry %08p: %s\n", dentry, dentry->d_name.name);
+               printk("Freeing dentry %p: %s\n", dentry, dentry->d_name.name);
        assert(dentry->d_op);   /* catch bugs.  a while back, some lacked d_op */
        dentry->d_op->d_release(dentry);
        /* TODO: check/test the boundaries on this. */
@@ -916,7 +924,7 @@ struct inode *get_inode(struct dentry *dentry)
 }
 
 /* Helper: loads/ reads in the inode numbered ino and attaches it to dentry */
-void load_inode(struct dentry *dentry, unsigned int ino)
+void load_inode(struct dentry *dentry, unsigned long ino)
 {
        struct inode *inode;
 
@@ -1044,14 +1052,18 @@ void inode_release(struct kref *kref)
        } else {
                inode->i_sb->s_op->delete_inode(inode);
        }
+       if (S_ISFIFO(inode->i_mode)) {
+               page_decref(kva2page(inode->i_pipe->p_buf));
+               kfree(inode->i_pipe);
+       }
+       /* TODO: (BDEV) */
+       // kref_put(inode->i_bdev->kref); /* assuming it's a bdev, could be a pipe*/
        /* Either way, we dealloc the in-memory version */
        inode->i_sb->s_op->dealloc_inode(inode);        /* FS-specific clean-up */
        kref_put(&inode->i_sb->s_kref);
        /* TODO: clean this up */
        assert(inode->i_mapping == &inode->i_pm);
        kmem_cache_free(inode_kcache, inode);
-       /* TODO: (BDEV) */
-       // kref_put(inode->i_bdev->kref); /* assuming it's a bdev */
 }
 
 /* Fills in kstat with the stat information for the inode */
@@ -1075,7 +1087,7 @@ void stat_inode(struct inode *inode, struct kstat *kstat)
 /* Inode Cache management.  In general, search on the ino, get a refcnt'd value
  * back.  Remove does not give you a reference back - it should only be called
  * in inode_release(). */
-struct inode *icache_get(struct super_block *sb, unsigned int ino)
+struct inode *icache_get(struct super_block *sb, unsigned long ino)
 {
        /* This is the same style as in pid2proc, it's the "safely create a strong
         * reference from a weak one, so long as other strong ones exist" pattern */
@@ -1097,7 +1109,7 @@ void icache_put(struct super_block *sb, struct inode *inode)
        spin_unlock(&sb->s_icache_lock);
 }
 
-struct inode *icache_remove(struct super_block *sb, unsigned int ino)
+struct inode *icache_remove(struct super_block *sb, unsigned long ino)
 {
        struct inode *inode;
        /* Presumably these hashtable removals could be easier since callers
@@ -1111,16 +1123,16 @@ struct inode *icache_remove(struct super_block *sb, unsigned int ino)
 
 /* File functions */
 
-/* Read count bytes from the file into buf, starting at *offset, which is increased
- * accordingly, returning the number of bytes transfered.  Most filesystems will
- * use this function for their f_op->read.  Note, this uses the page cache.
- * Want to try out page remapping later on... */
+/* Read count bytes from the file into buf, starting at *offset, which is
+ * increased accordingly, returning the number of bytes transfered.  Most
+ * filesystems will use this function for their f_op->read.
+ * Note, this uses the page cache. */
 ssize_t generic_file_read(struct file *file, char *buf, size_t count,
-                          off_t *offset)
+                          off64_t *offset)
 {
        struct page *page;
        int error;
-       off_t page_off;
+       off64_t page_off;
        unsigned long first_idx, last_idx;
        size_t copy_amt;
        char *buf_end;
@@ -1163,17 +1175,19 @@ ssize_t generic_file_read(struct file *file, char *buf, size_t count,
        return count;
 }
 
-/* Write count bytes from buf to the file, starting at *offset, which is increased
- * accordingly, returning the number of bytes transfered.  Most filesystems will
- * use this function for their f_op->write.  Note, this uses the page cache.
+/* Write count bytes from buf to the file, starting at *offset, which is
+ * increased accordingly, returning the number of bytes transfered.  Most
+ * filesystems will use this function for their f_op->write.  Note, this uses
+ * the page cache.
+ *
  * Changes don't get flushed to disc til there is an fsync, page cache eviction,
  * or other means of trying to writeback the pages. */
 ssize_t generic_file_write(struct file *file, const char *buf, size_t count,
-                           off_t *offset)
+                           off64_t *offset)
 {
        struct page *page;
        int error;
-       off_t page_off;
+       off64_t page_off;
        unsigned long first_idx, last_idx;
        size_t copy_amt;
        const char *buf_end;
@@ -1215,7 +1229,7 @@ ssize_t generic_file_write(struct file *file, const char *buf, size_t count,
  * currently expects us to do a readdir (short of doing linux's getdents).  Will
  * probably need work, based on whatever real programs want. */
 ssize_t generic_dir_read(struct file *file, char *u_buf, size_t count,
-                         off_t *offset)
+                         off64_t *offset)
 {
        struct kdirent dir_r = {0}, *dirent = &dir_r;
        int retval = 1;
@@ -1230,7 +1244,9 @@ ssize_t generic_dir_read(struct file *file, char *u_buf, size_t count,
                return 0;
        /* start readdir from where it left off: */
        dirent->d_off = *offset;
-       for (; (u_buf < buf_end) && (retval == 1); u_buf += sizeof(struct kdirent)){
+       for (   ;
+               u_buf + sizeof(struct kdirent) <= buf_end;
+               u_buf += sizeof(struct kdirent)) {
                /* TODO: UMEM/KFOP (pin the u_buf in the syscall, ditch the local copy,
                 * get rid of this memcpy and reliance on current, etc).  Might be
                 * tricky with the dirent->d_off and trust issues */
@@ -1247,6 +1263,9 @@ ssize_t generic_dir_read(struct file *file, char *u_buf, size_t count,
                        memcpy(u_buf, dirent, sizeof(struct dirent));
                }
                amt_copied += sizeof(struct dirent);
+               /* 0 signals end of directory */
+               if (retval == 0)
+                       break;
        }
        /* Next time read is called, we pick up where we left off */
        *offset = dirent->d_off;        /* UMEM */
@@ -1308,6 +1327,8 @@ struct file *do_file_open(char *path, int flags, int mode)
                }
                /* Create the inode/file.  get a fresh dentry too: */
                file_d = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
+               if (!file_d)
+                       goto out_path_only;
                parent_i = nd->dentry->d_inode;
                /* Note that the mode technically should only apply to future opens,
                 * but we apply it immediately. */
@@ -1364,10 +1385,8 @@ int do_symlink(char *path, const char *symname, int mode)
        }
        /* Doesn't already exist, let's try to make it: */
        sym_d = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
-       if (!sym_d) {
-               set_errno(ENOMEM);
+       if (!sym_d)
                goto out_path_only;
-       }
        parent_i = nd->dentry->d_inode;
        if (create_symlink(parent_i, sym_d, symname, mode))
                goto out_sym_d;
@@ -1406,10 +1425,8 @@ int do_link(char *old_path, char *new_path)
        /* Doesn't already exist, let's try to make it.  Still need to stitch it to
         * an inode and set its FS-specific stuff after this.*/
        link_d = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
-       if (!link_d) {
-               set_errno(ENOMEM);
+       if (!link_d)
                goto out_path_only;
-       }
        /* Now let's get the old_path target */
        old_d = lookup_dentry(old_path, LOOKUP_FOLLOW);
        if (!old_d)                                     /* errno set by lookup_dentry */
@@ -1556,10 +1573,8 @@ int do_mkdir(char *path, int mode)
        }
        /* Doesn't already exist, let's try to make it: */
        dentry = get_dentry(nd->dentry->d_sb, nd->dentry, nd->last.name);
-       if (!dentry) {
-               set_errno(ENOMEM);
+       if (!dentry)
                goto out_path_only;
-       }
        parent_i = nd->dentry->d_inode;
        if (create_dir(parent_i, dentry, mode))
                goto out_dentry;
@@ -1631,16 +1646,302 @@ out_path_only:
        return retval;
 }
 
-/* Opens and returns the file specified by dentry */
-struct file *dentry_open(struct dentry *dentry, int flags)
+/* Pipes: Doing a simple buffer with reader and writer offsets.  Size is power
+ * of two, so we can easily compute its status and whatnot. */
+
+#define PIPE_SZ                                        (1 << PGSHIFT)
+
+static size_t pipe_get_rd_idx(struct pipe_inode_info *pii)
+{
+       return pii->p_rd_off & (PIPE_SZ - 1);
+}
+
+static size_t pipe_get_wr_idx(struct pipe_inode_info *pii)
+{
+
+       return pii->p_wr_off & (PIPE_SZ - 1);
+}
+
+static bool pipe_is_empty(struct pipe_inode_info *pii)
+{
+       return __ring_empty(pii->p_wr_off, pii->p_rd_off);
+}
+
+static bool pipe_is_full(struct pipe_inode_info *pii)
+{
+       return __ring_full(PIPE_SZ, pii->p_wr_off, pii->p_rd_off);
+}
+
+static size_t pipe_nr_full(struct pipe_inode_info *pii)
+{
+       return __ring_nr_full(pii->p_wr_off, pii->p_rd_off);
+}
+
+static size_t pipe_nr_empty(struct pipe_inode_info *pii)
+{
+       return __ring_nr_empty(PIPE_SZ, pii->p_wr_off, pii->p_rd_off);
+}
+
+ssize_t pipe_file_read(struct file *file, char *buf, size_t count,
+                       off64_t *offset)
+{
+       struct pipe_inode_info *pii = file->f_dentry->d_inode->i_pipe;
+       size_t copy_amt, amt_copied = 0;
+
+       cv_lock(&pii->p_cv);
+       while (pipe_is_empty(pii)) {
+               /* We wait til the pipe is drained before sending EOF if there are no
+                * writers (instead of aborting immediately) */
+               if (!pii->p_nr_writers) {
+                       cv_unlock(&pii->p_cv);
+                       return 0;
+               }
+               if (file->f_flags & O_NONBLOCK) {
+                       cv_unlock(&pii->p_cv);
+                       set_errno(EAGAIN);
+                       return -1;
+               }
+               cv_wait(&pii->p_cv);
+               cpu_relax();
+       }
+       /* We might need to wrap-around with our copy, so we'll do the copy in two
+        * passes.  This will copy up to the end of the buffer, then on the next
+        * pass will copy the rest to the beginning of the buffer (if necessary) */
+       for (int i = 0; i < 2; i++) {
+               copy_amt = MIN(PIPE_SZ - pipe_get_rd_idx(pii),
+                              MIN(pipe_nr_full(pii), count));
+               assert(current);        /* shouldn't pipe from the kernel */
+               memcpy_to_user(current, buf, pii->p_buf + pipe_get_rd_idx(pii),
+                              copy_amt);
+               buf += copy_amt;
+               count -= copy_amt;
+               pii->p_rd_off += copy_amt;
+               amt_copied += copy_amt;
+       }
+       /* Just using one CV for both readers and writers.  We should rarely have
+        * multiple readers or writers. */
+       if (amt_copied)
+               __cv_broadcast(&pii->p_cv);
+       cv_unlock(&pii->p_cv);
+       return amt_copied;
+}
+
+/* Note: we're not dealing with PIPE_BUF and minimum atomic chunks, unless I
+ * have to later. */
+ssize_t pipe_file_write(struct file *file, const char *buf, size_t count,
+                        off64_t *offset)
+{
+       struct pipe_inode_info *pii = file->f_dentry->d_inode->i_pipe;
+       size_t copy_amt, amt_copied = 0;
+
+       cv_lock(&pii->p_cv);
+       /* Write aborts right away if there are no readers, regardless of pipe
+        * status. */
+       if (!pii->p_nr_readers) {
+               cv_unlock(&pii->p_cv);
+               set_errno(EPIPE);
+               return -1;
+       }
+       while (pipe_is_full(pii)) {
+               if (file->f_flags & O_NONBLOCK) {
+                       cv_unlock(&pii->p_cv);
+                       set_errno(EAGAIN);
+                       return -1;
+               }
+               cv_wait(&pii->p_cv);
+               cpu_relax();
+               /* Still need to check in the loop, in case the last reader left while
+                * we slept. */
+               if (!pii->p_nr_readers) {
+                       cv_unlock(&pii->p_cv);
+                       set_errno(EPIPE);
+                       return -1;
+               }
+       }
+       /* We might need to wrap-around with our copy, so we'll do the copy in two
+        * passes.  This will copy up to the end of the buffer, then on the next
+        * pass will copy the rest to the beginning of the buffer (if necessary) */
+       for (int i = 0; i < 2; i++) {
+               copy_amt = MIN(PIPE_SZ - pipe_get_wr_idx(pii),
+                              MIN(pipe_nr_empty(pii), count));
+               assert(current);        /* shouldn't pipe from the kernel */
+               memcpy_from_user(current, pii->p_buf + pipe_get_wr_idx(pii), buf,
+                                copy_amt);
+               buf += copy_amt;
+               count -= copy_amt;
+               pii->p_wr_off += copy_amt;
+               amt_copied += copy_amt;
+       }
+       /* Just using one CV for both readers and writers.  We should rarely have
+        * multiple readers or writers. */
+       if (amt_copied)
+               __cv_broadcast(&pii->p_cv);
+       cv_unlock(&pii->p_cv);
+       return amt_copied;
+}
+
+/* In open and release, we need to track the number of readers and writers,
+ * which we can differentiate by the file flags. */
+int pipe_open(struct inode *inode, struct file *file)
+{
+       struct pipe_inode_info *pii = inode->i_pipe;
+       cv_lock(&pii->p_cv);
+       /* Ugliness due to not using flags for O_RDONLY and friends... */
+       if (file->f_mode == S_IRUSR) {
+               pii->p_nr_readers++;
+       } else if (file->f_mode == S_IWUSR) {
+               pii->p_nr_writers++;
+       } else {
+               warn("Bad pipe file flags 0x%x\n", file->f_flags);
+       }
+       cv_unlock(&pii->p_cv);
+       return 0;
+}
+
+int pipe_release(struct inode *inode, struct file *file)
+{
+       struct pipe_inode_info *pii = inode->i_pipe;
+       cv_lock(&pii->p_cv);
+       /* Ugliness due to not using flags for O_RDONLY and friends... */
+       if (file->f_mode == S_IRUSR) {
+               pii->p_nr_readers--;
+       } else if (file->f_mode == S_IWUSR) {
+               pii->p_nr_writers--;
+       } else {
+               warn("Bad pipe file flags 0x%x\n", file->f_flags);
+       }
+       /* need to wake up any sleeping readers/writers, since we might be done */
+       __cv_broadcast(&pii->p_cv);
+       cv_unlock(&pii->p_cv);
+       return 0;
+}
+
+struct file_operations pipe_f_op = {
+       .read = pipe_file_read,
+       .write = pipe_file_write,
+       .open = pipe_open,
+       .release = pipe_release,
+       0
+};
+
+void pipe_debug(struct file *f)
+{
+       struct pipe_inode_info *pii = f->f_dentry->d_inode->i_pipe;
+       assert(pii);
+       printk("PIPE %p\n", pii);
+       printk("\trdoff %p\n", pii->p_rd_off);
+       printk("\twroff %p\n", pii->p_wr_off);
+       printk("\tnr_rds %d\n", pii->p_nr_readers);
+       printk("\tnr_wrs %d\n", pii->p_nr_writers);
+       printk("\tcv waiters %d\n", pii->p_cv.nr_waiters);
+
+}
+
+/* General plan: get a dentry/inode to represent the pipe.  We'll alloc it from
+ * the default_ns SB, but won't actually link it anywhere.  It'll only be held
+ * alive by the krefs, til all the FDs are closed. */
+int do_pipe(struct file **pipe_files, int flags)
+{
+       struct dentry *pipe_d;
+       struct inode *pipe_i;
+       struct file *pipe_f_read, *pipe_f_write;
+       struct super_block *def_sb = default_ns.root->mnt_sb;
+       struct pipe_inode_info *pii;
+
+       pipe_d = get_dentry(def_sb, 0, "pipe");
+       if (!pipe_d)
+               return -1;
+       pipe_d->d_op = &dummy_d_op;
+       pipe_i = get_inode(pipe_d);
+       if (!pipe_i)
+               goto error_post_dentry;
+       /* preemptively mark the dentry for deletion.  we have an unlinked dentry
+        * right off the bat, held in only by the kref chain (pipe_d is the ref). */
+       pipe_d->d_flags |= DENTRY_DYING;
+       /* pipe_d->d_inode still has one ref to pipe_i, keeping the inode alive */
+       kref_put(&pipe_i->i_kref);
+       /* init inode fields.  note we're using the dummy ops for i_op and d_op */
+       pipe_i->i_mode = S_IRWXU | S_IRWXG | S_IRWXO;
+       SET_FTYPE(pipe_i->i_mode, __S_IFIFO);   /* using type == FIFO */
+       pipe_i->i_nlink = 1;                    /* one for the dentry */
+       pipe_i->i_uid = 0;
+       pipe_i->i_gid = 0;
+       pipe_i->i_size = PGSIZE;
+       pipe_i->i_blocks = 0;
+       pipe_i->i_atime.tv_sec = 0;
+       pipe_i->i_atime.tv_nsec = 0;
+       pipe_i->i_mtime.tv_sec = 0;
+       pipe_i->i_mtime.tv_nsec = 0;
+       pipe_i->i_ctime.tv_sec = 0;
+       pipe_i->i_ctime.tv_nsec = 0;
+       pipe_i->i_fs_info = 0;
+       pipe_i->i_op = &dummy_i_op;
+       pipe_i->i_fop = &pipe_f_op;
+       pipe_i->i_socket = FALSE;
+       /* Actually build the pipe.  We're using one page, hanging off the
+        * pipe_inode_info struct.  When we release the inode, we free the pipe
+        * memory too */
+       pipe_i->i_pipe = kmalloc(sizeof(struct pipe_inode_info), KMALLOC_WAIT);
+       pii = pipe_i->i_pipe;
+       if (!pii) {
+               set_errno(ENOMEM);
+               goto error_kmalloc;
+       }
+       pii->p_buf = kpage_zalloc_addr();
+       if (!pii->p_buf) {
+               set_errno(ENOMEM);
+               goto error_kpage;
+       }
+       pii->p_rd_off = 0;
+       pii->p_wr_off = 0;
+       pii->p_nr_readers = 0;
+       pii->p_nr_writers = 0;
+       cv_init(&pii->p_cv);    /* must do this before dentry_open / pipe_open */
+       /* Now we have an inode for the pipe.  We need two files for the read and
+        * write ends of the pipe. */
+       flags &= ~(O_ACCMODE);  /* avoid user bugs */
+       pipe_f_read = dentry_open(pipe_d, flags | O_RDONLY);
+       if (!pipe_f_read)
+               goto error_f_read;
+       pipe_f_write = dentry_open(pipe_d, flags | O_WRONLY);
+       if (!pipe_f_write)
+               goto error_f_write;
+       pipe_files[0] = pipe_f_read;
+       pipe_files[1] = pipe_f_write;
+       return 0;
+
+error_f_write:
+       kref_put(&pipe_f_read->f_kref);
+error_f_read:
+       page_decref(kva2page(pii->p_buf));
+error_kpage:
+       kfree(pipe_i->i_pipe);
+error_kmalloc:
+       /* We don't need to free the pipe_i; putting the dentry will free it */
+error_post_dentry:
+       /* Note we only free the dentry on failure. */
+       kref_put(&pipe_d->d_kref);
+       return -1;
+}
+
+struct file *alloc_file(void)
 {
-       struct inode *inode;
-       int desired_mode;
        struct file *file = kmem_cache_alloc(file_kcache, 0);
        if (!file) {
                set_errno(ENOMEM);
                return 0;
        }
+       /* one for the ref passed out*/
+       kref_init(&file->f_kref, file_release, 1);
+       return file;
+}
+
+/* Opens and returns the file specified by dentry */
+struct file *dentry_open(struct dentry *dentry, int flags)
+{
+       struct inode *inode;
+       struct file *file;
+       int desired_mode;
        inode = dentry->d_inode;
        /* Do the mode first, since we can still error out.  f_mode stores how the
         * OS file is open, which can be more restrictive than the i_mode */
@@ -1659,9 +1960,10 @@ struct file *dentry_open(struct dentry *dentry, int flags)
        }
        if (check_perms(inode, desired_mode))
                goto error_access;
+       file = alloc_file();
+       if (!file)
+               return 0;
        file->f_mode = desired_mode;
-       /* one for the ref passed out, and *none* for the sb TAILQ */
-       kref_init(&file->f_kref, file_release, 1);
        /* Add to the list of all files of this SB */
        TAILQ_INSERT_TAIL(&inode->i_sb->s_files, file, f_list);
        kref_get(&dentry->d_kref, 1);
@@ -1677,13 +1979,12 @@ struct file *dentry_open(struct dentry *dentry, int flags)
        file->f_error = 0;
 //     struct event_poll_tailq         f_ep_links;
        spinlock_init(&file->f_ep_lock);
-       file->f_fs_info = 0;                                            /* prob overriden by the fs */
+       file->f_privdata = 0;                                           /* prob overriden by the fs */
        file->f_mapping = inode->i_mapping;
        file->f_op->open(inode, file);
        return file;
 error_access:
        set_errno(EACCES);
-       kmem_cache_free(file_kcache, file);
        return 0;
 }
 
@@ -1755,7 +2056,6 @@ struct file *put_file_from_fd(struct files_struct *open_files, int file_desc)
        spin_unlock(&open_files->lock);
        return file;
 }
-
 /* Inserts the file in the files_struct, returning the corresponding new file
  * descriptor, or an error code.  We start looking for open fds from low_fd. */
 int insert_file(struct files_struct *open_files, struct file *file, int low_fd)
@@ -1981,3 +2281,138 @@ int ls_dash_r(char *path)
        path_release(nd);
        return 0;
 }
+
+/* Dummy ops, to catch weird operations we weren't expecting */
+int dummy_create(struct inode *dir, struct dentry *dentry, int mode,
+                 struct nameidata *nd)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+struct dentry *dummy_lookup(struct inode *dir, struct dentry *dentry,
+                          struct nameidata *nd)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return 0;
+}
+
+int dummy_link(struct dentry *old_dentry, struct inode *dir,
+             struct dentry *new_dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_unlink(struct inode *dir, struct dentry *dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_mkdir(struct inode *dir, struct dentry *dentry, int mode)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_rmdir(struct inode *dir, struct dentry *dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_rename(struct inode *old_dir, struct dentry *old_dentry,
+               struct inode *new_dir, struct dentry *new_dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+char *dummy_readlink(struct dentry *dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return 0;
+}
+
+void dummy_truncate(struct inode *inode)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+}
+
+int dummy_permission(struct inode *inode, int mode, struct nameidata *nd)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_d_revalidate(struct dentry *dir, struct nameidata *nd)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_d_hash(struct dentry *dentry, struct qstr *name)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_d_compare(struct dentry *dir, struct qstr *name1, struct qstr *name2)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_d_delete(struct dentry *dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+int dummy_d_release(struct dentry *dentry)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+       return -1;
+}
+
+void dummy_d_iput(struct dentry *dentry, struct inode *inode)
+{
+       printk("Dummy VFS function %s called!\n", __FUNCTION__);
+}
+
+struct inode_operations dummy_i_op = {
+       dummy_create,
+       dummy_lookup,
+       dummy_link,
+       dummy_unlink,
+       dummy_symlink,
+       dummy_mkdir,
+       dummy_rmdir,
+       dummy_mknod,
+       dummy_rename,
+       dummy_readlink,
+       dummy_truncate,
+       dummy_permission,
+};
+
+struct dentry_operations dummy_d_op = {
+       dummy_d_revalidate,
+       dummy_d_hash,
+       dummy_d_compare,
+       dummy_d_delete,
+       dummy_d_release,
+       dummy_d_iput,
+};