drm/nouveau: off by one in init_i2c_device_find()
[safe/jmp/linux-2.6] / fs / 9p / vfs_inode.c
index 175b4d9..4331b3b 100644 (file)
 #include <linux/namei.h>
 #include <linux/idr.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 #include <net/9p/9p.h>
 #include <net/9p/client.h>
 
 #include "v9fs.h"
 #include "v9fs_vfs.h"
 #include "fid.h"
+#include "cache.h"
 
 static const struct inode_operations v9fs_dir_inode_operations;
-static const struct inode_operations v9fs_dir_inode_operations_ext;
+static const struct inode_operations v9fs_dir_inode_operations_dotu;
+static const struct inode_operations v9fs_dir_inode_operations_dotl;
 static const struct inode_operations v9fs_file_inode_operations;
+static const struct inode_operations v9fs_file_inode_operations_dotl;
 static const struct inode_operations v9fs_symlink_inode_operations;
+static const struct inode_operations v9fs_symlink_inode_operations_dotl;
 
 /**
  * unixmode2p9mode - convert unix mode bits to plan 9
@@ -59,7 +64,7 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
        res = mode & 0777;
        if (S_ISDIR(mode))
                res |= P9_DMDIR;
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                if (S_ISLNK(mode))
                        res |= P9_DMSYMLINK;
                if (v9ses->nodev == 0) {
@@ -77,6 +82,8 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
                        res |= P9_DMSETUID;
                if ((mode & S_ISGID) == S_ISGID)
                        res |= P9_DMSETGID;
+               if ((mode & S_ISVTX) == S_ISVTX)
+                       res |= P9_DMSETVTX;
                if ((mode & P9_DMLINK))
                        res |= P9_DMLINK;
        }
@@ -99,32 +106,41 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
 
        if ((mode & P9_DMDIR) == P9_DMDIR)
                res |= S_IFDIR;
-       else if ((mode & P9_DMSYMLINK) && (v9fs_extended(v9ses)))
+       else if ((mode & P9_DMSYMLINK) && (v9fs_proto_dotu(v9ses)))
                res |= S_IFLNK;
-       else if ((mode & P9_DMSOCKET) && (v9fs_extended(v9ses))
+       else if ((mode & P9_DMSOCKET) && (v9fs_proto_dotu(v9ses))
                 && (v9ses->nodev == 0))
                res |= S_IFSOCK;
-       else if ((mode & P9_DMNAMEDPIPE) && (v9fs_extended(v9ses))
+       else if ((mode & P9_DMNAMEDPIPE) && (v9fs_proto_dotu(v9ses))
                 && (v9ses->nodev == 0))
                res |= S_IFIFO;
-       else if ((mode & P9_DMDEVICE) && (v9fs_extended(v9ses))
+       else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses))
                 && (v9ses->nodev == 0))
                res |= S_IFBLK;
        else
                res |= S_IFREG;
 
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                if ((mode & P9_DMSETUID) == P9_DMSETUID)
                        res |= S_ISUID;
 
                if ((mode & P9_DMSETGID) == P9_DMSETGID)
                        res |= S_ISGID;
+
+               if ((mode & P9_DMSETVTX) == P9_DMSETVTX)
+                       res |= S_ISVTX;
        }
 
        return res;
 }
 
-int v9fs_uflags2omode(int uflags)
+/**
+ * v9fs_uflags2omode- convert posix open flags to plan 9 mode bits
+ * @uflags: flags to convert
+ * @extended: if .u extensions are active
+ */
+
+int v9fs_uflags2omode(int uflags, int extended)
 {
        int ret;
 
@@ -144,26 +160,27 @@ int v9fs_uflags2omode(int uflags)
                break;
        }
 
-       if (uflags & O_EXCL)
-               ret |= P9_OEXCL;
-
        if (uflags & O_TRUNC)
                ret |= P9_OTRUNC;
 
-       if (uflags & O_APPEND)
-               ret |= P9_OAPPEND;
+       if (extended) {
+               if (uflags & O_EXCL)
+                       ret |= P9_OEXCL;
+
+               if (uflags & O_APPEND)
+                       ret |= P9_OAPPEND;
+       }
 
        return ret;
 }
 
 /**
  * v9fs_blank_wstat - helper function to setup a 9P stat structure
- * @v9ses: 9P session info (for determining extended mode)
  * @wstat: structure to initialize
  *
  */
 
