Whamcloud - gitweb
03357b10e64203ea5f2f4007e1ba5f235a12f1be
[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                     e2_blkcnt_t bcount,
31                     blk_t       ref_blk,
32                     int         ref_offset,
33                     void        *priv_data);
34         e2_blkcnt_t     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                               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                                       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                             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                               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                                       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                             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                               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                                       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                                             e2_blkcnt_t 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         int     limit;
324
325         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
326
327         /*
328          * Check to see if we need to limit large files
329          */
330         if (flags & BLOCK_FLAG_NO_LARGE) {
331                 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
332                 if (ctx.errcode)
333                         return ctx.errcode;
334                 got_inode = 1;
335                 if (!LINUX_S_ISDIR(inode.i_mode) &&
336                     (inode.i_size_high != 0))
337                         return EXT2_ET_FILE_TOO_BIG;
338         }
339
340         retval = ext2fs_get_blocks(fs, ino, blocks);
341         if (retval)
342                 return retval;
343
344         limit = fs->blocksize >> 2;
345
346         ctx.fs = fs;
347         ctx.func = func;
348         ctx.priv_data = priv_data;
349         ctx.flags = flags;
350         ctx.bcount = 0;
351         if (block_buf) {
352                 ctx.ind_buf = block_buf;
353         } else {
354                 retval = ext2fs_get_mem(fs->blocksize * 3,
355                                         (void **) &ctx.ind_buf);
356                 if (retval)
357                         return retval;
358         }
359         ctx.dind_buf = ctx.ind_buf + fs->blocksize;
360         ctx.tind_buf = ctx.dind_buf + fs->blocksize;
361
362         /*
363          * Iterate over the HURD translator block (if present)
364          */
365         if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
366             !(flags & BLOCK_FLAG_DATA_ONLY)) {
367                 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
368                 if (ctx.errcode)
369                         goto abort;
370                 got_inode = 1;
371                 if (inode.osd1.hurd1.h_i_translator) {
372                         ret |= (*ctx.func)(fs,
373                                            &inode.osd1.hurd1.h_i_translator,
374                                            BLOCK_COUNT_TRANSLATOR,
375                                            0, 0, priv_data);
376                         if (ret & BLOCK_ABORT)
377                                 goto abort;
378                 }
379         }
380         
381         /*
382          * Iterate over normal data blocks
383          */
384         for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
385                 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
386                         ret |= (*ctx.func)(fs, &blocks[i],
387                                             ctx.bcount, 0, i, priv_data);
388                         if (ret & BLOCK_ABORT)
389                                 goto abort;
390                 }
391         }
392         if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
393                 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
394                                          0, EXT2_IND_BLOCK, &ctx);
395                 if (ret & BLOCK_ABORT)
396                         goto abort;
397         } else
398                 ctx.bcount += limit;
399         if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
400                 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
401                                           0, EXT2_DIND_BLOCK, &ctx);
402                 if (ret & BLOCK_ABORT)
403                         goto abort;
404         } else
405                 ctx.bcount += limit * limit;
406         if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
407                 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
408                                           0, EXT2_TIND_BLOCK, &ctx);
409                 if (ret & BLOCK_ABORT)
410                         goto abort;
411         }
412
413 abort:
414         if (ret & BLOCK_CHANGED) {
415                 if (!got_inode) {
416                         retval = ext2fs_read_inode(fs, ino, &inode);
417                         if (retval)
418                                 return retval;
419                 }
420                 for (i=0; i < EXT2_N_BLOCKS; i++)
421                         inode.i_block[i] = blocks[i];
422                 retval = ext2fs_write_inode(fs, ino, &inode);
423                 if (retval)
424                         return retval;
425         }
426
427         if (!block_buf)
428                 ext2fs_free_mem((void **) &ctx.ind_buf);
429
430         return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
431 }
432
433 /*
434  * Emulate the old ext2fs_block_iterate function!
435  */
436
437 struct xlate {
438         int (*func)(ext2_filsys fs,
439                     blk_t       *blocknr,
440                     int         bcount,
441                     void        *priv_data);
442         void *real_private;
443 };
444
445 #ifdef __TURBOC__
446 #pragma argsused
447 #endif
448 static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
449                       blk_t ref_block, int ref_offset, void *priv_data)
450 {
451         struct xlate *xl = (struct xlate *) priv_data;
452
453         return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
454 }
455
456 errcode_t ext2fs_block_iterate(ext2_filsys fs,
457                                ino_t    ino,
458                                int      flags,
459                                char *block_buf,
460                                int (*func)(ext2_filsys fs,
461                                            blk_t        *blocknr,
462                                            int  blockcnt,
463                                            void *priv_data),
464                                void *priv_data)
465 {
466         struct xlate xl;
467         
468         xl.real_private = priv_data;
469         xl.func = func;
470
471         return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
472                                      block_buf, xlate_func, &xl);
473 }
474