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