aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--fs/9p/vfs_dir.c94
-rw-r--r--include/net/9p/client.h7
-rw-r--r--net/9p/client.c5
3 files changed, 72 insertions, 34 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
diff --git a/include/net/9p/client.h b/include/net/9p/client.h
index e26812274b75..fb00b329f0d3 100644
--- a/include/net/9p/client.h
+++ b/include/net/9p/client.h
@@ -159,8 +159,7 @@ struct p9_client {
159 * @qid: the &p9_qid server identifier this handle points to 159 * @qid: the &p9_qid server identifier this handle points to
160 * @iounit: the server reported maximum transaction size for this file 160 * @iounit: the server reported maximum transaction size for this file
161 * @uid: the numeric uid of the local user who owns this handle 161 * @uid: the numeric uid of the local user who owns this handle
162 * @aux: transport specific information (unused?) 162 * @rdir: readdir accounting structure (allocated on demand)
163 * @rdir_fpos: tracks offset of file position when reading directory contents
164 * @flist: per-client-instance fid tracking 163 * @flist: per-client-instance fid tracking
165 * @dlist: per-dentry fid tracking 164 * @dlist: per-dentry fid tracking
166 * 165 *
@@ -174,9 +173,9 @@ struct p9_fid {
174 struct p9_qid qid; 173 struct p9_qid qid;
175 u32 iounit; 174 u32 iounit;
176 uid_t uid; 175 uid_t uid;
177 void *aux;
178 176
179 int rdir_fpos; 177 void *rdir;
178
180 struct list_head flist; 179 struct list_head flist;
181 struct list_head dlist; /* list of all fids attached to a dentry */ 180 struct list_head dlist; /* list of all fids attached to a dentry */
182}; 181};
diff --git a/net/9p/client.c b/net/9p/client.c
index 5bf5f227dbe0..8af95b2dddd6 100644
--- a/net/9p/client.c
+++ b/net/9p/client.c
@@ -582,11 +582,9 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt)
582 582
583 memset(&fid->qid, 0, sizeof(struct p9_qid)); 583 memset(&fid->qid, 0, sizeof(struct p9_qid));
584 fid->mode = -1; 584 fid->mode = -1;
585 fid->rdir_fpos = 0;
586 fid->uid = current_fsuid(); 585 fid->uid = current_fsuid();
587 fid->clnt = clnt; 586 fid->clnt = clnt;
588 fid->aux = NULL; 587 fid->rdir = NULL;
589
590 spin_lock_irqsave(&clnt->lock, flags); 588 spin_lock_irqsave(&clnt->lock, flags);
591 list_add(&fid->flist, &clnt->fidlist); 589 list_add(&fid->flist, &clnt->fidlist);
592 spin_unlock_irqrestore(&clnt->lock, flags); 590 spin_unlock_irqrestore(&clnt->lock, flags);
@@ -609,6 +607,7 @@ static void p9_fid_destroy(struct p9_fid *fid)
609 spin_lock_irqsave(&clnt->lock, flags); 607 spin_lock_irqsave(&clnt->lock, flags);
610 list_del(&fid->flist); 608 list_del(&fid->flist);
611 spin_unlock_irqrestore(&clnt->lock, flags); 609 spin_unlock_irqrestore(&clnt->lock, flags);
610 kfree(fid->rdir);
612 kfree(fid); 611 kfree(fid);
613} 612}
614 613