diff options
Diffstat (limited to 'fs/read_write.c')
-rw-r--r-- | fs/read_write.c | 101 |
1 files changed, 67 insertions, 34 deletions
diff --git a/fs/read_write.c b/fs/read_write.c index 679dd535380f..32d54cca9bd9 100644 --- a/fs/read_write.c +++ b/fs/read_write.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/syscalls.h> | 16 | #include <linux/syscalls.h> |
17 | #include <linux/pagemap.h> | 17 | #include <linux/pagemap.h> |
18 | #include "read_write.h" | ||
18 | 19 | ||
19 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
20 | #include <asm/unistd.h> | 21 | #include <asm/unistd.h> |
@@ -450,6 +451,62 @@ unsigned long iov_shorten(struct iovec *iov, unsigned long nr_segs, size_t to) | |||
450 | 451 | ||
451 | EXPORT_UNUSED_SYMBOL(iov_shorten); /* June 2006 */ | 452 | EXPORT_UNUSED_SYMBOL(iov_shorten); /* June 2006 */ |
452 | 453 | ||
454 | ssize_t do_sync_readv_writev(struct file *filp, const struct iovec *iov, | ||
455 | unsigned long nr_segs, size_t len, loff_t *ppos, iov_fn_t fn) | ||
456 | { | ||
457 | struct kiocb kiocb; | ||
458 | ssize_t ret; | ||
459 | |||
460 | init_sync_kiocb(&kiocb, filp); | ||
461 | kiocb.ki_pos = *ppos; | ||
462 | kiocb.ki_left = len; | ||
463 | kiocb.ki_nbytes = len; | ||
464 | |||
465 | for (;;) { | ||
466 | ret = fn(&kiocb, iov, nr_segs, kiocb.ki_pos); | ||
467 | if (ret != -EIOCBRETRY) | ||
468 | break; | ||
469 | wait_on_retry_sync_kiocb(&kiocb); | ||
470 | } | ||
471 | |||
472 | if (ret == -EIOCBQUEUED) | ||
473 | ret = wait_on_sync_kiocb(&kiocb); | ||
474 | *ppos = kiocb.ki_pos; | ||
475 | return ret; | ||
476 | } | ||
477 | |||
478 | /* Do it by hand, with file-ops */ | ||
479 | ssize_t do_loop_readv_writev(struct file *filp, struct iovec *iov, | ||
480 | unsigned long nr_segs, loff_t *ppos, io_fn_t fn) | ||
481 | { | ||
482 | struct iovec *vector = iov; | ||
483 | ssize_t ret = 0; | ||
484 | |||
485 | while (nr_segs > 0) { | ||
486 | void __user *base; | ||
487 | size_t len; | ||
488 | ssize_t nr; | ||
489 | |||
490 | base = vector->iov_base; | ||
491 | len = vector->iov_len; | ||
492 | vector++; | ||
493 | nr_segs--; | ||
494 | |||
495 | nr = fn(filp, base, len, ppos); | ||
496 | |||
497 | if (nr < 0) { | ||
498 | if (!ret) | ||
499 | ret = nr; | ||
500 | break; | ||
501 | } | ||
502 | ret += nr; | ||
503 | if (nr != len) | ||
504 | break; | ||
505 | } | ||
506 | |||
507 | return ret; | ||
508 | } | ||
509 | |||
453 | /* A write operation does a read from user space and vice versa */ | 510 | /* A write operation does a read from user space and vice versa */ |
454 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) | 511 | #define vrfy_dir(type) ((type) == READ ? VERIFY_WRITE : VERIFY_READ) |
455 | 512 | ||
@@ -457,12 +514,9 @@ static ssize_t do_readv_writev(int type, struct file *file, | |||
457 | const struct iovec __user * uvector, | 514 | const struct iovec __user * uvector, |
458 | unsigned long nr_segs, loff_t *pos) | 515 | unsigned long nr_segs, loff_t *pos) |
459 | { | 516 | { |
460 | typedef ssize_t (*io_fn_t)(struct file *, char __user *, size_t, loff_t *); | ||
461 | typedef ssize_t (*iov_fn_t)(struct file *, const struct iovec *, unsigned long, loff_t *); | ||
462 | |||
463 | size_t tot_len; | 517 | size_t tot_len; |
464 | struct iovec iovstack[UIO_FASTIOV]; | 518 | struct iovec iovstack[UIO_FASTIOV]; |
465 | struct iovec *iov=iovstack, *vector; | 519 | struct iovec *iov = iovstack; |
466 | ssize_t ret; | 520 | ssize_t ret; |
467 | int seg; | 521 | int seg; |
468 | io_fn_t fn; | 522 | io_fn_t fn; |
@@ -532,39 +586,18 @@ static ssize_t do_readv_writev(int type, struct file *file, | |||
532 | fnv = NULL; | 586 | fnv = NULL; |
533 | if (type == READ) { | 587 | if (type == READ) { |
534 | fn = file->f_op->read; | 588 | fn = file->f_op->read; |
535 | fnv = file->f_op->readv; | 589 | fnv = file->f_op->aio_read; |
536 | } else { | 590 | } else { |
537 | fn = (io_fn_t)file->f_op->write; | 591 | fn = (io_fn_t)file->f_op->write; |
538 | fnv = file->f_op->writev; | 592 | fnv = file->f_op->aio_write; |
539 | } | ||
540 | if (fnv) { | ||
541 | ret = fnv(file, iov, nr_segs, pos); | ||
542 | goto out; | ||
543 | } | 593 | } |
544 | 594 | ||
545 | /* Do it by hand, with file-ops */ | 595 | if (fnv) |
546 | ret = 0; | 596 | ret = do_sync_readv_writev(file, iov, nr_segs, tot_len, |
547 | vector = iov; | 597 | pos, fnv); |
548 | while (nr_segs > 0) { | 598 | else |
549 | void __user * base; | 599 | ret = do_loop_readv_writev(file, iov, nr_segs, pos, fn); |
550 | size_t len; | ||
551 | ssize_t nr; | ||
552 | |||
553 | base = vector->iov_base; | ||
554 | len = vector->iov_len; | ||
555 | vector++; | ||
556 | nr_segs--; | ||
557 | |||
558 | nr = fn(file, base, len, pos); | ||
559 | 600 | ||
560 | if (nr < 0) { | ||
561 | if (!ret) ret = nr; | ||
562 | break; | ||
563 | } | ||
564 | ret += nr; | ||
565 | if (nr != len) | ||
566 | break; | ||
567 | } | ||
568 | out: | 601 | out: |
569 | if (iov != iovstack) | 602 | if (iov != iovstack) |
570 | kfree(iov); | 603 | kfree(iov); |
@@ -585,7 +618,7 @@ ssize_t vfs_readv(struct file *file, const struct iovec __user *vec, | |||
585 | { | 618 | { |
586 | if (!(file->f_mode & FMODE_READ)) | 619 | if (!(file->f_mode & FMODE_READ)) |
587 | return -EBADF; | 620 | return -EBADF; |
588 | if (!file->f_op || (!file->f_op->readv && !file->f_op->read)) | 621 | if (!file->f_op || (!file->f_op->aio_read && !file->f_op->read)) |
589 | return -EINVAL; | 622 | return -EINVAL; |
590 | 623 | ||
591 | return do_readv_writev(READ, file, vec, vlen, pos); | 624 | return do_readv_writev(READ, file, vec, vlen, pos); |
@@ -598,7 +631,7 @@ ssize_t vfs_writev(struct file *file, const struct iovec __user *vec, | |||
598 | { | 631 | { |
599 | if (!(file->f_mode & FMODE_WRITE)) | 632 | if (!(file->f_mode & FMODE_WRITE)) |
600 | return -EBADF; | 633 | return -EBADF; |
601 | if (!file->f_op || (!file->f_op->writev && !file->f_op->write)) | 634 | if (!file->f_op || (!file->f_op->aio_write && !file->f_op->write)) |
602 | return -EINVAL; | 635 | return -EINVAL; |
603 | 636 | ||
604 | return do_readv_writev(WRITE, file, vec, vlen, pos); | 637 | return do_readv_writev(WRITE, file, vec, vlen, pos); |