Whamcloud - gitweb
Implement the ability for the test_io mechanism to abort after n reads
[tools/e2fsprogs.git] / lib / ext2fs / bmap.c
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 BMAP_ALLOC      1
33
34 #define inode_bmap(inode, nr) ((inode)->i_block[(nr)])
35
36 static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, 
37                                               blk_t ind, char *block_buf, 
38                                               int *blocks_alloc,
39                                               blk_t nr, blk_t *ret_blk)
40 {
41         errcode_t       retval;
42         blk_t           b;
43
44         if (!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         b = ((blk_t *) block_buf)[nr];
53
54 #ifdef EXT2FS_ENABLE_SWAPFS
55         if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
56             (fs->flags & EXT2_FLAG_SWAP_BYTES_READ))
57                 b = ext2fs_swab32(b);
58 #endif
59
60         if (!b && (flags & BMAP_ALLOC)) {
61                 b = nr ? ((blk_t *) block_buf)[nr-1] : 0;
62                 retval = ext2fs_alloc_block(fs, b,
63                                             block_buf + fs->blocksize, &b);
64                 if (retval)
65                         return retval;
66
67 #ifdef EXT2FS_ENABLE_SWAPFS
68                 if ((fs->flags & EXT2_FLAG_SWAP_BYTES) ||
69                     (fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE))
70                         ((blk_t *) block_buf)[nr] = ext2fs_swab32(b);
71                 else
72 #endif
73                         ((blk_t *) block_buf)[nr] = b;
74
75                 retval = io_channel_write_blk(fs->io, ind, 1, block_buf);
76                 if (retval)
77                         return retval;
78
79                 (*blocks_alloc)++;
80         }
81
82         *ret_blk = b;
83         return 0;
84 }
85
86 static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags,
87                                                blk_t dind, char *block_buf, 
88                                                int *blocks_alloc,
89                                                blk_t nr, blk_t *ret_blk)
90 {
91         blk_t           b;
92         errcode_t       retval;
93         blk_t           addr_per_block;
94         
95         addr_per_block = (blk_t) fs->blocksize >> 2;
96
97         retval = block_ind_bmap(fs, flags, dind, block_buf, blocks_alloc,
98                                 nr / addr_per_block, &b);
99         if (retval)
100                 return retval;
101         retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
102                                 nr % addr_per_block, ret_blk);
103         return retval;
104 }
105
106 static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags,
107                                                blk_t tind, char *block_buf, 
108                                                int *blocks_alloc,
109                                                blk_t nr, blk_t *ret_blk)
110 {
111         blk_t           b;
112         errcode_t       retval;
113         blk_t           addr_per_block;
114         
115         addr_per_block = (blk_t) fs->blocksize >> 2;
116
117         retval = block_dind_bmap(fs, flags, tind, block_buf, blocks_alloc,
118                                  nr / addr_per_block, &b);
119         if (retval)
120                 return retval;
121         retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc,
122                                 nr % addr_per_block, ret_blk);
123         return retval;
124 }
125
126 errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode,
127                       char *block_buf, int bmap_flags, blk_t block,
128                       blk_t *phys_blk)
129 {
130         struct ext2_inode inode_buf;
131         blk_t addr_per_block;
132         blk_t   b;
133         char    *buf = 0;
134         errcode_t       retval = 0;
135         int             blocks_alloc = 0;
136
137         *phys_blk = 0;
138
139         /* Read inode structure if necessary */
140         if (!inode) {
141                 retval = ext2fs_read_inode(fs, ino, &inode_buf);
142                 if (retval)
143                         return retval;
144                 inode = &inode_buf;
145         }
146         addr_per_block = (blk_t) fs->blocksize >> 2;
147
148         if (!block_buf) {
149                 retval = ext2fs_get_mem(fs->blocksize * 2, &buf);
150                 if (retval)
151                         return retval;
152                 block_buf = buf;
153         }
154
155         if (block < EXT2_NDIR_BLOCKS) {
156                 *phys_blk = inode_bmap(inode, block);
157                 b = block ? inode_bmap(inode, block-1) : 0;
158                 
159                 if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) {
160                         retval = ext2fs_alloc_block(fs, b, block_buf, &b);
161                         if (retval)
162                                 goto done;
163                         inode_bmap(inode, block) = b;
164                         blocks_alloc++;
165                         *phys_blk = b;
166                 }
167                 goto done;
168         }
169         
170         /* Indirect block */
171         block -= EXT2_NDIR_BLOCKS;
172         if (block < addr_per_block) {
173                 b = inode_bmap(inode, EXT2_IND_BLOCK);
174                 if (!b) {
175                         if (!(bmap_flags & BMAP_ALLOC))
176                             goto done;
177
178                         b = inode_bmap(inode, EXT2_IND_BLOCK-1);
179                         retval = ext2fs_alloc_block(fs, b, block_buf, &b);
180                         if (retval)
181                                 goto done;
182                         inode_bmap(inode, EXT2_IND_BLOCK) = b;
183                         blocks_alloc++;
184                 }
185                 retval = block_ind_bmap(fs, bmap_flags, b, block_buf, 
186                                         &blocks_alloc, block, phys_blk);
187                 goto done;
188         }
189         
190         /* Doubly indirect block  */
191         block -= addr_per_block;
192         if (block < addr_per_block * addr_per_block) {
193                 b = inode_bmap(inode, EXT2_DIND_BLOCK);
194                 if (!b) {
195                         if (!(bmap_flags & BMAP_ALLOC))
196                             goto done;
197
198                         b = inode_bmap(inode, EXT2_IND_BLOCK);
199                         retval = ext2fs_alloc_block(fs, b, block_buf, &b);
200                         if (retval)
201                                 goto done;
202                         inode_bmap(inode, EXT2_DIND_BLOCK) = b;
203                         blocks_alloc++;
204                 }
205                 retval = block_dind_bmap(fs, bmap_flags, b, block_buf, 
206                                          &blocks_alloc, block, phys_blk);
207                 goto done;
208         }
209
210         /* Triply indirect block */
211         block -= addr_per_block * addr_per_block;
212         b = inode_bmap(inode, EXT2_TIND_BLOCK);
213         if (!b) {
214                 if (!(bmap_flags & BMAP_ALLOC))
215                         goto done;
216
217                 b = inode_bmap(inode, EXT2_DIND_BLOCK);
218                 retval = ext2fs_alloc_block(fs, b, block_buf, &b);
219                 if (retval)
220                         goto done;
221                 inode_bmap(inode, EXT2_TIND_BLOCK) = b;
222                 blocks_alloc++;
223         }
224         retval = block_tind_bmap(fs, bmap_flags, b, block_buf, 
225                                  &blocks_alloc, block, phys_blk);
226 done:
227         if (buf)
228                 ext2fs_free_mem(&buf);
229         if ((retval == 0) && blocks_alloc) {
230                 inode->i_blocks += (blocks_alloc * fs->blocksize) / 512;
231                 retval = ext2fs_write_inode(fs, ino, inode);
232         }
233         return retval;
234 }
235
236
237