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