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 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. */ 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 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