diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-23 13:22:40 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-23 13:22:40 -0400 |
| commit | 5ed487bc2c44ca4e9668ef9cb54c830e2a9fac47 (patch) | |
| tree | af19ed28db83e8f52690872ac99336da1cf2fd3b /fs/nfsd/vfs.c | |
| parent | 5b34653963de7a6d0d8c783527457d68fddc60fb (diff) | |
| parent | fd217f4d70172c526478f2bc76859e909fdfa674 (diff) | |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs-2.6: (46 commits)
[PATCH] fs: add a sanity check in d_free
[PATCH] i_version: remount support
[patch] vfs: make security_inode_setattr() calling consistent
[patch 1/3] FS_MBCACHE: don't needlessly make it built-in
[PATCH] move executable checking into ->permission()
[PATCH] fs/dcache.c: update comment of d_validate()
[RFC PATCH] touch_mnt_namespace when the mount flags change
[PATCH] reiserfs: add missing llseek method
[PATCH] fix ->llseek for more directories
[PATCH vfs-2.6 6/6] vfs: add LOOKUP_RENAME_TARGET intent
[PATCH vfs-2.6 5/6] vfs: remove LOOKUP_PARENT from non LOOKUP_PARENT lookup
[PATCH vfs-2.6 4/6] vfs: remove unnecessary fsnotify_d_instantiate()
[PATCH vfs-2.6 3/6] vfs: add __d_instantiate() helper
[PATCH vfs-2.6 2/6] vfs: add d_ancestor()
[PATCH vfs-2.6 1/6] vfs: replace parent == dentry->d_parent by IS_ROOT()
[PATCH] get rid of on-stack dentry in udf
[PATCH 2/2] anondev: switch to IDA
[PATCH 1/2] anondev: init IDR statically
[JFFS2] Use d_splice_alias() not d_add() in jffs2_lookup()
[PATCH] Optimise NFS readdir hack slightly.
...
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 */ |
