[PATCH] knfsd: check error status from vfs_getattr and i_op->fsync
authorDavid Shaw <dshaw@jabberwocky.com>
Fri, 6 Jan 2006 08:19:58 +0000 (00:19 -0800)
committerLinus Torvalds <torvalds@g5.osdl.org>
Fri, 6 Jan 2006 16:33:59 +0000 (08:33 -0800)
Both vfs_getattr and i_op->fsync return error statuses which nfsd was
largely ignoring.  This as noticed when exporting directories using fuse.

This patch cleans up most of the offences, which involves moving the call
to vfs_getattr out of the xdr encoding routines (where it is too late to
report an error) into the main NFS procedure handling routines.

There is still a called to vfs_gettattr (related to the ACL code) where the
status is ignored, and called to nfsd_sync_dir don't check return status
either.

Signed-off-by: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
fs/nfsd/nfs3proc.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfsxdr.c
fs/nfsd/vfs.c
include/linux/nfsd/xdr.h
include/linux/nfsd/xdr3.h

index 041380f..6d2dfed 100644 (file)
@@ -56,13 +56,20 @@ static int
 nfsd3_proc_getattr(struct svc_rqst *rqstp, struct nfsd_fhandle  *argp,
                                           struct nfsd3_attrstat *resp)
 {
-       int     nfserr;
+       int     err, nfserr;
 
        dprintk("nfsd: GETATTR(3)  %s\n",
-                               SVCFH_fmt(&argp->fh));
+               SVCFH_fmt(&argp->fh));
 
        fh_copy(&resp->fh, &argp->fh);
        nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP);
+       if (nfserr)
+               RETURN_STATUS(nfserr);
+
+       err = vfs_getattr(resp->fh.fh_export->ex_mnt,
+                         resp->fh.fh_dentry, &resp->stat);
+       nfserr = nfserrno(err);
+
        RETURN_STATUS(nfserr);
 }
 
index 9147b85..243d94b 100644 (file)
@@ -154,37 +154,34 @@ decode_sattr3(u32 *p, struct iattr *iap)
 }
 
 static inline u32 *
-encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+encode_fattr3(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp,
+             struct kstat *stat)
 {
-       struct vfsmount *mnt = fhp->fh_export->ex_mnt;
        struct dentry   *dentry = fhp->fh_dentry;
-       struct kstat stat;
        struct timespec time;
 
-       vfs_getattr(mnt, dentry, &stat);
-
-       *p++ = htonl(nfs3_ftypes[(stat.mode & S_IFMT) >> 12]);
-       *p++ = htonl((u32) stat.mode);
-       *p++ = htonl((u32) stat.nlink);
-       *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid));
-       *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid));
-       if (S_ISLNK(stat.mode) && stat.size > NFS3_MAXPATHLEN) {
+       *p++ = htonl(nfs3_ftypes[(stat->mode & S_IFMT) >> 12]);
+       *p++ = htonl((u32) stat->mode);
+       *p++ = htonl((u32) stat->nlink);
+       *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid));
+       *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid));
+       if (S_ISLNK(stat->mode) && stat->size > NFS3_MAXPATHLEN) {
                p = xdr_encode_hyper(p, (u64) NFS3_MAXPATHLEN);
        } else {
-               p = xdr_encode_hyper(p, (u64) stat.size);
+               p = xdr_encode_hyper(p, (u64) stat->size);
        }
-       p = xdr_encode_hyper(p, ((u64)stat.blocks) << 9);
-       *p++ = htonl((u32) MAJOR(stat.rdev));
-       *p++ = htonl((u32) MINOR(stat.rdev));
+       p = xdr_encode_hyper(p, ((u64)stat->blocks) << 9);
+       *p++ = htonl((u32) MAJOR(stat->rdev));
+       *p++ = htonl((u32) MINOR(stat->rdev));
        if (is_fsid(fhp, rqstp->rq_reffh))
                p = xdr_encode_hyper(p, (u64) fhp->fh_export->ex_fsid);
        else
-               p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat.dev));
-       p = xdr_encode_hyper(p, (u64) stat.ino);
-       p = encode_time3(p, &stat.atime);
+               p = xdr_encode_hyper(p, (u64) huge_encode_dev(stat->dev));
+       p = xdr_encode_hyper(p, (u64) stat->ino);
+       p = encode_time3(p, &stat->atime);
        lease_get_mtime(dentry->d_inode, &time); 
        p = encode_time3(p, &time);
