patch-2.4.1 linux/fs/reiserfs/super.c

Next file: linux/fs/reiserfs/tail_conversion.c
Previous file: linux/fs/reiserfs/stree.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.0/linux/fs/reiserfs/super.c linux/fs/reiserfs/super.c
@@ -0,0 +1,879 @@
+/*
+ * Copyright 2000 by Hans Reiser, licensing governed by reiserfs/README
+ */
+
+#ifdef __KERNEL__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/reiserfs_fs.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+
+#else
+
+#include "nokernel.h"
+#include <stdlib.h> // for simple_strtoul
+
+#endif
+
+#define SUPPORT_OLD_FORMAT
+
+#define REISERFS_OLD_BLOCKSIZE 4096
+#define REISERFS_SUPER_MAGIC_STRING_OFFSET_NJ 20
+
+
+#if 0
+// this one is not used currently
+inline void reiserfs_mark_buffer_dirty (struct buffer_head * bh, int flag)
+{
+  mark_buffer_dirty (bh, flag);
+}
+#endif
+
+//
+// a portion of this function, particularly the VFS interface portion,
+// was derived from minix or ext2's analog and evolved as the
+// prototype did. You should be able to tell which portion by looking
+// at the ext2 code and comparing. It's subfunctions contain no code
+// used as a template unless they are so labeled.
+//
+void reiserfs_write_super (struct super_block * s)
+{
+
+  int dirty = 0 ;
+  lock_kernel() ;
+  if (!(s->s_flags & MS_RDONLY)) {
+    dirty = flush_old_commits(s, 1) ;
+  }
+  s->s_dirt = dirty;
+  unlock_kernel() ;
+}
+
+//
+// a portion of this function, particularly the VFS interface portion,
+// was derived from minix or ext2's analog and evolved as the
+// prototype did. You should be able to tell which portion by looking
+// at the ext2 code and comparing. It's subfunctions contain no code
+// used as a template unless they are so labeled.
+//
+void reiserfs_write_super_lockfs (struct super_block * s)
+{
+
+  int dirty = 0 ;
+  struct reiserfs_transaction_handle th ;
+  lock_kernel() ;
+  if (!(s->s_flags & MS_RDONLY)) {
+    journal_begin(&th, s, 1) ;
+    journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+    reiserfs_block_writes(&th) ;
+    journal_end(&th, s, 1) ;
+  }
+  s->s_dirt = dirty;
+  unlock_kernel() ;
+}
+
+void reiserfs_unlockfs(struct super_block *s) {
+  reiserfs_allow_writes(s) ;
+}
+
+//
+// a portion of this function, particularly the VFS interface portion,
+// was derived from minix or ext2's analog and evolved as the
+// prototype did. You should be able to tell which portion by looking
+// at the ext2 code and comparing. It's subfunctions contain no code
+// used as a template unless they are so labeled.
+//
+/* there should be no suspected recipients already. True and cautious
+   bitmaps should not differ. We only have to free preserve list and
+   write both bitmaps */
+void reiserfs_put_super (struct super_block * s)
+{
+  int i;
+  struct reiserfs_transaction_handle th ;
+  
+  /* change file system state to current state if it was mounted with read-write permissions */
+  if (!(s->s_flags & MS_RDONLY)) {
+    journal_begin(&th, s, 10) ;
+    reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+    s->u.reiserfs_sb.s_rs->s_state = le16_to_cpu (s->u.reiserfs_sb.s_mount_state);
+    journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+  }
+
+  /* note, journal_release checks for readonly mount, and can decide not
+  ** to do a journal_end
+  */
+  journal_release(&th, s) ;
+
+  for (i = 0; i < SB_BMAP_NR (s); i ++)
+    brelse (SB_AP_BITMAP (s)[i]);
+
+  reiserfs_kfree (SB_AP_BITMAP (s), sizeof (struct buffer_head *) * SB_BMAP_NR (s), s);
+
+  brelse (SB_BUFFER_WITH_SB (s));
+
+  print_statistics (s);
+
+  if (s->u.reiserfs_sb.s_kmallocs != 0) {
+    reiserfs_warning ("vs-2004: reiserfs_put_super: aloocated memory left %d\n",
+		      s->u.reiserfs_sb.s_kmallocs);
+  }
+
+  return;
+}
+
+struct super_operations reiserfs_sops = 
+{
+  read_inode: reiserfs_read_inode,
+  read_inode2: reiserfs_read_inode2,
+  write_inode: reiserfs_write_inode,
+  dirty_inode: reiserfs_dirty_inode,
+  delete_inode: reiserfs_delete_inode,
+  put_super: reiserfs_put_super,
+  write_super: reiserfs_write_super,
+  write_super_lockfs: reiserfs_write_super_lockfs,
+  unlockfs: reiserfs_unlockfs,
+  statfs: reiserfs_statfs,
+  remount_fs: reiserfs_remount,
+
+};
+
+/* this was (ext2)parse_options */
+static int parse_options (char * options, unsigned long * mount_options, unsigned long * blocks)
+{
+    char * this_char;
+    char * value;
+  
+    *blocks = 0;
+    if (!options)
+	/* use default configuration: create tails, journaling on, no
+           conversion to newest format */
+	return 1;
+    for (this_char = strtok (options, ","); this_char != NULL; this_char = strtok (NULL, ",")) {
+	if ((value = strchr (this_char, '=')) != NULL)
+	    *value++ = 0;
+	if (!strcmp (this_char, "notail")) {
+	    set_bit (NOTAIL, mount_options);
+	} else if (!strcmp (this_char, "conv")) {
+	    // if this is set, we update super block such that
+	    // the partition will not be mounable by 3.5.x anymore
+	    set_bit (REISERFS_CONVERT, mount_options);
+	} else if (!strcmp (this_char, "noborder")) {
+				/* this is used for benchmarking
+                                   experimental variations, it is not
+                                   intended for users to use, only for
+                                   developers who want to casually
+                                   hack in something to test */
+	    set_bit (REISERFS_NO_BORDER, mount_options);
+	} else if (!strcmp (this_char, "no_unhashed_relocation")) {
+	    set_bit (REISERFS_NO_UNHASHED_RELOCATION, mount_options);
+	} else if (!strcmp (this_char, "hashed_relocation")) {
+	    set_bit (REISERFS_HASHED_RELOCATION, mount_options);
+	} else if (!strcmp (this_char, "test4")) {
+	    set_bit (REISERFS_TEST4, mount_options);
+	} else if (!strcmp (this_char, "nolog")) {
+	    reiserfs_warning("reiserfs: nolog mount option not supported yet\n");
+	} else if (!strcmp (this_char, "replayonly")) {
+	    set_bit (REPLAYONLY, mount_options);
+	} else if (!strcmp (this_char, "resize")) {
+	    if (value && *value){
+		*blocks = simple_strtoul (value, &value, 0);
+	    } else {
+	  	printk("reiserfs: resize option requires a value\n");
+		return 0;
+	    }
+	} else if (!strcmp (this_char, "hash")) {
+	    if (value && *value) {
+		/* if they specify any hash option, we force detection
+		** to make sure they aren't using the wrong hash
+		*/
+	        if (!strcmp(value, "rupasov")) {
+		    set_bit (FORCE_RUPASOV_HASH, mount_options);
+		    set_bit (FORCE_HASH_DETECT, mount_options);
+		} else if (!strcmp(value, "tea")) {
+		    set_bit (FORCE_TEA_HASH, mount_options);
+		    set_bit (FORCE_HASH_DETECT, mount_options);
+		} else if (!strcmp(value, "r5")) {
+		    set_bit (FORCE_R5_HASH, mount_options);
+		    set_bit (FORCE_HASH_DETECT, mount_options);
+		} else if (!strcmp(value, "detect")) {
+		    set_bit (FORCE_HASH_DETECT, mount_options);
+		} else {
+		    printk("reiserfs: invalid hash function specified\n") ;
+		    return 0 ;
+		}
+	    } else {
+	  	printk("reiserfs: hash option requires a value\n");
+		return 0 ;
+	    }
+	} else {
+	    printk ("reiserfs: Unrecognized mount option %s\n", this_char);
+	    return 0;
+	}
+    }
+    return 1;
+}
+
+
+int reiserfs_is_super(struct super_block *s) {
+   return (s->s_dev != 0 && s->s_op == &reiserfs_sops) ;
+}
+
+
+//
+// a portion of this function, particularly the VFS interface portion,
+// was derived from minix or ext2's analog and evolved as the
+// prototype did. You should be able to tell which portion by looking
+// at the ext2 code and comparing. It's subfunctions contain no code
+// used as a template unless they are so labeled.
+//
+int reiserfs_remount (struct super_block * s, int * flags, char * data)
+{
+  struct reiserfs_super_block * rs;
+  struct reiserfs_transaction_handle th ;
+  unsigned long blocks;
+  unsigned long mount_options;
+
+  rs = SB_DISK_SUPER_BLOCK (s);
+
+  if (!parse_options(data, &mount_options, &blocks))
+  	return 0;
+
+  if(blocks) {
+      int rc = reiserfs_resize(s, blocks);
+      if (rc != 0)
+	  return rc;
+  }
+
+  if ((unsigned long)(*flags & MS_RDONLY) == (s->s_flags & MS_RDONLY)) {
+    /* there is nothing to do to remount read-only fs as read-only fs */
+    return 0;
+  }
+  
+  if (*flags & MS_RDONLY) {
+    /* try to remount file system with read-only permissions */
+    if (le16_to_cpu (rs->s_state) == REISERFS_VALID_FS || s->u.reiserfs_sb.s_mount_state != REISERFS_VALID_FS) {
+      return 0;
+    }
+
+    journal_begin(&th, s, 10) ;
+    /* Mounting a rw partition read-only. */
+    reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+    rs->s_state = cpu_to_le16 (s->u.reiserfs_sb.s_mount_state);
+    journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+    s->s_dirt = 0;
+  } else {
+    s->u.reiserfs_sb.s_mount_state = le16_to_cpu(rs->s_state) ;
+    s->s_flags &= ~MS_RDONLY ; /* now it is safe to call journal_begin */
+    journal_begin(&th, s, 10) ;
+
+    /* Mount a partition which is read-only, read-write */
+    reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+    s->u.reiserfs_sb.s_mount_state = le16_to_cpu (rs->s_state);
+    s->s_flags &= ~MS_RDONLY;
+    rs->s_state = cpu_to_le16 (REISERFS_ERROR_FS);
+    /* mark_buffer_dirty (SB_BUFFER_WITH_SB (s), 1); */
+    journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+    s->s_dirt = 0;
+    s->u.reiserfs_sb.s_mount_state = REISERFS_VALID_FS ;
+  }
+  /* this will force a full flush of all journal lists */
+  SB_JOURNAL(s)->j_must_wait = 1 ;
+  journal_end(&th, s, 10) ;
+  return 0;
+}
+
+
+static int read_bitmaps (struct super_block * s)
+{
+    int i, bmp, dl ;
+    struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK(s);
+
+    SB_AP_BITMAP (s) = reiserfs_kmalloc (sizeof (struct buffer_head *) * le16_to_cpu (rs->s_bmap_nr), GFP_BUFFER, s);
+    if (SB_AP_BITMAP (s) == 0)
+	return 1;
+    memset (SB_AP_BITMAP (s), 0, sizeof (struct buffer_head *) * le16_to_cpu (rs->s_bmap_nr));
+
+    /* reiserfs leaves the first 64k unused so that any partition
+       labeling scheme currently used will have enough space. Then we
+       need one block for the super.  -Hans */
+    bmp = (REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize) + 1;	/* first of bitmap blocks */
+    SB_AP_BITMAP (s)[0] = reiserfs_bread (s->s_dev, bmp, s->s_blocksize);
+    if(!SB_AP_BITMAP(s)[0])
+	return 1;
+    for (i = 1, bmp = dl = rs->s_blocksize * 8; i < le16_to_cpu (rs->s_bmap_nr); i ++) {
+	SB_AP_BITMAP (s)[i] = reiserfs_bread (s->s_dev, bmp, s->s_blocksize);
+	if (!SB_AP_BITMAP (s)[i])
+	    return 1;
+	bmp += dl;
+    }
+
+    return 0;
+}
+
+static int read_old_bitmaps (struct super_block * s)
+{
+  int i ;
+  struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK(s);
+  int bmp1 = (REISERFS_OLD_DISK_OFFSET_IN_BYTES / s->s_blocksize) + 1;  /* first of bitmap blocks */
+
+  /* read true bitmap */
+  SB_AP_BITMAP (s) = reiserfs_kmalloc (sizeof (struct buffer_head *) * le16_to_cpu (rs->s_bmap_nr), GFP_BUFFER, s);
+  if (SB_AP_BITMAP (s) == 0)
+    return 1;
+
+  memset (SB_AP_BITMAP (s), 0, sizeof (struct buffer_head *) * le16_to_cpu (rs->s_bmap_nr));
+
+  for (i = 0; i < le16_to_cpu (rs->s_bmap_nr); i ++) {
+    SB_AP_BITMAP (s)[i] = reiserfs_bread (s->s_dev, bmp1 + i, s->s_blocksize);
+    if (!SB_AP_BITMAP (s)[i])
+      return 1;
+  }
+
+  return 0;
+}
+
+void check_bitmap (struct super_block * s)
+{
+  int i = 0;
+  int free = 0;
+  char * buf;
+
+  while (i < SB_BLOCK_COUNT (s)) {
+    buf = SB_AP_BITMAP (s)[i / (s->s_blocksize * 8)]->b_data;
+    if (!reiserfs_test_le_bit (i % (s->s_blocksize * 8), buf))
+      free ++;
+    i ++;
+  }
+
+  if (free != SB_FREE_BLOCKS (s))
+    reiserfs_warning ("vs-4000: check_bitmap: %d free blocks, must be %d\n",
+		      free, SB_FREE_BLOCKS (s));
+}
+
+#ifdef SUPPORT_OLD_FORMAT 
+
+/* support old disk layout */
+static int read_old_super_block (struct super_block * s, int size)
+{
+    struct buffer_head * bh;
+    struct reiserfs_super_block * rs;
+
+    printk("read_old_super_block: try to find super block in old location\n");
+    /* there are only 4k-sized blocks in v3.5.10 */
+    if (size != REISERFS_OLD_BLOCKSIZE)
+	set_blocksize(s->s_dev, REISERFS_OLD_BLOCKSIZE);
+    bh = bread (s->s_dev, 
+		REISERFS_OLD_DISK_OFFSET_IN_BYTES / REISERFS_OLD_BLOCKSIZE, 
+		REISERFS_OLD_BLOCKSIZE);
+    if (!bh) {
+	printk("read_old_super_block: unable to read superblock on dev %s\n", kdevname(s->s_dev));
+	return 1;
+    }
+
+    rs = (struct reiserfs_super_block *)bh->b_data;
+    if (strncmp (rs->s_magic,  REISERFS_SUPER_MAGIC_STRING, strlen ( REISERFS_SUPER_MAGIC_STRING))) {
+	/* pre-journaling version check */
+	if(!strncmp((char*)rs + REISERFS_SUPER_MAGIC_STRING_OFFSET_NJ,
+		    REISERFS_SUPER_MAGIC_STRING, strlen(REISERFS_SUPER_MAGIC_STRING))) {
+	    printk("read_old_super_blockr: a pre-journaling reiserfs filesystem isn't suitable there.\n");
+	    brelse(bh);
+	    return 1;
+	}
+	  
+	brelse (bh);
+	printk ("read_old_super_block: can't find a reiserfs filesystem on dev %s.\n", kdevname(s->s_dev));
+	return 1;
+    }
+
+    if(REISERFS_OLD_BLOCKSIZE != le16_to_cpu (rs->s_blocksize)) {
+	printk("read_old_super_block: blocksize mismatch, super block corrupted\n");
+	brelse(bh);
+	return 1;
+    }	
+
+    s->s_blocksize = REISERFS_OLD_BLOCKSIZE;
+    s->s_blocksize_bits = 0;
+    while ((1 << s->s_blocksize_bits) != s->s_blocksize)
+	s->s_blocksize_bits ++;
+
+    SB_BUFFER_WITH_SB (s) = bh;
+    SB_DISK_SUPER_BLOCK (s) = rs;
+    s->s_op = &reiserfs_sops;
+    return 0;
+}
+#endif
+
+//
+// FIXME: mounting old filesystems we _must_ change magic string to
+// make then unmountable by reiserfs of 3.5.x
+//
+static int read_super_block (struct super_block * s, int size)
+{
+    struct buffer_head * bh;
+    struct reiserfs_super_block * rs;
+
+    bh = bread (s->s_dev, REISERFS_DISK_OFFSET_IN_BYTES / size, size);
+    if (!bh) {
+	printk("read_super_block: unable to read superblock on dev %s\n", kdevname(s->s_dev));
+	return 1;
+    }
+
+    rs = (struct reiserfs_super_block *)bh->b_data;
+    if (!is_reiserfs_magic_string (rs)) {
+	printk ("read_super_block: can't find a reiserfs filesystem on dev %s\n",
+		kdevname(s->s_dev));
+	brelse (bh);
+	return 1;
+    }
+
+    //
+    // ok, reiserfs signature (old or new) found in 64-th 1k block of
+    // the device
+    //
+
+#ifndef SUPPORT_OLD_FORMAT 
+    // with SUPPORT_OLD_FORMAT undefined - detect old format by
+    // checking super block version
+    if (le16_to_cpu (rs->s_version) != REISERFS_VERSION_2) { 
+	brelse (bh);
+	printk ("read_super_block: unsupported version (%d) of reiserfs found on dev %s\n",
+		le16_to_cpu (rs->s_version), kdevname(s->s_dev));
+	return 1;
+    }
+#endif
+    
+    s->s_blocksize = le16_to_cpu (rs->s_blocksize);
+    s->s_blocksize_bits = 0;
+    while ((1 << s->s_blocksize_bits) != s->s_blocksize)
+	s->s_blocksize_bits ++;
+
+    brelse (bh);
+    
+    if (s->s_blocksize != size)
+	set_blocksize (s->s_dev, s->s_blocksize);
+    bh = reiserfs_bread (s->s_dev, REISERFS_DISK_OFFSET_IN_BYTES / s->s_blocksize, s->s_blocksize);
+    if (!bh) {
+	printk("read_super_block: unable to read superblock on dev %s\n", kdevname(s->s_dev));
+	return 1;
+    }
+    
+    rs = (struct reiserfs_super_block *)bh->b_data;
+    if (!is_reiserfs_magic_string (rs) ||
+	le16_to_cpu (rs->s_blocksize) != s->s_blocksize) {
+	brelse (bh);
+	printk ("read_super_block: can't find a reiserfs filesystem on dev %s.\n", kdevname(s->s_dev));
+	return 1;
+    }
+    /* must check to be sure we haven't pulled an old format super out
+    ** of the old format's log.  This is a kludge of a check, but it
+    ** will work.  If block we've just read in is inside the
+    ** journal for that super, it can't be valid.  
+    */
+    if (bh->b_blocknr >= le32_to_cpu(rs->s_journal_block) && 
+	bh->b_blocknr < (le32_to_cpu(rs->s_journal_block) + JOURNAL_BLOCK_COUNT)) {
+	brelse(bh) ;
+	printk("super-459: read_super_block: super found at block %lu is within its own log. "
+	       "It must not be of this format type.\n", bh->b_blocknr) ;
+	return 1 ;
+    }
+    SB_BUFFER_WITH_SB (s) = bh;
+    SB_DISK_SUPER_BLOCK (s) = rs;
+    s->s_op = &reiserfs_sops;
+    return 0;
+}
+
+/* after journal replay, reread all bitmap and super blocks */
+static int reread_meta_blocks(struct super_block *s) {
+  int i ;
+  ll_rw_block(READ, 1, &(SB_BUFFER_WITH_SB(s))) ;
+  wait_on_buffer(SB_BUFFER_WITH_SB(s)) ;
+  if (!buffer_uptodate(SB_BUFFER_WITH_SB(s))) {
+    printk("reread_meta_blocks, error reading the super\n") ;
+    return 1 ;
+  }
+
+  for (i = 0; i < SB_BMAP_NR(s) ; i++) {
+    ll_rw_block(READ, 1, &(SB_AP_BITMAP(s)[i])) ;
+    wait_on_buffer(SB_AP_BITMAP(s)[i]) ;
+    if (!buffer_uptodate(SB_AP_BITMAP(s)[i])) {
+      printk("reread_meta_blocks, error reading bitmap block number %d at %ld\n", i, SB_AP_BITMAP(s)[i]->b_blocknr) ;
+      return 1 ;
+    }
+  }
+  return 0 ;
+
+}
+
+
+/////////////////////////////////////////////////////
+// hash detection stuff
+
+
+// if root directory is empty - we set default - Yura's - hash and
+// warn about it
+// FIXME: we look for only one name in a directory. If tea and yura
+// bith have the same value - we ask user to send report to the
+// mailing list
+__u32 find_hash_out (struct super_block * s)
+{
+    int retval;
+    struct inode * inode;
+    struct cpu_key key;
+    INITIALIZE_PATH (path);
+    struct reiserfs_dir_entry de;
+    __u32 hash = DEFAULT_HASH;
+
+    inode = s->s_root->d_inode;
+
+    while (1) {
+	make_cpu_key (&key, inode, ~0, TYPE_DIRENTRY, 3);
+	retval = search_by_entry_key (s, &key, &path, &de);
+	if (retval == IO_ERROR) {
+	    pathrelse (&path);
+	    return UNSET_HASH ;
+	}
+	if (retval == NAME_NOT_FOUND)
+	    de.de_entry_num --;
+	set_de_name_and_namelen (&de);
+	if (le32_to_cpu (de.de_deh[de.de_entry_num].deh_offset) == DOT_DOT_OFFSET) {
+	    /* allow override in this case */
+	    if (reiserfs_rupasov_hash(s)) {
+		hash = YURA_HASH ;
+	    }
+	    reiserfs_warning("reiserfs: FS seems to be empty, autodetect "
+	                     "is using the default hash\n");
+	    break;
+	}
+	if (GET_HASH_VALUE(yura_hash (de.de_name, de.de_namelen)) == 
+	    GET_HASH_VALUE(keyed_hash (de.de_name, de.de_namelen))) {
+	    reiserfs_warning ("reiserfs: Could not detect hash function "
+			      "please mount with -o hash={tea,rupasov,r5}\n") ;
+	    hash = UNSET_HASH ;
+	    break;
+	}
+	if (GET_HASH_VALUE(le32_to_cpu(de.de_deh[de.de_entry_num].deh_offset))==
+	    GET_HASH_VALUE (yura_hash (de.de_name, de.de_namelen)))
+	    hash = YURA_HASH;
+	else
+	    hash = TEA_HASH;
+	break;
+    }
+
+    pathrelse (&path);
+    return hash;
+}
+
+// finds out which hash names are sorted with
+static int what_hash (struct super_block * s)
+{
+    __u32 code;
+
+    code = le32_to_cpu (s->u.reiserfs_sb.s_rs->s_hash_function_code);
+
+    /* reiserfs_hash_detect() == true if any of the hash mount options
+    ** were used.  We must check them to make sure the user isn't
+    ** using a bad hash value
+    */
+    if (code == UNSET_HASH || reiserfs_hash_detect(s))
+	code = find_hash_out (s);
+
+    if (code != UNSET_HASH && reiserfs_hash_detect(s)) {
+	/* detection has found the hash, and we must check against the 
+	** mount options 
+	*/
+	if (reiserfs_rupasov_hash(s) && code != YURA_HASH) {
+	    printk("REISERFS: Error, tea hash detected, "
+		   "unable to force rupasov hash\n") ;
+	    code = UNSET_HASH ;
+	} else if (reiserfs_tea_hash(s) && code != TEA_HASH) {
+	    printk("REISERFS: Error, rupasov hash detected, "
+		   "unable to force tea hash\n") ;
+	    code = UNSET_HASH ;
+	} else if (reiserfs_r5_hash(s) && code != R5_HASH) {
+	    printk("REISERFS: Error, r5 hash detected, "
+		   "unable to force r5 hash\n") ;
+	    code = UNSET_HASH ;
+	} 
+    } else { 
+        /* find_hash_out was not called or could not determine the hash */
+	if (reiserfs_rupasov_hash(s)) {
+	    code = YURA_HASH ;
+	} else if (reiserfs_tea_hash(s)) {
+	    code = TEA_HASH ;
+	} else if (reiserfs_r5_hash(s)) {
+	    code = R5_HASH ;
+	} 
+    }
+
+    /* if we are mounted RW, and we have a new valid hash code, update 
+    ** the super
+    */
+    if (code != UNSET_HASH && 
+	!(s->s_flags & MS_RDONLY) && 
+        code != le32_to_cpu (s->u.reiserfs_sb.s_rs->s_hash_function_code)) {
+        s->u.reiserfs_sb.s_rs->s_hash_function_code = cpu_to_le32(code) ;
+    }
+    return code;
+}
+
+// return pointer to appropriate function
+static hashf_t hash_function (struct super_block * s)
+{
+    switch (what_hash (s)) {
+    case TEA_HASH:
+	reiserfs_warning ("Using tea hash to sort names\n");
+	return keyed_hash;
+    case YURA_HASH:
+	reiserfs_warning ("Using rupasov hash to sort names\n");
+	return yura_hash;
+    case R5_HASH:
+	reiserfs_warning ("Using r5 hash to sort names\n");
+	return r5_hash;
+    }
+    return NULL;
+}
+
+// this is used to set up correct value for old partitions
+int function2code (hashf_t func)
+{
+    if (func == keyed_hash)
+	return TEA_HASH;
+    if (func == yura_hash)
+	return YURA_HASH;
+    if (func == r5_hash)
+	return R5_HASH;
+
+    BUG() ; // should never happen 
+
+    return 0;
+}
+
+
+//
+// a portion of this function, particularly the VFS interface portion,
+// was derived from minix or ext2's analog and evolved as the
+// prototype did. You should be able to tell which portion by looking
+// at the ext2 code and comparing. It's subfunctions contain no code
+// used as a template unless they are so labeled.
+//
+struct super_block * reiserfs_read_super (struct super_block * s, void * data, int silent)
+{
+    int size;
+    struct inode *root_inode;
+    kdev_t dev = s->s_dev;
+    int j;
+    extern int *blksize_size[];
+    struct reiserfs_transaction_handle th ;
+    int old_format = 0;
+    unsigned long blocks;
+    int jinit_done = 0 ;
+    struct reiserfs_iget4_args args ;
+
+
+    memset (&s->u.reiserfs_sb, 0, sizeof (struct reiserfs_sb_info));
+
+    if (parse_options ((char *) data, &(s->u.reiserfs_sb.s_mount_opt), &blocks) == 0) {
+	return NULL;
+    }
+
+    if (blocks) {
+  	printk("reserfs: resize option for remount only\n");
+	return NULL;
+    }	
+
+    if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)] != 0) {
+	/* as blocksize is set for partition we use it */
+	size = blksize_size[MAJOR(dev)][MINOR(dev)];
+    } else {
+	size = BLOCK_SIZE;
+	set_blocksize (s->s_dev, BLOCK_SIZE);
+    }
+
+    /* read block (64-th 1k block), which can contain reiserfs super block */
+    if (read_super_block (s, size)) {
+#ifdef SUPPORT_OLD_FORMAT
+	// try old format (undistributed bitmap, super block in 8-th 1k block of a device)
+	if(read_old_super_block(s,size)) 
+	    goto error;
+	else
+	    old_format = 1;
+#endif
+	goto error ;
+    }
+
+    s->u.reiserfs_sb.s_mount_state = le16_to_cpu (SB_DISK_SUPER_BLOCK (s)->s_state); /* journal victim */
+    s->u.reiserfs_sb.s_mount_state = REISERFS_VALID_FS ;
+
+    if (old_format ? read_old_bitmaps(s) : read_bitmaps(s)) { 
+	printk ("reiserfs_read_super: unable to read bitmap\n");
+	goto error;
+    }
+#ifdef CONFIG_REISERFS_CHECK
+    printk("reiserfs:warning: CONFIG_REISERFS_CHECK is set ON\n");
+    printk("reiserfs:warning: - it is slow mode for debugging.\n");
+#endif
+
+    // set_device_ro(s->s_dev, 1) ;
+    if (journal_init(s)) {
+	printk("reiserfs_read_super: unable to initialize journal space\n") ;
+	goto error ;
+    } else {
+	jinit_done = 1 ; /* once this is set, journal_release must be called
+			 ** if we error out of the mount 
+			 */
+    }
+    if (reread_meta_blocks(s)) {
+	printk("reiserfs_read_super: unable to reread meta blocks after journal init\n") ;
+	goto error ;
+    }
+
+    if (replay_only (s))
+	goto error;
+
+    if (is_read_only(s->s_dev) && !(s->s_flags & MS_RDONLY)) {
+        printk("clm-7000: Detected readonly device, marking FS readonly\n") ;
+	s->s_flags |= MS_RDONLY ;
+    }
+    args.objectid = REISERFS_ROOT_PARENT_OBJECTID ;
+    root_inode = iget4 (s, REISERFS_ROOT_OBJECTID, 0, (void *)(&args));
+    if (!root_inode) {
+	printk ("reiserfs_read_super: get root inode failed\n");
+	goto error;
+    }
+
+    s->s_root = d_alloc_root(root_inode);  
+    if (!s->s_root) {
+	iput(root_inode);
+	goto error;
+    }
+
+    // define and initialize hash function
+    s->u.reiserfs_sb.s_hash_function = hash_function (s);
+    if (s->u.reiserfs_sb.s_hash_function == NULL) {
+      dput(s->s_root) ;
+      s->s_root = NULL ;
+      goto error ;
+    }
+
+    if (!(s->s_flags & MS_RDONLY)) {
+	struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
+
+	journal_begin(&th, s, 1) ;
+	reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ;
+
+	rs->s_state = cpu_to_le16 (REISERFS_ERROR_FS);
+
+        if (strncmp (rs->s_magic,  REISER2FS_SUPER_MAGIC_STRING, 
+		     strlen ( REISER2FS_SUPER_MAGIC_STRING))) {
+	    if (le16_to_cpu(rs->s_version) != 0)
+		BUG ();
+	    // filesystem created under 3.5.x found
+	    if (!old_format_only (s)) {
+		reiserfs_warning("reiserfs: converting 3.5.x filesystem to the new format\n") ;
+		// after this 3.5.x will not be able to mount this partition
+		memcpy (rs->s_magic, REISER2FS_SUPER_MAGIC_STRING, 
+			sizeof (REISER2FS_SUPER_MAGIC_STRING));
+
+		reiserfs_convert_objectid_map_v1(s) ;
+	    } else {
+		reiserfs_warning("reiserfs: using 3.5.x disk format\n") ;
+	    }
+	} else {
+	    // new format found
+	    set_bit (REISERFS_CONVERT, &(s->u.reiserfs_sb.s_mount_opt));
+	}
+
+	// mark hash in super block: it could be unset. overwrite should be ok
+	rs->s_hash_function_code = cpu_to_le32 (function2code (s->u.reiserfs_sb.s_hash_function));
+
+	journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s));
+	journal_end(&th, s, 1) ;
+	s->s_dirt = 0;
+    } else {
+	struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
+	if (strncmp (rs->s_magic,  REISER2FS_SUPER_MAGIC_STRING, 
+		     strlen ( REISER2FS_SUPER_MAGIC_STRING))) {
+	    reiserfs_warning("reiserfs: using 3.5.x disk format\n") ;
+	}
+    }
+
+    init_waitqueue_head (&(s->u.reiserfs_sb.s_wait));
+
+    printk("%s\n", reiserfs_get_version_string()) ;
+    return s;
+
+ error:
+    if (jinit_done) { /* kill the commit thread, free journal ram */
+	journal_release_error(NULL, s) ;
+    }
+    if (SB_DISK_SUPER_BLOCK (s)) {
+	for (j = 0; j < SB_BMAP_NR (s); j ++) {
+	    if (SB_AP_BITMAP (s))
+		brelse (SB_AP_BITMAP (s)[j]);
+	}
+	if (SB_AP_BITMAP (s))
+	    reiserfs_kfree (SB_AP_BITMAP (s), sizeof (struct buffer_head *) * SB_BMAP_NR (s), s);
+    }
+    if (SB_BUFFER_WITH_SB (s))
+	brelse(SB_BUFFER_WITH_SB (s));
+
+    return NULL;
+}
+
+
+//
+// a portion of this function, particularly the VFS interface portion,
+// was derived from minix or ext2's analog and evolved as the
+// prototype did. You should be able to tell which portion by looking
+// at the ext2 code and comparing. It's subfunctions contain no code
+// used as a template unless they are so labeled.
+//
+int reiserfs_statfs (struct super_block * s, struct statfs * buf)
+{
+  struct reiserfs_super_block * rs = SB_DISK_SUPER_BLOCK (s);
+  
+				/* changed to accomodate gcc folks.*/
+  buf->f_type =  REISERFS_SUPER_MAGIC;
+  buf->f_bsize = le32_to_cpu (s->s_blocksize);
+  buf->f_blocks = le32_to_cpu (rs->s_block_count) - le16_to_cpu (rs->s_bmap_nr) - 1;
+  buf->f_bfree = le32_to_cpu (rs->s_free_blocks);
+  buf->f_bavail = buf->f_bfree;
+  buf->f_files = -1;
+  buf->f_ffree = -1;
+  buf->f_namelen = (REISERFS_MAX_NAME_LEN (s->s_blocksize));
+  return 0;
+}
+
+#ifdef __KERNEL__
+
+static DECLARE_FSTYPE_DEV(reiserfs_fs_type,"reiserfs",reiserfs_read_super);
+
+//
+// this is exactly what 2.3.99-pre9's init_ext2_fs is
+//
+static int __init init_reiserfs_fs (void)
+{
+        return register_filesystem(&reiserfs_fs_type);
+}
+
+EXPORT_NO_SYMBOLS;
+
+//
+// this is exactly what 2.3.99-pre9's init_ext2_fs is
+//
+static void __exit exit_reiserfs_fs(void)
+{
+        unregister_filesystem(&reiserfs_fs_type);
+}
+
+module_init(init_reiserfs_fs) ;
+module_exit(exit_reiserfs_fs) ;
+
+#endif
+
+
+

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)