-static void
+void
 v9fs_blank_wstat(struct p9_wstat *wstat)
 {
        wstat->type = ~0;
@@ -185,6 +202,39 @@ v9fs_blank_wstat(struct p9_wstat *wstat)
        wstat->extension = NULL;
 }
 
+#ifdef CONFIG_9P_FSCACHE
+/**
+ * v9fs_alloc_inode - helper function to allocate an inode
+ * This callback is executed before setting up the inode so that we
+ * can associate a vcookie with each inode.
+ *
+ */
+
+struct inode *v9fs_alloc_inode(struct super_block *sb)
+{
+       struct v9fs_cookie *vcookie;
+       vcookie = (struct v9fs_cookie *)kmem_cache_alloc(vcookie_cache,
+                                                        GFP_KERNEL);
+       if (!vcookie)
+               return NULL;
+
+       vcookie->fscache = NULL;
+       vcookie->qid = NULL;
+       spin_lock_init(&vcookie->lock);
+       return &vcookie->inode;
+}
+
+/**
+ * v9fs_destroy_inode - destroy an inode
+ *
+ */
+
+void v9fs_destroy_inode(struct inode *inode)
+{
+       kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode));
+}
+#endif
+
 /**
  * v9fs_get_inode - helper function to setup an inode
  * @sb: superblock
@@ -194,65 +244,89 @@ v9fs_blank_wstat(struct p9_wstat *wstat)
 
 struct inode *v9fs_get_inode(struct super_block *sb, int mode)
 {
+       int err;
        struct inode *inode;
        struct v9fs_session_info *v9ses = sb->s_fs_info;
 
        P9_DPRINTK(P9_DEBUG_VFS, "super block: %p mode: %o\n", sb, mode);
 
        inode = new_inode(sb);
-       if (inode) {
-               inode->i_mode = mode;
-               inode->i_uid = current->fsuid;
-               inode->i_gid = current->fsgid;
-               inode->i_blocks = 0;
-               inode->i_rdev = 0;
-               inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
-               inode->i_mapping->a_ops = &v9fs_addr_operations;
-
-               switch (mode & S_IFMT) {
-               case S_IFIFO:
-               case S_IFBLK:
-               case S_IFCHR:
-               case S_IFSOCK:
-                       if (!v9fs_extended(v9ses)) {
-                               P9_DPRINTK(P9_DEBUG_ERROR,
-                                     "special files without extended mode\n");
-                               return ERR_PTR(-EINVAL);
-                       }
-                       init_special_inode(inode, inode->i_mode,
-                                          inode->i_rdev);
-                       break;
-               case S_IFREG:
+       if (!inode) {
+               P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       inode_init_owner(inode, NULL, mode);
+       inode->i_blocks = 0;
+       inode->i_rdev = 0;
+       inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
+       inode->i_mapping->a_ops = &v9fs_addr_operations;
+
+       switch (mode & S_IFMT) {
+       case S_IFIFO:
+       case S_IFBLK:
+       case S_IFCHR:
+       case S_IFSOCK:
+               if (!v9fs_proto_dotu(v9ses)) {
+                       P9_DPRINTK(P9_DEBUG_ERROR,
+                                  "special files without extended mode\n");
+                       err = -EINVAL;
+                       goto error;
+               }
+               init_special_inode(inode, inode->i_mode, inode->i_rdev);
+               break;
+       case S_IFREG:
+               if (v9fs_proto_dotl(v9ses)) {
+                       inode->i_op = &v9fs_file_inode_operations_dotl;
+                       inode->i_fop = &v9fs_file_operations_dotl;
+               } else {
                        inode->i_op = &v9fs_file_inode_operations;
                        inode->i_fop = &v9fs_file_operations;
-                       break;
-               case S_IFLNK:
-                       if (!v9fs_extended(v9ses)) {
-                               P9_DPRINTK(P9_DEBUG_ERROR,
-                                       "extended modes used w/o 9P2000.u\n");
-                               return ERR_PTR(-EINVAL);
-                       }
+               }
+
+               break;
+
+       case S_IFLNK:
+               if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) {
+                       P9_DPRINTK(P9_DEBUG_ERROR, "extended modes used with "
+                                               "legacy protocol.\n");
+                       err = -EINVAL;
+                       goto error;
+               }
+
+               if (v9fs_proto_dotl(v9ses))
+                       inode->i_op = &v9fs_symlink_inode_operations_dotl;
+               else
                        inode->i_op = &v9fs_symlink_inode_operations;
-                       break;
-               case S_IFDIR:
-                       inc_nlink(inode);
-                       if (v9fs_extended(v9ses))
-                               inode->i_op = &v9fs_dir_inode_operations_ext;
-                       else
-                               inode->i_op = &v9fs_dir_inode_operations;
+
+               break;
+       case S_IFDIR:
+               inc_nlink(inode);
+               if (v9fs_proto_dotl(v9ses))
+                       inode->i_op = &v9fs_dir_inode_operations_dotl;
+               else if (v9fs_proto_dotu(v9ses))
+                       inode->i_op = &v9fs_dir_inode_operations_dotu;
+               else
+                       inode->i_op = &v9fs_dir_inode_operations;
+
+               if (v9fs_proto_dotl(v9ses))
+                       inode->i_fop = &v9fs_dir_operations_dotl;
+               else
                        inode->i_fop = &v9fs_dir_operations;
-                       break;
-               default:
-                       P9_DPRINTK(P9_DEBUG_ERROR,
-                               "BAD mode 0x%x S_IFMT 0x%x\n",
-                               mode, mode & S_IFMT);
-                       return ERR_PTR(-EINVAL);
-               }
-       } else {
-               P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
-               return ERR_PTR(-ENOMEM);
+
+               break;
+       default:
+               P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
+                          mode, mode & S_IFMT);
+               err = -EINVAL;
+               goto error;
        }
+
        return inode;
+
+error:
+       iput(inode);
+       return ERR_PTR(err);
 }
 
 /*
@@ -307,40 +381,64 @@ error:
 }
 */
 
