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