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