+
+/**
+ * v9fs_clear_inode - release an inode
+ * @inode: inode to release
+ *
+ */
+void v9fs_clear_inode(struct inode *inode)
+{
+       filemap_fdatawrite(inode->i_mapping);
+
+#ifdef CONFIG_9P_FSCACHE
+       v9fs_cache_inode_put_cookie(inode);
+#endif
+}
+
+/**
+ * v9fs_inode_from_fid - populate an inode by issuing a attribute request
+ * @v9ses: session information
+ * @fid: fid to issue attribute request for
+ * @sb: superblock on which to create inode
+ *
+ */
+
 static struct inode *
 v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
        struct super_block *sb)
 {
        int err, umode;
        struct inode *ret;
-       struct p9_stat *st;
+       struct p9_wstat *st;
 
        ret = NULL;
        st = p9_client_stat(fid);
-       if (IS_ERR(st)) {
-               err = PTR_ERR(st);
-               st = NULL;
-               goto error;
-       }
+       if (IS_ERR(st))
+               return ERR_CAST(st);
 
        umode = p9mode2unixmode(v9ses, st->mode);
        ret = v9fs_get_inode(sb, umode);
        if (IS_ERR(ret)) {
                err = PTR_ERR(ret);
-               ret = NULL;
                goto error;
        }
 
        v9fs_stat2inode(st, ret, sb);
        ret->i_ino = v9fs_qid2ino(&st->qid);
+
+#ifdef CONFIG_9P_FSCACHE
+       v9fs_vcookie_set_qid(ret, &st->qid);
+       v9fs_cache_inode_get_cookie(ret);
+#endif
+       p9stat_free(st);
        kfree(st);
+
        return ret;
 
 error:
+       p9stat_free(st);
        kfree(st);
-       if (ret)
-               iput(ret);
-
        return ERR_PTR(err);
 }
 
@@ -354,20 +452,22 @@ error:
 
 static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
 {
+       int retval;
        struct inode *file_inode;
-       struct v9fs_session_info *v9ses;
        struct p9_fid *v9fid;
 
        P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
                rmdir);
 
        file_inode = file->d_inode;
-       v9ses = v9fs_inode2v9ses(file_inode);
        v9fid = v9fs_fid_clone(file);
        if (IS_ERR(v9fid))
                return PTR_ERR(v9fid);
 
