Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 117465
b: refs/heads/master
c: 14f7dd6
h: refs/heads/master
i:
  117463: 285b4a6
v: v3
  • Loading branch information
David Woodhouse authored and Al Viro committed Oct 23, 2008
1 parent 9fbe398 commit b165efc
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 16 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 2628b766363aa3791e816a7e5807373ef551c776
refs/heads/master: 14f7dd632011bb89c035722edd6ea0d90ca6b078
108 changes: 93 additions & 15 deletions trunk/fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -1813,27 +1813,105 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
return err;
}

static int nfsd_do_readdir(struct file *file, filldir_t func,
struct readdir_cd *cdp, loff_t *offsetp)
/*
* 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;
};

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)
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;

/*
* 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);
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 */

*offsetp = vfs_llseek(file, 0, 1);
while (1) {
unsigned int reclen;

buf.used = 0;

host_err = vfs_readdir(file, nfsd_buffered_filldir, &buf);
if (host_err)
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, 1);
}

done:
free_page((unsigned long)(buf.dirent));

if (host_err)
return nfserrno(host_err);
else
return cdp->err;

*offsetp = offset;
return cdp->err;
}

/*
Expand All @@ -1858,7 +1936,7 @@ nfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t *offsetp,
goto out_close;
}

err = nfsd_do_readdir(file, func, cdp, offsetp);
err = nfsd_buffered_readdir(file, func, cdp, offsetp);

if (err == nfserr_eof || err == nfserr_toosmall)
err = nfs_ok; /* can still be found in ->err */
Expand Down

0 comments on commit b165efc

Please sign in to comment.