1/* 2 * linux/fs/ext4/block_validity.c 3 * 4 * Copyright (C) 2009 5 * Theodore Ts'o (tytso@mit.edu) 6 * 7 * Track which blocks in the filesystem are metadata blocks that 8 * should never be used as data blocks by files or directories. 9 */ 10 11#include <linux/time.h> 12#include <linux/fs.h> 13#include <linux/namei.h> 14#include <linux/quotaops.h> 15#include <linux/buffer_head.h> 16#include <linux/swap.h> 17#include <linux/pagemap.h> 18#include <linux/blkdev.h> 19#include <linux/slab.h> 20#include "ext4.h" 21 22struct ext4_system_zone { 23 struct rb_node node; 24 ext4_fsblk_t start_blk; 25 unsigned int count; 26}; 27 28static struct kmem_cache *ext4_system_zone_cachep; 29 30int __init ext4_init_system_zone(void) 31{ 32 ext4_system_zone_cachep = KMEM_CACHE(ext4_system_zone, 0); 33 if (ext4_system_zone_cachep == NULL) 34 return -ENOMEM; 35 return 0; 36} 37 38void ext4_exit_system_zone(void) 39{ 40 kmem_cache_destroy(ext4_system_zone_cachep); 41} 42 43static inline int can_merge(struct ext4_system_zone *entry1, 44 struct ext4_system_zone *entry2) 45{ 46 if ((entry1->start_blk + entry1->count) == entry2->start_blk) 47 return 1; 48 return 0; 49} 50 51/* 52 * Mark a range of blocks as belonging to the "system zone" --- that 53 * is, filesystem metadata blocks which should never be used by 54 * inodes. 55 */ 56static int add_system_zone(struct ext4_sb_info *sbi, 57 ext4_fsblk_t start_blk, 58 unsigned int count) 59{ 60 struct ext4_system_zone *new_entry = NULL, *entry; 61 struct rb_node **n = &sbi->system_blks.rb_node, *node; 62 struct rb_node *parent = NULL, *new_node = NULL; 63 64 while (*n) { 65 parent = *n; 66 entry = rb_entry(parent, struct ext4_system_zone, node); 67 if (start_blk < entry->start_blk) 68 n = &(*n)->rb_left; 69 else if (start_blk >= (entry->start_blk + entry->count)) 70 n = &(*n)->rb_right; 71 else { 72 if (start_blk + count > (entry->start_blk + 73 entry->count)) 74 entry->count = (start_blk + count - 75 entry->start_blk); 76 new_node = *n; 77 new_entry = rb_entry(new_node, struct ext4_system_zone, 78 node); 79 break; 80 } 81 } 82 83 if (!new_entry) { 84 new_entry = kmem_cache_alloc(ext4_system_zone_cachep, 85 GFP_KERNEL); 86 if (!new_entry) 87 return -ENOMEM; 88 new_entry->start_blk = start_blk; 89 new_entry->count = count; 90 new_node = &new_entry->node; 91 92 rb_link_node(new_node, parent, n); 93 rb_insert_color(new_node, &sbi->system_blks); 94 } 95 96 /* Can we merge to the left? */ 97 node = rb_prev(new_node); 98 if (node) { 99 entry = rb_entry(node, struct ext4_system_zone, node); 100 if (can_merge(entry, new_entry)) { 101 new_entry->start_blk = entry->start_blk; 102 new_entry->count += entry->count; 103 rb_erase(node, &sbi->system_blks); 104 kmem_cache_free(ext4_system_zone_cachep, entry); 105 } 106 } 107 108 /* Can we merge to the right? */ 109 node = rb_next(new_node); 110 if (node) { 111 entry = rb_entry(node, struct ext4_system_zone, node); 112 if (can_merge(new_entry, entry)) { 113 new_entry->count += entry->count; 114 rb_erase(node, &sbi->system_blks); 115 kmem_cache_free(ext4_system_zone_cachep, entry); 116 } 117 } 118 return 0; 119} 120 121static void debug_print_tree(struct ext4_sb_info *sbi) 122{ 123 struct rb_node *node; 124 struct ext4_system_zone *entry; 125 int first = 1; 126 127 printk(KERN_INFO "System zones: "); 128 node = rb_first(&sbi->system_blks); 129 while (node) { 130 entry = rb_entry(node, struct ext4_system_zone, node); 131 printk("%s%llu-%llu", first ? "" : ", ", 132 entry->start_blk, entry->start_blk + entry->count - 1); 133 first = 0; 134 node = rb_next(node); 135 } 136 printk("\n"); 137} 138 139int ext4_setup_system_zone(struct super_block *sb) 140{ 141 ext4_group_t ngroups = ext4_get_groups_count(sb); 142 struct ext4_sb_info *sbi = EXT4_SB(sb); 143 struct ext4_group_desc *gdp; 144 ext4_group_t i; 145 int flex_size = ext4_flex_bg_size(sbi); 146 int ret; 147 148 if (!test_opt(sb, BLOCK_VALIDITY)) { 149 if (EXT4_SB(sb)->system_blks.rb_node) 150 ext4_release_system_zone(sb); 151 return 0; 152 } 153 if (EXT4_SB(sb)->system_blks.rb_node) 154 return 0; 155 156 for (i=0; i < ngroups; i++) { 157 if (ext4_bg_has_super(sb, i) && 158 ((i < 5) || ((i % flex_size) == 0))) 159 add_system_zone(sbi, ext4_group_first_block_no(sb, i), 160 ext4_bg_num_gdb(sb, i) + 1); 161 gdp = ext4_get_group_desc(sb, i, NULL); 162 ret = add_system_zone(sbi, ext4_block_bitmap(sb, gdp), 1); 163 if (ret) 164 return ret; 165 ret = add_system_zone(sbi, ext4_inode_bitmap(sb, gdp), 1); 166 if (ret) 167 return ret; 168 ret = add_system_zone(sbi, ext4_inode_table(sb, gdp), 169 sbi->s_itb_per_group); 170 if (ret) 171 return ret; 172 } 173 174 if (test_opt(sb, DEBUG)) 175 debug_print_tree(EXT4_SB(sb)); 176 return 0; 177} 178 179/* Called when the filesystem is unmounted */ 180void ext4_release_system_zone(struct super_block *sb) 181{ 182 struct ext4_system_zone *entry, *n; 183 184 rbtree_postorder_for_each_entry_safe(entry, n, 185 &EXT4_SB(sb)->system_blks, node) 186 kmem_cache_free(ext4_system_zone_cachep, entry); 187 188 EXT4_SB(sb)->system_blks = RB_ROOT; 189} 190 191/* 192 * Returns 1 if the passed-in block region (start_blk, 193 * start_blk+count) is valid; 0 if some part of the block region 194 * overlaps with filesystem metadata blocks. 195 */ 196int ext4_data_block_valid(struct ext4_sb_info *sbi, ext4_fsblk_t start_blk, 197 unsigned int count) 198{ 199 struct ext4_system_zone *entry; 200 struct rb_node *n = sbi->system_blks.rb_node; 201 202 if ((start_blk <= le32_to_cpu(sbi->s_es->s_first_data_block)) || 203 (start_blk + count < start_blk) || 204 (start_blk + count > ext4_blocks_count(sbi->s_es))) { 205 sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); 206 return 0; 207 } 208 while (n) { 209 entry = rb_entry(n, struct ext4_system_zone, node); 210 if (start_blk + count - 1 < entry->start_blk) 211 n = n->rb_left; 212 else if (start_blk >= (entry->start_blk + entry->count)) 213 n = n->rb_right; 214 else { 215 sbi->s_es->s_last_error_block = cpu_to_le64(start_blk); 216 return 0; 217 } 218 } 219 return 1; 220} 221 222int ext4_check_blockref(const char *function, unsigned int line, 223 struct inode *inode, __le32 *p, unsigned int max) 224{ 225 struct ext4_super_block *es = EXT4_SB(inode->i_sb)->s_es; 226 __le32 *bref = p; 227 unsigned int blk; 228 229 while (bref < p+max) { 230 blk = le32_to_cpu(*bref++); 231 if (blk && 232 unlikely(!ext4_data_block_valid(EXT4_SB(inode->i_sb), 233 blk, 1))) { 234 es->s_last_error_block = cpu_to_le64(blk); 235 ext4_error_inode(inode, function, line, blk, 236 "invalid block"); 237 return -EFSCORRUPTED; 238 } 239 } 240 return 0; 241} 242 243