-       return p9_client_remove(v9fid);
+       retval = p9_client_remove(v9fid);
+       if (!retval)
+               drop_nlink(file_inode);
+       return retval;
 }
 
 static int
@@ -379,7 +479,10 @@ v9fs_open_created(struct inode *inode, struct file *file)
 
 /**
  * v9fs_create - Create a file
+ * @v9ses: session information
+ * @dir: directory that dentry is being created in
  * @dentry:  dentry that is being created
+ * @extension: 9p2000.u extension string to support devices, etc.
  * @perm: create permissions
  * @mode: open mode
  *
@@ -393,42 +496,47 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
        struct p9_fid *dfid, *ofid, *fid;
        struct inode *inode;
 
+       P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);
+
        err = 0;
        ofid = NULL;
        fid = NULL;
        name = (char *) dentry->d_name.name;
-       dfid = v9fs_fid_clone(dentry->d_parent);
+       dfid = v9fs_fid_lookup(dentry->d_parent);
        if (IS_ERR(dfid)) {
                err = PTR_ERR(dfid);
-               dfid = NULL;
-               goto error;
+               P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err);
+               return ERR_PTR(err);
        }
 
        /* clone a fid to use for creation */
        ofid = p9_client_walk(dfid, 0, NULL, 1);
        if (IS_ERR(ofid)) {
                err = PTR_ERR(ofid);
-               ofid = NULL;
-               goto error;
+               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
+               return ERR_PTR(err);
        }
 
        err = p9_client_fcreate(ofid, name, perm, mode, extension);
-       if (err < 0)
+       if (err < 0) {
+               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);
                goto error;
+       }
 
        /* now walk from the parent so we can get unopened fid */
-       fid = p9_client_walk(dfid, 1, &name, 0);
+       fid = p9_client_walk(dfid, 1, &name, 1);
        if (IS_ERR(fid)) {
                err = PTR_ERR(fid);
+               P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);
                fid = NULL;
                goto error;
-       } else
-               dfid = NULL;
+       }
 
        /* instantiate inode and assign the unopened fid to the dentry */
        inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb);
        if (IS_ERR(inode)) {
                err = PTR_ERR(inode);
+               P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
                goto error;
        }
 
@@ -438,13 +546,13 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
                dentry->d_op = &v9fs_dentry_operations;
 
        d_instantiate(dentry, inode);
-       v9fs_fid_add(dentry, fid);
+       err = v9fs_fid_add(dentry, fid);
+       if (err < 0)
+               goto error;
+
        return ofid;
 
 error:
-       if (dfid)
-               p9_client_clunk(dfid);
-
        if (ofid)
                p9_client_clunk(ofid);
 
