vserver 1.9.5.x5
[linux-2.6.git] / fs / nfsd / vfs.c
index f5586d8..a5a58a1 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/time.h>
 #include <linux/errno.h>
 #include <linux/fs.h>
+#include <linux/file.h>
 #include <linux/mount.h>
 #include <linux/major.h>
 #include <linux/ext2_fs.h>
 #include <linux/nfsd/nfsfh.h>
 #include <linux/quotaops.h>
 #include <linux/dnotify.h>
+#ifdef CONFIG_NFSD_V4
+#include <linux/posix_acl.h>
+#include <linux/posix_acl_xattr.h>
+#include <linux/xattr_acl.h>
+#include <linux/xattr.h>
+#include <linux/nfs4.h>
+#include <linux/nfs4_acl.h>
+#include <linux/nfsd_idmap.h>
+#include <linux/security.h>
+#endif /* CONFIG_NFSD_V4 */
 
 #include <asm/uaccess.h>
 
@@ -69,6 +80,7 @@ struct raparms {
        unsigned int            p_count;
        ino_t                   p_ino;
        dev_t                   p_dev;
+       int                     p_set;
        struct file_ra_state    p_ra;
 };
 
@@ -141,10 +153,11 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
        /* Obtain dentry and export. */
        err = fh_verify(rqstp, fhp, S_IFDIR, MAY_EXEC);
        if (err)
-               goto out;
+               return err;
 
        dparent = fhp->fh_dentry;
        exp  = fhp->fh_export;
+       exp_get(exp);
 
        err = nfserr_acces;
 
@@ -208,7 +221,9 @@ nfsd_lookup(struct svc_rqst *rqstp, struct svc_fh *fhp, const char *name,
        err = fh_compose(resfh, exp, dentry, fhp);
        if (!err && !dentry->d_inode)
                err = nfserr_noent;
+       dput(dentry);
 out:
+       exp_put(exp);
        return err;
 
 out_nfserr:
@@ -288,8 +303,10 @@ nfsd_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp, struct iattr *iap,
                 * If we are changing the size of the file, then
                 * we need to break all leases.
                 */
-               err = break_lease(inode, FMODE_WRITE);
-               if (err)
+               err = break_lease(inode, FMODE_WRITE | O_NONBLOCK);
+               if (err == -EWOULDBLOCK)
+                       err = -ETIMEDOUT;
+               if (err) /* ENOMEM or EWOULDBLOCK */
                        goto out_nfserr;
 
                err = get_write_access(inode);
@@ -341,6 +358,175 @@ out_nfserr:
        goto out;
 }
 
