blob: dbd04f8469e4f69be8fa7720b685da9bb4215517
1 | /* 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 | |
22 | struct 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 | |
40 | static 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 | |
114 | static 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 | |
190 | static 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 | |
267 | errcode_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 | */ |
346 | for (i = 0; i < EXT2_NDIR_BLOCKS; i++, ctx.bcount++) { |
347 | 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 | |
375 | abort_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 | |
399 | struct 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 |
410 | static 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 | |
420 | errcode_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 |