summaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfsfh.c
diff options
context:
space:
mode:
authorNeil Brown <neilb@suse.de>2008-05-08 13:03:09 +1000
committerJ. Bruce Fields <bfields@citi.umich.edu>2008-06-30 15:24:11 -0400
commit496d6c32d4d057cb44272d9bd587ff97d023ee92 (patch)
tree5cfe2928782d5c58eab88f7b439b617858618b3f /fs/nfsd/nfsfh.c
parent863a24882ed0a57ff25daaf39885f3a47b706e4b (diff)
downloadlinux-stable-496d6c32d4d057cb44272d9bd587ff97d023ee92.tar.gz
linux-stable-496d6c32d4d057cb44272d9bd587ff97d023ee92.tar.bz2
linux-stable-496d6c32d4d057cb44272d9bd587ff97d023ee92.zip
nfsd: fix spurious EACCESS in reconnect_path()
Thanks to Frank Van Maarseveen for the original problem report: "A privileged process on an NFS client which drops privileges after using them to change the current working directory, will experience incorrect EACCES after an NFS server reboot. This problem can also occur after memory pressure on the server, particularly when the client side is quiet for some time." This occurs because the filehandle points to a directory whose parents are no longer in the dentry cache, and we're attempting to reconnect the directory to its parents without adequate permissions to perform lookups in the parent directories. We can therefore fix the problem by acquiring the necessary capabilities before attempting the reconnection. We do this only in the no_subtree_check case, since the documented behavior of the subtree_check export option requires the server to check that the user has lookup permissions on all parents. The subtree_check case still has a problem, since reconnect_path() unnecessarily requires both read and lookup permissions on all parent directories. However, a fix in that case would be more delicate, and use of subtree_check is already discouraged for other reasons. Signed-off-by: Neil Brown <neilb@suse.de> Cc: Frank van Maarseveen <frankvm@frankvm.com> Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
Diffstat (limited to 'fs/nfsd/nfsfh.c')
-rw-r--r--fs/nfsd/nfsfh.c29
1 files changed, 26 insertions, 3 deletions
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index c7b0fdaeac96..f45451eb1e38 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -176,9 +176,24 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
if (IS_ERR(exp))
return nfserrno(PTR_ERR(exp));
- error = nfsd_setuser_and_check_port(rqstp, exp);
- if (error)
- goto out;
+ if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) {
+ /* Elevate privileges so that the lack of 'r' or 'x'
+ * permission on some parent directory will
+ * not stop exportfs_decode_fh from being able
+ * to reconnect a directory into the dentry cache.
+ * The same problem can affect "SUBTREECHECK" exports,
+ * but as nfsd_acceptable depends on correct
+ * access control settings being in effect, we cannot
+ * fix that case easily.
+ */
+ current->cap_effective =
+ cap_raise_nfsd_set(current->cap_effective,
+ current->cap_permitted);
+ } else {
+ error = nfsd_setuser_and_check_port(rqstp, exp);
+ if (error)
+ goto out;
+ }
/*
* Look up the dentry using the NFS file handle.
@@ -215,6 +230,14 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
goto out;
}
+ if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) {
+ error = nfsd_setuser_and_check_port(rqstp, exp);
+ if (error) {
+ dput(dentry);
+ goto out;
+ }
+ }
+
if (S_ISDIR(dentry->d_inode->i_mode) &&
(dentry->d_flags & DCACHE_DISCONNECTED)) {
printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %s/%s\n",