-static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- loff_t pos;
- size_t num_written = 0;
- int err = 0;
- int ret = 0;
- struct inode *inode = file->f_path.dentry->d_inode;
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct page *pages[8];
- struct page *pinned[2] = { NULL, NULL };
- unsigned long first_index;
- unsigned long last_index;
- u64 start_pos;
- u64 num_blocks;
- u64 alloc_extent_start;
- struct btrfs_trans_handle *trans;
- struct btrfs_key ins;
-
- if (file->f_flags & O_DIRECT)
- return -EINVAL;
- pos = *ppos;
- vfs_check_frozen(inode->i_sb, SB_FREEZE_WRITE);
- current->backing_dev_info = inode->i_mapping->backing_dev_info;
- err = generic_write_checks(file, &pos, &count, S_ISBLK(inode->i_mode));
- if (err)
- goto out;
- if (count == 0)
- goto out;
- err = remove_suid(file->f_path.dentry);
- if (err)
- goto out;
- file_update_time(file);
-
- start_pos = pos & ~((u64)PAGE_CACHE_SIZE - 1);
- num_blocks = (count + pos - start_pos + root->blocksize - 1) >>
- inode->i_blkbits;
-
- mutex_lock(&inode->i_mutex);
- first_index = pos >> PAGE_CACHE_SHIFT;
- last_index = (pos + count) >> PAGE_CACHE_SHIFT;
-
- if ((first_index << PAGE_CACHE_SHIFT) < inode->i_size &&
- (pos & (PAGE_CACHE_SIZE - 1))) {
- pinned[0] = grab_cache_page(inode->i_mapping, first_index);
- if (!PageUptodate(pinned[0])) {
- ret = mpage_readpage(pinned[0], btrfs_get_block);
- BUG_ON(ret);
- } else {
- unlock_page(pinned[0]);
- }
- }
- if (first_index != last_index &&
- (last_index << PAGE_CACHE_SHIFT) < inode->i_size &&
- (count & (PAGE_CACHE_SIZE - 1))) {
- pinned[1] = grab_cache_page(inode->i_mapping, last_index);
- if (!PageUptodate(pinned[1])) {
- ret = mpage_readpage(pinned[1], btrfs_get_block);
- BUG_ON(ret);
- } else {
- unlock_page(pinned[1]);
- }
- }
-
- mutex_lock(&root->fs_info->fs_mutex);
- trans = btrfs_start_transaction(root, 1);
- if (!trans) {
- err = -ENOMEM;
- mutex_unlock(&root->fs_info->fs_mutex);
- goto out_unlock;
- }
- btrfs_set_trans_block_group(trans, inode);
- /* FIXME blocksize != 4096 */
- inode->i_blocks += num_blocks << 3;
- if (start_pos < inode->i_size) {
- /* FIXME blocksize != pagesize */
- ret = drop_extents(trans, root, inode,
- start_pos,
- (pos + count + root->blocksize -1) &
- ~((u64)root->blocksize - 1));
- BUG_ON(ret);
- }
- if (inode->i_size >= PAGE_CACHE_SIZE || pos + count < inode->i_size ||
- pos + count - start_pos > BTRFS_MAX_INLINE_DATA_SIZE(root)) {
- ret = btrfs_alloc_extent(trans, root, inode->i_ino,
- num_blocks, 1, (u64)-1, &ins);
- BUG_ON(ret);
- ret = btrfs_insert_file_extent(trans, root, inode->i_ino,
- start_pos, ins.objectid, ins.offset);
- BUG_ON(ret);
- } else {
- ins.offset = 0;
- ins.objectid = 0;
- }
- BUG_ON(ret);
- alloc_extent_start = ins.objectid;
- btrfs_update_inode_block_group(trans, inode);
- ret = btrfs_end_transaction(trans, root);
- mutex_unlock(&root->fs_info->fs_mutex);
-
- while(count > 0) {
- size_t offset = pos & (PAGE_CACHE_SIZE - 1);
- size_t write_bytes = min(count, PAGE_CACHE_SIZE - offset);
- size_t num_pages = (write_bytes + PAGE_CACHE_SIZE - 1) >>
- PAGE_CACHE_SHIFT;
-
- memset(pages, 0, sizeof(pages));
- ret = prepare_pages(root, file, pages, num_pages,
- pos, first_index, last_index,
- write_bytes, alloc_extent_start);
- BUG_ON(ret);
-
- /* FIXME blocks != pagesize */
- if (alloc_extent_start)
- alloc_extent_start += num_pages;
- ret = btrfs_copy_from_user(pos, num_pages,
- write_bytes, pages, buf);
- BUG_ON(ret);
-
- ret = dirty_and_release_pages(NULL, root, file, pages,
- num_pages, pos, write_bytes);
- BUG_ON(ret);
- btrfs_drop_pages(pages, num_pages);
-
- buf += write_bytes;
- count -= write_bytes;
- pos += write_bytes;
- num_written += write_bytes;
-
- balance_dirty_pages_ratelimited(inode->i_mapping);
- cond_resched();
- }
-out_unlock:
- mutex_unlock(&inode->i_mutex);
-out:
- if (pinned[0])
- page_cache_release(pinned[0]);
- if (pinned[1])
- page_cache_release(pinned[1]);
- *ppos = pos;
- current->backing_dev_info = NULL;
- mark_inode_dirty(inode);
- return num_written ? num_written : err;
-}
-
-static int btrfs_read_actor(read_descriptor_t *desc, struct page *page,
- unsigned long offset, unsigned long size)
-{
- char *kaddr;
- unsigned long left, count = desc->count;
- struct inode *inode = page->mapping->host;
-
- if (size > count)
- size = count;
-
- if (!PageChecked(page)) {
- /* FIXME, do it per block */
- struct btrfs_root *root = BTRFS_I(inode)->root;
-
- int ret = btrfs_csum_verify_file_block(root,
- page->mapping->host->i_ino,
- page->index << PAGE_CACHE_SHIFT,
- kmap(page), PAGE_CACHE_SIZE);
- if (ret) {
- printk("failed to verify ino %lu page %lu\n",
- page->mapping->host->i_ino,
- page->index);
- memset(page_address(page), 0, PAGE_CACHE_SIZE);
- }
- SetPageChecked(page);
- kunmap(page);
- }
- /*
- * Faults on the destination of a read are common, so do it before
- * taking the kmap.
- */
- if (!fault_in_pages_writeable(desc->arg.buf, size)) {
- kaddr = kmap_atomic(page, KM_USER0);
- left = __copy_to_user_inatomic(desc->arg.buf,
- kaddr + offset, size);
- kunmap_atomic(kaddr, KM_USER0);
- if (left == 0)
- goto success;
- }
-
- /* Do it the slow way */
- kaddr = kmap(page);
- left = __copy_to_user(desc->arg.buf, kaddr + offset, size);
- kunmap(page);
-
- if (left) {
- size -= left;
- desc->error = -EFAULT;
- }
-success:
- desc->count = count - size;
- desc->written += size;
- desc->arg.buf += size;
- return size;
-}