Whamcloud - gitweb
debian: add support for DEB_BUILD_OPTIONS=parallel=N
[tools/e2fsprogs.git] / resize / main.c
index f25df40..f914c05 100644 (file)
@@ -47,8 +47,9 @@ static char *device_name, *io_options;
 static void usage (char *prog)
 {
        fprintf (stderr, _("Usage: %s [-d debug_flags] [-f] [-F] [-M] [-P] "
-                          "[-p] device [-b|-s|new_size] [-z undo_file]\n\n"),
-                prog);
+                          "[-p] device [-b|-s|new_size] [-S RAID-stride] "
+                          "[-z undo_file]\n\n"),
+                prog ? prog : "resize2fs");
 
        exit (1);
 }
@@ -110,8 +111,8 @@ static void determine_fs_stride(ext2_filsys fs)
        unsigned int    group;
        unsigned long long sum;
        unsigned int    has_sb, prev_has_sb = 0, num;
+       unsigned int    flexbg_size = 1U << fs->super->s_log_groups_per_flex;
        int             i_stride, b_stride;
-       int             flexbg_size = 1 << fs->super->s_log_groups_per_flex;
 
        if (fs->stride)
                return;
@@ -167,11 +168,12 @@ static void bigalloc_check(ext2_filsys fs, int force)
        }
 }
 
-static int resize2fs_setup_tdb(const char *device_name, char *undo_file,
+static int resize2fs_setup_tdb(const char *device, char *undo_file,
                               io_manager *io_ptr)
 {
        errcode_t retval = ENOMEM;
-       char *tdb_dir = NULL, *tdb_file = NULL;
+       const char *tdb_dir = NULL;
+       char *tdb_file = NULL;
        char *dev_name, *tmp_name;
 
        /* (re)open a specific undo file */
@@ -186,7 +188,7 @@ static int resize2fs_setup_tdb(const char *device_name, char *undo_file,
                printf(_("Overwriting existing filesystem; this can be undone "
                         "using the command:\n"
                         "    e2undo %s %s\n\n"),
-                       undo_file, device_name);
+                       undo_file, device);
                return retval;
        }
 
@@ -202,7 +204,7 @@ static int resize2fs_setup_tdb(const char *device_name, char *undo_file,
            access(tdb_dir, W_OK))
                return 0;
 
-       tmp_name = strdup(device_name);
+       tmp_name = strdup(device);
        if (!tmp_name)
                goto errout;
        dev_name = basename(tmp_name);
@@ -230,7 +232,7 @@ static int resize2fs_setup_tdb(const char *device_name, char *undo_file,
                goto errout;
        printf(_("Overwriting existing filesystem; this can be undone "
                 "using the command:\n"
-                "    e2undo %s %s\n\n"), tdb_file, device_name);
+                "    e2undo %s %s\n\n"), tdb_file, device);
 
        free(tdb_file);
        return 0;
@@ -254,6 +256,7 @@ int main (int argc, char ** argv)
        int             force_min_size = 0;
        int             print_min_size = 0;
        int             fd, ret;
+       int             open_flags = O_RDWR;
        blk64_t         new_size = 0;
        blk64_t         max_size = 0;
        blk64_t         min_size = 0;
@@ -267,6 +270,8 @@ int main (int argc, char ** argv)
        long            sysval;
        int             len, mount_flags;
        char            *mtpt, *undo_file = NULL;
+       dgrp_t          new_group_desc_count;
+       unsigned long   new_desc_blocks;
 
 #ifdef ENABLE_NLS
        setlocale(LC_MESSAGES, "");
@@ -282,6 +287,8 @@ int main (int argc, char ** argv)
                 E2FSPROGS_VERSION, E2FSPROGS_DATE);
        if (argc && *argv)
                program_name = *argv;
