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