diff options
Diffstat (limited to 'fs/fuse/dev.c')
| -rw-r--r-- | fs/fuse/dev.c | 229 |
1 files changed, 223 insertions, 6 deletions
diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 9424796d6634..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 | * |
| @@ -535,13 +553,13 @@ static void fuse_copy_finish(struct fuse_copy_state *cs) | |||
| 535 | if (!cs->write) { | 553 | if (!cs->write) { |
| 536 | buf->ops->unmap(cs->pipe, buf, cs->mapaddr); | 554 | buf->ops->unmap(cs->pipe, buf, cs->mapaddr); |
| 537 | } else { | 555 | } else { |
| 538 | kunmap_atomic(cs->mapaddr, KM_USER0); | 556 | kunmap(buf->page); |
| 539 | buf->len = PAGE_SIZE - cs->len; | 557 | buf->len = PAGE_SIZE - cs->len; |
| 540 | } | 558 | } |
| 541 | cs->currbuf = NULL; | 559 | cs->currbuf = NULL; |
| 542 | cs->mapaddr = NULL; | 560 | cs->mapaddr = NULL; |
| 543 | } else if (cs->mapaddr) { | 561 | } else if (cs->mapaddr) { |
| 544 | kunmap_atomic(cs->mapaddr, KM_USER0); | 562 | kunmap(cs->pg); |
| 545 | if (cs->write) { | 563 | if (cs->write) { |
| 546 | flush_dcache_page(cs->pg); | 564 | flush_dcache_page(cs->pg); |
| 547 | set_page_dirty_lock(cs->pg); | 565 | set_page_dirty_lock(cs->pg); |
| @@ -572,7 +590,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
| 572 | 590 | ||
| 573 | BUG_ON(!cs->nr_segs); | 591 | BUG_ON(!cs->nr_segs); |
| 574 | cs->currbuf = buf; | 592 | cs->currbuf = buf; |
| 575 | cs->mapaddr = buf->ops->map(cs->pipe, buf, 1); | 593 | cs->mapaddr = buf->ops->map(cs->pipe, buf, 0); |
| 576 | cs->len = buf->len; | 594 | cs->len = buf->len; |
| 577 | cs->buf = cs->mapaddr + buf->offset; | 595 | cs->buf = cs->mapaddr + buf->offset; |
| 578 | cs->pipebufs++; | 596 | cs->pipebufs++; |
| @@ -592,7 +610,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
| 592 | buf->len = 0; | 610 | buf->len = 0; |
| 593 | 611 | ||
| 594 | cs->currbuf = buf; | 612 | cs->currbuf = buf; |
| 595 | cs->mapaddr = kmap_atomic(page, KM_USER0); | 613 | cs->mapaddr = kmap(page); |
| 596 | cs->buf = cs->mapaddr; | 614 | cs->buf = cs->mapaddr; |
| 597 | cs->len = PAGE_SIZE; | 615 | cs->len = PAGE_SIZE; |
| 598 | cs->pipebufs++; | 616 | cs->pipebufs++; |
| @@ -611,7 +629,7 @@ static int fuse_copy_fill(struct fuse_copy_state *cs) | |||
| 611 | return err; | 629 | return err; |
| 612 | BUG_ON(err != 1); | 630 | BUG_ON(err != 1); |
| 613 | offset = cs->addr % PAGE_SIZE; | 631 | offset = cs->addr % PAGE_SIZE; |
| 614 | cs->mapaddr = kmap_atomic(cs->pg, KM_USER0); | 632 | cs->mapaddr = kmap(cs->pg); |
| 615 | cs->buf = cs->mapaddr + offset; | 633 | cs->buf = cs->mapaddr + offset; |
| 616 | cs->len = min(PAGE_SIZE - offset, cs->seglen); | 634 | cs->len = min(PAGE_SIZE - offset, cs->seglen); |
| 617 | cs->seglen -= cs->len; | 635 | cs->seglen -= cs->len; |
| @@ -1231,6 +1249,199 @@ err: | |||
| 1231 | return err; | 1249 | return err; |
| 1232 | } | 1250 | } |
| 1233 | 1251 | ||
| 1252 | static int fuse_notify_store(struct fuse_conn *fc, unsigned int size, | ||
| 1253 | struct fuse_copy_state *cs) | ||
| 1254 | { | ||
| 1255 | struct fuse_notify_store_out outarg; | ||
| 1256 | struct inode *inode; | ||
| 1257 | struct address_space *mapping; | ||
| 1258 | u64 nodeid; | ||
| 1259 | int err; | ||
| 1260 | pgoff_t index; | ||
| 1261 | unsigned int offset; | ||
| 1262 | unsigned int num; | ||
| 1263 | loff_t file_size; | ||
| 1264 | loff_t end; | ||
| 1265 | |||
| 1266 | err = -EINVAL; | ||
| 1267 | if (size < sizeof(outarg)) | ||
| 1268 | goto out_finish; | ||
| 1269 | |||
| 1270 | err = fuse_copy_one(cs, &outarg, sizeof(outarg)); | ||
| 1271 | if (err) | ||
| 1272 | goto out_finish; | ||
| 1273 | |||
| 1274 | err = -EINVAL; | ||
| 1275 | if (size - sizeof(outarg) != outarg.size) | ||
| 1276 | goto out_finish; | ||
| 1277 | |||
| 1278 | nodeid = outarg.nodeid; | ||
| 1279 | |||
| 1280 | down_read(&fc->killsb); | ||
| 1281 | |||
| 1282 | err = -ENOENT; | ||
| 1283 | if (!fc->sb) | ||
| 1284 | goto out_up_killsb; | ||
| 1285 | |||
| 1286 | inode = ilookup5(fc->sb, nodeid, fuse_inode_eq, &nodeid); | ||
| 1287 | if (!inode) | ||
| 1288 | goto out_up_killsb; | ||
| 1289 | |||
| 1290 | mapping = inode->i_mapping; | ||
| 1291 | index = outarg.offset >> PAGE_CACHE_SHIFT; | ||
| 1292 | offset = outarg.offset & ~PAGE_CACHE_MASK; | ||
| 1293 | file_size = i_size_read(inode); | ||
| 1294 | end = outarg.offset + outarg.size; | ||
| 1295 | if (end > file_size) { | ||
| 1296 | file_size = end; | ||
| 1297 | fuse_write_update_size(inode, file_size); | ||
| 1298 | } | ||
| 1299 | |||
| 1300 | num = outarg.size; | ||
| 1301 | while (num) { | ||
| 1302 | struct page *page; | ||
| 1303 | unsigned int this_num; | ||
| 1304 | |||
| 1305 | err = -ENOMEM; | ||
| 1306 | page = find_or_create_page(mapping, index, | ||
| 1307 | mapping_gfp_mask(mapping)); | ||
| 1308 | if (!page) | ||
| 1309 | goto out_iput; | ||
| 1310 | |||
| 1311 | this_num = min_t(unsigned, num, PAGE_CACHE_SIZE - offset); | ||
| 1312 | err = fuse_copy_page(cs, &page, offset, this_num, 0); | ||
| 1313 | if (!err && offset == 0 && (num != 0 || file_size == end)) | ||
| 1314 | SetPageUptodate(page); | ||
| 1315 | unlock_page(page); | ||
| 1316 | page_cache_release(page); | ||
| 1317 | |||
| 1318 | if (err) | ||
| 1319 | goto out_iput; | ||
| 1320 | |||
| 1321 | num -= this_num; | ||
| 1322 | offset = 0; | ||
| 1323 | index++; | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | err = 0; | ||
| 1327 | |||
| 1328 | out_iput: | ||
| 1329 | iput(inode); | ||
| 1330 | out_up_killsb: | ||
| 1331 | up_read(&fc->killsb); | ||
| 1332 | out_finish: | ||
| 1333 | fuse_copy_finish(cs); | ||
| 1334 | return err; | ||
| 1335 | } | ||
| 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 | |||
| 1234 | 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, |
| 1235 | unsigned int size, struct fuse_copy_state *cs) | 1446 | unsigned int size, struct fuse_copy_state *cs) |
| 1236 | { | 1447 | { |
| @@ -1244,6 +1455,12 @@ static int fuse_notify(struct fuse_conn *fc, enum fuse_notify_code code, | |||
| 1244 | case FUSE_NOTIFY_INVAL_ENTRY: | 1455 | case FUSE_NOTIFY_INVAL_ENTRY: |
| 1245 | return fuse_notify_inval_entry(fc, size, cs); | 1456 | return fuse_notify_inval_entry(fc, size, cs); |
| 1246 | 1457 | ||
| 1458 | case FUSE_NOTIFY_STORE: | ||
| 1459 | return fuse_notify_store(fc, size, cs); | ||
| 1460 | |||
| 1461 | case FUSE_NOTIFY_RETRIEVE: | ||
| 1462 | return fuse_notify_retrieve(fc, size, cs); | ||
| 1463 | |||
| 1247 | default: | 1464 | default: |
| 1248 | fuse_copy_finish(cs); | 1465 | fuse_copy_finish(cs); |
| 1249 | return -EINVAL; | 1466 | return -EINVAL; |
