]>
Commit | Line | Data |
---|---|---|
30fab293 | 1 | /* |
80e808fc | 2 | * bmap.c --- logical to physical block mapping |
30fab293 TT |
3 | * |
4 | * Copyright (C) 1997 Theodore Ts'o. | |
5 | * | |
6 | * %Begin-Header% | |
543547a5 TT |
7 | * This file may be redistributed under the terms of the GNU Library |
8 | * General Public License, version 2. | |
30fab293 TT |
9 | * %End-Header% |
10 | */ | |
11 | ||
12 | #include <stdio.h> | |
13 | #include <string.h> | |
14 | #if HAVE_UNISTD_H | |
15 | #include <unistd.h> | |
16 | #endif | |
cc9bf5d2 | 17 | #include <errno.h> |
30fab293 | 18 | |
b5abe6fa | 19 | #include "ext2_fs.h" |
30fab293 TT |
20 | #include "ext2fs.h" |
21 | ||
78d8f90f | 22 | #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) |
30fab293 TT |
23 | #define _BMAP_INLINE_ __inline__ |
24 | #else | |
25 | #define _BMAP_INLINE_ | |
26 | #endif | |
27 | ||
31dbecd4 | 28 | extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, |
efc6f628 | 29 | struct ext2_inode *inode, |
30fab293 TT |
30 | char *block_buf, int bmap_flags, |
31 | blk_t block, blk_t *phys_blk); | |
32 | ||
30fab293 TT |
33 | #define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) |
34 | ||
efc6f628 TT |
35 | static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, |
36 | blk_t ind, char *block_buf, | |
30fab293 TT |
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) { | |
1d667534 TT |
44 | if (flags & BMAP_SET) |
45 | return EXT2_ET_SET_BMAP_NO_IND; | |
30fab293 TT |
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 | ||
1d667534 TT |
53 | if (flags & BMAP_SET) { |
54 | b = *ret_blk; | |
126a291c TT |
55 | #ifdef WORDS_BIGENDIAN |
56 | b = ext2fs_swab32(b); | |
1d667534 TT |
57 | #endif |
58 | ((blk_t *) block_buf)[nr] = b; | |
59 | return io_channel_write_blk(fs->io, ind, 1, block_buf); | |
60 | } | |
61 | ||
30fab293 TT |
62 | b = ((blk_t *) block_buf)[nr]; |
63 | ||
126a291c TT |
64 | #ifdef WORDS_BIGENDIAN |
65 | b = ext2fs_swab32(b); | |
5df55d7f | 66 | #endif |
30fab293 TT |
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 | ||
126a291c TT |
75 | #ifdef WORDS_BIGENDIAN |
76 | ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); | |
77 | #else | |
78 | ((blk_t *) block_buf)[nr] = b; | |
5df55d7f | 79 | #endif |
30fab293 TT |
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 | ||
546a1ff1 | 92 | static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, |
efc6f628 | 93 | blk_t dind, char *block_buf, |
30fab293 TT |
94 | int *blocks_alloc, |
95 | blk_t nr, blk_t *ret_blk) | |
96 | { | |
97 | blk_t b; | |
98 | errcode_t retval; | |
2eb374c9 | 99 | blk_t addr_per_block; |
efc6f628 | 100 | |
2eb374c9 | 101 | addr_per_block = (blk_t) fs->blocksize >> 2; |
30fab293 | 102 | |
efc6f628 | 103 | retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, |
1d667534 | 104 | blocks_alloc, nr / addr_per_block, &b); |
30fab293 TT |
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 | ||
546a1ff1 | 112 | static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, |
efc6f628 | 113 | blk_t tind, char *block_buf, |
30fab293 TT |
114 | int *blocks_alloc, |
115 | blk_t nr, blk_t *ret_blk) | |
116 | { | |
117 | blk_t b; | |
118 | errcode_t retval; | |
2eb374c9 | 119 | blk_t addr_per_block; |
efc6f628 | 120 | |
2eb374c9 | 121 | addr_per_block = (blk_t) fs->blocksize >> 2; |
30fab293 | 122 | |
efc6f628 | 123 | retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, |
1d667534 | 124 | blocks_alloc, nr / addr_per_block, &b); |
30fab293 TT |
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 | ||
cc9bf5d2 TT |
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) | |
30fab293 TT |
135 | { |
136 | struct ext2_inode inode_buf; | |
cc9bf5d2 | 137 | ext2_extent_handle_t handle = 0; |
062cc472 | 138 | blk64_t blk64; |
2eb374c9 | 139 | blk_t addr_per_block; |
cc9bf5d2 | 140 | blk_t b, blk32; |
30fab293 TT |
141 | char *buf = 0; |
142 | errcode_t retval = 0; | |
1d667534 | 143 | int blocks_alloc = 0, inode_dirty = 0; |
30fab293 | 144 | |
1d667534 TT |
145 | if (!(bmap_flags & BMAP_SET)) |
146 | *phys_blk = 0; | |
30fab293 | 147 | |
cc9bf5d2 TT |
148 | if (ret_flags) |
149 | *ret_flags = 0; | |
150 | ||
30fab293 TT |
151 | /* Read inode structure if necessary */ |
152 | if (!inode) { | |
153 | retval = ext2fs_read_inode(fs, ino, &inode_buf); | |
b38cd283 | 154 | if (retval) |
30fab293 TT |
155 | return retval; |
156 | inode = &inode_buf; | |
157 | } | |
2eb374c9 | 158 | addr_per_block = (blk_t) fs->blocksize >> 2; |
30fab293 | 159 | |
cc9bf5d2 TT |
160 | if (inode->i_flags & EXT4_EXTENTS_FL) { |
161 | struct ext2fs_extent extent; | |
2d328bb7 | 162 | unsigned int offset; |
cc9bf5d2 | 163 | |
84b239ae | 164 | retval = ext2fs_extent_open2(fs, ino, inode, &handle); |
cc9bf5d2 TT |
165 | if (retval) |
166 | goto done; | |
ec9d6dd3 TT |
167 | if (bmap_flags & BMAP_SET) { |
168 | retval = ext2fs_extent_set_bmap(handle, block, | |
169 | *phys_blk, 0); | |
170 | goto done; | |
171 | } | |
cc9bf5d2 TT |
172 | retval = ext2fs_extent_goto(handle, block); |
173 | if (retval) { | |
174 | /* If the extent is not found, return phys_blk = 0 */ | |
175 | if (retval == EXT2_ET_EXTENT_NOT_FOUND) | |
9033faf5 | 176 | goto got_block; |
cc9bf5d2 TT |
177 | goto done; |
178 | } | |
179 | retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); | |
180 | if (retval) | |
181 | goto done; | |
182 | offset = block - extent.e_lblk; | |
183 | if (block >= extent.e_lblk && (offset <= extent.e_len)) { | |
184 | *phys_blk = extent.e_pblk + offset; | |
185 | if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) | |
186 | *ret_flags |= BMAP_RET_UNINIT; | |
187 | } | |
9033faf5 | 188 | got_block: |
ec9d6dd3 | 189 | if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { |
062cc472 TT |
190 | retval = ext2fs_bmap2(fs, ino, inode, block_buf, |
191 | 0, block-1, 0, &blk64); | |
192 | if (retval) | |
193 | blk64 = 0; | |
194 | retval = ext2fs_alloc_block2(fs, blk64, block_buf, | |
195 | &blk64); | |
ec9d6dd3 TT |
196 | if (retval) |
197 | goto done; | |
198 | retval = ext2fs_extent_set_bmap(handle, block, | |
062cc472 | 199 | blk64, 0); |
ec9d6dd3 TT |
200 | if (retval) |
201 | goto done; | |
9033faf5 TT |
202 | /* Update inode after setting extent */ |
203 | retval = ext2fs_read_inode(fs, ino, inode); | |
204 | if (retval) | |
205 | return retval; | |
ec9d6dd3 | 206 | blocks_alloc++; |
062cc472 | 207 | *phys_blk = blk64; |
ec9d6dd3 | 208 | } |
cc9bf5d2 TT |
209 | retval = 0; |
210 | goto done; | |
211 | } | |
212 | ||
30fab293 | 213 | if (!block_buf) { |
ee01079a | 214 | retval = ext2fs_get_array(2, fs->blocksize, &buf); |
7b4e4534 TT |
215 | if (retval) |
216 | return retval; | |
30fab293 TT |
217 | block_buf = buf; |
218 | } | |
219 | ||
220 | if (block < EXT2_NDIR_BLOCKS) { | |
1d667534 TT |
221 | if (bmap_flags & BMAP_SET) { |
222 | b = *phys_blk; | |
1d667534 TT |
223 | inode_bmap(inode, block) = b; |
224 | inode_dirty++; | |
225 | goto done; | |
226 | } | |
227 | ||
30fab293 TT |
228 | *phys_blk = inode_bmap(inode, block); |
229 | b = block ? inode_bmap(inode, block-1) : 0; | |
efc6f628 | 230 | |
30fab293 TT |
231 | if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { |
232 | retval = ext2fs_alloc_block(fs, b, block_buf, &b); | |
233 | if (retval) | |
234 | goto done; | |
235 | inode_bmap(inode, block) = b; | |
236 | blocks_alloc++; | |
237 | *phys_blk = b; | |
238 | } | |
239 | goto done; | |
240 | } | |
efc6f628 | 241 | |
30fab293 TT |
242 | /* Indirect block */ |
243 | block -= EXT2_NDIR_BLOCKS; | |
cc9bf5d2 | 244 | blk32 = *phys_blk; |
30fab293 TT |
245 | if (block < addr_per_block) { |
246 | b = inode_bmap(inode, EXT2_IND_BLOCK); | |
247 | if (!b) { | |
1d667534 TT |
248 | if (!(bmap_flags & BMAP_ALLOC)) { |
249 | if (bmap_flags & BMAP_SET) | |
250 | retval = EXT2_ET_SET_BMAP_NO_IND; | |
251 | goto done; | |
252 | } | |
30fab293 TT |
253 | |
254 | b = inode_bmap(inode, EXT2_IND_BLOCK-1); | |
255 | retval = ext2fs_alloc_block(fs, b, block_buf, &b); | |
256 | if (retval) | |
257 | goto done; | |
258 | inode_bmap(inode, EXT2_IND_BLOCK) = b; | |
259 | blocks_alloc++; | |
260 | } | |
efc6f628 | 261 | retval = block_ind_bmap(fs, bmap_flags, b, block_buf, |
cc9bf5d2 TT |
262 | &blocks_alloc, block, &blk32); |
263 | if (retval == 0) | |
264 | *phys_blk = blk32; | |
30fab293 TT |
265 | goto done; |
266 | } | |
efc6f628 | 267 | |
30fab293 TT |
268 | /* Doubly indirect block */ |
269 | block -= addr_per_block; | |
270 | if (block < addr_per_block * addr_per_block) { | |
271 | b = inode_bmap(inode, EXT2_DIND_BLOCK); | |
272 | if (!b) { | |
1d667534 TT |
273 | if (!(bmap_flags & BMAP_ALLOC)) { |
274 | if (bmap_flags & BMAP_SET) | |
275 | retval = EXT2_ET_SET_BMAP_NO_IND; | |
276 | goto done; | |
277 | } | |
30fab293 TT |
278 | |
279 | b = inode_bmap(inode, EXT2_IND_BLOCK); | |
280 | retval = ext2fs_alloc_block(fs, b, block_buf, &b); | |
281 | if (retval) | |
282 | goto done; | |
283 | inode_bmap(inode, EXT2_DIND_BLOCK) = b; | |
284 | blocks_alloc++; | |
285 | } | |
efc6f628 | 286 | retval = block_dind_bmap(fs, bmap_flags, b, block_buf, |
cc9bf5d2 TT |
287 | &blocks_alloc, block, &blk32); |
288 | if (retval == 0) | |
289 | *phys_blk = blk32; | |
30fab293 TT |
290 | goto done; |
291 | } | |
292 | ||
293 | /* Triply indirect block */ | |
294 | block -= addr_per_block * addr_per_block; | |
295 | b = inode_bmap(inode, EXT2_TIND_BLOCK); | |
296 | if (!b) { | |
1d667534 TT |
297 | if (!(bmap_flags & BMAP_ALLOC)) { |
298 | if (bmap_flags & BMAP_SET) | |
299 | retval = EXT2_ET_SET_BMAP_NO_IND; | |
30fab293 | 300 | goto done; |
1d667534 | 301 | } |
30fab293 TT |
302 | |
303 | b = inode_bmap(inode, EXT2_DIND_BLOCK); | |
304 | retval = ext2fs_alloc_block(fs, b, block_buf, &b); | |
305 | if (retval) | |
306 | goto done; | |
307 | inode_bmap(inode, EXT2_TIND_BLOCK) = b; | |
308 | blocks_alloc++; | |
309 | } | |
efc6f628 | 310 | retval = block_tind_bmap(fs, bmap_flags, b, block_buf, |
cc9bf5d2 TT |
311 | &blocks_alloc, block, &blk32); |
312 | if (retval == 0) | |
313 | *phys_blk = blk32; | |
30fab293 TT |
314 | done: |
315 | if (buf) | |
c4e3d3f3 | 316 | ext2fs_free_mem(&buf); |
cc9bf5d2 TT |
317 | if (handle) |
318 | ext2fs_extent_free(handle); | |
1d667534 | 319 | if ((retval == 0) && (blocks_alloc || inode_dirty)) { |
1ca1059f | 320 | ext2fs_iblk_add_blocks(fs, inode, blocks_alloc); |
30fab293 TT |
321 | retval = ext2fs_write_inode(fs, ino, inode); |
322 | } | |
323 | return retval; | |
324 | } | |
325 | ||
cc9bf5d2 TT |
326 | errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, |
327 | char *block_buf, int bmap_flags, blk_t block, | |
328 | blk_t *phys_blk) | |
329 | { | |
330 | errcode_t ret; | |
9c9e1d5f | 331 | blk64_t ret_blk = *phys_blk; |
cc9bf5d2 TT |
332 | |
333 | ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block, | |
334 | 0, &ret_blk); | |
335 | if (ret) | |
336 | return ret; | |
337 | if (ret_blk >= ((long long) 1 << 32)) | |
338 | return EOVERFLOW; | |
339 | *phys_blk = ret_blk; | |
340 | return 0; | |
341 | } |