diff options
author | David Woodhouse <dwmw2@infradead.org> | 2009-04-20 18:18:37 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2009-04-20 23:01:16 -0400 |
commit | 2f9092e1020246168b1309b35e085ecd7ff9ff72 (patch) | |
tree | f8318c1e62e789718ae7637869f6c075b815bcb2 /fs/nfsd/vfs.c | |
parent | 1ba0c7dbbbc24230394100c5f0d0df38cb400cff (diff) |
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>
Diffstat (limited to 'fs/nfsd/vfs.c')
-rw-r--r-- | fs/nfsd/vfs.c | 25 |
1 files changed, 19 insertions, 6 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 46e6bd2d4f07..6c68ffd6b4bb 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -1890,8 +1890,8 @@ static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen, | |||
1890 | return 0; | 1890 | return 0; |
1891 | } | 1891 | } |
1892 | 1892 | ||
1893 | static int nfsd_buffered_readdir(struct file *file, filldir_t func, | 1893 | static __be32 nfsd_buffered_readdir(struct file *file, filldir_t func, |
1894 | struct readdir_cd *cdp, loff_t *offsetp) | 1894 | struct readdir_cd *cdp, loff_t *offsetp) |
1895 | { | 1895 | { |
1896 | struct readdir_data buf; | 1896 | struct readdir_data buf; |
1897 | struct buffered_dirent *de; | 1897 | struct buffered_dirent *de; |
@@ -1901,11 +1901,12 @@ static int nfsd_buffered_readdir(struct file *file, filldir_t func, | |||
1901 | 1901 | ||
1902 | buf.dirent = (void *)__get_free_page(GFP_KERNEL); | 1902 | buf.dirent = (void *)__get_free_page(GFP_KERNEL); |
1903 | if (!buf.dirent) | 1903 | if (!buf.dirent) |
1904 | return -ENOMEM; | 1904 | return nfserrno(-ENOMEM); |
1905 | 1905 | ||
1906 | offset = *offsetp; | 1906 | offset = *offsetp; |
1907 | 1907 | ||
1908 | while (1) { | 1908 | while (1) { |
1909 | struct inode *dir_inode = file->f_path.dentry->d_inode; | ||
1909 | unsigned int reclen; | 1910 | unsigned int reclen; |
1910 | 1911 | ||
1911 | cdp->err = nfserr_eof; /* will be cleared on successful read */ | 1912 | 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, | |||
1924 | if (!size) | 1925 | if (!size) |
1925 | break; | 1926 | break; |
1926 | 1927 | ||
1928 | /* | ||
1929 | * Various filldir functions may end up calling back into | ||
1930 | * lookup_one_len() and the file system's ->lookup() method. | ||
1931 | * These expect i_mutex to be held, as it would within readdir. | ||
1932 | */ | ||
1933 | host_err = mutex_lock_killable(&dir_inode->i_mutex); | ||
1934 | if (host_err) | ||
1935 | break; | ||
1936 | |||
1927 | de = (struct buffered_dirent *)buf.dirent; | 1937 | de = (struct buffered_dirent *)buf.dirent; |
1928 | while (size > 0) { | 1938 | while (size > 0) { |
1929 | offset = de->offset; | 1939 | offset = de->offset; |
1930 | 1940 | ||
1931 | if (func(cdp, de->name, de->namlen, de->offset, | 1941 | if (func(cdp, de->name, de->namlen, de->offset, |
1932 | de->ino, de->d_type)) | 1942 | de->ino, de->d_type)) |
1933 | goto done; | 1943 | break; |
1934 | 1944 | ||
1935 | if (cdp->err != nfs_ok) | 1945 | if (cdp->err != nfs_ok) |
1936 | goto done; | 1946 | break; |
1937 | 1947 | ||
1938 | reclen = ALIGN(sizeof(*de) + de->namlen, | 1948 | reclen = ALIGN(sizeof(*de) + de->namlen, |
1939 | sizeof(u64)); | 1949 | sizeof(u64)); |
1940 | size -= reclen; | 1950 | size -= reclen; |
1941 | de = (struct buffered_dirent *)((char *)de + reclen); | 1951 | de = (struct buffered_dirent *)((char *)de + reclen); |
1942 | } | 1952 | } |
1953 | mutex_unlock(&dir_inode->i_mutex); | ||
1954 | if (size > 0) /* We bailed out early */ | ||
1955 | break; | ||
1956 | |||
1943 | offset = vfs_llseek(file, 0, SEEK_CUR); | 1957 | offset = vfs_llseek(file, 0, SEEK_CUR); |
1944 | } | 1958 | } |
1945 | 1959 | ||
1946 | done: | ||
1947 | free_page((unsigned long)(buf.dirent)); | 1960 | free_page((unsigned long)(buf.dirent)); |
1948 | 1961 | ||
1949 | if (host_err) | 1962 | if (host_err) |