358 lines
9.0 KiB
C
358 lines
9.0 KiB
C
/*
|
|
* Copyright 2000-2004 by Hans Reiser, licensing governed by
|
|
* reiserfsprogs/README
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include "resize.h"
|
|
#include "misc/device.h"
|
|
#include "misc/malloc.h"
|
|
#include "util/device.h"
|
|
#include "util/print.h"
|
|
|
|
#include <getopt.h>
|
|
#include <errno.h>
|
|
#include <fcntl.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;
|
|
|
|
/* 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(reiserfs_sb_t * sb1,
|
|
reiserfs_sb_t * sb2)
|
|
{
|
|
printf(
|
|
"ReiserFS report:\n"
|
|
"blocksize %d\n"
|
|
"block count %d (%d)\n"
|
|
"free blocks %d (%d)\n"
|
|
"bitmap block count %d (%d)\n",
|
|
reiserfs_sb_get_blksize(sb1),
|
|
reiserfs_sb_get_blocks(sb1), reiserfs_sb_get_blocks(sb2),
|
|
reiserfs_sb_get_free(sb1), reiserfs_sb_get_free(sb2),
|
|
reiserfs_sb_get_bmaps(sb1), reiserfs_sb_get_bmaps(sb2));
|
|
};
|
|
|
|
/* conditional bwrite */
|
|
static int bwrite_cond (reiserfs_bh_t * bh)
|
|
{
|
|
if(!opt_nowrite) {
|
|
reiserfs_buffer_mkuptodate(bh,1);
|
|
reiserfs_buffer_mkdirty(bh);
|
|
reiserfs_buffer_write(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;
|
|
unsigned int block_count_old;
|
|
reiserfs_sb_t * sb;
|
|
unsigned int i;
|
|
|
|
|
|
reiserfs_fs_reopen(fs, O_RDWR);
|
|
if (reiserfs_bitmap_open(fs))
|
|
reiserfs_exit(1, "cannot open ondisk bitmap");
|
|
|
|
sb = fs->fs_ondisk_sb;
|
|
|
|
reiserfs_sb_set_state (fs->fs_ondisk_sb, FS_ERROR);
|
|
|
|
bwrite_cond(fs->fs_super_bh);
|
|
|
|
|
|
if (reiserfs_bitmap_expand (fs->fs_bitmap2, block_count_new))
|
|
reiserfs_exit(1, "cannot expand bitmap\n");
|
|
|
|
|
|
/* count bitmap blocks in new fs */
|
|
block_count_old = reiserfs_sb_get_blocks(sb);
|
|
bmap_nr_old = reiserfs_bmap_nr(block_count_old, fs->fs_blocksize);
|
|
bmap_nr_new = reiserfs_bmap_nr(block_count_new, fs->fs_blocksize);
|
|
|
|
/* update super block buffer*/
|
|
reiserfs_sb_set_free (sb, reiserfs_sb_get_free(sb) +
|
|
(block_count_new - reiserfs_sb_get_blocks(sb)) -
|
|
(bmap_nr_new - bmap_nr_old));
|
|
reiserfs_sb_set_blocks (sb, block_count_new);
|
|
reiserfs_sb_set_bmaps (sb, reiserfs_bmap_over(bmap_nr_new) ?
|
|
0 : 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 == reiserfs_sb_get_blocks(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 < reiserfs_sb_get_blocks(fs->fs_ondisk_sb)) {
|
|
if (util_device_mounted(fs->fs_file_name) > 0) {
|
|
reiserfs_warning (stderr, "Can't shrink filesystem on-line.\n\n");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (new_size >= reiserfs_sb_get_blocks(fs->fs_ondisk_sb)) {
|
|
unsigned long long offset = (unsigned long long)new_size * fs->fs_blocksize - 1;
|
|
|
|
if(!misc_device_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,
|
|
misc_device_count_blocks(fs->fs_file_name, fs->fs_blocksize),
|
|
reiserfs_sb_get_blocks(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;
|
|
reiserfs_sb_t * sb;
|
|
|
|
int c;
|
|
int error;
|
|
|
|
reiserfs_sb_t *sb_old;
|
|
|
|
long long int block_count_new;
|
|
|
|
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 ();
|
|
}
|
|
}
|
|
|
|
util_print_banner (argv[0]);
|
|
|
|
if (opt_banner)
|
|
exit(0);
|
|
|
|
|
|
devname = argv[optind];
|
|
|
|
fs = reiserfs_fs_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_journal_open (fs, jdevice_name, O_RDWR
|
|
#ifdef O_LARGEFILE
|
|
| O_LARGEFILE
|
|
#endif
|
|
))
|
|
{
|
|
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_journal_close(fs);
|
|
}
|
|
}
|
|
|
|
/* forced to continue without journal available/specified */
|
|
|
|
if (fs == NULL) {
|
|
reiserfs_exit(1, "no reiserfs found on the device.");
|
|
}
|
|
|
|
if (!reiserfs_bitmap_spread (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(reiserfs_sb_get_blocks(sb),
|
|
fs->fs_blocksize, bytes_count_str) :
|
|
misc_device_count_blocks(devname, fs->fs_blocksize);
|
|
|
|
if (resizer_check_fs_size(fs, block_count_new))
|
|
return 1;
|
|
|
|
if (util_device_mounted(devname) > 0) {
|
|
reiserfs_fs_close(fs);
|
|
fs = NULL;
|
|
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_sb_state_ok (fs)) {
|
|
reiserfs_warning (stderr, "\n\nresize_reiserfs: run reiserfsck --check "
|
|
"first\n\n");
|
|
reiserfs_fs_close (fs);
|
|
fs = NULL;
|
|
return 1;
|
|
}
|
|
|
|
if (reiserfs_sb_get_umount(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 = misc_getmem(REISERFS_SB_SIZE);
|
|
memcpy(sb_old, fs->fs_ondisk_sb, REISERFS_SB_SIZE);
|
|
}
|
|
|
|
error = (block_count_new > reiserfs_sb_get_blocks(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);
|
|
misc_freemem(sb_old);
|
|
}
|
|
|
|
reiserfs_sb_set_state (fs->fs_ondisk_sb, FS_CONSISTENT);
|
|
bwrite_cond(fs->fs_super_bh);
|
|
|
|
if (opt_verbose) {
|
|
printf("\nSyncing..");
|
|
fflush(stdout);
|
|
}
|
|
reiserfs_fs_close (fs);
|
|
if (opt_verbose)
|
|
printf("done\n");
|
|
|
|
reiserfs_warning(stderr, "\n\nresize_reiserfs: Resizing finished "
|
|
"successfully.\n\n ");
|
|
|
|
return 0;
|
|
}
|