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