Whamcloud - gitweb
Don't write changes to the backup superblocks by default
[tools/e2fsprogs.git] / e2fsck / unix.c
index 5f1aaa2..72091f8 100644 (file)
@@ -98,7 +98,8 @@ static void usage(e2fsck_t ctx)
 static void show_stats(e2fsck_t        ctx)
 {
        ext2_filsys fs = ctx->fs;
-       int inodes, inodes_used, blocks, blocks_used;
+       ext2_ino_t inodes, inodes_used;
+       blk_t blocks, blocks_used;
        int dir_links;
        int num_files, num_links;
        int frag_percent;
@@ -117,49 +118,48 @@ static void show_stats(e2fsck_t   ctx)
        frag_percent = (frag_percent + 5) / 10;
        
        if (!verbose) {
-               printf(_("%s: %d/%d files (%0d.%d%% non-contiguous), %d/%d blocks\n"),
+               printf(_("%s: %u/%u files (%0d.%d%% non-contiguous), %u/%u blocks\n"),
                       ctx->device_name, inodes_used, inodes,
                       frag_percent / 10, frag_percent % 10,
                       blocks_used, blocks);
                return;
        }
-       printf (P_("\n%8d inode used (%d%%)\n", "\n%8d inodes used (%d%%)\n",
-                  inodes_used), inodes_used, 100 * inodes_used / inodes);
-       printf (P_("%8d non-contiguous inode (%0d.%d%%)\n",
-                  "%8d non-contiguous inodes (%0d.%d%%)\n",
+       printf (P_("\n%8u inode used (%2.2f%%)\n", "\n%8u inodes used (%2.2f%%)\n",
+                  inodes_used), inodes_used, 100.0 * inodes_used / inodes);
+       printf (P_("%8u non-contiguous inode (%0d.%d%%)\n",
+                  "%8u non-contiguous inodes (%0d.%d%%)\n",
                   ctx->fs_fragmented),
                ctx->fs_fragmented, frag_percent / 10, frag_percent % 10);
-       printf (_("         # of inodes with ind/dind/tind blocks: %d/%d/%d\n"),
+       printf (_("         # of inodes with ind/dind/tind blocks: %u/%u/%u\n"),
                ctx->fs_ind_count, ctx->fs_dind_count, ctx->fs_tind_count);
-       printf (P_("%8d block used (%d%%)\n", "%8d blocks used (%d%%)\n",
-                  blocks_used),
-               blocks_used, (int) ((long long) 100 * blocks_used / blocks));
-       printf (P_("%8d bad block\n", "%8d bad blocks\n",
+       printf (P_("%8u block used (%2.2f%%)\n", "%8u blocks used (%2.2f%%)\n",
+                  blocks_used), blocks_used, 100.0 * blocks_used / blocks);
+       printf (P_("%8u bad block\n", "%8u bad blocks\n",
                   ctx->fs_badblocks_count), ctx->fs_badblocks_count);
-       printf (P_("%8d large file\n", "%8d large files\n",
+       printf (P_("%8u large file\n", "%8u large files\n",
                   ctx->large_files), ctx->large_files);
-       printf (P_("\n%8d regular file\n", "\n%8d regular files\n",
+       printf (P_("\n%8u regular file\n", "\n%8u regular files\n",
                   ctx->fs_regular_count), ctx->fs_regular_count);
-       printf (P_("%8d directory\n", "%8d directories\n",
+       printf (P_("%8u directory\n", "%8u directories\n",
                   ctx->fs_directory_count), ctx->fs_directory_count);
-       printf (P_("%8d character device file\n",
-                  "%8d character device files\n", ctx->fs_chardev_count),
+       printf (P_("%8u character device file\n",
+                  "%8u character device files\n", ctx->fs_chardev_count),
                ctx->fs_chardev_count);
-       printf (P_("%8d block device file\n", "%8d block device files\n",
+       printf (P_("%8u block device file\n", "%8u block device files\n",
                   ctx->fs_blockdev_count), ctx->fs_blockdev_count);
-       printf (P_("%8d fifo\n", "%8d fifos\n", ctx->fs_fifo_count),
+       printf (P_("%8u fifo\n", "%8u fifos\n", ctx->fs_fifo_count),
                ctx->fs_fifo_count);
-       printf (P_("%8d link\n", "%8d links\n",
+       printf (P_("%8u link\n", "%8u links\n",
                   ctx->fs_links_count - dir_links),
                ctx->fs_links_count - dir_links);
-       printf (P_("%8d symbolic link", "%8d symbolic links",
+       printf (P_("%8u symbolic link", "%8u symbolic links",
                   ctx->fs_symlinks_count), ctx->fs_symlinks_count);
-       printf (P_(" (%d fast symbolic link)\n", " (%d fast symbolic links)\n",
+       printf (P_(" (%u fast symbolic link)\n", " (%u fast symbolic links)\n",
                   ctx->fs_fast_symlinks_count), ctx->fs_fast_symlinks_count);
-       printf (P_("%8d socket\n", "%8d sockets\n", ctx->fs_sockets_count),
+       printf (P_("%8u socket\n", "%8u sockets\n", ctx->fs_sockets_count),
                ctx->fs_sockets_count);
        printf ("--------\n");
-       printf (P_("%8d file\n", "%8d files\n",
+       printf (P_("%8u file\n", "%8u files\n",
                   ctx->fs_total_count - dir_links),
                ctx->fs_total_count - dir_links);
 }
@@ -179,15 +179,18 @@ static void check_mount(e2fsck_t ctx)
        }
 
        /*
-        * If the filesystem isn't mounted, or it's the root filesystem
-        * and it's mounted read-only, then everything's fine.
+        * If the filesystem isn't mounted, or it's the root
+        * filesystem and it's mounted read-only, and we're not doing
+        * a read/write check, then everything's fine.
         */
        if ((!(ctx->mount_flags & EXT2_MF_MOUNTED)) ||
            ((ctx->mount_flags & EXT2_MF_ISROOT) &&
-            (ctx->mount_flags & EXT2_MF_READONLY)))
+            (ctx->mount_flags & EXT2_MF_READONLY) &&
+            !(ctx->options & E2F_OPT_WRITECHECK)))
                return;
 
-       if (ctx->options & E2F_OPT_READONLY) {
+       if ((ctx->options & E2F_OPT_READONLY) &&
+           !(ctx->options & E2F_OPT_WRITECHECK)) {
                printf(_("Warning!  %s is mounted.\n"), ctx->filesystem_name);
                return;
        }
@@ -256,7 +259,14 @@ static void check_if_skip(e2fsck_t ctx)
        unsigned int reason_arg = 0;
        long next_check;
        int batt = is_on_batt();
-       
+       int defer_check_on_battery;
+
+       profile_get_boolean(ctx->profile, "options",
+                           "defer_check_on_battery", 0, 1, 
+                           &defer_check_on_battery);
+       if (!defer_check_on_battery)
+               batt = 0;
+
        if ((ctx->options & E2F_OPT_FORCE) || bad_blocks_file ||
            cflag || swapfs)
                return;
@@ -289,7 +299,7 @@ static void check_if_skip(e2fsck_t ctx)
                fputs(_(", check forced.\n"), stdout);
                return;
        }
-       printf(_("%s: clean, %d/%d files, %d/%d blocks"), ctx->device_name,
+       printf(_("%s: clean, %u/%u files, %u/%u blocks"), ctx->device_name,
               fs->super->s_inodes_count - fs->super->s_free_inodes_count,
               fs->super->s_inodes_count,
               fs->super->s_blocks_count - fs->super->s_free_blocks_count,
@@ -304,9 +314,13 @@ static void check_if_skip(e2fsck_t ctx)
            ((ctx->now - fs->super->s_lastcheck) >= fs->super->s_checkinterval))
                next_check = 1;
        if (next_check <= 5) {
-               if (next_check == 1)
-                       fputs(_(" (check after next mount)"), stdout);
-               else
+               if (next_check == 1) {
+                       if (batt) 
+                               fputs(_(" (check deferred; on battery)"),
+                                     stdout);
+                       else
+                               fputs(_(" (check after next mount)"), stdout);
+               } else
                        printf(_(" (check in %ld mounts)"), next_check);
        }
        fputc('\n', stdout);
@@ -509,7 +523,7 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                if (p) {
                        *p = 0;
                        next = p+1;
-               } 
+               }
                arg = strchr(token, '=');
                if (arg) {
                        *arg = 0;
@@ -535,6 +549,8 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                        extended_usage++;
                }
        }
+       free(buf);
+
        if (extended_usage) {
                fputs(("\nExtended options are separated by commas, "
                       "and may take an argument which\n"
@@ -543,8 +559,17 @@ static void parse_extended_opts(e2fsck_t ctx, const char *opts)
                       "\tea_ver=<ea_version (1 or 2)>\n\n"), stderr);
                exit(1);
        }
-}      
+}
 
+static void syntax_err_report(const char *filename, long err, int line_num)
+{
+       fprintf(stderr, 
+               _("Syntax error in e2fsck config file (%s, line #%d)\n\t%s\n"),
+               filename, line_num, error_message(err));
+       exit(FSCK_ERROR);
+}
+
+static const char *config_fn[] = { ROOT_SYSCONFDIR "/e2fsck.conf", 0 };
 
 static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
 {
@@ -559,6 +584,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        struct sigaction        sa;
 #endif
        char            *extended_opts = 0;
+       char            *cp;
 
        retval = e2fsck_allocate_context(&ctx);
        if (retval)
@@ -576,7 +602,8 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        }
        memset(bar, '=', sizeof(bar)-1);
        memset(spaces, ' ', sizeof(spaces)-1);
-       initialize_ext2_error_table();
+       add_error_table(&et_ext2_error_table);
+       add_error_table(&et_prof_error_table);
        blkid_get_cache(&ctx->blkid, NULL);
        
        if (argc && *argv)
@@ -613,7 +640,7 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
                        if (ctx->options & (E2F_OPT_YES|E2F_OPT_NO)) {
                        conflict_opt:
                                fatal_error(ctx, 
-       _("Only one the options -p/-a, -n or -y may be specified."));
+       _("Only one of the options -p/-a, -n or -y may be specified."));
                        }
                        ctx->options |= E2F_OPT_PREEN;
                        break;
@@ -728,7 +755,12 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        }
        if (extended_opts)
                parse_extended_opts(ctx, extended_opts);
-       
+
+       if ((cp = getenv("E2FSCK_CONFIG")) != NULL)
+               config_fn[0] = cp;
+       profile_set_syntax_err_cb(syntax_err_report);
+       profile_init(config_fn, &ctx->profile);
+
        if (flush) {
                fd = open(ctx->filesystem_name, O_RDONLY, 0);
                if (fd < 0) {
@@ -780,19 +812,22 @@ static errcode_t PRS(int argc, char *argv[], e2fsck_t *ret_ctx)
        /* Update our PATH to include /sbin if we need to run badblocks  */
        if (cflag) {
                char *oldpath = getenv("PATH");
+               char *newpath;
+               int len = sizeof(PATH_SET) + 1;
+
+               if (oldpath)
+                       len += strlen(oldpath);
+
+               newpath = malloc(len);
+               if (!newpath)
+                       fatal_error(ctx, "Couldn't malloc() newpath");
+               strcpy(newpath, PATH_SET);
+
                if (oldpath) {
-                       char *newpath;
-
-                       newpath = (char *) malloc(sizeof (PATH_SET) + 1 +
-                                                 strlen (oldpath));
-                       if (!newpath)
-                               fatal_error(ctx, "Couldn't malloc() newpath");
-                       strcpy (newpath, PATH_SET);
-                       strcat (newpath, ":");
-                       strcat (newpath, oldpath);
-                       putenv (newpath);
-               } else
-                       putenv (PATH_SET);
+                       strcat(newpath, ":");
+                       strcat(newpath, oldpath);
+               }
+               putenv(newpath);
        }
 #ifdef CONFIG_JBD_DEBUG
        if (getenv("E2FSCK_JBD_DEBUG"))
@@ -880,6 +915,8 @@ restart:
        flags = 0;
        if ((ctx->options & E2F_OPT_READONLY) == 0)
                flags |= EXT2_FLAG_RW;
+       if ((ctx->mount_flags & EXT2_MF_MOUNTED) == 0)
+               flags |= EXT2_FLAG_EXCLUSIVE;
 
        if (ctx->superblock && ctx->blocksize) {
                retval = ext2fs_open2(ctx->filesystem_name, ctx->io_options, 
@@ -931,6 +968,9 @@ restart:
                               "r/o" : "r/w");
                else if (retval == ENXIO)
                        printf(_("Possibly non-existent or swap device?\n"));
+               else if (retval == EBUSY)
+                       printf(_("Filesystem mounted or opened exclusively "
+                                "by another program?\n"));
 #ifdef EROFS
                else if (retval == EROFS)
                        printf(_("Disk write-protected; use the -n option "
@@ -941,6 +981,54 @@ restart:
                        fix_problem(ctx, PR_0_SB_CORRUPT, &pctx);
                fatal_error(ctx, 0);
        }
+       /*
+        * We only update the master superblock because (a) paranoia;
+        * we don't want to corrupt the backup superblocks, and (b) we
+        * don't need to update the mount count and last checked
+        * fields in the backup superblock (the kernel doesn't update
+        * the backup superblocks anyway).  With newer versions of the
+        * library this flag is set by ext2fs_open2(), but we set this
+        * here just to be sure.  (No, we don't support e2fsck running
+        * with some other libext2fs than the one that it was shipped
+        * with, but just in case....)
+        */
+       fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
+
+       if (!(ctx->flags & E2F_FLAG_GOT_DEVSIZE)) {
+               __u32 blocksize = EXT2_BLOCK_SIZE(fs->super);
+               int need_restart = 0;
+
+               pctx.errcode = ext2fs_get_device_size(ctx->filesystem_name,
+                                                     blocksize,
+                                                     &ctx->num_blocks);
+               /*
+                * The floppy driver refuses to allow anyone else to
+                * open the device if has been opened with O_EXCL;
+                * this is unlike other block device drivers in Linux.
+                * To handle this, we close the filesystem and then
+                * reopen the filesystem after we get the device size.
+                */
+               if (pctx.errcode == EBUSY) {
+                       ext2fs_close(fs);
+                       need_restart++;
+                       pctx.errcode = 
+                               ext2fs_get_device_size(ctx->filesystem_name, 
+                                                      blocksize,
+                                                      &ctx->num_blocks);
+               }
+               if (pctx.errcode == EXT2_ET_UNIMPLEMENTED)
+                       ctx->num_blocks = 0;
+               else if (pctx.errcode) {
+                       fix_problem(ctx, PR_0_GETSIZE_ERROR, &pctx);
+                       ctx->flags |= E2F_FLAG_ABORT;
+                       fatal_error(ctx, 0);
+                       return;
+               }
+               ctx->flags |= E2F_FLAG_GOT_DEVSIZE;
+               if (need_restart)
+                       goto restart;
+       }
+
        ctx->fs = fs;
        fs->priv_data = ctx;
        fs->now = ctx->now;
@@ -1051,15 +1139,6 @@ restart:
            !(ctx->options & E2F_OPT_READONLY))
                ext2fs_mark_super_dirty(fs);
 
-       /*
-        * We only update the master superblock because (a) paranoia;
-        * we don't want to corrupt the backup superblocks, and (b) we
-        * don't need to update the mount count and last checked
-        * fields in the backup superblock (the kernel doesn't
-        * update the backup superblocks anyway).
-        */
-       fs->flags |= EXT2_FLAG_MASTER_SB_ONLY;
-
        ehandler_init(fs->io);
 
        if (ctx->superblock)
@@ -1140,15 +1219,26 @@ restart:
                        exit_value |= FSCK_REBOOT;
                }
        }
-       if (!ext2fs_test_valid(fs)) {
+       if (!ext2fs_test_valid(fs) ||
+           ((exit_value & FSCK_CANCELED) && 
+            (sb->s_state & EXT2_ERROR_FS))) {
                printf(_("\n%s: ********** WARNING: Filesystem still has "
                         "errors **********\n\n"), ctx->device_name);
                exit_value |= FSCK_UNCORRECTED;
                exit_value &= ~FSCK_NONDESTRUCT;
        }
-       if (exit_value & FSCK_CANCELED)
+       if (exit_value & FSCK_CANCELED) {
+               int     allow_cancellation;
+
+               profile_get_boolean(ctx->profile, "options",
+                                   "allow_cancellation", 0, 0, 
+                                   &allow_cancellation);
                exit_value &= ~FSCK_NONDESTRUCT;
-       else {
+               if (allow_cancellation && ext2fs_test_valid(fs) &&
+                   (sb->s_state & EXT2_VALID_FS) && 
+                   !(sb->s_state & EXT2_ERROR_FS))
+                       exit_value = 0;
+       } else {
                show_stats(ctx);
                if (!(ctx->options & E2F_OPT_READONLY)) {
                        if (ext2fs_test_valid(fs)) {
@@ -1175,5 +1265,7 @@ restart:
                print_resource_track(NULL, &ctx->global_rtrack);
 #endif
        e2fsck_free_context(ctx);
+       remove_error_table(&et_ext2_error_table);
+       remove_error_table(&et_prof_error_table);
        return exit_value;
 }