blob: 50745877de2427ae5302ad898c614c01703c74a2 [file] [log] [blame]
Theodore Ts'o30fab291997-10-25 22:37:42 +00001/*
Theodore Ts'o80e808f2000-02-02 16:19:59 +00002 * bmap.c --- logical to physical block mapping
Theodore Ts'o30fab291997-10-25 22:37:42 +00003 *
4 * Copyright (C) 1997 Theodore Ts'o.
5 *
6 * %Begin-Header%
Theodore Ts'o543547a2010-05-17 21:31:56 -04007 * This file may be redistributed under the terms of the GNU Library
8 * General Public License, version 2.
Theodore Ts'o30fab291997-10-25 22:37:42 +00009 * %End-Header%
10 */
11
Theodore Ts'od1154eb2011-09-18 17:34:37 -040012#include "config.h"
Theodore Ts'o30fab291997-10-25 22:37:42 +000013#include <stdio.h>
14#include <string.h>
15#if HAVE_UNISTD_H
16#include <unistd.h>
17#endif
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -050018#include <errno.h>
Theodore Ts'o30fab291997-10-25 22:37:42 +000019
Theodore Ts'ob5abe6f1998-01-19 14:47:53 +000020#include "ext2_fs.h"
Theodore Ts'o30fab291997-10-25 22:37:42 +000021#include "ext2fs.h"
22
Theodore Ts'o78d8f901997-10-26 01:53:39 +000023#if defined(__GNUC__) && !defined(NO_INLINE_FUNCS)
Theodore Ts'o30fab291997-10-25 22:37:42 +000024#define _BMAP_INLINE_ __inline__
25#else
26#define _BMAP_INLINE_
27#endif
28
Theodore Ts'o31dbecd2001-01-11 04:54:39 +000029extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino,
Theodore Ts'oefc6f622008-08-27 23:07:54 -040030 struct ext2_inode *inode,
Theodore Ts'o30fab291997-10-25 22:37:42 +000031 char *block_buf, int bmap_flags,
32 blk_t block, blk_t *phys_blk);
33
Theodore Ts'o30fab291997-10-25 22:37:42 +000034#define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
35
Theodore Ts'oefc6f622008-08-27 23:07:54 -040036static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags,
37 blk_t ind, char *block_buf,
Theodore Ts'o30fab291997-10-25 22:37:42 +000038 int *blocks_alloc,
39 blk_t nr, blk_t *ret_blk)
40{
41 errcode_t retval;
42 blk_t b;
43
44 if (!ind) {
Theodore Ts'o1d667532004-12-23 13:55:34 -050045 if (flags & BMAP_SET)
46 return EXT2_ET_SET_BMAP_NO_IND;
Theodore Ts'o30fab291997-10-25 22:37:42 +000047 *ret_blk = 0;
48 return 0;
49 }
50 retval = io_channel_read_blk(fs->io, ind, 1, block_buf);
51 if (retval)
52 return retval;
53
Theodore Ts'o1d667532004-12-23 13:55:34 -050054 if (flags & BMAP_SET) {
55 b = *ret_blk;
Theodore Ts'o126a2912007-08-11 01:56:48 -040056#ifdef WORDS_BIGENDIAN
57 b = ext2fs_swab32(b);
Theodore Ts'o1d667532004-12-23 13:55:34 -050058#endif
59 ((blk_t *) block_buf)[nr] = b;
60 return io_channel_write_blk(fs->io, ind, 1, block_buf);
61 }
62
Theodore Ts'o30fab291997-10-25 22:37:42 +000063 b = ((blk_t *) block_buf)[nr];
64
Theodore Ts'o126a2912007-08-11 01:56:48 -040065#ifdef WORDS_BIGENDIAN
66 b = ext2fs_swab32(b);
Theodore Ts'o5df55d72001-06-11 07:00:04 +000067#endif
Theodore Ts'o30fab291997-10-25 22:37:42 +000068
69 if (!b && (flags & BMAP_ALLOC)) {
70 b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
71 retval = ext2fs_alloc_block(fs, b,
72 block_buf + fs->blocksize, &b);
73 if (retval)
74 return retval;
75
Theodore Ts'o126a2912007-08-11 01:56:48 -040076#ifdef WORDS_BIGENDIAN
77 ((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
78#else
79 ((blk_t *) block_buf)[nr] = b;
Theodore Ts'o5df55d72001-06-11 07:00:04 +000080#endif
Theodore Ts'o30fab291997-10-25 22:37:42 +000081
82 retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
83 if (retval)
84 return retval;
85
86 (*blocks_alloc)++;
87 }
88
89 *ret_blk = b;
90 return 0;
91}
92
Theodore Ts'o546a1ff2002-03-07 23:52:56 -050093static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags,
Theodore Ts'oefc6f622008-08-27 23:07:54 -040094 blk_t dind, char *block_buf,
Theodore Ts'o30fab291997-10-25 22:37:42 +000095 int *blocks_alloc,
96 blk_t nr, blk_t *ret_blk)
97{
Andreas Dilger1d6fd6d2012-11-29 05:47:52 -070098 blk_t b = 0;
Theodore Ts'o30fab291997-10-25 22:37:42 +000099 errcode_t retval;
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000100 blk_t addr_per_block;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400101
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000102 addr_per_block = (blk_t) fs->blocksize >> 2;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000103
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400104 retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf,
Theodore Ts'o1d667532004-12-23 13:55:34 -0500105 blocks_alloc, nr / addr_per_block, &b);
Theodore Ts'o30fab291997-10-25 22:37:42 +0000106 if (retval)
107 return retval;
108 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
109 nr % addr_per_block, ret_blk);
110 return retval;
111}
112
Theodore Ts'o546a1ff2002-03-07 23:52:56 -0500113static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags,
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400114 blk_t tind, char *block_buf,
Theodore Ts'o30fab291997-10-25 22:37:42 +0000115 int *blocks_alloc,
116 blk_t nr, blk_t *ret_blk)
117{
Andreas Dilger1d6fd6d2012-11-29 05:47:52 -0700118 blk_t b = 0;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000119 errcode_t retval;
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000120 blk_t addr_per_block;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400121
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000122 addr_per_block = (blk_t) fs->blocksize >> 2;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000123
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400124 retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf,
Theodore Ts'o1d667532004-12-23 13:55:34 -0500125 blocks_alloc, nr / addr_per_block, &b);
Theodore Ts'o30fab291997-10-25 22:37:42 +0000126 if (retval)
127 return retval;
128 retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
129 nr % addr_per_block, ret_blk);
130 return retval;
131}
132
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400133static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino,
134 struct ext2_inode *inode,
135 ext2_extent_handle_t handle,
136 char *block_buf, int bmap_flags, blk64_t block,
137 int *ret_flags, int *blocks_alloc,
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400138 blk64_t *phys_blk);
139
140static errcode_t implied_cluster_alloc(ext2_filsys fs, ext2_ino_t ino,
141 struct ext2_inode *inode,
142 ext2_extent_handle_t handle,
Darrick J. Wong2a091422013-10-08 12:08:46 -0400143 blk64_t lblk, blk64_t *phys_blk)
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400144{
145 blk64_t base_block, pblock = 0;
146 int i;
147
148 if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super,
149 EXT4_FEATURE_RO_COMPAT_BIGALLOC))
150 return 0;
151
Darrick J. Wong2a091422013-10-08 12:08:46 -0400152 base_block = lblk & ~EXT2FS_CLUSTER_MASK(fs);
153 /*
154 * Except for the logical block (lblk) that was passed in, search all
155 * blocks in this logical cluster for a mapping to a physical cluster.
156 * If any such map exists, calculate the physical block that maps to
157 * the logical block and return that.
158 *
159 * The old code wouldn't even look if (block % cluster_ratio) == 0;
160 * this is incorrect if we're allocating blocks in reverse order.
161 */
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400162 for (i = 0; i < EXT2FS_CLUSTER_RATIO(fs); i++) {
Darrick J. Wong2a091422013-10-08 12:08:46 -0400163 if (base_block + i == lblk)
164 continue;
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400165 extent_bmap(fs, ino, inode, handle, 0, 0,
166 base_block + i, 0, 0, &pblock);
167 if (pblock)
168 break;
169 }
170 if (pblock == 0)
171 return 0;
Darrick J. Wong2a091422013-10-08 12:08:46 -0400172 *phys_blk = pblock - i + (lblk - base_block);
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400173 return 0;
174}
175
176static errcode_t extent_bmap(ext2_filsys fs, ext2_ino_t ino,
177 struct ext2_inode *inode,
178 ext2_extent_handle_t handle,
179 char *block_buf, int bmap_flags, blk64_t block,
180 int *ret_flags, int *blocks_alloc,
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400181 blk64_t *phys_blk)
182{
183 struct ext2fs_extent extent;
184 unsigned int offset;
185 errcode_t retval = 0;
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400186 blk64_t blk64 = 0;
187 int alloc = 0;
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400188
189 if (bmap_flags & BMAP_SET) {
190 retval = ext2fs_extent_set_bmap(handle, block,
191 *phys_blk, 0);
192 return retval;
193 }
194 retval = ext2fs_extent_goto(handle, block);
195 if (retval) {
196 /* If the extent is not found, return phys_blk = 0 */
197 if (retval == EXT2_ET_EXTENT_NOT_FOUND)
198 goto got_block;
199 return retval;
200 }
201 retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent);
202 if (retval)
203 return retval;
204 offset = block - extent.e_lblk;
205 if (block >= extent.e_lblk && (offset <= extent.e_len)) {
206 *phys_blk = extent.e_pblk + offset;
207 if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT)
208 *ret_flags |= BMAP_RET_UNINIT;
209 }
210got_block:
211 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400212 implied_cluster_alloc(fs, ino, inode, handle, block, &blk64);
213 if (blk64)
214 goto set_extent;
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400215 retval = extent_bmap(fs, ino, inode, handle, block_buf,
216 0, block-1, 0, blocks_alloc, &blk64);
217 if (retval)
218 blk64 = 0;
219 retval = ext2fs_alloc_block2(fs, blk64, block_buf,
220 &blk64);
221 if (retval)
222 return retval;
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400223 blk64 &= ~EXT2FS_CLUSTER_MASK(fs);
224 blk64 += EXT2FS_CLUSTER_MASK(fs) & block;
225 alloc++;
226 set_extent:
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400227 retval = ext2fs_extent_set_bmap(handle, block,
228 blk64, 0);
229 if (retval)
230 return retval;
231 /* Update inode after setting extent */
232 retval = ext2fs_read_inode(fs, ino, inode);
233 if (retval)
234 return retval;
Theodore Ts'o8e8a1902011-07-07 22:27:42 -0400235 *blocks_alloc += alloc;
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400236 *phys_blk = blk64;
237 }
238 return 0;
239}
240
241
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500242errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
243 char *block_buf, int bmap_flags, blk64_t block,
244 int *ret_flags, blk64_t *phys_blk)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000245{
246 struct ext2_inode inode_buf;
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500247 ext2_extent_handle_t handle = 0;
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000248 blk_t addr_per_block;
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500249 blk_t b, blk32;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000250 char *buf = 0;
251 errcode_t retval = 0;
Theodore Ts'o1d667532004-12-23 13:55:34 -0500252 int blocks_alloc = 0, inode_dirty = 0;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000253
Theodore Ts'o1d667532004-12-23 13:55:34 -0500254 if (!(bmap_flags & BMAP_SET))
255 *phys_blk = 0;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000256
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500257 if (ret_flags)
258 *ret_flags = 0;
259
Theodore Ts'o30fab291997-10-25 22:37:42 +0000260 /* Read inode structure if necessary */
261 if (!inode) {
262 retval = ext2fs_read_inode(fs, ino, &inode_buf);
Theodore Ts'ob38cd282002-05-11 22:13:20 -0400263 if (retval)
Theodore Ts'o30fab291997-10-25 22:37:42 +0000264 return retval;
265 inode = &inode_buf;
266 }
Theodore Ts'o2eb374c1998-09-03 01:22:57 +0000267 addr_per_block = (blk_t) fs->blocksize >> 2;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000268
269 if (!block_buf) {
Theodore Ts'oee010792007-11-09 19:01:06 -0500270 retval = ext2fs_get_array(2, fs->blocksize, &buf);
Theodore Ts'o7b4e4531997-10-26 03:41:24 +0000271 if (retval)
272 return retval;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000273 block_buf = buf;
274 }
275
Theodore Ts'o551e2e42011-07-07 14:02:29 -0400276 if (inode->i_flags & EXT4_EXTENTS_FL) {
277 retval = ext2fs_extent_open2(fs, ino, inode, &handle);
278 if (retval)
279 goto done;
280 retval = extent_bmap(fs, ino, inode, handle, block_buf,
281 bmap_flags, block, ret_flags,
282 &blocks_alloc, phys_blk);
283 goto done;
284 }
285
Theodore Ts'o30fab291997-10-25 22:37:42 +0000286 if (block < EXT2_NDIR_BLOCKS) {
Theodore Ts'o1d667532004-12-23 13:55:34 -0500287 if (bmap_flags & BMAP_SET) {
288 b = *phys_blk;
Theodore Ts'o1d667532004-12-23 13:55:34 -0500289 inode_bmap(inode, block) = b;
290 inode_dirty++;
291 goto done;
292 }
293
Theodore Ts'o30fab291997-10-25 22:37:42 +0000294 *phys_blk = inode_bmap(inode, block);
295 b = block ? inode_bmap(inode, block-1) : 0;
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400296
Theodore Ts'o30fab291997-10-25 22:37:42 +0000297 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
298 retval = ext2fs_alloc_block(fs, b, block_buf, &b);
299 if (retval)
300 goto done;
301 inode_bmap(inode, block) = b;
302 blocks_alloc++;
303 *phys_blk = b;
304 }
305 goto done;
306 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400307
Theodore Ts'o30fab291997-10-25 22:37:42 +0000308 /* Indirect block */
309 block -= EXT2_NDIR_BLOCKS;
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500310 blk32 = *phys_blk;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000311 if (block < addr_per_block) {
312 b = inode_bmap(inode, EXT2_IND_BLOCK);
313 if (!b) {
Theodore Ts'o1d667532004-12-23 13:55:34 -0500314 if (!(bmap_flags & BMAP_ALLOC)) {
315 if (bmap_flags & BMAP_SET)
316 retval = EXT2_ET_SET_BMAP_NO_IND;
317 goto done;
318 }
Theodore Ts'o30fab291997-10-25 22:37:42 +0000319
320 b = inode_bmap(inode, EXT2_IND_BLOCK-1);
321 retval = ext2fs_alloc_block(fs, b, block_buf, &b);
322 if (retval)
323 goto done;
324 inode_bmap(inode, EXT2_IND_BLOCK) = b;
325 blocks_alloc++;
326 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400327 retval = block_ind_bmap(fs, bmap_flags, b, block_buf,
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500328 &blocks_alloc, block, &blk32);
329 if (retval == 0)
330 *phys_blk = blk32;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000331 goto done;
332 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400333
Theodore Ts'o30fab291997-10-25 22:37:42 +0000334 /* Doubly indirect block */
335 block -= addr_per_block;
336 if (block < addr_per_block * addr_per_block) {
337 b = inode_bmap(inode, EXT2_DIND_BLOCK);
338 if (!b) {
Theodore Ts'o1d667532004-12-23 13:55:34 -0500339 if (!(bmap_flags & BMAP_ALLOC)) {
340 if (bmap_flags & BMAP_SET)
341 retval = EXT2_ET_SET_BMAP_NO_IND;
342 goto done;
343 }
Theodore Ts'o30fab291997-10-25 22:37:42 +0000344
345 b = inode_bmap(inode, EXT2_IND_BLOCK);
346 retval = ext2fs_alloc_block(fs, b, block_buf, &b);
347 if (retval)
348 goto done;
349 inode_bmap(inode, EXT2_DIND_BLOCK) = b;
350 blocks_alloc++;
351 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400352 retval = block_dind_bmap(fs, bmap_flags, b, block_buf,
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500353 &blocks_alloc, block, &blk32);
354 if (retval == 0)
355 *phys_blk = blk32;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000356 goto done;
357 }
358
359 /* Triply indirect block */
360 block -= addr_per_block * addr_per_block;
361 b = inode_bmap(inode, EXT2_TIND_BLOCK);
362 if (!b) {
Theodore Ts'o1d667532004-12-23 13:55:34 -0500363 if (!(bmap_flags & BMAP_ALLOC)) {
364 if (bmap_flags & BMAP_SET)
365 retval = EXT2_ET_SET_BMAP_NO_IND;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000366 goto done;
Theodore Ts'o1d667532004-12-23 13:55:34 -0500367 }
Theodore Ts'o30fab291997-10-25 22:37:42 +0000368
369 b = inode_bmap(inode, EXT2_DIND_BLOCK);
370 retval = ext2fs_alloc_block(fs, b, block_buf, &b);
371 if (retval)
372 goto done;
373 inode_bmap(inode, EXT2_TIND_BLOCK) = b;
374 blocks_alloc++;
375 }
Theodore Ts'oefc6f622008-08-27 23:07:54 -0400376 retval = block_tind_bmap(fs, bmap_flags, b, block_buf,
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500377 &blocks_alloc, block, &blk32);
378 if (retval == 0)
379 *phys_blk = blk32;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000380done:
381 if (buf)
Theodore Ts'oc4e3d3f2003-08-01 09:41:07 -0400382 ext2fs_free_mem(&buf);
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500383 if (handle)
384 ext2fs_extent_free(handle);
Theodore Ts'o1d667532004-12-23 13:55:34 -0500385 if ((retval == 0) && (blocks_alloc || inode_dirty)) {
Theodore Ts'o1ca10592008-04-09 11:39:11 -0400386 ext2fs_iblk_add_blocks(fs, inode, blocks_alloc);
Theodore Ts'o30fab291997-10-25 22:37:42 +0000387 retval = ext2fs_write_inode(fs, ino, inode);
388 }
389 return retval;
390}
391
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500392errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
393 char *block_buf, int bmap_flags, blk_t block,
394 blk_t *phys_blk)
395{
396 errcode_t ret;
Theodore Ts'o9c9e1d52009-11-29 00:08:54 -0500397 blk64_t ret_blk = *phys_blk;
Theodore Ts'o30fab291997-10-25 22:37:42 +0000398
Theodore Ts'occ9bf5d2008-02-18 14:59:45 -0500399 ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block,
400 0, &ret_blk);
401 if (ret)
402 return ret;
403 if (ret_blk >= ((long long) 1 << 32))
404 return EOVERFLOW;
405 *phys_blk = ret_blk;
406 return 0;
407}