[PATCH] NFS: Ensure we always update inode->i_mode when doing O_EXCL creates
When the client performs an exclusive create and opens the file for writing, a Netapp filer will first create the file using the mode 01777. It does this since an NFSv3/v4 exclusive create cannot immediately set the mode bits. The 01777 mode then gets put into the inode->i_mode. After the file creation is successful, we then do a setattr to change the mode to the correct value (as per the NFS spec). The problem is that nfs_refresh_inode() no longer updates inode->i_mode, so the latter retains the 01777 mode. A bit later, the VFS notices this, and calls remove_suid(). This of course now resets the file mode to inode->i_mode & 0777. Hey presto, the file mode on the server is now magically changed to 0777. Duh... Fixes http://bugzilla.linux-nfs.org/show_bug.cgi?id=32 Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
This commit is contained in:
parent
367ae3cd74
commit
65e4308d25
|
@ -814,28 +814,39 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
|
||||||
nfs_wb_all(inode);
|
nfs_wb_all(inode);
|
||||||
}
|
}
|
||||||
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
|
error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
|
||||||
if (error == 0) {
|
if (error == 0)
|
||||||
nfs_refresh_inode(inode, &fattr);
|
nfs_refresh_inode(inode, &fattr);
|
||||||
|
nfs_end_data_update(inode);
|
||||||
|
unlock_kernel();
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nfs_setattr_update_inode - Update inode metadata after a setattr call.
|
||||||
|
* @inode: pointer to struct inode
|
||||||
|
* @attr: pointer to struct iattr
|
||||||
|
*
|
||||||
|
* Note: we do this in the *proc.c in order to ensure that
|
||||||
|
* it works for things like exclusive creates too.
|
||||||
|
*/
|
||||||
|
void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
|
||||||
|
{
|
||||||
|
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0) {
|
||||||
if ((attr->ia_valid & ATTR_MODE) != 0) {
|
if ((attr->ia_valid & ATTR_MODE) != 0) {
|
||||||
int mode;
|
int mode = attr->ia_mode & S_IALLUGO;
|
||||||
mode = inode->i_mode & ~S_IALLUGO;
|
mode |= inode->i_mode & ~S_IALLUGO;
|
||||||
mode |= attr->ia_mode & S_IALLUGO;
|
|
||||||
inode->i_mode = mode;
|
inode->i_mode = mode;
|
||||||
}
|
}
|
||||||
if ((attr->ia_valid & ATTR_UID) != 0)
|
if ((attr->ia_valid & ATTR_UID) != 0)
|
||||||
inode->i_uid = attr->ia_uid;
|
inode->i_uid = attr->ia_uid;
|
||||||
if ((attr->ia_valid & ATTR_GID) != 0)
|
if ((attr->ia_valid & ATTR_GID) != 0)
|
||||||
inode->i_gid = attr->ia_gid;
|
inode->i_gid = attr->ia_gid;
|
||||||
if ((attr->ia_valid & ATTR_SIZE) != 0) {
|
|
||||||
inode->i_size = attr->ia_size;
|
|
||||||
vmtruncate(inode, attr->ia_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
|
|
||||||
NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
NFS_FLAGS(inode) |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
|
||||||
nfs_end_data_update(inode);
|
}
|
||||||
unlock_kernel();
|
if ((attr->ia_valid & ATTR_SIZE) != 0) {
|
||||||
return error;
|
inode->i_size = attr->ia_size;
|
||||||
|
vmtruncate(inode, attr->ia_size);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -120,6 +120,8 @@ nfs3_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
dprintk("NFS call setattr\n");
|
dprintk("NFS call setattr\n");
|
||||||
fattr->valid = 0;
|
fattr->valid = 0;
|
||||||
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
|
status = rpc_call(NFS_CLIENT(inode), NFS3PROC_SETATTR, &arg, fattr, 0);
|
||||||
|
if (status == 0)
|
||||||
|
nfs_setattr_update_inode(inode, sattr);
|
||||||
dprintk("NFS reply setattr: %d\n", status);
|
dprintk("NFS reply setattr: %d\n", status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -370,6 +372,8 @@ again:
|
||||||
* not sure this buys us anything (and I'd have
|
* not sure this buys us anything (and I'd have
|
||||||
* to revamp the NFSv3 XDR code) */
|
* to revamp the NFSv3 XDR code) */
|
||||||
status = nfs3_proc_setattr(dentry, &fattr, sattr);
|
status = nfs3_proc_setattr(dentry, &fattr, sattr);
|
||||||
|
if (status == 0)
|
||||||
|
nfs_setattr_update_inode(dentry->d_inode, sattr);
|
||||||
nfs_refresh_inode(dentry->d_inode, &fattr);
|
nfs_refresh_inode(dentry->d_inode, &fattr);
|
||||||
dprintk("NFS reply setattr (post-create): %d\n", status);
|
dprintk("NFS reply setattr (post-create): %d\n", status);
|
||||||
}
|
}
|
||||||
|
|
|
@ -753,6 +753,7 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
|
||||||
.rpc_argp = &arg,
|
.rpc_argp = &arg,
|
||||||
.rpc_resp = &res,
|
.rpc_resp = &res,
|
||||||
};
|
};
|
||||||
|
int status;
|
||||||
|
|
||||||
fattr->valid = 0;
|
fattr->valid = 0;
|
||||||
|
|
||||||
|
@ -762,7 +763,8 @@ static int _nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
|
||||||
} else
|
} else
|
||||||
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
|
memcpy(&arg.stateid, &zero_stateid, sizeof(arg.stateid));
|
||||||
|
|
||||||
return rpc_call_sync(server->client, &msg, 0);
|
status = rpc_call_sync(server->client, &msg, 0);
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
|
static int nfs4_do_setattr(struct nfs_server *server, struct nfs_fattr *fattr,
|
||||||
|
@ -1145,6 +1147,8 @@ nfs4_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
|
|
||||||
status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
|
status = nfs4_do_setattr(NFS_SERVER(inode), fattr,
|
||||||
NFS_FH(inode), sattr, state);
|
NFS_FH(inode), sattr, state);
|
||||||
|
if (status == 0)
|
||||||
|
nfs_setattr_update_inode(inode, sattr);
|
||||||
if (state != NULL)
|
if (state != NULL)
|
||||||
nfs4_close_state(state, FMODE_WRITE);
|
nfs4_close_state(state, FMODE_WRITE);
|
||||||
put_rpccred(cred);
|
put_rpccred(cred);
|
||||||
|
@ -1449,8 +1453,10 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
|
||||||
struct nfs_fattr fattr;
|
struct nfs_fattr fattr;
|
||||||
status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
|
status = nfs4_do_setattr(NFS_SERVER(dir), &fattr,
|
||||||
NFS_FH(state->inode), sattr, state);
|
NFS_FH(state->inode), sattr, state);
|
||||||
if (status == 0)
|
if (status == 0) {
|
||||||
|
nfs_setattr_update_inode(state->inode, sattr);
|
||||||
goto out;
|
goto out;
|
||||||
|
}
|
||||||
} else if (flags != 0)
|
} else if (flags != 0)
|
||||||
goto out;
|
goto out;
|
||||||
nfs4_close_state(state, flags);
|
nfs4_close_state(state, flags);
|
||||||
|
|
|
@ -114,6 +114,8 @@ nfs_proc_setattr(struct dentry *dentry, struct nfs_fattr *fattr,
|
||||||
dprintk("NFS call setattr\n");
|
dprintk("NFS call setattr\n");
|
||||||
fattr->valid = 0;
|
fattr->valid = 0;
|
||||||
status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
|
status = rpc_call(NFS_CLIENT(inode), NFSPROC_SETATTR, &arg, fattr, 0);
|
||||||
|
if (status == 0)
|
||||||
|
nfs_setattr_update_inode(inode, sattr);
|
||||||
dprintk("NFS reply setattr: %d\n", status);
|
dprintk("NFS reply setattr: %d\n", status);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,7 @@ extern int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode);
|
||||||
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
|
extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
|
||||||
extern void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
|
extern void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
|
||||||
extern int nfs_setattr(struct dentry *, struct iattr *);
|
extern int nfs_setattr(struct dentry *, struct iattr *);
|
||||||
|
extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
|
||||||
extern void nfs_begin_attr_update(struct inode *);
|
extern void nfs_begin_attr_update(struct inode *);
|
||||||
extern void nfs_end_attr_update(struct inode *);
|
extern void nfs_end_attr_update(struct inode *);
|
||||||
extern void nfs_begin_data_update(struct inode *);
|
extern void nfs_begin_data_update(struct inode *);
|
||||||
|
|
Loading…
Reference in New Issue