-       p = encode_time3(p, &stat.ctime);
+       p = encode_time3(p, &stat->ctime);
 
        return p;
 }
@@ -232,8 +229,14 @@ encode_post_op_attr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
        struct dentry *dentry = fhp->fh_dentry;
        if (dentry && dentry->d_inode != NULL) {
-               *p++ = xdr_one;         /* attributes follow */
-               return encode_fattr3(rqstp, p, fhp);
+               int err;
+               struct kstat stat;
+
+               err = vfs_getattr(fhp->fh_export->ex_mnt, dentry, &stat);
+               if (!err) {
+                       *p++ = xdr_one;         /* attributes follow */
+                       return encode_fattr3(rqstp, p, fhp, &stat);
+               }
        }
        *p++ = xdr_zero;
        return p;
@@ -616,7 +619,7 @@ nfs3svc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd3_attrstat *resp)
 {
        if (resp->status == 0)
-               p = encode_fattr3(rqstp, p, &resp->fh);
+               p = encode_fattr3(rqstp, p, &resp->fh, &resp->stat);
        return xdr_ressize_check(rqstp, p);
 }
 
index b45999f..aa7bb41 100644 (file)
@@ -152,46 +152,44 @@ decode_sattr(u32 *p, struct iattr *iap)
 }
 
 static inline u32 *
-encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
+encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp,
+            struct kstat *stat)
 {
-       struct vfsmount *mnt = fhp->fh_export->ex_mnt;
        struct dentry   *dentry = fhp->fh_dentry;
-       struct kstat stat;
        int type;
        struct timespec time;
 
-       vfs_getattr(mnt, dentry, &stat);
-       type = (stat.mode & S_IFMT);
+       type = (stat->mode & S_IFMT);
 
        *p++ = htonl(nfs_ftypes[type >> 12]);
-       *p++ = htonl((u32) stat.mode);
-       *p++ = htonl((u32) stat.nlink);
-       *p++ = htonl((u32) nfsd_ruid(rqstp, stat.uid));
-       *p++ = htonl((u32) nfsd_rgid(rqstp, stat.gid));
+       *p++ = htonl((u32) stat->mode);
+       *p++ = htonl((u32) stat->nlink);
+       *p++ = htonl((u32) nfsd_ruid(rqstp, stat->uid));
+       *p++ = htonl((u32) nfsd_rgid(rqstp, stat->gid));
 
-       if (S_ISLNK(type) && stat.size > NFS_MAXPATHLEN) {
+       if (S_ISLNK(type) && stat->size > NFS_MAXPATHLEN) {
                *p++ = htonl(NFS_MAXPATHLEN);
        } else {
-               *p++ = htonl((u32) stat.size);
+               *p++ = htonl((u32) stat->size);
        }
-       *p++ = htonl((u32) stat.blksize);
+       *p++ = htonl((u32) stat->blksize);
        if (S_ISCHR(type) || S_ISBLK(type))
-               *p++ = htonl(new_encode_dev(stat.rdev));
+               *p++ = htonl(new_encode_dev(stat->rdev));
        else
                *p++ = htonl(0xffffffff);
-       *p++ = htonl((u32) stat.blocks);
+       *p++ = htonl((u32) stat->blocks);
        if (is_fsid(fhp, rqstp->rq_reffh))
                *p++ = htonl((u32) fhp->fh_export->ex_fsid);
        else
-               *p++ = htonl(new_encode_dev(stat.dev));
-       *p++ = htonl((u32) stat.ino);
-       *p++ = htonl((u32) stat.atime.tv_sec);
-       *p++ = htonl(stat.atime.tv_nsec ? stat.atime.tv_nsec / 1000 : 0);
+               *p++ = htonl(new_encode_dev(stat->dev));
+       *p++ = htonl((u32) stat->ino);
+       *p++ = htonl((u32) stat->atime.tv_sec);
+       *p++ = htonl(stat->atime.tv_nsec ? stat->atime.tv_nsec / 1000 : 0);
        lease_get_mtime(dentry->d_inode, &time); 
        *p++ = htonl((u32) time.tv_sec);
        *p++ = htonl(time.tv_nsec ? time.tv_nsec / 1000 : 0); 
-       *p++ = htonl((u32) stat.ctime.tv_sec);
-       *p++ = htonl(stat.ctime.tv_nsec ? stat.ctime.tv_nsec / 1000 : 0);
+       *p++ = htonl((u32) stat->ctime.tv_sec);
+       *p++ = htonl(stat->ctime.tv_nsec ? stat->ctime.tv_nsec / 1000 : 0);
 
        return p;
 }
@@ -199,7 +197,9 @@ encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 /* Helper function for NFSv2 ACL code */
 u32 *nfs2svc_encode_fattr(struct svc_rqst *rqstp, u32 *p, struct svc_fh *fhp)
 {
-       return encode_fattr(rqstp, p, fhp);
+       struct kstat stat;
+       vfs_getattr(fhp->fh_export->ex_mnt, fhp->fh_dentry, &stat);
+       return encode_fattr(rqstp, p, fhp, &stat);
 }
 
 /*
@@ -394,7 +394,7 @@ int
 nfssvc_encode_attrstat(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd_attrstat *resp)
 {
-       p = encode_fattr(rqstp, p, &resp->fh);
+       p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
        return xdr_ressize_check(rqstp, p);
 }
 
@@ -403,7 +403,7 @@ nfssvc_encode_diropres(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd_diropres *resp)
 {
        p = encode_fh(p, &resp->fh);
-       p = encode_fattr(rqstp, p, &resp->fh);
+       p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
        return xdr_ressize_check(rqstp, p);
 }
 
@@ -428,7 +428,7 @@ int
 nfssvc_encode_readres(struct svc_rqst *rqstp, u32 *p,
                                        struct nfsd_readres *resp)
 {
-       p = encode_fattr(rqstp, p, &resp->fh);
+       p = encode_fattr(rqstp, p, &resp->fh, &resp->stat);
        *p++ = htonl(resp->count);
        xdr_ressize_check(rqstp, p);
 
index af7c3c3..f83ab4c 100644 (file)
@@ -717,27 +717,33 @@ nfsd_close(struct file *filp)
  * As this calls fsync (not fdatasync) there is no need for a write_inode
  * after it.
  */
-static inline void nfsd_dosync(struct file *filp, struct dentry *dp,
-                              struct file_operations *fop)
+static inline int nfsd_dosync(struct file *filp, struct dentry *dp,
+                             struct file_operations *fop)
 {
        struct inode *inode = dp->d_inode;
        int (*fsync) (struct file *, struct dentry *, int);
+       int err = nfs_ok;
 
        filemap_fdatawrite(inode->i_mapping);
        if (fop && (fsync = fop->fsync))
-               fsync(filp, dp, 0);
+               err=fsync(filp, dp, 0);
        filemap_fdatawait(inode->i_mapping);
+
+       return nfserrno(err);
 }
        
 
-static void
+static int
 nfsd_sync(struct file *filp)
 {
+        int err;
        struct inode *inode = filp->f_dentry->d_inode;
        dprintk("nfsd: sync file %s\n", filp->f_dentry->d_name.name);
        down(&inode->i_sem);
-       nfsd_dosync(filp, filp->f_dentry, filp->f_op);
+       err=nfsd_dosync(filp, filp->f_dentry, filp->f_op);
        up(&inode->i_sem);
+
+       return err;
 }
 
 void
@@ -962,7 +968,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
 
                        if (inode->i_state & I_DIRTY) {
                                dprintk("nfsd: write sync %d\n", current->pid);
-                               nfsd_sync(file);
+                               err=nfsd_sync(file);
                        }
 #if 0
                        wake_up(&inode->i_wait);
@@ -1066,7 +1072,7 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp,
                return err;
        if (EX_ISSYNC(fhp->fh_export)) {
                if (file->f_op && file->f_op->fsync) {
-                       nfsd_sync(file);
+                       err = nfsd_sync(file);
                } else {
                        err = nfserr_notsupp;
                }
index 130d4f5..3f4f714 100644 (file)
@@ -88,10 +88,12 @@ struct nfsd_readdirargs {
 
 struct nfsd_attrstat {
        struct svc_fh           fh;
+       struct kstat            stat;
 };
 
 struct nfsd_diropres  {
        struct svc_fh           fh;
+       struct kstat            stat;
 };
 
 struct nfsd_readlinkres {
@@ -101,6 +103,7 @@ struct nfsd_readlinkres {
 struct nfsd_readres {
        struct svc_fh           fh;
        unsigned long           count;
+       struct kstat            stat;
 };
 
 struct nfsd_readdirres {
index 3c2a71b..a432274 100644 (file)
@@ -126,6 +126,7 @@ struct nfsd3_setaclargs {
 struct nfsd3_attrstat {
        __u32                   status;
        struct svc_fh           fh;
+       struct kstat            stat;
 };
 
 /* LOOKUP, CREATE, MKDIR, SYMLINK, MKNOD */