+       else
+               usage(NULL);
 
        while ((c = getopt(argc, argv, "d:fFhMPpS:bsz:")) != EOF) {
                switch (c) {
@@ -359,7 +366,10 @@ int main (int argc, char ** argv)
                len = 2 * len;
        }
 
-       fd = ext2fs_open_file(device_name, O_RDWR, 0);
+       if (print_min_size)
+               open_flags = O_RDONLY;
+
+       fd = ext2fs_open_file(device_name, open_flags, 0);
        if (fd < 0) {
                com_err("open", errno, _("while opening %s"),
                        device_name);
@@ -397,10 +407,12 @@ int main (int argc, char ** argv)
 #endif
                io_ptr = unix_io_manager;
 
-       if (!(mount_flags & EXT2_MF_MOUNTED))
+       if (!(mount_flags & EXT2_MF_MOUNTED) && !print_min_size)
                io_flags = EXT2_FLAG_RW | EXT2_FLAG_EXCLUSIVE;
+       if (mount_flags & EXT2_MF_MOUNTED)
+               io_flags |= EXT2_FLAG_DIRECT_IO;
 
-       io_flags |= EXT2_FLAG_64BITS;
+       io_flags |= EXT2_FLAG_64BITS | EXT2_FLAG_THREADS;
        if (undo_file) {
                retval = resize2fs_setup_tdb(device_name, undo_file, &io_ptr);
                if (retval)
@@ -421,7 +433,7 @@ int main (int argc, char ** argv)
         * unless the user is forcing it.
         *
         * We do ERROR and VALID checks even if we're only printing the
-        * minimimum size, because traversal of a badly damaged filesystem
+        * minimum size, because traversal of a badly damaged filesystem
         * can cause issues as well.  We don't require it to be fscked after
         * the last mount time in this case, though, as this is a bit less
         * risky.
@@ -439,11 +451,20 @@ int main (int argc, char ** argv)
                    !print_min_size)
                        checkit = 1;
 
+               if ((ext2fs_free_blocks_count(fs->super) >
+                    ext2fs_blocks_count(fs->super)) ||
+                   (fs->super->s_free_inodes_count > fs->super->s_inodes_count))
+                       checkit = 1;
+
+               if ((fs->super->s_last_orphan != 0) ||
+                   ext2fs_has_feature_journal_needs_recovery(fs->super))
+                       checkit = 1;
+
                if (checkit) {
                        fprintf(stderr,
                                _("Please run 'e2fsck -f %s' first.\n\n"),
                                device_name);
-                       exit(1);
+                       goto errout;
                }
        }
 
@@ -454,14 +475,17 @@ int main (int argc, char ** argv)
        if (fs->super->s_feature_compat & ~EXT2_LIB_FEATURE_COMPAT_SUPP) {
                com_err(program_name, EXT2_ET_UNSUPP_FEATURE,
                        "(%s)", device_name);
-               exit(1);
+               goto errout;
        }
 
        min_size = calculate_minimum_resize_size(fs, flags);
 
        if (print_min_size) {
                printf(_("Estimated minimum size of the filesystem: %llu\n"),
-                      min_size);
+                      (unsigned long long) min_size);
+       success_exit:
+               (void) ext2fs_close_free(&fs);
+               remove_error_table(&et_ext2_error_table);
                exit(0);
        }
 
@@ -488,7 +512,7 @@ int main (int argc, char ** argv)
        if (retval) {
                com_err(program_name, retval, "%s",
                        _("while trying to determine filesystem size"));
-               exit(1);
+               goto errout;
        }
        if (force_min_size)
                new_size = min_size;
@@ -498,13 +522,13 @@ int main (int argc, char ** argv)
                if (new_size == 0) {
                        com_err(program_name, 0,
                                _("Invalid new size: %s\n"), new_size_str);
-                       exit(1);
+                       goto errout;
                }
        } else {
                new_size = max_size;
                /* Round down to an even multiple of a pagesize */
                if (sys_page_size > blocksize)
-                       new_size &= ~((sys_page_size / blocksize)-1);
+                       new_size &= ~((blk64_t)((sys_page_size / blocksize)-1));
        }
        /* If changing 64bit, don't change the filesystem size. */
        if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) {
@@ -518,20 +542,39 @@ int main (int argc, char ** argv)
                        com_err(program_name, 0, "%s",
                                _("New size too large to be "
                                  "expressed in 32 bits\n"));
-                       exit(1);
+                       goto errout;
                }
        }
 
