diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2010-07-12 08:41:40 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2010-07-12 08:41:40 -0400 |
commit | 2d45ba381a74a743eeaa2b06c7c5c0d2bf73ba1a (patch) | |
tree | 8a195d072cab99c088004b74ac5889252f348542 /fs/fuse/dev.c | |
parent | a1d75f258230b75d46aecdf28b2e732413028863 (diff) |
fuse: add retrieve request
Userspace filesystem can request data to be retrieved from the inode's
mapping. This request is synchronous and the retrieved data is queued
as a new request. If the write to the fuse device returns an error
then the retrieve request was not completed and a reply will not be
sent.
Only present pages are returned in the retrieve reply. Retrieving
stops when it finds a non-present page and only data prior to that is
returned.
This request doesn't change the dirty state of pages.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Diffstat (limited to 'fs/fuse/dev.c')
-rw-r--r-- | fs/fuse/dev.c | 131 |
1 files changed, 130 insertions, 1 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 8e01c865586e..69ad053ffd78 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c | |||
@@ -239,7 +239,6 @@ static u64 fuse_get_unique(struct fuse_conn *fc) | |||
239 | 239 | ||
240 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) | 240 | static void queue_request(struct fuse_conn *fc, struct fuse_req *req) |
241 | { | 241 | { |
242 | req->in.h.unique = fuse_get_unique(fc); | ||
243 | req->in.h.len = sizeof(struct fuse_in_header) + | 242 | req->in.h.len = sizeof(struct fuse_in_header) + |
244 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); | 243 | len_args(req->in.numargs, (struct fuse_arg *) req->in.args); |
245 | list_add_tail(&req->list, &fc->pending); | 244 | list_add_tail(&req->list, &fc->pending); |
@@ -261,6 +260,7 @@ static void flush_bg_queue(struct fuse_conn *fc) | |||
261 | req = list_entry(fc->bg_queue.next, struct fuse_req, list); | 260 | req = list_entry(fc->bg_queue.next, struct fuse_req, list); |
262 | list_del(&req->list); | 261 | list_del(&req->list); |
263 | fc->active_background++; | 262 | fc->active_background++; |
263 | req->in.h.unique = fuse_get_unique(fc); | ||
264 | queue_request(fc, req); | 264 | queue_request(fc, req); |
265 | } | 265 | } |
266 | } | 266 | } |
@@ -398,6 +398,7 @@ void fuse_request_send(struct fuse_conn *fc, struct fuse_req *req) | |||
398 | else if (fc->conn_error) | 398 | else if (fc->conn_error) |
399 | req->out.h.error = -ECONNREFUSED; | 399 | req->out.h.error = -ECONNREFUSED; |
400 | else { | 400 | else { |
401 | req->in.h.unique = fuse_get_unique(fc); | ||
401 | queue_request(fc, req); | 402 | queue_request(fc, req); |
402 | /* acquire extra reference, since request is still needed | 403 | /* acquire extra reference, since request is still needed |
403 | after request_end() */ | 404 | after request_end() */ |
@@ -450,6 +451,23 @@ void fuse_request_send_background(struct fuse_conn *fc, struct fuse_req *req) | |||
450 | } | 451 | } |
451 | EXPORT_SYMBOL_GPL(fuse_request_send_background); | 452 | EXPORT_SYMBOL_GPL(fuse_request_send_background); |
452 | 453 | ||
454 | static int fuse_request_send_notify_reply(struct fuse_conn *fc, | ||
455 | struct fuse_req *req, u64 unique) | ||
456 | { | ||
457 | int err = -ENODEV; | ||
458 | |||
459 | req->isreply = 0; | ||
460 | req->in.h.unique = unique; | ||
461 | spin_lock(&fc->lock); | ||
462 | if (fc->connected) { | ||
463 | queue_request(fc, req); | ||
464 | err = 0; | ||
465 | } | ||
466 | spin_unlock(&fc->lock); | ||
467 | |||
468 | return err; | ||
469 | } | ||
470 | |||
453 | /* | 471 | /* |
454 | * Called under fc->lock | 472 | * Called under fc->lock |
455 | * | 473 | * |
@@ -1316,6 +1334,114 @@ out_finish: | |||
1316 | return err; | 1334 | return err; |
1317 | } | 1335 | } |
1318 | 1336 | ||
1337 | static void fuse_retrieve_end(struct fuse_conn *fc, struct fuse_req *req) | ||
1338 | { | ||
1339 | int i; | ||
1340 | |||
1341 | for (i = 0; i < req->num_pages; i++) { | ||
1342 | struct page *page = req->pages[i]; | ||
1343 | page_cache_release(page); | ||
1344 | } | ||
1345 | } | ||
1346 | |||
1347 | static int fuse_retrieve(struct fuse_conn *fc, struct inode *inode, | ||
1348 | struct fuse_notify_retrieve_out *outarg) | ||
1349 | { | ||
1350 | int err; | ||
1351 | struct address_space *mapping = inode->i_mapping; | ||
1352 | struct fuse_req *req; | ||
1353 | pgoff_t index; | ||
1354 | loff_t file_size; | ||
1355 | unsigned int num; | ||
1356 | unsigned int offset; | ||
1357 | size_t total_len; | ||
1358 | |||
1359 | req = fuse_get_req(fc); | ||
1360 | if (IS_ERR(req)) | ||
1361 | return PTR_ERR(req); | ||
1362 | |||
1363 | offset = outarg->offset & ~PAGE_CACHE_MASK; | ||
1364 | |||
1365 | req->in.h.opcode = FUSE_NOTIFY_REPLY; | ||
1366 | req->in.h.nodeid = outarg->nodeid; | ||
1367 | req->in.numargs = 2; | ||
1368 | req->in.argpages = 1; | ||
1369 | req->page_offset = offset; | ||
1370 | req->end = fuse_retrieve_end; | ||
1371 | |||
1372 | index = outarg->offset >> PAGE_CACHE_SHIFT; | ||
1373 | file_size = i_size_read(inode); | ||
1374 | num = outarg->size; | ||
1375 | if (outarg->offset > file_size) | ||
1376 | num = 0; | ||
1377 | else if (outarg->offset + num > file_size) | ||
1378 | num = file_size - outarg->offset; | ||
1379 | |||
1380 | while (num) { | ||
1381 | struct page *page; | ||
1382 | unsigned int this_num; | ||
1383 | |||
1384 | page = find_get_page(mapping, index); | ||
1385 | if (!page) | ||
1386 | break; | ||
1387 | |||
1388 | this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); | ||
1389 | req->pages[req->num_pages] = page; | ||
1390 | req->num_pages++; | ||
1391 | |||
1392 | num -= this_num; | ||
1393 | total_len += this_num; | ||
1394 | } | ||
1395 | req->misc.retrieve_in.offset = outarg->offset; | ||
1396 | req->misc.retrieve_in.size = total_len; | ||
1397 | req->in.args[0].size = sizeof(req->misc.retrieve_in); | ||
1398 | req->in.args[0].value = &req->misc.retrieve_in; | ||
1399 | req->in.args[1].size = total_len; | ||
1400 | |||
1401 | err = fuse_request_send_notify_reply(fc, req, outarg->notify_unique); | ||
1402 | if (err) | ||
1403 | fuse_retrieve_end(fc, req); | ||
1404 | |||
1405 | return err; | ||
1406 | } | ||
1407 | |||
1408 | static int fuse_notify_retrieve(struct fuse_conn *fc, unsigned int size, | ||
1409 | struct fuse_copy_state *cs) | ||
1410 | { | ||
1411 | struct fuse_notify_retrieve_out outarg; | ||
1412 | struct inode *inode; | ||
1413 | int err; | ||
1414 | |||
1415 | err = -EINVAL; | ||
1416 | if (size != sizeof(outarg)) | ||
1417 | goto copy_finish; | ||
1418 | |||
1419 | err = fuse_copy_one(cs, &outarg, sizeof(outarg)); | ||
1420 | if (err) | ||
1421 | goto copy_finish; | ||
1422 | |||
1423 | fuse_copy_finish(cs); | ||
1424 | |||
1425 | down_read(&fc->killsb); | ||
1426 | err = -ENOENT; | ||
1427 | if (fc->sb) { | ||
1428 | u64 nodeid = outarg.nodeid; | ||
1429 | |||
1430 | inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); | ||
1431 | if (inode) { | ||
1432 | err = fuse_retrieve(fc, inode, &outarg); | ||
1433 | iput(inode); | ||
1434 | } | ||
1435 | } | ||
1436 | up_read(&fc->killsb); | ||
1437 | |||
1438 | return err; | ||
1439 | |||
1440 | copy_finish: | ||
1441 | fuse_copy_finish(cs); | ||
1442 | return err; | ||
1443 | } | ||
1444 | |||
1319 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | 1445 | static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, |
1320 | unsigned int size, struct fuse_copy_state *cs) | 1446 | unsigned int size, struct fuse_copy_state *cs) |
1321 | { | 1447 | { |
@@ -1332,6 +1458,9 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | |||
1332 | case FUSE_NOTIFY_STORE: | 1458 | case FUSE_NOTIFY_STORE: |
1333 | return fuse_notify_store(fc, size, cs); | 1459 | return fuse_notify_store(fc, size, cs); |
1334 | 1460 | ||
1461 | case FUSE_NOTIFY_RETRIEVE: | ||
1462 | return fuse_notify_retrieve(fc, size, cs); | ||
1463 | |||
1335 | default: | 1464 | default: |
1336 | fuse_copy_finish(cs); | 1465 | fuse_copy_finish(cs); |
1337 | return -EINVAL; | 1466 | return -EINVAL; |