bmap.c revision 126a291c768b523bc228b276d3bea82675a86d09
1/* 2 * bmap.c --- logical to physical block mapping 3 * 4 * Copyright (C) 1997 Theodore Ts'o. 5 * 6 * %Begin-Header% 7 * This file may be redistributed under the terms of the GNU Public 8 * License. 9 * %End-Header% 10 */ 11 12#include <stdio.h> 13#include <string.h> 14#if HAVE_UNISTD_H 15#include <unistd.h> 16#endif 17 18#include "ext2_fs.h" 19#include "ext2fs.h" 20 21#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) 22#define _BMAP_INLINE_ __inline__ 23#else 24#define _BMAP_INLINE_ 25#endif 26 27extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, 28 struct ext2_inode *inode, 29 char *block_buf, int bmap_flags, 30 blk_t block, blk_t *phys_blk); 31 32#define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) 33 34static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 35 blk_t ind, char *block_buf, 36 int *blocks_alloc, 37 blk_t nr, blk_t *ret_blk) 38{ 39 errcode_t retval; 40 blk_t b; 41 42 if (!ind) { 43 if (flags & BMAP_SET) 44 return EXT2_ET_SET_BMAP_NO_IND; 45 *ret_blk = 0; 46 return 0; 47 } 48 retval = io_channel_read_blk(fs->io, ind, 1, block_buf); 49 if (retval) 50 return retval; 51 52 if (flags & BMAP_SET) { 53 b = *ret_blk; 54#ifdef WORDS_BIGENDIAN 55 b = ext2fs_swab32(b); 56#endif 57 ((blk_t *) block_buf)[nr] = b; 58 return io_channel_write_blk(fs->io, ind, 1, block_buf); 59 } 60 61 b = ((blk_t *) block_buf)[nr]; 62 63#ifdef WORDS_BIGENDIAN 64 b = ext2fs_swab32(b); 65#endif 66 67 if (!b && (flags & BMAP_ALLOC)) { 68 b = nr ? ((blk_t *) block_buf)[nr-1] : 0; 69 retval = ext2fs_alloc_block(fs, b, 70 block_buf + fs->blocksize, &b); 71 if (retval) 72 return retval; 73 74#ifdef WORDS_BIGENDIAN 75 ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); 76#else 77 ((blk_t *) block_buf)[nr] = b; 78#endif 79 80 retval = io_channel_write_blk(fs->io, ind, 1, block_buf); 81 if (retval) 82 return retval; 83 84 (*blocks_alloc)++; 85 } 86 87 *ret_blk = b; 88 return 0; 89} 90 91static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, 92 blk_t dind, char *block_buf, 93 int *blocks_alloc, 94 blk_t nr, blk_t *ret_blk) 95{ 96 blk_t b; 97 errcode_t retval; 98 blk_t addr_per_block; 99 100 addr_per_block = (blk_t) fs->blocksize >> 2; 101 102 retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, 103 blocks_alloc, nr / addr_per_block, &b); 104 if (retval) 105 return retval; 106 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, 107 nr % addr_per_block, ret_blk); 108 return retval; 109} 110 111static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, 112 blk_t tind, char *block_buf, 113 int *blocks_alloc, 114 blk_t nr, blk_t *ret_blk) 115{ 116 blk_t b; 117 errcode_t retval; 118 blk_t addr_per_block; 119 120 addr_per_block = (blk_t) fs->blocksize >> 2; 121 122 retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, 123 blocks_alloc, nr / addr_per_block, &b); 124 if (retval) 125 return retval; 126 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, 127 nr % addr_per_block, ret_blk); 128 return retval; 129} 130 131errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, 132 char *block_buf, int bmap_flags, blk_t block, 133 blk_t *phys_blk) 134{ 135 struct ext2_inode inode_buf; 136 blk_t addr_per_block; 137 blk_t b; 138 char *buf = 0; 139 errcode_t retval = 0; 140 int blocks_alloc = 0, inode_dirty = 0; 141 142 if (!(bmap_flags & BMAP_SET)) 143 *phys_blk = 0; 144 145 /* Read inode structure if necessary */ 146 if (!inode) { 147 retval = ext2fs_read_inode(fs, ino, &inode_buf); 148 if (retval) 149 return retval; 150 inode = &inode_buf; 151 } 152 addr_per_block = (blk_t) fs->blocksize >> 2; 153 154 if (!block_buf) { 155 retval = ext2fs_get_mem(fs->blocksize * 2, &buf); 156 if (retval) 157 return retval; 158 block_buf = buf; 159 } 160 161 if (block < EXT2_NDIR_BLOCKS) { 162 if (bmap_flags & BMAP_SET) { 163 b = *phys_blk; 164#ifdef WORDS_BIGENDIAN 165 b = ext2fs_swab32(b); 166#endif 167 inode_bmap(inode, block) = b; 168 inode_dirty++; 169 goto done; 170 } 171 172 *phys_blk = inode_bmap(inode, block); 173 b = block ? inode_bmap(inode, block-1) : 0; 174 175 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { 176 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 177 if (retval) 178 goto done; 179 inode_bmap(inode, block) = b; 180 blocks_alloc++; 181 *phys_blk = b; 182 } 183 goto done; 184 } 185 186 /* Indirect block */ 187 block -= EXT2_NDIR_BLOCKS; 188 if (block < addr_per_block) { 189 b = inode_bmap(inode, EXT2_IND_BLOCK); 190 if (!b) { 191 if (!(bmap_flags & BMAP_ALLOC)) { 192 if (bmap_flags & BMAP_SET) 193 retval = EXT2_ET_SET_BMAP_NO_IND; 194 goto done; 195 } 196 197 b = inode_bmap(inode, EXT2_IND_BLOCK-1); 198 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 199 if (retval) 200 goto done; 201 inode_bmap(inode, EXT2_IND_BLOCK) = b; 202 blocks_alloc++; 203 } 204 retval = block_ind_bmap(fs, bmap_flags, b, block_buf, 205 &blocks_alloc, block, phys_blk); 206 goto done; 207 } 208 209 /* Doubly indirect block */ 210 block -= addr_per_block; 211 if (block < addr_per_block * addr_per_block) { 212 b = inode_bmap(inode, EXT2_DIND_BLOCK); 213 if (!b) { 214 if (!(bmap_flags & BMAP_ALLOC)) { 215 if (bmap_flags & BMAP_SET) 216 retval = EXT2_ET_SET_BMAP_NO_IND; 217 goto done; 218 } 219 220 b = inode_bmap(inode, EXT2_IND_BLOCK); 221 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 222 if (retval) 223 goto done; 224 inode_bmap(inode, EXT2_DIND_BLOCK) = b; 225 blocks_alloc++; 226 } 227 retval = block_dind_bmap(fs, bmap_flags, b, block_buf, 228 &blocks_alloc, block, phys_blk); 229 goto done; 230 } 231 232 /* Triply indirect block */ 233 block -= addr_per_block * addr_per_block; 234 b = inode_bmap(inode, EXT2_TIND_BLOCK); 235 if (!b) { 236 if (!(bmap_flags & BMAP_ALLOC)) { 237 if (bmap_flags & BMAP_SET) 238 retval = EXT2_ET_SET_BMAP_NO_IND; 239 goto done; 240 } 241 242 b = inode_bmap(inode, EXT2_DIND_BLOCK); 243 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 244 if (retval) 245 goto done; 246 inode_bmap(inode, EXT2_TIND_BLOCK) = b; 247 blocks_alloc++; 248 } 249 retval = block_tind_bmap(fs, bmap_flags, b, block_buf, 250 &blocks_alloc, block, phys_blk); 251done: 252 if (buf) 253 ext2fs_free_mem(&buf); 254 if ((retval == 0) && (blocks_alloc || inode_dirty)) { 255 inode->i_blocks += (blocks_alloc * fs->blocksize) / 512; 256 retval = ext2fs_write_inode(fs, ino, inode); 257 } 258 return retval; 259} 260 261 262 263