diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/affs/file.c | 101 |
1 files changed, 59 insertions, 42 deletions
diff --git a/fs/affs/file.c b/fs/affs/file.c index c314a35f0918..6e0c9399200e 100644 --- a/fs/affs/file.c +++ b/fs/affs/file.c | |||
@@ -395,25 +395,33 @@ static int affs_writepage(struct page *page, struct writeback_control *wbc) | |||
395 | { | 395 | { |
396 | return block_write_full_page(page, affs_get_block, wbc); | 396 | return block_write_full_page(page, affs_get_block, wbc); |
397 | } | 397 | } |
398 | |||
398 | static int affs_readpage(struct file *file, struct page *page) | 399 | static int affs_readpage(struct file *file, struct page *page) |
399 | { | 400 | { |
400 | return block_read_full_page(page, affs_get_block); | 401 | return block_read_full_page(page, affs_get_block); |
401 | } | 402 | } |
402 | static int affs_prepare_write(struct file *file, struct page *page, unsigned from, unsigned to) | 403 | |
404 | static int affs_write_begin(struct file *file, struct address_space *mapping, | ||
405 | loff_t pos, unsigned len, unsigned flags, | ||
406 | struct page **pagep, void **fsdata) | ||
403 | { | 407 | { |
404 | return cont_prepare_write(page, from, to, affs_get_block, | 408 | *pagep = NULL; |
405 | &AFFS_I(page->mapping->host)->mmu_private); | 409 | return cont_write_begin(file, mapping, pos, len, flags, pagep, fsdata, |
410 | affs_get_block, | ||
411 | &AFFS_I(mapping->host)->mmu_private); | ||
406 | } | 412 | } |
413 | |||
407 | static sector_t _affs_bmap(struct address_space *mapping, sector_t block) | 414 | static sector_t _affs_bmap(struct address_space *mapping, sector_t block) |
408 | { | 415 | { |
409 | return generic_block_bmap(mapping,block,affs_get_block); | 416 | return generic_block_bmap(mapping,block,affs_get_block); |
410 | } | 417 | } |
418 | |||
411 | const struct address_space_operations affs_aops = { | 419 | const struct address_space_operations affs_aops = { |
412 | .readpage = affs_readpage, | 420 | .readpage = affs_readpage, |
413 | .writepage = affs_writepage, | 421 | .writepage = affs_writepage, |
414 | .sync_page = block_sync_page, | 422 | .sync_page = block_sync_page, |
415 | .prepare_write = affs_prepare_write, | 423 | .write_begin = affs_write_begin, |
416 | .commit_write = generic_commit_write, | 424 | .write_end = generic_write_end, |
417 | .bmap = _affs_bmap | 425 | .bmap = _affs_bmap |
418 | }; | 426 | }; |
419 | 427 | ||
@@ -603,54 +611,65 @@ affs_readpage_ofs(struct file *file, struct page *page) | |||
603 | return err; | 611 | return err; |
604 | } | 612 | } |
605 | 613 | ||
606 | static int affs_prepare_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to) | 614 | static int affs_write_begin_ofs(struct file *file, struct address_space *mapping, |
615 | loff_t pos, unsigned len, unsigned flags, | ||
616 | struct page **pagep, void **fsdata) | ||
607 | { | 617 | { |
608 | struct inode *inode = page->mapping->host; | 618 | struct inode *inode = mapping->host; |
609 | u32 size, offset; | 619 | struct page *page; |
610 | u32 tmp; | 620 | pgoff_t index; |
611 | int err = 0; | 621 | int err = 0; |
612 | 622 | ||
613 | pr_debug("AFFS: prepare_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); | 623 | pr_debug("AFFS: write_begin(%u, %llu, %llu)\n", (u32)inode->i_ino, (unsigned long long)pos, (unsigned long long)pos + len); |
614 | offset = page->index << PAGE_CACHE_SHIFT; | 624 | if (pos > AFFS_I(inode)->mmu_private) { |
615 | if (offset + from > AFFS_I(inode)->mmu_private) { | 625 | /* XXX: this probably leaves a too-big i_size in case of |
616 | err = affs_extent_file_ofs(inode, offset + from); | 626 | * failure. Should really be updating i_size at write_end time |
627 | */ | ||
628 | err = affs_extent_file_ofs(inode, pos); | ||
617 | if (err) | 629 | if (err) |
618 | return err; | 630 | return err; |
619 | } | 631 | } |
620 | size = inode->i_size; | 632 | |
633 | index = pos >> PAGE_CACHE_SHIFT; | ||
634 | page = __grab_cache_page(mapping, index); | ||
635 | if (!page) | ||
636 | return -ENOMEM; | ||
637 | *pagep = page; | ||
621 | 638 | ||
622 | if (PageUptodate(page)) | 639 | if (PageUptodate(page)) |
623 | return 0; | 640 | return 0; |
624 | 641 | ||
625 | if (from) { | 642 | /* XXX: inefficient but safe in the face of short writes */ |
626 | err = affs_do_readpage_ofs(file, page, 0, from); | 643 | err = affs_do_readpage_ofs(file, page, 0, PAGE_CACHE_SIZE); |
627 | if (err) | 644 | if (err) { |
628 | return err; | 645 | unlock_page(page); |
629 | } | 646 | page_cache_release(page); |
630 | if (to < PAGE_CACHE_SIZE) { | ||
631 | zero_user_page(page, to, PAGE_CACHE_SIZE - to, KM_USER0); | ||
632 | if (size > offset + to) { | ||
633 | if (size < offset + PAGE_CACHE_SIZE) | ||
634 | tmp = size & ~PAGE_CACHE_MASK; | ||
635 | else | ||
636 | tmp = PAGE_CACHE_SIZE; | ||
637 | err = affs_do_readpage_ofs(file, page, to, tmp); | ||
638 | } | ||
639 | } | 647 | } |
640 | return err; | 648 | return err; |
641 | } | 649 | } |
642 | 650 | ||
643 | static int affs_commit_write_ofs(struct file *file, struct page *page, unsigned from, unsigned to) | 651 | static int affs_write_end_ofs(struct file *file, struct address_space *mapping, |
652 | loff_t pos, unsigned len, unsigned copied, | ||
653 | struct page *page, void *fsdata) | ||
644 | { | 654 | { |
645 | struct inode *inode = page->mapping->host; | 655 | struct inode *inode = mapping->host; |
646 | struct super_block *sb = inode->i_sb; | 656 | struct super_block *sb = inode->i_sb; |
647 | struct buffer_head *bh, *prev_bh; | 657 | struct buffer_head *bh, *prev_bh; |
648 | char *data; | 658 | char *data; |
649 | u32 bidx, boff, bsize; | 659 | u32 bidx, boff, bsize; |
660 | unsigned from, to; | ||
650 | u32 tmp; | 661 | u32 tmp; |
651 | int written; | 662 | int written; |
652 | 663 | ||
653 | pr_debug("AFFS: commit_write(%u, %ld, %d, %d)\n", (u32)inode->i_ino, page->index, from, to); | 664 | from = pos & (PAGE_CACHE_SIZE - 1); |
665 | to = pos + len; | ||
666 | /* | ||
667 | * XXX: not sure if this can handle short copies (len < copied), but | ||
668 | * we don't have to, because the page should always be uptodate here, | ||
669 | * due to write_begin. | ||
670 | */ | ||
671 | |||
672 | pr_debug("AFFS: write_begin(%u, %llu, %llu)\n", (u32)inode->i_ino, (unsigned long long)pos, (unsigned long long)pos + len); | ||
654 | bsize = AFFS_SB(sb)->s_data_blksize; | 673 | bsize = AFFS_SB(sb)->s_data_blksize; |
655 | data = page_address(page); | 674 | data = page_address(page); |
656 | 675 | ||
@@ -748,6 +767,9 @@ done: | |||
748 | if (tmp > inode->i_size) | 767 | if (tmp > inode->i_size) |
749 | inode->i_size = AFFS_I(inode)->mmu_private = tmp; | 768 | inode->i_size = AFFS_I(inode)->mmu_private = tmp; |
750 | 769 | ||
770 | unlock_page(page); | ||
771 | page_cache_release(page); | ||
772 | |||
751 | return written; | 773 | return written; |
752 | 774 | ||
753 | out: | 775 | out: |
@@ -761,8 +783,8 @@ const struct address_space_operations affs_aops_ofs = { | |||
761 | .readpage = affs_readpage_ofs, | 783 | .readpage = affs_readpage_ofs, |
762 | //.writepage = affs_writepage_ofs, | 784 | //.writepage = affs_writepage_ofs, |
763 | //.sync_page = affs_sync_page_ofs, | 785 | //.sync_page = affs_sync_page_ofs, |
764 | .prepare_write = affs_prepare_write_ofs, | 786 | .write_begin = affs_write_begin_ofs, |
765 | .commit_write = affs_commit_write_ofs | 787 | .write_end = affs_write_end_ofs |
766 | }; | 788 | }; |
767 | 789 | ||
768 | /* Free any preallocated blocks. */ | 790 | /* Free any preallocated blocks. */ |
@@ -805,18 +827,13 @@ affs_truncate(struct inode *inode) | |||
805 | if (inode->i_size > AFFS_I(inode)->mmu_private) { | 827 | if (inode->i_size > AFFS_I(inode)->mmu_private) { |
806 | struct address_space *mapping = inode->i_mapping; | 828 | struct address_space *mapping = inode->i_mapping; |
807 | struct page *page; | 829 | struct page *page; |
808 | u32 size = inode->i_size - 1; | 830 | void *fsdata; |
831 | u32 size = inode->i_size; | ||
809 | int res; | 832 | int res; |
810 | 833 | ||
811 | page = grab_cache_page(mapping, size >> PAGE_CACHE_SHIFT); | 834 | res = mapping->a_ops->write_begin(NULL, mapping, size, 0, 0, &page, &fsdata); |
812 | if (!page) | ||
813 | return; | ||
814 | size = (size & (PAGE_CACHE_SIZE - 1)) + 1; | ||
815 | res = mapping->a_ops->prepare_write(NULL, page, size, size); | ||
816 | if (!res) | 835 | if (!res) |
817 | res = mapping->a_ops->commit_write(NULL, page, size, size); | 836 | res = mapping->a_ops->write_end(NULL, mapping, size, 0, 0, page, fsdata); |
818 | unlock_page(page); | ||
819 | page_cache_release(page); | ||
820 | mark_inode_dirty(inode); | 837 | mark_inode_dirty(inode); |
821 | return; | 838 | return; |
822 | } else if (inode->i_size == AFFS_I(inode)->mmu_private) | 839 | } else if (inode->i_size == AFFS_I(inode)->mmu_private) |