inode.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. * BTRFS filesystem implementation for U-Boot
  3. *
  4. * 2017 Marek Behun, CZ.NIC, marek.behun@nic.cz
  5. *
  6. * SPDX-License-Identifier: GPL-2.0+
  7. */
  8. #include "btrfs.h"
  9. #include <malloc.h>
  10. u64 btrfs_lookup_inode_ref(struct btrfs_root *root, u64 inr,
  11. struct btrfs_inode_ref *refp, char *name)
  12. {
  13. struct btrfs_path path;
  14. struct btrfs_key *key;
  15. struct btrfs_inode_ref *ref;
  16. u64 res = -1ULL;
  17. key = btrfs_search_tree_key_type(root, inr, BTRFS_INODE_REF_KEY,
  18. &path);
  19. if (!key)
  20. return -1ULL;
  21. ref = btrfs_path_item_ptr(&path, struct btrfs_inode_ref);
  22. btrfs_inode_ref_to_cpu(ref);
  23. if (refp)
  24. *refp = *ref;
  25. if (name) {
  26. if (ref->name_len > BTRFS_NAME_MAX) {
  27. printf("%s: inode name too long: %u\n", __func__,
  28. ref->name_len);
  29. goto out;
  30. }
  31. memcpy(name, ref + 1, ref->name_len);
  32. }
  33. res = key->offset;
  34. out:
  35. btrfs_free_path(&path);
  36. return res;
  37. }
  38. int btrfs_lookup_inode(const struct btrfs_root *root,
  39. struct btrfs_key *location,
  40. struct btrfs_inode_item *item,
  41. struct btrfs_root *new_root)
  42. {
  43. struct btrfs_root tmp_root = *root;
  44. struct btrfs_path path;
  45. int res = -1;
  46. if (location->type == BTRFS_ROOT_ITEM_KEY) {
  47. if (btrfs_find_root(location->objectid, &tmp_root, NULL))
  48. return -1;
  49. location->objectid = tmp_root.root_dirid;
  50. location->type = BTRFS_INODE_ITEM_KEY;
  51. location->offset = 0;
  52. }
  53. if (btrfs_search_tree(&tmp_root, location, &path))
  54. return res;
  55. if (btrfs_comp_keys(location, btrfs_path_leaf_key(&path)))
  56. goto out;
  57. if (item) {
  58. *item = *btrfs_path_item_ptr(&path, struct btrfs_inode_item);
  59. btrfs_inode_item_to_cpu(item);
  60. }
  61. if (new_root)
  62. *new_root = tmp_root;
  63. res = 0;
  64. out:
  65. btrfs_free_path(&path);
  66. return res;
  67. }
  68. int btrfs_readlink(const struct btrfs_root *root, u64 inr, char *target)
  69. {
  70. struct btrfs_path path;
  71. struct btrfs_key key;
  72. struct btrfs_file_extent_item *extent;
  73. const char *data_ptr;
  74. int res = -1;
  75. key.objectid = inr;
  76. key.type = BTRFS_EXTENT_DATA_KEY;
  77. key.offset = 0;
  78. if (btrfs_search_tree(root, &key, &path))
  79. return -1;
  80. if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)))
  81. goto out;
  82. extent = btrfs_path_item_ptr(&path, struct btrfs_file_extent_item);
  83. if (extent->type != BTRFS_FILE_EXTENT_INLINE) {
  84. printf("%s: Extent for symlink %llu not of INLINE type\n",
  85. __func__, inr);
  86. goto out;
  87. }
  88. btrfs_file_extent_item_to_cpu_inl(extent);
  89. if (extent->compression != BTRFS_COMPRESS_NONE) {
  90. printf("%s: Symlink %llu extent data compressed!\n", __func__,
  91. inr);
  92. goto out;
  93. } else if (extent->encryption != 0) {
  94. printf("%s: Symlink %llu extent data encrypted!\n", __func__,
  95. inr);
  96. goto out;
  97. } else if (extent->ram_bytes >= btrfs_info.sb.sectorsize) {
  98. printf("%s: Symlink %llu extent data too long (%llu)!\n",
  99. __func__, inr, extent->ram_bytes);
  100. goto out;
  101. }
  102. data_ptr = (const char *) extent
  103. + offsetof(struct btrfs_file_extent_item, disk_bytenr);
  104. memcpy(target, data_ptr, extent->ram_bytes);
  105. target[extent->ram_bytes] = '\0';
  106. res = 0;
  107. out:
  108. btrfs_free_path(&path);
  109. return res;
  110. }
  111. /* inr must be a directory (for regular files with multiple hard links this
  112. function returns only one of the parents of the file) */
  113. static u64 get_parent_inode(struct btrfs_root *root, u64 inr,
  114. struct btrfs_inode_item *inode_item)
  115. {
  116. struct btrfs_key key;
  117. u64 res;
  118. if (inr == BTRFS_FIRST_FREE_OBJECTID) {
  119. if (root->objectid != btrfs_info.fs_root.objectid) {
  120. u64 parent;
  121. struct btrfs_root_ref ref;
  122. parent = btrfs_lookup_root_ref(root->objectid, &ref,
  123. NULL);
  124. if (parent == -1ULL)
  125. return -1ULL;
  126. if (btrfs_find_root(parent, root, NULL))
  127. return -1ULL;
  128. inr = ref.dirid;
  129. }
  130. if (inode_item) {
  131. key.objectid = inr;
  132. key.type = BTRFS_INODE_ITEM_KEY;
  133. key.offset = 0;
  134. if (btrfs_lookup_inode(root, &key, inode_item, NULL))
  135. return -1ULL;
  136. }
  137. return inr;
  138. }
  139. res = btrfs_lookup_inode_ref(root, inr, NULL, NULL);
  140. if (res == -1ULL)
  141. return -1ULL;
  142. if (inode_item) {
  143. key.objectid = res;
  144. key.type = BTRFS_INODE_ITEM_KEY;
  145. key.offset = 0;
  146. if (btrfs_lookup_inode(root, &key, inode_item, NULL))
  147. return -1ULL;
  148. }
  149. return res;
  150. }
  151. static inline int next_length(const char *path)
  152. {
  153. int res = 0;
  154. while (*path != '\0' && *path != '/' && res <= BTRFS_NAME_LEN)
  155. ++res, ++path;
  156. return res;
  157. }
  158. static inline const char *skip_current_directories(const char *cur)
  159. {
  160. while (1) {
  161. if (cur[0] == '/')
  162. ++cur;
  163. else if (cur[0] == '.' && cur[1] == '/')
  164. cur += 2;
  165. else
  166. break;
  167. }
  168. return cur;
  169. }
  170. u64 btrfs_lookup_path(struct btrfs_root *root, u64 inr, const char *path,
  171. u8 *type_p, struct btrfs_inode_item *inode_item_p,
  172. int symlink_limit)
  173. {
  174. struct btrfs_dir_item item;
  175. struct btrfs_inode_item inode_item;
  176. u8 type = BTRFS_FT_DIR;
  177. int len, have_inode = 0;
  178. const char *cur = path;
  179. if (*cur == '/') {
  180. ++cur;
  181. inr = root->root_dirid;
  182. }
  183. do {
  184. cur = skip_current_directories(cur);
  185. len = next_length(cur);
  186. if (len > BTRFS_NAME_LEN) {
  187. printf("%s: Name too long at \"%.*s\"\n", __func__,
  188. BTRFS_NAME_LEN, cur);
  189. return -1ULL;
  190. }
  191. if (len == 1 && cur[0] == '.')
  192. break;
  193. if (len == 2 && cur[0] == '.' && cur[1] == '.') {
  194. cur += 2;
  195. inr = get_parent_inode(root, inr, &inode_item);
  196. if (inr == -1ULL)
  197. return -1ULL;
  198. type = BTRFS_FT_DIR;
  199. continue;
  200. }
  201. if (!*cur)
  202. break;
  203. if (btrfs_lookup_dir_item(root, inr, cur, len, &item))
  204. return -1ULL;
  205. type = item.type;
  206. have_inode = 1;
  207. if (btrfs_lookup_inode(root, &item.location, &inode_item, root))
  208. return -1ULL;
  209. if (item.type == BTRFS_FT_SYMLINK && symlink_limit >= 0) {
  210. char *target;
  211. if (!symlink_limit) {
  212. printf("%s: Too much symlinks!\n", __func__);
  213. return -1ULL;
  214. }
  215. target = malloc(min(inode_item.size + 1,
  216. (u64) btrfs_info.sb.sectorsize));
  217. if (!target)
  218. return -1ULL;
  219. if (btrfs_readlink(root, item.location.objectid,
  220. target)) {
  221. free(target);
  222. return -1ULL;
  223. }
  224. inr = btrfs_lookup_path(root, inr, target, &type,
  225. &inode_item, symlink_limit - 1);
  226. free(target);
  227. if (inr == -1ULL)
  228. return -1ULL;
  229. } else if (item.type != BTRFS_FT_DIR && cur[len]) {
  230. printf("%s: \"%.*s\" not a directory\n", __func__,
  231. (int) (cur - path + len), path);
  232. return -1ULL;
  233. } else {
  234. inr = item.location.objectid;
  235. }
  236. cur += len;
  237. } while (*cur);
  238. if (type_p)
  239. *type_p = type;
  240. if (inode_item_p) {
  241. if (!have_inode) {
  242. struct btrfs_key key;
  243. key.objectid = inr;
  244. key.type = BTRFS_INODE_ITEM_KEY;
  245. key.offset = 0;
  246. if (btrfs_lookup_inode(root, &key, &inode_item, NULL))
  247. return -1ULL;
  248. }
  249. *inode_item_p = inode_item;
  250. }
  251. return inr;
  252. }
  253. u64 btrfs_file_read(const struct btrfs_root *root, u64 inr, u64 offset,
  254. u64 size, char *buf)
  255. {
  256. struct btrfs_path path;
  257. struct btrfs_key key;
  258. struct btrfs_file_extent_item *extent;
  259. int res = 0;
  260. u64 rd, rd_all = -1ULL;
  261. key.objectid = inr;
  262. key.type = BTRFS_EXTENT_DATA_KEY;
  263. key.offset = offset;
  264. if (btrfs_search_tree(root, &key, &path))
  265. return -1ULL;
  266. if (btrfs_comp_keys(&key, btrfs_path_leaf_key(&path)) < 0) {
  267. if (btrfs_prev_slot(&path))
  268. goto out;
  269. if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
  270. goto out;
  271. }
  272. rd_all = 0;
  273. do {
  274. if (btrfs_comp_keys_type(&key, btrfs_path_leaf_key(&path)))
  275. break;
  276. extent = btrfs_path_item_ptr(&path,
  277. struct btrfs_file_extent_item);
  278. if (extent->type == BTRFS_FILE_EXTENT_INLINE) {
  279. btrfs_file_extent_item_to_cpu_inl(extent);
  280. rd = btrfs_read_extent_inline(&path, extent, offset,
  281. size, buf);
  282. } else {
  283. btrfs_file_extent_item_to_cpu(extent);
  284. rd = btrfs_read_extent_reg(&path, extent, offset, size,
  285. buf);
  286. }
  287. if (rd == -1ULL) {
  288. printf("%s: Error reading extent\n", __func__);
  289. rd_all = -1;
  290. goto out;
  291. }
  292. offset = 0;
  293. buf += rd;
  294. rd_all += rd;
  295. size -= rd;
  296. if (!size)
  297. break;
  298. } while (!(res = btrfs_next_slot(&path)));
  299. if (res)
  300. return -1ULL;
  301. out:
  302. btrfs_free_path(&path);
  303. return rd_all;
  304. }