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