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 
     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