blob: 691d85598c3f2bb25bf007f772c6a83210f58678 [file] [log] [blame]
/*
* Copyright 2000-2004 by Hans Reiser, licensing governed by
* reiserfsprogs/README
*/
/*
* Written by Alexander Zarochentcev.
*
* FS resize utility
*
*/
#define _GNU_SOURCE
#include "resize.h"
#include <limits.h>
int opt_banner = 0;
int opt_force = 0;
int opt_verbose = 1; /* now "verbose" option is default */
int opt_nowrite = 0;
int opt_safe = 0;
int opt_skipj = 0;
char * g_progname;
/* calculate the new fs size (in blocks) from old fs size and the string
representation of new size */
static long long int calc_new_fs_size(unsigned long count,
unsigned int bs,
char *bytes_str)
{
long long int bytes;
long long int blocks;
char *end;
int rel;
end = bytes_str + strlen(bytes_str) - 1;
rel = bytes_str[0] == '+' || bytes_str[0] == '-';
bytes = strtoll(bytes_str, &bytes_str, 10);
/* Some error occured while convertion or the specified
string is not valid. */
if (bytes == LONG_LONG_MIN || bytes == LONG_LONG_MAX ||
(bytes_str != end && bytes_str != end + 1))
return -EINVAL;
switch (*end) {
case 'G':
case 'g':
bytes *= 1024;
case 'M':
case 'm':
bytes *= 1024;
case 'K':
case 'k':
bytes *= 1024;
}
blocks = bytes / bs;
return rel ? count + blocks : blocks;
}
/* print some fs parameters */
static void sb_report(struct reiserfs_super_block * sb1,
struct reiserfs_super_block * sb2)
{
printf(
"ReiserFS report:\n"
"blocksize %d\n"
"block count %d (%d)\n"
"free blocks %d (%d)\n"
"bitmap block count %d (%d)\n",
get_sb_block_size(sb1),
get_sb_block_count(sb1), get_sb_block_count(sb2),
get_sb_free_blocks(sb1), get_sb_free_blocks(sb2),
get_sb_bmap_nr(sb1), get_sb_bmap_nr(sb2));
};
/* conditional bwrite */
static int bwrite_cond (struct buffer_head * bh)
{
if(!opt_nowrite) {
mark_buffer_uptodate(bh,1);
mark_buffer_dirty(bh);
bwrite(bh);
}
return 0;
}
/* the first one of the most important functions */
static int expand_fs (reiserfs_filsys_t * fs, long long int block_count_new) {
unsigned int bmap_nr_new, bmap_nr_old;
struct reiserfs_super_block * sb;
unsigned int i;
reiserfs_reopen(fs, O_RDWR);
if (reiserfs_open_ondisk_bitmap (fs))
reiserfs_exit(1, "cannot open ondisk bitmap");
sb = fs->fs_ondisk_sb;
set_sb_fs_state (fs->fs_ondisk_sb, FS_ERROR);
bwrite_cond(fs->fs_super_bh);
if (reiserfs_expand_bitmap(fs->fs_bitmap2, block_count_new))
reiserfs_exit(1, "cannot expand bitmap\n");
/* count bitmap blocks in new fs */
bmap_nr_new = (block_count_new - 1) / (fs->fs_blocksize * 8) + 1;
bmap_nr_old = get_sb_bmap_nr(sb);
/* update super block buffer*/
set_sb_free_blocks (sb, get_sb_free_blocks(sb) +
(block_count_new - get_sb_block_count(sb)) -
(bmap_nr_new - bmap_nr_old));
set_sb_block_count (sb, block_count_new);
set_sb_bmap_nr (sb, bmap_nr_new);
/* mark new bitmap blocks as used */
for (i = bmap_nr_old; i < bmap_nr_new; i++)
reiserfs_bitmap_set_bit (fs->fs_bitmap2, i * fs->fs_blocksize * 8);
/* normally, this is done by reiserfs_bitmap_set_bit, but if we
** haven't actually added any bitmap blocks, the bitmap won't be dirtied.
**
** In memory, reiserfsprogs puts zeros for the bits past the end of
** the old filesystem. But, on disk that bitmap is full of ones.
** we explicitly dirty the bitmap here to make sure the zeros get written
** to disk
*/
fs->fs_bitmap2->bm_dirty = 1 ;
return 0;
}
static int resizer_check_fs_size(reiserfs_filsys_t *fs, long long int new_size) {
if (new_size < 0) {
reiserfs_warning(stderr, "\nresizer_reiserfs: the new size "
"value is wrong.\n\n");
return new_size;
}
if (new_size == get_sb_block_count(fs->fs_ondisk_sb)) {
reiserfs_warning (stderr, "%s already is of the needed size. "
"Nothing to be done\n\n", fs->fs_file_name);
return 1;
}
if (new_size < get_sb_block_count(fs->fs_ondisk_sb)) {
if (misc_device_mounted(fs->fs_file_name) > 0) {
reiserfs_warning (stderr, "Can't shrink filesystem on-line.\n\n");
return 1;
}
}
if (new_size >= get_sb_block_count(fs->fs_ondisk_sb)) {
loff_t offset = (loff_t)new_size * fs->fs_blocksize - 1;
if(!valid_offset(fs->fs_dev, offset)) {
reiserfs_warning (stderr, "%s is of %lu blocks size only with "
"reiserfs of %d blocks\nsize on it. You are "
"trying to expand reiserfs up to %lu blocks "
"size.\nYou probably forgot to expand your "
"partition size.\n\n", fs->fs_file_name,
count_blocks(fs->fs_file_name, fs->fs_blocksize),
get_sb_block_count(fs->fs_ondisk_sb), new_size);
return 1;
}
}
return 0;
}
int main(int argc, char *argv[]) {
char * bytes_count_str = NULL;
char * devname;
char * jdevice_name = NULL;
reiserfs_filsys_t * fs;
struct reiserfs_super_block * sb;
int c;
int error;
struct reiserfs_super_block *sb_old;
long long int block_count_new;
g_progname = basename(argv[0]);
if (argc < 2)
print_usage_and_exit();
while ((c = getopt(argc, argv, "fvcqks:j:V")) != EOF) {
switch (c) {
case 's' :
if (!optarg)
reiserfs_exit(1, "Missing argument to -s option");
bytes_count_str = optarg;
break;
case 'j' :
if (!optarg)
reiserfs_exit(1, "Missing argument to -j option");
jdevice_name = optarg;
case 'f':
opt_force = 1;
break;
case 'v':
opt_verbose++;
break;
case 'n':
/* no nowrite option at this moment */
/* opt_nowrite = 1; */
break;
case 'c':
opt_safe = 1;
break;
case 'q':
opt_verbose = 0;
break;
case 'k':
opt_skipj = 1;
break;
case 'V':
opt_banner++;
break;
default:
print_usage_and_exit ();
}
}
print_banner (g_progname);
if (opt_banner)
exit(0);
devname = argv[optind];
fs = reiserfs_open(devname, O_RDONLY, &error, 0, 1);
if (!fs) {
if (error) {
reiserfs_exit(1, "cannot open '%s': %s",
devname, strerror(error));
} else {
exit(1);
}
}
if (reiserfs_open_journal (fs, jdevice_name, O_RDWR | O_LARGEFILE)) {
reiserfs_exit(1, "Failed to open the journal device (%s).",
jdevice_name);
}
if (reiserfs_journal_params_check(fs)) {
if (!opt_skipj) {
reiserfs_exit(1, "Wrong journal parameters detected on (%s)",
jdevice_name);
} else {
reiserfs_close_journal(fs);
}
}
/* forced to continue without journal available/specified */
if (no_reiserfs_found (fs)) {
reiserfs_exit(1, "no reiserfs found on the device.");
}
if (!spread_bitmaps (fs)) {
reiserfs_exit(1, "cannot resize reiserfs in old (not spread "
"bitmap) format.");
}
sb = fs->fs_ondisk_sb;
/* If size change was specified by user, calculate it,
otherwise take the whole device. */
block_count_new = bytes_count_str ?
calc_new_fs_size(get_sb_block_count(sb),
fs->fs_blocksize, bytes_count_str) :
count_blocks(devname, fs->fs_blocksize);
if (resizer_check_fs_size(fs, block_count_new))
return 1;
if (misc_device_mounted(devname) > 0) {
reiserfs_close(fs);
error = resize_fs_online(devname, block_count_new);
reiserfs_warning(stderr, "\n\nresize_reiserfs: On-line resizing %s.\n\n",
error ? "failed" : "finished successfully");
return error;
}
if (!reiserfs_is_fs_consistent (fs)) {
reiserfs_warning (stderr, "\n\nresize_reiserfs: run reiserfsck --check "
"first\n\n");
reiserfs_close (fs);
return 1;
}
if (get_sb_umount_state(sb) != FS_CLEANLY_UMOUNTED)
/* fixme: shouldn't we check for something like: fsck guarantees: fs is ok */
reiserfs_exit(1, "the file system isn't in valid state.");
/* Needed to keep idiot compiler from issuing false warning */
sb_old = 0;
/* save SB for reporting */
if(opt_verbose) {
sb_old = getmem(SB_SIZE);
memcpy(sb_old, fs->fs_ondisk_sb, SB_SIZE);
}
error = (block_count_new > get_sb_block_count(fs->fs_ondisk_sb)) ?
expand_fs(fs, block_count_new) : shrink_fs(fs, block_count_new);
if (error) {
reiserfs_warning(stderr, "\n\nresize_reiserfs: Resizing failed.\n\n ");
return error;
}
if(opt_verbose) {
sb_report(fs->fs_ondisk_sb, sb_old);
freemem(sb_old);
}
set_sb_fs_state (fs->fs_ondisk_sb, FS_CONSISTENT);
bwrite_cond(fs->fs_super_bh);
if (opt_verbose) {
printf("\nSyncing..");
fflush(stdout);
}
reiserfs_close (fs);
if (opt_verbose)
printf("done\n");
reiserfs_warning(stderr, "\n\nresize_reiserfs: Resizing finished "
"successfully.\n\n ");
return 0;
}