Commit 114de382 authored by Trond Myklebust's avatar Trond Myklebust Committed by Anna Schumaker
Browse files

NFS: Directory page cache pages need to be locked when read



When a NFS directory page cache page is removed from the page cache,
its contents are freed through a call to nfs_readdir_clear_array().
To prevent the removal of the page cache entry until after we've
finished reading it, we must take the page lock.

Fixes: 11de3b11 ("NFS: Fix a memory leak in nfs_readdir")
Cc: stable@vger.kernel.org # v2.6.37+
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Reviewed-by: default avatarBenjamin Coddington <bcodding@redhat.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent 4b310319
Loading
Loading
Loading
Loading
+19 −11
Original line number Diff line number Diff line
@@ -702,8 +702,6 @@ int nfs_readdir_filler(void *data, struct page* page)
static
void cache_page_release(nfs_readdir_descriptor_t *desc)
{
	if (!desc->page->mapping)
		nfs_readdir_clear_array(desc->page);
	put_page(desc->page);
	desc->page = NULL;
}
@@ -717,18 +715,27 @@ struct page *get_cache_page(nfs_readdir_descriptor_t *desc)

/*
 * Returns 0 if desc->dir_cookie was found on page desc->page_index
 * and locks the page to prevent removal from the page cache.
 */
static
int find_cache_page(nfs_readdir_descriptor_t *desc)
int find_and_lock_cache_page(nfs_readdir_descriptor_t *desc)
{
	int res;

	desc->page = get_cache_page(desc);
	if (IS_ERR(desc->page))
		return PTR_ERR(desc->page);

	res = nfs_readdir_search_array(desc);
	res = lock_page_killable(desc->page);
	if (res != 0)
		goto error;
	res = -EAGAIN;
	if (desc->page->mapping != NULL) {
		res = nfs_readdir_search_array(desc);
		if (res == 0)
			return 0;
	}
	unlock_page(desc->page);
error:
	cache_page_release(desc);
	return res;
}
@@ -744,7 +751,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
		desc->last_cookie = 0;
	}
	do {
		res = find_cache_page(desc);
		res = find_and_lock_cache_page(desc);
	} while (res == -EAGAIN);
	return res;
}
@@ -783,7 +790,6 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc)
		desc->eof = true;

	kunmap(desc->page);
	cache_page_release(desc);
	dfprintk(DIRCACHE, "NFS: nfs_do_filldir() filling ended @ cookie %Lu; returning = %d\n",
			(unsigned long long)*desc->dir_cookie, res);
	return res;
@@ -829,13 +835,13 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)

	status = nfs_do_filldir(desc);

 out_release:
	nfs_readdir_clear_array(desc->page);
	cache_page_release(desc);
 out:
	dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
			__func__, status);
	return status;
 out_release:
	cache_page_release(desc);
	goto out;
}

/* The file offset position represents the dirent entry number.  A
@@ -900,6 +906,8 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
			break;

		res = nfs_do_filldir(desc);
		unlock_page(desc->page);
		cache_page_release(desc);
		if (res < 0)
			break;
	} while (!desc->eof);