btrfs-progs: tests: add shell quoting to fuzz test scripts
[platform/upstream/btrfs-progs.git] / transaction.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public
4  * License v2 as published by the Free Software Foundation.
5  *
6  * This program is distributed in the hope that it will be useful,
7  * but WITHOUT ANY WARRANTY; without even the implied warranty of
8  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
9  * General Public License for more details.
10  *
11  * You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the
13  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
14  * Boston, MA 021110-1307, USA.
15  */
16
17 #include "kerncompat.h"
18 #include "disk-io.h"
19 #include "transaction.h"
20
21 #include "messages.h"
22
23 struct btrfs_trans_handle* btrfs_start_transaction(struct btrfs_root *root,
24                 int num_blocks)
25 {
26         struct btrfs_fs_info *fs_info = root->fs_info;
27         struct btrfs_trans_handle *h = kzalloc(sizeof(*h), GFP_NOFS);
28
29         if (fs_info->transaction_aborted)
30                 return ERR_PTR(-EROFS);
31
32         if (!h)
33                 return ERR_PTR(-ENOMEM);
34         if (root->commit_root) {
35                 error("commit_root aleady set when starting transaction");
36                 kfree(h);
37                 return ERR_PTR(-EINVAL);
38         }
39         if (fs_info->running_transaction) {
40                 error("attempt to start transaction over already running one");
41                 kfree(h);
42                 return ERR_PTR(-EINVAL);
43         }
44         h->fs_info = fs_info;
45         fs_info->running_transaction = h;
46         fs_info->generation++;
47         h->transid = fs_info->generation;
48         h->blocks_reserved = num_blocks;
49         root->last_trans = h->transid;
50         root->commit_root = root->node;
51         extent_buffer_get(root->node);
52
53         return h;
54 }
55
56 static int update_cowonly_root(struct btrfs_trans_handle *trans,
57                                struct btrfs_root *root)
58 {
59         int ret;
60         u64 old_root_bytenr;
61         struct btrfs_root *tree_root = root->fs_info->tree_root;
62
63         btrfs_write_dirty_block_groups(trans, root);
64         while(1) {
65                 old_root_bytenr = btrfs_root_bytenr(&root->root_item);
66                 if (old_root_bytenr == root->node->start)
67                         break;
68                 btrfs_set_root_bytenr(&root->root_item,
69                                        root->node->start);
70                 btrfs_set_root_generation(&root->root_item,
71                                           trans->transid);
72                 root->root_item.level = btrfs_header_level(root->node);
73                 ret = btrfs_update_root(trans, tree_root,
74                                         &root->root_key,
75                                         &root->root_item);
76                 BUG_ON(ret);
77                 btrfs_write_dirty_block_groups(trans, root);
78         }
79         return 0;
80 }
81
82 int commit_tree_roots(struct btrfs_trans_handle *trans,
83                              struct btrfs_fs_info *fs_info)
84 {
85         struct btrfs_root *root;
86         struct list_head *next;
87         struct extent_buffer *eb;
88         int ret;
89
90         if (fs_info->readonly)
91                 return 0;
92
93         eb = fs_info->tree_root->node;
94         extent_buffer_get(eb);
95         ret = btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb);
96         free_extent_buffer(eb);
97         if (ret)
98                 return ret;
99
100         while(!list_empty(&fs_info->dirty_cowonly_roots)) {
101                 next = fs_info->dirty_cowonly_roots.next;
102                 list_del_init(next);
103                 root = list_entry(next, struct btrfs_root, dirty_list);
104                 update_cowonly_root(trans, root);
105                 free_extent_buffer(root->commit_root);
106                 root->commit_root = NULL;
107         }
108
109         return 0;
110 }
111
112 int __commit_transaction(struct btrfs_trans_handle *trans,
113                                 struct btrfs_root *root)
114 {
115         u64 start;
116         u64 end;
117         struct btrfs_fs_info *fs_info = root->fs_info;
118         struct extent_buffer *eb;
119         struct extent_io_tree *tree = &fs_info->extent_cache;
120         int ret;
121
122         while(1) {
123                 ret = find_first_extent_bit(tree, 0, &start, &end,
124                                             EXTENT_DIRTY);
125                 if (ret)
126                         break;
127                 while(start <= end) {
128                         eb = find_first_extent_buffer(tree, start);
129                         BUG_ON(!eb || eb->start != start);
130                         ret = write_tree_block(trans, fs_info, eb);
131                         BUG_ON(ret);
132                         start += eb->len;
133                         clear_extent_buffer_dirty(eb);
134                         free_extent_buffer(eb);
135                 }
136         }
137         return 0;
138 }
139
140 int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
141                              struct btrfs_root *root)
142 {
143         u64 transid = trans->transid;
144         int ret = 0;
145         struct btrfs_fs_info *fs_info = root->fs_info;
146
147         if (trans->fs_info->transaction_aborted)
148                 return -EROFS;
149
150         if (root->commit_root == root->node)
151                 goto commit_tree;
152         if (root == root->fs_info->tree_root)
153                 goto commit_tree;
154         if (root == root->fs_info->chunk_root)
155                 goto commit_tree;
156
157         free_extent_buffer(root->commit_root);
158         root->commit_root = NULL;
159
160         btrfs_set_root_bytenr(&root->root_item, root->node->start);
161         btrfs_set_root_generation(&root->root_item, trans->transid);
162         root->root_item.level = btrfs_header_level(root->node);
163         ret = btrfs_update_root(trans, root->fs_info->tree_root,
164                                 &root->root_key, &root->root_item);
165         BUG_ON(ret);
166 commit_tree:
167         ret = commit_tree_roots(trans, fs_info);
168         BUG_ON(ret);
169         ret = __commit_transaction(trans, root);
170         BUG_ON(ret);
171         write_ctree_super(trans, fs_info);
172         btrfs_finish_extent_commit(trans, fs_info->extent_root,
173                                    &fs_info->pinned_extents);
174         kfree(trans);
175         free_extent_buffer(root->commit_root);
176         root->commit_root = NULL;
177         fs_info->running_transaction = NULL;
178         fs_info->last_trans_committed = transid;
179         return 0;
180 }
181
182 void btrfs_abort_transaction(struct btrfs_trans_handle *trans, int error)
183 {
184         trans->fs_info->transaction_aborted = error;
185 }