-
-failed_release:
- btrfs_drop_pages(pages, num_pages);
- return err;
-
-failed_truncate:
- btrfs_drop_pages(pages, num_pages);
- if (pos > isize)
- vmtruncate(inode, isize);
- return err;
-}
-
-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];
- 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;
-
- pinned[0] = NULL;
- pinned[1] = NULL;
- 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);
- wait_on_page_locked(pinned[0]);
- } 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);
- wait_on_page_locked(pinned[1]);
- } 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, 1);
- 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);
- btrfs_btree_balance_dirty(root);
- 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;