+#if defined(CONFIG_NFSD_V4)
+
+static int
+set_nfsv4_acl_one(struct dentry *dentry, struct posix_acl *pacl, char *key)
+{
+       int len;
+       size_t buflen;
+       char *buf = NULL;
+       int error = 0;
+       struct inode *inode = dentry->d_inode;
+
+       buflen = posix_acl_xattr_size(pacl->a_count);
+       buf = kmalloc(buflen, GFP_KERNEL);
+       error = -ENOMEM;
+       if (buf == NULL)
+               goto out;
+
+       len = posix_acl_to_xattr(pacl, buf, buflen);
+       if (len < 0) {
+               error = len;
+               goto out;
+       }
+
+       error = -EOPNOTSUPP;
+       if (inode->i_op && inode->i_op->setxattr) {
+               down(&inode->i_sem);
+               security_inode_setxattr(dentry, key, buf, len, 0);
+               error = inode->i_op->setxattr(dentry, key, buf, len, 0);
+               if (!error)
+                       security_inode_post_setxattr(dentry, key, buf, len, 0);
+               up(&inode->i_sem);
+       }
+out:
+       kfree(buf);
+       return (error);
+}
+
+int
+nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
+    struct nfs4_acl *acl)
+{
+       int error;
+       struct dentry *dentry;
+       struct inode *inode;
+       struct posix_acl *pacl = NULL, *dpacl = NULL;
+       unsigned int flags = 0;
+
+       /* Get inode */
+       error = fh_verify(rqstp, fhp, 0 /* S_IFREG */, MAY_SATTR);
+       if (error)
+               goto out;
+
+       dentry = fhp->fh_dentry;
+       inode = dentry->d_inode;
+       if (S_ISDIR(inode->i_mode))
+               flags = NFS4_ACL_DIR;
+
+       error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
+       if (error < 0)
+               goto out_nfserr;
+
+       if (pacl) {
+               error = set_nfsv4_acl_one(dentry, pacl, XATTR_NAME_ACL_ACCESS);
+               if (error < 0)
+                       goto out_nfserr;
+       }
+
+       if (dpacl) {
+               error = set_nfsv4_acl_one(dentry, dpacl, XATTR_NAME_ACL_DEFAULT);
+               if (error < 0)
+                       goto out_nfserr;
+       }
+
+       error = nfs_ok;
+
+out:
+       posix_acl_release(pacl);
+       posix_acl_release(dpacl);
+       return (error);
+out_nfserr:
+       error = nfserrno(error);
+       goto out;
+}
+
+static struct posix_acl *
+_get_posix_acl(struct dentry *dentry, char *key)
+{
+       struct inode *inode = dentry->d_inode;
+       char *buf = NULL;
+       int buflen, error = 0;
+       struct posix_acl *pacl = NULL;
+
+       error = -EOPNOTSUPP;
+       if (inode->i_op == NULL)
+               goto out_err;
+       if (inode->i_op->getxattr == NULL)
+               goto out_err;
+
+       error = security_inode_getxattr(dentry, key);
+       if (error)
+               goto out_err;
+
+       buflen = inode->i_op->getxattr(dentry, key, NULL, 0);
+       if (buflen <= 0) {
+               error = buflen < 0 ? buflen : -ENODATA;
+               goto out_err;
+       }
+
+       buf = kmalloc(buflen, GFP_KERNEL);
+       if (buf == NULL) {
+               error = -ENOMEM;
+               goto out_err;
+       }
+
+       error = inode->i_op->getxattr(dentry, key, buf, buflen);
+       if (error < 0)
+               goto out_err;
+
+       pacl = posix_acl_from_xattr(buf, buflen);
+ out:
+       kfree(buf);
+       return pacl;
+ out_err:
+       pacl = ERR_PTR(error);
+       goto out;
+}
+
+int
+nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_acl **acl)
+{
+       struct inode *inode = dentry->d_inode;
+       int error = 0;
+       struct posix_acl *pacl = NULL, *dpacl = NULL;
+       unsigned int flags = 0;
+
+       pacl = _get_posix_acl(dentry, XATTR_NAME_ACL_ACCESS);
+       if (IS_ERR(pacl) && PTR_ERR(pacl) == -ENODATA)
+               pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
+       if (IS_ERR(pacl)) {
+               error = PTR_ERR(pacl);
+               pacl = NULL;
+               goto out;
+       }
+
+       if (S_ISDIR(inode->i_mode)) {
+               dpacl = _get_posix_acl(dentry, XATTR_NAME_ACL_DEFAULT);
+               if (IS_ERR(dpacl) && PTR_ERR(dpacl) == -ENODATA)
+                       dpacl = NULL;
+               else if (IS_ERR(dpacl)) {
+                       error = PTR_ERR(dpacl);
+                       dpacl = NULL;
+                       goto out;
+               }
+               flags = NFS4_ACL_DIR;
+       }
+
+       *acl = nfs4_acl_posix_to_nfsv4(pacl, dpacl, flags);
+       if (IS_ERR(*acl)) {
+               error = PTR_ERR(*acl);
+               *acl = NULL;
+       }
+ out:
+       posix_acl_release(pacl);
+       posix_acl_release(dpacl);
+       return error;
+}
+
+#endif /* defined(CONFIG_NFS_V4) */
+
 #ifdef CONFIG_NFSD_V3
 /*
  * Check server access rights to a file system object
@@ -373,12 +559,13 @@ static struct accessmap   nfs3_anyaccess[] = {
         * to the server to check for access for things like /dev/null
         * (which really, the server doesn't care about).  So
         * We provide simple access checking for them, looking
-        * mainly at mode bits
+        * mainly at mode bits, and we make sure to ignore read-only
+        * filesystem checks
         */
     {  NFS3_ACCESS_READ,       MAY_READ                        },
     {  NFS3_ACCESS_EXECUTE,    MAY_EXEC                        },
-    {  NFS3_ACCESS_MODIFY,     MAY_WRITE                       },
-    {  NFS3_ACCESS_EXTEND,     MAY_WRITE                       },
+    {  NFS3_ACCESS_MODIFY,     MAY_WRITE|MAY_LOCAL_ACCESS      },
+    {  NFS3_ACCESS_EXTEND,     MAY_WRITE|MAY_LOCAL_ACCESS      },
 
     {  0,                      0                               }
 };
@@ -451,7 +638,7 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
  */
 int
 nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
