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