diff options
Diffstat (limited to 'e2fsprogs/ext2fs/block.c')
-rw-r--r-- | e2fsprogs/ext2fs/block.c | 437 |
1 files changed, 437 insertions, 0 deletions
diff --git a/e2fsprogs/ext2fs/block.c b/e2fsprogs/ext2fs/block.c new file mode 100644 index 000000000..7685680df --- /dev/null +++ b/e2fsprogs/ext2fs/block.c | |||
@@ -0,0 +1,437 @@ | |||
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 | |||
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 | |||
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 | |||
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 | |||
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_mem(fs->blocksize * 3, &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 | ||
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 | |||
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 | |||