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 #include <unistd.h>
15 #include <stdlib.h>
16 #if HAVE_ERRNO_H
17 #include <errno.h>
18 #endif
19
20 #include <linux/ext2_fs.h>
21
22 #include "ext2fs.h"
23
24 struct block_context {
25         ext2_filsys     fs;
26         int (*func)(ext2_filsys fs,
27                     blk_t       *blocknr,
28                     int         bcount,
29                     blk_t       ref_blk,
30                     int         ref_offset,
31                     void        *private);
32         int             bcount;
33         int             bsize;
34         int             flags;
35         errcode_t       errcode;
36         char    *ind_buf;
37         char    *dind_buf;
38         char    *tind_buf;
39         void    *private;
40 };
41
42 static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
43                              int ref_offset, struct block_context *ctx)
44 {
45         int     ret = 0, changed = 0;
46         int     i, flags, limit, offset;
47         blk_t   *block_nr;
48
49         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
50             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
51                 ret = (*ctx->func)(ctx->fs, ind_block,
52                                    BLOCK_COUNT_IND, ref_block,
53                                    ref_offset, ctx->private);
54         if (!*ind_block || (ret & BLOCK_ABORT))
55                 return ret;
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         ctx->errcode = io_channel_read_blk(ctx->fs->io, *ind_block,
63                                            1, ctx->ind_buf);
64         if (ctx->errcode) {
65                 ret |= BLOCK_ERROR;
66                 return ret;
67         }
68         limit = ctx->fs->blocksize >> 2;
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         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
133             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
134                 ret = (*ctx->func)(ctx->fs, dind_block,
135                                    BLOCK_COUNT_DIND, ref_block,
136                                    ref_offset, ctx->private);
137         if (!*dind_block || (ret & BLOCK_ABORT))
138                 return ret;
139         if (*dind_block >= ctx->fs->super->s_blocks_count ||
140             *dind_block < ctx->fs->super->s_first_data_block) {
141                 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
142                 ret |= BLOCK_ERROR;
143                 return ret;
144         }
145         ctx->errcode = io_channel_read_blk(ctx->fs->io, *dind_block,
146                                            1, ctx->dind_buf);
147         if (ctx->errcode) {
148                 ret |= BLOCK_ERROR;
149                 return ret;
150         }
151         limit = ctx->fs->blocksize >> 2;
152         if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
153             (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) {
154                 block_nr = (blk_t *) ctx->dind_buf;
155                 for (i = 0; i < limit; i++, block_nr++)
156                         *block_nr = ext2fs_swab32(*block_nr);
157         }
158         block_nr = (blk_t *) ctx->dind_buf;
159         offset = 0;
160         if (ctx->flags & BLOCK_FLAG_APPEND) {
161                 for (i = 0; i < limit; i++, block_nr++) {
162                         flags = block_iterate_ind(block_nr,
163                                                   *dind_block, offset,
164                                                   ctx);
165                         changed |= flags;
166                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
167                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
168                                 break;
169                         }
170                         offset += sizeof(blk_t);
171                 }
172         } else {
173                 for (i = 0; i < limit; i++, block_nr++) {
174                         if (*block_nr == 0)
175                                 continue;
176                         flags = block_iterate_ind(block_nr,
177                                                   *dind_block, offset,
178                                                   ctx);
179                         changed |= flags;
180                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
181                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
182                                 break;
183                         }
184                         offset += sizeof(blk_t);
185                 }
186         }
187         if (changed & BLOCK_CHANGED) {
188                 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
189                     (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
190                         block_nr = (blk_t *) ctx->dind_buf;
191                         for (i = 0; i < limit; i++, block_nr++)
192                                 *block_nr = ext2fs_swab32(*block_nr);
193                 }
194                 ctx->errcode = io_channel_write_blk(ctx->fs->io, *dind_block,
195                                                     1, ctx->dind_buf);
196                 if (ctx->errcode)
197                         ret |= BLOCK_ERROR | BLOCK_ABORT;
198         }
199         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
200             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
201             !(ret & BLOCK_ABORT))
202                 ret |= (*ctx->func)(ctx->fs, dind_block,
203                                     BLOCK_COUNT_DIND, ref_block,
204                                     ref_offset, ctx->private);
205         return ret;
206 }
207         
208 static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
209                               int ref_offset, struct block_context *ctx)
210 {
211         int     ret = 0, changed = 0;
212         int     i, flags, limit, offset;
213         blk_t   *block_nr;
214
215         if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
216             !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
217                 ret = (*ctx->func)(ctx->fs, tind_block,
218                                    BLOCK_COUNT_TIND, ref_block,
219                                    ref_offset, ctx->private);
220         if (!*tind_block || (ret & BLOCK_ABORT))
221                 return ret;
222         if (*tind_block >= ctx->fs->super->s_blocks_count ||
223             *tind_block < ctx->fs->super->s_first_data_block) {
224                 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
225                 ret |= BLOCK_ERROR;
226                 return ret;
227         }
228         ctx->errcode = io_channel_read_blk(ctx->fs->io, *tind_block,
229                                            1, ctx->tind_buf);
230         if (ctx->errcode) {
231                 ret |= BLOCK_ERROR;
232                 return ret;
233         }
234         limit = ctx->fs->blocksize >> 2;
235         if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
236             (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) {
237                 block_nr = (blk_t *) ctx->tind_buf;
238                 for (i = 0; i < limit; i++, block_nr++)
239                         *block_nr = ext2fs_swab32(*block_nr);
240         }
241         block_nr = (blk_t *) ctx->tind_buf;
242         offset = 0;
243         if (ctx->flags & BLOCK_FLAG_APPEND) {
244                 for (i = 0; i < limit; i++, block_nr++) {
245                         flags = block_iterate_dind(block_nr,
246                                                    *tind_block,
247                                                    offset, ctx);
248                         changed |= flags;
249                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
250                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
251                                 break;
252                         }
253                         offset += sizeof(blk_t);
254                 }
255         } else {
256                 for (i = 0; i < limit; i++, block_nr++) {
257                         if (*block_nr == 0)
258                                 continue;
259                         flags = block_iterate_dind(block_nr,
260                                                    *tind_block,
261                                                    offset, ctx);
262                         changed |= flags;
263                         if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
264                                 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
265                                 break;
266                         }
267                         offset += sizeof(blk_t);
268                 }
269         }
270         if (changed & BLOCK_CHANGED) {
271                 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
272                     (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
273                         block_nr = (blk_t *) ctx->tind_buf;
274                         for (i = 0; i < limit; i++, block_nr++)
275                                 *block_nr = ext2fs_swab32(*block_nr);
276                 }
277                 ctx->errcode = io_channel_write_blk(ctx->fs->io, *tind_block,
278                                                     1, ctx->tind_buf);
279                 if (ctx->errcode)
280                         ret |= BLOCK_ERROR | BLOCK_ABORT;
281         }
282         if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
283             !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
284             !(ret & BLOCK_ABORT))
285                 ret |= (*ctx->func)(ctx->fs, tind_block,
286                                     BLOCK_COUNT_TIND, ref_block,
287                                     ref_offset, ctx->private);
288         
289         return ret;
290 }
291         
292 errcode_t ext2fs_block_iterate2(ext2_filsys fs,
293                                 ino_t   ino,
294                                 int     flags,
295                                 char *block_buf,
296                                 int (*func)(ext2_filsys fs,
297                                             blk_t       *blocknr,
298                                             int blockcnt,
299                                             blk_t       ref_blk,
300                                             int         ref_offset,
301                                             void        *private),
302                                 void *private)
303 {
304         int     i;
305         int     got_inode = 0;
306         int     ret = 0;
307         blk_t   blocks[EXT2_N_BLOCKS];  /* directory data blocks */
308         struct ext2_inode inode;
309         errcode_t       retval;
310         struct block_context ctx;
311
312         EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
313
314         ret = ext2fs_get_blocks(fs, ino, blocks);
315         if (ret)
316                 return ret;
317
318         ctx.fs = fs;
319         ctx.func = func;
320         ctx.private = private;
321         ctx.flags = flags;
322         ctx.bcount = 0;
323         if (block_buf) {
324                 ctx.ind_buf = block_buf;
325         } else {
326                 ctx.ind_buf = malloc(fs->blocksize * 3);
327                 if (!ctx.ind_buf)
328                         return ENOMEM;
329         }
330         ctx.dind_buf = ctx.ind_buf + fs->blocksize;
331         ctx.tind_buf = ctx.dind_buf + fs->blocksize;
332
333         /*
334          * Iterate over the HURD translator block (if present)
335          */
336         if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
337             !(flags & BLOCK_FLAG_DATA_ONLY)) {
338                 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
339                 if (ctx.errcode)
340                         goto abort;
341                 got_inode = 1;
342                 if (inode.osd1.hurd1.h_i_translator) {
343                         ret |= (*ctx.func)(fs,
344                                            &inode.osd1.hurd1.h_i_translator,
345                                            BLOCK_COUNT_TRANSLATOR,
346                                            0, 0, private);
347                         if (ret & BLOCK_ABORT)
348                                 goto abort;
349                 }
350         }
351         
352         /*
353          * Iterate over normal data blocks
354          */
355         for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
356                 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
357                         ret |= (*ctx.func)(fs, &blocks[i],
358                                             ctx.bcount, 0, 0, private);
359                         if (ret & BLOCK_ABORT)
360                                 goto abort;
361                 }
362         }
363         if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
364                 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
365                                          0, 0, &ctx);
366                 if (ret & BLOCK_ABORT)
367                         goto abort;
368         }
369         if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
370                 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
371                                           0, 0, &ctx);
372                 if (ret & BLOCK_ABORT)
373                         goto abort;
374         }
375         if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
376                 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
377                                           0, 0, &ctx);
378                 if (ret & BLOCK_ABORT)
379                         goto abort;
380         }
381
382 abort:
383         if (ret & BLOCK_CHANGED) {
384                 if (!got_inode) {
385                         retval = ext2fs_read_inode(fs, ino, &inode);
386                         if (retval)
387                                 return retval;
388                 }
389                 for (i=0; i < EXT2_N_BLOCKS; i++)
390                         inode.i_block[i] = blocks[i];
391                 retval = ext2fs_write_inode(fs, ino, &inode);
392                 if (retval)
393                         return retval;
394         }
395
396         if (!block_buf)
397                 free(ctx.ind_buf);
398
399         return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
400 }
401
402 struct xlate {
403         int (*func)(ext2_filsys fs,
404                     blk_t       *blocknr,
405                     int         bcount,
406                     void        *private);
407         void *real_private;
408 };
409
410 static int xlate_func(ext2_filsys fs, blk_t *blocknr, int blockcnt,
411                       blk_t ref_block, int ref_offset, void *private)
412 {
413         struct xlate *xl = private;
414
415         return (*xl->func)(fs, blocknr, blockcnt, xl->real_private);
416 }
417
418 errcode_t ext2fs_block_iterate(ext2_filsys fs,
419                                ino_t    ino,
420                                int      flags,
421                                char *block_buf,
422                                int (*func)(ext2_filsys fs,
423                                            blk_t        *blocknr,
424                                            int  blockcnt,
425                                            void *private),
426                                void *private)
427 {
428         struct xlate xl;
429         
430         xl.real_private = private;
431         xl.func = func;
432
433         return ext2fs_block_iterate2(fs, ino, flags, block_buf,
434                                     xlate_func, &xl);
435 }
436
437