aboutsummaryrefslogtreecommitdiffstats
path: root/fs/9p/vfs_dir.c
diff options
context:
space:
mode:
authorEric Van Hensbergen <ericvh@gmail.com>2009-11-02 09:39:28 -0500
committerEric Van Hensbergen <ericvh@gmail.com>2009-11-02 09:43:45 -0500
commit3e2796a90cf349527e50b3bc4d0b2f4019b1ce7a (patch)
tree80bddc0f5d36a589db5a77b9b60e4c94c75994ed /fs/9p/vfs_dir.c
parent2511cd0b3b9e9b1c3e9360cc565c3745ac3f3f3f (diff)
9p: fix readdir corner cases
The patch below also addresses a couple of other corner cases in readdir seen with a large (e.g. 64k) msize. I'm not sure what people think of my co-opting of fid->aux here. I'd be happy to rework if there's a better way. When the size of the user supplied buffer passed to readdir is smaller than the data returned in one go by the 9P read request, v9fs_dir_readdir() currently discards extra data so that, on the next call, a 9P read request will be issued with offset < previous offset + bytes returned, which voilates the constraint described in paragraph 3 of read(5) description. This patch preseves the leftover data in fid->aux for use in the next call. Signed-off-by: Jim Garlick <garlick@llnl.gov> Signed-off-by: Eric Van Hensbergen <ericvh@gmail.com>
Diffstat (limited to 'fs/9p/vfs_dir.c')
-rw-r--r--fs/9p/vfs_dir.c94
1 files changed, 67 insertions, 27 deletions
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c
index cae53d405f21..15cce53bf61e 100644
--- a/fs/9p/vfs_dir.c
+++ b/fs/9p/vfs_dir.c
@@ -40,6 +40,24 @@
40#include "fid.h" 40#include "fid.h"
41 41
42/** 42/**
43 * struct p9_rdir - readdir accounting
44 * @mutex: mutex protecting readdir
45 * @head: start offset of current dirread buffer
46 * @tail: end offset of current dirread buffer
47 * @buf: dirread buffer
48 *
49 * private structure for keeping track of readdir
50 * allocated on demand
51 */
52
53struct p9_rdir {
54 struct mutex mutex;
55 int head;
56 int tail;
57 uint8_t *buf;
58};
59
60/**
43 * dt_type - return file type 61 * dt_type - return file type
44 * @mistat: mistat structure 62 * @mistat: mistat structure
45 * 63 *
@@ -70,57 +88,79 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
70{ 88{
71 int over; 89 int over;
72 struct p9_wstat st; 90 struct p9_wstat st;
73 int err; 91 int err = 0;
74 struct p9_fid *fid; 92 struct p9_fid *fid;
75 int buflen; 93 int buflen;
76 char *statbuf; 94 int reclen = 0;
77 int n, i = 0; 95 struct p9_rdir *rdir;
78 96
79 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); 97 P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
80 fid = filp->private_data; 98 fid = filp->private_data;
81 99
82 buflen = fid->clnt->msize - P9_IOHDRSZ; 100 buflen = fid->clnt->msize - P9_IOHDRSZ;
83 statbuf = kmalloc(buflen, GFP_KERNEL); 101
84 if (!statbuf) 102 /* allocate rdir on demand */
85 return -ENOMEM; 103 if (!fid->rdir) {
86 104 rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
87 while (1) { 105
88 err = v9fs_file_readn(filp, statbuf, NULL, buflen, 106 if (rdir == NULL) {
89 fid->rdir_fpos); 107 err = -ENOMEM;
90 if (err <= 0) 108 goto exit;
91 break; 109 }
92 110 spin_lock(&filp->f_dentry->d_lock);
93 i = 0; 111 if (!fid->rdir) {
94 n = err; 112 rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir);
95 while (i < n) { 113 mutex_init(&rdir->mutex);
96 err = p9stat_read(statbuf + i, buflen-i, &st, 114 rdir->head = rdir->tail = 0;
97 fid->clnt->dotu); 115 fid->rdir = (void *) rdir;
116 rdir = NULL;
117 }
118 spin_unlock(&filp->f_dentry->d_lock);
119 kfree(rdir);
120 }
121 rdir = (struct p9_rdir *) fid->rdir;
122
123 err = mutex_lock_interruptible(&rdir->mutex);
124 while (err == 0) {
125 if (rdir->tail == rdir->head) {
126 err = v9fs_file_readn(filp, rdir->buf, NULL,
127 buflen, filp->f_pos);
128 if (err <= 0)
129 goto unlock_and_exit;
130
131 rdir->head = 0;
132 rdir->tail = err;
133 }
134
135 while (rdir->head < rdir->tail) {
136 err = p9stat_read(rdir->buf + rdir->head,
137 buflen - rdir->head, &st,
138 fid->clnt->dotu);
98 if (err) { 139 if (err) {
99 P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); 140 P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
100 err = -EIO; 141 err = -EIO;
101 p9stat_free(&st); 142 p9stat_free(&st);
102 goto free_and_exit; 143 goto unlock_and_exit;
103 } 144 }
104 145 reclen = st.size+2;
105 i += st.size+2;
106 fid->rdir_fpos += st.size+2;
107 146
108 over = filldir(dirent, st.name, strlen(st.name), 147 over = filldir(dirent, st.name, strlen(st.name),
109 filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st)); 148 filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
110 149
111 filp->f_pos += st.size+2;
112
113 p9stat_free(&st); 150 p9stat_free(&st);
114 151
115 if (over) { 152 if (over) {
116 err = 0; 153 err = 0;
117 goto free_and_exit; 154 goto unlock_and_exit;
118 } 155 }
156 rdir->head += reclen;
157 filp->f_pos += reclen;
119 } 158 }
120 } 159 }
121 160
122free_and_exit: 161unlock_and_exit:
123 kfree(statbuf); 162 mutex_unlock(&rdir->mutex);
163exit:
124 return err; 164 return err;
125} 165}
126 166