diff options
-rw-r--r-- | fs/9p/vfs_dir.c | 134 | ||||
-rw-r--r-- | include/net/9p/9p.h | 17 | ||||
-rw-r--r-- | include/net/9p/client.h | 18 | ||||
-rw-r--r-- | net/9p/client.c | 47 | ||||
-rw-r--r-- | net/9p/protocol.c | 27 |
5 files changed, 227 insertions, 16 deletions
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 36d961f342af..16c8a2a98c1b 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c | |||
@@ -87,29 +87,19 @@ static void p9stat_init(struct p9_wstat *stbuf) | |||
87 | } | 87 | } |
88 | 88 | ||
89 | /** | 89 | /** |
90 | * v9fs_dir_readdir - read a directory | 90 | * v9fs_alloc_rdir_buf - Allocate buffer used for read and readdir |
91 | * @filp: opened file structure | 91 | * @filp: opened file structure |
92 | * @dirent: directory structure ??? | 92 | * @buflen: Length in bytes of buffer to allocate |
93 | * @filldir: function to populate directory structure ??? | ||
94 | * | 93 | * |
95 | */ | 94 | */ |
96 | 95 | ||
97 | static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) | 96 | static int v9fs_alloc_rdir_buf(struct file *filp, int buflen) |
98 | { | 97 | { |
99 | int over; | ||
100 | struct p9_wstat st; | ||
101 | int err = 0; | ||
102 | struct p9_fid *fid; | ||
103 | int buflen; | ||
104 | int reclen = 0; | ||
105 | struct p9_rdir *rdir; | 98 | struct p9_rdir *rdir; |
99 | struct p9_fid *fid; | ||
100 | int err = 0; | ||
106 | 101 | ||
107 | P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); | ||
108 | fid = filp->private_data; | 102 | fid = filp->private_data; |
109 | |||
110 | buflen = fid->clnt->msize - P9_IOHDRSZ; | ||
111 | |||
112 | /* allocate rdir on demand */ | ||
113 | if (!fid->rdir) { | 103 | if (!fid->rdir) { |
114 | rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); | 104 | rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL); |
115 | 105 | ||
@@ -128,6 +118,36 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
128 | spin_unlock(&filp->f_dentry->d_lock); | 118 | spin_unlock(&filp->f_dentry->d_lock); |
129 | kfree(rdir); | 119 | kfree(rdir); |
130 | } | 120 | } |
121 | exit: | ||
122 | return err; | ||
123 | } | ||
124 | |||
125 | /** | ||
126 | * v9fs_dir_readdir - read a directory | ||
127 | * @filp: opened file structure | ||
128 | * @dirent: directory structure ??? | ||
129 | * @filldir: function to populate directory structure ??? | ||
130 | * | ||
131 | */ | ||
132 | |||
133 | static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir) | ||
134 | { | ||
135 | int over; | ||
136 | struct p9_wstat st; | ||
137 | int err = 0; | ||
138 | struct p9_fid *fid; | ||
139 | int buflen; | ||
140 | int reclen = 0; | ||
141 | struct p9_rdir *rdir; | ||
142 | |||
143 | P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); | ||
144 | fid = filp->private_data; | ||
145 | |||
146 | buflen = fid->clnt->msize - P9_IOHDRSZ; | ||
147 | |||
148 | err = v9fs_alloc_rdir_buf(filp, buflen); | ||
149 | if (err) | ||
150 | goto exit; | ||
131 | rdir = (struct p9_rdir *) fid->rdir; | 151 | rdir = (struct p9_rdir *) fid->rdir; |
132 | 152 | ||
133 | err = mutex_lock_interruptible(&rdir->mutex); | 153 | err = mutex_lock_interruptible(&rdir->mutex); |
@@ -176,6 +196,88 @@ exit: | |||
176 | return err; | 196 | return err; |
177 | } | 197 | } |
178 | 198 | ||
199 | /** | ||
200 | * v9fs_dir_readdir_dotl - read a directory | ||
201 | * @filp: opened file structure | ||
202 | * @dirent: buffer to fill dirent structures | ||
203 | * @filldir: function to populate dirent structures | ||
204 | * | ||
205 | */ | ||
206 | static int v9fs_dir_readdir_dotl(struct file *filp, void *dirent, | ||
207 | filldir_t filldir) | ||
208 | { | ||
209 | int over; | ||
210 | int err = 0; | ||
211 | struct p9_fid *fid; | ||
212 | int buflen; | ||
213 | struct p9_rdir *rdir; | ||
214 | struct p9_dirent curdirent; | ||
215 | u64 oldoffset = 0; | ||
216 | |||
217 | P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name); | ||
218 | fid = filp->private_data; | ||
219 | |||
220 | buflen = fid->clnt->msize - P9_READDIRHDRSZ; | ||
221 | |||
222 | err = v9fs_alloc_rdir_buf(filp, buflen); | ||
223 | if (err) | ||
224 | goto exit; | ||
225 | rdir = (struct p9_rdir *) fid->rdir; | ||
226 | |||
227 | err = mutex_lock_interruptible(&rdir->mutex); | ||
228 | if (err) | ||
229 | return err; | ||
230 | |||
231 | while (err == 0) { | ||
232 | if (rdir->tail == rdir->head) { | ||
233 | err = p9_client_readdir(fid, rdir->buf, buflen, | ||
234 | filp->f_pos); | ||
235 | if (err <= 0) | ||
236 | goto unlock_and_exit; | ||
237 | |||
238 | rdir->head = 0; | ||
239 | rdir->tail = err; | ||
240 | } | ||
241 | |||
242 | while (rdir->head < rdir->tail) { | ||
243 | |||
244 | err = p9dirent_read(rdir->buf + rdir->head, | ||
245 | buflen - rdir->head, &curdirent, | ||
246 | fid->clnt->proto_version); | ||
247 | if (err < 0) { | ||
248 | P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err); | ||
249 | err = -EIO; | ||
250 | goto unlock_and_exit; | ||
251 | } | ||
252 | |||
253 | /* d_off in dirent structure tracks the offset into | ||
254 | * the next dirent in the dir. However, filldir() | ||
255 | * expects offset into the current dirent. Hence | ||
256 | * while calling filldir send the offset from the | ||
257 | * previous dirent structure. | ||
258 | */ | ||
259 | over = filldir(dirent, curdirent.d_name, | ||
260 | strlen(curdirent.d_name), | ||
261 | oldoffset, v9fs_qid2ino(&curdirent.qid), | ||
262 | curdirent.d_type); | ||
263 | oldoffset = curdirent.d_off; | ||
264 | |||
265 | if (over) { | ||
266 | err = 0; | ||
267 | goto unlock_and_exit; | ||
268 | } | ||
269 | |||
270 | filp->f_pos = curdirent.d_off; | ||
271 | rdir->head += err; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | unlock_and_exit: | ||
276 | mutex_unlock(&rdir->mutex); | ||
277 | exit: | ||
278 | return err; | ||
279 | } | ||
280 | |||
179 | 281 | ||
180 | /** | 282 | /** |
181 | * v9fs_dir_release - close a directory | 283 | * v9fs_dir_release - close a directory |
@@ -207,7 +309,7 @@ const struct file_operations v9fs_dir_operations = { | |||
207 | const struct file_operations v9fs_dir_operations_dotl = { | 309 | const struct file_operations v9fs_dir_operations_dotl = { |
208 | .read = generic_read_dir, | 310 | .read = generic_read_dir, |
209 | .llseek = generic_file_llseek, | 311 | .llseek = generic_file_llseek, |
210 | .readdir = v9fs_dir_readdir, | 312 | .readdir = v9fs_dir_readdir_dotl, |
211 | .open = v9fs_file_open, | 313 | .open = v9fs_file_open, |
212 | .release = v9fs_dir_release, | 314 | .release = v9fs_dir_release, |
213 | }; | 315 | }; |
diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 156c26bb8bd7..f1b0b310265d 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h | |||
@@ -133,6 +133,8 @@ enum p9_msg_t { | |||
133 | P9_RSTATFS, | 133 | P9_RSTATFS, |
134 | P9_TRENAME = 20, | 134 | P9_TRENAME = 20, |
135 | P9_RRENAME, | 135 | P9_RRENAME, |
136 | P9_TREADDIR = 40, | ||
137 | P9_RREADDIR, | ||
136 | P9_TVERSION = 100, | 138 | P9_TVERSION = 100, |
137 | P9_RVERSION, | 139 | P9_RVERSION, |
138 | P9_TAUTH = 102, | 140 | P9_TAUTH = 102, |
@@ -275,6 +277,9 @@ enum p9_qid_t { | |||
275 | /* ample room for Twrite/Rread header */ | 277 | /* ample room for Twrite/Rread header */ |
276 | #define P9_IOHDRSZ 24 | 278 | #define P9_IOHDRSZ 24 |
277 | 279 | ||
280 | /* Room for readdir header */ | ||
281 | #define P9_READDIRHDRSZ 24 | ||
282 | |||
278 | /** | 283 | /** |
279 | * struct p9_str - length prefixed string type | 284 | * struct p9_str - length prefixed string type |
280 | * @len: length of the string | 285 | * @len: length of the string |
@@ -485,6 +490,18 @@ struct p9_rwrite { | |||
485 | u32 count; | 490 | u32 count; |
486 | }; | 491 | }; |
487 | 492 | ||
493 | struct p9_treaddir { | ||
494 | u32 fid; | ||
495 | u64 offset; | ||
496 | u32 count; | ||
497 | }; | ||
498 | |||
499 | struct p9_rreaddir { | ||
500 | u32 count; | ||
501 | u8 *data; | ||
502 | }; | ||
503 | |||
504 | |||
488 | struct p9_tclunk { | 505 | struct p9_tclunk { |
489 | u32 fid; | 506 | u32 fid; |
490 | }; | 507 | }; |
diff --git a/include/net/9p/client.h b/include/net/9p/client.h index 7dd3ed85c782..2ec93685e6db 100644 --- a/include/net/9p/client.h +++ b/include/net/9p/client.h | |||
@@ -195,6 +195,21 @@ struct p9_fid { | |||
195 | struct list_head dlist; /* list of all fids attached to a dentry */ | 195 | struct list_head dlist; /* list of all fids attached to a dentry */ |
196 | }; | 196 | }; |
197 | 197 | ||
198 | /** | ||
199 | * struct p9_dirent - directory entry structure | ||
200 | * @qid: The p9 server qid for this dirent | ||
201 | * @d_off: offset to the next dirent | ||
202 | * @d_type: type of file | ||
203 | * @d_name: file name | ||
204 | */ | ||
205 | |||
206 | struct p9_dirent { | ||
207 | struct p9_qid qid; | ||
208 | u64 d_off; | ||
209 | unsigned char d_type; | ||
210 | char d_name[256]; | ||
211 | }; | ||
212 | |||
198 | int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb); | 213 | int p9_client_statfs(struct p9_fid *fid, struct p9_rstatfs *sb); |
199 | int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name); | 214 | int p9_client_rename(struct p9_fid *fid, struct p9_fid *newdirfid, char *name); |
200 | int p9_client_version(struct p9_client *); | 215 | int p9_client_version(struct p9_client *); |
@@ -217,6 +232,9 @@ int p9_client_read(struct p9_fid *fid, char *data, char __user *udata, | |||
217 | u64 offset, u32 count); | 232 | u64 offset, u32 count); |
218 | int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, | 233 | int p9_client_write(struct p9_fid *fid, char *data, const char __user *udata, |
219 | u64 offset, u32 count); | 234 | u64 offset, u32 count); |
235 | int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset); | ||
236 | int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, | ||
237 | int proto_version); | ||
220 | struct p9_wstat *p9_client_stat(struct p9_fid *fid); | 238 | struct p9_wstat *p9_client_stat(struct p9_fid *fid); |
221 | int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); | 239 | int p9_client_wstat(struct p9_fid *fid, struct p9_wstat *wst); |
222 | 240 | ||
diff --git a/net/9p/client.c b/net/9p/client.c index 37c8da07a80b..a80357483a47 100644 --- a/net/9p/client.c +++ b/net/9p/client.c | |||
@@ -1432,3 +1432,50 @@ error: | |||
1432 | } | 1432 | } |
1433 | EXPORT_SYMBOL(p9_client_rename); | 1433 | EXPORT_SYMBOL(p9_client_rename); |
1434 | 1434 | ||
1435 | int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset) | ||
1436 | { | ||
1437 | int err, rsize, total; | ||
1438 | struct p9_client *clnt; | ||
1439 | struct p9_req_t *req; | ||
1440 | char *dataptr; | ||
1441 | |||
1442 | P9_DPRINTK(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n", | ||
1443 | fid->fid, (long long unsigned) offset, count); | ||
1444 | |||
1445 | err = 0; | ||
1446 | clnt = fid->clnt; | ||
1447 | total = 0; | ||
1448 | |||
1449 | rsize = fid->iounit; | ||
1450 | if (!rsize || rsize > clnt->msize-P9_READDIRHDRSZ) | ||
1451 | rsize = clnt->msize - P9_READDIRHDRSZ; | ||
1452 | |||
1453 | if (count < rsize) | ||
1454 | rsize = count; | ||
1455 | |||
1456 | req = p9_client_rpc(clnt, P9_TREADDIR, "dqd", fid->fid, offset, rsize); | ||
1457 | if (IS_ERR(req)) { | ||
1458 | err = PTR_ERR(req); | ||
1459 | goto error; | ||
1460 | } | ||
1461 | |||
1462 | err = p9pdu_readf(req->rc, clnt->proto_version, "D", &count, &dataptr); | ||
1463 | if (err) { | ||
1464 | p9pdu_dump(1, req->rc); | ||
1465 | goto free_and_error; | ||
1466 | } | ||
1467 | |||
1468 | P9_DPRINTK(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count); | ||
1469 | |||
1470 | if (data) | ||
1471 | memmove(data, dataptr, count); | ||
1472 | |||
1473 | p9_free_req(clnt, req); | ||
1474 | return count; | ||
1475 | |||
1476 | free_and_error: | ||
1477 | p9_free_req(clnt, req); | ||
1478 | error: | ||
1479 | return err; | ||
1480 | } | ||
1481 | EXPORT_SYMBOL(p9_client_readdir); | ||
diff --git a/net/9p/protocol.c b/net/9p/protocol.c index 149f82160130..b645c8263538 100644 --- a/net/9p/protocol.c +++ b/net/9p/protocol.c | |||
@@ -580,3 +580,30 @@ void p9pdu_reset(struct p9_fcall *pdu) | |||
580 | pdu->offset = 0; | 580 | pdu->offset = 0; |
581 | pdu->size = 0; | 581 | pdu->size = 0; |
582 | } | 582 | } |
583 | |||
584 | int p9dirent_read(char *buf, int len, struct p9_dirent *dirent, | ||
585 | int proto_version) | ||
586 | { | ||
587 | struct p9_fcall fake_pdu; | ||
588 | int ret; | ||
589 | char *nameptr; | ||
590 | |||
591 | fake_pdu.size = len; | ||
592 | fake_pdu.capacity = len; | ||
593 | fake_pdu.sdata = buf; | ||
594 | fake_pdu.offset = 0; | ||
595 | |||
596 | ret = p9pdu_readf(&fake_pdu, proto_version, "Qqbs", &dirent->qid, | ||
597 | &dirent->d_off, &dirent->d_type, &nameptr); | ||
598 | if (ret) { | ||
599 | P9_DPRINTK(P9_DEBUG_9P, "<<< p9dirent_read failed: %d\n", ret); | ||
600 | p9pdu_dump(1, &fake_pdu); | ||
601 | goto out; | ||
602 | } | ||
603 | |||
604 | strcpy(dirent->d_name, nameptr); | ||
605 | |||
606 | out: | ||
607 | return fake_pdu.offset; | ||
608 | } | ||
609 | EXPORT_SYMBOL(p9dirent_read); | ||