blob: 04a04fa0f7c4bedf3a739170f37ca1ad372fbefb [file] [log] [blame]
Stefan Behrens07b30a42013-08-15 17:11:17 +02001/*
2 * Copyright (C) STRATO AG 2013. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License v2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public
14 * License along with this program; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 021110-1307, USA.
17 */
18#include <linux/uuid.h>
19#include <asm/unaligned.h>
20#include "ctree.h"
21#include "transaction.h"
22#include "disk-io.h"
23#include "print-tree.h"
24
25
26static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key)
27{
28 key->type = type;
29 key->objectid = get_unaligned_le64(uuid);
30 key->offset = get_unaligned_le64(uuid + sizeof(u64));
31}
32
33/* return -ENOENT for !found, < 0 for errors, or 0 if an item was found */
34static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
35 u8 type, u64 subid)
36{
37 int ret;
38 struct btrfs_path *path = NULL;
39 struct extent_buffer *eb;
40 int slot;
41 u32 item_size;
42 unsigned long offset;
43 struct btrfs_key key;
44
45 if (WARN_ON_ONCE(!uuid_root)) {
46 ret = -ENOENT;
47 goto out;
48 }
49
50 path = btrfs_alloc_path();
51 if (!path) {
52 ret = -ENOMEM;
53 goto out;
54 }
55
56 btrfs_uuid_to_key(uuid, type, &key);
57 ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
58 if (ret < 0) {
59 goto out;
60 } else if (ret > 0) {
61 ret = -ENOENT;
62 goto out;
63 }
64
65 eb = path->nodes[0];
66 slot = path->slots[0];
67 item_size = btrfs_item_size_nr(eb, slot);
68 offset = btrfs_item_ptr_offset(eb, slot);
69 ret = -ENOENT;
70
71 if (!IS_ALIGNED(item_size, sizeof(u64))) {
72 pr_warn("btrfs: uuid item with illegal size %lu!\n",
73 (unsigned long)item_size);
74 goto out;
75 }
76 while (item_size) {
77 __le64 data;
78
79 read_extent_buffer(eb, &data, offset, sizeof(data));
80 if (le64_to_cpu(data) == subid) {
81 ret = 0;
82 break;
83 }
84 offset += sizeof(data);
85 item_size -= sizeof(data);
86 }
87
88out:
89 btrfs_free_path(path);
90 return ret;
91}
92
93int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans,
94 struct btrfs_root *uuid_root, u8 *uuid, u8 type,
95 u64 subid_cpu)
96{
97 int ret;
98 struct btrfs_path *path = NULL;
99 struct btrfs_key key;
100 struct extent_buffer *eb;
101 int slot;
102 unsigned long offset;
103 __le64 subid_le;
104
105 ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
106 if (ret != -ENOENT)
107 return ret;
108
109 if (WARN_ON_ONCE(!uuid_root)) {
110 ret = -EINVAL;
111 goto out;
112 }
113
114 btrfs_uuid_to_key(uuid, type, &key);
115
116 path = btrfs_alloc_path();
117 if (!path) {
118 ret = -ENOMEM;
119 goto out;
120 }
121
122 ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
123 sizeof(subid_le));
124 if (ret >= 0) {
125 /* Add an item for the type for the first time */
126 eb = path->nodes[0];
127 slot = path->slots[0];
128 offset = btrfs_item_ptr_offset(eb, slot);
129 } else if (ret == -EEXIST) {
130 /*
131 * An item with that type already exists.
132 * Extend the item and store the new subid at the end.
133 */
134 btrfs_extend_item(uuid_root, path, sizeof(subid_le));
135 eb = path->nodes[0];
136 slot = path->slots[0];
137 offset = btrfs_item_ptr_offset(eb, slot);
138 offset += btrfs_item_size_nr(eb, slot) - sizeof(subid_le);
139 } else if (ret < 0) {
140 pr_warn("btrfs: insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!\n",
141 ret, (unsigned long long)key.objectid,
142 (unsigned long long)key.offset, type);
143 goto out;
144 }
145
146 ret = 0;
147 subid_le = cpu_to_le64(subid_cpu);
148 write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
149 btrfs_mark_buffer_dirty(eb);
150
151out:
152 btrfs_free_path(path);
153 return ret;
154}
155
156int btrfs_uuid_tree_rem(struct btrfs_trans_handle *trans,
157 struct btrfs_root *uuid_root, u8 *uuid, u8 type,
158 u64 subid)
159{
160 int ret;
161 struct btrfs_path *path = NULL;
162 struct btrfs_key key;
163 struct extent_buffer *eb;
164 int slot;
165 unsigned long offset;
166 u32 item_size;
167 unsigned long move_dst;
168 unsigned long move_src;
169 unsigned long move_len;
170
171 if (WARN_ON_ONCE(!uuid_root)) {
172 ret = -EINVAL;
173 goto out;
174 }
175
176 btrfs_uuid_to_key(uuid, type, &key);
177
178 path = btrfs_alloc_path();
179 if (!path) {
180 ret = -ENOMEM;
181 goto out;
182 }
183
184 ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
185 if (ret < 0) {
186 pr_warn("btrfs: error %d while searching for uuid item!\n",
187 ret);
188 goto out;
189 }
190 if (ret > 0) {
191 ret = -ENOENT;
192 goto out;
193 }
194
195 eb = path->nodes[0];
196 slot = path->slots[0];
197 offset = btrfs_item_ptr_offset(eb, slot);
198 item_size = btrfs_item_size_nr(eb, slot);
199 if (!IS_ALIGNED(item_size, sizeof(u64))) {
200 pr_warn("btrfs: uuid item with illegal size %lu!\n",
201 (unsigned long)item_size);
202 ret = -ENOENT;
203 goto out;
204 }
205 while (item_size) {
206 __le64 read_subid;
207
208 read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
209 if (le64_to_cpu(read_subid) == subid)
210 break;
211 offset += sizeof(read_subid);
212 item_size -= sizeof(read_subid);
213 }
214
215 if (!item_size) {
216 ret = -ENOENT;
217 goto out;
218 }
219
220 item_size = btrfs_item_size_nr(eb, slot);
221 if (item_size == sizeof(subid)) {
222 ret = btrfs_del_item(trans, uuid_root, path);
223 goto out;
224 }
225
226 move_dst = offset;
227 move_src = offset + sizeof(subid);
228 move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
229 memmove_extent_buffer(eb, move_dst, move_src, move_len);
230 btrfs_truncate_item(uuid_root, path, item_size - sizeof(subid), 1);
231
232out:
233 btrfs_free_path(path);
234 return ret;
235}