-                       int access, struct file *filp)
+                       int access, struct file **filp)
 {
        struct dentry   *dentry;
        struct inode    *inode;
@@ -469,12 +656,15 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
        dentry = fhp->fh_dentry;
        inode = dentry->d_inode;
 
-       /* Disallow access to files with the append-only bit set or
-        * with mandatory locking enabled
+       /* Disallow write access to files with the append-only bit set
+        * or any access when mandatory locking enabled
         */
        err = nfserr_perm;
-       if (IS_APPEND(inode) || IS_ISMNDLK(inode))
+       if (IS_APPEND(inode) && (access & MAY_WRITE))
                goto out;
+       if (IS_ISMNDLK(inode))
+               goto out;
+
        if (!inode->i_fop)
                goto out;
 
@@ -482,26 +672,20 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
         * Check to see if there are any leases on this file.
         * This may block while leases are broken.
         */
-       err = break_lease(inode, (access & MAY_WRITE) ? FMODE_WRITE : 0);
-       if (err)
+       err = break_lease(inode, O_NONBLOCK | ((access & MAY_WRITE) ? FMODE_WRITE : 0));
+       if (err == -EWOULDBLOCK)
+               err = -ETIMEDOUT;
+       if (err) /* NOMEM or WOULDBLOCK */
                goto out_nfserr;
 
        if (access & MAY_WRITE) {
-               err = get_write_access(inode);
-               if (err)
-                       goto out_nfserr;
-
                flags = O_WRONLY|O_LARGEFILE;
 
                DQUOT_INIT(inode);
        }
-
-       err = open_private_file(filp, dentry, flags);
-       if (!err) {
-               filp->f_vfsmnt = fhp->fh_export->ex_mnt;
-       } else if (access & MAY_WRITE)
-               put_write_access(inode);
-
+       *filp = dentry_open(dget(dentry), mntget(fhp->fh_export->ex_mnt), flags);
+       if (IS_ERR(*filp))
+               err = PTR_ERR(*filp);
 out_nfserr:
        if (err)
                err = nfserrno(err);
@@ -515,12 +699,7 @@ out:
 void
 nfsd_close(struct file *filp)
 {
-       struct dentry   *dentry = filp->f_dentry;
-       struct inode    *inode = dentry->d_inode;
-
-       close_private_file(filp);
-       if (filp->f_mode & FMODE_WRITE)
-               put_write_access(inode);
+       fput(filp);
 }
 
 /*
@@ -561,7 +740,7 @@ nfsd_sync_dir(struct dentry *dp)
  * Obtain the readahead parameters for the file
  * specified by (dev, ino).
  */
-static spinlock_t ra_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(ra_lock);
 
 static inline struct raparms *
 nfsd_get_raparms(dev_t dev, ino_t ino)
@@ -586,7 +765,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino)
        ra = *frap;
        ra->p_dev = dev;
        ra->p_ino = ino;
-       memset(&ra->p_ra, 0, sizeof(ra->p_ra));
+       ra->p_set = 0;
 found:
        if (rap != &raparm_cache) {
                *rap = ra->p_next;
@@ -608,7 +787,7 @@ static int
 nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset , unsigned long size)
 {
        unsigned long count = desc->count;
-       struct svc_rqst *rqstp = (struct svc_rqst *)desc->buf;
+       struct svc_rqst *rqstp = desc->arg.data;
 
        if (size > count)
                size = count;
@@ -638,19 +817,19 @@ nfsd_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset
  */
 int
 nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
-          struct iovec *vec, int vlen, unsigned long *count)
+          struct kvec *vec, int vlen, unsigned long *count)
 {
        struct raparms  *ra;
        mm_segment_t    oldfs;
        int             err;
-       struct file     file;
+       struct file     *file;
        struct inode    *inode;
 
        err = nfsd_open(rqstp, fhp, S_IFREG, MAY_READ, &file);
        if (err)
                goto out;
        err = nfserr_perm;
-       inode = file.f_dentry->d_inode;
+       inode = file->f_dentry->d_inode;
 #ifdef MSNFS
        if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
                (!lock_may_read(inode, offset, *count)))
