blob: 53c193c2a28bb83aac884139f44bda1ef0f541d5 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * block.c --- iterate over all blocks in an inode
3 *
4 * Copyright (C) 1993 Theodore Ts'o. This file may be redistributed
5 * under the terms of the GNU Public License.
6 */
7
8#include <stdio.h>
9#include <string.h>
10#include <unistd.h>
11#include <stdlib.h>
Theodore Ts'of3db3561997-04-26 13:34:30 +000012
Theodore Ts'o3839e651997-04-26 13:21:57 +000013#include <linux/ext2_fs.h>
14
15#include "ext2fs.h"
16
17struct block_context {
18 ext2_filsys fs;
19 int (*func)(ext2_filsys fs,
20 blk_t *blocknr,
21 int bcount,
22 void *private);
23 int bcount;
24 int bsize;
25 int flags;
26 errcode_t errcode;
27 char *ind_buf;
28 char *dind_buf;
29 char *tind_buf;
30 void *private;
31};
32
33static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx)
34{
35 int ret = 0, changed = 0;
36 int i, flags;
37 blk_t *block_nr;
38
39 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE))
40 ret = (*ctx->func)(ctx->fs, ind_block, -1, ctx->private);
41 if (!*ind_block || (ret & BLOCK_ABORT))
42 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +000043 if (*ind_block >= ctx->fs->super->s_blocks_count ||
44 *ind_block < ctx->fs->super->s_first_data_block) {
45 ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
46 ret |= BLOCK_ERROR;
47 return ret;
48 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000049 ctx->errcode = io_channel_read_blk(ctx->fs->io, *ind_block,
50 1, ctx->ind_buf);
51 if (ctx->errcode) {
52 ret |= BLOCK_ERROR;
53 return ret;
54 }
55 for (i = 0; i < (ctx->fs->blocksize >> 2); i++, ctx->bcount++) {
56 block_nr = (blk_t *) ctx->ind_buf + i;
57 if (*block_nr || (ctx->flags & BLOCK_FLAG_APPEND)) {
58 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
59 ctx->private);
60 changed |= flags & BLOCK_CHANGED;
61 if (flags & BLOCK_ABORT) {
62 ret |= BLOCK_ABORT;
63 break;
64 }
65 }
66 }
67 if (changed) {
68 ctx->errcode = io_channel_write_blk(ctx->fs->io, *ind_block,
69 1, ctx->ind_buf);
70 if (ctx->errcode)
71 ret |= BLOCK_ERROR | BLOCK_ABORT;
72 }
73 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
74 !(ret & BLOCK_ABORT))
75 ret |= (*ctx->func)(ctx->fs, ind_block, -1, ctx->private);
76 return ret;
77}
78
79static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx)
80{
81 int ret = 0, changed = 0;
82 int i, flags;
83 blk_t *block_nr;
84
85 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE))
86 ret = (*ctx->func)(ctx->fs, dind_block, -2, ctx->private);
87 if (!*dind_block || (ret & BLOCK_ABORT))
88 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +000089 if (*dind_block >= ctx->fs->super->s_blocks_count ||
90 *dind_block < ctx->fs->super->s_first_data_block) {
91 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
92 ret |= BLOCK_ERROR;
93 return ret;
94 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000095 ctx->errcode = io_channel_read_blk(ctx->fs->io, *dind_block,
96 1, ctx->dind_buf);
97 if (ctx->errcode) {
98 ret |= BLOCK_ERROR;
99 return ret;
100 }
101 for (i = 0; i < (ctx->fs->blocksize >> 2); i++) {
102 block_nr = (blk_t *) ctx->dind_buf + i;
103 if (*block_nr || (ctx->flags & BLOCK_FLAG_APPEND)) {
104 flags = block_iterate_ind(block_nr, ctx);
105 changed |= flags & BLOCK_CHANGED;
106 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
107 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
108 break;
109 }
110 }
111 }
112 if (changed) {
113 ctx->errcode = io_channel_write_blk(ctx->fs->io, *dind_block,
114 1, ctx->dind_buf);
115 if (ctx->errcode)
116 ret |= BLOCK_ERROR | BLOCK_ABORT;
117 }
118 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
119 !(ret & BLOCK_ABORT))
120 ret |= (*ctx->func)(ctx->fs, dind_block, -2, ctx->private);
121 return ret;
122}
123
124static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx)
125{
126 int ret = 0, changed = 0;
127 int i, flags;
128 blk_t *block_nr;
129
130 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE))
131 ret = (*ctx->func)(ctx->fs, tind_block, -3, ctx->private);
132 if (!*tind_block || (ret & BLOCK_ABORT))
133 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000134 if (*tind_block >= ctx->fs->super->s_blocks_count ||
135 *tind_block < ctx->fs->super->s_first_data_block) {
136 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
137 ret |= BLOCK_ERROR;
138 return ret;
139 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000140 ctx->errcode = io_channel_read_blk(ctx->fs->io, *tind_block,
141 1, ctx->tind_buf);
142 if (ctx->errcode) {
143 ret |= BLOCK_ERROR;
144 return ret;
145 }
146 for (i = 0; i < (ctx->fs->blocksize >> 2); i++) {
147 block_nr = (blk_t *) ctx->tind_buf + i;
148 if (*block_nr || (ctx->flags & BLOCK_FLAG_APPEND)) {
149 flags = block_iterate_dind(block_nr, ctx);
150 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
151 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
152 break;
153 }
154 }
155 }
156 if (changed) {
157 ctx->errcode = io_channel_write_blk(ctx->fs->io, *tind_block,
158 1, ctx->tind_buf);
159 if (ctx->errcode)
160 ret |= BLOCK_ERROR | BLOCK_ABORT;
161 }
162 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
163 !(ret & BLOCK_ABORT))
164 ret |= (*ctx->func)(ctx->fs, tind_block, -3, ctx->private);
165
166 return ret;
167}
168
169errcode_t ext2fs_block_iterate(ext2_filsys fs,
170 ino_t ino,
171 int flags,
172 char *block_buf,
173 int (*func)(ext2_filsys fs,
174 blk_t *blocknr,
175 int blockcnt,
176 void *private),
177 void *private)
178{
179 int i;
180 int ret = 0;
181 struct block_context ctx;
182 blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */
183 struct ext2_inode inode;
184 errcode_t retval;
185
Theodore Ts'of3db3561997-04-26 13:34:30 +0000186 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
187
Theodore Ts'o3839e651997-04-26 13:21:57 +0000188 ret = ext2fs_get_blocks(fs, ino, blocks);
189 if (ret)
190 return ret;
191
192 ctx.fs = fs;
193 ctx.func = func;
194 ctx.private = private;
195 ctx.bcount = 0;
196 ctx.flags = flags;
197 if (block_buf) {
198 ctx.ind_buf = block_buf;
199 } else {
200 ctx.ind_buf = malloc(fs->blocksize * 3);
201 if (!ctx.ind_buf)
202 return ENOMEM;
203 }
204 ctx.dind_buf = ctx.ind_buf + fs->blocksize;
205 ctx.tind_buf = ctx.dind_buf + fs->blocksize;
206
207 for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
208 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
209 ret |= (*func)(fs, &blocks[i], ctx.bcount, private);
210 if (ret & BLOCK_ABORT)
211 goto abort;
212 }
213 }
214 if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
215 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, &ctx);
216 if (ret & BLOCK_ABORT)
217 goto abort;
218 }
219 if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
220 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, &ctx);
221 if (ret & BLOCK_ABORT)
222 goto abort;
223 }
224 if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND))
225 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, &ctx);
226
227abort:
228 if (ret & BLOCK_CHANGED) {
229 retval = ext2fs_read_inode(fs, ino, &inode);
230 if (retval)
231 return retval;
232 for (i=0; i < EXT2_N_BLOCKS; i++)
233 inode.i_block[i] = blocks[i];
234 retval = ext2fs_write_inode(fs, ino, &inode);
235 if (retval)
236 return retval;
237 }
238
239 if (!block_buf)
240 free(ctx.ind_buf);
241
242 return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
243}