Initial public busybox upstream commit
[busybox4maemo] / e2fsprogs / old_e2fsprogs / ext2fs / block.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * block.c --- iterate over all blocks in an inode
4  *
5  * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
6  *
7  * %Begin-Header%
8  * This file may be redistributed under the terms of the GNU Public
9  * License.
10  * %End-Header%
11  */
12
13 #include <stdio.h>
14 #include <string.h>
15 #if HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18
19 #include "ext2_fs.h"
20 #include "ext2fs.h"
21
22 struct block_context {
23         ext2_filsys     fs;
24         int (*func)(ext2_filsys fs,
25                     blk_t       *blocknr,
26                     e2_blkcnt_t bcount,
27                     blk_t       ref_blk,
28                     int         ref_offset,
29                     void        *priv_data);
30         e2_blkcnt_t     bcount;
31         int             bsize;
32         int             flags;
33         errcode_t       errcode;
34         char    *ind_buf;
35         char    *dind_buf;
36         char    *tind_buf;
37         void    *priv_data;
38 };
39
40 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
41                              int ref_offset, struct block_context *ctx)
42 {
43         int     ret = 0, changed = 0;
44         int     i, flags, limit, offset;
45         blk_t   *block_nr;
46
47         limit = ctx->fs->blocksize >> 2;
48         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
49             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
50                 ret = (*ctx->func)(ctx->fs, ind_block,
51                                    BLOCK_COUNT_IND, ref_block,
52                                    ref_offset, ctx->priv_data);
53         if (!*ind_block || (ret & BLOCK_ABORT)) {
54                 ctx->bcount += limit;
55                 return ret;
56         }
57         if (*ind_block >= ctx->fs->super->s_blocks_count ||
58             *ind_block < ctx->fs->super->s_first_data_block) {
59                 ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
60                 ret |= BLOCK_ERROR;
61                 return ret;
62         }
63         ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
64                                              ctx->ind_buf);
65         if (ctx->errcode) {
66                 ret |= BLOCK_ERROR;
67                 return ret;
68         }
69
70         block_nr = (blk_t *) ctx->ind_buf;
71         offset = 0;
72         if (ctx->flags & BLOCK_FLAG_APPEND) {
73                 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
74                         flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
75                                              *ind_block, offset,
76                                              ctx->priv_data);
77                         changed |= flags;
78                         if (flags & BLOCK_ABORT) {
79                                 ret |= BLOCK_ABORT;
80                                 break;
81                         }
82                         offset += sizeof(blk_t);
83                 }
84         } else {
85                 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
86                         if (*block_nr == 0)
87                                 continue;
88                         flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
89                                              *ind_block, offset,
90                                              ctx->priv_data);
91                         changed |= flags;
92                         if (flags & BLOCK_ABORT) {
93                                 ret |= BLOCK_ABORT;
94                                 break;
95                         }
96                         offset += sizeof(blk_t);
97                 }
98         }
99         if (changed & BLOCK_CHANGED) {
100                 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
101                                                       ctx->ind_buf);
102                 if (ctx->errcode)
103                         ret |= BLOCK_ERROR | BLOCK_ABORT;
104         }
105         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
106             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
107             !(ret & BLOCK_ABORT))
108                 ret |= (*ctx->func)(ctx->fs, ind_block,
109                                     BLOCK_COUNT_IND, ref_block,
110                                     ref_offset, ctx->priv_data);
111         return ret;
112 }
113
114 static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
115                               int ref_offset, struct block_context *ctx)
116 {
117         int     ret = 0, changed = 0;
118         int     i, flags, limit, offset;
119         blk_t   *block_nr;
120
121         limit = ctx->fs->blocksize >> 2;
122         if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
123                             BLOCK_FLAG_DATA_ONLY)))
124                 ret = (*ctx->func)(ctx->fs, dind_block,
125                                    BLOCK_COUNT_DIND, ref_block,
126                                    ref_offset, ctx->priv_data);
127         if (!*dind_block || (ret & BLOCK_ABORT)) {
128                 ctx->bcount += limit*limit;
129                 return ret;
130         }
131         if (*dind_block >= ctx->fs->super->s_blocks_count ||
132             *dind_block < ctx->fs->super->s_first_data_block) {
133                 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
134                 ret |= BLOCK_ERROR;
135                 return ret;
136         }
137         ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
138                                              ctx->dind_buf);
139         if (ctx->errcode) {
140                 ret |= BLOCK_ERROR;
141                 return ret;
142         }
143
144         block_nr = (blk_t *) ctx->dind_buf;
145         offset = 0;
146         if (ctx->flags & BLOCK_FLAG_APPEND) {
147                 for (i = 0; i < limit; i++, block_nr++) {
148                         flags = block_iterate_ind(block_nr,
149                                                   *dind_block, offset,
150                                                   ctx);
151                         changed |= flags;
152                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
153                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
154                                 break;
155                         }
156                         offset += sizeof(blk_t);
157                 }
158         } else {
159                 for (i = 0; i < limit; i++, block_nr++) {
160                         if (*block_nr == 0) {
161                                 ctx->bcount += limit;
162                                 continue;
163                         }
164                         flags = block_iterate_ind(block_nr,
165                                                   *dind_block, offset,
166                                                   ctx);
167                         changed |= flags;
168                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
169                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
170                                 break;
171                         }
172                         offset += sizeof(blk_t);
173                 }
174         }
175         if (changed & BLOCK_CHANGED) {
176                 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
177                                                       ctx->dind_buf);
178                 if (ctx->errcode)
179                         ret |= BLOCK_ERROR | BLOCK_ABORT;
180         }
181         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
182             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
183             !(ret & BLOCK_ABORT))
184                 ret |= (*ctx->func)(ctx->fs, dind_block,
185                                     BLOCK_COUNT_DIND, ref_block,
186                                     ref_offset, ctx->priv_data);
187         return ret;
188 }
189
190 static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
191                               int ref_offset, struct block_context *ctx)
192 {
193         int     ret = 0, changed = 0;
194         int     i, flags, limit, offset;
195         blk_t   *block_nr;
196
197         limit = ctx->fs->blocksize >> 2;
198         if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
199                             BLOCK_FLAG_DATA_ONLY)))
200                 ret = (*ctx->func)(ctx->fs, tind_block,
201                                    BLOCK_COUNT_TIND, ref_block,
202                                    ref_offset, ctx->priv_data);
203         if (!*tind_block || (ret & BLOCK_ABORT)) {
204                 ctx->bcount += limit*limit*limit;
205                 return ret;
206         }
207         if (*tind_block >= ctx->fs->super->s_blocks_count ||
208             *tind_block < ctx->fs->super->s_first_data_block) {
209                 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
210                 ret |= BLOCK_ERROR;
211                 return ret;
212         }
213         ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
214                                              ctx->tind_buf);
215         if (ctx->errcode) {
216                 ret |= BLOCK_ERROR;
217                 return ret;
218         }
219
220         block_nr = (blk_t *) ctx->tind_buf;
221         offset = 0;
222         if (ctx->flags & BLOCK_FLAG_APPEND) {
223                 for (i = 0; i < limit; i++, block_nr++) {
224                         flags = block_iterate_dind(block_nr,
225                                                    *tind_block,
226                                                    offset, ctx);
227                         changed |= flags;
228                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
229                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
230                                 break;
231                         }
232                         offset += sizeof(blk_t);
233                 }
234         } else {
235                 for (i = 0; i < limit; i++, block_nr++) {
236                         if (*block_nr == 0) {
237                                 ctx->bcount += limit*limit;
238                                 continue;
239                         }
240                         flags = block_iterate_dind(block_nr,
241                                                    *tind_block,
242                                                    offset, ctx);
243                         changed |= flags;
244                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
245                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
246                                 break;
247                         }
248                         offset += sizeof(blk_t);
249                 }
250         }
251         if (changed & BLOCK_CHANGED) {
252                 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
253                                                       ctx->tind_buf);
254                 if (ctx->errcode)
255                         ret |= BLOCK_ERROR | BLOCK_ABORT;
256         }
257         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
258             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
259             !(ret & BLOCK_ABORT))
260                 ret |= (*ctx->func)(ctx->fs, tind_block,
261                                     BLOCK_COUNT_TIND, ref_block,
262                                     ref_offset, ctx->priv_data);
263
264         return ret;
265 }
266
267 errcode_t ext2fs_block_iterate2(ext2_filsys fs,
268                                 ext2_ino_t ino,
269                                 int     flags,
270                                 char *block_buf,
271                                 int (*func)(ext2_filsys fs,
272                                             blk_t       *blocknr,
273                                             e2_blkcnt_t blockcnt,
274                                             blk_t       ref_blk,
275                                             int         ref_offset,
276                                             void        *priv_data),
277                                 void *priv_data)
278 {
279         int     i;
280         int     got_inode = 0;
281         int     ret = 0;
282         blk_t   blocks[EXT2_N_BLOCKS];  /* directory data blocks */
283         struct ext2_inode inode;
284         errcode_t       retval;
285         struct block_context ctx;
286         int     limit;
287
288         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
289
290         /*
291          * Check to see if we need to limit large files
292          */
293         if (flags & BLOCK_FLAG_NO_LARGE) {
294                 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
295                 if (ctx.errcode)
296                         return ctx.errcode;
297                 got_inode = 1;
298                 if (!LINUX_S_ISDIR(inode.i_mode) &&
299                     (inode.i_size_high != 0))
300                         return EXT2_ET_FILE_TOO_BIG;
301         }
302
303         retval = ext2fs_get_blocks(fs, ino, blocks);
304         if (retval)
305                 return retval;
306
307         limit = fs->blocksize >> 2;
308
309         ctx.fs = fs;
310         ctx.func = func;
311         ctx.priv_data = priv_data;
312         ctx.flags = flags;
313         ctx.bcount = 0;
314         if (block_buf) {
315                 ctx.ind_buf = block_buf;
316         } else {
317                 retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
318                 if (retval)
319                         return retval;
320         }
321         ctx.dind_buf = ctx.ind_buf + fs->blocksize;
322         ctx.tind_buf = ctx.dind_buf + fs->blocksize;
323
324         /*
325          * Iterate over the HURD translator block (if present)
326          */
327         if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
328             !(flags & BLOCK_FLAG_DATA_ONLY)) {
329                 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
330                 if (ctx.errcode)
331                         goto abort_exit;
332                 got_inode = 1;
333                 if (inode.osd1.hurd1.h_i_translator) {
334                         ret |= (*ctx.func)(fs,
335                                            &inode.osd1.hurd1.h_i_translator,
336                                            BLOCK_COUNT_TRANSLATOR,
337                                            0, 0, priv_data);
338                         if (ret & BLOCK_ABORT)
339                                 goto abort_exit;
340                 }
341         }
342
343         /*
344          * Iterate over normal data blocks
345          */
346         for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) {
347                 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
348                         ret |= (*ctx.func)(fs, &blocks[i],
349                                             ctx.bcount, 0, i, priv_data);
350                         if (ret & BLOCK_ABORT)
351                                 goto abort_exit;
352                 }
353         }
354         if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
355                 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
356                                          0, EXT2_IND_BLOCK, &ctx);
357                 if (ret & BLOCK_ABORT)
358                         goto abort_exit;
359         } else
360                 ctx.bcount += limit;
361         if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
362                 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
363                                           0, EXT2_DIND_BLOCK, &ctx);
364                 if (ret & BLOCK_ABORT)
365                         goto abort_exit;
366         } else
367                 ctx.bcount += limit * limit;
368         if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
369                 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
370                                           0, EXT2_TIND_BLOCK, &ctx);
371                 if (ret & BLOCK_ABORT)
372                         goto abort_exit;
373         }
374
375 abort_exit:
376         if (ret & BLOCK_CHANGED) {
377                 if (!got_inode) {
378                         retval = ext2fs_read_inode(fs, ino, &inode);
379                         if (retval)
380                                 return retval;
381                 }
382                 for (i=0; i < EXT2_N_BLOCKS; i++)
383                         inode.i_block[i] = blocks[i];
384                 retval = ext2fs_write_inode(fs, ino, &inode);
385                 if (retval)
386                         return retval;
387         }
388
389         if (!block_buf)
390                 ext2fs_free_mem(&ctx.ind_buf);
391
392         return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
393 }
394
395 /*
396  * Emulate the old ext2fs_block_iterate function!
397  */
398
399 struct xlate {
400         int (*func)(ext2_filsys fs,
401                     blk_t       *blocknr,
402                     int         bcount,
403                     void        *priv_data);
404         void *real_private;
405 };
406
407 #ifdef __TURBOC__
408 # pragma argsused
409 #endif
410 static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
411                       blk_t ref_block EXT2FS_ATTR((unused)),
412                       int ref_offset EXT2FS_ATTR((unused)),
413                       void *priv_data)
414 {
415         struct xlate *xl = (struct xlate *) priv_data;
416
417         return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
418 }
419
420 errcode_t ext2fs_block_iterate(ext2_filsys fs,
421                                ext2_ino_t ino,
422                                int      flags,
423                                char *block_buf,
424                                int (*func)(ext2_filsys fs,
425                                            blk_t        *blocknr,
426                                            int  blockcnt,
427                                            void *priv_data),
428                                void *priv_data)
429 {
430         struct xlate xl;
431
432         xl.real_private = priv_data;
433         xl.func = func;
434
435         return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
436                                      block_buf, xlate_func, &xl);
437 }
438