Commit 718f6bfc authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] kNFSd: Enforce open_downgrade requirement

From: NeilBrown <neilb@cse.unsw.edu.au>

The spec requires that the access and deny bits requested in open_downgrade
represent the union of the bits for some subset of the OPENS for the given
open_owner and file.  Enforce that requirement.
parent 11a4f276
...@@ -381,6 +381,20 @@ nfsd4_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_loo ...@@ -381,6 +381,20 @@ 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); 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 static inline int
nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read) nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read *read)
{ {
...@@ -419,7 +433,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read ...@@ -419,7 +433,7 @@ nfsd4_read(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_read
goto out; goto out;
} }
status = nfserr_openmode; status = nfserr_openmode;
if (!(stp->st_share_access & NFS4_SHARE_ACCESS_READ)) { if (!access_bits_permit_read(stp->st_access_bmap)) {
dprintk("NFSD: nfsd4_read: file not opened for read!\n"); dprintk("NFSD: nfsd4_read: file not opened for read!\n");
goto out; goto out;
} }
...@@ -527,7 +541,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_se ...@@ -527,7 +541,7 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_se
goto out; goto out;
} }
status = nfserr_openmode; status = nfserr_openmode;
if (!(stp->st_share_access & NFS4_SHARE_ACCESS_WRITE)) { if (!access_bits_permit_write(stp->st_access_bmap)) {
dprintk("NFSD: nfsd4_setattr: not opened for write!\n"); dprintk("NFSD: nfsd4_setattr: not opened for write!\n");
goto out; goto out;
} }
...@@ -568,7 +582,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ ...@@ -568,7 +582,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_writ
} }
status = nfserr_openmode; status = nfserr_openmode;
if (!(stp->st_share_access & NFS4_SHARE_ACCESS_WRITE)) { if (!access_bits_permit_write(stp->st_access_bmap)) {
dprintk("NFSD: nfsd4_write: file not open for write!\n"); dprintk("NFSD: nfsd4_write: file not open for write!\n");
goto out; goto out;
} }
......
...@@ -844,8 +844,10 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfs4_stateow ...@@ -844,8 +844,10 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfs4_stateow
stp->st_stateid.si_stateownerid = sop->so_id; stp->st_stateid.si_stateownerid = sop->so_id;
stp->st_stateid.si_fileid = fp->fi_id; stp->st_stateid.si_fileid = fp->fi_id;
stp->st_stateid.si_generation = 0; stp->st_stateid.si_generation = 0;
stp->st_share_access = open->op_share_access; stp->st_access_bmap = 0;
stp->st_share_deny = open->op_share_deny; stp->st_deny_bmap = 0;
__set_bit(open->op_share_access, &stp->st_access_bmap);
__set_bit(open->op_share_deny, &stp->st_deny_bmap);
} }
static void static void
...@@ -962,15 +964,46 @@ find_file(unsigned int hashval, struct inode *ino, struct nfs4_file **fp) { ...@@ -962,15 +964,46 @@ find_file(unsigned int hashval, struct inode *ino, struct nfs4_file **fp) {
return 0; return 0;
} }
#define TEST_ACCESS(x) ((x > 0 || x < 4)?1:0)
#define TEST_DENY(x) ((x >= 0 || x < 5)?1:0)
void
set_access(unsigned int *access, unsigned long bmap) {
int i;
*access = 0;
for (i = 1; i < 4; i++) {
if(test_bit(i, &bmap))
*access |= i;
}
}
void
set_deny(unsigned int *deny, unsigned long bmap) {
int i;
*deny = 0;
for (i = 0; i < 4; i++) {
if(test_bit(i, &bmap))
*deny |= i ;
}
}
static int static int
test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) { test_share(struct nfs4_stateid *stp, struct nfsd4_open *open) {
if ((stp->st_share_access & open->op_share_deny) || unsigned int access, deny;
(stp->st_share_deny & open->op_share_access)) {
set_access(&access, stp->st_access_bmap);
set_deny(&deny, stp->st_deny_bmap);
if ((access & open->op_share_deny) || (deny & open->op_share_access))
return 0; return 0;
}
return 1; return 1;
} }
/*
* Called to check deny when READ with all zero stateid or
* WRITE with all zero or all one stateid
*/
int int
nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
{ {
...@@ -987,7 +1020,8 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type) ...@@ -987,7 +1020,8 @@ nfs4_share_conflict(struct svc_fh *current_fh, unsigned int deny_type)
/* Search for conflicting share reservations */ /* Search for conflicting share reservations */
list_for_each_safe(pos, next, &fp->fi_perfile) { list_for_each_safe(pos, next, &fp->fi_perfile) {
stp = list_entry(pos, struct nfs4_stateid, st_perfile); stp = list_entry(pos, struct nfs4_stateid, st_perfile);
if (stp->st_share_deny & deny_type) if (test_bit(deny_type, &stp->st_deny_bmap) ||
test_bit(NFS4_SHARE_DENY_BOTH, &stp->st_deny_bmap))
return nfserr_share_denied; return nfserr_share_denied;
} }
} }
...@@ -1130,6 +1164,10 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1130,6 +1164,10 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
ino = current_fh->fh_dentry->d_inode; ino = current_fh->fh_dentry->d_inode;
status = nfserr_inval;
if (!TEST_ACCESS(open->op_share_access) || !TEST_DENY(open->op_share_deny))
goto out;
nfs4_lock_state(); nfs4_lock_state();
fi_hashval = file_hashval(ino); fi_hashval = file_hashval(ino);
if (find_file(fi_hashval, ino, &fp)) { if (find_file(fi_hashval, ino, &fp)) {
...@@ -1181,15 +1219,18 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf ...@@ -1181,15 +1219,18 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
/* This is an upgrade of an existing OPEN. /* This is an upgrade of an existing OPEN.
* OR the incoming share with the existing * OR the incoming share with the existing
* nfs4_stateid share */ * nfs4_stateid share */
int share_access = open->op_share_access; unsigned int share_access;
share_access &= ~(stp->st_share_access); set_access(&share_access, stp->st_access_bmap);
share_access = ~share_access;
share_access &= open->op_share_access;
/* update the struct file */ /* update the struct file */
if ((status = nfs4_file_upgrade(&stp->st_vfs_file, share_access))) if ((status = nfs4_file_upgrade(&stp->st_vfs_file, share_access)))
goto out; goto out;
stp->st_share_access |= share_access; /* remember the open */
stp->st_share_deny |= open->op_share_deny; set_bit(open->op_share_access, &stp->st_access_bmap);
set_bit(open->op_share_deny, &stp->st_deny_bmap);
/* bump the stateid */ /* bump the stateid */
update_stateid(&stp->st_stateid); update_stateid(&stp->st_stateid);
} }
...@@ -1517,10 +1558,11 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei ...@@ -1517,10 +1558,11 @@ nfs4_preprocess_seqid_op(struct svc_fh *current_fh, u32 seqid, stateid_t *statei
printk("NFSD: preprocess_seqid_op: retransmission?\n"); printk("NFSD: preprocess_seqid_op: retransmission?\n");
/* indicate replay to calling function */ /* indicate replay to calling function */
status = NFSERR_REPLAY_ME; status = NFSERR_REPLAY_ME;
} else } else {
printk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d\n", sop->so_seqid +1, seqid); printk("NFSD: preprocess_seqid_op: bad seqid (expected %d, got %d\n", sop->so_seqid +1, seqid);
status = nfserr_bad_seqid; status = nfserr_bad_seqid;
}
goto out; goto out;
} }
...@@ -1562,16 +1604,48 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs ...@@ -1562,16 +1604,48 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfs
nfs4_unlock_state(); nfs4_unlock_state();
return status; return status;
} }
/*
* unset all bits in union bitmap (bmap) that
* do not exist in share (from successful OPEN_DOWNGRADE)
*/
static void
reset_union_bmap_access(unsigned long access, unsigned long *bmap)
{
int i;
for (i = 1; i < 4; i++) {
if ((i & access) != i)
__clear_bit(i, bmap);
}
}
static void
reset_union_bmap_deny(unsigned long deny, unsigned long *bmap)
{
int i;
for (i = 0; i < 4; i++) {
if ((i & deny) != i)
__clear_bit(i, bmap);
}
}
int int
nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_downgrade *od) nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open_downgrade *od)
{ {
int status; int status;
struct nfs4_stateid *stp; struct nfs4_stateid *stp;
unsigned int share_access;
dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n",
(int)current_fh->fh_dentry->d_name.len, (int)current_fh->fh_dentry->d_name.len,
current_fh->fh_dentry->d_name.name); current_fh->fh_dentry->d_name.name);
status = nfserr_inval;
if (!TEST_ACCESS(od->od_share_access) || !TEST_DENY(od->od_share_deny))
goto out;
nfs4_lock_state(); nfs4_lock_state();
if ((status = nfs4_preprocess_seqid_op(current_fh, od->od_seqid, if ((status = nfs4_preprocess_seqid_op(current_fh, od->od_seqid,
&od->od_stateid, &od->od_stateid,
...@@ -1580,20 +1654,23 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct n ...@@ -1580,20 +1654,23 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct n
goto out; goto out;
status = nfserr_inval; status = nfserr_inval;
if (od->od_share_access & ~stp->st_share_access) { if (!test_bit(od->od_share_access, &stp->st_access_bmap)) {
dprintk("NFSD:access not a subset current=%08x, desired=%08x\n", dprintk("NFSD:access not a subset current bitmap: 0x%lx, input access=%08x\n",
stp->st_share_access, od->od_share_access); stp->st_access_bmap, od->od_share_access);
goto out; goto out;
} }
if (od->od_share_deny & ~stp->st_share_deny) { if (!test_bit(od->od_share_deny, &stp->st_deny_bmap)) {
dprintk("NFSD:deny not a subset current=%08x, desired=%08x\n", dprintk("NFSD:deny not a subset current bitmap: 0x%lx, input deny=%08x\n",
stp->st_share_deny, od->od_share_deny); stp->st_deny_bmap, od->od_share_deny);
goto out; goto out;
} }
set_access(&share_access, stp->st_access_bmap);
nfs4_file_downgrade(&stp->st_vfs_file, nfs4_file_downgrade(&stp->st_vfs_file,
stp->st_share_access & ~od->od_share_access); share_access & ~od->od_share_access);
stp->st_share_access = od->od_share_access;
stp->st_share_deny = od->od_share_deny; reset_union_bmap_access(od->od_share_access, &stp->st_access_bmap);
reset_union_bmap_deny(od->od_share_deny, &stp->st_deny_bmap);
update_stateid(&stp->st_stateid); update_stateid(&stp->st_stateid);
memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t)); memcpy(&od->od_stateid, &stp->st_stateid, sizeof(stateid_t));
status = nfs_ok; status = nfs_ok;
...@@ -1820,8 +1897,8 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc ...@@ -1820,8 +1897,8 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
stp->st_stateid.si_generation = 0; stp->st_stateid.si_generation = 0;
stp->st_vfs_file = open_stp->st_vfs_file; stp->st_vfs_file = open_stp->st_vfs_file;
stp->st_vfs_set = open_stp->st_vfs_set; stp->st_vfs_set = open_stp->st_vfs_set;
stp->st_share_access = -1; stp->st_access_bmap = open_stp->st_access_bmap;
stp->st_share_deny = -1; stp->st_deny_bmap = open_stp->st_deny_bmap;
out: out:
return stp; return stp;
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#define NFS4_SHARE_ACCESS_BOTH 0x0003 #define NFS4_SHARE_ACCESS_BOTH 0x0003
#define NFS4_SHARE_DENY_READ 0x0001 #define NFS4_SHARE_DENY_READ 0x0001
#define NFS4_SHARE_DENY_WRITE 0x0002 #define NFS4_SHARE_DENY_WRITE 0x0002
#define NFS4_SHARE_DENY_BOTH 0x0003
#define NFS4_SET_TO_SERVER_TIME 0 #define NFS4_SET_TO_SERVER_TIME 0
#define NFS4_SET_TO_CLIENT_TIME 1 #define NFS4_SET_TO_CLIENT_TIME 1
......
...@@ -170,8 +170,8 @@ struct nfs4_file { ...@@ -170,8 +170,8 @@ struct nfs4_file {
* st_perfile: file_hashtbl[] entry. * st_perfile: file_hashtbl[] entry.
* st_perfile_state: nfs4_stateowner->so_perfilestate * st_perfile_state: nfs4_stateowner->so_perfilestate
* st_perlockowner: (open stateid) list of lock nfs4_stateowners * st_perlockowner: (open stateid) list of lock nfs4_stateowners
* st_share_access: used only for open stateid * st_access_bmap: used only for open stateid
* st_share_deny: used only for open stateid * st_deny_bmap: used only for open stateid
*/ */
struct nfs4_stateid { struct nfs4_stateid {
...@@ -184,8 +184,8 @@ struct nfs4_stateid { ...@@ -184,8 +184,8 @@ struct nfs4_stateid {
stateid_t st_stateid; stateid_t st_stateid;
struct file st_vfs_file; struct file st_vfs_file;
int st_vfs_set; int st_vfs_set;
unsigned int st_share_access; unsigned long st_access_bmap;
unsigned int st_share_deny; unsigned long st_deny_bmap;
}; };
/* flags for preprocess_seqid_op() */ /* flags for preprocess_seqid_op() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment