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 27 extern 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 34 static _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 EXT2FS_ENABLE_SWAPFS 55 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || 56 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) 57 b = ext2fs_swab32(b); 58 #endif 59 ((blk_t *) block_buf)[nr] = b; 60 return io_channel_write_blk(fs->io, ind, 1, block_buf); 61 } 62 63 b = ((blk_t *) block_buf)[nr]; 64 65 #ifdef EXT2FS_ENABLE_SWAPFS 66 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || 67 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) 68 b = ext2fs_swab32(b); 69 #endif 70 71 if (!b && (flags & BMAP_ALLOC)) { 72 b = nr ? ((blk_t *) block_buf)[nr-1] : 0; 73 retval = ext2fs_alloc_block(fs, b, 74 block_buf + fs->blocksize, &b); 75 if (retval) 76 return retval; 77 78 #ifdef EXT2FS_ENABLE_SWAPFS 79 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || 80 (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) 81 ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); 82 else 83 #endif 84 ((blk_t *) block_buf)[nr] = b; 85 86 retval = io_channel_write_blk(fs->io, ind, 1, block_buf); 87 if (retval) 88 return retval; 89 90 (*blocks_alloc)++; 91 } 92 93 *ret_blk = b; 94 return 0; 95 } 96 97 static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, 98 blk_t dind, char *block_buf, 99 int *blocks_alloc, 100 blk_t nr, blk_t *ret_blk) 101 { 102 blk_t b; 103 errcode_t retval; 104 blk_t addr_per_block; 105 106 addr_per_block = (blk_t) fs->blocksize >> 2; 107 108 retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, 109 blocks_alloc, nr / addr_per_block, &b); 110 if (retval) 111 return retval; 112 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, 113 nr % addr_per_block, ret_blk); 114 return retval; 115 } 116 117 static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, 118 blk_t tind, char *block_buf, 119 int *blocks_alloc, 120 blk_t nr, blk_t *ret_blk) 121 { 122 blk_t b; 123 errcode_t retval; 124 blk_t addr_per_block; 125 126 addr_per_block = (blk_t) fs->blocksize >> 2; 127 128 retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, 129 blocks_alloc, nr / addr_per_block, &b); 130 if (retval) 131 return retval; 132 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, 133 nr % addr_per_block, ret_blk); 134 return retval; 135 } 136 137 errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, 138 char *block_buf, int bmap_flags, blk_t block, 139 blk_t *phys_blk) 140 { 141 struct ext2_inode inode_buf; 142 blk_t addr_per_block; 143 blk_t b; 144 char *buf = 0; 145 errcode_t retval = 0; 146 int blocks_alloc = 0, inode_dirty = 0; 147 148 if (!(bmap_flags & BMAP_SET)) 149 *phys_blk = 0; 150 151 /* Read inode structure if necessary */ 152 if (!inode) { 153 retval = ext2fs_read_inode(fs, ino, &inode_buf); 154 if (retval) 155 return retval; 156 inode = &inode_buf; 157 } 158 addr_per_block = (blk_t) fs->blocksize >> 2; 159 160 if (!block_buf) { 161 retval = ext2fs_get_array(2, fs->blocksize, &buf); 162 if (retval) 163 return retval; 164 block_buf = buf; 165 } 166 167 if (block < EXT2_NDIR_BLOCKS) { 168 if (bmap_flags & BMAP_SET) { 169 b = *phys_blk; 170 #ifdef EXT2FS_ENABLE_SWAPFS 171 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) || 172 (fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) 173 b = ext2fs_swab32(b); 174 #endif 175 inode_bmap(inode, block) = b; 176 inode_dirty++; 177 goto done; 178 } 179 180 *phys_blk = inode_bmap(inode, block); 181 b = block ? inode_bmap(inode, block-1) : 0; 182 183 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { 184 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 185 if (retval) 186 goto done; 187 inode_bmap(inode, block) = b; 188 blocks_alloc++; 189 *phys_blk = b; 190 } 191 goto done; 192 } 193 194 /* Indirect block */ 195 block -= EXT2_NDIR_BLOCKS; 196 if (block < addr_per_block) { 197 b = inode_bmap(inode, EXT2_IND_BLOCK); 198 if (!b) { 199 if (!(bmap_flags & BMAP_ALLOC)) { 200 if (bmap_flags & BMAP_SET) 201 retval = EXT2_ET_SET_BMAP_NO_IND; 202 goto done; 203 } 204 205 b = inode_bmap(inode, EXT2_IND_BLOCK-1); 206 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 207 if (retval) 208 goto done; 209 inode_bmap(inode, EXT2_IND_BLOCK) = b; 210 blocks_alloc++; 211 } 212 retval = block_ind_bmap(fs, bmap_flags, b, block_buf, 213 &blocks_alloc, block, phys_blk); 214 goto done; 215 } 216 217 /* Doubly indirect block */ 218 block -= addr_per_block; 219 if (block < addr_per_block * addr_per_block) { 220 b = inode_bmap(inode, EXT2_DIND_BLOCK); 221 if (!b) { 222 if (!(bmap_flags & BMAP_ALLOC)) { 223 if (bmap_flags & BMAP_SET) 224 retval = EXT2_ET_SET_BMAP_NO_IND; 225 goto done; 226 } 227 228 b = inode_bmap(inode, EXT2_IND_BLOCK); 229 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 230 if (retval) 231 goto done; 232 inode_bmap(inode, EXT2_DIND_BLOCK) = b; 233 blocks_alloc++; 234 } 235 retval = block_dind_bmap(fs, bmap_flags, b, block_buf, 236 &blocks_alloc, block, phys_blk); 237 goto done; 238 } 239 240 /* Triply indirect block */ 241 block -= addr_per_block * addr_per_block; 242 b = inode_bmap(inode, EXT2_TIND_BLOCK); 243 if (!b) { 244 if (!(bmap_flags & BMAP_ALLOC)) { 245 if (bmap_flags & BMAP_SET) 246 retval = EXT2_ET_SET_BMAP_NO_IND; 247 goto done; 248 } 249 250 b = inode_bmap(inode, EXT2_DIND_BLOCK); 251 retval = ext2fs_alloc_block(fs, b, block_buf, &b); 252 if (retval) 253 goto done; 254 inode_bmap(inode, EXT2_TIND_BLOCK) = b; 255 blocks_alloc++; 256 } 257 retval = block_tind_bmap(fs, bmap_flags, b, block_buf, 258 &blocks_alloc, block, phys_blk); 259 done: 260 if (buf) 261 ext2fs_free_mem(&buf); 262 if ((retval == 0) && (blocks_alloc || inode_dirty)) { 263 inode->i_blocks += (blocks_alloc * fs->blocksize) / 512; 264 retval = ext2fs_write_inode(fs, ino, inode); 265 } 266 return retval; 267 } 268 269 270 271