v4.19.13 snapshot.
diff --git a/fs/coda/Kconfig b/fs/coda/Kconfig
new file mode 100644
index 0000000..c0e5a7f
--- /dev/null
+++ b/fs/coda/Kconfig
@@ -0,0 +1,21 @@
+config CODA_FS
+	tristate "Coda file system support (advanced network fs)"
+	depends on INET
+	help
+	  Coda is an advanced network file system, similar to NFS in that it
+	  enables you to mount file systems of a remote server and access them
+	  with regular Unix commands as if they were sitting on your hard
+	  disk.  Coda has several advantages over NFS: support for
+	  disconnected operation (e.g. for laptops), read/write server
+	  replication, security model for authentication and encryption,
+	  persistent client caches and write back caching.
+
+	  If you say Y here, your Linux box will be able to act as a Coda
+	  *client*.  You will need user level code as well, both for the
+	  client and server.  Servers are currently user level, i.e. they need
+	  no kernel support.  Please read
+	  <file:Documentation/filesystems/coda.txt> and check out the Coda
+	  home page <http://www.coda.cs.cmu.edu/>.
+
+	  To compile the coda client support as a module, choose M here: the
+	  module will be called coda.
diff --git a/fs/coda/Makefile b/fs/coda/Makefile
new file mode 100644
index 0000000..1bab69a
--- /dev/null
+++ b/fs/coda/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the Linux Coda filesystem routines.
+#
+
+obj-$(CONFIG_CODA_FS) += coda.o
+
+coda-objs := psdev.o cache.o cnode.o inode.o dir.o file.o upcall.o \
+	     coda_linux.o symlink.o pioctl.o sysctl.o 
+
+# If you want debugging output, please uncomment the following line.
+
+# ccflags-y := -DDEBUG -DDEBUG_SMB_MALLOC=1
diff --git a/fs/coda/cache.c b/fs/coda/cache.c
new file mode 100644
index 0000000..201fc08
--- /dev/null
+++ b/fs/coda/cache.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cache operations for Coda.
+ * For Linux 2.1: (C) 1997 Carnegie Mellon University
+ * For Linux 2.3: (C) 2000 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project http://www.coda.cs.cmu.edu/ <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+#include "coda_linux.h"
+#include "coda_cache.h"
+
+static atomic_t permission_epoch = ATOMIC_INIT(0);
+
+/* replace or extend an acl cache hit */
+void coda_cache_enter(struct inode *inode, int mask)
+{
+	struct coda_inode_info *cii = ITOC(inode);
+
+	spin_lock(&cii->c_lock);
+	cii->c_cached_epoch = atomic_read(&permission_epoch);
+	if (!uid_eq(cii->c_uid, current_fsuid())) {
+		cii->c_uid = current_fsuid();
+                cii->c_cached_perm = mask;
+        } else
+                cii->c_cached_perm |= mask;
+	spin_unlock(&cii->c_lock);
+}
+
+/* remove cached acl from an inode */
+void coda_cache_clear_inode(struct inode *inode)
+{
+	struct coda_inode_info *cii = ITOC(inode);
+	spin_lock(&cii->c_lock);
+	cii->c_cached_epoch = atomic_read(&permission_epoch) - 1;
+	spin_unlock(&cii->c_lock);
+}
+
+/* remove all acl caches */
+void coda_cache_clear_all(struct super_block *sb)
+{
+	atomic_inc(&permission_epoch);
+}
+
+
+/* check if the mask has been matched against the acl already */
+int coda_cache_check(struct inode *inode, int mask)
+{
+	struct coda_inode_info *cii = ITOC(inode);
+	int hit;
+	
+	spin_lock(&cii->c_lock);
+	hit = (mask & cii->c_cached_perm) == mask &&
+	    uid_eq(cii->c_uid, current_fsuid()) &&
+	    cii->c_cached_epoch == atomic_read(&permission_epoch);
+	spin_unlock(&cii->c_lock);
+
+	return hit;
+}
+
+
+/* Purging dentries and children */
+/* The following routines drop dentries which are not
+   in use and flag dentries which are in use to be 
+   zapped later.
+
+   The flags are detected by:
+   - coda_dentry_revalidate (for lookups) if the flag is C_PURGE
+   - coda_dentry_delete: to remove dentry from the cache when d_count
+     falls to zero
+   - an inode method coda_revalidate (for attributes) if the 
+     flag is C_VATTR
+*/
+
+/* this won't do any harm: just flag all children */
+static void coda_flag_children(struct dentry *parent, int flag)
+{
+	struct dentry *de;
+
+	spin_lock(&parent->d_lock);
+	list_for_each_entry(de, &parent->d_subdirs, d_child) {
+		/* don't know what to do with negative dentries */
+		if (d_inode(de) ) 
+			coda_flag_inode(d_inode(de), flag);
+	}
+	spin_unlock(&parent->d_lock);
+	return; 
+}
+
+void coda_flag_inode_children(struct inode *inode, int flag)
+{
+	struct dentry *alias_de;
+
+	if ( !inode || !S_ISDIR(inode->i_mode)) 
+		return; 
+
+	alias_de = d_find_alias(inode);
+	if (!alias_de)
+		return;
+	coda_flag_children(alias_de, flag);
+	shrink_dcache_parent(alias_de);
+	dput(alias_de);
+}
+
diff --git a/fs/coda/cnode.c b/fs/coda/cnode.c
new file mode 100644
index 0000000..845b5a6
--- /dev/null
+++ b/fs/coda/cnode.c
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0
+/* cnode related routines for the coda kernel code
+   (C) 1996 Peter Braam
+   */
+
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/time.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+#include <linux/pagemap.h>
+#include "coda_linux.h"
+
+static inline int coda_fideq(struct CodaFid *fid1, struct CodaFid *fid2)
+{
+	return memcmp(fid1, fid2, sizeof(*fid1)) == 0;
+}
+
+static const struct inode_operations coda_symlink_inode_operations = {
+	.get_link	= page_get_link,
+	.setattr	= coda_setattr,
+};
+
+/* cnode.c */
+static void coda_fill_inode(struct inode *inode, struct coda_vattr *attr)
+{
+        coda_vattr_to_iattr(inode, attr);
+
+        if (S_ISREG(inode->i_mode)) {
+                inode->i_op = &coda_file_inode_operations;
+                inode->i_fop = &coda_file_operations;
+        } else if (S_ISDIR(inode->i_mode)) {
+                inode->i_op = &coda_dir_inode_operations;
+                inode->i_fop = &coda_dir_operations;
+        } else if (S_ISLNK(inode->i_mode)) {
+		inode->i_op = &coda_symlink_inode_operations;
+		inode_nohighmem(inode);
+		inode->i_data.a_ops = &coda_symlink_aops;
+		inode->i_mapping = &inode->i_data;
+	} else
+                init_special_inode(inode, inode->i_mode, huge_decode_dev(attr->va_rdev));
+}
+
+static int coda_test_inode(struct inode *inode, void *data)
+{
+	struct CodaFid *fid = (struct CodaFid *)data;
+	struct coda_inode_info *cii = ITOC(inode);
+	return coda_fideq(&cii->c_fid, fid);
+}
+
+static int coda_set_inode(struct inode *inode, void *data)
+{
+	struct CodaFid *fid = (struct CodaFid *)data;
+	struct coda_inode_info *cii = ITOC(inode);
+	cii->c_fid = *fid;
+	return 0;
+}
+
+struct inode * coda_iget(struct super_block * sb, struct CodaFid * fid,
+			 struct coda_vattr * attr)
+{
+	struct inode *inode;
+	struct coda_inode_info *cii;
+	unsigned long hash = coda_f2i(fid);
+
+	inode = iget5_locked(sb, hash, coda_test_inode, coda_set_inode, fid);
+
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	if (inode->i_state & I_NEW) {
+		cii = ITOC(inode);
+		/* we still need to set i_ino for things like stat(2) */
+		inode->i_ino = hash;
+		/* inode is locked and unique, no need to grab cii->c_lock */
+		cii->c_mapcount = 0;
+		unlock_new_inode(inode);
+	}
+
+	/* always replace the attributes, type might have changed */
+	coda_fill_inode(inode, attr);
+	return inode;
+}
+
+/* this is effectively coda_iget:
+   - get attributes (might be cached)
+   - get the inode for the fid using vfs iget
+   - link the two up if this is needed
+   - fill in the attributes
+*/
+struct inode *coda_cnode_make(struct CodaFid *fid, struct super_block *sb)
+{
+        struct coda_vattr attr;
+	struct inode *inode;
+        int error;
+        
+	/* We get inode numbers from Venus -- see venus source */
+	error = venus_getattr(sb, fid, &attr);
+	if (error)
+		return ERR_PTR(error);
+
+	inode = coda_iget(sb, fid, &attr);
+	if (IS_ERR(inode))
+		pr_warn("%s: coda_iget failed\n", __func__);
+	return inode;
+}
+
+
+/* Although we treat Coda file identifiers as immutable, there is one
+ * special case for files created during a disconnection where they may
+ * not be globally unique. When an identifier collision is detected we
+ * first try to flush the cached inode from the kernel and finally
+ * resort to renaming/rehashing in-place. Userspace remembers both old
+ * and new values of the identifier to handle any in-flight upcalls.
+ * The real solution is to use globally unique UUIDs as identifiers, but
+ * retrofitting the existing userspace code for this is non-trivial. */
+void coda_replace_fid(struct inode *inode, struct CodaFid *oldfid, 
+		      struct CodaFid *newfid)
+{
+	struct coda_inode_info *cii = ITOC(inode);
+	unsigned long hash = coda_f2i(newfid);
+	
+	BUG_ON(!coda_fideq(&cii->c_fid, oldfid));
+
+	/* replace fid and rehash inode */
+	/* XXX we probably need to hold some lock here! */
+	remove_inode_hash(inode);
+	cii->c_fid = *newfid;
+	inode->i_ino = hash;
+	__insert_inode_hash(inode, hash);
+}
+
+/* convert a fid to an inode. */
+struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb) 
+{
+	struct inode *inode;
+	unsigned long hash = coda_f2i(fid);
+
+	if ( !sb ) {
+		pr_warn("%s: no sb!\n", __func__);
+		return NULL;
+	}
+
+	inode = ilookup5(sb, hash, coda_test_inode, fid);
+	if ( !inode )
+		return NULL;
+
+	/* we should never see newly created inodes because we intentionally
+	 * fail in the initialization callback */
+	BUG_ON(inode->i_state & I_NEW);
+
+	return inode;
+}
+
+/* the CONTROL inode is made without asking attributes from Venus */
+struct inode *coda_cnode_makectl(struct super_block *sb)
+{
+	struct inode *inode = new_inode(sb);
+	if (inode) {
+		inode->i_ino = CTL_INO;
+		inode->i_op = &coda_ioctl_inode_operations;
+		inode->i_fop = &coda_ioctl_operations;
+		inode->i_mode = 0444;
+		return inode;
+	}
+	return ERR_PTR(-ENOMEM);
+}
+
diff --git a/fs/coda/coda_cache.h b/fs/coda/coda_cache.h
new file mode 100644
index 0000000..c9f7a77
--- /dev/null
+++ b/fs/coda/coda_cache.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Coda filesystem -- Linux Minicache
+ *
+ * Copyright (C) 1989 - 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this software to
+ * contribute improvements to the Coda project. Contact Peter Braam
+ * <coda@cs.cmu.edu>
+ */
+
+#ifndef _CFSNC_HEADER_
+#define _CFSNC_HEADER_
+
+/* credential cache */
+void coda_cache_enter(struct inode *inode, int mask);
+void coda_cache_clear_inode(struct inode *);
+void coda_cache_clear_all(struct super_block *sb);
+int coda_cache_check(struct inode *inode, int mask);
+
+/* for downcalls and attributes and lookups */
+void coda_flag_inode_children(struct inode *inode, int flag);
+
+#endif /* _CFSNC_HEADER_ */
diff --git a/fs/coda/coda_fs_i.h b/fs/coda/coda_fs_i.h
new file mode 100644
index 0000000..d702ba1
--- /dev/null
+++ b/fs/coda/coda_fs_i.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  coda_fs_i.h
+ *
+ *  Copyright (C) 1998 Carnegie Mellon University
+ *
+ */
+
+#ifndef _LINUX_CODA_FS_I
+#define _LINUX_CODA_FS_I
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/coda.h>
+
+/*
+ * coda fs inode data
+ * c_lock protects accesses to c_flags, c_mapcount, c_cached_epoch, c_uid and
+ * c_cached_perm.
+ * vfs_inode is set only when the inode is created and never changes.
+ * c_fid is set when the inode is created and should be considered immutable.
+ */
+struct coda_inode_info {
+	struct CodaFid	   c_fid;	/* Coda identifier */
+	u_short	           c_flags;     /* flags (see below) */
+	unsigned int	   c_mapcount;  /* nr of times this inode is mapped */
+	unsigned int	   c_cached_epoch; /* epoch for cached permissions */
+	kuid_t		   c_uid;	/* fsuid for cached permissions */
+	unsigned int       c_cached_perm; /* cached access permissions */
+	spinlock_t	   c_lock;
+	struct inode	   vfs_inode;
+};
+
+/*
+ * coda fs file private data
+ */
+#define CODA_MAGIC 0xC0DAC0DA
+struct coda_file_info {
+	int		   cfi_magic;	  /* magic number */
+	struct file	  *cfi_container; /* container file for this cnode */
+	unsigned int	   cfi_mapcount;  /* nr of times this file is mapped */
+};
+
+#define CODA_FTOC(file) ((struct coda_file_info *)((file)->private_data))
+
+/* flags */
+#define C_VATTR       0x1   /* Validity of vattr in inode */
+#define C_FLUSH       0x2   /* used after a flush */
+#define C_DYING       0x4   /* from venus (which died) */
+#define C_PURGE       0x8
+
+struct inode *coda_cnode_make(struct CodaFid *, struct super_block *);
+struct inode *coda_iget(struct super_block *sb, struct CodaFid *fid, struct coda_vattr *attr);
+struct inode *coda_cnode_makectl(struct super_block *sb);
+struct inode *coda_fid_to_inode(struct CodaFid *fid, struct super_block *sb);
+void coda_replace_fid(struct inode *, struct CodaFid *, struct CodaFid *);
+
+#endif
diff --git a/fs/coda/coda_int.h b/fs/coda/coda_int.h
new file mode 100644
index 0000000..bb0b3e0
--- /dev/null
+++ b/fs/coda/coda_int.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CODA_INT_
+#define _CODA_INT_
+
+struct dentry;
+struct file;
+
+extern struct file_system_type coda_fs_type;
+extern unsigned long coda_timeout;
+extern int coda_hard;
+extern int coda_fake_statfs;
+
+void coda_destroy_inodecache(void);
+int __init coda_init_inodecache(void);
+int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync);
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+
+#endif  /*  _CODA_INT_  */
+
+
diff --git a/fs/coda/coda_linux.c b/fs/coda/coda_linux.c
new file mode 100644
index 0000000..f3d543d
--- /dev/null
+++ b/fs/coda/coda_linux.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+#include "coda_linux.h"
+
+/* initialize the debugging variables */
+int coda_fake_statfs;
+
+/* print a fid */
+char * coda_f2s(struct CodaFid *f)
+{
+	static char s[60];
+
+ 	sprintf(s, "(%08x.%08x.%08x.%08x)", f->opaque[0], f->opaque[1], f->opaque[2], f->opaque[3]);
+
+	return s;
+}
+
+/* recognize special .CONTROL name */
+int coda_iscontrol(const char *name, size_t length)
+{
+	return ((CODA_CONTROLLEN == length) && 
+                (strncmp(name, CODA_CONTROL, CODA_CONTROLLEN) == 0));
+}
+
+unsigned short coda_flags_to_cflags(unsigned short flags)
+{
+	unsigned short coda_flags = 0;
+	
+	if ((flags & O_ACCMODE) == O_RDONLY)
+		coda_flags |= C_O_READ;
+
+	if ((flags & O_ACCMODE) == O_RDWR)
+		coda_flags |= C_O_READ | C_O_WRITE;
+
+	if ((flags & O_ACCMODE) == O_WRONLY)
+		coda_flags |= C_O_WRITE;
+
+	if (flags & O_TRUNC)
+		coda_flags |= C_O_TRUNC;
+
+	if (flags & O_CREAT)
+		coda_flags |= C_O_CREAT;
+
+	if (flags & O_EXCL)
+		coda_flags |= C_O_EXCL;
+
+	return coda_flags;
+}
+
+
+/* utility functions below */
+void coda_vattr_to_iattr(struct inode *inode, struct coda_vattr *attr)
+{
+        int inode_type;
+        /* inode's i_flags, i_ino are set by iget 
+           XXX: is this all we need ??
+           */
+        switch (attr->va_type) {
+        case C_VNON:
+                inode_type  = 0;
+                break;
+        case C_VREG:
+                inode_type = S_IFREG;
+                break;
+        case C_VDIR:
+                inode_type = S_IFDIR;
+                break;
+        case C_VLNK:
+                inode_type = S_IFLNK;
+                break;
+        default:
+                inode_type = 0;
+        }
+	inode->i_mode |= inode_type;
+
+	if (attr->va_mode != (u_short) -1)
+	        inode->i_mode = attr->va_mode | inode_type;
+        if (attr->va_uid != -1) 
+	        inode->i_uid = make_kuid(&init_user_ns, (uid_t) attr->va_uid);
+        if (attr->va_gid != -1)
+	        inode->i_gid = make_kgid(&init_user_ns, (gid_t) attr->va_gid);
+	if (attr->va_nlink != -1)
+		set_nlink(inode, attr->va_nlink);
+	if (attr->va_size != -1)
+	        inode->i_size = attr->va_size;
+	if (attr->va_size != -1)
+		inode->i_blocks = (attr->va_size + 511) >> 9;
+	if (attr->va_atime.tv_sec != -1) 
+		inode->i_atime = timespec_to_timespec64(attr->va_atime);
+	if (attr->va_mtime.tv_sec != -1)
+		inode->i_mtime = timespec_to_timespec64(attr->va_mtime);
+        if (attr->va_ctime.tv_sec != -1)
+		inode->i_ctime = timespec_to_timespec64(attr->va_ctime);
+}
+
+
+/* 
+ * BSD sets attributes that need not be modified to -1. 
+ * Linux uses the valid field to indicate what should be
+ * looked at.  The BSD type field needs to be deduced from linux 
+ * mode.
+ * So we have to do some translations here.
+ */
+
+void coda_iattr_to_vattr(struct iattr *iattr, struct coda_vattr *vattr)
+{
+        unsigned int valid;
+
+        /* clean out */        
+	vattr->va_mode = -1;
+        vattr->va_uid = (vuid_t) -1; 
+        vattr->va_gid = (vgid_t) -1;
+        vattr->va_size = (off_t) -1;
+	vattr->va_atime.tv_sec = (time_t) -1;
+	vattr->va_atime.tv_nsec =  (time_t) -1;
+        vattr->va_mtime.tv_sec = (time_t) -1;
+        vattr->va_mtime.tv_nsec = (time_t) -1;
+	vattr->va_ctime.tv_sec = (time_t) -1;
+	vattr->va_ctime.tv_nsec = (time_t) -1;
+        vattr->va_type = C_VNON;
+	vattr->va_fileid = -1;
+	vattr->va_gen = -1;
+	vattr->va_bytes = -1;
+	vattr->va_nlink = -1;
+	vattr->va_blocksize = -1;
+	vattr->va_rdev = -1;
+        vattr->va_flags = 0;
+
+        /* determine the type */
+#if 0
+        mode = iattr->ia_mode;
+                if ( S_ISDIR(mode) ) {
+                vattr->va_type = C_VDIR; 
+        } else if ( S_ISREG(mode) ) {
+                vattr->va_type = C_VREG;
+        } else if ( S_ISLNK(mode) ) {
+                vattr->va_type = C_VLNK;
+        } else {
+                /* don't do others */
+                vattr->va_type = C_VNON;
+        }
+#endif 
+
+        /* set those vattrs that need change */
+        valid = iattr->ia_valid;
+        if ( valid & ATTR_MODE ) {
+                vattr->va_mode = iattr->ia_mode;
+	}
+        if ( valid & ATTR_UID ) {
+                vattr->va_uid = (vuid_t) from_kuid(&init_user_ns, iattr->ia_uid);
+	}
+        if ( valid & ATTR_GID ) {
+                vattr->va_gid = (vgid_t) from_kgid(&init_user_ns, iattr->ia_gid);
+	}
+        if ( valid & ATTR_SIZE ) {
+                vattr->va_size = iattr->ia_size;
+	}
+        if ( valid & ATTR_ATIME ) {
+		vattr->va_atime = timespec64_to_timespec(iattr->ia_atime);
+	}
+        if ( valid & ATTR_MTIME ) {
+		vattr->va_mtime = timespec64_to_timespec(iattr->ia_mtime);
+	}
+        if ( valid & ATTR_CTIME ) {
+		vattr->va_ctime = timespec64_to_timespec(iattr->ia_ctime);
+	}
+}
+
diff --git a/fs/coda/coda_linux.h b/fs/coda/coda_linux.h
new file mode 100644
index 0000000..126155c
--- /dev/null
+++ b/fs/coda/coda_linux.h
@@ -0,0 +1,105 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* 
+ * Coda File System, Linux Kernel module
+ * 
+ * Original version, adapted from cfs_mach.c, (C) Carnegie Mellon University
+ * Linux modifications (C) 1996, Peter J. Braam
+ * Rewritten for Linux 2.1 (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this software to
+ * contribute improvements to the Coda project.
+ */
+
+#ifndef _LINUX_CODA_FS
+#define _LINUX_CODA_FS
+
+#ifdef pr_fmt
+#undef pr_fmt
+#endif
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/wait.h>		
+#include <linux/types.h>
+#include <linux/fs.h>
+#include "coda_fs_i.h"
+
+/* operations */
+extern const struct inode_operations coda_dir_inode_operations;
+extern const struct inode_operations coda_file_inode_operations;
+extern const struct inode_operations coda_ioctl_inode_operations;
+
+extern const struct dentry_operations coda_dentry_operations;
+
+extern const struct address_space_operations coda_file_aops;
+extern const struct address_space_operations coda_symlink_aops;
+
+extern const struct file_operations coda_dir_operations;
+extern const struct file_operations coda_file_operations;
+extern const struct file_operations coda_ioctl_operations;
+
+/* operations shared over more than one file */
+int coda_open(struct inode *i, struct file *f);
+int coda_release(struct inode *i, struct file *f);
+int coda_permission(struct inode *inode, int mask);
+int coda_revalidate_inode(struct inode *);
+int coda_getattr(const struct path *, struct kstat *, u32, unsigned int);
+int coda_setattr(struct dentry *, struct iattr *);
+
+/* this file:  heloers */
+char *coda_f2s(struct CodaFid *f);
+int coda_iscontrol(const char *name, size_t length);
+
+void coda_vattr_to_iattr(struct inode *, struct coda_vattr *);
+void coda_iattr_to_vattr(struct iattr *, struct coda_vattr *);
+unsigned short coda_flags_to_cflags(unsigned short);
+
+/* sysctl.h */
+void coda_sysctl_init(void);
+void coda_sysctl_clean(void);
+
+#define CODA_ALLOC(ptr, cast, size) do { \
+    if (size < PAGE_SIZE) \
+        ptr = kzalloc((unsigned long) size, GFP_KERNEL); \
+    else \
+        ptr = (cast)vzalloc((unsigned long) size); \
+    if (!ptr) \
+	pr_warn("kernel malloc returns 0 at %s:%d\n", __FILE__, __LINE__); \
+} while (0)
+
+
+#define CODA_FREE(ptr, size) kvfree((ptr))
+
+/* inode to cnode access functions */
+
+static inline struct coda_inode_info *ITOC(struct inode *inode)
+{
+	return container_of(inode, struct coda_inode_info, vfs_inode);
+}
+
+static __inline__ struct CodaFid *coda_i2f(struct inode *inode)
+{
+	return &(ITOC(inode)->c_fid);
+}
+
+static __inline__ char *coda_i2s(struct inode *inode)
+{
+	return coda_f2s(&(ITOC(inode)->c_fid));
+}
+
+/* this will not zap the inode away */
+static __inline__ void coda_flag_inode(struct inode *inode, int flag)
+{
+	struct coda_inode_info *cii = ITOC(inode);
+
+	spin_lock(&cii->c_lock);
+	cii->c_flags |= flag;
+	spin_unlock(&cii->c_lock);
+}		
+
+#endif
diff --git a/fs/coda/dir.c b/fs/coda/dir.c
new file mode 100644
index 0000000..00876dd
--- /dev/null
+++ b/fs/coda/dir.c
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Directory operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/namei.h>
+#include <linux/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+#include "coda_linux.h"
+#include "coda_cache.h"
+
+#include "coda_int.h"
+
+/* same as fs/bad_inode.c */
+static int coda_return_EIO(void)
+{
+	return -EIO;
+}
+#define CODA_EIO_ERROR ((void *) (coda_return_EIO))
+
+/* inode operations for directories */
+/* access routines: lookup, readlink, permission */
+static struct dentry *coda_lookup(struct inode *dir, struct dentry *entry, unsigned int flags)
+{
+	struct super_block *sb = dir->i_sb;
+	const char *name = entry->d_name.name;
+	size_t length = entry->d_name.len;
+	struct inode *inode;
+	int type = 0;
+
+	if (length > CODA_MAXNAMLEN) {
+		pr_err("name too long: lookup, %s (%*s)\n",
+		       coda_i2s(dir), (int)length, name);
+		return ERR_PTR(-ENAMETOOLONG);
+	}
+
+	/* control object, create inode on the fly */
+	if (is_root_inode(dir) && coda_iscontrol(name, length)) {
+		inode = coda_cnode_makectl(sb);
+		type = CODA_NOCACHE;
+	} else {
+		struct CodaFid fid = { { 0, } };
+		int error = venus_lookup(sb, coda_i2f(dir), name, length,
+				     &type, &fid);
+		inode = !error ? coda_cnode_make(&fid, sb) : ERR_PTR(error);
+	}
+
+	if (!IS_ERR(inode) && (type & CODA_NOCACHE))
+		coda_flag_inode(inode, C_VATTR | C_PURGE);
+
+	if (inode == ERR_PTR(-ENOENT))
+		inode = NULL;
+
+	return d_splice_alias(inode, entry);
+}
+
+
+int coda_permission(struct inode *inode, int mask)
+{
+	int error;
+
+	if (mask & MAY_NOT_BLOCK)
+		return -ECHILD;
+
+	mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
+ 
+	if (!mask)
+		return 0;
+
+	if ((mask & MAY_EXEC) && !execute_ok(inode))
+		return -EACCES;
+
+	if (coda_cache_check(inode, mask))
+		return 0;
+
+	error = venus_access(inode->i_sb, coda_i2f(inode), mask);
+    
+	if (!error)
+		coda_cache_enter(inode, mask);
+
+	return error;
+}
+
+
+static inline void coda_dir_update_mtime(struct inode *dir)
+{
+#ifdef REQUERY_VENUS_FOR_MTIME
+	/* invalidate the directory cnode's attributes so we refetch the
+	 * attributes from venus next time the inode is referenced */
+	coda_flag_inode(dir, C_VATTR);
+#else
+	/* optimistically we can also act as if our nose bleeds. The
+	 * granularity of the mtime is coarse anyways so we might actually be
+	 * right most of the time. Note: we only do this for directories. */
+	dir->i_mtime = dir->i_ctime = current_time(dir);
+#endif
+}
+
+/* we have to wrap inc_nlink/drop_nlink because sometimes userspace uses a
+ * trick to fool GNU find's optimizations. If we can't be sure of the link
+ * (because of volume mount points) we set i_nlink to 1 which forces find
+ * to consider every child as a possible directory. We should also never
+ * see an increment or decrement for deleted directories where i_nlink == 0 */
+static inline void coda_dir_inc_nlink(struct inode *dir)
+{
+	if (dir->i_nlink >= 2)
+		inc_nlink(dir);
+}
+
+static inline void coda_dir_drop_nlink(struct inode *dir)
+{
+	if (dir->i_nlink > 2)
+		drop_nlink(dir);
+}
+
+/* creation routines: create, mknod, mkdir, link, symlink */
+static int coda_create(struct inode *dir, struct dentry *de, umode_t mode, bool excl)
+{
+	int error;
+	const char *name=de->d_name.name;
+	int length=de->d_name.len;
+	struct inode *inode;
+	struct CodaFid newfid;
+	struct coda_vattr attrs;
+
+	if (is_root_inode(dir) && coda_iscontrol(name, length))
+		return -EPERM;
+
+	error = venus_create(dir->i_sb, coda_i2f(dir), name, length, 
+				0, mode, &newfid, &attrs);
+	if (error)
+		goto err_out;
+
+	inode = coda_iget(dir->i_sb, &newfid, &attrs);
+	if (IS_ERR(inode)) {
+		error = PTR_ERR(inode);
+		goto err_out;
+	}
+
+	/* invalidate the directory cnode's attributes */
+	coda_dir_update_mtime(dir);
+	d_instantiate(de, inode);
+	return 0;
+err_out:
+	d_drop(de);
+	return error;
+}
+
+static int coda_mkdir(struct inode *dir, struct dentry *de, umode_t mode)
+{
+	struct inode *inode;
+	struct coda_vattr attrs;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	int error;
+	struct CodaFid newfid;
+
+	if (is_root_inode(dir) && coda_iscontrol(name, len))
+		return -EPERM;
+
+	attrs.va_mode = mode;
+	error = venus_mkdir(dir->i_sb, coda_i2f(dir), 
+			       name, len, &newfid, &attrs);
+	if (error)
+		goto err_out;
+         
+	inode = coda_iget(dir->i_sb, &newfid, &attrs);
+	if (IS_ERR(inode)) {
+		error = PTR_ERR(inode);
+		goto err_out;
+	}
+
+	/* invalidate the directory cnode's attributes */
+	coda_dir_inc_nlink(dir);
+	coda_dir_update_mtime(dir);
+	d_instantiate(de, inode);
+	return 0;
+err_out:
+	d_drop(de);
+	return error;
+}
+
+/* try to make de an entry in dir_inodde linked to source_de */ 
+static int coda_link(struct dentry *source_de, struct inode *dir_inode, 
+	  struct dentry *de)
+{
+	struct inode *inode = d_inode(source_de);
+        const char * name = de->d_name.name;
+	int len = de->d_name.len;
+	int error;
+
+	if (is_root_inode(dir_inode) && coda_iscontrol(name, len))
+		return -EPERM;
+
+	error = venus_link(dir_inode->i_sb, coda_i2f(inode),
+			   coda_i2f(dir_inode), (const char *)name, len);
+	if (error) {
+		d_drop(de);
+		return error;
+	}
+
+	coda_dir_update_mtime(dir_inode);
+	ihold(inode);
+	d_instantiate(de, inode);
+	inc_nlink(inode);
+	return 0;
+}
+
+
+static int coda_symlink(struct inode *dir_inode, struct dentry *de,
+			const char *symname)
+{
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	int symlen;
+	int error;
+
+	if (is_root_inode(dir_inode) && coda_iscontrol(name, len))
+		return -EPERM;
+
+	symlen = strlen(symname);
+	if (symlen > CODA_MAXPATHLEN)
+		return -ENAMETOOLONG;
+
+	/*
+	 * This entry is now negative. Since we do not create
+	 * an inode for the entry we have to drop it.
+	 */
+	d_drop(de);
+	error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len,
+			      symname, symlen);
+
+	/* mtime is no good anymore */
+	if (!error)
+		coda_dir_update_mtime(dir_inode);
+
+	return error;
+}
+
+/* destruction routines: unlink, rmdir */
+static int coda_unlink(struct inode *dir, struct dentry *de)
+{
+        int error;
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+
+	error = venus_remove(dir->i_sb, coda_i2f(dir), name, len);
+	if (error)
+		return error;
+
+	coda_dir_update_mtime(dir);
+	drop_nlink(d_inode(de));
+	return 0;
+}
+
+static int coda_rmdir(struct inode *dir, struct dentry *de)
+{
+	const char *name = de->d_name.name;
+	int len = de->d_name.len;
+	int error;
+
+	error = venus_rmdir(dir->i_sb, coda_i2f(dir), name, len);
+	if (!error) {
+		/* VFS may delete the child */
+		if (d_really_is_positive(de))
+			clear_nlink(d_inode(de));
+
+		/* fix the link count of the parent */
+		coda_dir_drop_nlink(dir);
+		coda_dir_update_mtime(dir);
+	}
+	return error;
+}
+
+/* rename */
+static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
+		       struct inode *new_dir, struct dentry *new_dentry,
+		       unsigned int flags)
+{
+	const char *old_name = old_dentry->d_name.name;
+	const char *new_name = new_dentry->d_name.name;
+	int old_length = old_dentry->d_name.len;
+	int new_length = new_dentry->d_name.len;
+	int error;
+
+	if (flags)
+		return -EINVAL;
+
+	error = venus_rename(old_dir->i_sb, coda_i2f(old_dir),
+			     coda_i2f(new_dir), old_length, new_length,
+			     (const char *) old_name, (const char *)new_name);
+	if (!error) {
+		if (d_really_is_positive(new_dentry)) {
+			if (d_is_dir(new_dentry)) {
+				coda_dir_drop_nlink(old_dir);
+				coda_dir_inc_nlink(new_dir);
+			}
+			coda_dir_update_mtime(old_dir);
+			coda_dir_update_mtime(new_dir);
+			coda_flag_inode(d_inode(new_dentry), C_VATTR);
+		} else {
+			coda_flag_inode(old_dir, C_VATTR);
+			coda_flag_inode(new_dir, C_VATTR);
+		}
+	}
+	return error;
+}
+
+static inline unsigned int CDT2DT(unsigned char cdt)
+{
+	unsigned int dt;
+
+	switch(cdt) {
+	case CDT_UNKNOWN: dt = DT_UNKNOWN; break;
+	case CDT_FIFO:	  dt = DT_FIFO;    break;
+	case CDT_CHR:	  dt = DT_CHR;     break;
+	case CDT_DIR:	  dt = DT_DIR;     break;
+	case CDT_BLK:	  dt = DT_BLK;     break;
+	case CDT_REG:	  dt = DT_REG;     break;
+	case CDT_LNK:	  dt = DT_LNK;     break;
+	case CDT_SOCK:	  dt = DT_SOCK;    break;
+	case CDT_WHT:	  dt = DT_WHT;     break;
+	default:	  dt = DT_UNKNOWN; break;
+	}
+	return dt;
+}
+
+/* support routines */
+static int coda_venus_readdir(struct file *coda_file, struct dir_context *ctx)
+{
+	struct coda_file_info *cfi;
+	struct coda_inode_info *cii;
+	struct file *host_file;
+	struct venus_dirent *vdir;
+	unsigned long vdir_size = offsetof(struct venus_dirent, d_name);
+	unsigned int type;
+	struct qstr name;
+	ino_t ino;
+	int ret;
+
+	cfi = CODA_FTOC(coda_file);
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+	host_file = cfi->cfi_container;
+
+	cii = ITOC(file_inode(coda_file));
+
+	vdir = kmalloc(sizeof(*vdir), GFP_KERNEL);
+	if (!vdir) return -ENOMEM;
+
+	if (!dir_emit_dots(coda_file, ctx))
+		goto out;
+
+	while (1) {
+		loff_t pos = ctx->pos - 2;
+
+		/* read entries from the directory file */
+		ret = kernel_read(host_file, vdir, sizeof(*vdir), &pos);
+		if (ret < 0) {
+			pr_err("%s: read dir %s failed %d\n",
+			       __func__, coda_f2s(&cii->c_fid), ret);
+			break;
+		}
+		if (ret == 0) break; /* end of directory file reached */
+
+		/* catch truncated reads */
+		if (ret < vdir_size || ret < vdir_size + vdir->d_namlen) {
+			pr_err("%s: short read on %s\n",
+			       __func__, coda_f2s(&cii->c_fid));
+			ret = -EBADF;
+			break;
+		}
+		/* validate whether the directory file actually makes sense */
+		if (vdir->d_reclen < vdir_size + vdir->d_namlen) {
+			pr_err("%s: invalid dir %s\n",
+			       __func__, coda_f2s(&cii->c_fid));
+			ret = -EBADF;
+			break;
+		}
+
+		name.len = vdir->d_namlen;
+		name.name = vdir->d_name;
+
+		/* Make sure we skip '.' and '..', we already got those */
+		if (name.name[0] == '.' && (name.len == 1 ||
+		    (name.name[1] == '.' && name.len == 2)))
+			vdir->d_fileno = name.len = 0;
+
+		/* skip null entries */
+		if (vdir->d_fileno && name.len) {
+			ino = vdir->d_fileno;
+			type = CDT2DT(vdir->d_type);
+			if (!dir_emit(ctx, name.name, name.len, ino, type))
+				break;
+		}
+		/* we'll always have progress because d_reclen is unsigned and
+		 * we've already established it is non-zero. */
+		ctx->pos += vdir->d_reclen;
+	}
+out:
+	kfree(vdir);
+	return 0;
+}
+
+/* file operations for directories */
+static int coda_readdir(struct file *coda_file, struct dir_context *ctx)
+{
+	struct coda_file_info *cfi;
+	struct file *host_file;
+	int ret;
+
+	cfi = CODA_FTOC(coda_file);
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+	host_file = cfi->cfi_container;
+
+	if (host_file->f_op->iterate || host_file->f_op->iterate_shared) {
+		struct inode *host_inode = file_inode(host_file);
+		ret = -ENOENT;
+		if (!IS_DEADDIR(host_inode)) {
+			if (host_file->f_op->iterate_shared) {
+				inode_lock_shared(host_inode);
+				ret = host_file->f_op->iterate_shared(host_file, ctx);
+				file_accessed(host_file);
+				inode_unlock_shared(host_inode);
+			} else {
+				inode_lock(host_inode);
+				ret = host_file->f_op->iterate(host_file, ctx);
+				file_accessed(host_file);
+				inode_unlock(host_inode);
+			}
+		}
+		return ret;
+	}
+	/* Venus: we must read Venus dirents from a file */
+	return coda_venus_readdir(coda_file, ctx);
+}
+
+/* called when a cache lookup succeeds */
+static int coda_dentry_revalidate(struct dentry *de, unsigned int flags)
+{
+	struct inode *inode;
+	struct coda_inode_info *cii;
+
+	if (flags & LOOKUP_RCU)
+		return -ECHILD;
+
+	inode = d_inode(de);
+	if (!inode || is_root_inode(inode))
+		goto out;
+	if (is_bad_inode(inode))
+		goto bad;
+
+	cii = ITOC(d_inode(de));
+	if (!(cii->c_flags & (C_PURGE | C_FLUSH)))
+		goto out;
+
+	shrink_dcache_parent(de);
+
+	/* propagate for a flush */
+	if (cii->c_flags & C_FLUSH) 
+		coda_flag_inode_children(inode, C_FLUSH);
+
+	if (d_count(de) > 1)
+		/* pretend it's valid, but don't change the flags */
+		goto out;
+
+	/* clear the flags. */
+	spin_lock(&cii->c_lock);
+	cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
+	spin_unlock(&cii->c_lock);
+bad:
+	return 0;
+out:
+	return 1;
+}
+
+/*
+ * This is the callback from dput() when d_count is going to 0.
+ * We use this to unhash dentries with bad inodes.
+ */
+static int coda_dentry_delete(const struct dentry * dentry)
+{
+	int flags;
+
+	if (d_really_is_negative(dentry)) 
+		return 0;
+
+	flags = (ITOC(d_inode(dentry))->c_flags) & C_PURGE;
+	if (is_bad_inode(d_inode(dentry)) || flags) {
+		return 1;
+	}
+	return 0;
+}
+
+
+
+/*
+ * This is called when we want to check if the inode has
+ * changed on the server.  Coda makes this easy since the
+ * cache manager Venus issues a downcall to the kernel when this 
+ * happens 
+ */
+int coda_revalidate_inode(struct inode *inode)
+{
+	struct coda_vattr attr;
+	int error;
+	int old_mode;
+	ino_t old_ino;
+	struct coda_inode_info *cii = ITOC(inode);
+
+	if (!cii->c_flags)
+		return 0;
+
+	if (cii->c_flags & (C_VATTR | C_PURGE | C_FLUSH)) {
+		error = venus_getattr(inode->i_sb, &(cii->c_fid), &attr);
+		if (error)
+			return -EIO;
+
+		/* this inode may be lost if:
+		   - it's ino changed 
+		   - type changes must be permitted for repair and
+		   missing mount points.
+		*/
+		old_mode = inode->i_mode;
+		old_ino = inode->i_ino;
+		coda_vattr_to_iattr(inode, &attr);
+
+		if ((old_mode & S_IFMT) != (inode->i_mode & S_IFMT)) {
+			pr_warn("inode %ld, fid %s changed type!\n",
+				inode->i_ino, coda_f2s(&(cii->c_fid)));
+		}
+
+		/* the following can happen when a local fid is replaced 
+		   with a global one, here we lose and declare the inode bad */
+		if (inode->i_ino != old_ino)
+			return -EIO;
+		
+		coda_flag_inode_children(inode, C_FLUSH);
+
+		spin_lock(&cii->c_lock);
+		cii->c_flags &= ~(C_VATTR | C_PURGE | C_FLUSH);
+		spin_unlock(&cii->c_lock);
+	}
+	return 0;
+}
+
+const struct dentry_operations coda_dentry_operations = {
+	.d_revalidate	= coda_dentry_revalidate,
+	.d_delete	= coda_dentry_delete,
+};
+
+const struct inode_operations coda_dir_inode_operations = {
+	.create		= coda_create,
+	.lookup		= coda_lookup,
+	.link		= coda_link,
+	.unlink		= coda_unlink,
+	.symlink	= coda_symlink,
+	.mkdir		= coda_mkdir,
+	.rmdir		= coda_rmdir,
+	.mknod		= CODA_EIO_ERROR,
+	.rename		= coda_rename,
+	.permission	= coda_permission,
+	.getattr	= coda_getattr,
+	.setattr	= coda_setattr,
+};
+
+const struct file_operations coda_dir_operations = {
+	.llseek		= generic_file_llseek,
+	.read		= generic_read_dir,
+	.iterate	= coda_readdir,
+	.open		= coda_open,
+	.release	= coda_release,
+	.fsync		= coda_fsync,
+};
diff --git a/fs/coda/file.c b/fs/coda/file.c
new file mode 100644
index 0000000..1cbc1f2
--- /dev/null
+++ b/fs/coda/file.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * File operations for Coda.
+ * Original version: (C) 1996 Peter Braam 
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/cred.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+
+#include "coda_linux.h"
+#include "coda_int.h"
+
+static ssize_t
+coda_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+	struct file *coda_file = iocb->ki_filp;
+	struct coda_file_info *cfi = CODA_FTOC(coda_file);
+
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+
+	return vfs_iter_read(cfi->cfi_container, to, &iocb->ki_pos, 0);
+}
+
+static ssize_t
+coda_file_write_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+	struct file *coda_file = iocb->ki_filp;
+	struct inode *coda_inode = file_inode(coda_file);
+	struct coda_file_info *cfi = CODA_FTOC(coda_file);
+	struct file *host_file;
+	ssize_t ret;
+
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+
+	host_file = cfi->cfi_container;
+	file_start_write(host_file);
+	inode_lock(coda_inode);
+	ret = vfs_iter_write(cfi->cfi_container, to, &iocb->ki_pos, 0);
+	coda_inode->i_size = file_inode(host_file)->i_size;
+	coda_inode->i_blocks = (coda_inode->i_size + 511) >> 9;
+	coda_inode->i_mtime = coda_inode->i_ctime = current_time(coda_inode);
+	inode_unlock(coda_inode);
+	file_end_write(host_file);
+	return ret;
+}
+
+static int
+coda_file_mmap(struct file *coda_file, struct vm_area_struct *vma)
+{
+	struct coda_file_info *cfi;
+	struct coda_inode_info *cii;
+	struct file *host_file;
+	struct inode *coda_inode, *host_inode;
+
+	cfi = CODA_FTOC(coda_file);
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+	host_file = cfi->cfi_container;
+
+	if (!host_file->f_op->mmap)
+		return -ENODEV;
+
+	coda_inode = file_inode(coda_file);
+	host_inode = file_inode(host_file);
+
+	cii = ITOC(coda_inode);
+	spin_lock(&cii->c_lock);
+	coda_file->f_mapping = host_file->f_mapping;
+	if (coda_inode->i_mapping == &coda_inode->i_data)
+		coda_inode->i_mapping = host_inode->i_mapping;
+
+	/* only allow additional mmaps as long as userspace isn't changing
+	 * the container file on us! */
+	else if (coda_inode->i_mapping != host_inode->i_mapping) {
+		spin_unlock(&cii->c_lock);
+		return -EBUSY;
+	}
+
+	/* keep track of how often the coda_inode/host_file has been mmapped */
+	cii->c_mapcount++;
+	cfi->cfi_mapcount++;
+	spin_unlock(&cii->c_lock);
+
+	return call_mmap(host_file, vma);
+}
+
+int coda_open(struct inode *coda_inode, struct file *coda_file)
+{
+	struct file *host_file = NULL;
+	int error;
+	unsigned short flags = coda_file->f_flags & (~O_EXCL);
+	unsigned short coda_flags = coda_flags_to_cflags(flags);
+	struct coda_file_info *cfi;
+
+	cfi = kmalloc(sizeof(struct coda_file_info), GFP_KERNEL);
+	if (!cfi)
+		return -ENOMEM;
+
+	error = venus_open(coda_inode->i_sb, coda_i2f(coda_inode), coda_flags,
+			   &host_file);
+	if (!host_file)
+		error = -EIO;
+
+	if (error) {
+		kfree(cfi);
+		return error;
+	}
+
+	host_file->f_flags |= coda_file->f_flags & (O_APPEND | O_SYNC);
+
+	cfi->cfi_magic = CODA_MAGIC;
+	cfi->cfi_mapcount = 0;
+	cfi->cfi_container = host_file;
+
+	BUG_ON(coda_file->private_data != NULL);
+	coda_file->private_data = cfi;
+	return 0;
+}
+
+int coda_release(struct inode *coda_inode, struct file *coda_file)
+{
+	unsigned short flags = (coda_file->f_flags) & (~O_EXCL);
+	unsigned short coda_flags = coda_flags_to_cflags(flags);
+	struct coda_file_info *cfi;
+	struct coda_inode_info *cii;
+	struct inode *host_inode;
+	int err;
+
+	cfi = CODA_FTOC(coda_file);
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+
+	err = venus_close(coda_inode->i_sb, coda_i2f(coda_inode),
+			  coda_flags, coda_file->f_cred->fsuid);
+
+	host_inode = file_inode(cfi->cfi_container);
+	cii = ITOC(coda_inode);
+
+	/* did we mmap this file? */
+	spin_lock(&cii->c_lock);
+	if (coda_inode->i_mapping == &host_inode->i_data) {
+		cii->c_mapcount -= cfi->cfi_mapcount;
+		if (!cii->c_mapcount)
+			coda_inode->i_mapping = &coda_inode->i_data;
+	}
+	spin_unlock(&cii->c_lock);
+
+	fput(cfi->cfi_container);
+	kfree(coda_file->private_data);
+	coda_file->private_data = NULL;
+
+	/* VFS fput ignores the return value from file_operations->release, so
+	 * there is no use returning an error here */
+	return 0;
+}
+
+int coda_fsync(struct file *coda_file, loff_t start, loff_t end, int datasync)
+{
+	struct file *host_file;
+	struct inode *coda_inode = file_inode(coda_file);
+	struct coda_file_info *cfi;
+	int err;
+
+	if (!(S_ISREG(coda_inode->i_mode) || S_ISDIR(coda_inode->i_mode) ||
+	      S_ISLNK(coda_inode->i_mode)))
+		return -EINVAL;
+
+	err = filemap_write_and_wait_range(coda_inode->i_mapping, start, end);
+	if (err)
+		return err;
+	inode_lock(coda_inode);
+
+	cfi = CODA_FTOC(coda_file);
+	BUG_ON(!cfi || cfi->cfi_magic != CODA_MAGIC);
+	host_file = cfi->cfi_container;
+
+	err = vfs_fsync(host_file, datasync);
+	if (!err && !datasync)
+		err = venus_fsync(coda_inode->i_sb, coda_i2f(coda_inode));
+	inode_unlock(coda_inode);
+
+	return err;
+}
+
+const struct file_operations coda_file_operations = {
+	.llseek		= generic_file_llseek,
+	.read_iter	= coda_file_read_iter,
+	.write_iter	= coda_file_write_iter,
+	.mmap		= coda_file_mmap,
+	.open		= coda_open,
+	.release	= coda_release,
+	.fsync		= coda_fsync,
+	.splice_read	= generic_file_splice_read,
+};
+
diff --git a/fs/coda/inode.c b/fs/coda/inode.c
new file mode 100644
index 0000000..97424cf
--- /dev/null
+++ b/fs/coda/inode.c
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Super block/filesystem wide operations
+ *
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk> and 
+ * Michael Callahan <callahan@maths.ox.ac.uk> 
+ * 
+ * Rewritten for Linux 2.1.  Peter Braam <braam@cs.cmu.edu>
+ * Copyright (C) Carnegie Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/unistd.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/file.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/pid_namespace.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/vmalloc.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+#include "coda_linux.h"
+#include "coda_cache.h"
+
+#include "coda_int.h"
+
+/* VFS super_block ops */
+static void coda_evict_inode(struct inode *);
+static void coda_put_super(struct super_block *);
+static int coda_statfs(struct dentry *dentry, struct kstatfs *buf);
+
+static struct kmem_cache * coda_inode_cachep;
+
+static struct inode *coda_alloc_inode(struct super_block *sb)
+{
+	struct coda_inode_info *ei;
+	ei = kmem_cache_alloc(coda_inode_cachep, GFP_KERNEL);
+	if (!ei)
+		return NULL;
+	memset(&ei->c_fid, 0, sizeof(struct CodaFid));
+	ei->c_flags = 0;
+	ei->c_uid = GLOBAL_ROOT_UID;
+	ei->c_cached_perm = 0;
+	spin_lock_init(&ei->c_lock);
+	return &ei->vfs_inode;
+}
+
+static void coda_i_callback(struct rcu_head *head)
+{
+	struct inode *inode = container_of(head, struct inode, i_rcu);
+	kmem_cache_free(coda_inode_cachep, ITOC(inode));
+}
+
+static void coda_destroy_inode(struct inode *inode)
+{
+	call_rcu(&inode->i_rcu, coda_i_callback);
+}
+
+static void init_once(void *foo)
+{
+	struct coda_inode_info *ei = (struct coda_inode_info *) foo;
+
+	inode_init_once(&ei->vfs_inode);
+}
+
+int __init coda_init_inodecache(void)
+{
+	coda_inode_cachep = kmem_cache_create("coda_inode_cache",
+				sizeof(struct coda_inode_info), 0,
+				SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|
+				SLAB_ACCOUNT, init_once);
+	if (coda_inode_cachep == NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+void coda_destroy_inodecache(void)
+{
+	/*
+	 * Make sure all delayed rcu free inodes are flushed before we
+	 * destroy cache.
+	 */
+	rcu_barrier();
+	kmem_cache_destroy(coda_inode_cachep);
+}
+
+static int coda_remount(struct super_block *sb, int *flags, char *data)
+{
+	sync_filesystem(sb);
+	*flags |= SB_NOATIME;
+	return 0;
+}
+
+/* exported operations */
+static const struct super_operations coda_super_operations =
+{
+	.alloc_inode	= coda_alloc_inode,
+	.destroy_inode	= coda_destroy_inode,
+	.evict_inode	= coda_evict_inode,
+	.put_super	= coda_put_super,
+	.statfs		= coda_statfs,
+	.remount_fs	= coda_remount,
+};
+
+static int get_device_index(struct coda_mount_data *data)
+{
+	struct fd f;
+	struct inode *inode;
+	int idx;
+
+	if (data == NULL) {
+		pr_warn("%s: Bad mount data\n", __func__);
+		return -1;
+	}
+
+	if (data->version != CODA_MOUNT_VERSION) {
+		pr_warn("%s: Bad mount version\n", __func__);
+		return -1;
+	}
+
+	f = fdget(data->fd);
+	if (!f.file)
+		goto Ebadf;
+	inode = file_inode(f.file);
+	if (!S_ISCHR(inode->i_mode) || imajor(inode) != CODA_PSDEV_MAJOR) {
+		fdput(f);
+		goto Ebadf;
+	}
+
+	idx = iminor(inode);
+	fdput(f);
+
+	if (idx < 0 || idx >= MAX_CODADEVS) {
+		pr_warn("%s: Bad minor number\n", __func__);
+		return -1;
+	}
+
+	return idx;
+Ebadf:
+	pr_warn("%s: Bad file\n", __func__);
+	return -1;
+}
+
+static int coda_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct inode *root = NULL;
+	struct venus_comm *vc;
+	struct CodaFid fid;
+	int error;
+	int idx;
+
+	if (task_active_pid_ns(current) != &init_pid_ns)
+		return -EINVAL;
+
+	idx = get_device_index((struct coda_mount_data *) data);
+
+	/* Ignore errors in data, for backward compatibility */
+	if(idx == -1)
+		idx = 0;
+	
+	pr_info("%s: device index: %i\n", __func__,  idx);
+
+	vc = &coda_comms[idx];
+	mutex_lock(&vc->vc_mutex);
+
+	if (!vc->vc_inuse) {
+		pr_warn("%s: No pseudo device\n", __func__);
+		error = -EINVAL;
+		goto unlock_out;
+	}
+
+	if (vc->vc_sb) {
+		pr_warn("%s: Device already mounted\n", __func__);
+		error = -EBUSY;
+		goto unlock_out;
+	}
+
+	vc->vc_sb = sb;
+	mutex_unlock(&vc->vc_mutex);
+
+	sb->s_fs_info = vc;
+	sb->s_flags |= SB_NOATIME;
+	sb->s_blocksize = 4096;	/* XXXXX  what do we put here?? */
+	sb->s_blocksize_bits = 12;
+	sb->s_magic = CODA_SUPER_MAGIC;
+	sb->s_op = &coda_super_operations;
+	sb->s_d_op = &coda_dentry_operations;
+
+	error = super_setup_bdi(sb);
+	if (error)
+		goto error;
+
+	/* get root fid from Venus: this needs the root inode */
+	error = venus_rootfid(sb, &fid);
+	if ( error ) {
+		pr_warn("%s: coda_get_rootfid failed with %d\n",
+			__func__, error);
+		goto error;
+	}
+	pr_info("%s: rootfid is %s\n", __func__, coda_f2s(&fid));
+	
+	/* make root inode */
+        root = coda_cnode_make(&fid, sb);
+        if (IS_ERR(root)) {
+		error = PTR_ERR(root);
+		pr_warn("Failure of coda_cnode_make for root: error %d\n",
+			error);
+		goto error;
+	} 
+
+	pr_info("%s: rootinode is %ld dev %s\n",
+		__func__, root->i_ino, root->i_sb->s_id);
+	sb->s_root = d_make_root(root);
+	if (!sb->s_root) {
+		error = -EINVAL;
+		goto error;
+	}
+	return 0;
+
+error:
+	mutex_lock(&vc->vc_mutex);
+	vc->vc_sb = NULL;
+	sb->s_fs_info = NULL;
+unlock_out:
+	mutex_unlock(&vc->vc_mutex);
+	return error;
+}
+
+static void coda_put_super(struct super_block *sb)
+{
+	struct venus_comm *vcp = coda_vcp(sb);
+	mutex_lock(&vcp->vc_mutex);
+	vcp->vc_sb = NULL;
+	sb->s_fs_info = NULL;
+	mutex_unlock(&vcp->vc_mutex);
+
+	pr_info("Bye bye.\n");
+}
+
+static void coda_evict_inode(struct inode *inode)
+{
+	truncate_inode_pages_final(&inode->i_data);
+	clear_inode(inode);
+	coda_cache_clear_inode(inode);
+}
+
+int coda_getattr(const struct path *path, struct kstat *stat,
+		 u32 request_mask, unsigned int flags)
+{
+	int err = coda_revalidate_inode(d_inode(path->dentry));
+	if (!err)
+		generic_fillattr(d_inode(path->dentry), stat);
+	return err;
+}
+
+int coda_setattr(struct dentry *de, struct iattr *iattr)
+{
+	struct inode *inode = d_inode(de);
+	struct coda_vattr vattr;
+	int error;
+
+	memset(&vattr, 0, sizeof(vattr)); 
+
+	inode->i_ctime = current_time(inode);
+	coda_iattr_to_vattr(iattr, &vattr);
+	vattr.va_type = C_VNON; /* cannot set type */
+
+	/* Venus is responsible for truncating the container-file!!! */
+	error = venus_setattr(inode->i_sb, coda_i2f(inode), &vattr);
+
+	if (!error) {
+	        coda_vattr_to_iattr(inode, &vattr); 
+		coda_cache_clear_inode(inode);
+	}
+	return error;
+}
+
+const struct inode_operations coda_file_inode_operations = {
+	.permission	= coda_permission,
+	.getattr	= coda_getattr,
+	.setattr	= coda_setattr,
+};
+
+static int coda_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+	int error;
+	
+	error = venus_statfs(dentry, buf);
+
+	if (error) {
+		/* fake something like AFS does */
+		buf->f_blocks = 9000000;
+		buf->f_bfree  = 9000000;
+		buf->f_bavail = 9000000;
+		buf->f_files  = 9000000;
+		buf->f_ffree  = 9000000;
+	}
+
+	/* and fill in the rest */
+	buf->f_type = CODA_SUPER_MAGIC;
+	buf->f_bsize = 4096;
+	buf->f_namelen = CODA_MAXNAMLEN;
+
+	return 0; 
+}
+
+/* init_coda: used by filesystems.c to register coda */
+
+static struct dentry *coda_mount(struct file_system_type *fs_type,
+	int flags, const char *dev_name, void *data)
+{
+	return mount_nodev(fs_type, flags, data, coda_fill_super);
+}
+
+struct file_system_type coda_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "coda",
+	.mount		= coda_mount,
+	.kill_sb	= kill_anon_super,
+	.fs_flags	= FS_BINARY_MOUNTDATA,
+};
+MODULE_ALIAS_FS("coda");
+
diff --git a/fs/coda/pioctl.c b/fs/coda/pioctl.c
new file mode 100644
index 0000000..e0c17b7
--- /dev/null
+++ b/fs/coda/pioctl.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Pioctl operations for Coda.
+ * Original version: (C) 1996 Peter Braam
+ * Rewritten for Linux 2.1: (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon encourages users of this code to contribute improvements
+ * to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/namei.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+
+#include "coda_linux.h"
+
+/* pioctl ops */
+static int coda_ioctl_permission(struct inode *inode, int mask);
+static long coda_pioctl(struct file *filp, unsigned int cmd,
+			unsigned long user_data);
+
+/* exported from this file */
+const struct inode_operations coda_ioctl_inode_operations = {
+	.permission	= coda_ioctl_permission,
+	.setattr	= coda_setattr,
+};
+
+const struct file_operations coda_ioctl_operations = {
+	.unlocked_ioctl	= coda_pioctl,
+	.llseek		= noop_llseek,
+};
+
+/* the coda pioctl inode ops */
+static int coda_ioctl_permission(struct inode *inode, int mask)
+{
+	return (mask & MAY_EXEC) ? -EACCES : 0;
+}
+
+static long coda_pioctl(struct file *filp, unsigned int cmd,
+			unsigned long user_data)
+{
+	struct path path;
+	int error;
+	struct PioctlData data;
+	struct inode *inode = file_inode(filp);
+	struct inode *target_inode = NULL;
+	struct coda_inode_info *cnp;
+
+	/* get the Pioctl data arguments from user space */
+	if (copy_from_user(&data, (void __user *)user_data, sizeof(data)))
+		return -EINVAL;
+
+	/*
+	 * Look up the pathname. Note that the pathname is in
+	 * user memory, and namei takes care of this
+	 */
+	if (data.follow)
+		error = user_path(data.path, &path);
+	else
+		error = user_lpath(data.path, &path);
+
+	if (error)
+		return error;
+
+	target_inode = d_inode(path.dentry);
+
+	/* return if it is not a Coda inode */
+	if (target_inode->i_sb != inode->i_sb) {
+		error = -EINVAL;
+		goto out;
+	}
+
+	/* now proceed to make the upcall */
+	cnp = ITOC(target_inode);
+
+	error = venus_pioctl(inode->i_sb, &(cnp->c_fid), cmd, &data);
+out:
+	path_put(&path);
+	return error;
+}
diff --git a/fs/coda/psdev.c b/fs/coda/psdev.c
new file mode 100644
index 0000000..c5234c2
--- /dev/null
+++ b/fs/coda/psdev.c
@@ -0,0 +1,437 @@
+/*
+ *      	An implementation of a loadable kernel mode driver providing
+ *		multiple kernel/user space bidirectional communications links.
+ *
+ * 		Author: 	Alan Cox <alan@lxorguk.ukuu.org.uk>
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ * 
+ *              Adapted to become the Linux 2.0 Coda pseudo device
+ *              Peter  Braam  <braam@maths.ox.ac.uk> 
+ *              Michael Callahan <mjc@emmy.smith.edu>           
+ *
+ *              Changes for Linux 2.1
+ *              Copyright (c) 1997 Carnegie-Mellon University
+ */
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/time.h>
+#include <linux/sched/signal.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/vmalloc.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/pid_namespace.h>
+#include <asm/io.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+
+#include "coda_linux.h"
+
+#include "coda_int.h"
+
+/* statistics */
+int           coda_hard;         /* allows signals during upcalls */
+unsigned long coda_timeout = 30; /* .. secs, then signals will dequeue */
+
+
+struct venus_comm coda_comms[MAX_CODADEVS];
+static struct class *coda_psdev_class;
+
+/*
+ * Device operations
+ */
+
+static __poll_t coda_psdev_poll(struct file *file, poll_table * wait)
+{
+        struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+	__poll_t mask = EPOLLOUT | EPOLLWRNORM;
+
+	poll_wait(file, &vcp->vc_waitq, wait);
+	mutex_lock(&vcp->vc_mutex);
+	if (!list_empty(&vcp->vc_pending))
+                mask |= EPOLLIN | EPOLLRDNORM;
+	mutex_unlock(&vcp->vc_mutex);
+
+	return mask;
+}
+
+static long coda_psdev_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
+{
+	unsigned int data;
+
+	switch(cmd) {
+	case CIOC_KERNEL_VERSION:
+		data = CODA_KERNEL_VERSION;
+		return put_user(data, (int __user *) arg);
+	default:
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+/*
+ *	Receive a message written by Venus to the psdev
+ */
+ 
+static ssize_t coda_psdev_write(struct file *file, const char __user *buf, 
+				size_t nbytes, loff_t *off)
+{
+        struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+        struct upc_req *req = NULL;
+        struct upc_req *tmp;
+	struct list_head *lh;
+	struct coda_in_hdr hdr;
+	ssize_t retval = 0, count = 0;
+	int error;
+
+        /* Peek at the opcode, uniquefier */
+	if (copy_from_user(&hdr, buf, 2 * sizeof(u_long)))
+	        return -EFAULT;
+
+        if (DOWNCALL(hdr.opcode)) {
+		union outputArgs *dcbuf;
+		int size = sizeof(*dcbuf);
+
+		if  ( nbytes < sizeof(struct coda_out_hdr) ) {
+			pr_warn("coda_downcall opc %d uniq %d, not enough!\n",
+				hdr.opcode, hdr.unique);
+			count = nbytes;
+			goto out;
+		}
+		if ( nbytes > size ) {
+			pr_warn("downcall opc %d, uniq %d, too much!",
+				hdr.opcode, hdr.unique);
+		        nbytes = size;
+		}
+		CODA_ALLOC(dcbuf, union outputArgs *, nbytes);
+		if (copy_from_user(dcbuf, buf, nbytes)) {
+			CODA_FREE(dcbuf, nbytes);
+			retval = -EFAULT;
+			goto out;
+		}
+
+		/* what downcall errors does Venus handle ? */
+		error = coda_downcall(vcp, hdr.opcode, dcbuf);
+
+		CODA_FREE(dcbuf, nbytes);
+		if (error) {
+			pr_warn("%s: coda_downcall error: %d\n",
+				__func__, error);
+			retval = error;
+			goto out;
+		}
+		count = nbytes;
+		goto out;
+	}
+        
+	/* Look for the message on the processing queue. */
+	mutex_lock(&vcp->vc_mutex);
+	list_for_each(lh, &vcp->vc_processing) {
+		tmp = list_entry(lh, struct upc_req , uc_chain);
+		if (tmp->uc_unique == hdr.unique) {
+			req = tmp;
+			list_del(&req->uc_chain);
+			break;
+		}
+	}
+	mutex_unlock(&vcp->vc_mutex);
+
+	if (!req) {
+		pr_warn("%s: msg (%d, %d) not found\n",
+			__func__, hdr.opcode, hdr.unique);
+		retval = -ESRCH;
+		goto out;
+	}
+
+        /* move data into response buffer. */
+	if (req->uc_outSize < nbytes) {
+		pr_warn("%s: too much cnt: %d, cnt: %ld, opc: %d, uniq: %d.\n",
+			__func__, req->uc_outSize, (long)nbytes,
+			hdr.opcode, hdr.unique);
+		nbytes = req->uc_outSize; /* don't have more space! */
+	}
+        if (copy_from_user(req->uc_data, buf, nbytes)) {
+		req->uc_flags |= CODA_REQ_ABORT;
+		wake_up(&req->uc_sleep);
+		retval = -EFAULT;
+		goto out;
+	}
+
+	/* adjust outsize. is this useful ?? */
+	req->uc_outSize = nbytes;
+	req->uc_flags |= CODA_REQ_WRITE;
+	count = nbytes;
+
+	/* Convert filedescriptor into a file handle */
+	if (req->uc_opcode == CODA_OPEN_BY_FD) {
+		struct coda_open_by_fd_out *outp =
+			(struct coda_open_by_fd_out *)req->uc_data;
+		if (!outp->oh.result)
+			outp->fh = fget(outp->fd);
+	}
+
+        wake_up(&req->uc_sleep);
+out:
+        return(count ? count : retval);  
+}
+
+/*
+ *	Read a message from the kernel to Venus
+ */
+
+static ssize_t coda_psdev_read(struct file * file, char __user * buf, 
+			       size_t nbytes, loff_t *off)
+{
+	DECLARE_WAITQUEUE(wait, current);
+        struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+        struct upc_req *req;
+	ssize_t retval = 0, count = 0;
+
+	if (nbytes == 0)
+		return 0;
+
+	mutex_lock(&vcp->vc_mutex);
+
+	add_wait_queue(&vcp->vc_waitq, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	while (list_empty(&vcp->vc_pending)) {
+		if (file->f_flags & O_NONBLOCK) {
+			retval = -EAGAIN;
+			break;
+		}
+		if (signal_pending(current)) {
+			retval = -ERESTARTSYS;
+			break;
+		}
+		mutex_unlock(&vcp->vc_mutex);
+		schedule();
+		mutex_lock(&vcp->vc_mutex);
+	}
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&vcp->vc_waitq, &wait);
+
+	if (retval)
+		goto out;
+
+	req = list_entry(vcp->vc_pending.next, struct upc_req,uc_chain);
+	list_del(&req->uc_chain);
+
+	/* Move the input args into userspace */
+	count = req->uc_inSize;
+	if (nbytes < req->uc_inSize) {
+		pr_warn("%s: Venus read %ld bytes of %d in message\n",
+			__func__, (long)nbytes, req->uc_inSize);
+		count = nbytes;
+        }
+
+	if (copy_to_user(buf, req->uc_data, count))
+	        retval = -EFAULT;
+        
+	/* If request was not a signal, enqueue and don't free */
+	if (!(req->uc_flags & CODA_REQ_ASYNC)) {
+		req->uc_flags |= CODA_REQ_READ;
+		list_add_tail(&(req->uc_chain), &vcp->vc_processing);
+		goto out;
+	}
+
+	CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+	kfree(req);
+out:
+	mutex_unlock(&vcp->vc_mutex);
+	return (count ? count : retval);
+}
+
+static int coda_psdev_open(struct inode * inode, struct file * file)
+{
+	struct venus_comm *vcp;
+	int idx, err;
+
+	if (task_active_pid_ns(current) != &init_pid_ns)
+		return -EINVAL;
+
+	if (current_user_ns() != &init_user_ns)
+		return -EINVAL;
+
+	idx = iminor(inode);
+	if (idx < 0 || idx >= MAX_CODADEVS)
+		return -ENODEV;
+
+	err = -EBUSY;
+	vcp = &coda_comms[idx];
+	mutex_lock(&vcp->vc_mutex);
+
+	if (!vcp->vc_inuse) {
+		vcp->vc_inuse++;
+
+		INIT_LIST_HEAD(&vcp->vc_pending);
+		INIT_LIST_HEAD(&vcp->vc_processing);
+		init_waitqueue_head(&vcp->vc_waitq);
+		vcp->vc_sb = NULL;
+		vcp->vc_seq = 0;
+
+		file->private_data = vcp;
+		err = 0;
+	}
+
+	mutex_unlock(&vcp->vc_mutex);
+	return err;
+}
+
+
+static int coda_psdev_release(struct inode * inode, struct file * file)
+{
+	struct venus_comm *vcp = (struct venus_comm *) file->private_data;
+	struct upc_req *req, *tmp;
+
+	if (!vcp || !vcp->vc_inuse ) {
+		pr_warn("%s: Not open.\n", __func__);
+		return -1;
+	}
+
+	mutex_lock(&vcp->vc_mutex);
+
+	/* Wakeup clients so they can return. */
+	list_for_each_entry_safe(req, tmp, &vcp->vc_pending, uc_chain) {
+		list_del(&req->uc_chain);
+
+		/* Async requests need to be freed here */
+		if (req->uc_flags & CODA_REQ_ASYNC) {
+			CODA_FREE(req->uc_data, sizeof(struct coda_in_hdr));
+			kfree(req);
+			continue;
+		}
+		req->uc_flags |= CODA_REQ_ABORT;
+		wake_up(&req->uc_sleep);
+	}
+
+	list_for_each_entry_safe(req, tmp, &vcp->vc_processing, uc_chain) {
+		list_del(&req->uc_chain);
+
+		req->uc_flags |= CODA_REQ_ABORT;
+		wake_up(&req->uc_sleep);
+	}
+
+	file->private_data = NULL;
+	vcp->vc_inuse--;
+	mutex_unlock(&vcp->vc_mutex);
+	return 0;
+}
+
+
+static const struct file_operations coda_psdev_fops = {
+	.owner		= THIS_MODULE,
+	.read		= coda_psdev_read,
+	.write		= coda_psdev_write,
+	.poll		= coda_psdev_poll,
+	.unlocked_ioctl	= coda_psdev_ioctl,
+	.open		= coda_psdev_open,
+	.release	= coda_psdev_release,
+	.llseek		= noop_llseek,
+};
+
+static int init_coda_psdev(void)
+{
+	int i, err = 0;
+	if (register_chrdev(CODA_PSDEV_MAJOR, "coda", &coda_psdev_fops)) {
+		pr_err("%s: unable to get major %d\n",
+		       __func__, CODA_PSDEV_MAJOR);
+              return -EIO;
+	}
+	coda_psdev_class = class_create(THIS_MODULE, "coda");
+	if (IS_ERR(coda_psdev_class)) {
+		err = PTR_ERR(coda_psdev_class);
+		goto out_chrdev;
+	}		
+	for (i = 0; i < MAX_CODADEVS; i++) {
+		mutex_init(&(&coda_comms[i])->vc_mutex);
+		device_create(coda_psdev_class, NULL,
+			      MKDEV(CODA_PSDEV_MAJOR, i), NULL, "cfs%d", i);
+	}
+	coda_sysctl_init();
+	goto out;
+
+out_chrdev:
+	unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
+out:
+	return err;
+}
+
+MODULE_AUTHOR("Jan Harkes, Peter J. Braam");
+MODULE_DESCRIPTION("Coda Distributed File System VFS interface");
+MODULE_ALIAS_CHARDEV_MAJOR(CODA_PSDEV_MAJOR);
+MODULE_LICENSE("GPL");
+MODULE_VERSION("6.6");
+
+static int __init init_coda(void)
+{
+	int status;
+	int i;
+
+	status = coda_init_inodecache();
+	if (status)
+		goto out2;
+	status = init_coda_psdev();
+	if ( status ) {
+		pr_warn("Problem (%d) in init_coda_psdev\n", status);
+		goto out1;
+	}
+	
+	status = register_filesystem(&coda_fs_type);
+	if (status) {
+		pr_warn("failed to register filesystem!\n");
+		goto out;
+	}
+	return 0;
+out:
+	for (i = 0; i < MAX_CODADEVS; i++)
+		device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
+	class_destroy(coda_psdev_class);
+	unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
+	coda_sysctl_clean();
+out1:
+	coda_destroy_inodecache();
+out2:
+	return status;
+}
+
+static void __exit exit_coda(void)
+{
+        int err, i;
+
+	err = unregister_filesystem(&coda_fs_type);
+	if (err != 0)
+		pr_warn("failed to unregister filesystem\n");
+	for (i = 0; i < MAX_CODADEVS; i++)
+		device_destroy(coda_psdev_class, MKDEV(CODA_PSDEV_MAJOR, i));
+	class_destroy(coda_psdev_class);
+	unregister_chrdev(CODA_PSDEV_MAJOR, "coda");
+	coda_sysctl_clean();
+	coda_destroy_inodecache();
+}
+
+module_init(init_coda);
+module_exit(exit_coda);
+
diff --git a/fs/coda/symlink.c b/fs/coda/symlink.c
new file mode 100644
index 0000000..202297d
--- /dev/null
+++ b/fs/coda/symlink.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Symlink inode operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/pagemap.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+
+#include "coda_linux.h"
+
+static int coda_symlink_filler(struct file *file, struct page *page)
+{
+	struct inode *inode = page->mapping->host;
+	int error;
+	struct coda_inode_info *cii;
+	unsigned int len = PAGE_SIZE;
+	char *p = page_address(page);
+
+	cii = ITOC(inode);
+
+	error = venus_readlink(inode->i_sb, &cii->c_fid, p, &len);
+	if (error)
+		goto fail;
+	SetPageUptodate(page);
+	unlock_page(page);
+	return 0;
+
+fail:
+	SetPageError(page);
+	unlock_page(page);
+	return error;
+}
+
+const struct address_space_operations coda_symlink_aops = {
+	.readpage	= coda_symlink_filler,
+};
diff --git a/fs/coda/sysctl.c b/fs/coda/sysctl.c
new file mode 100644
index 0000000..0301d45
--- /dev/null
+++ b/fs/coda/sysctl.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sysctl operations for Coda filesystem
+ * Original version: (C) 1996 P. Braam and M. Callahan
+ * Rewritten for Linux 2.1. (C) 1997 Carnegie Mellon University
+ * 
+ * Carnegie Mellon encourages users to contribute improvements to
+ * the Coda project. Contact Peter Braam (coda@cs.cmu.edu).
+ */
+
+#include <linux/sysctl.h>
+
+#include "coda_int.h"
+
+#ifdef CONFIG_SYSCTL
+static struct ctl_table_header *fs_table_header;
+
+static struct ctl_table coda_table[] = {
+	{
+		.procname	= "timeout",
+		.data		= &coda_timeout,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
+	{
+		.procname	= "hard",
+		.data		= &coda_hard,
+		.maxlen		= sizeof(int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec
+	},
+	{
+		.procname	= "fake_statfs",
+		.data		= &coda_fake_statfs,
+		.maxlen		= sizeof(int),
+		.mode		= 0600,
+		.proc_handler	= proc_dointvec
+	},
+	{}
+};
+
+static struct ctl_table fs_table[] = {
+	{
+		.procname	= "coda",
+		.mode		= 0555,
+		.child		= coda_table
+	},
+	{}
+};
+
+void coda_sysctl_init(void)
+{
+	if ( !fs_table_header )
+		fs_table_header = register_sysctl_table(fs_table);
+}
+
+void coda_sysctl_clean(void)
+{
+	if ( fs_table_header ) {
+		unregister_sysctl_table(fs_table_header);
+		fs_table_header = NULL;
+	}
+}
+
+#else
+void coda_sysctl_init(void)
+{
+}
+
+void coda_sysctl_clean(void)
+{
+}
+#endif
diff --git a/fs/coda/upcall.c b/fs/coda/upcall.c
new file mode 100644
index 0000000..1175a17
--- /dev/null
+++ b/fs/coda/upcall.c
@@ -0,0 +1,882 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mostly platform independent upcall operations to Venus:
+ *  -- upcalls
+ *  -- upcall routines
+ *
+ * Linux 2.0 version
+ * Copyright (C) 1996 Peter J. Braam <braam@maths.ox.ac.uk>, 
+ * Michael Callahan <callahan@maths.ox.ac.uk> 
+ * 
+ * Redone for Linux 2.1
+ * Copyright (C) 1997 Carnegie Mellon University
+ *
+ * Carnegie Mellon University encourages users of this code to contribute
+ * improvements to the Coda project. Contact Peter Braam <coda@cs.cmu.edu>.
+ */
+
+#include <linux/signal.h>
+#include <linux/sched/signal.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/time.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/stat.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/vfs.h>
+
+#include <linux/coda.h>
+#include <linux/coda_psdev.h>
+#include "coda_linux.h"
+#include "coda_cache.h"
+
+#include "coda_int.h"
+
+static int coda_upcall(struct venus_comm *vc, int inSize, int *outSize,
+		       union inputArgs *buffer);
+
+static void *alloc_upcall(int opcode, int size)
+{
+	union inputArgs *inp;
+
+	CODA_ALLOC(inp, union inputArgs *, size);
+        if (!inp)
+		return ERR_PTR(-ENOMEM);
+
+        inp->ih.opcode = opcode;
+	inp->ih.pid = task_pid_nr_ns(current, &init_pid_ns);
+	inp->ih.pgid = task_pgrp_nr_ns(current, &init_pid_ns);
+	inp->ih.uid = from_kuid(&init_user_ns, current_fsuid());
+
+	return (void*)inp;
+}
+
+#define UPARG(op)\
+do {\
+	inp = (union inputArgs *)alloc_upcall(op, insize); \
+        if (IS_ERR(inp)) { return PTR_ERR(inp); }\
+        outp = (union outputArgs *)(inp); \
+        outsize = insize; \
+} while (0)
+
+#define INSIZE(tag) sizeof(struct coda_ ## tag ## _in)
+#define OUTSIZE(tag) sizeof(struct coda_ ## tag ## _out)
+#define SIZE(tag)  max_t(unsigned int, INSIZE(tag), OUTSIZE(tag))
+
+
+/* the upcalls */
+int venus_rootfid(struct super_block *sb, struct CodaFid *fidp)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+
+        insize = SIZE(root);
+        UPARG(CODA_ROOT);
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error)
+		*fidp = outp->coda_root.VFid;
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_getattr(struct super_block *sb, struct CodaFid *fid, 
+		     struct coda_vattr *attr) 
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+
+        insize = SIZE(getattr); 
+	UPARG(CODA_GETATTR);
+        inp->coda_getattr.VFid = *fid;
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error)
+		*attr = outp->coda_getattr.attr;
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_setattr(struct super_block *sb, struct CodaFid *fid, 
+		  struct coda_vattr *vattr)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+	
+	insize = SIZE(setattr);
+	UPARG(CODA_SETATTR);
+
+        inp->coda_setattr.VFid = *fid;
+	inp->coda_setattr.attr = *vattr;
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+        CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_lookup(struct super_block *sb, struct CodaFid *fid, 
+		    const char *name, int length, int * type, 
+		    struct CodaFid *resfid)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+	int offset;
+
+	offset = INSIZE(lookup);
+        insize = max_t(unsigned int, offset + length +1, OUTSIZE(lookup));
+	UPARG(CODA_LOOKUP);
+
+        inp->coda_lookup.VFid = *fid;
+	inp->coda_lookup.name = offset;
+	inp->coda_lookup.flags = CLU_CASE_SENSITIVE;
+        /* send Venus a null terminated string */
+        memcpy((char *)(inp) + offset, name, length);
+        *((char *)inp + offset + length) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error) {
+		*resfid = outp->coda_lookup.VFid;
+		*type = outp->coda_lookup.vtype;
+	}
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_close(struct super_block *sb, struct CodaFid *fid, int flags,
+		kuid_t uid)
+{
+	union inputArgs *inp;
+	union outputArgs *outp;
+	int insize, outsize, error;
+	
+	insize = SIZE(release);
+	UPARG(CODA_CLOSE);
+	
+	inp->ih.uid = from_kuid(&init_user_ns, uid);
+        inp->coda_close.VFid = *fid;
+        inp->coda_close.flags = flags;
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_open(struct super_block *sb, struct CodaFid *fid,
+		  int flags, struct file **fh)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+       
+	insize = SIZE(open_by_fd);
+	UPARG(CODA_OPEN_BY_FD);
+
+	inp->coda_open_by_fd.VFid = *fid;
+	inp->coda_open_by_fd.flags = flags;
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error)
+		*fh = outp->coda_open_by_fd.fh;
+
+	CODA_FREE(inp, insize);
+	return error;
+}	
+
+int venus_mkdir(struct super_block *sb, struct CodaFid *dirfid, 
+		   const char *name, int length, 
+		   struct CodaFid *newfid, struct coda_vattr *attrs)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+	offset = INSIZE(mkdir);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(mkdir));
+	UPARG(CODA_MKDIR);
+
+        inp->coda_mkdir.VFid = *dirfid;
+        inp->coda_mkdir.attr = *attrs;
+	inp->coda_mkdir.name = offset;
+        /* Venus must get null terminated string */
+        memcpy((char *)(inp) + offset, name, length);
+        *((char *)inp + offset + length) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error) {
+		*attrs = outp->coda_mkdir.attr;
+		*newfid = outp->coda_mkdir.VFid;
+	}
+
+	CODA_FREE(inp, insize);
+	return error;        
+}
+
+
+int venus_rename(struct super_block *sb, struct CodaFid *old_fid, 
+		 struct CodaFid *new_fid, size_t old_length, 
+		 size_t new_length, const char *old_name, 
+		 const char *new_name)
+{
+	union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error; 
+	int offset, s;
+	
+	offset = INSIZE(rename);
+	insize = max_t(unsigned int, offset + new_length + old_length + 8,
+		     OUTSIZE(rename)); 
+ 	UPARG(CODA_RENAME);
+
+        inp->coda_rename.sourceFid = *old_fid;
+        inp->coda_rename.destFid =  *new_fid;
+        inp->coda_rename.srcname = offset;
+
+        /* Venus must receive an null terminated string */
+        s = ( old_length & ~0x3) +4; /* round up to word boundary */
+        memcpy((char *)(inp) + offset, old_name, old_length);
+        *((char *)inp + offset + old_length) = '\0';
+
+        /* another null terminated string for Venus */
+        offset += s;
+        inp->coda_rename.destname = offset;
+        s = ( new_length & ~0x3) +4; /* round up to word boundary */
+        memcpy((char *)(inp) + offset, new_name, new_length);
+        *((char *)inp + offset + new_length) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_create(struct super_block *sb, struct CodaFid *dirfid, 
+		 const char *name, int length, int excl, int mode,
+		 struct CodaFid *newfid, struct coda_vattr *attrs) 
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+        offset = INSIZE(create);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(create));
+	UPARG(CODA_CREATE);
+
+        inp->coda_create.VFid = *dirfid;
+        inp->coda_create.attr.va_mode = mode;
+	inp->coda_create.excl = excl;
+        inp->coda_create.mode = mode;
+        inp->coda_create.name = offset;
+
+        /* Venus must get null terminated string */
+        memcpy((char *)(inp) + offset, name, length);
+        *((char *)inp + offset + length) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error) {
+		*attrs = outp->coda_create.attr;
+		*newfid = outp->coda_create.VFid;
+	}
+
+	CODA_FREE(inp, insize);
+	return error;        
+}
+
+int venus_rmdir(struct super_block *sb, struct CodaFid *dirfid, 
+		    const char *name, int length)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+        offset = INSIZE(rmdir);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(rmdir));
+	UPARG(CODA_RMDIR);
+
+        inp->coda_rmdir.VFid = *dirfid;
+        inp->coda_rmdir.name = offset;
+        memcpy((char *)(inp) + offset, name, length);
+	*((char *)inp + offset + length) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_remove(struct super_block *sb, struct CodaFid *dirfid, 
+		    const char *name, int length)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int error=0, insize, outsize, offset;
+
+        offset = INSIZE(remove);
+	insize = max_t(unsigned int, offset + length + 1, OUTSIZE(remove));
+	UPARG(CODA_REMOVE);
+
+        inp->coda_remove.VFid = *dirfid;
+        inp->coda_remove.name = offset;
+        memcpy((char *)(inp) + offset, name, length);
+	*((char *)inp + offset + length) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_readlink(struct super_block *sb, struct CodaFid *fid, 
+		      char *buffer, int *length)
+{ 
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int retlen;
+        char *result;
+        
+	insize = max_t(unsigned int,
+		     INSIZE(readlink), OUTSIZE(readlink)+ *length);
+	UPARG(CODA_READLINK);
+
+        inp->coda_readlink.VFid = *fid;
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+	if (!error) {
+		retlen = outp->coda_readlink.count;
+		if (retlen >= *length)
+			retlen = *length - 1;
+		*length = retlen;
+		result =  (char *)outp + (long)outp->coda_readlink.data;
+		memcpy(buffer, result, retlen);
+		*(buffer + retlen) = '\0';
+	}
+
+        CODA_FREE(inp, insize);
+        return error;
+}
+
+
+
+int venus_link(struct super_block *sb, struct CodaFid *fid, 
+		  struct CodaFid *dirfid, const char *name, int len )
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset;
+
+	offset = INSIZE(link);
+	insize = max_t(unsigned int, offset  + len + 1, OUTSIZE(link));
+        UPARG(CODA_LINK);
+
+        inp->coda_link.sourceFid = *fid;
+        inp->coda_link.destFid = *dirfid;
+        inp->coda_link.tname = offset;
+
+        /* make sure strings are null terminated */
+        memcpy((char *)(inp) + offset, name, len);
+        *((char *)inp + offset + len) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_symlink(struct super_block *sb, struct CodaFid *fid,
+		     const char *name, int len,
+		     const char *symname, int symlen)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        int offset, s;
+
+        offset = INSIZE(symlink);
+	insize = max_t(unsigned int, offset + len + symlen + 8, OUTSIZE(symlink));
+	UPARG(CODA_SYMLINK);
+        
+        /*        inp->coda_symlink.attr = *tva; XXXXXX */ 
+        inp->coda_symlink.VFid = *fid;
+
+	/* Round up to word boundary and null terminate */
+        inp->coda_symlink.srcname = offset;
+        s = ( symlen  & ~0x3 ) + 4; 
+        memcpy((char *)(inp) + offset, symname, symlen);
+        *((char *)inp + offset + symlen) = '\0';
+        
+	/* Round up to word boundary and null terminate */
+        offset += s;
+        inp->coda_symlink.tname = offset;
+        s = (len & ~0x3) + 4;
+        memcpy((char *)(inp) + offset, name, len);
+        *((char *)inp + offset + len) = '\0';
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+        return error;
+}
+
+int venus_fsync(struct super_block *sb, struct CodaFid *fid)
+{
+        union inputArgs *inp;
+        union outputArgs *outp; 
+	int insize, outsize, error;
+	
+	insize=SIZE(fsync);
+	UPARG(CODA_FSYNC);
+
+	inp->coda_fsync.VFid = *fid;
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_access(struct super_block *sb, struct CodaFid *fid, int mask)
+{
+        union inputArgs *inp;
+        union outputArgs *outp; 
+	int insize, outsize, error;
+
+	insize = SIZE(access);
+	UPARG(CODA_ACCESS);
+
+        inp->coda_access.VFid = *fid;
+        inp->coda_access.flags = mask;
+
+	error = coda_upcall(coda_vcp(sb), insize, &outsize, inp);
+
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+
+int venus_pioctl(struct super_block *sb, struct CodaFid *fid,
+		 unsigned int cmd, struct PioctlData *data)
+{
+        union inputArgs *inp;
+        union outputArgs *outp;  
+	int insize, outsize, error;
+	int iocsize;
+
+	insize = VC_MAXMSGSIZE;
+	UPARG(CODA_IOCTL);
+
+        /* build packet for Venus */
+        if (data->vi.in_size > VC_MAXDATASIZE) {
+		error = -EINVAL;
+		goto exit;
+        }
+
+        if (data->vi.out_size > VC_MAXDATASIZE) {
+		error = -EINVAL;
+		goto exit;
+	}
+
+        inp->coda_ioctl.VFid = *fid;
+    
+        /* the cmd field was mutated by increasing its size field to
+         * reflect the path and follow args. We need to subtract that
+         * out before sending the command to Venus.  */
+        inp->coda_ioctl.cmd = (cmd & ~(PIOCPARM_MASK << 16));	
+        iocsize = ((cmd >> 16) & PIOCPARM_MASK) - sizeof(char *) - sizeof(int);
+        inp->coda_ioctl.cmd |= (iocsize & PIOCPARM_MASK) <<	16;	
+    
+        /* in->coda_ioctl.rwflag = flag; */
+        inp->coda_ioctl.len = data->vi.in_size;
+        inp->coda_ioctl.data = (char *)(INSIZE(ioctl));
+     
+        /* get the data out of user space */
+	if (copy_from_user((char *)inp + (long)inp->coda_ioctl.data,
+			   data->vi.in, data->vi.in_size)) {
+		error = -EINVAL;
+	        goto exit;
+	}
+
+	error = coda_upcall(coda_vcp(sb), SIZE(ioctl) + data->vi.in_size,
+			    &outsize, inp);
+
+        if (error) {
+		pr_warn("%s: Venus returns: %d for %s\n",
+			__func__, error, coda_f2s(fid));
+		goto exit; 
+	}
+
+	if (outsize < (long)outp->coda_ioctl.data + outp->coda_ioctl.len) {
+		error = -EINVAL;
+		goto exit;
+	}
+        
+	/* Copy out the OUT buffer. */
+        if (outp->coda_ioctl.len > data->vi.out_size) {
+		error = -EINVAL;
+		goto exit;
+        }
+
+	/* Copy out the OUT buffer. */
+	if (copy_to_user(data->vi.out,
+			 (char *)outp + (long)outp->coda_ioctl.data,
+			 outp->coda_ioctl.len)) {
+		error = -EFAULT;
+		goto exit;
+	}
+
+ exit:
+	CODA_FREE(inp, insize);
+	return error;
+}
+
+int venus_statfs(struct dentry *dentry, struct kstatfs *sfs)
+{ 
+        union inputArgs *inp;
+        union outputArgs *outp;
+        int insize, outsize, error;
+        
+	insize = max_t(unsigned int, INSIZE(statfs), OUTSIZE(statfs));
+	UPARG(CODA_STATFS);
+
+	error = coda_upcall(coda_vcp(dentry->d_sb), insize, &outsize, inp);
+	if (!error) {
+		sfs->f_blocks = outp->coda_statfs.stat.f_blocks;
+		sfs->f_bfree  = outp->coda_statfs.stat.f_bfree;
+		sfs->f_bavail = outp->coda_statfs.stat.f_bavail;
+		sfs->f_files  = outp->coda_statfs.stat.f_files;
+		sfs->f_ffree  = outp->coda_statfs.stat.f_ffree;
+	}
+
+        CODA_FREE(inp, insize);
+        return error;
+}
+
+/*
+ * coda_upcall and coda_downcall routines.
+ */
+static void coda_block_signals(sigset_t *old)
+{
+	spin_lock_irq(&current->sighand->siglock);
+	*old = current->blocked;
+
+	sigfillset(&current->blocked);
+	sigdelset(&current->blocked, SIGKILL);
+	sigdelset(&current->blocked, SIGSTOP);
+	sigdelset(&current->blocked, SIGINT);
+
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+}
+
+static void coda_unblock_signals(sigset_t *old)
+{
+	spin_lock_irq(&current->sighand->siglock);
+	current->blocked = *old;
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+}
+
+/* Don't allow signals to interrupt the following upcalls before venus
+ * has seen them,
+ * - CODA_CLOSE or CODA_RELEASE upcall  (to avoid reference count problems)
+ * - CODA_STORE				(to avoid data loss)
+ */
+#define CODA_INTERRUPTIBLE(r) (!coda_hard && \
+			       (((r)->uc_opcode != CODA_CLOSE && \
+				 (r)->uc_opcode != CODA_STORE && \
+				 (r)->uc_opcode != CODA_RELEASE) || \
+				(r)->uc_flags & CODA_REQ_READ))
+
+static inline void coda_waitfor_upcall(struct venus_comm *vcp,
+				       struct upc_req *req)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	unsigned long timeout = jiffies + coda_timeout * HZ;
+	sigset_t old;
+	int blocked;
+
+	coda_block_signals(&old);
+	blocked = 1;
+
+	add_wait_queue(&req->uc_sleep, &wait);
+	for (;;) {
+		if (CODA_INTERRUPTIBLE(req))
+			set_current_state(TASK_INTERRUPTIBLE);
+		else
+			set_current_state(TASK_UNINTERRUPTIBLE);
+
+		/* got a reply */
+		if (req->uc_flags & (CODA_REQ_WRITE | CODA_REQ_ABORT))
+			break;
+
+		if (blocked && time_after(jiffies, timeout) &&
+		    CODA_INTERRUPTIBLE(req))
+		{
+			coda_unblock_signals(&old);
+			blocked = 0;
+		}
+
+		if (signal_pending(current)) {
+			list_del(&req->uc_chain);
+			break;
+		}
+
+		mutex_unlock(&vcp->vc_mutex);
+		if (blocked)
+			schedule_timeout(HZ);
+		else
+			schedule();
+		mutex_lock(&vcp->vc_mutex);
+	}
+	if (blocked)
+		coda_unblock_signals(&old);
+
+	remove_wait_queue(&req->uc_sleep, &wait);
+	set_current_state(TASK_RUNNING);
+}
+
+
+/*
+ * coda_upcall will return an error in the case of
+ * failed communication with Venus _or_ will peek at Venus
+ * reply and return Venus' error.
+ *
+ * As venus has 2 types of errors, normal errors (positive) and internal
+ * errors (negative), normal errors are negated, while internal errors
+ * are all mapped to -EINTR, while showing a nice warning message. (jh)
+ */
+static int coda_upcall(struct venus_comm *vcp,
+		       int inSize, int *outSize,
+		       union inputArgs *buffer)
+{
+	union outputArgs *out;
+	union inputArgs *sig_inputArgs;
+	struct upc_req *req = NULL, *sig_req;
+	int error;
+
+	mutex_lock(&vcp->vc_mutex);
+
+	if (!vcp->vc_inuse) {
+		pr_notice("Venus dead, not sending upcall\n");
+		error = -ENXIO;
+		goto exit;
+	}
+
+	/* Format the request message. */
+	req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
+	if (!req) {
+		error = -ENOMEM;
+		goto exit;
+	}
+
+	req->uc_data = (void *)buffer;
+	req->uc_flags = 0;
+	req->uc_inSize = inSize;
+	req->uc_outSize = *outSize ? *outSize : inSize;
+	req->uc_opcode = ((union inputArgs *)buffer)->ih.opcode;
+	req->uc_unique = ++vcp->vc_seq;
+	init_waitqueue_head(&req->uc_sleep);
+
+	/* Fill in the common input args. */
+	((union inputArgs *)buffer)->ih.unique = req->uc_unique;
+
+	/* Append msg to pending queue and poke Venus. */
+	list_add_tail(&req->uc_chain, &vcp->vc_pending);
+
+	wake_up_interruptible(&vcp->vc_waitq);
+	/* We can be interrupted while we wait for Venus to process
+	 * our request.  If the interrupt occurs before Venus has read
+	 * the request, we dequeue and return. If it occurs after the
+	 * read but before the reply, we dequeue, send a signal
+	 * message, and return. If it occurs after the reply we ignore
+	 * it. In no case do we want to restart the syscall.  If it
+	 * was interrupted by a venus shutdown (psdev_close), return
+	 * ENODEV.  */
+
+	/* Go to sleep.  Wake up on signals only after the timeout. */
+	coda_waitfor_upcall(vcp, req);
+
+	/* Op went through, interrupt or not... */
+	if (req->uc_flags & CODA_REQ_WRITE) {
+		out = (union outputArgs *)req->uc_data;
+		/* here we map positive Venus errors to kernel errors */
+		error = -out->oh.result;
+		*outSize = req->uc_outSize;
+		goto exit;
+	}
+
+	error = -EINTR;
+	if ((req->uc_flags & CODA_REQ_ABORT) || !signal_pending(current)) {
+		pr_warn("Unexpected interruption.\n");
+		goto exit;
+	}
+
+	/* Interrupted before venus read it. */
+	if (!(req->uc_flags & CODA_REQ_READ))
+		goto exit;
+
+	/* Venus saw the upcall, make sure we can send interrupt signal */
+	if (!vcp->vc_inuse) {
+		pr_info("Venus dead, not sending signal.\n");
+		goto exit;
+	}
+
+	error = -ENOMEM;
+	sig_req = kmalloc(sizeof(struct upc_req), GFP_KERNEL);
+	if (!sig_req) goto exit;
+
+	CODA_ALLOC((sig_req->uc_data), char *, sizeof(struct coda_in_hdr));
+	if (!sig_req->uc_data) {
+		kfree(sig_req);
+		goto exit;
+	}
+
+	error = -EINTR;
+	sig_inputArgs = (union inputArgs *)sig_req->uc_data;
+	sig_inputArgs->ih.opcode = CODA_SIGNAL;
+	sig_inputArgs->ih.unique = req->uc_unique;
+
+	sig_req->uc_flags = CODA_REQ_ASYNC;
+	sig_req->uc_opcode = sig_inputArgs->ih.opcode;
+	sig_req->uc_unique = sig_inputArgs->ih.unique;
+	sig_req->uc_inSize = sizeof(struct coda_in_hdr);
+	sig_req->uc_outSize = sizeof(struct coda_in_hdr);
+
+	/* insert at head of queue! */
+	list_add(&(sig_req->uc_chain), &vcp->vc_pending);
+	wake_up_interruptible(&vcp->vc_waitq);
+
+exit:
+	kfree(req);
+	mutex_unlock(&vcp->vc_mutex);
+	return error;
+}
+
+/*  
+    The statements below are part of the Coda opportunistic
+    programming -- taken from the Mach/BSD kernel code for Coda. 
+    You don't get correct semantics by stating what needs to be
+    done without guaranteeing the invariants needed for it to happen.
+    When will be have time to find out what exactly is going on?  (pjb)
+*/
+
+
+/* 
+ * There are 7 cases where cache invalidations occur.  The semantics
+ *  of each is listed here:
+ *
+ * CODA_FLUSH     -- flush all entries from the name cache and the cnode cache.
+ * CODA_PURGEUSER -- flush all entries from the name cache for a specific user
+ *                  This call is a result of token expiration.
+ *
+ * The next arise as the result of callbacks on a file or directory.
+ * CODA_ZAPFILE   -- flush the cached attributes for a file.
+
+ * CODA_ZAPDIR    -- flush the attributes for the dir and
+ *                  force a new lookup for all the children
+                    of this dir.
+
+ *
+ * The next is a result of Venus detecting an inconsistent file.
+ * CODA_PURGEFID  -- flush the attribute for the file
+ *                  purge it and its children from the dcache
+ *
+ * The last  allows Venus to replace local fids with global ones
+ * during reintegration.
+ *
+ * CODA_REPLACE -- replace one CodaFid with another throughout the name cache */
+
+int coda_downcall(struct venus_comm *vcp, int opcode, union outputArgs *out)
+{
+	struct inode *inode = NULL;
+	struct CodaFid *fid = NULL, *newfid;
+	struct super_block *sb;
+
+	/* Handle invalidation requests. */
+	mutex_lock(&vcp->vc_mutex);
+	sb = vcp->vc_sb;
+	if (!sb || !sb->s_root)
+		goto unlock_out;
+
+	switch (opcode) {
+	case CODA_FLUSH:
+		coda_cache_clear_all(sb);
+		shrink_dcache_sb(sb);
+		if (d_really_is_positive(sb->s_root))
+			coda_flag_inode(d_inode(sb->s_root), C_FLUSH);
+		break;
+
+	case CODA_PURGEUSER:
+		coda_cache_clear_all(sb);
+		break;
+
+	case CODA_ZAPDIR:
+		fid = &out->coda_zapdir.CodaFid;
+		break;
+
+	case CODA_ZAPFILE:
+		fid = &out->coda_zapfile.CodaFid;
+		break;
+
+	case CODA_PURGEFID:
+		fid = &out->coda_purgefid.CodaFid;
+		break;
+
+	case CODA_REPLACE:
+		fid = &out->coda_replace.OldFid;
+		break;
+	}
+	if (fid)
+		inode = coda_fid_to_inode(fid, sb);
+
+unlock_out:
+	mutex_unlock(&vcp->vc_mutex);
+
+	if (!inode)
+		return 0;
+
+	switch (opcode) {
+	case CODA_ZAPDIR:
+		coda_flag_inode_children(inode, C_PURGE);
+		coda_flag_inode(inode, C_VATTR);
+		break;
+
+	case CODA_ZAPFILE:
+		coda_flag_inode(inode, C_VATTR);
+		break;
+
+	case CODA_PURGEFID:
+		coda_flag_inode_children(inode, C_PURGE);
+
+		/* catch the dentries later if some are still busy */
+		coda_flag_inode(inode, C_PURGE);
+		d_prune_aliases(inode);
+		break;
+
+	case CODA_REPLACE:
+		newfid = &out->coda_replace.NewFid;
+		coda_replace_fid(inode, fid, newfid);
+		break;
+	}
+	iput(inode);
+	return 0;
+}
+