]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blame - e2fsck/pass3.c
ChangeLog, expect:
[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 85 pctx.errcode = ext2fs_allocate_inode_bitmap(fs,
0c4a0726 86 _("inode loop detection bitmap"), &inode_loop_detect);
1b6bf175
TT
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 }
0c4a0726 93 pctx.errcode = ext2fs_allocate_inode_bitmap(fs, _("inode done bitmap"),
1b6bf175
TT
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);
0c4a0726 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);
0c4a0726 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 */
6fdc7a32 454 pctx.errcode = ext2fs_link(fs, EXT2_ROOT_INO, name, ino, EXT2_FT_DIR);
1b6bf175
TT
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 */
6fdc7a32 477int e2fsck_reconnect_file(e2fsck_t ctx, ino_t ino)
3839e657 478{
1b6bf175 479 ext2_filsys fs = ctx->fs;
3839e657
TT
480 errcode_t retval;
481 char name[80];
1b6bf175 482 struct problem_context pctx;
6fdc7a32
TT
483 struct ext2_inode inode;
484 int file_type = 0;
1b6bf175
TT
485
486 clear_problem_context(&pctx);
6fdc7a32 487 pctx.ino = ino;
1b6bf175
TT
488
489 if (!bad_lost_and_found && !lost_and_found) {
490 lost_and_found = get_lost_and_found(ctx);
491 if (!lost_and_found)
492 bad_lost_and_found++;
493 }
3839e657 494 if (bad_lost_and_found) {
1b6bf175 495 fix_problem(ctx, PR_3_NO_LPF, &pctx);
3839e657
TT
496 return 1;
497 }
1b6bf175 498
6fdc7a32
TT
499 sprintf(name, "#%lu", ino);
500 if (ext2fs_read_inode(fs, ino, &inode) == 0)
501 file_type = ext2_file_type(inode.i_mode);
502 retval = ext2fs_link(fs, lost_and_found, name, ino, file_type);
3839e657 503 if (retval == EXT2_ET_DIR_NO_SPACE) {
1b6bf175 504 if (!fix_problem(ctx, PR_3_EXPAND_LF_DIR, &pctx))
3839e657 505 return 1;
1b6bf175 506 retval = expand_directory(ctx, lost_and_found);
3839e657 507 if (retval) {
1b6bf175
TT
508 pctx.errcode = retval;
509 fix_problem(ctx, PR_3_CANT_EXPAND_LPF, &pctx);
3839e657
TT
510 return 1;
511 }
6fdc7a32 512 retval = ext2fs_link(fs, lost_and_found, name, ino, file_type);
3839e657
TT
513 }
514 if (retval) {
1b6bf175
TT
515 pctx.errcode = retval;
516 fix_problem(ctx, PR_3_CANT_RECONNECT, &pctx);
3839e657
TT
517 return 1;
518 }
6fdc7a32 519 adjust_inode_count(ctx, ino, +1);
3839e657
TT
520
521 return 0;
522}
523
524/*
525 * Utility routine to adjust the inode counts on an inode.
526 */
1b6bf175 527static errcode_t adjust_inode_count(e2fsck_t ctx, ino_t ino, int adj)
3839e657 528{
1b6bf175 529 ext2_filsys fs = ctx->fs;
3839e657
TT
530 errcode_t retval;
531 struct ext2_inode inode;
532
533 if (!ino)
534 return 0;
535
536 retval = ext2fs_read_inode(fs, ino, &inode);
537 if (retval)
538 return retval;
539
540#if 0
f3db3566 541 printf("Adjusting link count for inode %lu by %d (from %d)\n", ino, adj,
3839e657
TT
542 inode.i_links_count);
543#endif
544
21c84b71 545 if (adj == 1) {
1b6bf175 546 ext2fs_icount_increment(ctx->inode_count, ino, 0);
c1faf9cc
TT
547 if (inode.i_links_count == (__u16) ~0)
548 return 0;
1b6bf175 549 ext2fs_icount_increment(ctx->inode_link_info, ino, 0);
c1faf9cc
TT
550 inode.i_links_count++;
551 } else if (adj == -1) {
1b6bf175 552 ext2fs_icount_decrement(ctx->inode_count, ino, 0);
c1faf9cc
TT
553 if (inode.i_links_count == 0)
554 return 0;
1b6bf175 555 ext2fs_icount_decrement(ctx->inode_link_info, ino, 0);
c1faf9cc
TT
556 inode.i_links_count--;
557 } else {
558 /* Should never happen */
0c4a0726
TT
559 printf(_("Debug error in e2fsck adjust_inode_count, "
560 "should never happen.\n"));
c1faf9cc 561 exit(1);
21c84b71
TT
562 }
563
3839e657
TT
564 retval = ext2fs_write_inode(fs, ino, &inode);
565 if (retval)
566 return retval;
567
568 return 0;
569}
570
571/*
572 * Fix parent --- this routine fixes up the parent of a directory.
573 */
574struct fix_dotdot_struct {
575 ext2_filsys fs;
576 ino_t parent;
577 int done;
1b6bf175 578 e2fsck_t ctx;
3839e657
TT
579};
580
581static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
582 int offset,
583 int blocksize,
584 char *buf,
54dc7ca2 585 void *priv_data)
3839e657 586{
54dc7ca2 587 struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
3839e657 588 errcode_t retval;
1b6bf175 589 struct problem_context pctx;
3839e657 590
b6f79831 591 if ((dirent->name_len & 0xFF) != 2)
3839e657
TT
592 return 0;
593 if (strncmp(dirent->name, "..", 2))
594 return 0;
3839e657 595
1b6bf175
TT
596 clear_problem_context(&pctx);
597
598 retval = adjust_inode_count(fp->ctx, dirent->inode, -1);
599 if (retval) {
600 pctx.errcode = retval;
601 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
602 }
603 retval = adjust_inode_count(fp->ctx, fp->parent, 1);
604 if (retval) {
605 pctx.errcode = retval;
606 fix_problem(fp->ctx, PR_3_ADJUST_INODE, &pctx);
607 }
3839e657
TT
608 dirent->inode = fp->parent;
609
610 fp->done++;
611 return DIRENT_ABORT | DIRENT_CHANGED;
612}
613
1b6bf175 614static void fix_dotdot(e2fsck_t ctx, struct dir_info *dir, ino_t parent)
3839e657 615{
1b6bf175 616 ext2_filsys fs = ctx->fs;
3839e657
TT
617 errcode_t retval;
618 struct fix_dotdot_struct fp;
1b6bf175 619 struct problem_context pctx;
3839e657
TT
620
621 fp.fs = fs;
622 fp.parent = parent;
623 fp.done = 0;
1b6bf175 624 fp.ctx = ctx;
3839e657
TT
625
626#if 0
f3db3566 627 printf("Fixing '..' of inode %lu to be %lu...\n", dir->ino, parent);
3839e657
TT
628#endif
629
630 retval = ext2fs_dir_iterate(fs, dir->ino, DIRENT_FLAG_INCLUDE_EMPTY,
631 0, fix_dotdot_proc, &fp);
632 if (retval || !fp.done) {
1b6bf175
TT
633 clear_problem_context(&pctx);
634 pctx.ino = dir->ino;
635 pctx.errcode = retval;
636 fix_problem(ctx, retval ? PR_3_FIX_PARENT_ERR :
637 PR_3_FIX_PARENT_NOFIND, &pctx);
3839e657
TT
638 ext2fs_unmark_valid(fs);
639 }
640 dir->dotdot = parent;
641
642 return;
643}
644
645/*
646 * These routines are responsible for expanding a /lost+found if it is
647 * too small.
648 */
649
650struct expand_dir_struct {
c1faf9cc
TT
651 int done;
652 int newblocks;
653 errcode_t err;
654 e2fsck_t ctx;
3839e657
TT
655};
656
657static int expand_dir_proc(ext2_filsys fs,
658 blk_t *blocknr,
659 int blockcnt,
54dc7ca2 660 void *priv_data)
3839e657 661{
54dc7ca2 662 struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
3839e657
TT
663 blk_t new_blk;
664 static blk_t last_blk = 0;
665 char *block;
666 errcode_t retval;
1b6bf175
TT
667 e2fsck_t ctx;
668
669 ctx = es->ctx;
3839e657
TT
670
671 if (*blocknr) {
672 last_blk = *blocknr;
673 return 0;
674 }
1b6bf175
TT
675 retval = ext2fs_new_block(fs, last_blk, ctx->block_found_map,
676 &new_blk);
3839e657
TT
677 if (retval) {
678 es->err = retval;
679 return BLOCK_ABORT;
680 }
681 if (blockcnt > 0) {
682 retval = ext2fs_new_dir_block(fs, 0, 0, &block);
683 if (retval) {
684 es->err = retval;
685 return BLOCK_ABORT;
686 }
687 es->done = 1;
b8647faa 688 retval = ext2fs_write_dir_block(fs, new_blk, block);
3839e657 689 } else {
08b21301
TT
690 retval = ext2fs_get_mem(fs->blocksize, (void **) &block);
691 if (retval) {
692 es->err = retval;
3839e657
TT
693 return BLOCK_ABORT;
694 }
695 memset(block, 0, fs->blocksize);
b8647faa 696 retval = io_channel_write_blk(fs->io, new_blk, 1, block);
3839e657 697 }
3839e657
TT
698 if (retval) {
699 es->err = retval;
700 return BLOCK_ABORT;
701 }
08b21301 702 ext2fs_free_mem((void **) &block);
3839e657 703 *blocknr = new_blk;
1b6bf175 704 ext2fs_mark_block_bitmap(ctx->block_found_map, new_blk);
f3db3566 705 ext2fs_mark_block_bitmap(fs->block_map, new_blk);
3839e657 706 ext2fs_mark_bb_dirty(fs);
c1faf9cc
TT
707 es->newblocks++;
708
3839e657
TT
709 if (es->done)
710 return (BLOCK_CHANGED | BLOCK_ABORT);
711 else
712 return BLOCK_CHANGED;
713}
714
1b6bf175 715static errcode_t expand_directory(e2fsck_t ctx, ino_t dir)
3839e657 716{
1b6bf175 717 ext2_filsys fs = ctx->fs;
3839e657
TT
718 errcode_t retval;
719 struct expand_dir_struct es;
720 struct ext2_inode inode;
721
722 if (!(fs->flags & EXT2_FLAG_RW))
723 return EXT2_ET_RO_FILSYS;
724
b8647faa
TT
725 /*
726 * Read the inode and block bitmaps in; we'll be messing with
727 * them.
728 */
729 e2fsck_read_bitmaps(ctx);
c1faf9cc 730
3839e657
TT
731 retval = ext2fs_check_directory(fs, dir);
732 if (retval)
733 return retval;
734
735 es.done = 0;
736 es.err = 0;
c1faf9cc 737 es.newblocks = 0;
1b6bf175 738 es.ctx = ctx;
3839e657
TT
739
740 retval = ext2fs_block_iterate(fs, dir, BLOCK_FLAG_APPEND,
741 0, expand_dir_proc, &es);
742
743 if (es.err)
744 return es.err;
745 if (!es.done)
746 return EXT2_ET_EXPAND_DIR_ERR;
747
748 /*
749 * Update the size and block count fields in the inode.
750 */
751 retval = ext2fs_read_inode(fs, dir, &inode);
752 if (retval)
753 return retval;
754
755 inode.i_size += fs->blocksize;
c1faf9cc 756 inode.i_blocks += (fs->blocksize / 512) * es.newblocks;
3839e657 757
08b21301 758 e2fsck_write_inode(ctx, dir, &inode, "expand_directory");
3839e657
TT
759
760 return 0;
761}