blob: b7f0aa3b5c8bf84b222a0466bccc37962abd3137 [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'o50e1e101997-04-26 13:58:21 +000012#if HAVE_ERRNO_H
13#include <errno.h>
14#endif
Theodore Ts'of3db3561997-04-26 13:34:30 +000015
Theodore Ts'o3839e651997-04-26 13:21:57 +000016#include <linux/ext2_fs.h>
17
18#include "ext2fs.h"
19
20struct block_context {
21 ext2_filsys fs;
22 int (*func)(ext2_filsys fs,
23 blk_t *blocknr,
24 int bcount,
25 void *private);
26 int bcount;
27 int bsize;
28 int flags;
29 errcode_t errcode;
30 char *ind_buf;
31 char *dind_buf;
32 char *tind_buf;
33 void *private;
34};
35
36static int block_iterate_ind(blk_t *ind_block, struct block_context *ctx)
37{
38 int ret = 0, changed = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +000039 int i, flags, limit;
Theodore Ts'o3839e651997-04-26 13:21:57 +000040 blk_t *block_nr;
41
Theodore Ts'o1e3472c1997-04-29 14:53:37 +000042 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
43 !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
44 ret = (*ctx->func)(ctx->fs, ind_block,
45 BLOCK_COUNT_IND, ctx->private);
Theodore Ts'o3839e651997-04-26 13:21:57 +000046 if (!*ind_block || (ret & BLOCK_ABORT))
47 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +000048 if (*ind_block >= ctx->fs->super->s_blocks_count ||
49 *ind_block < ctx->fs->super->s_first_data_block) {
50 ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
51 ret |= BLOCK_ERROR;
52 return ret;
53 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000054 ctx->errcode = io_channel_read_blk(ctx->fs->io, *ind_block,
55 1, ctx->ind_buf);
56 if (ctx->errcode) {
57 ret |= BLOCK_ERROR;
58 return ret;
59 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +000060 limit = ctx->fs->blocksize >> 2;
Theodore Ts'o5c576471997-04-29 15:29:49 +000061 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
62 (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +000063 block_nr = (blk_t *) ctx->ind_buf;
64 for (i = 0; i < limit; i++, block_nr++)
65 *block_nr = ext2fs_swab32(*block_nr);
66 }
67 block_nr = (blk_t *) ctx->ind_buf;
68 if (ctx->flags & BLOCK_FLAG_APPEND) {
69 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +000070 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
71 ctx->private);
Theodore Ts'o50e1e101997-04-26 13:58:21 +000072 changed |= flags;
73 if (flags & BLOCK_ABORT) {
74 ret |= BLOCK_ABORT;
75 break;
76 }
77 }
78 } else {
79 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
80 if (*block_nr == 0)
81 continue;
82 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
83 ctx->private);
84 changed |= flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +000085 if (flags & BLOCK_ABORT) {
86 ret |= BLOCK_ABORT;
87 break;
88 }
89 }
90 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +000091 if (changed & BLOCK_CHANGED) {
Theodore Ts'o5c576471997-04-29 15:29:49 +000092 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
93 (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +000094 block_nr = (blk_t *) ctx->ind_buf;
95 for (i = 0; i < limit; i++, block_nr++)
96 *block_nr = ext2fs_swab32(*block_nr);
97 }
Theodore Ts'o3839e651997-04-26 13:21:57 +000098 ctx->errcode = io_channel_write_blk(ctx->fs->io, *ind_block,
99 1, ctx->ind_buf);
100 if (ctx->errcode)
101 ret |= BLOCK_ERROR | BLOCK_ABORT;
102 }
103 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000104 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
Theodore Ts'o3839e651997-04-26 13:21:57 +0000105 !(ret & BLOCK_ABORT))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000106 ret |= (*ctx->func)(ctx->fs, ind_block,
107 BLOCK_COUNT_IND, ctx->private);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000108 return ret;
109}
110
111static int block_iterate_dind(blk_t *dind_block, struct block_context *ctx)
112{
113 int ret = 0, changed = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000114 int i, flags, limit;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000115 blk_t *block_nr;
116
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000117 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
118 !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
119 ret = (*ctx->func)(ctx->fs, dind_block,
120 BLOCK_COUNT_DIND, ctx->private);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000121 if (!*dind_block || (ret & BLOCK_ABORT))
122 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000123 if (*dind_block >= ctx->fs->super->s_blocks_count ||
124 *dind_block < ctx->fs->super->s_first_data_block) {
125 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
126 ret |= BLOCK_ERROR;
127 return ret;
128 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000129 ctx->errcode = io_channel_read_blk(ctx->fs->io, *dind_block,
130 1, ctx->dind_buf);
131 if (ctx->errcode) {
132 ret |= BLOCK_ERROR;
133 return ret;
134 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000135 limit = ctx->fs->blocksize >> 2;
Theodore Ts'o5c576471997-04-29 15:29:49 +0000136 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
137 (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000138 block_nr = (blk_t *) ctx->dind_buf;
139 for (i = 0; i < limit; i++, block_nr++)
140 *block_nr = ext2fs_swab32(*block_nr);
141 }
142 block_nr = (blk_t *) ctx->dind_buf;
143 if (ctx->flags & BLOCK_FLAG_APPEND) {
144 for (i = 0; i < limit; i++, block_nr++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000145 flags = block_iterate_ind(block_nr, ctx);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000146 changed |= flags;
147 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
148 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
149 break;
150 }
151 }
152 } else {
153 for (i = 0; i < limit; i++, block_nr++) {
154 if (*block_nr == 0)
155 continue;
156 flags = block_iterate_ind(block_nr, ctx);
157 changed |= flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000158 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
159 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
160 break;
161 }
162 }
163 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000164 if (changed & BLOCK_CHANGED) {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000165 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
166 (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000167 block_nr = (blk_t *) ctx->dind_buf;
168 for (i = 0; i < limit; i++, block_nr++)
169 *block_nr = ext2fs_swab32(*block_nr);
170 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000171 ctx->errcode = io_channel_write_blk(ctx->fs->io, *dind_block,
172 1, ctx->dind_buf);
173 if (ctx->errcode)
174 ret |= BLOCK_ERROR | BLOCK_ABORT;
175 }
176 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000177 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
Theodore Ts'o3839e651997-04-26 13:21:57 +0000178 !(ret & BLOCK_ABORT))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000179 ret |= (*ctx->func)(ctx->fs, dind_block,
180 BLOCK_COUNT_DIND, ctx->private);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000181 return ret;
182}
183
184static int block_iterate_tind(blk_t *tind_block, struct block_context *ctx)
185{
186 int ret = 0, changed = 0;
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000187 int i, flags, limit;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000188 blk_t *block_nr;
189
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000190 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
191 !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
192 ret = (*ctx->func)(ctx->fs, tind_block,
193 BLOCK_COUNT_TIND, ctx->private);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000194 if (!*tind_block || (ret & BLOCK_ABORT))
195 return ret;
Theodore Ts'of3db3561997-04-26 13:34:30 +0000196 if (*tind_block >= ctx->fs->super->s_blocks_count ||
197 *tind_block < ctx->fs->super->s_first_data_block) {
198 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
199 ret |= BLOCK_ERROR;
200 return ret;
201 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000202 ctx->errcode = io_channel_read_blk(ctx->fs->io, *tind_block,
203 1, ctx->tind_buf);
204 if (ctx->errcode) {
205 ret |= BLOCK_ERROR;
206 return ret;
207 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000208 limit = ctx->fs->blocksize >> 2;
Theodore Ts'o5c576471997-04-29 15:29:49 +0000209 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
210 (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_READ)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000211 block_nr = (blk_t *) ctx->tind_buf;
212 for (i = 0; i < limit; i++, block_nr++)
213 *block_nr = ext2fs_swab32(*block_nr);
214 }
215 block_nr = (blk_t *) ctx->tind_buf;
216 if (ctx->flags & BLOCK_FLAG_APPEND) {
217 for (i = 0; i < limit; i++, block_nr++) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000218 flags = block_iterate_dind(block_nr, ctx);
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000219 changed |= flags;
220 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
221 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
222 break;
223 }
224 }
225 } else {
226 for (i = 0; i < limit; i++, block_nr++) {
227 if (*block_nr == 0)
228 continue;
229 flags = block_iterate_dind(block_nr, ctx);
230 changed |= flags;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000231 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
232 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
233 break;
234 }
235 }
236 }
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000237 if (changed & BLOCK_CHANGED) {
Theodore Ts'o5c576471997-04-29 15:29:49 +0000238 if ((ctx->fs->flags & EXT2_FLAG_SWAP_BYTES) ||
239 (ctx->fs->flags & EXT2_FLAG_SWAP_BYTES_WRITE)) {
Theodore Ts'o50e1e101997-04-26 13:58:21 +0000240 block_nr = (blk_t *) ctx->tind_buf;
241 for (i = 0; i < limit; i++, block_nr++)
242 *block_nr = ext2fs_swab32(*block_nr);
243 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000244 ctx->errcode = io_channel_write_blk(ctx->fs->io, *tind_block,
245 1, ctx->tind_buf);
246 if (ctx->errcode)
247 ret |= BLOCK_ERROR | BLOCK_ABORT;
248 }
249 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000250 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
Theodore Ts'o3839e651997-04-26 13:21:57 +0000251 !(ret & BLOCK_ABORT))
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000252 ret |= (*ctx->func)(ctx->fs, tind_block,
253 BLOCK_COUNT_TIND, ctx->private);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000254
255 return ret;
256}
257
258errcode_t ext2fs_block_iterate(ext2_filsys fs,
259 ino_t ino,
260 int flags,
261 char *block_buf,
262 int (*func)(ext2_filsys fs,
263 blk_t *blocknr,
264 int blockcnt,
265 void *private),
266 void *private)
267{
268 int i;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000269 int got_inode = 0;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000270 int ret = 0;
271 struct block_context ctx;
272 blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */
273 struct ext2_inode inode;
274 errcode_t retval;
275
Theodore Ts'of3db3561997-04-26 13:34:30 +0000276 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
277
Theodore Ts'o3839e651997-04-26 13:21:57 +0000278 ret = ext2fs_get_blocks(fs, ino, blocks);
279 if (ret)
280 return ret;
281
282 ctx.fs = fs;
283 ctx.func = func;
284 ctx.private = private;
285 ctx.bcount = 0;
286 ctx.flags = flags;
287 if (block_buf) {
288 ctx.ind_buf = block_buf;
289 } else {
290 ctx.ind_buf = malloc(fs->blocksize * 3);
291 if (!ctx.ind_buf)
292 return ENOMEM;
293 }
294 ctx.dind_buf = ctx.ind_buf + fs->blocksize;
295 ctx.tind_buf = ctx.dind_buf + fs->blocksize;
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000296
297 /*
298 * Iterate over the HURD translator block (if present)
299 */
300 if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
Theodore Ts'o5c576471997-04-29 15:29:49 +0000301 !(flags & BLOCK_FLAG_DATA_ONLY)) {
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000302 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
303 if (ctx.errcode)
304 goto abort;
305 got_inode = 1;
Theodore Ts'o5c576471997-04-29 15:29:49 +0000306 if (inode.osd1.hurd1.h_i_translator) {
307 ret |= (*func)(fs, &inode.osd1.hurd1.h_i_translator,
308 BLOCK_COUNT_TRANSLATOR, private);
309 if (ret & BLOCK_ABORT)
310 goto abort;
311 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000312 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000313
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000314 /*
315 * Iterate over normal data blocks
316 */
Theodore Ts'o3839e651997-04-26 13:21:57 +0000317 for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) {
318 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
319 ret |= (*func)(fs, &blocks[i], ctx.bcount, private);
320 if (ret & BLOCK_ABORT)
321 goto abort;
322 }
323 }
324 if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
325 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK, &ctx);
326 if (ret & BLOCK_ABORT)
327 goto abort;
328 }
329 if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
330 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK, &ctx);
331 if (ret & BLOCK_ABORT)
332 goto abort;
333 }
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000334 if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000335 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK, &ctx);
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000336 if (ret & BLOCK_ABORT)
337 goto abort;
338 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000339
340abort:
341 if (ret & BLOCK_CHANGED) {
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000342 if (!got_inode) {
343 retval = ext2fs_read_inode(fs, ino, &inode);
344 if (retval)
345 return retval;
346 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000347 for (i=0; i < EXT2_N_BLOCKS; i++)
348 inode.i_block[i] = blocks[i];
349 retval = ext2fs_write_inode(fs, ino, &inode);
350 if (retval)
351 return retval;
352 }
353
354 if (!block_buf)
355 free(ctx.ind_buf);
356
357 return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
358}