diff options
author | Eric Van Hensbergen <ericvh@gmail.com> | 2009-11-02 09:39:28 -0500 |
---|---|---|
committer | Eric Van Hensbergen <ericvh@gmail.com> | 2009-11-02 09:43:45 -0500 |
commit | 3e2796a90cf349527e50b3bc4d0b2f4019b1ce7a (patch) | |
tree | 80bddc0f5d36a589db5a77b9b60e4c94c75994ed /fs/9p/vfs_dir.c | |
parent | 2511cd0b3b9e9b1c3e9360cc565c3745ac3f3f3f (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.c | 94 |
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 | |||
53 | struct 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 | ||
122 | free_and_exit: | 161 | unlock_and_exit: |
123 | kfree(statbuf); | 162 | mutex_unlock(&rdir->mutex); |
163 | exit: | ||
124 | return err; | 164 | return err; |
125 | } | 165 | } |
126 | 166 | ||