#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>
#include <linux/nfs4.h>
#include <linux/nfsd/state.h>
#include <linux/nfsd/xdr4.h>
+#include <linux/nfs4_acl.h>
#define NFSDDBG_FACILITY NFSDDBG_PROC
/* 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,
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);
}
-/*
- * 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 */
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
* 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;
}
/*
status = exp_pseudoroot(rqstp->rq_client, current_fh,
&rqstp->rq_chandle);
if (!status)
- status = nfsd_setuser(rqstp, current_fh->fh_export);
+ status = nfserrno(nfsd_setuser(rqstp, current_fh->fh_export));
return status;
}
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();
{
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;
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);
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");
- return status;
- }
-
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;
- }
- status = nfserr_openmode;
- if (!access_bits_permit_write(stp->st_access_bmap)) {
- dprintk("NFSD: nfsd4_setattr: not opened for write!\n");
- goto out;
- }
+ 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;
+ }
}
- return (nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr, 0, (time_t)0));
-out:
- nfs4_unlock_state();
+ status = nfs_ok;
+ if (setattr->sa_acl != NULL)
+ status = nfsd4_set_nfs4_acl(rqstp, current_fh, setattr->sa_acl);
+ if (status)
+ return status;
+ status = nfsd_setattr(rqstp, current_fh, &setattr->sa_iattr,
+ 0, (time_t)0);
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
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;
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
/* 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;
}
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);
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;
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);
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);
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);
}
out:
*/
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 = {