Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / fs / nfsd / nfs4proc.c
index 5583541..ee4eff2 100644 (file)
@@ -45,6 +45,7 @@
 #include <linux/param.h>
 #include <linux/major.h>
 #include <linux/slab.h>
+#include <linux/file.h>
 
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
@@ -117,7 +118,6 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o
 
                /* set reply cache */
                fh_dup2(current_fh, &resfh);
-               /* XXXJBF: keep a saved svc_fh struct instead?? */
                open->op_stateowner->so_replay.rp_openfh_len =
                        resfh.fh_handle.fh_size;
                memcpy(open->op_stateowner->so_replay.rp_openfh,
@@ -153,7 +153,7 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
                current_fh->fh_handle.fh_size);
 
        open->op_truncate = (open->op_iattr.ia_valid & ATTR_SIZE) &&
-       !open->op_iattr.ia_size;
+               (open->op_iattr.ia_size == 0);
 
        status = do_open_permission(rqstp, current_fh, open);
 
@@ -161,28 +161,18 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_
 }
 
 
-/*
- * nfs4_unlock_state() called in encode
- */
 static inline int
-nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open)
+nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open, struct nfs4_stateowner **replay_owner)
 {
        int status;
        dprintk("NFSD: nfsd4_open filename %.*s op_stateowner %p\n",
                (int)open->op_fname.len, open->op_fname.data,
                open->op_stateowner);
 
-       if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
-               return nfserr_grace;
-
-       if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
-               return nfserr_no_grace;
-
        /* This check required by spec. */
        if (open->op_create && open->op_claim_type != NFS4_OPEN_CLAIM_NULL)
                return nfserr_inval;
 
-       open->op_stateowner = NULL;
        nfs4_lock_state();
 
        /* check seqid for replay. set nfs4_owner */
@@ -201,30 +191,57 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
                        status = NFSERR_REPLAY_ME;
        }
        if (status)
