OSDN Git Service

51385345f641ac6522be39ae57268988aef8205b
[android-x86/external-e2fsprogs.git] / lib / ext2fs / block.c
1 /*
2  * block.c --- iterate over all blocks in an inode
3  * 
4  * Copyright (C) 1993 Theodore Ts'o.  This file may be redistributed
5  * under the terms of the GNU Public License.
6  */
7
8 #include <stdio.h>
9 #include <string.h>
10 #include <unistd.h>
11 #include <stdlib.h>
12 #if HAVE_ERRNO_H
13 #include <errno.h>
14 #endif
15
16 #include <linux/ext2_fs.h>
17
18 #include "ext2fs.h"
19
20 struct block_context {
21         ext2_filsys     fs;
22         int (*func)(ext2_filsys fs,
23                     blk_t       *blocknr,
24                     int         bcount,
25                     void        *private);
26         int             bcount;
27         int             bsize;
28         int             flags;
29         errcode_t       errcode;
30         char    *ind_buf;
31         char    *dind_buf;
32         char    *tind_buf;
33         void    *private;
34 };
35
36 static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx)
37 {
38         int     ret = 0, changed = 0;
39         int     i, flags, limit;
40         blk_t   *block_nr;
41
42         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
43             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
44                 ret = (*ctx->func)(ctx->fs, ind_block,
45                                    BLOCK_COUNT_IND, ctx->private);
46         if (!*ind_block || (ret & BLOCK_ABORT))
47                 return ret;
48         if (*ind_block >= ctx->fs->super->s_blocks_count ||
49             *ind_block < ctx->fs->super->s_first_data_block) {
50                 ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
51                 ret |= BLOCK_ERROR;
52                 return ret;
53         }
54         ctx->errcode = io_channel_read_blk(ctx->fs->io, *ind_block,
55                                            1, ctx->ind_buf);
56         if (ctx->errcode) {
57                 ret |= BLOCK_ERROR;
58                 return ret;
59         }
60         limit = ctx->fs->blocksize >> 2;
61         if ((ctx->fs->flags & EXT2_SWAP_BYTES) ||
62             (ctx->fs->flags & EXT2_SWAP_BYTES_READ)) {
63                 block_nr = (blk_t *) ctx->ind_buf;
64                 for (i = 0; i < limit; i++, block_nr++)
65                         *block_nr = ext2fs_swab32(*block_nr);
66         }
67         block_nr = (blk_t *) ctx->ind_buf;
68         if (ctx->flags & BLOCK_FLAG_APPEND) {
69                 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
70                         flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
71                                              ctx->private);
72                         changed |= flags;
73                         if (flags & BLOCK_ABORT) {
74                                 ret |= BLOCK_ABORT;
75                                 break;
76                         }
77                 }
78         } else {
79                 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
80                         if (*block_nr == 0)
81                                 continue;
82                         flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
83                                              ctx->private);
84                         changed |= flags;
85                         if (flags & BLOCK_ABORT) {
86                                 ret |= BLOCK_ABORT;
87                                 break;
88                         }
89                 }
90         }
91         if (changed & BLOCK_CHANGED) {
92                 if ((ctx->fs->flags & EXT2_SWAP_BYTES) ||
93                     (ctx->fs->flags & EXT2_SWAP_BYTES_WRITE)) {
94                         block_nr = (blk_t *) ctx->ind_buf;
95                         for (i = 0; i < limit; i++, block_nr++)
96                                 *block_nr = ext2fs_swab32(*block_nr);
97                 }
98                 ctx->errcode = io_channel_write_blk(ctx->fs->io, *ind_block,
99                                                     1, ctx->ind_buf);
100                 if (ctx->errcode)
101                         ret |= BLOCK_ERROR | BLOCK_ABORT;
102         }
103         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
104             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
105             !(ret & BLOCK_ABORT))
106                 ret |= (*ctx->func)(ctx->fs, ind_block,
107                                     BLOCK_COUNT_IND, ctx->private);
108         return ret;
109 }
110         
111 static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx)
112 {
113         int     ret = 0, changed = 0;
114         int     i, flags, limit;
115         blk_t   *block_nr;
116
117         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
118             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
119                 ret = (*ctx->func)(ctx->fs, dind_block,
120                                    BLOCK_COUNT_DIND, ctx->private);
121         if (!*dind_block || (ret & BLOCK_ABORT))
122                 return ret;
123         if (*dind_block >= ctx->fs->super->s_blocks_count ||
124             *dind_block < ctx->fs->super->s_first_data_block) {
125                 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
126                 ret |= BLOCK_ERROR;
127                 return ret;
128         }
129         ctx->errcode = io_channel_read_blk(ctx->fs->io, *dind_block,
130                                            1, ctx->dind_buf);
131         if (ctx->errcode) {
132                 ret |= BLOCK_ERROR;
133                 return ret;
134         }
135         limit = ctx->fs->blocksize >> 2;
136         if ((ctx->fs->flags & EXT2_SWAP_BYTES) ||
137             (ctx->fs->flags & EXT2_SWAP_BYTES_READ)) {
138                 block_nr = (blk_t *) ctx->dind_buf;
139                 for (i = 0; i < limit; i++, block_nr++)
140                         *block_nr = ext2fs_swab32(*block_nr);
141         }
142         block_nr = (blk_t *) ctx->dind_buf;
143         if (ctx->flags & BLOCK_FLAG_APPEND) {
144                 for (i = 0; i < limit; i++, block_nr++) {
145                         flags = block_iterate_ind(block_nr, ctx);
146                         changed |= flags;
147                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
148                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
149                                 break;
150                         }
151                 }
152         } else {
153                 for (i = 0; i < limit; i++, block_nr++) {
154                         if (*block_nr == 0)
155                                 continue;
156                         flags = block_iterate_ind(block_nr, ctx);
157                         changed |= flags;
158                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
159                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
160                                 break;
161                         }
162                 }
163         }
164         if (changed & BLOCK_CHANGED) {
165                 if ((ctx->fs->flags & EXT2_SWAP_BYTES) ||
166                     (ctx->fs->flags & EXT2_SWAP_BYTES_WRITE)) {
167                         block_nr = (blk_t *) ctx->dind_buf;
168                         for (i = 0; i < limit; i++, block_nr++)
169                                 *block_nr = ext2fs_swab32(*block_nr);
170                 }
171                 ctx->errcode = io_channel_write_blk(ctx->fs->io, *dind_block,
172                                                     1, ctx->dind_buf);
173                 if (ctx->errcode)
174                         ret |= BLOCK_ERROR | BLOCK_ABORT;
175         }
176         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
177             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
178             !(ret & BLOCK_ABORT))
179                 ret |= (*ctx->func)(ctx->fs, dind_block,
180                                     BLOCK_COUNT_DIND, ctx->private);
181         return ret;
182 }
183         
184 static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx)
185 {
186         int     ret = 0, changed = 0;
187         int     i, flags, limit;
188         blk_t   *block_nr;
189
190         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
191             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
192                 ret = (*ctx->func)(ctx->fs, tind_block,
193                                    BLOCK_COUNT_TIND, ctx->private);
194         if (!*tind_block || (ret & BLOCK_ABORT))
195                 return ret;
196         if (*tind_block >= ctx->fs->super->s_blocks_count ||
197             *tind_block < ctx->fs->super->s_first_data_block) {
198                 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
199                 ret |= BLOCK_ERROR;
200                 return ret;
201         }
202         ctx->errcode = io_channel_read_blk(ctx->fs->io, *tind_block,
203                                            1, ctx->tind_buf);
204         if (ctx->errcode) {
205                 ret |= BLOCK_ERROR;
206                 return ret;
207         }
208         limit = ctx->fs->blocksize >> 2;
209         if ((ctx->fs->flags & EXT2_SWAP_BYTES) ||
210             (ctx->fs->flags & EXT2_SWAP_BYTES_READ)) {
211                 block_nr = (blk_t *) ctx->tind_buf;
212                 for (i = 0; i < limit; i++, block_nr++)
213                         *block_nr = ext2fs_swab32(*block_nr);
214         }
215         block_nr = (blk_t *) ctx->tind_buf;
216         if (ctx->flags & BLOCK_FLAG_APPEND) {
217                 for (i = 0; i < limit; i++, block_nr++) {
218                         flags = block_iterate_dind(block_nr, ctx);
219                         changed |= flags;
220                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
221                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
222                                 break;
223                         }
224                 }
225         } else {
226                 for (i = 0; i < limit; i++, block_nr++) {
227                         if (*block_nr == 0)
228                                 continue;
229                         flags = block_iterate_dind(block_nr, ctx);
230                         changed |= flags;
231                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
232                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
233                                 break;
234                         }
235                 }
236         }
237         if (changed & BLOCK_CHANGED) {
238                 if ((ctx->fs->flags & EXT2_SWAP_BYTES) ||
239                     (ctx->fs->flags & EXT2_SWAP_BYTES_WRITE)) {
240                         block_nr = (blk_t *) ctx->tind_buf;
241                         for (i = 0; i < limit; i++, block_nr++)
242                                 *block_nr = ext2fs_swab32(*block_nr);
243                 }
244                 ctx->errcode = io_channel_write_blk(ctx->fs->io, *tind_block,
245                                                     1, ctx->tind_buf);
246                 if (ctx->errcode)
247                         ret |= BLOCK_ERROR | BLOCK_ABORT;
248         }
249         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
250             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
251             !(ret & BLOCK_ABORT))
252                 ret |= (*ctx->func)(ctx->fs, tind_block,
253                                     BLOCK_COUNT_TIND, ctx->private);
254         
255         return ret;
256 }
257         
258 errcode_t ext2fs_block_iterate(ext2_filsys fs,
259                                ino_t    ino,
260                                int      flags,
261                                char *block_buf,
262                                int (*func)(ext2_filsys fs,
263                                            blk_t        *blocknr,
264                                            int  blockcnt,
265                                            void *private),
266                                void *private)
267 {
268         int     i;
269         int     got_inode = 0;
270         int     ret = 0;
271         struct block_context ctx;
272         blk_t   blocks[EXT2_N_BLOCKS];  /* directory data blocks */
273         struct ext2_inode inode;
274         errcode_t       retval;
275         
276         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
277
278         ret = ext2fs_get_blocks(fs, ino, blocks);
279         if (ret)
280                 return ret;
281
282         ctx.fs = fs;
283         ctx.func = func;
284         ctx.private = private;
285         ctx.bcount = 0;
286         ctx.flags = flags;
287         if (block_buf) {
288                 ctx.ind_buf = block_buf;
289         } else {
290                 ctx.ind_buf = malloc(fs->blocksize * 3);
291                 if (!ctx.ind_buf)
292                         return ENOMEM;
293         }
294         ctx.dind_buf = ctx.ind_buf + fs->blocksize;
295         ctx.tind_buf = ctx.dind_buf + fs->blocksize;
296
297         /*
298          * Iterate over the HURD translator block (if present)
299          */
300         if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
301             !(flags & BLOCK_FLAG_DATA_ONLY) &&
302             inode.osd1.hurd1.h_i_translator) {
303                 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
304                 if (ctx.errcode)
305                         goto abort;
306                 got_inode = 1;
307                 ret |= (*func)(fs, &inode.osd1.hurd1.h_i_translator,
308                                BLOCK_COUNT_TRANSLATOR, private);
309                 if (ret & BLOCK_ABORT)
310                         goto abort;
311         }
312         
313         /*
314          * Iterate over normal data blocks
315          */
316         for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
317                 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
318                         ret |= (*func)(fs, &blocks[i], ctx.bcount, private);
319                         if (ret & BLOCK_ABORT)
320                                 goto abort;
321                 }
322         }
323         if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
324                 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, &ctx);
325                 if (ret & BLOCK_ABORT)
326                         goto abort;
327         }
328         if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
329                 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, &ctx);
330                 if (ret & BLOCK_ABORT)
331                         goto abort;
332         }
333         if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
334                 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, &ctx);
335                 if (ret & BLOCK_ABORT)
336                         goto abort;
337         }
338
339 abort:
340         if (ret & BLOCK_CHANGED) {
341                 if (!got_inode) {
342                         retval = ext2fs_read_inode(fs, ino, &inode);
343                         if (retval)
344                                 return retval;
345                 }
346                 for (i=0; i < EXT2_N_BLOCKS; i++)
347                         inode.i_block[i] = blocks[i];
348                 retval = ext2fs_write_inode(fs, ino, &inode);
349                 if (retval)
350                         return retval;
351         }
352
353         if (!block_buf)
354                 free(ctx.ind_buf);
355
356         return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
357 }