diff options
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) |