@@ -456,7 +564,7 @@ error:
 
 /**
  * v9fs_vfs_create - VFS hook to create files
- * @inode: directory inode that is being created
+ * @dir: directory inode that is being created
  * @dentry:  dentry that is being deleted
  * @mode: create permissions
  * @nd: path information
@@ -484,7 +592,8 @@ v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode,
                flags = O_RDWR;
 
        fid = v9fs_create(v9ses, dir, dentry, NULL, perm,
-                                               v9fs_uflags2omode(flags));
+                               v9fs_uflags2omode(flags,
+                                               v9fs_proto_dotu(v9ses)));
        if (IS_ERR(fid)) {
                err = PTR_ERR(fid);
                fid = NULL;
@@ -514,7 +623,7 @@ error:
 
 /**
  * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
- * @inode:  inode that is being unlinked
+ * @dir:  inode that is being unlinked
  * @dentry: dentry that is being unlinked
  * @mode: mode for new directory
  *
@@ -564,19 +673,22 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
        P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
                dir, dentry->d_name.name, dentry, nameidata);
 
+       if (dentry->d_name.len > NAME_MAX)
+               return ERR_PTR(-ENAMETOOLONG);
+
        sb = dir->i_sb;
        v9ses = v9fs_inode2v9ses(dir);
        dfid = v9fs_fid_lookup(dentry->d_parent);
        if (IS_ERR(dfid))
-               return ERR_PTR(PTR_ERR(dfid));
+               return ERR_CAST(dfid);
 
        name = (char *) dentry->d_name.name;
        fid = p9_client_walk(dfid, 1, &name, 1);
        if (IS_ERR(fid)) {
                result = PTR_ERR(fid);
                if (result == -ENOENT) {
-                       d_add(dentry, NULL);
-                       return NULL;
+                       inode = NULL;
+                       goto inst_out;
                }
 
                return ERR_PTR(result);
@@ -593,7 +705,8 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
        if (result < 0)
                goto error;
 
-       if ((fid->qid.version) && (v9ses->cache))
+inst_out:
+       if (v9ses->cache)
                dentry->d_op = &v9fs_cached_dentry_operations;
        else
                dentry->d_op = &v9fs_dentry_operations;
@@ -602,8 +715,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
        return NULL;
 
 error:
-       if (fid)
-               p9_client_clunk(fid);
+       p9_client_clunk(fid);
 
        return ERR_PTR(result);
 }
@@ -673,6 +785,13 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto clunk_olddir;
        }
 
+       if (v9fs_proto_dotl(v9ses)) {
+               retval = p9_client_rename(oldfid, newdirfid,
+                                       (char *) new_dentry->d_name.name);
+               if (retval != -ENOSYS)
+                       goto clunk_newdir;
+       }
+
        /* 9P can only handle file rename in the same directory */
        if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) {
                P9_DPRINTK(P9_DEBUG_ERROR,
@@ -687,10 +806,10 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        retval = p9_client_wstat(oldfid, &wstat);
 
 clunk_newdir:
-       p9_client_clunk(olddirfid);
+       p9_client_clunk(newdirfid);
 
 clunk_olddir:
-       p9_client_clunk(newdirfid);
+       p9_client_clunk(olddirfid);
 
 done:
        return retval;
@@ -698,9 +817,9 @@ done:
 
 /**
  * v9fs_vfs_getattr - retrieve file metadata
- * @mnt - mount information
- * @dentry - file to get attributes on
- * @stat - metadata structure to populate
+ * @mnt: mount information
+ * @dentry: file to get attributes on
+ * @stat: metadata structure to populate
  *
  */
 
@@ -711,12 +830,12 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
        int err;
        struct v9fs_session_info *v9ses;
        struct p9_fid *fid;
-       struct p9_stat *st;
+       struct p9_wstat *st;
 
        P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry);
        err = -EPERM;
        v9ses = v9fs_inode2v9ses(dentry->d_inode);
-       if (v9ses->cache == CACHE_LOOSE)
+       if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE)
                return simple_getattr(mnt, dentry, stat);
 
        fid = v9fs_fid_lookup(dentry);
@@ -768,7 +887,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
        if (iattr->ia_valid & ATTR_SIZE)
                wstat.length = iattr->ia_size;
 
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                if (iattr->ia_valid & ATTR_UID)
                        wstat.n_uid = iattr->ia_uid;
 
@@ -792,11 +911,12 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
  */
 
 void
