aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 13:16:29 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2005-06-22 16:07:35 -0400
commit08e9eac42edab63bce14b5c8419771f3c92aa3f4 (patch)
tree5dd77a7fa392710dab47d30ae42bfb88b932b097 /fs/nfs
parent202b50dc127cf4714ffdcc6a64f1648373f9414f (diff)
[PATCH] NFSv4: Fix up races in nfs4_proc_setattr()
If we do not hold a valid stateid that is open for writes, there is little point in doing an extra open of the file, as the RFC does not appear to mandate this... Make setattr use the correct stateid if we're holding mandatory byte range locks. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/nfs4proc.c55
1 files changed, 19 insertions, 36 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 91e7fe867d58..af80b5981486 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -756,11 +756,10 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
756 756
757 fattr->valid = 0; 757 fattr->valid = 0;
758 758
759 if (state != NULL) 759 if (state != NULL) {
760 msg.rpc_cred = state->owner->so_cred; 760 msg.rpc_cred = state->owner->so_cred;
761 if (sattr->ia_valid & ATTR_SIZE) 761 nfs4_copy_stateid(&arg.stateid, state, current->files);
762 nfs4_copy_stateid(&arg.stateid, state, NULL); 762 } else
763 else
764 memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid)); 763 memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
765 764
766 return rpc_call_sync(server->client, &msg, 0); 765 return rpc_call_sync(server->client, &msg, 0);
@@ -1124,47 +1123,31 @@ static int
1124nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr, 1123nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
1125 struct iattr *sattr) 1124 struct iattr *sattr)
1126{ 1125{
1127 struct inode * inode = dentry->d_inode; 1126 struct rpc_cred *cred;
1128 int size_change = sattr->ia_valid & ATTR_SIZE; 1127 struct inode *inode = dentry->d_inode;
1129 struct nfs4_state *state = NULL; 1128 struct nfs4_state *state;
1130 int need_iput = 0;
1131 int status; 1129 int status;
1132 1130
1133 fattr->valid = 0; 1131 fattr->valid = 0;
1134 1132
1135 if (size_change) { 1133 cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0);
1136 struct rpc_cred *cred = rpcauth_lookupcred(NFS_SERVER(inode)->client->cl_auth, 0); 1134 if (IS_ERR(cred))
1137 if (IS_ERR(cred)) 1135 return PTR_ERR(cred);
1138 return PTR_ERR(cred); 1136 /* Search for an existing WRITE delegation first */
1137 state = nfs4_open_delegated(inode, FMODE_WRITE, cred);
1138 if (!IS_ERR(state)) {
1139 /* NB: nfs4_open_delegated() bumps the inode->i_count */
1140 iput(inode);
1141 } else {
1142 /* Search for an existing open(O_WRITE) stateid */
1139 state = nfs4_find_state(inode, cred, FMODE_WRITE); 1143 state = nfs4_find_state(inode, cred, FMODE_WRITE);
1140 if (state == NULL) {
1141 state = nfs4_open_delegated(dentry->d_inode,
1142 FMODE_WRITE, cred);
1143 if (IS_ERR(state))
1144 state = nfs4_do_open(dentry->d_parent->d_inode,
1145 dentry, FMODE_WRITE,
1146 NULL, cred);
1147 need_iput = 1;
1148 }
1149 put_rpccred(cred);
1150 if (IS_ERR(state))
1151 return PTR_ERR(state);
1152
1153 if (state->inode != inode) {
1154 printk(KERN_WARNING "nfs: raced in setattr (%p != %p), returning -EIO\n", inode, state->inode);
1155 status = -EIO;
1156 goto out;
1157 }
1158 } 1144 }
1145
1159 status = nfs4_do_setattr(NFS_SERVER(inode), fattr, 1146 status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
1160 NFS_FH(inode), sattr, state); 1147 NFS_FH(inode), sattr, state);
1161out: 1148 if (state != NULL)
1162 if (state) {
1163 inode = state->inode;
1164 nfs4_close_state(state, FMODE_WRITE); 1149 nfs4_close_state(state, FMODE_WRITE);
1165 if (need_iput) 1150 put_rpccred(cred);
1166 iput(inode);
1167 }
1168 return status; 1151 return status;
1169} 1152}
1170 1153