blob: 49809699554fd25a8b54a683231264fcd872d2f0 [file] [log] [blame]
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +00001/* vi: set sw=4 ts=4: */
2/*
3 * block.c --- iterate over all blocks in an inode
4 *
5 * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o.
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the GNU Public
9 * License.
10 * %End-Header%
11 */
12
13#include <stdio.h>
14#include <string.h>
15#if HAVE_UNISTD_H
16#include <unistd.h>
17#endif
18
19#include "ext2_fs.h"
20#include "ext2fs.h"
21
22struct block_context {
23 ext2_filsys fs;
24 int (*func)(ext2_filsys fs,
25 blk_t *blocknr,
26 e2_blkcnt_t bcount,
27 blk_t ref_blk,
28 int ref_offset,
29 void *priv_data);
30 e2_blkcnt_t bcount;
31 int bsize;
32 int flags;
33 errcode_t errcode;
34 char *ind_buf;
35 char *dind_buf;
36 char *tind_buf;
37 void *priv_data;
38};
39
40static int block_iterate_ind(blk_t *ind_block, blk_t ref_block,
41 int ref_offset, struct block_context *ctx)
42{
43 int ret = 0, changed = 0;
44 int i, flags, limit, offset;
45 blk_t *block_nr;
46
47 limit = ctx->fs->blocksize >> 2;
48 if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
49 !(ctx->flags & BLOCK_FLAG_DATA_ONLY))
50 ret = (*ctx->func)(ctx->fs, ind_block,
51 BLOCK_COUNT_IND, ref_block,
52 ref_offset, ctx->priv_data);
53 if (!*ind_block || (ret & BLOCK_ABORT)) {
54 ctx->bcount += limit;
55 return ret;
56 }
57 if (*ind_block >= ctx->fs->super->s_blocks_count ||
58 *ind_block < ctx->fs->super->s_first_data_block) {
59 ctx->errcode = EXT2_ET_BAD_IND_BLOCK;
60 ret |= BLOCK_ERROR;
61 return ret;
62 }
63 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block,
64 ctx->ind_buf);
65 if (ctx->errcode) {
66 ret |= BLOCK_ERROR;
67 return ret;
68 }
69
70 block_nr = (blk_t *) ctx->ind_buf;
71 offset = 0;
72 if (ctx->flags & BLOCK_FLAG_APPEND) {
73 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
74 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
75 *ind_block, offset,
76 ctx->priv_data);
77 changed |= flags;
78 if (flags & BLOCK_ABORT) {
79 ret |= BLOCK_ABORT;
80 break;
81 }
82 offset += sizeof(blk_t);
83 }
84 } else {
85 for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) {
86 if (*block_nr == 0)
87 continue;
88 flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount,
89 *ind_block, offset,
90 ctx->priv_data);
91 changed |= flags;
92 if (flags & BLOCK_ABORT) {
93 ret |= BLOCK_ABORT;
94 break;
95 }
96 offset += sizeof(blk_t);
97 }
98 }
99 if (changed & BLOCK_CHANGED) {
100 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block,
101 ctx->ind_buf);
102 if (ctx->errcode)
103 ret |= BLOCK_ERROR | BLOCK_ABORT;
104 }
105 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
106 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
107 !(ret & BLOCK_ABORT))
108 ret |= (*ctx->func)(ctx->fs, ind_block,
109 BLOCK_COUNT_IND, ref_block,
110 ref_offset, ctx->priv_data);
111 return ret;
112}
113
114static int block_iterate_dind(blk_t *dind_block, blk_t ref_block,
115 int ref_offset, struct block_context *ctx)
116{
117 int ret = 0, changed = 0;
118 int i, flags, limit, offset;
119 blk_t *block_nr;
120
121 limit = ctx->fs->blocksize >> 2;
122 if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
123 BLOCK_FLAG_DATA_ONLY)))
124 ret = (*ctx->func)(ctx->fs, dind_block,
125 BLOCK_COUNT_DIND, ref_block,
126 ref_offset, ctx->priv_data);
127 if (!*dind_block || (ret & BLOCK_ABORT)) {
128 ctx->bcount += limit*limit;
129 return ret;
130 }
131 if (*dind_block >= ctx->fs->super->s_blocks_count ||
132 *dind_block < ctx->fs->super->s_first_data_block) {
133 ctx->errcode = EXT2_ET_BAD_DIND_BLOCK;
134 ret |= BLOCK_ERROR;
135 return ret;
136 }
137 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block,
138 ctx->dind_buf);
139 if (ctx->errcode) {
140 ret |= BLOCK_ERROR;
141 return ret;
142 }
143
144 block_nr = (blk_t *) ctx->dind_buf;
145 offset = 0;
146 if (ctx->flags & BLOCK_FLAG_APPEND) {
147 for (i = 0; i < limit; i++, block_nr++) {
148 flags = block_iterate_ind(block_nr,
149 *dind_block, offset,
150 ctx);
151 changed |= flags;
152 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
153 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
154 break;
155 }
156 offset += sizeof(blk_t);
157 }
158 } else {
159 for (i = 0; i < limit; i++, block_nr++) {
160 if (*block_nr == 0) {
161 ctx->bcount += limit;
162 continue;
163 }
164 flags = block_iterate_ind(block_nr,
165 *dind_block, offset,
166 ctx);
167 changed |= flags;
168 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
169 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
170 break;
171 }
172 offset += sizeof(blk_t);
173 }
174 }
175 if (changed & BLOCK_CHANGED) {
176 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block,
177 ctx->dind_buf);
178 if (ctx->errcode)
179 ret |= BLOCK_ERROR | BLOCK_ABORT;
180 }
181 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
182 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
183 !(ret & BLOCK_ABORT))
184 ret |= (*ctx->func)(ctx->fs, dind_block,
185 BLOCK_COUNT_DIND, ref_block,
186 ref_offset, ctx->priv_data);
187 return ret;
188}
189
190static int block_iterate_tind(blk_t *tind_block, blk_t ref_block,
191 int ref_offset, struct block_context *ctx)
192{
193 int ret = 0, changed = 0;
194 int i, flags, limit, offset;
195 blk_t *block_nr;
196
197 limit = ctx->fs->blocksize >> 2;
198 if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE |
199 BLOCK_FLAG_DATA_ONLY)))
200 ret = (*ctx->func)(ctx->fs, tind_block,
201 BLOCK_COUNT_TIND, ref_block,
202 ref_offset, ctx->priv_data);
203 if (!*tind_block || (ret & BLOCK_ABORT)) {
204 ctx->bcount += limit*limit*limit;
205 return ret;
206 }
207 if (*tind_block >= ctx->fs->super->s_blocks_count ||
208 *tind_block < ctx->fs->super->s_first_data_block) {
209 ctx->errcode = EXT2_ET_BAD_TIND_BLOCK;
210 ret |= BLOCK_ERROR;
211 return ret;
212 }
213 ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block,
214 ctx->tind_buf);
215 if (ctx->errcode) {
216 ret |= BLOCK_ERROR;
217 return ret;
218 }
219
220 block_nr = (blk_t *) ctx->tind_buf;
221 offset = 0;
222 if (ctx->flags & BLOCK_FLAG_APPEND) {
223 for (i = 0; i < limit; i++, block_nr++) {
224 flags = block_iterate_dind(block_nr,
225 *tind_block,
226 offset, ctx);
227 changed |= flags;
228 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
229 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
230 break;
231 }
232 offset += sizeof(blk_t);
233 }
234 } else {
235 for (i = 0; i < limit; i++, block_nr++) {
236 if (*block_nr == 0) {
237 ctx->bcount += limit*limit;
238 continue;
239 }
240 flags = block_iterate_dind(block_nr,
241 *tind_block,
242 offset, ctx);
243 changed |= flags;
244 if (flags & (BLOCK_ABORT | BLOCK_ERROR)) {
245 ret |= flags & (BLOCK_ABORT | BLOCK_ERROR);
246 break;
247 }
248 offset += sizeof(blk_t);
249 }
250 }
251 if (changed & BLOCK_CHANGED) {
252 ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block,
253 ctx->tind_buf);
254 if (ctx->errcode)
255 ret |= BLOCK_ERROR | BLOCK_ABORT;
256 }
257 if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) &&
258 !(ctx->flags & BLOCK_FLAG_DATA_ONLY) &&
259 !(ret & BLOCK_ABORT))
260 ret |= (*ctx->func)(ctx->fs, tind_block,
261 BLOCK_COUNT_TIND, ref_block,
262 ref_offset, ctx->priv_data);
263
264 return ret;
265}
266
267errcode_t ext2fs_block_iterate2(ext2_filsys fs,
268 ext2_ino_t ino,
269 int flags,
270 char *block_buf,
271 int (*func)(ext2_filsys fs,
272 blk_t *blocknr,
273 e2_blkcnt_t blockcnt,
274 blk_t ref_blk,
275 int ref_offset,
276 void *priv_data),
277 void *priv_data)
278{
279 int i;
280 int got_inode = 0;
281 int ret = 0;
282 blk_t blocks[EXT2_N_BLOCKS]; /* directory data blocks */
283 struct ext2_inode inode;
284 errcode_t retval;
285 struct block_context ctx;
286 int limit;
287
288 EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
289
290 /*
291 * Check to see if we need to limit large files
292 */
293 if (flags & BLOCK_FLAG_NO_LARGE) {
294 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
295 if (ctx.errcode)
296 return ctx.errcode;
297 got_inode = 1;
298 if (!LINUX_S_ISDIR(inode.i_mode) &&
299 (inode.i_size_high != 0))
300 return EXT2_ET_FILE_TOO_BIG;
301 }
302
303 retval = ext2fs_get_blocks(fs, ino, blocks);
304 if (retval)
305 return retval;
306
307 limit = fs->blocksize >> 2;
308
309 ctx.fs = fs;
310 ctx.func = func;
311 ctx.priv_data = priv_data;
312 ctx.flags = flags;
313 ctx.bcount = 0;
314 if (block_buf) {
315 ctx.ind_buf = block_buf;
316 } else {
317 retval = ext2fs_get_mem(fs->blocksize * 3, &ctx.ind_buf);
318 if (retval)
319 return retval;
320 }
321 ctx.dind_buf = ctx.ind_buf + fs->blocksize;
322 ctx.tind_buf = ctx.dind_buf + fs->blocksize;
323
324 /*
325 * Iterate over the HURD translator block (if present)
326 */
327 if ((fs->super->s_creator_os == EXT2_OS_HURD) &&
328 !(flags & BLOCK_FLAG_DATA_ONLY)) {
329 ctx.errcode = ext2fs_read_inode(fs, ino, &inode);
330 if (ctx.errcode)
331 goto abort_exit;
332 got_inode = 1;
333 if (inode.osd1.hurd1.h_i_translator) {
334 ret |= (*ctx.func)(fs,
335 &inode.osd1.hurd1.h_i_translator,
336 BLOCK_COUNT_TRANSLATOR,
337 0, 0, priv_data);
338 if (ret & BLOCK_ABORT)
339 goto abort_exit;
340 }
341 }
342
343 /*
344 * Iterate over normal data blocks
345 */
Denis Vlasenkob71c6682007-07-21 15:08:09 +0000346 for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) {
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000347 if (blocks[i] || (flags & BLOCK_FLAG_APPEND)) {
348 ret |= (*ctx.func)(fs, &blocks[i],
349 ctx.bcount, 0, i, priv_data);
350 if (ret & BLOCK_ABORT)
351 goto abort_exit;
352 }
353 }
354 if (*(blocks + EXT2_IND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
355 ret |= block_iterate_ind(blocks + EXT2_IND_BLOCK,
356 0, EXT2_IND_BLOCK, &ctx);
357 if (ret & BLOCK_ABORT)
358 goto abort_exit;
359 } else
360 ctx.bcount += limit;
361 if (*(blocks + EXT2_DIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
362 ret |= block_iterate_dind(blocks + EXT2_DIND_BLOCK,
363 0, EXT2_DIND_BLOCK, &ctx);
364 if (ret & BLOCK_ABORT)
365 goto abort_exit;
366 } else
367 ctx.bcount += limit * limit;
368 if (*(blocks + EXT2_TIND_BLOCK) || (flags & BLOCK_FLAG_APPEND)) {
369 ret |= block_iterate_tind(blocks + EXT2_TIND_BLOCK,
370 0, EXT2_TIND_BLOCK, &ctx);
371 if (ret & BLOCK_ABORT)
372 goto abort_exit;
373 }
374
375abort_exit:
376 if (ret & BLOCK_CHANGED) {
377 if (!got_inode) {
378 retval = ext2fs_read_inode(fs, ino, &inode);
379 if (retval)
380 return retval;
381 }
382 for (i=0; i < EXT2_N_BLOCKS; i++)
383 inode.i_block[i] = blocks[i];
384 retval = ext2fs_write_inode(fs, ino, &inode);
385 if (retval)
386 return retval;
387 }
388
389 if (!block_buf)
390 ext2fs_free_mem(&ctx.ind_buf);
391
392 return (ret & BLOCK_ERROR) ? ctx.errcode : 0;
393}
394
395/*
396 * Emulate the old ext2fs_block_iterate function!
397 */
398
399struct xlate {
400 int (*func)(ext2_filsys fs,
401 blk_t *blocknr,
402 int bcount,
403 void *priv_data);
404 void *real_private;
405};
406
407#ifdef __TURBOC__
408# pragma argsused
409#endif
410static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt,
411 blk_t ref_block EXT2FS_ATTR((unused)),
412 int ref_offset EXT2FS_ATTR((unused)),
413 void *priv_data)
414{
415 struct xlate *xl = (struct xlate *) priv_data;
416
417 return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private);
418}
419
420errcode_t ext2fs_block_iterate(ext2_filsys fs,
421 ext2_ino_t ino,
422 int flags,
423 char *block_buf,
424 int (*func)(ext2_filsys fs,
425 blk_t *blocknr,
426 int blockcnt,
427 void *priv_data),
428 void *priv_data)
429{
430 struct xlate xl;
431
432 xl.real_private = priv_data;
433 xl.func = func;
434
435 return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags,
436 block_buf, xlate_func, &xl);
437}
438