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