Whamcloud - gitweb
ChangeLog:
[tools/e2fsprogs.git] / e2fsck / pass3.c
index 9af3be7..2e4bfe2 100644 (file)
@@ -52,8 +52,8 @@ static errcode_t expand_directory(e2fsck_t ctx, ino_t dir);
 static ino_t lost_and_found = 0;
 static int bad_lost_and_found = 0;
 
-static ext2fs_inode_bitmap inode_loop_detect;
-static ext2fs_inode_bitmap inode_done_map;
+static ext2fs_inode_bitmap inode_loop_detect = 0;
+static ext2fs_inode_bitmap inode_done_map = 0;
        
 void e2fsck_pass3(e2fsck_t ctx)
 {
@@ -64,7 +64,7 @@ void e2fsck_pass3(e2fsck_t ctx)
 #endif
        struct problem_context  pctx;
        struct dir_info *dir;
-       unsigned long max, count;
+       unsigned long maxdirs, count;
 
 #ifdef RESOURCE_TRACK
        init_resource_track(&rtrack);
@@ -88,7 +88,7 @@ void e2fsck_pass3(e2fsck_t ctx)
                pctx.num = 1;
                fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
                ctx->flags |= E2F_FLAG_ABORT;
-               return;
+               goto abort_exit;
        }
        pctx.errcode = ext2fs_allocate_inode_bitmap(fs, "inode done bitmap",
                                                    &inode_done_map);
@@ -96,39 +96,53 @@ void e2fsck_pass3(e2fsck_t ctx)
                pctx.num = 2;
                fix_problem(ctx, PR_3_ALLOCATE_IBITMAP_ERROR, &pctx);
                ctx->flags |= E2F_FLAG_ABORT;
-               return;
+               goto abort_exit;
        }
 #ifdef RESOURCE_TRACK
-       if (ctx->options & E2F_OPT_TIME)
+       if (ctx->options & E2F_OPT_TIME) {
+               e2fsck_clear_progbar(ctx);
                print_resource_track("Peak memory", &ctx->global_rtrack);
+       }
 #endif
 
        check_root(ctx);
-       if (ctx->flags & E2F_FLAG_ABORT)
-               return;
+       if (ctx->flags & E2F_FLAG_SIGNAL_MASK)
+               goto abort_exit;
 
        ext2fs_mark_inode_bitmap(inode_done_map, EXT2_ROOT_INO);
 
-       max = e2fsck_get_num_dirinfo(ctx);
-       count = 0;
+       maxdirs = e2fsck_get_num_dirinfo(ctx);
+       count = 1;
 
        if (ctx->progress)
-               (ctx->progress)(ctx, 3, 0, max);
+               if ((ctx->progress)(ctx, 3, 0, maxdirs))
+                       goto abort_exit;
+       
        for (i=0; (dir = e2fsck_dir_info_iter(ctx, &i)) != 0;) {
                if (ctx->progress)
-                       (ctx->progress)(ctx, 3, count++, max);
+                       if ((ctx->progress)(ctx, 3, count++, maxdirs))
+                               goto abort_exit;
                if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, dir->ino))
                        check_directory(ctx, dir, &pctx);
        }
-       if (ctx->progress)
-               (ctx->progress)(ctx, 3, max, max);
-       
+
+       /*
+        * Force the creation of /lost+found if not present
+        */
+       if ((ctx->flags & E2F_OPT_READONLY) == 0)
+               get_lost_and_found(ctx);
+
+abort_exit:
        e2fsck_free_dir_info(ctx);
-       ext2fs_free_inode_bitmap(inode_loop_detect);
-       ext2fs_free_inode_bitmap(inode_done_map);
+       if (inode_loop_detect)
+               ext2fs_free_inode_bitmap(inode_loop_detect);
+       if (inode_done_map)
+               ext2fs_free_inode_bitmap(inode_done_map);
 #ifdef RESOURCE_TRACK
-       if (ctx->options & E2F_OPT_TIME2)
+       if (ctx->options & E2F_OPT_TIME2) {
+               e2fsck_clear_progbar(ctx);
                print_resource_track("Pass 3", &rtrack);
+       }
 #endif
 }
 
@@ -252,17 +266,19 @@ static void check_directory(e2fsck_t ctx, struct dir_info *dir,
        ext2_filsys fs = ctx->fs;
        struct dir_info *p = dir;
 
+       if (!p)
+               return;
+
        ext2fs_clear_inode_bitmap(inode_loop_detect);
-       while (p) {
-               /*
-                * If we find a parent which we've already checked,
-                * then stop; we know it's either already connected to
-                * the directory tree, or it isn't but the user has
-                * already told us he doesn't want us to reconnect the
-                * disconnected subtree.
-                */
-               if (ext2fs_test_inode_bitmap(inode_done_map, p->ino))
-                       goto check_dot_dot;
+
+       /*
+        * Keep going until we find a parent which we've already
+        * checked.  We know it's either already connected to the
+        * directory tree, or it isn't but the user has already told
+        * us he doesn't want us to reconnect the disconnected
+        * subtree.
+        */
+       while (!ext2fs_test_inode_bitmap(inode_done_map, p->ino)) {
                /*
                 * Mark this inode as being "done"; by the time we
                 * return from this function, the inode we either be
@@ -271,6 +287,7 @@ static void check_directory(e2fsck_t ctx, struct dir_info *dir,
                 * lost+found.
                 */
                ext2fs_mark_inode_bitmap(inode_done_map, p->ino);
+
                /*
                 * If this directory doesn't have a parent, or we've
                 * seen the parent once already, then offer to
@@ -278,23 +295,25 @@ static void check_directory(e2fsck_t ctx, struct dir_info *dir,
                 */
                if (!p->parent ||
                    (ext2fs_test_inode_bitmap(inode_loop_detect,
-                                             p->parent)))
+                                             p->parent))) {
+                       pctx->ino = p->ino;
+                       if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
+                               if (e2fsck_reconnect_file(ctx, p->ino))
+                                       ext2fs_unmark_valid(fs);
+                               else {
+                                       p->parent = lost_and_found;
+                                       fix_dotdot(ctx, p, lost_and_found);
+                               }
+                       }
                        break;
