Files
reiserfsprogs/mkreiserfs/mkreiserfs.c
T

505 lines
15 KiB
C

/*
* Copyright 1996, 1997, 1998, 1999 Hans Reiser
*/
/* mkreiserfs is very simple. It supports only 4 and 8K blocks. It skips
first 64k of device, and then writes the super
block, the needed amount of bitmap blocks (this amount is calculated
based on file system size), and root block. Bitmap policy is
primitive: it assumes, that device does not have unreadable blocks,
and it occupies first blocks for super, bitmap and root blocks.
bitmap blocks are interleaved across the disk, mainly to make
resizing faster. */
//
// FIXME: not 'not-i386' safe
//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <asm/types.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/vfs.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <linux/major.h>
#include <sys/stat.h>
#include <linux/kdev_t.h>
#include "io.h"
#include "misc.h"
#include "reiserfs_lib.h"
#include "../version.h"
#define print_usage_and_exit() die ("Usage: %s [ -f ] [ -h tea | rupasov | r5 ]"\
" [ -v 1 | 2] [ -q ] device [block-count]\n\n", argv[0])
#define DEFAULT_BLOCKSIZE 4096
struct buffer_head * g_sb_bh;
struct buffer_head * g_bitmap_bh;
struct buffer_head * g_rb_bh;
struct buffer_head * g_journal_bh ;
int g_block_size = DEFAULT_BLOCKSIZE;
unsigned long int g_block_number;
int g_hash = DEFAULT_HASH;
int g_3_6_format = 1; /* new format is default */
int quiet = 0;
/* reiserfs needs at least: enough blocks for journal, 64 k at the beginning,
one block for super block, bitmap block and root block */
static unsigned long min_block_amount (int block_size, unsigned long journal_size)
{
unsigned long blocks;
blocks = REISERFS_DISK_OFFSET_IN_BYTES / block_size +
1 + 1 + 1 + journal_size;
if (blocks > block_size * 8)
die ("mkreiserfs: journal size specified incorrectly");
return blocks;
}
/* form super block (old one) */
static void make_super_block (int dev)
{
struct reiserfs_super_block * rs;
int sb_size = g_3_6_format ? SB_SIZE : SB_SIZE_V1;
__u32 * oids;
if (SB_SIZE > g_block_size)
die ("mkreiserfs: blocksize (%d) too small", g_block_size);
/* get buffer for super block */
g_sb_bh = getblk (dev, REISERFS_DISK_OFFSET_IN_BYTES / g_block_size, g_block_size);
rs = (struct reiserfs_super_block *)g_sb_bh->b_data;
set_blocksize (rs, g_block_size);
set_block_count (rs, g_block_number);
set_state (rs, REISERFS_VALID_FS);
set_tree_height (rs, 2);
set_bmap_nr (rs, (g_block_number + (g_block_size * 8 - 1)) / (g_block_size * 8));
set_version (rs, g_3_6_format ? REISERFS_VERSION_2 : REISERFS_VERSION_1);
set_hash (rs, g_hash);
// journal things
rs->s_v1.s_journal_dev = cpu_to_le32 (0) ;
rs->s_v1.s_orig_journal_size = cpu_to_le32 (JOURNAL_BLOCK_COUNT) ;
rs->s_v1.s_journal_trans_max = cpu_to_le32 (0) ;
rs->s_v1.s_journal_block_count = cpu_to_le32 (0) ;
rs->s_v1.s_journal_max_batch = cpu_to_le32 (0) ;
rs->s_v1.s_journal_max_commit_age = cpu_to_le32 (0) ;
rs->s_v1.s_journal_max_trans_age = cpu_to_le32 (0) ;
// the differences between sb V1 and sb V2 are: magic string
memcpy (rs->s_v1.s_magic, g_3_6_format ? REISER2FS_SUPER_MAGIC_STRING : REISERFS_SUPER_MAGIC_STRING,
strlen (g_3_6_format ? REISER2FS_SUPER_MAGIC_STRING : REISERFS_SUPER_MAGIC_STRING));
// start of objectid map
oids = (__u32 *)((char *)rs + sb_size);
// max size of objectid map
rs->s_v1.s_oid_maxsize = cpu_to_le16 ((g_block_size - sb_size) / sizeof(__u32) / 2 * 2);
oids[0] = cpu_to_le32 (1);
oids[1] = cpu_to_le32 (REISERFS_ROOT_OBJECTID + 1);
set_objectid_map_size (rs, 2);
mark_buffer_dirty (g_sb_bh);
mark_buffer_uptodate (g_sb_bh, 1);
return;
}
void zero_journal_blocks(int dev, int start, int len) {
int i ;
struct buffer_head *bh ;
unsigned long done = 0;
printf ("Initializing journal - "); fflush (stdout);
for (i = 0 ; i < len ; i++) {
print_how_far (&done, len, 1, quiet);
bh = getblk (dev, start + i, g_block_size) ;
memset(bh->b_data, 0, g_block_size) ;
mark_buffer_dirty(bh) ;
mark_buffer_uptodate(bh,0) ;
bwrite (bh);
brelse(bh) ;
}
printf ("\n"); fflush (stdout);
}
/* this only sets few first bits in bitmap block. Fills not initialized fields
of super block (root block and bitmap block numbers) */
static void make_bitmap (void)
{
struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data;
int i, j;
/* get buffer for bitmap block */
g_bitmap_bh = getblk (g_sb_bh->b_dev, g_sb_bh->b_blocknr + 1, g_sb_bh->b_size);
/* mark, that first 8K of device is busy */
for (i = 0; i < REISERFS_DISK_OFFSET_IN_BYTES / g_block_size; i ++)
set_bit (i, g_bitmap_bh->b_data);
/* mark that super block is busy */
set_bit (i++, g_bitmap_bh->b_data);
/* mark first bitmap block as busy */
set_bit (i ++, g_bitmap_bh->b_data);
/* sb->s_journal_block = g_block_number - JOURNAL_BLOCK_COUNT ; */ /* journal goes at end of disk */
set_journal_start (rs, i);
/* mark journal blocks as busy BUG! we need to check to make sure journal
will fit in the first bitmap block */
for (j = 0 ; j < (JOURNAL_BLOCK_COUNT + 1); j++) /* the descriptor block goes after the journal */
set_bit (i ++, g_bitmap_bh->b_data);
/* and tree root is busy */
set_bit (i, g_bitmap_bh->b_data);
set_root_block (rs, i);
set_free_blocks (rs, rs_block_count (rs) - i - 1);
/* count bitmap blocks not resides in first s_blocksize blocks - ?? */
set_free_blocks (rs, rs_free_blocks (rs) - (rs_bmap_nr (rs) - 1));
mark_buffer_dirty (g_bitmap_bh);
mark_buffer_uptodate (g_bitmap_bh, 0);
mark_buffer_dirty (g_sb_bh);
return;
}
/* form the root block of the tree (the block head, the item head, the
root directory) */
static void make_root_block (void)
{
struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data;
char * rb;
struct item_head * ih;
/* get memory for root block */
g_rb_bh = getblk (g_sb_bh->b_dev, rs_root_block (rs), rs_blocksize (rs));
rb = g_rb_bh->b_data;
/* block head */
set_leaf_node_level (g_rb_bh);
set_node_item_number (g_rb_bh, 0);
set_node_free_space (g_rb_bh, rs_blocksize (rs) - BLKH_SIZE);
/* first item is stat data item of root directory */
ih = (struct item_head *)(g_rb_bh->b_data + BLKH_SIZE);
make_dir_stat_data (g_block_size, g_3_6_format ? KEY_FORMAT_2 : KEY_FORMAT_1,
REISERFS_ROOT_PARENT_OBJECTID, REISERFS_ROOT_OBJECTID,
ih, g_rb_bh->b_data + g_block_size - (g_3_6_format ? SD_SIZE : SD_V1_SIZE));
set_ih_location (ih, g_block_size - ih_item_len (ih));
// adjust block head
set_node_item_number (g_rb_bh, node_item_number (g_rb_bh) + 1);
set_node_free_space (g_rb_bh, node_free_space (g_rb_bh) - (IH_SIZE + ih_item_len (ih)));
/* second item is root directory item, containing "." and ".." */
ih ++;
ih->ih_key.k_dir_id = cpu_to_le32 (REISERFS_ROOT_PARENT_OBJECTID);
ih->ih_key.k_objectid = cpu_to_le32 (REISERFS_ROOT_OBJECTID);
ih->ih_key.u.k_offset_v1.k_offset = cpu_to_le32 (DOT_OFFSET);
ih->ih_key.u.k_offset_v1.k_uniqueness = cpu_to_le32 (DIRENTRY_UNIQUENESS);
ih->ih_item_len = cpu_to_le16 (g_3_6_format ? EMPTY_DIR_SIZE : EMPTY_DIR_SIZE_V1);
ih->ih_item_location = cpu_to_le16 (ih_location (ih-1) - ih_item_len (ih));
ih->u.ih_entry_count = cpu_to_le16 (2);
set_key_format (ih, KEY_FORMAT_1);
if (g_3_6_format)
make_empty_dir_item (g_rb_bh->b_data + ih_location (ih),
REISERFS_ROOT_PARENT_OBJECTID, REISERFS_ROOT_OBJECTID,
0, REISERFS_ROOT_PARENT_OBJECTID);
else
make_empty_dir_item_v1 (g_rb_bh->b_data + ih_location (ih),
REISERFS_ROOT_PARENT_OBJECTID, REISERFS_ROOT_OBJECTID,
0, REISERFS_ROOT_PARENT_OBJECTID);
// adjust block head
set_node_item_number (g_rb_bh, node_item_number (g_rb_bh) + 1);
set_node_free_space (g_rb_bh, node_free_space (g_rb_bh) - (IH_SIZE + ih_item_len (ih)));
print_block (stdout, 0, g_rb_bh, 3, -1, -1);
mark_buffer_dirty (g_rb_bh);
mark_buffer_uptodate (g_rb_bh, 0);
return;
}
/*
* write the super block, the bitmap blocks and the root of the tree
*/
static void write_super_and_root_blocks (void)
{
struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data;
int i;
zero_journal_blocks(g_sb_bh->b_dev, rs_journal_start (rs), JOURNAL_BLOCK_COUNT + 1) ;
/* super block */
bwrite (g_sb_bh);
/* bitmap blocks */
for (i = 0; i < rs_bmap_nr (rs); i ++) {
if (i != 0) {
g_bitmap_bh->b_blocknr = i * rs_blocksize (rs) * 8;
memset (g_bitmap_bh->b_data, 0, g_bitmap_bh->b_size);
set_bit (0, g_bitmap_bh->b_data);
}
if (i == rs_bmap_nr (rs) - 1) {
int j;
/* fill unused part of last bitmap block with 1s */
if (rs_block_count (rs) % (rs_blocksize (rs) * 8))
for (j = rs_block_count (rs) % (rs_blocksize (rs) * 8); j < rs_blocksize (rs) * 8; j ++) {
set_bit (j, g_bitmap_bh->b_data);
}
}
/* write bitmap */
mark_buffer_dirty (g_bitmap_bh);
bwrite (g_bitmap_bh);
}
/* root block */
bwrite (g_rb_bh);
brelse (g_rb_bh);
brelse (g_bitmap_bh);
brelse (g_sb_bh);
}
static void report (char * devname)
{
struct reiserfs_super_block * rs = (struct reiserfs_super_block *)g_sb_bh->b_data;
unsigned int i;
printf ("Creating reiserfs of %s format\n", g_3_6_format ? "3.6" : "3.5");
printf ("Block size %d bytes\n", rs_blocksize (rs));
printf ("Block count %d\n", rs_block_count (rs));
printf ("Used blocks %d\n", rs_block_count (rs) - rs_free_blocks (rs));
printf ("Free blocks count %d\n", rs_free_blocks (rs));
printf ("First %ld blocks skipped\n", g_sb_bh->b_blocknr);
printf ("Super block is in %ld\n", g_sb_bh->b_blocknr);
printf ("Bitmap blocks (%d) are : \n\t%ld", rs_bmap_nr (rs), g_bitmap_bh->b_blocknr);
for (i = 1; i < rs_bmap_nr (rs); i ++) {
printf (", %d", i * rs_blocksize (rs) * 8);
}
printf ("\nJournal size %d (blocks %d-%d of file %s)\n",
JOURNAL_BLOCK_COUNT, rs_journal_start (rs),
rs_journal_start (rs) + JOURNAL_BLOCK_COUNT, devname);
printf ("Root block %u\n", rs_root_block (rs));
printf ("Hash function \"%s\"\n", g_hash == TEA_HASH ? "tea" :
((g_hash == YURA_HASH) ? "rupasov" : "r5"));
fflush (stdout);
}
/* wipe out first 2 k of a device and both possible reiserfs super block */
static void invalidate_other_formats (int dev)
{
struct buffer_head * bh;
bh = getblk (dev, 0, 2048);
mark_buffer_uptodate (bh, 1);
mark_buffer_dirty (bh);
bwrite (bh);
brelse (bh);
bh = getblk(dev, REISERFS_OLD_DISK_OFFSET_IN_BYTES / 1024, 1024) ;
mark_buffer_uptodate (bh, 1);
mark_buffer_dirty (bh);
bwrite (bh);
brelse (bh);
bh = getblk(dev, REISERFS_DISK_OFFSET_IN_BYTES / 1024, 1024) ;
mark_buffer_uptodate (bh, 1);
mark_buffer_dirty (bh);
bwrite (bh);
brelse (bh);
}
static void set_hash_function (char * str)
{
if (!strcmp (str, "tea"))
g_hash = TEA_HASH;
else if (!strcmp (str, "rupasov"))
g_hash = YURA_HASH;
else if (!strcmp (str, "r5"))
g_hash = R5_HASH;
else
printf ("mkreiserfs: wrong hash type specified. Using default\n");
}
static void set_reiserfs_version (char * str)
{
if (!strcmp (str, "1"))
g_3_6_format = 0;
else if (!strcmp (str, "2"))
g_3_6_format = 1;
else
printf ("mkreiserfs: wrong reiserfs version specified. Using default 3.5 format\n");
}
int main (int argc, char **argv)
{
char *tmp;
int dev;
int force = 0;
struct stat st;
char * device_name;
char c;
print_banner ("mkreiserfs");
if (argc < 2)
print_usage_and_exit ();
while ( ( c = getopt( argc, argv, "fh:v:q" ) ) != EOF )
switch( c )
{
case 'f' : /* force if file is not a block device or fs is
mounted. Confirm still required */
force = 1;
break;
case 'h':
set_hash_function (optarg);
break;
case 'v':
set_reiserfs_version (optarg);
break;
case 'q':
quiet = 1;
break;
default :
print_usage_and_exit ();
}
device_name = argv [optind];
/* get block number for file system */
if (optind == argc - 2) {
g_block_number = strtol (argv[optind + 1], &tmp, 0);
if (*tmp == 0) { /* The string is integer */
if (g_block_number > count_blocks (device_name, g_block_size, -1))
die ("mkreiserfs: specified block number (%d) is too high", g_block_number);
} else {
die ("mkreiserfs: bad block count : %s\n", argv[optind + 1]);
}
} else
if (optind == argc - 1) {
/* number of blocks is not specified */
g_block_number = count_blocks (device_name, g_block_size, -1);
tmp = "";
} else
print_usage_and_exit ();
/*g_block_number = g_block_number / 8 * 8;*/
if (g_block_number < min_block_amount (g_block_size, JOURNAL_BLOCK_COUNT + 1))
die ("mkreiserfs: can not create filesystem on that small device (%lu blocks).\n"
"It should have at least %lu blocks",
g_block_number, min_block_amount (g_block_size, JOURNAL_BLOCK_COUNT + 1));
if (is_mounted (device_name)) {
printf ("mkreiserfs: '%s' contains a mounted file system\n", device_name);
if (!force)
exit (1);
if (!user_confirmed ("Forced to continue, but please confirm (y/n)", "y"))
exit (1);
}
dev = open (device_name, O_RDWR);
if (dev == -1)
die ("mkreiserfs: can not open '%s': %s", device_name, strerror (errno));
if (fstat (dev, &st) < 0)
die ("mkreiserfs: unable to stat %s", device_name);
if (!S_ISBLK (st.st_mode)) {
printf ("mkreiserfs: %s is not a block special device.\n", device_name);
if (!force) {
exit (1);
}
if (!user_confirmed ("Forced to continue, but please confirm (y/n)", "y"))
exit (1);
} else {
// from e2progs-1.18/misc/mke2fs.c
if ((MAJOR (st.st_rdev) == HD_MAJOR && MINOR (st.st_rdev)%64 == 0) ||
(SCSI_BLK_MAJOR (MAJOR(st.st_rdev)) && MINOR (st.st_rdev) % 16 == 0)) {
printf ("mkreiserfs: %s is entire device, not just one partition! Continue? (y/n) ",
device_name);
if (!user_confirmed ("Continue (y/n)", "y"))
exit (1);
}
}
/* these fill buffers (super block, first bitmap, root block) with
reiserfs structures */
make_super_block (dev);
make_bitmap ();
make_root_block ();
report (device_name);
printf ("ATTENTION: YOU SHOULD REBOOT AFTER FDISK!\n\t ALL DATA WILL BE LOST ON '%s'! ", device_name);
if (!user_confirmed ("(y/n)", "y\n"))
die ("mkreiserfs: Disk was not formatted");
invalidate_other_formats (dev);
write_super_and_root_blocks ();
check_and_free_buffer_mem ();
printf ("Syncing.."); fflush (stdout);
close(dev) ;
sync ();
printf ("\n\nReiserFS core development sponsored by SuSE Labs (suse.com)\n\n"
"Journaling sponsored by MP3.com.\n\n"
//"Item handlers sponsored by Ecila.com\n\n
"To learn about the programmers and ReiserFS, please go to\n"
"http://www.devlinux.com/namesys\n\nHave fun.\n\n");
fflush (stdout);
return 0;
}