1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2007 Oracle. All rights reserved.
4 */
5
6 #include <linux/unaligned.h>
7 #include "messages.h"
8 #include "extent_io.h"
9 #include "fs.h"
10 #include "accessors.h"
11
report_setget_bounds(const struct extent_buffer * eb,const void * ptr,unsigned off,int size)12 static void __cold report_setget_bounds(const struct extent_buffer *eb,
13 const void *ptr, unsigned off, int size)
14 {
15 unsigned long member_offset = (unsigned long)ptr + off;
16
17 btrfs_warn(eb->fs_info,
18 "bad eb member %s: ptr 0x%lx start %llu member offset %lu size %d",
19 (member_offset > eb->len ? "start" : "end"),
20 (unsigned long)ptr, eb->start, member_offset, size);
21 }
22
23 /* Copy bytes from @src1 and @src2 to @dest. */
memcpy_split_src(char * dest,const char * src1,const char * src2,const size_t len1,const size_t total)24 static __always_inline void memcpy_split_src(char *dest, const char *src1,
25 const char *src2, const size_t len1,
26 const size_t total)
27 {
28 memcpy(dest, src1, len1);
29 memcpy(dest + len1, src2, total - len1);
30 }
31
32 /*
33 * Macro templates that define helpers to read/write extent buffer data of a
34 * given size, that are also used via ctree.h for access to item members by
35 * specialized helpers.
36 *
37 * Generic helpers:
38 * - btrfs_set_8 (for 8/16/32/64)
39 * - btrfs_get_8 (for 8/16/32/64)
40 *
41 * The set/get functions handle data spanning two pages transparently, in case
42 * metadata block size is larger than page. Every pointer to metadata items is
43 * an offset into the extent buffer page array, cast to a specific type. This
44 * gives us all the type checking.
45 *
46 * The extent buffer pages stored in the array folios may not form a contiguous
47 * phyusical range, but the API functions assume the linear offset to the range
48 * from 0 to metadata node size.
49 */
50
51 #define DEFINE_BTRFS_SETGET_BITS(bits) \
52 u##bits btrfs_get_##bits(const struct extent_buffer *eb, \
53 const void *ptr, unsigned long off) \
54 { \
55 const unsigned long member_offset = (unsigned long)ptr + off; \
56 const unsigned long idx = get_eb_folio_index(eb, member_offset);\
57 const unsigned long oif = get_eb_offset_in_folio(eb, \
58 member_offset);\
59 char *kaddr = folio_address(eb->folios[idx]) + oif; \
60 const int part = eb->folio_size - oif; \
61 u8 lebytes[sizeof(u##bits)]; \
62 \
63 if (unlikely(member_offset + sizeof(u##bits) > eb->len)) { \
64 report_setget_bounds(eb, ptr, off, sizeof(u##bits)); \
65 return 0; \
66 } \
67 if (INLINE_EXTENT_BUFFER_PAGES == 1 || sizeof(u##bits) == 1 || \
68 likely(sizeof(u##bits) <= part)) \
69 return get_unaligned_le##bits(kaddr); \
70 \
71 if (sizeof(u##bits) == 2) { \
72 lebytes[0] = *kaddr; \
73 kaddr = folio_address(eb->folios[idx + 1]); \
74 lebytes[1] = *kaddr; \
75 } else { \
76 memcpy_split_src(lebytes, kaddr, \
77 folio_address(eb->folios[idx + 1]), \
78 part, sizeof(u##bits)); \
79 } \
80 return get_unaligned_le##bits(lebytes); \
81 } \
82 void btrfs_set_##bits(const struct extent_buffer *eb, void *ptr, \
83 unsigned long off, u##bits val) \
84 { \
85 const unsigned long member_offset = (unsigned long)ptr + off; \
86 const unsigned long idx = get_eb_folio_index(eb, member_offset);\
87 const unsigned long oif = get_eb_offset_in_folio(eb, \
88 member_offset);\
89 char *kaddr = folio_address(eb->folios[idx]) + oif; \
90 const int part = eb->folio_size - oif; \
91 u8 lebytes[sizeof(u##bits)]; \
92 \
93 if (unlikely(member_offset + sizeof(u##bits) > eb->len)) { \
94 report_setget_bounds(eb, ptr, off, sizeof(u##bits)); \
95 return; \
96 } \
97 if (INLINE_EXTENT_BUFFER_PAGES == 1 || sizeof(u##bits) == 1 || \
98 likely(sizeof(u##bits) <= part)) { \
99 put_unaligned_le##bits(val, kaddr); \
100 return; \
101 } \
102 put_unaligned_le##bits(val, lebytes); \
103 if (sizeof(u##bits) == 2) { \
104 *kaddr = lebytes[0]; \
105 kaddr = folio_address(eb->folios[idx + 1]); \
106 *kaddr = lebytes[1]; \
107 } else { \
108 memcpy(kaddr, lebytes, part); \
109 kaddr = folio_address(eb->folios[idx + 1]); \
110 memcpy(kaddr, lebytes + part, sizeof(u##bits) - part); \
111 } \
112 }
113
114 DEFINE_BTRFS_SETGET_BITS(8)
115 DEFINE_BTRFS_SETGET_BITS(16)
116 DEFINE_BTRFS_SETGET_BITS(32)
117 DEFINE_BTRFS_SETGET_BITS(64)
118
btrfs_node_key(const struct extent_buffer * eb,struct btrfs_disk_key * disk_key,int nr)119 void btrfs_node_key(const struct extent_buffer *eb,
120 struct btrfs_disk_key *disk_key, int nr)
121 {
122 unsigned long ptr = btrfs_node_key_ptr_offset(eb, nr);
123 read_eb_member(eb, (struct btrfs_key_ptr *)ptr,
124 struct btrfs_key_ptr, key, disk_key);
125 }
126