Fix i_mutex vs. readdir handling in nfsd

Commit 14f7dd63 ("Copy XFS readdir hack into nfsd code") introduced a
bug to generic code which had been extant for a long time in the XFS
version -- it started to call through into lookup_one_len() and hence
into the file systems' ->lookup() methods without i_mutex held on the
directory.

This patch fixes it by locking the directory's i_mutex again before
calling the filldir functions. The original deadlocks which commit
14f7dd63 was designed to avoid are still avoided, because they were due
to fs-internal locking, not i_mutex.

While we're at it, fix the return type of nfsd_buffered_readdir() which
should be a __be32 not an int -- it's an NFS errno, not a Linux errno.
And return nfserrno(-ENOMEM) when allocation fails, not just -ENOMEM.
Sparse would have caught that, if it wasn't so busy bitching about
__cold__.

Commit 05f4f678 ("nfsd4: don't do lookup within readdir in recovery
code") introduced a similar problem with calling lookup_one_len()
without i_mutex, which this patch also addresses. To fix that, it was
necessary to fix the called functions so that they expect i_mutex to be
held; that part was done by J. Bruce Fields.

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Umm-I-can-live-with-that-by: Al Viro <viro@zeniv.linux.org.uk>
Reported-by: J. R. Okajima <hooanon05@yahoo.co.jp>
Tested-by: J. Bruce Fields <bfields@citi.umich.edu>
LKML-Reference: <8036.1237474444@jrobl>
Cc: stable@kernel.org
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
David Woodhouse
2009-04-20 23:18:37 +01:00
committed by Al Viro
parent 1ba0c7dbbb
commit 2f9092e102
3 changed files with 30 additions and 43 deletions

View File

@@ -1890,8 +1890,8 @@ static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
return 0;
}
static int nfsd_buffered_readdir(struct file *file, filldir_t func,
struct readdir_cd *cdp, loff_t *offsetp)
static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func,
struct readdir_cd *cdp, loff_t *offsetp)
{
struct readdir_data buf;
struct buffered_dirent *de;
@@ -1901,11 +1901,12 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func,
buf.dirent = (void *)__get_free_page(GFP_KERNEL);
if (!buf.dirent)
return -ENOMEM;
return nfserrno(-ENOMEM);
offset = *offsetp;
while (1) {
struct inode *dir_inode = file->f_path.dentry->d_inode;
unsigned int reclen;
cdp->err = nfserr_eof; /* will be cleared on successful read */
@@ -1924,26 +1925,38 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func,
if (!size)
break;
/*
* Various filldir functions may end up calling back into
* lookup_one_len() and the file system's ->lookup() method.
* These expect i_mutex to be held, as it would within readdir.
*/
host_err = mutex_lock_killable(&dir_inode->i_mutex);
if (host_err)
break;
de = (struct buffered_dirent *)buf.dirent;
while (size > 0) {
offset = de->offset;
if (func(cdp, de->name, de->namlen, de->offset,
de->ino, de->d_type))
goto done;
break;
if (cdp->err != nfs_ok)
goto done;
break;
reclen = ALIGN(sizeof(*de) + de->namlen,
sizeof(u64));
size -= reclen;
de = (struct buffered_dirent *)((char *)de + reclen);
}
mutex_unlock(&dir_inode->i_mutex);
if (size > 0) /* We bailed out early */
break;
offset = vfs_llseek(file, 0, SEEK_CUR);
}
done:
free_page((unsigned long)(buf.dirent));
if (host_err)