@@ -659,33 +838,39 @@ nfsd_read(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 
        /* Get readahead parameters */
        ra = nfsd_get_raparms(inode->i_sb->s_dev, inode->i_ino);
-       if (ra)
-               file.f_ra = ra->p_ra;
 
-       if (file.f_op->sendfile) {
+       if (ra && ra->p_set)
+               file->f_ra = ra->p_ra;
+
+       if (file->f_op->sendfile) {
                svc_pushback_unused_pages(rqstp);
-               err = file.f_op->sendfile(&file, &offset, *count,
+               err = file->f_op->sendfile(file, &offset, *count,
                                                 nfsd_read_actor, rqstp);
        } else {
                oldfs = get_fs();
                set_fs(KERNEL_DS);
-               err = vfs_readv(&file, vec, vlen, &offset);
+               err = vfs_readv(file, (struct iovec __user *)vec, vlen, &offset);
                set_fs(oldfs);
        }
 
        /* Write back readahead params */
-       if (ra)
-               ra->p_ra = file.f_ra;
+       if (ra) {
+               spin_lock(&ra_lock);
+               ra->p_ra = file->f_ra;
+               ra->p_set = 1;
+               ra->p_count--;
+               spin_unlock(&ra_lock);
+       }
 
        if (err >= 0) {
                nfsdstats.io_read += err;
                *count = err;
                err = 0;
-               dnotify_parent(file.f_dentry, DN_ACCESS);
+               dnotify_parent(file->f_dentry, DN_ACCESS);
        } else 
                err = nfserrno(err);
 out_close:
-       nfsd_close(&file);
+       nfsd_close(file);
 out:
        return err;
 }
@@ -697,11 +882,11 @@ out:
  */
 int
 nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
-                               struct iovec *vec, int vlen,
+                               struct kvec *vec, int vlen,
                                unsigned long cnt, int *stablep)
 {
        struct svc_export       *exp;
-       struct file             file;
+       struct file             *file;
        struct dentry           *dentry;
        struct inode            *inode;
        mm_segment_t            oldfs;
@@ -717,11 +902,11 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 
 #ifdef MSNFS
        if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) &&
-               (!lock_may_write(file.f_dentry->d_inode, offset, cnt)))
+               (!lock_may_write(file->f_dentry->d_inode, offset, cnt)))
                goto out_close;
 #endif
 
-       dentry = file.f_dentry;
+       dentry = file->f_dentry;
        inode = dentry->d_inode;
        exp   = fhp->fh_export;
 
@@ -734,7 +919,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
         * flushing the data to disk is handled separately below.
         */
 
