aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/vfs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-10-23 13:22:40 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-10-23 13:22:40 -0400
commit5ed487bc2c44ca4e9668ef9cb54c830e2a9fac47 (patch)
treeaf19ed28db83e8f52690872ac99336da1cf2fd3b /fs/nfsd/vfs.c
parent5b34653963de7a6d0d8c783527457d68fddc60fb (diff)
parentfd217f4d70172c526478f2bc76859e909fdfa674 (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.c126
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 */
1827struct buffered_dirent {
1828 u64 ino;
1829 loff_t offset;
1830 int namlen;
1831 unsigned int d_type;
1832 char name[];
1833};
1834
1835struct readdir_data {
1836 char *dirent;
1837 size_t used;
1838 int full;
1839};
1840
1841static 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
1864static 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 */