+               }
                ext2fs_mark_inode_bitmap(inode_loop_detect,
                                         p->parent);
+               pctx->ino = p->parent;
                p = e2fsck_get_dir_info(ctx, p->parent);
-       }
-       /*
-        * If we've reached here, we've hit a detached directory
-        * inode; offer to reconnect it to lost+found.
-        */
-       pctx->ino = p->ino;
-       if (fix_problem(ctx, PR_3_UNCONNECTED_DIR, pctx)) {
-               if (e2fsck_reconnect_file(ctx, p->ino))
-                       ext2fs_unmark_valid(fs);
-               else {
-                       p->parent = lost_and_found;
-                       fix_dotdot(ctx, p, lost_and_found);
+               if (!p) {
+                       fix_problem(ctx, PR_3_NO_DIRINFO, pctx);
+                       return;
                }
        }
 
@@ -302,7 +321,6 @@ static void check_directory(e2fsck_t ctx, struct dir_info *dir,
         * Make sure that .. and the parent directory are the same;
         * offer to fix it if not.
         */
-check_dot_dot:
        if (dir->parent != dir->dotdot) {
                pctx->ino = dir->ino;
                pctx->ino2 = dir->dotdot;
@@ -326,14 +344,32 @@ ino_t get_lost_and_found(e2fsck_t ctx)
        char *                  block;
        const char              name[] = "lost+found";
        struct  problem_context pctx;
+       struct dir_info         *dirinfo;
 
        clear_problem_context(&pctx);
        
        retval = ext2fs_lookup(fs, EXT2_ROOT_INO, name,
                               sizeof(name)-1, 0, &ino);
-       if (!retval)
-               return ino;
-       if (retval != EXT2_ET_FILE_NOT_FOUND) {
+       if (!retval) {
+               if (ext2fs_test_inode_bitmap(ctx->inode_dir_map, ino))
+                       return ino;
+               /* Lost+found isn't a directory! */
+               pctx.ino = ino;
+               if (!fix_problem(ctx, PR_3_LPF_NOTDIR, &pctx))
+                       return 0;
+
+               /* OK, unlink the old /lost+found file. */
+               pctx.errcode = ext2fs_unlink(fs, EXT2_ROOT_INO, name, ino, 0);
+               if (pctx.errcode) {
+                       pctx.str = "ext2fs_unlink";
+                       fix_problem(ctx, PR_3_CREATE_LPF_ERROR, &pctx);
+                       return 0;
+               }
+               dirinfo = e2fsck_get_dir_info(ctx, ino);
+               if (dirinfo)
+                       dirinfo->parent = 0;
+               adjust_inode_count(ctx, ino, -1);
+       } else if (retval != EXT2_ET_FILE_NOT_FOUND) {
                pctx.errcode = retval;
                fix_problem(ctx, PR_3_ERR_FIND_LPF, &pctx);
        }
@@ -533,13 +569,13 @@ static int fix_dotdot_proc(struct ext2_dir_entry *dirent,
                           int  offset,
                           int  blocksize,
                           char *buf,
-                          void *private)
+                          void *priv_data)
 {
-       struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) private;
+       struct fix_dotdot_struct *fp = (struct fix_dotdot_struct *) priv_data;
        errcode_t       retval;
        struct problem_context pctx;
 
-       if (dirent->name_len != 2)
+       if ((dirent->name_len & 0xFF) != 2)
                return 0;
        if (strncmp(dirent->name, "..", 2))
                return 0;
@@ -607,9 +643,9 @@ struct expand_dir_struct {
 static int expand_dir_proc(ext2_filsys fs,
                           blk_t        *blocknr,
                           int  blockcnt,
-                          void *private)
+                          void *priv_data)
 {
-       struct expand_dir_struct *es = (struct expand_dir_struct *) private;
+       struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data;
        blk_t   new_blk;
        static blk_t    last_blk = 0;
        char            *block;
@@ -635,6 +671,7 @@ static int expand_dir_proc(ext2_filsys fs,
                        return BLOCK_ABORT;
                }
                es->done = 1;
+               retval = ext2fs_write_dir_block(fs, new_blk, block);
        } else {
                retval = ext2fs_get_mem(fs->blocksize, (void **) &block);
                if (retval) {
@@ -642,8 +679,8 @@ static int expand_dir_proc(ext2_filsys fs,
                        return BLOCK_ABORT;
                }
                memset(block, 0, fs->blocksize);
+               retval = io_channel_write_blk(fs->io, new_blk, 1, block);
        }       
-       retval = ext2fs_write_dir_block(fs, new_blk, block);
        if (retval) {
                es->err = retval;
                return BLOCK_ABORT;
@@ -669,6 +706,12 @@ static errcode_t expand_directory(e2fsck_t ctx, ino_t dir)
        if (!(fs->flags & EXT2_FLAG_RW))
                return EXT2_ET_RO_FILSYS;
 
+       /*
+        * Read the inode and block bitmaps in; we'll be messing with
+        * them.
+        */
+       e2fsck_read_bitmaps(ctx);
+       
        retval = ext2fs_check_directory(fs, dir);
        if (retval)
                return retval;