-       if (file.f_op->fsync == 0) {/* COMMIT3 cannot work */
+       if (file->f_op->fsync == 0) {/* COMMIT3 cannot work */
               stable = 2;
               *stablep = 2; /* FILE_SYNC */
        }
@@ -742,15 +927,15 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
        if (!EX_ISSYNC(exp))
                stable = 0;
        if (stable && !EX_WGATHER(exp))
-               file.f_flags |= O_SYNC;
+               file->f_flags |= O_SYNC;
 
        /* Write the data. */
        oldfs = get_fs(); set_fs(KERNEL_DS);
-       err = vfs_writev(&file, vec, vlen, &offset);
+       err = vfs_writev(file, (struct iovec __user *)vec, vlen, &offset);
        set_fs(oldfs);
        if (err >= 0) {
                nfsdstats.io_write += cnt;
-               dnotify_parent(file.f_dentry, DN_MODIFY);
+               dnotify_parent(file->f_dentry, DN_MODIFY);
        }
 
        /* clear setuid/setgid flag after write */
@@ -790,7 +975,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
 
                        if (inode->i_state & I_DIRTY) {
                                dprintk("nfsd: write sync %d\n", current->pid);
-                               nfsd_sync(&file);
+                               nfsd_sync(file);
                        }
 #if 0
                        wake_up(&inode->i_wait);
@@ -806,7 +991,7 @@ nfsd_write(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset,
        else 
                err = nfserrno(err);
 out_close:
-       nfsd_close(&file);
+       nfsd_close(file);
 out:
        return err;
 }
@@ -825,7 +1010,7 @@ int
 nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                loff_t offset, unsigned long count)
 {
-       struct file     file;
+       struct file     *file;
        int             err;
 
        if ((u64)count > ~(u64)offset)
@@ -834,14 +1019,14 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if ((err = nfsd_open(rqstp, fhp, S_IFREG, MAY_WRITE, &file)) != 0)
                return err;
        if (EX_ISSYNC(fhp->fh_export)) {
-               if (file.f_op && file.f_op->fsync) {
-                       nfsd_sync(&file);
+               if (file->f_op && file->f_op->fsync) {
+                       nfsd_sync(file);
                } else {
                        err = nfserr_notsupp;
                }
        }
 
-       nfsd_close(&file);
+       nfsd_close(file);
        return err;
 }
 #endif /* CONFIG_NFSD_V3 */
@@ -859,7 +1044,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                char *fname, int flen, struct iattr *iap,
                int type, dev_t rdev, struct svc_fh *resfhp)
 {
-       struct dentry   *dentry, *dchild;
+       struct dentry   *dentry, *dchild = NULL;
        struct inode    *dirp;
        int             err;
 
@@ -896,7 +1081,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
                        goto out;
        } else {
                /* called from nfsd_proc_create */
-               dchild = resfhp->fh_dentry;
+               dchild = dget(resfhp->fh_dentry);
                if (!fhp->fh_locked) {
                        /* not actually possible */
                        printk(KERN_ERR
@@ -965,6 +1150,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (!err)
                err = fh_update(resfhp);
 out:
+       if (dchild && !IS_ERR(dchild))
+               dput(dchild);
        return err;
 
 out_nfserr:
@@ -982,7 +1169,7 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
                struct svc_fh *resfhp, int createmode, u32 *verifier,
                int *truncp)
 {
-       struct dentry   *dentry, *dchild;
+       struct dentry   *dentry, *dchild = NULL;
        struct inode    *dirp;
        int             err;
        __u32           v_mtime=0, v_atime=0;
@@ -1111,6 +1298,8 @@ nfsd_create_v3(struct svc_rqst *rqstp, struct svc_fh *fhp,
 
  out:
        fh_unlock(fhp);
+       if (dchild && !IS_ERR(dchild))
+               dput(dchild);
        return err;
  
  out_nfserr:
@@ -1177,6 +1366,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
 {
        struct dentry   *dentry, *dnew;
        int             err, cerr;
+       umode_t         mode;
 
        err = nfserr_noent;
        if (!flen || !plen)
@@ -1195,6 +1385,11 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
        if (IS_ERR(dnew))
                goto out_nfserr;
 
+       mode = S_IALLUGO;
+       /* Only the MODE ATTRibute is even vaguely meaningful */
+       if (iap && (iap->ia_valid & ATTR_MODE))
+               mode = iap->ia_mode & S_IALLUGO;
+
        if (unlikely(path[plen] != 0)) {
                char *path_alloced = kmalloc(plen+1, GFP_KERNEL);
                if (path_alloced == NULL)
@@ -1202,34 +1397,21 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
                else {
                        strncpy(path_alloced, path, plen);
                        path_alloced[plen] = 0;
-                       err = vfs_symlink(dentry->d_inode, dnew, path_alloced);
+                       err = vfs_symlink(dentry->d_inode, dnew, path_alloced, mode);
                        kfree(path_alloced);
                }
        } else
-               err = vfs_symlink(dentry->d_inode, dnew, path);
+               err = vfs_symlink(dentry->d_inode, dnew, path, mode);
 
        if (!err) {
                if (EX_ISSYNC(fhp->fh_export))
                        nfsd_sync_dir(dentry);
-               if (iap) {
-                       iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/;
-                       if (iap->ia_valid) {
-                               iap->ia_valid |= ATTR_CTIME;
-                               iap->ia_mode = (iap->ia_mode&S_IALLUGO)
-                                       | S_IFLNK;
-                               err = notify_change(dnew, iap);
-                               if (err)
-                                       err = nfserrno(err);
-                               else if (EX_ISSYNC(fhp->fh_export))
-                                       write_inode_now(dentry->d_inode, 1);
-                      }
-               }
        } else
                err = nfserrno(err);
        fh_unlock(fhp);
 
-       /* Compose the fh so the dentry will be freed ... */
        cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp);
+       dput(dnew);
        if (err==0) err = cerr;
 out:
        return err;
@@ -1467,16 +1649,18 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
             struct readdir_cd *cdp, encode_dent_fn func)
 {
        int             err;
-       struct file     file;
+       struct file     *file;
        loff_t          offset = *offsetp;
 
        err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file);
        if (err)
                goto out;
-       if (offset > ~(u32) 0)
-               goto out_close;
 
-       file.f_pos = offset;
+       offset = vfs_llseek(file, offset, 0);
+       if (offset < 0) {
+               err = nfserrno((int)offset);
+               goto out_close;
+       }
 
        /*
         * Read the directory entries. This silly loop is necessary because
@@ -1486,18 +1670,18 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
 
        do {
                cdp->err = nfserr_eof; /* will be cleared on successful read */
-               err = vfs_readdir(&file, (filldir_t) func, cdp);
+               err = vfs_readdir(file, (filldir_t) func, cdp);
        } while (err >=0 && cdp->err == nfs_ok);
        if (err)
                err = nfserrno(err);
        else
                err = cdp->err;
-       *offsetp = file.f_pos;
+       *offsetp = vfs_llseek(file, 0, 1);
 
        if (err == nfserr_eof || err == nfserr_toosmall)
                err = nfs_ok; /* can still be found in ->err */
 out_close:
-       nfsd_close(&file);
+       nfsd_close(file);
 out:
        return err;
 }