Home | History | Annotate | Download | only in ext2fs
      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 #include <errno.h>
     18 
     19 #include "ext2_fs.h"
     20 #include "ext2fs.h"
     21 
     22 #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
     23 #define _BMAP_INLINE_	__inline__
     24 #else
     25 #define _BMAP_INLINE_
     26 #endif
     27 
     28 extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
     29 			     struct ext2_inode *inode,
     30 			     char *block_buf, int bmap_flags,
     31 			     blk_t block, blk_t *phys_blk);
     32 
     33 #define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
     34 
     35 static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags,
     36 					      blk_t ind, char *block_buf,
     37 					      int *blocks_alloc,
     38 					      blk_t nr, blk_t *ret_blk)
     39 {
     40 	errcode_t	retval;
     41 	blk_t		b;
     42 
     43 	if (!ind) {
     44 		if (flags & BMAP_SET)
     45 			return EXT2_ET_SET_BMAP_NO_IND;
     46 		*ret_blk = 0;
     47 		return 0;
     48 	}
     49 	retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
     50 	if (retval)
     51 		return retval;
     52 
     53 	if (flags & BMAP_SET) {
     54 		b = *ret_blk;
     55 #ifdef WORDS_BIGENDIAN
     56 		b = ext2fs_swab32(b);
     57 #endif
     58 		((blk_t *) block_buf)[nr] = b;
     59 		return io_channel_write_blk(fs->io, ind, 1, block_buf);
     60 	}
     61 
     62 	b = ((blk_t *) block_buf)[nr];
     63 
     64 #ifdef WORDS_BIGENDIAN
     65 	b = ext2fs_swab32(b);
     66 #endif
     67 
     68 	if (!b && (flags & BMAP_ALLOC)) {
     69 		b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
     70 		retval = ext2fs_alloc_block(fs, b,
     71 					    block_buf + fs->blocksize, &b);
     72 		if (retval)
     73 			return retval;
     74 
     75 #ifdef WORDS_BIGENDIAN
     76 		((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
     77 #else
     78 		((blk_t *) block_buf)[nr] = b;
     79 #endif
     80 
     81 		retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
     82 		if (retval)
     83 			return retval;
     84 
     85 		(*blocks_alloc)++;
     86 	}
     87 
     88 	*ret_blk = b;
     89 	return 0;
     90 }
     91 
     92 static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags,
     93 					       blk_t dind, char *block_buf,
     94 					       int *blocks_alloc,
     95 					       blk_t nr, blk_t *ret_blk)
     96 {
     97 	blk_t		b;
     98 	errcode_t	retval;
     99 	blk_t		addr_per_block;
    100 
    101 	addr_per_block = (blk_t) fs->blocksize >> 2;
    102 
    103 	retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf,
    104 				blocks_alloc, nr / addr_per_block, &b);
    105 	if (retval)
    106 		return retval;
    107 	retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
    108 				nr % addr_per_block, ret_blk);
    109 	return retval;
    110 }
    111 
    112 static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags,
    113 					       blk_t tind, char *block_buf,
    114 					       int *blocks_alloc,
    115 					       blk_t nr, blk_t *ret_blk)
    116 {
    117 	blk_t		b;
    118 	errcode_t	retval;
    119 	blk_t		addr_per_block;
    120 
    121 	addr_per_block = (blk_t) fs->blocksize >> 2;
    122 
    123 	retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf,
    124 				 blocks_alloc, nr / addr_per_block, &b);
    125 	if (retval)
    126 		return retval;
    127 	retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
    128 				nr % addr_per_block, ret_blk);
    129 	return retval;
    130 }
    131 
    132 errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
    133 		       char *block_buf, int bmap_flags, blk64_t block,
    134 		       int *ret_flags, blk64_t *phys_blk)
    135 {
    136 	struct ext2_inode inode_buf;
    137 	ext2_extent_handle_t handle = 0;
    138 	blk_t addr_per_block;
    139 	blk_t	b, blk32;
    140 	char	*buf = 0;
    141 	errcode_t	retval = 0;
    142 	int		blocks_alloc = 0, inode_dirty = 0;
    143 
    144 	if (!(bmap_flags & BMAP_SET))
    145 		*phys_blk = 0;
    146 
    147 	if (ret_flags)
    148 		*ret_flags = 0;
    149 
    150 	/* Read inode structure if necessary */
    151 	if (!inode) {
    152 		retval = ext2fs_read_inode(fs, ino, &inode_buf);
    153 		if (retval)
    154 			return retval;
    155 		inode = &inode_buf;
    156 	}
    157 	addr_per_block = (blk_t) fs->blocksize >> 2;
    158 
    159 	if (inode->i_flags & EXT4_EXTENTS_FL) {
    160 		struct ext2fs_extent	extent;
    161 		unsigned int		offset;
    162 
    163 		retval = ext2fs_extent_open2(fs, ino, inode, &handle);
    164 		if (retval)
    165 			goto done;
    166 		if (bmap_flags & BMAP_SET) {
    167 			retval = ext2fs_extent_set_bmap(handle, block,
    168 							*phys_blk, 0);
    169 			goto done;
    170 		}
    171 		retval = ext2fs_extent_goto(handle, block);
    172 		if (retval) {
    173 			/* If the extent is not found, return phys_blk = 0 */
    174 			if (retval == EXT2_ET_EXTENT_NOT_FOUND)
    175 				goto got_block;
    176 			goto done;
    177 		}
    178 		retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
    179 		if (retval)
    180 			goto done;
    181 		offset = block - extent.e_lblk;
    182 		if (block >= extent.e_lblk && (offset <= extent.e_len)) {
    183 			*phys_blk = extent.e_pblk + offset;
    184 			if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
    185 				*ret_flags |= BMAP_RET_UNINIT;
    186 		}
    187 	got_block:
    188 		if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
    189 			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
    190 			if (retval)
    191 				goto done;
    192 			retval = ext2fs_extent_set_bmap(handle, block,
    193 							(blk64_t) b, 0);
    194 			if (retval)
    195 				goto done;
    196 			/* Update inode after setting extent */
    197 			retval = ext2fs_read_inode(fs, ino, inode);
    198 			if (retval)
    199 				return retval;
    200 			blocks_alloc++;
    201 			*phys_blk = b;
    202 		}
    203 		retval = 0;
    204 		goto done;
    205 	}
    206 
    207 	if (!block_buf) {
    208 		retval = ext2fs_get_array(2, fs->blocksize, &buf);
    209 		if (retval)
    210 			return retval;
    211 		block_buf = buf;
    212 	}
    213 
    214 	if (block < EXT2_NDIR_BLOCKS) {
    215 		if (bmap_flags & BMAP_SET) {
    216 			b = *phys_blk;
    217 			inode_bmap(inode, block) = b;
    218 			inode_dirty++;
    219 			goto done;
    220 		}
    221 
    222 		*phys_blk = inode_bmap(inode, block);
    223 		b = block ? inode_bmap(inode, block-1) : 0;
    224 
    225 		if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
    226 			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
    227 			if (retval)
    228 				goto done;
    229 			inode_bmap(inode, block) = b;
    230 			blocks_alloc++;
    231 			*phys_blk = b;
    232 		}
    233 		goto done;
    234 	}
    235 
    236 	/* Indirect block */
    237 	block -= EXT2_NDIR_BLOCKS;
    238 	blk32 = *phys_blk;
    239 	if (block < addr_per_block) {
    240 		b = inode_bmap(inode, EXT2_IND_BLOCK);
    241 		if (!b) {
    242 			if (!(bmap_flags & BMAP_ALLOC)) {
    243 				if (bmap_flags & BMAP_SET)
    244 					retval = EXT2_ET_SET_BMAP_NO_IND;
    245 				goto done;
    246 			}
    247 
    248 			b = inode_bmap(inode, EXT2_IND_BLOCK-1);
    249  			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
    250 			if (retval)
    251 				goto done;
    252 			inode_bmap(inode, EXT2_IND_BLOCK) = b;
    253 			blocks_alloc++;
    254 		}
    255 		retval = block_ind_bmap(fs, bmap_flags, b, block_buf,
    256 					&blocks_alloc, block, &blk32);
    257 		if (retval == 0)
    258 			*phys_blk = blk32;
    259 		goto done;
    260 	}
    261 
    262 	/* Doubly indirect block  */
    263 	block -= addr_per_block;
    264 	if (block < addr_per_block * addr_per_block) {
    265 		b = inode_bmap(inode, EXT2_DIND_BLOCK);
    266 		if (!b) {
    267 			if (!(bmap_flags & BMAP_ALLOC)) {
    268 				if (bmap_flags & BMAP_SET)
    269 					retval = EXT2_ET_SET_BMAP_NO_IND;
    270 				goto done;
    271 			}
    272 
    273 			b = inode_bmap(inode, EXT2_IND_BLOCK);
    274  			retval = ext2fs_alloc_block(fs, b, block_buf, &b);
    275 			if (retval)
    276 				goto done;
    277 			inode_bmap(inode, EXT2_DIND_BLOCK) = b;
    278 			blocks_alloc++;
    279 		}
    280 		retval = block_dind_bmap(fs, bmap_flags, b, block_buf,
    281 					 &blocks_alloc, block, &blk32);
    282 		if (retval == 0)
    283 			*phys_blk = blk32;
    284 		goto done;
    285 	}
    286 
    287 	/* Triply indirect block */
    288 	block -= addr_per_block * addr_per_block;
    289 	b = inode_bmap(inode, EXT2_TIND_BLOCK);
    290 	if (!b) {
    291 		if (!(bmap_flags & BMAP_ALLOC)) {
    292 			if (bmap_flags & BMAP_SET)
    293 				retval = EXT2_ET_SET_BMAP_NO_IND;
    294 			goto done;
    295 		}
    296 
    297 		b = inode_bmap(inode, EXT2_DIND_BLOCK);
    298 		retval = ext2fs_alloc_block(fs, b, block_buf, &b);
    299 		if (retval)
    300 			goto done;
    301 		inode_bmap(inode, EXT2_TIND_BLOCK) = b;
    302 		blocks_alloc++;
    303 	}
    304 	retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
    305 				 &blocks_alloc, block, &blk32);
    306 	if (retval == 0)
    307 		*phys_blk = blk32;
    308 done:
    309 	if (buf)
    310 		ext2fs_free_mem(&buf);
    311 	if (handle)
    312 		ext2fs_extent_free(handle);
    313 	if ((retval == 0) && (blocks_alloc || inode_dirty)) {
    314 		ext2fs_iblk_add_blocks(fs, inode, blocks_alloc);
    315 		retval = ext2fs_write_inode(fs, ino, inode);
    316 	}
    317 	return retval;
    318 }
    319 
    320 errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
    321 		      char *block_buf, int bmap_flags, blk_t block,
    322 		      blk_t *phys_blk)
    323 {
    324 	errcode_t ret;
    325 	blk64_t	ret_blk = *phys_blk;
    326 
    327 	ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block,
    328 			    0, &ret_blk);
    329 	if (ret)
    330 		return ret;
    331 	if (ret_blk >= ((long long) 1 << 32))
    332 		return EOVERFLOW;
    333 	*phys_blk = ret_blk;
    334 	return 0;
    335 }
    336