-               return status;
-       if (open->op_claim_type == NFS4_OPEN_CLAIM_NULL) {
-       /*
-        * This block of code will (1) set CURRENT_FH to the file being opened,
-        * creating it if necessary, (2) set open->op_cinfo, 
-        * (3) set open->op_truncate if the file is to be truncated 
-        * after opening, (4) do permission checking.
-        */
-               status = do_open_lookup(rqstp, current_fh, open);
-               if (status)
-                       return status;
-       } else if (open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) {
-       /*
-       * The CURRENT_FH is already set to the file being opened. This
-       * block of code will (1) set open->op_cinfo, (2) set
-       * open->op_truncate if the file is to be truncated after opening,
-       * (3) do permission checking.
-       */
-               status = do_open_fhandle(rqstp, current_fh, open);
-               if (status)
-                       return status;
-       } else {
-               printk("NFSD: unsupported OPEN claim type\n");
-               return nfserr_inval;
+               goto out;
+
+       /* Openowner is now set, so sequence id will get bumped.  Now we need
+        * these checks before we do any creates: */
+       status = nfserr_grace;
+       if (nfs4_in_grace() && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS)
+               goto out;
+       status = nfserr_no_grace;
+       if (!nfs4_in_grace() && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS)
+               goto out;
+
+       switch (open->op_claim_type) {
+               case NFS4_OPEN_CLAIM_DELEGATE_CUR:
+                       status = nfserr_inval;
+                       if (open->op_create)
+                               goto out;
+                       /* fall through */
+               case NFS4_OPEN_CLAIM_NULL:
+                       /*
+                        * (1) set CURRENT_FH to the file being opened,
+                        * creating it if necessary, (2) set open->op_cinfo,
+                        * (3) set open->op_truncate if the file is to be
+                        * truncated after opening, (4) do permission checking.
+                        */
+                       status = do_open_lookup(rqstp, current_fh, open);
+                       if (status)
+                               goto out;
+                       break;
+               case NFS4_OPEN_CLAIM_PREVIOUS:
+                       open->op_stateowner->so_confirmed = 1;
+                       /*
+                        * The CURRENT_FH is already set to the file being
+                        * opened.  (1) set open->op_cinfo, (2) set
+                        * open->op_truncate if the file is to be truncated
+                        * after opening, (3) do permission checking.
+                       */
+                       status = do_open_fhandle(rqstp, current_fh, open);
+                       if (status)
+                               goto out;
+                       break;
+               case NFS4_OPEN_CLAIM_DELEGATE_PREV:
+                       open->op_stateowner->so_confirmed = 1;
+                       printk("NFSD: unsupported OPEN claim type %d\n",
+                               open->op_claim_type);
+                       status = nfserr_notsupp;
+                       goto out;
+               default:
+                       printk("NFSD: Invalid OPEN claim type %d\n",
+                               open->op_claim_type);
+                       status = nfserr_inval;
+                       goto out;
        }
        /*
         * nfsd4_process_open2() does the actual opening of the file.  If
@@ -232,9 +249,13 @@ nfsd4_open(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open
         * set, (2) sets open->op_stateid, (3) sets open->op_delegation.
         */
        status = nfsd4_process_open2(rqstp, current_fh, open);
-       if (status)
-               return status;
-       return 0;
+out:
+       if (open->op_stateowner) {
+               nfs4_get_stateowner(open->op_stateowner);
+               *replay_owner = open->op_stateowner;
+       }
+       nfs4_unlock_state();
+       return status;
 }
 
 /*
@@ -267,8 +288,6 @@ nfsd4_putrootfh(struct svc_rqst *rqstp, struct svc_fh *current_fh)
        fh_put(current_fh);
        status = exp_pseudoroot(rqstp->rq_client, current_fh,
                              &rqstp->rq_chandle);
-       if (!status)
-               status = nfsd_setuser(rqstp, current_fh->fh_export);
        return status;
 }
 
@@ -461,64 +480,26 @@ nfsd4_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_loo
        return nfsd_lookup(rqstp, current_fh, lookup->lo_name, lookup->lo_len, current_fh);
 }
 
-static inline int
-access_bits_permit_read(unsigned long access_bmap)
-{
-       return test_bit(NFS4_SHARE_ACCESS_READ, &access_bmap) ||
-               test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap);
-}
-
-static inline int
-access_bits_permit_write(unsigned long access_bmap)
-{
-       return test_bit(NFS4_SHARE_ACCESS_WRITE, &access_bmap) ||
-               test_bit(NFS4_SHARE_ACCESS_BOTH, &access_bmap);
-}
-
 static inline int
 nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read)
 {
-       struct nfs4_stateid *stp;
        int status;
 
        /* no need to check permission - this will be done in nfsd_read() */
-       if (nfs4_in_grace())
-               return nfserr_grace;
 
+       read->rd_filp = NULL;
        if (read->rd_offset >= OFFSET_MAX)
                return nfserr_inval;
 
        nfs4_lock_state();
-       status = nfs_ok;
-       /* For stateid -1, we don't check share reservations.  */
-       if (ONE_STATEID(&read->rd_stateid)) {
-               dprintk("NFSD: nfsd4_read: -1 stateid...\n");
-               goto out;
-       }
-       /*
-       * For stateid 0, the client doesn't have to have the file open, but
-       * we still check for share reservation conflicts. 
-       */
-       if (ZERO_STATEID(&read->rd_stateid)) {
-               dprintk("NFSD: nfsd4_read: zero stateid...\n");
-               if ((status = nfs4_share_conflict(current_fh, NFS4_SHARE_DENY_READ))) {
-                       dprintk("NFSD: nfsd4_read: conflicting share reservation!\n");
-                       goto out;
-               }
-               status = nfs_ok;
-               goto out;
-       }
        /* check stateid */