-v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
+v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
        struct super_block *sb)
 {
-       int n;
        char ext[32];
+       char tag_name[14];
+       unsigned int i_nlink;
        struct v9fs_session_info *v9ses = sb->s_fs_info;
 
        inode->i_nlink = 1;
@@ -808,22 +928,33 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
        inode->i_uid = v9ses->dfltuid;
        inode->i_gid = v9ses->dfltgid;
 
-       if (v9fs_extended(v9ses)) {
+       if (v9fs_proto_dotu(v9ses)) {
                inode->i_uid = stat->n_uid;
                inode->i_gid = stat->n_gid;
        }
-
+       if ((S_ISREG(inode->i_mode)) || (S_ISDIR(inode->i_mode))) {
+               if (v9fs_proto_dotu(v9ses) && (stat->extension[0] != '\0')) {
+                       /*
+                        * Hadlink support got added later to
+                        * to the .u extension. So there can be
+                        * server out there that doesn't support
+                        * this even with .u extension. So check
+                        * for non NULL stat->extension
+                        */
+                       strncpy(ext, stat->extension, sizeof(ext));
+                       /* HARDLINKCOUNT %u */
+                       sscanf(ext, "%13s %u", tag_name, &i_nlink);
+                       if (!strncmp(tag_name, "HARDLINKCOUNT", 13))
+                               inode->i_nlink = i_nlink;
+               }
+       }
        inode->i_mode = p9mode2unixmode(v9ses, stat->mode);
        if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
                char type = 0;
                int major = -1;
                int minor = -1;
 
-               n = stat->extension.len;
-               if (n > sizeof(ext)-1)
-                       n = sizeof(ext)-1;
-               memmove(ext, stat->extension.str, n);
-               ext[n] = 0;
+               strncpy(ext, stat->extension, sizeof(ext));
                sscanf(ext, "%c %u %u", &type, &major, &minor);
                switch (type) {
                case 'c':
@@ -834,17 +965,18 @@ v9fs_stat2inode(struct p9_stat *stat, struct inode *inode,
                        break;
                default:
                        P9_DPRINTK(P9_DEBUG_ERROR,
-                               "Unknown special type %c (%.*s)\n", type,
-                               stat->extension.len, stat->extension.str);
+                               "Unknown special type %c %s\n", type,
+                               stat->extension);
                };
                inode->i_rdev = MKDEV(major, minor);
+               init_special_inode(inode, inode->i_mode, inode->i_rdev);
        } else
                inode->i_rdev = 0;
 
-       inode->i_size = stat->length;
+       i_size_write(inode, stat->length);
 
        /* not real number of blocks, but 512 byte ones ... */
-       inode->i_blocks = (inode->i_size + 512 - 1) >> 9;
+       inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9;
 }
 
 /**
@@ -881,7 +1013,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
 
        struct v9fs_session_info *v9ses;
        struct p9_fid *fid;
-       struct p9_stat *st;
+       struct p9_wstat *st;
 
        P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);
        retval = -EPERM;
@@ -890,7 +1022,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
        if (IS_ERR(fid))
                return PTR_ERR(fid);
 
-       if (!v9fs_extended(v9ses))
+       if (!v9fs_proto_dotu(v9ses))
                return -EBADF;
 
        st = p9_client_stat(fid);
@@ -903,61 +1035,18 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
        }
 
        /* copy extension buffer into buffer */
-       if (st->extension.len < buflen)
-               buflen = st->extension.len + 1;
-
-       memmove(buffer, st->extension.str, buflen - 1);
-       buffer[buflen-1] = 0;
+       strncpy(buffer, st->extension, buflen);
 
        P9_DPRINTK(P9_DEBUG_VFS,
-               "%s -> %.*s (%s)\n", dentry->d_name.name, st->extension.len,
-               st->extension.str, buffer);
-
-       retval = buflen;
+               "%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);
 
+       retval = strnlen(buffer, buflen);
 done:
        kfree(st);
        return retval;
 }
 
 /**
- * v9fs_vfs_readlink - read a symlink's location
- * @dentry: dentry for symlink
- * @buf: buffer to load symlink location into
- * @buflen: length of buffer
- *
- */
-
-static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer,
-                            int buflen)
-{
-       int retval;
-       int ret;
-       char *link = __getname();
-
-       if (unlikely(!link))
-               return -ENOMEM;
-
-       if (buflen > PATH_MAX)
-               buflen = PATH_MAX;
-
-       P9_DPRINTK(P9_DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
-
-       retval = v9fs_readlink(dentry, link, buflen);
-
-       if (retval > 0) {
-               if ((ret = copy_to_user(buffer, link, retval)) != 0) {
-                       P9_DPRINTK(P9_DEBUG_ERROR,
-                                       "problem copying to user: %d\n", ret);
-                       retval = ret;
-               }
-       }
-
-       __putname(link);
-       return retval;
-}
-
-/**
  * v9fs_vfs_follow_link - follow a symlink path
  * @dentry: dentry for symlink
  * @nd: nameidata
@@ -980,7 +1069,7 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
                        __putname(link);
                        link = ERR_PTR(len);
                } else
-                       link[len] = 0;
+                       link[min(len, PATH_MAX-1)] = 0;
        }
        nd_set_link(nd, link);
 
@@ -991,18 +1080,30 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
  * v9fs_vfs_put_link - release a symlink path
  * @dentry: dentry for symlink
  * @nd: nameidata
+ * @p: unused
  *
  */
 
-static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
+static void
+v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
 {
        char *s = nd_get_link(nd);
 
-       P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name, s);
+       P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name,
+               IS_ERR(s) ? "<error>" : s);
        if (!IS_ERR(s))
                __putname(s);
 }
 
