diff options
Diffstat (limited to 'e2fsprogs/e2fsck/pass3.c')
-rw-r--r-- | e2fsprogs/e2fsck/pass3.c | 804 |
1 files changed, 804 insertions, 0 deletions
diff --git a/e2fsprogs/e2fsck/pass3.c b/e2fsprogs/e2fsck/pass3.c new file mode 100644 index 000000000..a92c8904c --- /dev/null +++ b/e2fsprogs/e2fsck/pass3.c | |||
@@ -0,0 +1,804 @@ | |||
1 | /* | ||
2 | * pass3.c -- pass #3 of e2fsck: Check for directory connectivity | ||
3 | * | ||
4 | * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 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 | * Pass #3 assures that all directories are connected to the | ||
12 | * filesystem tree, using the following algorithm: | ||
13 | * | ||
14 | * First, the root directory is checked to make sure it exists; if | ||
15 | * not, e2fsck will offer to create a new one. It is then marked as | ||
16 | * "done". | ||
17 | * | ||
18 | * Then, pass3 interates over all directory inodes; for each directory | ||
19 | * it attempts to trace up the filesystem tree, using dirinfo.parent | ||
20 | * until it reaches a directory which has been marked "done". If it | ||
21 | * can not do so, then the directory must be disconnected, and e2fsck | ||
22 | * will offer to reconnect it to /lost+found. While it is chasing | ||
23 | * parent pointers up the filesystem tree, if pass3 sees a directory | ||
24 | * twice, then it has detected a filesystem loop, and it will again | ||
25 | * offer to reconnect the directory to /lost+found in to break the | ||
26 | * filesystem loop. | ||
27 | * | ||
28 | * Pass 3 also contains the subroutine, e2fsck_reconnect_file() to | ||
29 | * reconnect inodes to /lost+found; this subroutine is also used by | ||
30 | * pass 4. e2fsck_reconnect_file() calls get_lost_and_found(), which | ||
31 | * is responsible for creating /lost+found if it does not exist. | ||
32 | * | ||
33 | * Pass 3 frees the following data structures: | ||
34 | * - The dirinfo directory information cache. | ||
35 | */ | ||
36 | |||
37 | #ifdef HAVE_ERRNO_H | ||
38 | #include <errno.h> | ||
39 | #endif | ||
40 | |||
41 | #include "e2fsck.h" | ||
42 | #include "problem.h" | ||
43 | |||
44 | static void check_root(e2fsck_t ctx); | ||
45 | static int check_directory(e2fsck_t ctx, struct dir_info *dir, | ||
46 | struct problem_context *pctx); | ||
47 | static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent); | ||
48 | |||
49 | static ext2fs_inode_bitmap inode_loop_detect = 0; | ||
50 | static ext2fs_inode_bitmap inode_done_map = 0; | ||
51 | |||
52 | void e2fsck_pass3(e2fsck_t ctx) | ||
53 | { | ||
54 | ext2_filsys fs = ctx->fs; | ||
55 | int i; | ||
56 | #ifdef RESOURCE_TRACK | ||
57 | struct resource_track rtrack; | ||
58 | #endif | ||
59 | struct problem_context pctx; | ||
60 | struct dir_info *dir; | ||
61 | unsigned long maxdirs, count; | ||
62 | |||
63 | #ifdef RESOURCE_TRACK | ||
64 | init_resource_track(&rtrack); | ||
65 | #endif | ||
66 | |||
67 | clear_problem_context(&pctx); | ||
68 | |||
69 | #ifdef MTRACE | ||
70 | mtrace_print("Pass 3"); | ||
71 | #endif | ||
72 | |||
73 | if (!(ctx->options & E2F_OPT_PREEN)) | ||
74 | fix_problem(ctx, PR_3_PASS_HEADER, &pctx); | ||
75 | |||
76 | /* | ||
77 | * Allocate some bitmaps to do loop detection. | ||
78 | */ | ||
79 | pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"), | ||
80 | &inode_done_map); | ||
81 | if (pctx.errcode) { | ||
82 | pctx.num = 2; | ||
83 | fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx); | ||
84 | ctx->flags |= E2F_FLAG_ABORT; | ||
85 | goto abort_exit; | ||
86 | } | ||
87 | #ifdef RESOURCE_TRACK | ||
88 | if (ctx->options & E2F_OPT_TIME) { | ||
89 | e2fsck_clear_progbar(ctx); | ||
90 | print_resource_track(_("Peak memory"), &ctx->global_rtrack); | ||
91 | } | ||
92 | #endif | ||
93 | |||
94 | check_root(ctx); | ||
95 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) | ||
96 | goto abort_exit; | ||
97 | |||
98 | ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO); | ||
99 | |||
100 | maxdirs = e2fsck_get_num_dirinfo(ctx); | ||
101 | count = 1; | ||
102 | |||
103 | if (ctx->progress) | ||
104 | if ((ctx->progress)(ctx, 3, 0, maxdirs)) | ||
105 | goto abort_exit; | ||
106 | |||
107 | for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) { | ||
108 | if (ctx->flags & E2F_FLAG_SIGNAL_MASK) | ||
109 | goto abort_exit; | ||
110 | if (ctx->progress && (ctx->progress)(ctx, 3, count++, maxdirs)) | ||
111 | goto abort_exit; | ||
112 | if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino)) | ||
113 | if (check_directory(ctx, dir, &pctx)) | ||
114 | goto abort_exit; | ||
115 | } | ||
116 | |||
117 | /* | ||
118 | * Force the creation of /lost+found if not present | ||
119 | */ | ||
120 | if ((ctx->flags & E2F_OPT_READONLY) == 0) | ||
121 | e2fsck_get_lost_and_found(ctx, 1); | ||
122 | |||
123 | /* | ||
124 | * If there are any directories that need to be indexed or | ||
125 | * optimized, do it here. | ||
126 | */ | ||
127 | e2fsck_rehash_directories(ctx); | ||
128 | |||
129 | abort_exit: | ||
130 | e2fsck_free_dir_info(ctx); | ||
131 | if (inode_loop_detect) { | ||
132 | ext2fs_free_inode_bitmap(inode_loop_detect); | ||
133 | inode_loop_detect = 0; | ||
134 | } | ||
135 | if (inode_done_map) { | ||
136 | ext2fs_free_inode_bitmap(inode_done_map); | ||
137 | inode_done_map = 0; | ||
138 | } | ||
139 | |||
140 | #ifdef RESOURCE_TRACK | ||
141 | if (ctx->options & E2F_OPT_TIME2) { | ||
142 | e2fsck_clear_progbar(ctx); | ||
143 | print_resource_track(_("Pass 3"), &rtrack); | ||
144 | } | ||
145 | #endif | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * This makes sure the root inode is present; if not, we ask if the | ||
150 | * user wants us to create it. Not creating it is a fatal error. | ||
151 | */ | ||
152 | static void check_root(e2fsck_t ctx) | ||
153 | { | ||
154 | ext2_filsys fs = ctx->fs; | ||
155 | blk_t blk; | ||
156 | struct ext2_inode inode; | ||
157 | char * block; | ||
158 | struct problem_context pctx; | ||
159 | |||
160 | clear_problem_context(&pctx); | ||
161 | |||
162 | if (ext2fs_test_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO)) { | ||
163 | /* | ||
164 | * If the root inode is not a directory, die here. The | ||
165 | * user must have answered 'no' in pass1 when we | ||
166 | * offered to clear it. | ||
167 | */ | ||
168 | if (!(ext2fs_test_inode_bitmap(ctx->inode_dir_map, | ||
169 | EXT2_ROOT_INO))) { | ||
170 | fix_problem(ctx, PR_3_ROOT_NOT_DIR_ABORT, &pctx); | ||
171 | ctx->flags |= E2F_FLAG_ABORT; | ||
172 | } | ||
173 | return; | ||
174 | } | ||
175 | |||
176 | if (!fix_problem(ctx, PR_3_NO_ROOT_INODE, &pctx)) { | ||
177 | fix_problem(ctx, PR_3_NO_ROOT_INODE_ABORT, &pctx); | ||
178 | ctx->flags |= E2F_FLAG_ABORT; | ||
179 | return; | ||
180 | } | ||
181 | |||
182 | e2fsck_read_bitmaps(ctx); | ||
183 | |||
184 | /* | ||
185 | * First, find a free block | ||
186 | */ | ||
187 | pctx.errcode = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); | ||
188 | if (pctx.errcode) { | ||
189 | pctx.str = "ext2fs_new_block"; | ||
190 | fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); | ||
191 | ctx->flags |= E2F_FLAG_ABORT; | ||
192 | return; | ||
193 | } | ||
194 | ext2fs_mark_block_bitmap(ctx->block_found_map, blk); | ||
195 | ext2fs_mark_block_bitmap(fs->block_map, blk); | ||
196 | ext2fs_mark_bb_dirty(fs); | ||
197 | |||
198 | /* | ||
199 | * Now let's create the actual data block for the inode | ||
200 | */ | ||
201 | pctx.errcode = ext2fs_new_dir_block(fs, EXT2_ROOT_INO, EXT2_ROOT_INO, | ||
202 | &block); | ||
203 | if (pctx.errcode) { | ||
204 | pctx.str = "ext2fs_new_dir_block"; | ||
205 | fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); | ||
206 | ctx->flags |= E2F_FLAG_ABORT; | ||
207 | return; | ||
208 | } | ||
209 | |||
210 | pctx.errcode = ext2fs_write_dir_block(fs, blk, block); | ||
211 | if (pctx.errcode) { | ||
212 | pctx.str = "ext2fs_write_dir_block"; | ||
213 | fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); | ||
214 | ctx->flags |= E2F_FLAG_ABORT; | ||
215 | return; | ||
216 | } | ||
217 | ext2fs_free_mem(&block); | ||
218 | |||
219 | /* | ||
220 | * Set up the inode structure | ||
221 | */ | ||
222 | memset(&inode, 0, sizeof(inode)); | ||
223 | inode.i_mode = 040755; | ||
224 | inode.i_size = fs->blocksize; | ||
225 | inode.i_atime = inode.i_ctime = inode.i_mtime = time(0); | ||
226 | inode.i_links_count = 2; | ||
227 | inode.i_blocks = fs->blocksize / 512; | ||
228 | inode.i_block[0] = blk; | ||
229 | |||
230 | /* | ||
231 | * Write out the inode. | ||
232 | */ | ||
233 | pctx.errcode = ext2fs_write_new_inode(fs, EXT2_ROOT_INO, &inode); | ||
234 | if (pctx.errcode) { | ||
235 | pctx.str = "ext2fs_write_inode"; | ||
236 | fix_problem(ctx, PR_3_CREATE_ROOT_ERROR, &pctx); | ||
237 | ctx->flags |= E2F_FLAG_ABORT; | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Miscellaneous bookkeeping... | ||
243 | */ | ||
244 | e2fsck_add_dir_info(ctx, EXT2_ROOT_INO, EXT2_ROOT_INO); | ||
245 | ext2fs_icount_store(ctx->inode_count, EXT2_ROOT_INO, 2); | ||
246 | ext2fs_icount_store(ctx->inode_link_info, EXT2_ROOT_INO, 2); | ||
247 | |||
248 | ext2fs_mark_inode_bitmap(ctx->inode_used_map, EXT2_ROOT_INO); | ||
249 | ext2fs_mark_inode_bitmap(ctx->inode_dir_map, EXT2_ROOT_INO); | ||
250 | ext2fs_mark_inode_bitmap(fs->inode_map, EXT2_ROOT_INO); | ||
251 | ext2fs_mark_ib_dirty(fs); | ||
252 | } | ||
253 | |||
254 | /* | ||
255 | * This subroutine is responsible for making sure that a particular | ||
256 | * directory is connected to the root; if it isn't we trace it up as | ||
257 | * far as we can go, and then offer to connect the resulting parent to | ||
258 | * the lost+found. We have to do loop detection; if we ever discover | ||
259 | * a loop, we treat that as a disconnected directory and offer to | ||
260 | * reparent it to lost+found. | ||
261 | * | ||
262 | * However, loop detection is expensive, because for very large | ||
263 | * filesystems, the inode_loop_detect bitmap is huge, and clearing it | ||
264 | * is non-trivial. Loops in filesystems are also a rare error case, | ||
265 | * and we shouldn't optimize for error cases. So we try two passes of | ||
266 | * the algorithm. The first time, we ignore loop detection and merely | ||
267 | * increment a counter; if the counter exceeds some extreme threshold, | ||
268 | * then we try again with the loop detection bitmap enabled. | ||
269 | */ | ||
270 | static int check_directory(e2fsck_t ctx, struct dir_info *dir, | ||
271 | struct problem_context *pctx) | ||
272 | { | ||
273 | ext2_filsys fs = ctx->fs; | ||
274 | struct dir_info *p = dir; | ||
275 | int loop_pass = 0, parent_count = 0; | ||
276 | |||
277 | if (!p) | ||
278 | return 0; | ||
279 | |||
280 | while (1) { | ||
281 | /* | ||
282 | * Mark this inode as being "done"; by the time we | ||
283 | * return from this function, the inode we either be | ||
284 | * verified as being connected to the directory tree, | ||
285 | * or we will have offered to reconnect this to | ||
286 | * lost+found. | ||
287 | * | ||
288 | * If it was marked done already, then we've reached a | ||
289 | * parent we've already checked. | ||
290 | */ | ||
291 | if (ext2fs_mark_inode_bitmap(inode_done_map, p->ino)) | ||
292 | break; | ||
293 | |||
294 | /* | ||
295 | * If this directory doesn't have a parent, or we've | ||
296 | * seen the parent once already, then offer to | ||
297 | * reparent it to lost+found | ||
298 | */ | ||
299 | if (!p->parent || | ||
300 | (loop_pass && | ||
301 | (ext2fs_test_inode_bitmap(inode_loop_detect, | ||
302 | p->parent)))) { | ||
303 | pctx->ino = p->ino; | ||
304 | if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) { | ||
305 | if (e2fsck_reconnect_file(ctx, pctx->ino)) | ||
306 | ext2fs_unmark_valid(fs); | ||
307 | else { | ||
308 | p = e2fsck_get_dir_info(ctx, pctx->ino); | ||
309 | p->parent = ctx->lost_and_found; | ||
310 | fix_dotdot(ctx, p, ctx->lost_and_found); | ||
311 | } | ||
312 | } | ||
313 | break; | ||
314 | } | ||
315 | p = e2fsck_get_dir_info(ctx, p->parent); | ||
316 | if (!p) { | ||
317 | fix_problem(ctx, PR_3_NO_DIRINFO, pctx); | ||
318 | return 0; | ||
319 | } | ||
320 | if (loop_pass) { | ||
321 | ext2fs_mark_inode_bitmap(inode_loop_detect, | ||
322 | p->ino); | ||
323 | } else if (parent_count++ > 2048) { | ||
324 | /* | ||
325 | * If we've run into a path depth that's | ||
326 | * greater than 2048, try again with the inode | ||
327 | * loop bitmap turned on and start from the | ||
328 | * top. | ||
329 | */ | ||
330 | loop_pass = 1; | ||
331 | if (inode_loop_detect) | ||
332 | ext2fs_clear_inode_bitmap(inode_loop_detect); | ||
333 | else { | ||
334 | pctx->errcode = ext2fs_allocate_inode_bitmap(fs, _("inode loop detection bitmap"), &inode_loop_detect); | ||
335 | if (pctx->errcode) { | ||
336 | pctx->num = 1; | ||
337 | fix_problem(ctx, | ||
338 | PR_3_ALLOCATE_IBITMAP_ERROR, pctx); | ||
339 | ctx->flags |= E2F_FLAG_ABORT; | ||
340 | return -1; | ||
341 | } | ||
342 | } | ||
343 | p = dir; | ||
344 | } | ||
345 | } | ||
346 | |||
347 | /* | ||
348 | * Make sure that .. and the parent directory are the same; | ||
349 | * offer to fix it if not. | ||
350 | */ | ||
351 | if (dir->parent != dir->dotdot) { | ||
352 | pctx->ino = dir->ino; | ||
353 | pctx->ino2 = dir->dotdot; | ||
354 | pctx->dir = dir->parent; | ||
355 | if (fix_problem(ctx, PR_3_BAD_DOT_DOT, pctx)) | ||
356 | fix_dotdot(ctx, dir, dir->parent); | ||
357 | } | ||
358 | return 0; | ||
359 | } | ||
360 | |||
361 | /* | ||
362 | * This routine gets the lost_and_found inode, making it a directory | ||
363 | * if necessary | ||
364 | */ | ||
365 | ext2_ino_t e2fsck_get_lost_and_found(e2fsck_t ctx, int fix) | ||
366 | { | ||
367 | ext2_filsys fs = ctx->fs; | ||
368 | ext2_ino_t ino; | ||
369 | blk_t blk; | ||
370 | errcode_t retval; | ||
371 | struct ext2_inode inode; | ||
372 | char * block; | ||
373 | static const char name[] = "lost+found"; | ||
374 | struct problem_context pctx; | ||
375 | struct dir_info *dirinfo; | ||
376 | |||
377 | if (ctx->lost_and_found) | ||
378 | return ctx->lost_and_found; | ||
379 | |||
380 | clear_problem_context(&pctx); | ||
381 | |||
382 | retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name, | ||
383 | sizeof(name)-1, 0, &ino); | ||
384 | if (retval && !fix) | ||
385 | return 0; | ||
386 | if (!retval) { | ||
387 | if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino)) { | ||
388 | ctx->lost_and_found = ino; | ||
389 | return ino; | ||
390 | } | ||
391 | |||
392 | /* Lost+found isn't a directory! */ | ||
393 | if (!fix) | ||
394 | return 0; | ||
395 | pctx.ino = ino; | ||
396 | if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx)) | ||
397 | return 0; | ||
398 | |||
399 | /* OK, unlink the old /lost+found file. */ | ||
400 | pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0); | ||
401 | if (pctx.errcode) { | ||
402 | pctx.str = "ext2fs_unlink"; | ||
403 | fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); | ||
404 | return 0; | ||
405 | } | ||
406 | dirinfo = e2fsck_get_dir_info(ctx, ino); | ||
407 | if (dirinfo) | ||
408 | dirinfo->parent = 0; | ||
409 | e2fsck_adjust_inode_count(ctx, ino, -1); | ||
410 | } else if (retval != EXT2_ET_FILE_NOT_FOUND) { | ||
411 | pctx.errcode = retval; | ||
412 | fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx); | ||
413 | } | ||
414 | if (!fix_problem(ctx, PR_3_NO_LF_DIR, 0)) | ||
415 | return 0; | ||
416 | |||
417 | /* | ||
418 | * Read the inode and block bitmaps in; we'll be messing with | ||
419 | * them. | ||
420 | */ | ||
421 | e2fsck_read_bitmaps(ctx); | ||
422 | |||
423 | /* | ||
424 | * First, find a free block | ||
425 | */ | ||
426 | retval = ext2fs_new_block(fs, 0, ctx->block_found_map, &blk); | ||
427 | if (retval) { | ||
428 | pctx.errcode = retval; | ||
429 | fix_problem(ctx, PR_3_ERR_LPF_NEW_BLOCK, &pctx); | ||
430 | return 0; | ||
431 | } | ||
432 | ext2fs_mark_block_bitmap(ctx->block_found_map, blk); | ||
433 | ext2fs_block_alloc_stats(fs, blk, +1); | ||
434 | |||
435 | /* | ||
436 | * Next find a free inode. | ||
437 | */ | ||
438 | retval = ext2fs_new_inode(fs, EXT2_ROOT_INO, 040700, | ||
439 | ctx->inode_used_map, &ino); | ||
440 | if (retval) { | ||
441 | pctx.errcode = retval; | ||
442 | fix_problem(ctx, PR_3_ERR_LPF_NEW_INODE, &pctx); | ||
443 | return 0; | ||
444 | } | ||
445 | ext2fs_mark_inode_bitmap(ctx->inode_used_map, ino); | ||
446 | ext2fs_mark_inode_bitmap(ctx->inode_dir_map, ino); | ||
447 | ext2fs_inode_alloc_stats2(fs, ino, +1, 1); | ||
448 | |||
449 | /* | ||
450 | * Now let's create the actual data block for the inode | ||
451 | */ | ||
452 | retval = ext2fs_new_dir_block(fs, ino, EXT2_ROOT_INO, &block); | ||
453 | if (retval) { | ||
454 | pctx.errcode = retval; | ||
455 | fix_problem(ctx, PR_3_ERR_LPF_NEW_DIR_BLOCK, &pctx); | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | retval = ext2fs_write_dir_block(fs, blk, block); | ||
460 | ext2fs_free_mem(&block); | ||
461 | if (retval) { | ||
462 | pctx.errcode = retval; | ||
463 | fix_problem(ctx, PR_3_ERR_LPF_WRITE_BLOCK, &pctx); | ||
464 | return 0; | ||
465 | } | ||
466 | |||
467 | /* | ||
468 | * Set up the inode structure | ||
469 | */ | ||
470 | memset(&inode, 0, sizeof(inode)); | ||
471 | inode.i_mode = 040700; | ||
472 | inode.i_size = fs->blocksize; | ||
473 | inode.i_atime = inode.i_ctime = inode.i_mtime = time(0); | ||
474 | inode.i_links_count = 2; | ||
475 | inode.i_blocks = fs->blocksize / 512; | ||
476 | inode.i_block[0] = blk; | ||
477 | |||
478 | /* | ||
479 | * Next, write out the inode. | ||
480 | */ | ||
481 | pctx.errcode = ext2fs_write_new_inode(fs, ino, &inode); | ||
482 | if (pctx.errcode) { | ||
483 | pctx.str = "ext2fs_write_inode"; | ||
484 | fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); | ||
485 | return 0; | ||
486 | } | ||
487 | /* | ||
488 | * Finally, create the directory link | ||
489 | */ | ||
490 | pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR); | ||
491 | if (pctx.errcode) { | ||
492 | pctx.str = "ext2fs_link"; | ||
493 | fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx); | ||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * Miscellaneous bookkeeping that needs to be kept straight. | ||
499 | */ | ||
500 | e2fsck_add_dir_info(ctx, ino, EXT2_ROOT_INO); | ||
501 | e2fsck_adjust_inode_count(ctx, EXT2_ROOT_INO, 1); | ||
502 | ext2fs_icount_store(ctx->inode_count, ino, 2); | ||
503 | ext2fs_icount_store(ctx->inode_link_info, ino, 2); | ||
504 | ctx->lost_and_found = ino; | ||
505 | #if 0 | ||
506 | printf("/lost+found created; inode #%lu\n", ino); | ||
507 | #endif | ||
508 | return ino; | ||
509 | } | ||
510 | |||
511 | /* | ||
512 | * This routine will connect a file to lost+found | ||
513 | */ | ||
514 | int e2fsck_reconnect_file(e2fsck_t ctx, ext2_ino_t ino) | ||
515 | { | ||
516 | ext2_filsys fs = ctx->fs; | ||
517 | errcode_t retval; | ||
518 | char name[80]; | ||
519 | struct problem_context pctx; | ||
520 | struct ext2_inode inode; | ||
521 | int file_type = 0; | ||
522 | |||
523 | clear_problem_context(&pctx); | ||
524 | pctx.ino = ino; | ||
525 | |||
526 | if (!ctx->bad_lost_and_found && !ctx->lost_and_found) { | ||
527 | if (e2fsck_get_lost_and_found(ctx, 1) == 0) | ||
528 | ctx->bad_lost_and_found++; | ||
529 | } | ||
530 | if (ctx->bad_lost_and_found) { | ||
531 | fix_problem(ctx, PR_3_NO_LPF, &pctx); | ||
532 | return 1; | ||
533 | } | ||
534 | |||
535 | sprintf(name, "#%u", ino); | ||
536 | if (ext2fs_read_inode(fs, ino, &inode) == 0) | ||
537 | file_type = ext2_file_type(inode.i_mode); | ||
538 | retval = ext2fs_link(fs, ctx->lost_and_found, name, ino, file_type); | ||
539 | if (retval == EXT2_ET_DIR_NO_SPACE) { | ||
540 | if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx)) | ||
541 | return 1; | ||
542 | retval = e2fsck_expand_directory(ctx, ctx->lost_and_found, | ||
543 | 1, 0); | ||
544 | if (retval) { | ||
545 | pctx.errcode = retval; | ||
546 | fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx); | ||
547 | return 1; | ||
548 | } | ||
549 | retval = ext2fs_link(fs, ctx->lost_and_found, name, | ||
550 | ino, file_type); | ||
551 | } | ||
552 | if (retval) { | ||
553 | pctx.errcode = retval; | ||
554 | fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx); | ||
555 | return 1; | ||
556 | } | ||
557 | e2fsck_adjust_inode_count(ctx, ino, 1); | ||
558 | |||
559 | return 0; | ||
560 | } | ||
561 | |||
562 | /* | ||
563 | * Utility routine to adjust the inode counts on an inode. | ||
564 | */ | ||
565 | errcode_t e2fsck_adjust_inode_count(e2fsck_t ctx, ext2_ino_t ino, int adj) | ||
566 | { | ||
567 | ext2_filsys fs = ctx->fs; | ||
568 | errcode_t retval; | ||
569 | struct ext2_inode inode; | ||
570 | |||
571 | if (!ino) | ||
572 | return 0; | ||
573 | |||
574 | retval = ext2fs_read_inode(fs, ino, &inode); | ||
575 | if (retval) | ||
576 | return retval; | ||
577 | |||
578 | #if 0 | ||
579 | printf("Adjusting link count for inode %lu by %d (from %d)\n", ino, adj, | ||
580 | inode.i_links_count); | ||
581 | #endif | ||
582 | |||
583 | if (adj == 1) { | ||
584 | ext2fs_icount_increment(ctx->inode_count, ino, 0); | ||
585 | if (inode.i_links_count == (__u16) ~0) | ||
586 | return 0; | ||
587 | ext2fs_icount_increment(ctx->inode_link_info, ino, 0); | ||
588 | inode.i_links_count++; | ||
589 | } else if (adj == -1) { | ||
590 | ext2fs_icount_decrement(ctx->inode_count, ino, 0); | ||
591 | if (inode.i_links_count == 0) | ||
592 | return 0; | ||
593 | ext2fs_icount_decrement(ctx->inode_link_info, ino, 0); | ||
594 | inode.i_links_count--; | ||
595 | } | ||
596 | |||
597 | retval = ext2fs_write_inode(fs, ino, &inode); | ||
598 | if (retval) | ||
599 | return retval; | ||
600 | |||
601 | return 0; | ||
602 | } | ||
603 | |||
604 | /* | ||
605 | * Fix parent --- this routine fixes up the parent of a directory. | ||
606 | */ | ||
607 | struct fix_dotdot_struct { | ||
608 | ext2_filsys fs; | ||
609 | ext2_ino_t parent; | ||
610 | int done; | ||
611 | e2fsck_t ctx; | ||
612 | }; | ||
613 | |||
614 | static int fix_dotdot_proc(struct ext2_dir_entry *dirent, | ||
615 | int offset EXT2FS_ATTR((unused)), | ||
616 | int blocksize EXT2FS_ATTR((unused)), | ||
617 | char *buf EXT2FS_ATTR((unused)), | ||
618 | void *priv_data) | ||
619 | { | ||
620 | struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data; | ||
621 | errcode_t retval; | ||
622 | struct problem_context pctx; | ||
623 | |||
624 | if ((dirent->name_len & 0xFF) != 2) | ||
625 | return 0; | ||
626 | if (strncmp(dirent->name, "..", 2)) | ||
627 | return 0; | ||
628 | |||
629 | clear_problem_context(&pctx); | ||
630 | |||
631 | retval = e2fsck_adjust_inode_count(fp->ctx, dirent->inode, -1); | ||
632 | if (retval) { | ||
633 | pctx.errcode = retval; | ||
634 | fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); | ||
635 | } | ||
636 | retval = e2fsck_adjust_inode_count(fp->ctx, fp->parent, 1); | ||
637 | if (retval) { | ||
638 | pctx.errcode = retval; | ||
639 | fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx); | ||
640 | } | ||
641 | dirent->inode = fp->parent; | ||
642 | |||
643 | fp->done++; | ||
644 | return DIRENT_ABORT | DIRENT_CHANGED; | ||
645 | } | ||
646 | |||
647 | static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ext2_ino_t parent) | ||
648 | { | ||
649 | ext2_filsys fs = ctx->fs; | ||
650 | errcode_t retval; | ||
651 | struct fix_dotdot_struct fp; | ||
652 | struct problem_context pctx; | ||
653 | |||
654 | fp.fs = fs; | ||
655 | fp.parent = parent; | ||
656 | fp.done = 0; | ||
657 | fp.ctx = ctx; | ||
658 | |||
659 | #if 0 | ||
660 | printf("Fixing '..' of inode %lu to be %lu...\n", dir->ino, parent); | ||
661 | #endif | ||
662 | |||
663 | retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY, | ||
664 | 0, fix_dotdot_proc, &fp); | ||
665 | if (retval || !fp.done) { | ||
666 | clear_problem_context(&pctx); | ||
667 | pctx.ino = dir->ino; | ||
668 | pctx.errcode = retval; | ||
669 | fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR : | ||
670 | PR_3_FIX_PARENT_NOFIND, &pctx); | ||
671 | ext2fs_unmark_valid(fs); | ||
672 | } | ||
673 | dir->dotdot = parent; | ||
674 | |||
675 | return; | ||
676 | } | ||
677 | |||
678 | /* | ||
679 | * These routines are responsible for expanding a /lost+found if it is | ||
680 | * too small. | ||
681 | */ | ||
682 | |||
683 | struct expand_dir_struct { | ||
684 | int num; | ||
685 | int guaranteed_size; | ||
686 | int newblocks; | ||
687 | int last_block; | ||
688 | errcode_t err; | ||
689 | e2fsck_t ctx; | ||
690 | }; | ||
691 | |||
692 | static int expand_dir_proc(ext2_filsys fs, | ||
693 | blk_t *blocknr, | ||
694 | e2_blkcnt_t blockcnt, | ||
695 | blk_t ref_block EXT2FS_ATTR((unused)), | ||
696 | int ref_offset EXT2FS_ATTR((unused)), | ||
697 | void *priv_data) | ||
698 | { | ||
699 | struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; | ||
700 | blk_t new_blk; | ||
701 | static blk_t last_blk = 0; | ||
702 | char *block; | ||
703 | errcode_t retval; | ||
704 | e2fsck_t ctx; | ||
705 | |||
706 | ctx = es->ctx; | ||
707 | |||
708 | if (es->guaranteed_size && blockcnt >= es->guaranteed_size) | ||
709 | return BLOCK_ABORT; | ||
710 | |||
711 | if (blockcnt > 0) | ||
712 | es->last_block = blockcnt; | ||
713 | if (*blocknr) { | ||
714 | last_blk = *blocknr; | ||
715 | return 0; | ||
716 | } | ||
717 | retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map, | ||
718 | &new_blk); | ||
719 | if (retval) { | ||
720 | es->err = retval; | ||
721 | return BLOCK_ABORT; | ||
722 | } | ||
723 | if (blockcnt > 0) { | ||
724 | retval = ext2fs_new_dir_block(fs, 0, 0, &block); | ||
725 | if (retval) { | ||
726 | es->err = retval; | ||
727 | return BLOCK_ABORT; | ||
728 | } | ||
729 | es->num--; | ||
730 | retval = ext2fs_write_dir_block(fs, new_blk, block); | ||
731 | } else { | ||
732 | retval = ext2fs_get_mem(fs->blocksize, &block); | ||
733 | if (retval) { | ||
734 | es->err = retval; | ||
735 | return BLOCK_ABORT; | ||
736 | } | ||
737 | memset(block, 0, fs->blocksize); | ||
738 | retval = io_channel_write_blk(fs->io, new_blk, 1, block); | ||
739 | } | ||
740 | if (retval) { | ||
741 | es->err = retval; | ||
742 | return BLOCK_ABORT; | ||
743 | } | ||
744 | ext2fs_free_mem(&block); | ||
745 | *blocknr = new_blk; | ||
746 | ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk); | ||
747 | ext2fs_block_alloc_stats(fs, new_blk, +1); | ||
748 | es->newblocks++; | ||
749 | |||
750 | if (es->num == 0) | ||
751 | return (BLOCK_CHANGED | BLOCK_ABORT); | ||
752 | else | ||
753 | return BLOCK_CHANGED; | ||
754 | } | ||
755 | |||
756 | errcode_t e2fsck_expand_directory(e2fsck_t ctx, ext2_ino_t dir, | ||
757 | int num, int guaranteed_size) | ||
758 | { | ||
759 | ext2_filsys fs = ctx->fs; | ||
760 | errcode_t retval; | ||
761 | struct expand_dir_struct es; | ||
762 | struct ext2_inode inode; | ||
763 | |||
764 | if (!(fs->flags & EXT2_FLAG_RW)) | ||
765 | return EXT2_ET_RO_FILSYS; | ||
766 | |||
767 | /* | ||
768 | * Read the inode and block bitmaps in; we'll be messing with | ||
769 | * them. | ||
770 | */ | ||
771 | e2fsck_read_bitmaps(ctx); | ||
772 | |||
773 | retval = ext2fs_check_directory(fs, dir); | ||
774 | if (retval) | ||
775 | return retval; | ||
776 | |||
777 | es.num = num; | ||
778 | es.guaranteed_size = guaranteed_size; | ||
779 | es.last_block = 0; | ||
780 | es.err = 0; | ||
781 | es.newblocks = 0; | ||
782 | es.ctx = ctx; | ||
783 | |||
784 | retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, | ||
785 | 0, expand_dir_proc, &es); | ||
786 | |||
787 | if (es.err) | ||
788 | return es.err; | ||
789 | |||
790 | /* | ||
791 | * Update the size and block count fields in the inode. | ||
792 | */ | ||
793 | retval = ext2fs_read_inode(fs, dir, &inode); | ||
794 | if (retval) | ||
795 | return retval; | ||
796 | |||
797 | inode.i_size = (es.last_block + 1) * fs->blocksize; | ||
798 | inode.i_blocks += (fs->blocksize / 512) * es.newblocks; | ||
799 | |||
800 | e2fsck_write_inode(ctx, dir, &inode, "expand_directory"); | ||
801 | |||
802 | return 0; | ||
803 | } | ||
804 | |||