uuid-tree.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) STRATO AG 2013. All rights reserved.
  4. */
  5. #include <linux/uuid.h>
  6. #include <asm/unaligned.h>
  7. #include "ctree.h"
  8. #include "transaction.h"
  9. #include "disk-io.h"
  10. #include "print-tree.h"
  11. static void btrfs_uuid_to_key(u8 *uuid, u8 type, struct btrfs_key *key)
  12. {
  13. key->type = type;
  14. key->objectid = get_unaligned_le64(uuid);
  15. key->offset = get_unaligned_le64(uuid + sizeof(u64));
  16. }
  17. /* return -ENOENT for !found, < 0 for errors, or 0 if an item was found */
  18. static int btrfs_uuid_tree_lookup(struct btrfs_root *uuid_root, u8 *uuid,
  19. u8 type, u64 subid)
  20. {
  21. int ret;
  22. struct btrfs_path *path = NULL;
  23. struct extent_buffer *eb;
  24. int slot;
  25. u32 item_size;
  26. unsigned long offset;
  27. struct btrfs_key key;
  28. if (WARN_ON_ONCE(!uuid_root)) {
  29. ret = -ENOENT;
  30. goto out;
  31. }
  32. path = btrfs_alloc_path();
  33. if (!path) {
  34. ret = -ENOMEM;
  35. goto out;
  36. }
  37. btrfs_uuid_to_key(uuid, type, &key);
  38. ret = btrfs_search_slot(NULL, uuid_root, &key, path, 0, 0);
  39. if (ret < 0) {
  40. goto out;
  41. } else if (ret > 0) {
  42. ret = -ENOENT;
  43. goto out;
  44. }
  45. eb = path->nodes[0];
  46. slot = path->slots[0];
  47. item_size = btrfs_item_size_nr(eb, slot);
  48. offset = btrfs_item_ptr_offset(eb, slot);
  49. ret = -ENOENT;
  50. if (!IS_ALIGNED(item_size, sizeof(u64))) {
  51. btrfs_warn(uuid_root->fs_info,
  52. "uuid item with illegal size %lu!",
  53. (unsigned long)item_size);
  54. goto out;
  55. }
  56. while (item_size) {
  57. __le64 data;
  58. read_extent_buffer(eb, &data, offset, sizeof(data));
  59. if (le64_to_cpu(data) == subid) {
  60. ret = 0;
  61. break;
  62. }
  63. offset += sizeof(data);
  64. item_size -= sizeof(data);
  65. }
  66. out:
  67. btrfs_free_path(path);
  68. return ret;
  69. }
  70. int btrfs_uuid_tree_add(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
  71. u64 subid_cpu)
  72. {
  73. struct btrfs_fs_info *fs_info = trans->fs_info;
  74. struct btrfs_root *uuid_root = fs_info->uuid_root;
  75. int ret;
  76. struct btrfs_path *path = NULL;
  77. struct btrfs_key key;
  78. struct extent_buffer *eb;
  79. int slot;
  80. unsigned long offset;
  81. __le64 subid_le;
  82. ret = btrfs_uuid_tree_lookup(uuid_root, uuid, type, subid_cpu);
  83. if (ret != -ENOENT)
  84. return ret;
  85. if (WARN_ON_ONCE(!uuid_root)) {
  86. ret = -EINVAL;
  87. goto out;
  88. }
  89. btrfs_uuid_to_key(uuid, type, &key);
  90. path = btrfs_alloc_path();
  91. if (!path) {
  92. ret = -ENOMEM;
  93. goto out;
  94. }
  95. ret = btrfs_insert_empty_item(trans, uuid_root, path, &key,
  96. sizeof(subid_le));
  97. if (ret >= 0) {
  98. /* Add an item for the type for the first time */
  99. eb = path->nodes[0];
  100. slot = path->slots[0];
  101. offset = btrfs_item_ptr_offset(eb, slot);
  102. } else if (ret == -EEXIST) {
  103. /*
  104. * An item with that type already exists.
  105. * Extend the item and store the new subid at the end.
  106. */
  107. btrfs_extend_item(path, sizeof(subid_le));
  108. eb = path->nodes[0];
  109. slot = path->slots[0];
  110. offset = btrfs_item_ptr_offset(eb, slot);
  111. offset += btrfs_item_size_nr(eb, slot) - sizeof(subid_le);
  112. } else {
  113. btrfs_warn(fs_info,
  114. "insert uuid item failed %d (0x%016llx, 0x%016llx) type %u!",
  115. ret, (unsigned long long)key.objectid,
  116. (unsigned long long)key.offset, type);
  117. goto out;
  118. }
  119. ret = 0;
  120. subid_le = cpu_to_le64(subid_cpu);
  121. write_extent_buffer(eb, &subid_le, offset, sizeof(subid_le));
  122. btrfs_mark_buffer_dirty(eb);
  123. out:
  124. btrfs_free_path(path);
  125. return ret;
  126. }
  127. int btrfs_uuid_tree_remove(struct btrfs_trans_handle *trans, u8 *uuid, u8 type,
  128. u64 subid)
  129. {
  130. struct btrfs_fs_info *fs_info = trans->fs_info;
  131. struct btrfs_root *uuid_root = fs_info->uuid_root;
  132. int ret;
  133. struct btrfs_path *path = NULL;
  134. struct btrfs_key key;
  135. struct extent_buffer *eb;
  136. int slot;
  137. unsigned long offset;
  138. u32 item_size;
  139. unsigned long move_dst;
  140. unsigned long move_src;
  141. unsigned long move_len;
  142. if (WARN_ON_ONCE(!uuid_root)) {
  143. ret = -EINVAL;
  144. goto out;
  145. }
  146. btrfs_uuid_to_key(uuid, type, &key);
  147. path = btrfs_alloc_path();
  148. if (!path) {
  149. ret = -ENOMEM;
  150. goto out;
  151. }
  152. ret = btrfs_search_slot(trans, uuid_root, &key, path, -1, 1);
  153. if (ret < 0) {
  154. btrfs_warn(fs_info, "error %d while searching for uuid item!",
  155. ret);
  156. goto out;
  157. }
  158. if (ret > 0) {
  159. ret = -ENOENT;
  160. goto out;
  161. }
  162. eb = path->nodes[0];
  163. slot = path->slots[0];
  164. offset = btrfs_item_ptr_offset(eb, slot);
  165. item_size = btrfs_item_size_nr(eb, slot);
  166. if (!IS_ALIGNED(item_size, sizeof(u64))) {
  167. btrfs_warn(fs_info, "uuid item with illegal size %lu!",
  168. (unsigned long)item_size);
  169. ret = -ENOENT;
  170. goto out;
  171. }
  172. while (item_size) {
  173. __le64 read_subid;
  174. read_extent_buffer(eb, &read_subid, offset, sizeof(read_subid));
  175. if (le64_to_cpu(read_subid) == subid)
  176. break;
  177. offset += sizeof(read_subid);
  178. item_size -= sizeof(read_subid);
  179. }
  180. if (!item_size) {
  181. ret = -ENOENT;
  182. goto out;
  183. }
  184. item_size = btrfs_item_size_nr(eb, slot);
  185. if (item_size == sizeof(subid)) {
  186. ret = btrfs_del_item(trans, uuid_root, path);
  187. goto out;
  188. }
  189. move_dst = offset;
  190. move_src = offset + sizeof(subid);
  191. move_len = item_size - (move_src - btrfs_item_ptr_offset(eb, slot));
  192. memmove_extent_buffer(eb, move_dst, move_src, move_len);
  193. btrfs_truncate_item(path, item_size - sizeof(subid), 1);
  194. out:
  195. btrfs_free_path(path);
  196. return ret;
  197. }
  198. static int btrfs_uuid_iter_rem(struct btrfs_root *uuid_root, u8 *uuid, u8 type,
  199. u64 subid)
  200. {
  201. struct btrfs_trans_handle *trans;
  202. int ret;
  203. /* 1 - for the uuid item */
  204. trans = btrfs_start_transaction(uuid_root, 1);
  205. if (IS_ERR(trans)) {
  206. ret = PTR_ERR(trans);
  207. goto out;
  208. }
  209. ret = btrfs_uuid_tree_remove(trans, uuid, type, subid);
  210. btrfs_end_transaction(trans);
  211. out:
  212. return ret;
  213. }
  214. int btrfs_uuid_tree_iterate(struct btrfs_fs_info *fs_info,
  215. int (*check_func)(struct btrfs_fs_info *, u8 *, u8,
  216. u64))
  217. {
  218. struct btrfs_root *root = fs_info->uuid_root;
  219. struct btrfs_key key;
  220. struct btrfs_path *path;
  221. int ret = 0;
  222. struct extent_buffer *leaf;
  223. int slot;
  224. u32 item_size;
  225. unsigned long offset;
  226. path = btrfs_alloc_path();
  227. if (!path) {
  228. ret = -ENOMEM;
  229. goto out;
  230. }
  231. key.objectid = 0;
  232. key.type = 0;
  233. key.offset = 0;
  234. again_search_slot:
  235. ret = btrfs_search_forward(root, &key, path, BTRFS_OLDEST_GENERATION);
  236. if (ret) {
  237. if (ret > 0)
  238. ret = 0;
  239. goto out;
  240. }
  241. while (1) {
  242. cond_resched();
  243. leaf = path->nodes[0];
  244. slot = path->slots[0];
  245. btrfs_item_key_to_cpu(leaf, &key, slot);
  246. if (key.type != BTRFS_UUID_KEY_SUBVOL &&
  247. key.type != BTRFS_UUID_KEY_RECEIVED_SUBVOL)
  248. goto skip;
  249. offset = btrfs_item_ptr_offset(leaf, slot);
  250. item_size = btrfs_item_size_nr(leaf, slot);
  251. if (!IS_ALIGNED(item_size, sizeof(u64))) {
  252. btrfs_warn(fs_info,
  253. "uuid item with illegal size %lu!",
  254. (unsigned long)item_size);
  255. goto skip;
  256. }
  257. while (item_size) {
  258. u8 uuid[BTRFS_UUID_SIZE];
  259. __le64 subid_le;
  260. u64 subid_cpu;
  261. put_unaligned_le64(key.objectid, uuid);
  262. put_unaligned_le64(key.offset, uuid + sizeof(u64));
  263. read_extent_buffer(leaf, &subid_le, offset,
  264. sizeof(subid_le));
  265. subid_cpu = le64_to_cpu(subid_le);
  266. ret = check_func(fs_info, uuid, key.type, subid_cpu);
  267. if (ret < 0)
  268. goto out;
  269. if (ret > 0) {
  270. btrfs_release_path(path);
  271. ret = btrfs_uuid_iter_rem(root, uuid, key.type,
  272. subid_cpu);
  273. if (ret == 0) {
  274. /*
  275. * this might look inefficient, but the
  276. * justification is that it is an
  277. * exception that check_func returns 1,
  278. * and that in the regular case only one
  279. * entry per UUID exists.
  280. */
  281. goto again_search_slot;
  282. }
  283. if (ret < 0 && ret != -ENOENT)
  284. goto out;
  285. key.offset++;
  286. goto again_search_slot;
  287. }
  288. item_size -= sizeof(subid_le);
  289. offset += sizeof(subid_le);
  290. }
  291. skip:
  292. ret = btrfs_next_item(root, path);
  293. if (ret == 0)
  294. continue;
  295. else if (ret > 0)
  296. ret = 0;
  297. break;
  298. }
  299. out:
  300. btrfs_free_path(path);
  301. return ret;
  302. }