diff options
Diffstat (limited to 'fs/btrfs/file.c')
-rw-r--r-- | fs/btrfs/file.c | 131 |
1 files changed, 81 insertions, 50 deletions
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 23f029359688..fef7ba1e707f 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c | |||
@@ -81,6 +81,62 @@ static void btrfs_drop_pages(struct page **pages, size_t num_pages) | |||
81 | } | 81 | } |
82 | } | 82 | } |
83 | 83 | ||
84 | static int insert_inline_extent(struct btrfs_root *root, struct inode *inode, | ||
85 | u64 offset, ssize_t size, | ||
86 | struct buffer_head *bh) | ||
87 | { | ||
88 | struct btrfs_key key; | ||
89 | struct btrfs_path *path; | ||
90 | char *ptr, *kaddr; | ||
91 | struct btrfs_trans_handle *trans; | ||
92 | struct btrfs_file_extent_item *ei; | ||
93 | u32 datasize; | ||
94 | int err = 0; | ||
95 | int ret; | ||
96 | |||
97 | path = btrfs_alloc_path(); | ||
98 | if (!path) | ||
99 | return -ENOMEM; | ||
100 | |||
101 | mutex_lock(&root->fs_info->fs_mutex); | ||
102 | trans = btrfs_start_transaction(root, 1); | ||
103 | btrfs_set_trans_block_group(trans, inode); | ||
104 | |||
105 | key.objectid = inode->i_ino; | ||
106 | key.offset = offset; | ||
107 | key.flags = 0; | ||
108 | btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); | ||
109 | BUG_ON(size >= PAGE_CACHE_SIZE); | ||
110 | datasize = btrfs_file_extent_calc_inline_size(size); | ||
111 | |||
112 | ret = btrfs_insert_empty_item(trans, root, path, &key, | ||
113 | datasize); | ||
114 | if (ret) { | ||
115 | err = ret; | ||
116 | goto fail; | ||
117 | } | ||
118 | ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), | ||
119 | path->slots[0], struct btrfs_file_extent_item); | ||
120 | btrfs_set_file_extent_generation(ei, trans->transid); | ||
121 | btrfs_set_file_extent_type(ei, | ||
122 | BTRFS_FILE_EXTENT_INLINE); | ||
123 | ptr = btrfs_file_extent_inline_start(ei); | ||
124 | |||
125 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
126 | btrfs_memcpy(root, path->nodes[0]->b_data, | ||
127 | ptr, kaddr + bh_offset(bh), | ||
128 | size); | ||
129 | kunmap_atomic(kaddr, KM_USER0); | ||
130 | mark_buffer_dirty(path->nodes[0]); | ||
131 | fail: | ||
132 | btrfs_free_path(path); | ||
133 | ret = btrfs_end_transaction(trans, root); | ||
134 | if (ret && !err) | ||
135 | err = ret; | ||
136 | mutex_unlock(&root->fs_info->fs_mutex); | ||
137 | return err; | ||
138 | } | ||
139 | |||
84 | static int dirty_and_release_pages(struct btrfs_trans_handle *trans, | 140 | static int dirty_and_release_pages(struct btrfs_trans_handle *trans, |
85 | struct btrfs_root *root, | 141 | struct btrfs_root *root, |
86 | struct file *file, | 142 | struct file *file, |
@@ -96,57 +152,22 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans, | |||
96 | int this_write; | 152 | int this_write; |
97 | struct inode *inode = file->f_path.dentry->d_inode; | 153 | struct inode *inode = file->f_path.dentry->d_inode; |
98 | struct buffer_head *bh; | 154 | struct buffer_head *bh; |
99 | struct btrfs_file_extent_item *ei; | ||
100 | 155 | ||
101 | for (i = 0; i < num_pages; i++) { | 156 | for (i = 0; i < num_pages; i++) { |
102 | offset = pos & (PAGE_CACHE_SIZE -1); | 157 | offset = pos & (PAGE_CACHE_SIZE -1); |
103 | this_write = min((size_t)PAGE_CACHE_SIZE - offset, write_bytes); | 158 | this_write = min((size_t)PAGE_CACHE_SIZE - offset, write_bytes); |
104 | /* FIXME, one block at a time */ | ||
105 | 159 | ||
160 | /* FIXME, one block at a time */ | ||
106 | bh = page_buffers(pages[i]); | 161 | bh = page_buffers(pages[i]); |
107 | 162 | ||
108 | if (buffer_mapped(bh) && bh->b_blocknr == 0) { | 163 | if (buffer_mapped(bh) && bh->b_blocknr == 0) { |
109 | struct btrfs_key key; | 164 | ret = insert_inline_extent(root, inode, |
110 | struct btrfs_path *path; | 165 | pages[i]->index << PAGE_CACHE_SHIFT, |
111 | char *ptr, *kaddr; | 166 | offset + this_write, bh); |
112 | u32 datasize; | 167 | if (ret) { |
113 | 168 | err = ret; | |
114 | mutex_lock(&root->fs_info->fs_mutex); | 169 | goto failed; |
115 | trans = btrfs_start_transaction(root, 1); | 170 | } |
116 | btrfs_set_trans_block_group(trans, inode); | ||
117 | |||
118 | /* create an inline extent, and copy the data in */ | ||
119 | path = btrfs_alloc_path(); | ||
120 | BUG_ON(!path); | ||
121 | key.objectid = inode->i_ino; | ||
122 | key.offset = pages[i]->index << PAGE_CACHE_SHIFT; | ||
123 | key.flags = 0; | ||
124 | btrfs_set_key_type(&key, BTRFS_EXTENT_DATA_KEY); | ||
125 | BUG_ON(write_bytes >= PAGE_CACHE_SIZE); | ||
126 | datasize = offset + | ||
127 | btrfs_file_extent_calc_inline_size(write_bytes); | ||
128 | |||
129 | ret = btrfs_insert_empty_item(trans, root, path, &key, | ||
130 | datasize); | ||
131 | BUG_ON(ret); | ||
132 | ei = btrfs_item_ptr(btrfs_buffer_leaf(path->nodes[0]), | ||
133 | path->slots[0], struct btrfs_file_extent_item); | ||
134 | btrfs_set_file_extent_generation(ei, trans->transid); | ||
135 | btrfs_set_file_extent_type(ei, | ||
136 | BTRFS_FILE_EXTENT_INLINE); | ||
137 | ptr = btrfs_file_extent_inline_start(ei); | ||
138 | |||
139 | kaddr = kmap_atomic(bh->b_page, KM_USER0); | ||
140 | btrfs_memcpy(root, path->nodes[0]->b_data, | ||
141 | ptr, kaddr + bh_offset(bh), | ||
142 | offset + write_bytes); | ||
143 | kunmap_atomic(kaddr, KM_USER0); | ||
144 | |||
145 | mark_buffer_dirty(path->nodes[0]); | ||
146 | btrfs_free_path(path); | ||
147 | ret = btrfs_end_transaction(trans, root); | ||
148 | BUG_ON(ret); | ||
149 | mutex_unlock(&root->fs_info->fs_mutex); | ||
150 | } | 171 | } |
151 | 172 | ||
152 | ret = btrfs_commit_write(file, pages[i], offset, | 173 | ret = btrfs_commit_write(file, pages[i], offset, |
@@ -321,6 +342,7 @@ next_slot: | |||
321 | btrfs_file_extent_disk_blocknr(extent); | 342 | btrfs_file_extent_disk_blocknr(extent); |
322 | } | 343 | } |
323 | ret = btrfs_del_item(trans, root, path); | 344 | ret = btrfs_del_item(trans, root, path); |
345 | /* TODO update progress marker and return */ | ||
324 | BUG_ON(ret); | 346 | BUG_ON(ret); |
325 | btrfs_release_path(root, path); | 347 | btrfs_release_path(root, path); |
326 | extent = NULL; | 348 | extent = NULL; |
@@ -452,7 +474,8 @@ static int prepare_pages(struct btrfs_root *root, | |||
452 | err = btrfs_drop_extents(trans, root, inode, | 474 | err = btrfs_drop_extents(trans, root, inode, |
453 | start_pos, (pos + write_bytes + root->blocksize -1) & | 475 | start_pos, (pos + write_bytes + root->blocksize -1) & |
454 | ~((u64)root->blocksize - 1), &hint_block); | 476 | ~((u64)root->blocksize - 1), &hint_block); |
455 | BUG_ON(err); | 477 | if (err) |
478 | goto failed_release; | ||
456 | } | 479 | } |
457 | 480 | ||
458 | /* insert any holes we need to create */ | 481 | /* insert any holes we need to create */ |
@@ -469,7 +492,8 @@ static int prepare_pages(struct btrfs_root *root, | |||
469 | last_pos_in_file, | 492 | last_pos_in_file, |
470 | 0, 0, hole_size); | 493 | 0, 0, hole_size); |
471 | } | 494 | } |
472 | BUG_ON(err); | 495 | if (err) |
496 | goto failed_release; | ||
473 | } | 497 | } |
474 | 498 | ||
475 | /* | 499 | /* |
@@ -481,11 +505,13 @@ static int prepare_pages(struct btrfs_root *root, | |||
481 | err = btrfs_alloc_extent(trans, root, inode->i_ino, | 505 | err = btrfs_alloc_extent(trans, root, inode->i_ino, |
482 | num_blocks, hint_block, (u64)-1, | 506 | num_blocks, hint_block, (u64)-1, |
483 | &ins, 1); | 507 | &ins, 1); |
484 | BUG_ON(err); | 508 | if (err) |
509 | goto failed_truncate; | ||
485 | err = btrfs_insert_file_extent(trans, root, inode->i_ino, | 510 | err = btrfs_insert_file_extent(trans, root, inode->i_ino, |
486 | start_pos, ins.objectid, ins.offset, | 511 | start_pos, ins.objectid, ins.offset, |
487 | ins.offset); | 512 | ins.offset); |
488 | BUG_ON(err); | 513 | if (err) |
514 | goto failed_truncate; | ||
489 | } else { | 515 | } else { |
490 | ins.offset = 0; | 516 | ins.offset = 0; |
491 | ins.objectid = 0; | 517 | ins.objectid = 0; |
@@ -618,16 +644,21 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, | |||
618 | ret = prepare_pages(root, file, pages, num_pages, | 644 | ret = prepare_pages(root, file, pages, num_pages, |
619 | pos, first_index, last_index, | 645 | pos, first_index, last_index, |
620 | write_bytes); | 646 | write_bytes); |
621 | BUG_ON(ret); | 647 | if (ret) |
648 | goto out; | ||
622 | 649 | ||
623 | ret = btrfs_copy_from_user(pos, num_pages, | 650 | ret = btrfs_copy_from_user(pos, num_pages, |
624 | write_bytes, pages, buf); | 651 | write_bytes, pages, buf); |
625 | BUG_ON(ret); | 652 | if (ret) { |
653 | btrfs_drop_pages(pages, num_pages); | ||
654 | goto out; | ||
655 | } | ||
626 | 656 | ||
627 | ret = dirty_and_release_pages(NULL, root, file, pages, | 657 | ret = dirty_and_release_pages(NULL, root, file, pages, |
628 | num_pages, pos, write_bytes); | 658 | num_pages, pos, write_bytes); |
629 | BUG_ON(ret); | ||
630 | btrfs_drop_pages(pages, num_pages); | 659 | btrfs_drop_pages(pages, num_pages); |
660 | if (ret) | ||
661 | goto out; | ||
631 | 662 | ||
632 | buf += write_bytes; | 663 | buf += write_bytes; |
633 | count -= write_bytes; | 664 | count -= write_bytes; |