diff options
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/vfs.c | 108 |
1 files changed, 93 insertions, 15 deletions
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 14eda20cc291..93b22f661d9d 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -1813,27 +1813,105 @@ out: | |||
1813 | return err; | 1813 | return err; |
1814 | } | 1814 | } |
1815 | 1815 | ||
1816 | static int nfsd_do_readdir(struct file *file, filldir_t func, | 1816 | /* |
1817 | struct readdir_cd *cdp, loff_t *offsetp) | 1817 | * We do this buffering because we must not call back into the file |
1818 | * system's ->lookup() method from the filldir callback. That may well | ||
1819 | * deadlock a number of file systems. | ||
1820 | * | ||
1821 | * This is based heavily on the implementation of same in XFS. | ||
1822 | */ | ||
1823 | struct buffered_dirent { | ||
1824 | u64 ino; | ||
1825 | loff_t offset; | ||
1826 | int namlen; | ||
1827 | unsigned int d_type; | ||
1828 | char name[]; | ||
1829 | }; | ||
1830 | |||
1831 | struct readdir_data { | ||
1832 | char *dirent; | ||
1833 | size_t used; | ||
1834 | }; | ||
1835 | |||
1836 | static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen, | ||
1837 | loff_t offset, u64 ino, unsigned int d_type) | ||
1838 | { | ||
1839 | struct readdir_data *buf = __buf; | ||
1840 | struct buffered_dirent *de = (void *)(buf->dirent + buf->used); | ||
1841 | unsigned int reclen; | ||
1842 | |||
1843 | reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64)); | ||
1844 | if (buf->used + reclen > PAGE_SIZE) | ||
1845 | return -EINVAL; | ||
1846 | |||
1847 | de->namlen = namlen; | ||
1848 | de->offset = offset; | ||
1849 | de->ino = ino; | ||
1850 | de->d_type = d_type; | ||
1851 | memcpy(de->name, name, namlen); | ||
1852 | buf->used += reclen; | ||
1853 | |||
1854 | return 0; | ||
1855 | } | ||
1856 | |||
1857 | static int nfsd_buffered_readdir(struct file *file, filldir_t func, | ||
1858 | struct readdir_cd *cdp, loff_t *offsetp) | ||
1818 | { | 1859 | { |
1860 | struct readdir_data buf; | ||
1861 | struct buffered_dirent *de; | ||
1819 | int host_err; | 1862 | int host_err; |
1863 | int size; | ||
1864 | loff_t offset; | ||
1820 | 1865 | ||
1821 | /* | 1866 | buf.dirent = (void *)__get_free_page(GFP_KERNEL); |
1822 | * Read the directory entries. This silly loop is necessary because | 1867 | if (!buf.dirent) |
1823 | * readdir() is not guaranteed to fill up the entire buffer, but | 1868 | return -ENOMEM; |
1824 | * may choose to do less. | 1869 | |
1825 | */ | 1870 | offset = *offsetp; |
1826 | do { | 1871 | cdp->err = nfserr_eof; /* will be cleared on successful read */ |
1827 | cdp->err = nfserr_eof; /* will be cleared on successful read */ | ||
1828 | host_err = vfs_readdir(file, func, cdp); | ||
1829 | } while (host_err >=0 && cdp->err == nfs_ok); | ||
1830 | 1872 | ||
1831 | *offsetp = vfs_llseek(file, 0, 1); | 1873 | while (1) { |
1874 | unsigned int reclen; | ||
1875 | |||
1876 | buf.used = 0; | ||
1877 | |||
1878 | host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf); | ||
1879 | if (host_err) | ||
1880 | break; | ||
1881 | |||
1882 | size = buf.used; | ||
1883 | |||
1884 | if (!size) | ||
1885 | break; | ||
1886 | |||
1887 | |||
1888 | de = (struct buffered_dirent *)buf.dirent; | ||
1889 | while (size > 0) { | ||
1890 | offset = de->offset; | ||
1891 | |||
1892 | if (func(cdp, de->name, de->namlen, de->offset, | ||
1893 | de->ino, de->d_type)) | ||
1894 | goto done; | ||
1895 | |||
1896 | if (cdp->err != nfs_ok) | ||
1897 | goto done; | ||
1898 | |||
1899 | reclen = ALIGN(sizeof(*de) + de->namlen, | ||
1900 | sizeof(u64)); | ||
1901 | size -= reclen; | ||
1902 | de = (struct buffered_dirent *)((char *)de + reclen); | ||
1903 | } | ||
1904 | offset = vfs_llseek(file, 0, 1); | ||
1905 | } | ||
1906 | |||
1907 | done: | ||
1908 | free_page((unsigned long)(buf.dirent)); | ||
1832 | 1909 | ||
1833 | if (host_err) | 1910 | if (host_err) |
1834 | return nfserrno(host_err); | 1911 | return nfserrno(host_err); |
1835 | else | 1912 | |
1836 | return cdp->err; | 1913 | *offsetp = offset; |
1914 | return cdp->err; | ||
1837 | } | 1915 | } |
1838 | 1916 | ||
1839 | /* | 1917 | /* |
@@ -1858,7 +1936,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp, | |||
1858 | goto out_close; | 1936 | goto out_close; |
1859 | } | 1937 | } |
1860 | 1938 | ||
1861 | err = nfsd_do_readdir(file, func, cdp, offsetp); | 1939 | err = nfsd_buffered_readdir(file, func, cdp, offsetp); |
1862 | 1940 | ||
1863 | if (err == nfserr_eof || err == nfserr_toosmall) | 1941 | if (err == nfserr_eof || err == nfserr_toosmall) |
1864 | err = nfs_ok; /* can still be found in ->err */ | 1942 | err = nfs_ok; /* can still be found in ->err */ |