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