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. ...
This commit is contained in:
126
fs/nfsd/vfs.c
126
fs/nfsd/vfs.c
@@ -1817,6 +1817,115 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
/*
|
||||
* We do this buffering because we must not call back into the file
|
||||
* system's ->lookup() method from the filldir callback. That may well
|
||||
* deadlock a number of file systems.
|
||||
*
|
||||
* This is based heavily on the implementation of same in XFS.
|
||||
*/
|
||||
struct buffered_dirent {
|
||||
u64 ino;
|
||||
loff_t offset;
|
||||
int namlen;
|
||||
unsigned int d_type;
|
||||
char name[];
|
||||
};
|
||||
|
||||
struct readdir_data {
|
||||
char *dirent;
|
||||
size_t used;
|
||||
int full;
|
||||
};
|
||||
|
||||
static int nfsd_buffered_filldir(void *__buf, const char *name, int namlen,
|
||||
loff_t offset, u64 ino, unsigned int d_type)
|
||||
{
|
||||
struct readdir_data *buf = __buf;
|
||||
struct buffered_dirent *de = (void *)(buf->dirent + buf->used);
|
||||
unsigned int reclen;
|
||||
|
||||
reclen = ALIGN(sizeof(struct buffered_dirent) + namlen, sizeof(u64));
|
||||
if (buf->used + reclen > PAGE_SIZE) {
|
||||
buf->full = 1;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
de->namlen = namlen;
|
||||
de->offset = offset;
|
||||
de->ino = ino;
|
||||
de->d_type = d_type;
|
||||
memcpy(de->name, name, namlen);
|
||||
buf->used += reclen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nfsd_buffered_readdir(struct file *file, filldir_t func,
|
||||
struct readdir_cd *cdp, loff_t *offsetp)
|
||||
{
|
||||
struct readdir_data buf;
|
||||
struct buffered_dirent *de;
|
||||
int host_err;
|
||||
int size;
|
||||
loff_t offset;
|
||||
|
||||
buf.dirent = (void *)__get_free_page(GFP_KERNEL);
|
||||
if (!buf.dirent)
|
||||
return -ENOMEM;
|
||||
|
||||
offset = *offsetp;
|
||||
cdp->err = nfserr_eof; /* will be cleared on successful read */
|
||||
|
||||
while (1) {
|
||||
unsigned int reclen;
|
||||
|
||||
buf.used = 0;
|
||||
buf.full = 0;
|
||||
|
||||
host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf);
|
||||
if (buf.full)
|
||||
host_err = 0;
|
||||
|
||||
if (host_err < 0)
|
||||
break;
|
||||
|
||||
size = buf.used;
|
||||
|
||||
if (!size)
|
||||
break;
|
||||
|
||||
de = (struct buffered_dirent *)buf.dirent;
|
||||
while (size > 0) {
|
||||
offset = de->offset;
|
||||
|
||||
if (func(cdp, de->name, de->namlen, de->offset,
|
||||
de->ino, de->d_type))
|
||||
goto done;
|
||||
|
||||
if (cdp->err != nfs_ok)
|
||||
goto done;
|
||||
|
||||
reclen = ALIGN(sizeof(*de) + de->namlen,
|
||||
sizeof(u64));
|
||||
size -= reclen;
|
||||
de = (struct buffered_dirent *)((char *)de + reclen);
|
||||
}
|
||||
offset = vfs_llseek(file, 0, SEEK_CUR);
|
||||
if (!buf.full)
|
||||
break;
|
||||
}
|
||||
|
||||
done:
|
||||
free_page((unsigned long)(buf.dirent));
|
||||
|
||||
if (host_err)
|
||||
return nfserrno(host_err);
|
||||
|
||||
*offsetp = offset;
|
||||
return cdp->err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read entries from a directory.
|
||||
* The NFSv3/4 verifier we ignore for now.
|
||||
@@ -1826,7 +1935,6 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
|
||||
struct readdir_cd *cdp, filldir_t func)
|
||||
{
|
||||
__be32 err;
|
||||
int host_err;
|
||||
struct file *file;
|
||||
loff_t offset = *offsetp;
|
||||
|
||||
@@ -1840,21 +1948,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the directory entries. This silly loop is necessary because
|
||||
* readdir() is not guaranteed to fill up the entire buffer, but
|
||||
* may choose to do less.
|
||||
*/
|
||||
|
||||
do {
|
||||
cdp->err = nfserr_eof; /* will be cleared on successful read */
|
||||
host_err = vfs_readdir(file, func, cdp);
|
||||
} while (host_err >=0 && cdp->err == nfs_ok);
|
||||
if (host_err)
|
||||
err = nfserrno(host_err);
|
||||
else
|
||||
err = cdp->err;
|
||||
*offsetp = vfs_llseek(file, 0, 1);
|
||||
err = nfsd_buffered_readdir(file, func, cdp, offsetp);
|
||||
|
||||
if (err == nfserr_eof || err == nfserr_toosmall)
|
||||
err = nfs_ok; /* can still be found in ->err */
|
||||
|
Reference in New Issue
Block a user