-       if ((status = nfs4_preprocess_stateid_op(current_fh, &read->rd_stateid, 
-                                       CHECK_FH | RDWR_STATE, &stp))) {
+       if ((status = nfs4_preprocess_stateid_op(current_fh, &read->rd_stateid,
+                               CHECK_FH | RD_STATE, &read->rd_filp))) {
                dprintk("NFSD: nfsd4_read: couldn't process stateid!\n");
                goto out;
        }
-       status = nfserr_openmode;
-       if (!access_bits_permit_read(stp->st_access_bmap)) {
-               dprintk("NFSD: nfsd4_read: file not opened for read!\n");
-               goto out;
-       }
+       if (read->rd_filp)
+               get_file(read->rd_filp);
        status = nfs_ok;
 out:
        nfs4_unlock_state();
@@ -563,6 +544,8 @@ nfsd4_remove(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_rem
 {
        int status;
 
+       if (nfs4_in_grace())
+               return nfserr_grace;
        status = nfsd_unlink(rqstp, current_fh, 0, remove->rm_name, remove->rm_namelen);
        if (status == nfserr_symlink)
                return nfserr_notdir;
@@ -581,6 +564,9 @@ nfsd4_rename(struct svc_rqst *rqstp, struct svc_fh *current_fh,
 
        if (!save_fh->fh_dentry)
                return status;
+       if (nfs4_in_grace() && !(save_fh->fh_export->ex_flags
+                                       & NFSEXP_NOSUBTREECHECK))
+               return nfserr_grace;
        status = nfsd_rename(rqstp, save_fh, rename->rn_sname,
                             rename->rn_snamelen, current_fh,
                             rename->rn_tname, rename->rn_tnamelen);
@@ -606,106 +592,68 @@ nfsd4_rename(struct svc_rqst *rqstp, struct svc_fh *current_fh,
 static inline int
 nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_setattr *setattr)
 {
-       struct nfs4_stateid *stp;
        int status = nfs_ok;
 
-       if (nfs4_in_grace())
-               return nfserr_grace;
-
-       if (!current_fh->fh_dentry)
-               return nfserr_nofilehandle;
-
-       status = nfs_ok;
        if (setattr->sa_iattr.ia_valid & ATTR_SIZE) {
-
-               status = nfserr_bad_stateid;
-               if (ZERO_STATEID(&setattr->sa_stateid) || ONE_STATEID(&setattr->sa_stateid)) {
-                       dprintk("NFSD: nfsd4_setattr: magic stateid!\n");
-                       goto out;
-               }
-
                nfs4_lock_state();
-               if ((status = nfs4_preprocess_stateid_op(current_fh, 
-                                               &setattr->sa_stateid, 
-                                               CHECK_FH | RDWR_STATE, &stp))) {
-                       dprintk("NFSD: nfsd4_setattr: couldn't process stateid!\n");
-                       goto out_unlock;
-               }
-               status = nfserr_openmode;
-               if (!access_bits_permit_write(stp->st_access_bmap)) {
-                       dprintk("NFSD: nfsd4_setattr: not opened for write!\n");
-                       goto out_unlock;
-               }
+               status = nfs4_preprocess_stateid_op(current_fh,
+                       &setattr->sa_stateid, CHECK_FH | WR_STATE, NULL);
                nfs4_unlock_state();
+               if (status) {
+                       dprintk("NFSD: nfsd4_setattr: couldn't process stateid!");
+                       return status;
+               }
        }
        status = nfs_ok;
        if (setattr->sa_acl != NULL)
                status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl);
        if (status)
-               goto out;
+               return status;
        status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr,
                                0, (time_t)0);
-out:
-       return status;
-out_unlock:
-       nfs4_unlock_state();
        return status;
 }
 
 static inline int
 nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_write *write)
 {
-       struct nfs4_stateid *stp;
        stateid_t *stateid = &write->wr_stateid;
+       struct file *filp = NULL;
        u32 *p;
        int status = nfs_ok;
 
-       if (nfs4_in_grace())
-               return nfserr_grace;
-
        /* no need to check permission - this will be done in nfsd_write() */
 
        if (write->wr_offset >= OFFSET_MAX)
                return nfserr_inval;
 
        nfs4_lock_state();
-       if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) {
-               dprintk("NFSD: nfsd4_write: zero stateid...\n");
-               if ((status = nfs4_share_conflict(current_fh, NFS4_SHARE_DENY_WRITE))) {
-                       dprintk("NFSD: nfsd4_write: conflicting share reservation!\n");
-                       goto out;
-               }
-               goto zero_stateid;
-       }
-       if ((status = nfs4_preprocess_stateid_op(current_fh, stateid, 
-                                       CHECK_FH | RDWR_STATE, &stp))) {
-               dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
-               goto out;
-       }
+       status = nfs4_preprocess_stateid_op(current_fh, stateid,
+                                       CHECK_FH | WR_STATE, &filp);
+       if (filp)
+               get_file(filp);
+       nfs4_unlock_state();
 
-       status = nfserr_openmode;
-       if (!access_bits_permit_write(stp->st_access_bmap)) {
-               dprintk("NFSD: nfsd4_write: file not open for write!\n");
-               goto out;
+       if (status) {
+               dprintk("NFSD: nfsd4_write: couldn't process stateid!\n");
+               return status;
        }
 
-zero_stateid:
-       nfs4_unlock_state();
        write->wr_bytes_written = write->wr_buflen;
        write->wr_how_written = write->wr_stable_how;
        p = (u32 *)write->wr_verifier.data;
        *p++ = nfssvc_boot.tv_sec;
        *p++ = nfssvc_boot.tv_usec;
 
-       status =  nfsd_write(rqstp, current_fh, write->wr_offset,
-                         write->wr_vec, write->wr_vlen, write->wr_buflen,
-                         &write->wr_how_written);
+       status =  nfsd_write(rqstp, current_fh, filp, write->wr_offset,
+                       write->wr_vec, write->wr_vlen, write->wr_buflen,
+                       &write->wr_how_written);
+       if (filp)
+               fput(filp);
+
        if (status == nfserr_symlink)
                status = nfserr_inval;
        return status;
-out:
-       nfs4_unlock_state();
-       return status;
 }
 
 /* This routine never returns NFS_OK!  If there are no other errors, it
@@ -773,6 +721,12 @@ nfsd4_proc_null(struct svc_rqst *rqstp, void *argp, void *resp)
        return nfs_ok;
 }
 
+static inline void nfsd4_increment_op_stats(u32 opnum)
+{
+       if (opnum >= FIRST_NFS4_OP && opnum <= LAST_NFS4_OP)
+               nfsdstats.nfs4_opcount[opnum]++;
+}
+
 
 /*
  * COMPOUND call.
@@ -785,6 +739,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        struct nfsd4_op *op;
        struct svc_fh   *current_fh = NULL;
        struct svc_fh   *save_fh = NULL;
+       struct nfs4_stateowner *replay_owner = NULL;
        int             slack_space;    /* in words, not bytes! */
        int             status;
 
@@ -820,6 +775,8 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
        while (!status && resp->opcnt < args->opcnt) {
                op = &args->ops[resp->opcnt++];
 
+               dprintk("nfsv4 compound op #%d: %d\n", resp->opcnt, op->opnum);
+
                /*
                 * The XDR decode routines may have pre-set op->status;
                 * for example, if there is a miscellaneous XDR error
@@ -844,17 +801,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                /* All operations except RENEW, SETCLIENTID, RESTOREFH
                * SETCLIENTID_CONFIRM, PUTFH and PUTROOTFH
                * require a valid current filehandle
-               *
-               * SETATTR NOFILEHANDLE error handled in nfsd4_setattr
-               * due to required returned bitmap argument
                */
                if ((!current_fh->fh_dentry) &&
                   !((op->opnum == OP_PUTFH) || (op->opnum == OP_PUTROOTFH) ||
                   (op->opnum == OP_SETCLIENTID) ||
                   (op->opnum == OP_SETCLIENTID_CONFIRM) ||
                   (op->opnum == OP_RENEW) || (op->opnum == OP_RESTOREFH) ||
-                  (op->opnum == OP_RELEASE_LOCKOWNER) ||
-                  (op->opnum == OP_SETATTR))) {
+                  (op->opnum == OP_RELEASE_LOCKOWNER))) {
                        op->status = nfserr_nofilehandle;
                        goto encode_op;
                }
@@ -863,10 +816,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                        op->status = nfsd4_access(rqstp, current_fh, &op->u.access);
                        break;
                case OP_CLOSE:
-                       op->status = nfsd4_close(rqstp, current_fh, &op->u.close);
-                       if (op->u.close.cl_stateowner)
-                               op->replay =
-                                       &op->u.close.cl_stateowner->so_replay;
+                       op->status = nfsd4_close(rqstp, current_fh, &op->u.close, &replay_owner);
                        break;
                case OP_COMMIT:
                        op->status = nfsd4_commit(rqstp, current_fh, &op->u.commit);
@@ -874,6 +824,9 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                case OP_CREATE:
                        op->status = nfsd4_create(rqstp, current_fh, &op->u.create);
                        break;
+               case OP_DELEGRETURN:
+                       op->status = nfsd4_delegreturn(rqstp, current_fh, &op->u.delegreturn);
+                       break;
                case OP_GETATTR:
                        op->status = nfsd4_getattr(rqstp, current_fh, &op->u.getattr);
                        break;
@@ -884,19 +837,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                        op->status = nfsd4_link(rqstp, current_fh, save_fh, &op->u.link);
                        break;
                case OP_LOCK:
-                       op->status = nfsd4_lock(rqstp, current_fh, &op->u.lock);
-                       if (op->u.lock.lk_stateowner)
-                               op->replay =
-                                       &op->u.lock.lk_stateowner->so_replay;
+                       op->status = nfsd4_lock(rqstp, current_fh, &op->u.lock, &replay_owner);
                        break;
                case OP_LOCKT:
                        op->status = nfsd4_lockt(rqstp, current_fh, &op->u.lockt);
                        break;
                case OP_LOCKU:
-                       op->status = nfsd4_locku(rqstp, current_fh, &op->u.locku);
-                       if (op->u.locku.lu_stateowner)
-                               op->replay =
-                                       &op->u.locku.lu_stateowner->so_replay;
+                       op->status = nfsd4_locku(rqstp, current_fh, &op->u.locku, &replay_owner);
                        break;
                case OP_LOOKUP:
                        op->status = nfsd4_lookup(rqstp, current_fh, &op->u.lookup);
@@ -910,22 +857,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
                                op->status = nfs_ok;
                        break;
                case OP_OPEN:
-                       op->status = nfsd4_open(rqstp, current_fh, &op->u.open);
-                       if (op->u.open.op_stateowner)
-                               op->replay =
-                                       &op->u.open.op_stateowner->so_replay;
+                       op->status = nfsd4_open(rqstp, current_fh, &op->u.open, &replay_owner);
                        break;
                case OP_OPEN_CONFIRM:
-                       op->status = nfsd4_open_confirm(rqstp, current_fh, &op->u.open_confirm);
-                       if (op->u.open_confirm.oc_stateowner)
-                               op->replay =
-                                       &op->u.open_confirm.oc_stateowner->so_replay;
+                       op->status = nfsd4_open_confirm(rqstp, current_fh, &op->u.open_confirm, &replay_owner);
                        break;
                case OP_OPEN_DOWNGRADE:
-                       op->status = nfsd4_open_downgrade(rqstp, current_fh, &op->u.open_downgrade);
-                       if (op->u.open_downgrade.od_stateowner)
-                               op->replay =
-                                       &op->u.open_downgrade.od_stateowner->so_replay;
+                       op->status = nfsd4_open_downgrade(rqstp, current_fh, &op->u.open_downgrade, &replay_owner);
                        break;
                case OP_PUTFH:
                        op->status = nfsd4_putfh(rqstp, current_fh, &op->u.putfh);
@@ -984,12 +922,22 @@ nfsd4_proc_compound(struct svc_rqst *rqstp,
 
 encode_op:
                if (op->status == NFSERR_REPLAY_ME) {
+                       op->replay = &replay_owner->so_replay;
                        nfsd4_encode_replay(resp, op);
                        status = op->status = op->replay->rp_status;
                } else {
                        nfsd4_encode_operation(resp, op);
                        status = op->status;
                }
+               if (replay_owner && (replay_owner != (void *)(-1))) {
+                       nfs4_put_stateowner(replay_owner);
+                       replay_owner = NULL;
+               }
+               /* XXX Ugh, we need to get rid of this kind of special case: */
+               if (op->opnum == OP_READ && op->u.read.rd_filp)
+                       fput(op->u.read.rd_filp);
+
+               nfsd4_increment_op_stats(op->opnum);
        }
 
 out:
@@ -1033,7 +981,7 @@ struct nfsd4_voidargs { int dummy; };
  */
 static struct svc_procedure            nfsd_procedures4[2] = {
   PROC(null,    void,          void,           void,     RC_NOCACHE, 1),
-  PROC(compound, compound,     compound,       compound, RC_NOCACHE, NFSD_BUFSIZE)
+  PROC(compound, compound,     compound,       compound, RC_NOCACHE, NFSD_BUFSIZE/4)
 };
 
 struct svc_version     nfsd_version4 = {