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