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