+       /* If using cluster allocations, trim down to a cluster boundary */
+       if (ext2fs_has_feature_bigalloc(fs->super)) {
+               new_size &= ~((blk64_t)(1ULL << fs->cluster_ratio_bits) - 1);
+       }
+
+       new_group_desc_count = ext2fs_div64_ceil(new_size -
+                               fs->super->s_first_data_block,
+                                                EXT2_BLOCKS_PER_GROUP(fs->super));
+       new_desc_blocks = ext2fs_div_ceil(new_group_desc_count,
+                                         EXT2_DESC_PER_BLOCK(fs->super));
+       if ((new_desc_blocks + fs->super->s_first_data_block) >
+           EXT2_BLOCKS_PER_GROUP(fs->super)) {
+               com_err(program_name, 0,
+                       _("New size results in too many block group "
+                         "descriptors.\n"));
+               goto errout;
+       }
+
        if (!force && new_size < min_size) {
                com_err(program_name, 0,
-                       _("New size smaller than minimum (%llu)\n"), min_size);
-               exit(1);
+                       _("New size smaller than minimum (%llu)\n"),
+                       (unsigned long long) min_size);
+               goto errout;
        }
        if (use_stride >= 0) {
                if (use_stride >= (int) fs->super->s_blocks_per_group) {
                        com_err(program_name, 0, "%s",
                                _("Invalid stride length"));
-                       exit(1);
+                       goto errout;
                }
                fs->stride = fs->super->s_raid_stride = use_stride;
                ext2fs_mark_super_dirty(fs);
@@ -556,50 +599,60 @@ int main (int argc, char ** argv)
        if (!force && (new_size > max_size)) {
                fprintf(stderr, _("The containing partition (or device)"
                        " is only %llu (%dk) blocks.\nYou requested a new size"
-                       " of %llu blocks.\n\n"), max_size,
-                       blocksize / 1024, new_size);
-               exit(1);
+                       " of %llu blocks.\n\n"), (unsigned long long) max_size,
+                       blocksize / 1024, (unsigned long long) new_size);
+               goto errout;
        }
        if ((flags & RESIZE_DISABLE_64BIT) && (flags & RESIZE_ENABLE_64BIT)) {
                fprintf(stderr, _("Cannot set and unset 64bit feature.\n"));
-               exit(1);
+               goto errout;
        } else if (flags & (RESIZE_DISABLE_64BIT | RESIZE_ENABLE_64BIT)) {
                if (new_size >= (1ULL << 32)) {
                        fprintf(stderr, _("Cannot change the 64bit feature "
                                "on a filesystem that is larger than "
                                "2^32 blocks.\n"));
-                       exit(1);
+                       goto errout;
                }
                if (mount_flags & EXT2_MF_MOUNTED) {
                        fprintf(stderr, _("Cannot change the 64bit feature "
                                "while the filesystem is mounted.\n"));
-                       exit(1);
+                       goto errout;
                }
                if (flags & RESIZE_ENABLE_64BIT &&
                    !ext2fs_has_feature_extents(fs->super)) {
                        fprintf(stderr, _("Please enable the extents feature "
                                "with tune2fs before enabling the 64bit "
                                "feature.\n"));
-                       exit(1);
+                       goto errout;
+               }
+       } else {
+               adjust_new_size(fs, &new_size);
+               if (new_size == ext2fs_blocks_count(fs->super)) {
+                       fprintf(stderr, _("The filesystem is already "
+                                         "%llu (%dk) blocks long.  "
+                                         "Nothing to do!\n\n"),
+                               (unsigned long long) new_size,
+                               blocksize / 1024);
+                       goto success_exit;
                }
-       } else if (new_size == ext2fs_blocks_count(fs->super)) {
-               fprintf(stderr, _("The filesystem is already %llu (%dk) "
-                       "blocks long.  Nothing to do!\n\n"), new_size,
-                       blocksize / 1024);
-               exit(0);
        }
        if ((flags & RESIZE_ENABLE_64BIT) &&
            ext2fs_has_feature_64bit(fs->super)) {
                fprintf(stderr, _("The filesystem is already 64-bit.\n"));
-               exit(0);
+               goto success_exit;
        }
        if ((flags & RESIZE_DISABLE_64BIT) &&
            !ext2fs_has_feature_64bit(fs->super)) {
                fprintf(stderr, _("The filesystem is already 32-bit.\n"));
-               exit(0);
+               goto success_exit;
+       }
+       if (new_size < ext2fs_blocks_count(fs->super) &&
+           ext2fs_has_feature_stable_inodes(fs->super)) {
+               fprintf(stderr, _("Cannot shrink this filesystem "
+                       "because it has the stable_inodes feature flag.\n"));
+               goto errout;
        }
        if (mount_flags & EXT2_MF_MOUNTED) {
-               bigalloc_check(fs, force);
                retval = online_resize_fs(fs, mtpt, &new_size, flags);
        } else {
                bigalloc_check(fs, force);
@@ -610,7 +663,8 @@ int main (int argc, char ** argv)
                else
                        printf(_("Resizing the filesystem on "
                                 "%s to %llu (%dk) blocks.\n"),
-                              device_name, new_size, blocksize / 1024);
+                              device_name, (unsigned long long) new_size,
+                              blocksize / 1024);
                retval = resize_fs(fs, &new_size, flags,
                                   ((flags & RESIZE_PERCENT_COMPLETE) ?
                                    resize_progress_func : 0));
@@ -623,11 +677,10 @@ int main (int argc, char ** argv)
                        _("Please run 'e2fsck -fy %s' to fix the filesystem\n"
                          "after the aborted resize operation.\n"),
                        device_name);
-               ext2fs_close_free(&fs);
-               exit(1);
+               goto errout;
        }
        printf(_("The filesystem on %s is now %llu (%dk) blocks long.\n\n"),
-              device_name, new_size, blocksize / 1024);
+              device_name, (unsigned long long) new_size, blocksize / 1024);
 
        if ((st_buf.st_size > new_file_size) &&
            (fd > 0)) {
@@ -647,5 +700,9 @@ int main (int argc, char ** argv)
        if (fd > 0)
                close(fd);
        remove_error_table(&et_ext2_error_table);
-       return (0);
+       return 0;
+errout:
+       (void) ext2fs_close_free(&fs);
+       remove_error_table(&et_ext2_error_table);
+       return 1;
 }