+/**
+ * v9fs_vfs_mkspecial - create a special file
+ * @dir: inode to create special file in
+ * @dentry: dentry to create
+ * @mode: mode to create special file
+ * @extension: 9p2000.u format extension string representing special file
+ *
+ */
+
 static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
        int mode, const char *extension)
 {
@@ -1011,7 +1112,7 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
        struct p9_fid *fid;
 
        v9ses = v9fs_inode2v9ses(dir);
-       if (!v9fs_extended(v9ses)) {
+       if (!v9fs_proto_dotu(v9ses)) {
                P9_DPRINTK(P9_DEBUG_ERROR, "not extended\n");
                return -EPERM;
        }
@@ -1032,7 +1133,7 @@ static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry,
  * @dentry: dentry for symlink
  * @symname: symlink data
  *
- * See 9P2000.u RFC for more information
+ * See Also: 9P2000.u RFC for more information
  *
  */
 
@@ -1053,10 +1154,6 @@ v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
  *
  */
 
-/* XXX - lots of code dup'd from symlink and creates,
- * figure out a better reuse strategy
- */
-
 static int
 v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
              struct dentry *dentry)
@@ -1093,7 +1190,7 @@ clunk_fid:
  * @dir: inode destination for new link
  * @dentry: dentry for file
  * @mode: mode for creation
- * @dev_t: device associated with special file
+ * @rdev: device associated with special file
  *
  */
 
@@ -1120,6 +1217,8 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
                sprintf(name, "c %u %u", MAJOR(rdev), MINOR(rdev));
        else if (S_ISFIFO(mode))
                *name = 0;
+       else if (S_ISSOCK(mode))
+               *name = 0;
        else {
                __putname(name);
                return -EINVAL;
@@ -1131,7 +1230,21 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
        return retval;
 }
 
-static const struct inode_operations v9fs_dir_inode_operations_ext = {
+static const struct inode_operations v9fs_dir_inode_operations_dotu = {
+       .create = v9fs_vfs_create,
+       .lookup = v9fs_vfs_lookup,
+       .symlink = v9fs_vfs_symlink,
+       .link = v9fs_vfs_link,
+       .unlink = v9fs_vfs_unlink,
+       .mkdir = v9fs_vfs_mkdir,
+       .rmdir = v9fs_vfs_rmdir,
+       .mknod = v9fs_vfs_mknod,
+       .rename = v9fs_vfs_rename,
+       .getattr = v9fs_vfs_getattr,
+       .setattr = v9fs_vfs_setattr,
+};
+
+static const struct inode_operations v9fs_dir_inode_operations_dotl = {
        .create = v9fs_vfs_create,
        .lookup = v9fs_vfs_lookup,
        .symlink = v9fs_vfs_symlink,
@@ -1141,7 +1254,6 @@ static const struct inode_operations v9fs_dir_inode_operations_ext = {
        .rmdir = v9fs_vfs_rmdir,
        .mknod = v9fs_vfs_mknod,
        .rename = v9fs_vfs_rename,
-       .readlink = v9fs_vfs_readlink,
        .getattr = v9fs_vfs_getattr,
        .setattr = v9fs_vfs_setattr,
 };
@@ -1163,8 +1275,21 @@ static const struct inode_operations v9fs_file_inode_operations = {
        .setattr = v9fs_vfs_setattr,
 };
 
+static const struct inode_operations v9fs_file_inode_operations_dotl = {
+       .getattr = v9fs_vfs_getattr,
+       .setattr = v9fs_vfs_setattr,
+};
+
 static const struct inode_operations v9fs_symlink_inode_operations = {
-       .readlink = v9fs_vfs_readlink,
+       .readlink = generic_readlink,
+       .follow_link = v9fs_vfs_follow_link,
+       .put_link = v9fs_vfs_put_link,
+       .getattr = v9fs_vfs_getattr,
+       .setattr = v9fs_vfs_setattr,
+};
+
+static const struct inode_operations v9fs_symlink_inode_operations_dotl = {
+       .readlink = generic_readlink,
        .follow_link = v9fs_vfs_follow_link,
        .put_link = v9fs_vfs_put_link,
        .getattr = v9fs_vfs_getattr,