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