diff options
| -rw-r--r-- | fs/fuse/dev.c | 131 | ||||
| -rw-r--r-- | fs/fuse/fuse_i.h | 1 | ||||
| -rw-r--r-- | include/linux/fuse.h | 21 |
3 files changed, 152 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; |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 61267d8d527b..57d4a3a0f102 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
| @@ -272,6 +272,7 @@ struct fuse_req { | |||
| 272 | struct fuse_write_in in; | 272 | struct fuse_write_in in; |
| 273 | struct fuse_write_out out; | 273 | struct fuse_write_out out; |
| 274 | } write; | 274 | } write; |
| 275 | struct fuse_notify_retrieve_in retrieve_in; | ||
| 275 | struct fuse_lk_in lk_in; | 276 | struct fuse_lk_in lk_in; |
| 276 | } misc; | 277 | } misc; |
| 277 | 278 | ||
diff --git a/include/linux/fuse.h b/include/linux/fuse.h index a90bd49834aa..c3c578e09833 100644 --- a/include/linux/fuse.h +++ b/include/linux/fuse.h | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | * | 40 | * |
| 41 | * 7.15 | 41 | * 7.15 |
| 42 | * - add store notify | 42 | * - add store notify |
| 43 | * - add retrieve notify | ||
| 43 | */ | 44 | */ |
| 44 | 45 | ||
| 45 | #ifndef _LINUX_FUSE_H | 46 | #ifndef _LINUX_FUSE_H |
| @@ -254,6 +255,7 @@ enum fuse_opcode { | |||
| 254 | FUSE_DESTROY = 38, | 255 | FUSE_DESTROY = 38, |
| 255 | FUSE_IOCTL = 39, | 256 | FUSE_IOCTL = 39, |
| 256 | FUSE_POLL = 40, | 257 | FUSE_POLL = 40, |
| 258 | FUSE_NOTIFY_REPLY = 41, | ||
| 257 | 259 | ||
| 258 | /* CUSE specific operations */ | 260 | /* CUSE specific operations */ |
| 259 | CUSE_INIT = 4096, | 261 | CUSE_INIT = 4096, |
| @@ -264,6 +266,7 @@ enum fuse_notify_code { | |||
| 264 | FUSE_NOTIFY_INVAL_INODE = 2, | 266 | FUSE_NOTIFY_INVAL_INODE = 2, |
| 265 | FUSE_NOTIFY_INVAL_ENTRY = 3, | 267 | FUSE_NOTIFY_INVAL_ENTRY = 3, |
| 266 | FUSE_NOTIFY_STORE = 4, | 268 | FUSE_NOTIFY_STORE = 4, |
| 269 | FUSE_NOTIFY_RETRIEVE = 5, | ||
| 267 | FUSE_NOTIFY_CODE_MAX, | 270 | FUSE_NOTIFY_CODE_MAX, |
| 268 | }; | 271 | }; |
| 269 | 272 | ||
| @@ -579,4 +582,22 @@ struct fuse_notify_store_out { | |||
| 579 | __u32 padding; | 582 | __u32 padding; |
| 580 | }; | 583 | }; |
| 581 | 584 | ||
| 585 | struct fuse_notify_retrieve_out { | ||
| 586 | __u64 notify_unique; | ||
| 587 | __u64 nodeid; | ||
| 588 | __u64 offset; | ||
| 589 | __u32 size; | ||
| 590 | __u32 padding; | ||
| 591 | }; | ||
| 592 | |||
| 593 | /* Matches the size of fuse_write_in */ | ||
| 594 | struct fuse_notify_retrieve_in { | ||
| 595 | __u64 dummy1; | ||
| 596 | __u64 offset; | ||
| 597 | __u32 size; | ||
| 598 | __u32 dummy2; | ||
| 599 | __u64 dummy3; | ||
| 600 | __u64 dummy4; | ||
| 601 | }; | ||
| 602 | |||
| 582 | #endif /* _LINUX_FUSE_H */ | 603 | #endif /* _LINUX_FUSE_H */ |
