/*****************************************************************************

       Copyright   1995, 1996 Digital Equipment Corporation,
                       Maynard, Massachusetts.

                        All Rights Reserved

Permission to use, copy, modify, and distribute this software and its 
documentation for any purpose and without fee is hereby granted, provided  
that the copyright notice and this permission notice appear in all copies  
of software and supporting documentation, and that the name of Digital not  
be used in advertising or publicity pertaining to distribution of the software 
without specific, written prior permission. Digital grants this permission 
provided that you prominently mark, as not part of the original, any 
modifications made to this software or documentation.

Digital Equipment Corporation disclaims all warranties and/or guarantees  
with regard to this software, including all implied warranties of fitness for 
a particular purpose and merchantability, and makes no representations 
regarding the use of, or the results of the use of, the software and 
documentation in terms of correctness, accuracy, reliability, currentness or
otherwise; and you rely on the software, documentation and results solely at 
your own risk. 

******************************************************************************/

/*****************************************************************************
*
* The portions of this that were updated to support ReiserFS are copyright 1996, 1997, 1998, 1999,
* 2000 Hans Reiser, excepting those portions which were taken from reiserfs/journal.c which are
* copyright Chris Mason 1999, 2000, and those portions (taken from reiserfs/hashes.c) labeled as
* copyright Jeremy Fitzhardinge.
*
*****************************************************************************/

/*
 * Keyed 32-bit hash function using TEA in a Davis-Meyer function
 *   H0 = Key
 *   Hi = E Mi(Hi-1) + Hi-1
 *
 * (see Applied Cryptography, 2nd edition, p448).
 *
 * Jeremy Fitzhardinge <jeremy@zip.com.au> 1998
 * 
 */



/* This is a set of functions that provides minimal filesystem
 * functionality to the Linux bootstrapper.  All we can do is
 * open and read files... but that's all we need 8-)
 */


#if 0
#define _LINUX_REISER_FS_SB
#define _LINUX_REISER_FS_H
#endif

#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/kdev_t.h>

#include <linux/locks.h>
#include <linux/init.h>
#include <linux/unistd.h>
//#include <string.h>
//#include <ctype.h>

#include <linux/reiserfs_fs_i.h>
#include <linux/reiserfs_fs_sb.h>
#include <linux/reiserfs_fs.h>

#if 0
#define MM_DEBUG_KEY
#define DEBUG_REISERFS
#endif


#include "fs.h"
#include "reiserfs.h"
#include "milo.h"

static struct reiserfs_journal_ptr *j_table=NULL;

static __u32 j_tab_count = 0;

static __u32	j_start ;
static __u32	j_last_flush_trans_id ;
static __u32	j_trans_id ;
#ifdef DEBUG_REISERFS
static __u32   j_mount_id;
#endif
#define MAX_OPEN_FILES		4

static int fd ;

#if 0
static unsigned long ErrorNumber;
#endif

static struct milo_item n_item_buf;
static struct milo_item it_buf;
static struct milo_item it_open;

#if 0
static struct m_item_head * ih;
#endif
static struct m_block_head * bh;
static struct m_reiserfs_super_block sb;

static struct m_key m_root_key = {
	REISERFS_ROOT_PARENT_OBJECTID,
	REISERFS_ROOT_OBJECTID,
	{ (struct offset_m_v1){ 0, 0 } }
};
static hashf_t p_hash_function;

#ifdef DEBUG_REISERFS
#define PRINTK printk
#else
#define PRINTK(a...)
#endif


/*
#define m_copy_short_key(to,from)  memcpy((to), (from), SHORT_KEY_SIZE )
#define m_copy_item_head(to,from)  memcpy((to), (from), M_IH_SIZE)
*/

static int blocksize;
static long s_dev;
//static char filename[256];

#ifdef MILO_SYMLINK_DIRS
static char blkbuf[REISERFS_BLOCK_SIZE];
#endif
static char iblkbuf[REISERFS_BLOCK_SIZE];


static struct inode_table_entry
{
	struct reiserfs_m_inode inode;
	int inumber;
	int free;
	unsigned short old_mode;
} inode_table[MAX_OPEN_FILES];

static struct reiserfs_m_inode *reiserfs_m_iget( struct m_key * ino_k, struct milo_item * tmp_item);
static int reiserfs_m_bread(int fd, long blkno, char *buffer);
static void reiserfs_m_breadi(struct reiserfs_m_inode *ip, long blkno, char *buffer);
static int reiserfs_m_mount(long device, int quiet);
static int reiserfs_m_open(char *filename);
static void reiserfs_m_cat(struct reiserfs_m_inode *ip, char *buffer);
static void reiserfs_m_ls(char *dirname, char *devname);
static void reiserfs_m_close(int fd);
static long reiserfs_m_fdsize(int fd);


struct bootfs reiserfs =
{
	"reiserfs",
	0,
	0,

	reiserfs_m_mount,
	reiserfs_m_open,
	reiserfs_m_fdsize,
	reiserfs_m_bread,
	reiserfs_m_ls,
	reiserfs_m_close,
};



/*>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>*/

#define bread_desc(a,b,c) mm_bread((a),(b),(c),(char *)desc)
#define bread_commit(a,b,c) mm_bread((a),(b),(c),(char *)commit)
#define bread_jh(a,b,c) mm_bread((a),(b),(c),(char *)jh)


static inline int mm_bread(int dev, __u32 blkno, long b_size, char * buf)
{
	__u32 ret;
	ret = device_read(fd, buf, b_size,(long)( blkno*b_size));
	if (ret != b_size)
		{
			printk("\nBread(inline) error on read blkno %d=%d/%d/%lu/%p",ret,dev,blkno, b_size,buf);
			return -1;
		}
	return 0;
}

static __u32 check_j_block(__u32 p_blkn)
{
	int i,l,k,h;
	__u32 ret;
	h=j_tab_count;
	l=0;
	i=(l+h)/2;
	ret =p_blkn;
	k=0;
	while (l<h)
		{
			if (p_blkn == (j_table+i)->r_BlockNr ) { ret=(j_table+i)->j_BlockNr;break;}
			if (k)
				{
					if ( (p_blkn > (j_table+i)->r_BlockNr) || k++>1) break;
					i--;
				}
			else
				{
					if (p_blkn >  (j_table+i)->r_BlockNr ) l=i;
					if (p_blkn <  (j_table+i)->r_BlockNr ) h=i;
					if ((l+1)==h) { i=h;k=1;}
					else i=(l+h)/2;
				}
		}
	
	if ( p_blkn != ret ) PRINTK("(%d:%d)",p_blkn,ret);
	return (ret);
}

/* compares description block with commit block.  returns 1 if they differ, 0 if they are the same */
static int journal_compare_desc_commit(struct reiserfs_m_journal_desc *p_desc, 
									   struct reiserfs_m_journal_commit *p_commit)
{
	if (p_commit->j_trans_id != p_desc->j_trans_id ||
		p_commit->j_len != p_desc->j_len ||
		p_commit->j_len > JOURNAL_TRANS_MAX || 
		p_commit->j_len <= 0 )
		{
			return 1 ;
		}
  return 0 ;
}

/* returns 0 if it did not find a description block  
** returns -1 if it found a corrupt commit block
** returns 1 if both desc and commit were valid 
*/
static int journal_transaction_is_valid(__u32 p_blknr,
										__u32 *oldest_invalid_trans_id,
										__u32 *newest_mount_id)
{

	struct m_reiserfs_super_block *rs = &sb ;

	__u32 offset ;

	if (desc->j_len > 0 && !memcmp(desc->j_magic, JOURNAL_DESC_MAGIC, 8))
		{
			if (oldest_invalid_trans_id &&
				*oldest_invalid_trans_id && 
				desc->j_trans_id > *oldest_invalid_trans_id)
				{
					return 0 ;
				}
			if (newest_mount_id && *newest_mount_id > desc->j_mount_id)
				{
					return -1 ;
				}
			offset = p_blknr - rs->s_journal_block ;
		
			/* ok, we have a journal description block, lets see if the 
			** transaction was valid 
			*/
			if (bread_commit(s_dev,
							  rs->s_journal_block + ((offset + desc->j_len + 1) % JOURNAL_BLOCK_COUNT), rs->s_blocksize) )
				return 0 ;
			PRINTK("c");
			
			if (journal_compare_desc_commit(desc, commit))
				{
					if (oldest_invalid_trans_id)
						*oldest_invalid_trans_id = desc->j_trans_id ;
					return -1; 
				}
			return 1 ;
		}
	else
		{
			return 0 ;
		}
}




static int journal_read_transaction(__u32 cur_dblock,
									__u32 oldest_start, 
									__u32 oldest_trans_id,
									__u32 newest_mount_id)
{
	struct m_reiserfs_super_block *rs = &sb ;
	__u32 trans_id = 0 ;
	__u32 trans_offset ;
	int i;

	if (bread_desc(s_dev, cur_dblock, rs->s_blocksize) )
		return 1 ;

	PRINTK("d");

	trans_offset = cur_dblock - rs->s_journal_block ;

	if (desc->j_trans_id < oldest_trans_id)
		{
			return 1 ;
		}
	if (desc->j_mount_id != newest_mount_id)
		{
			return 1 ;
		}
	if (bread_commit(s_dev, rs->s_journal_block + ((trans_offset + desc->j_len + 1) % JOURNAL_BLOCK_COUNT), 
					  rs->s_blocksize) )
			return 1 ;
	PRINTK("c");
	if (journal_compare_desc_commit(desc, commit))
		{
			return 1; 
		}
	trans_id = desc->j_trans_id ;

	/* now we know we've got a good transaction, and it was inside the valid time ranges */

	PRINTK("\nTransaction_id=%d len=%d mount=%d ",desc->j_trans_id,desc->j_len,desc->j_mount_id);
	
	/* get all the buffer heads */
	for(i = 0 ; i < desc->j_len ; i++)
		{
			(j_table+j_tab_count)->j_BlockNr =  rs->s_journal_block + (trans_offset + 1 + i) % JOURNAL_BLOCK_COUNT;
			(j_table+j_tab_count)->r_BlockNr =  ((i < JOURNAL_TRANS_HALF)?(desc->j_realblock[i]):(commit->j_realblock[i - JOURNAL_TRANS_HALF]));
				
			if ((j_table+j_tab_count)->r_BlockNr >= rs->s_journal_block &&
				(j_table+j_tab_count)->r_BlockNr < (rs->s_journal_block+JOURNAL_BLOCK_COUNT))
				{
					printk("clm-2026: REPLAY FAILURE fsck required! Trying to replay onto a log block\n") ;
					return -1 ;
				}
			j_tab_count++;
			PRINTK("!%d:(%d/%d)",j_tab_count,(j_table+j_tab_count)->r_BlockNr,(j_table+j_tab_count)->j_BlockNr);
		}
	PRINTK("*");
	cur_dblock = rs->s_journal_block + ((trans_offset + desc->j_len + 2) % JOURNAL_BLOCK_COUNT) ;


	/* init starting values for the first transaction, in case this is the last transaction to be replayed. */
	j_start = cur_dblock - rs->s_journal_block ;
	j_last_flush_trans_id = trans_id ;
	j_trans_id = trans_id + 1;
	return 0 ;
}


/*
** read and replay the log
** on a clean unmount, the journal header's next unflushed pointer will be to an invalid
** transaction.  This tests that before finding all the transactions in the log, whic makes normal mount times fast.
**
** After a crash, this starts with the next unflushed transaction, and replays until it finds one too old, or invalid.
**
** On exit, it sets things up so the first transaction will work correctly.
*/
static int m_journal_read(void)
{
	struct m_reiserfs_super_block *rs = &sb;
	__u32 last_flush_trans_id = 0 ;
	__u32 oldest_trans_id = 0;
	__u32 oldest_invalid_trans_id = 0 ;
	time_t start ;
	__u32 last_flush_start = 0;
	__u32 oldest_start = 0;
	__u32 cur_dblock = 0 ;
	__u32 newest_mount_id = 9 ;
	int valid_journal_header = 0 ;
	int replay_count = 0 ;
	int continue_replay = 1 ;
	int ret ;
	int tmp_i;
	
	cur_dblock = rs->s_journal_block ;
	start = CURRENT_TIME ;

	/* step 1, read in the journal header block.  Check the transaction is says is the first unflushed,
	** and if that transaction is not valid, replay is done
	*/


	if( !( jh=kmalloc( REISERFS_BLOCK_SIZE,0) ) )
		{
			printk("\n can't allocate jh");
			return -1;
		}
	if ( bread_jh(s_dev, rs->s_journal_block +	JOURNAL_BLOCK_COUNT,rs->s_blocksize) )
		{
			return -1;
		}
	PRINTK("\nH");
	if (jh->j_first_unflushed_offset >= 0 &&
		jh->j_first_unflushed_offset < JOURNAL_BLOCK_COUNT &&
		jh->j_last_flush_trans_id > 0)
		{
			last_flush_start = rs->s_journal_block + jh->j_first_unflushed_offset ;
			last_flush_trans_id = jh->j_last_flush_trans_id ;
			valid_journal_header = 1 ;
			
			/* now, we try to read the first unflushed offset.  If it is not valid, there is nothing more we can do,
			** and it makes no sense to read through the whole log.
			*/

			desc   = kmalloc( REISERFS_BLOCK_SIZE,0);
			commit = kmalloc( REISERFS_BLOCK_SIZE,0);

			if (!desc || !commit)
				{
					printk("\ncan't allocate desk or commit. reboot ");
					if (desc) kfree(desc);
					if (commit) kfree(commit);
					return -1;
				}

			bread_desc(s_dev, last_flush_start, rs->s_blocksize) ;
			PRINTK("d");
			ret = journal_transaction_is_valid(last_flush_start,NULL, NULL) ;

			if (!ret)
				{
					continue_replay = 0 ;
				}
		}
	
	/* ok, there are transactions that need to be replayed.  
	** start with the first log block, find all the valid transactions, 
	** and pick out the oldest.
	*/
	if (continue_replay) printk("\nnot cleanly unmounted, need journal replay.\nChecking, Please wait");
	tmp_i=0;
	while(continue_replay  &&   cur_dblock < (rs->s_journal_block + JOURNAL_BLOCK_COUNT) )
		{
			if ( ! ( (tmp_i++) % 256 ))printk(".");
			bread_desc(s_dev, cur_dblock, rs->s_blocksize) ;
			if ((ret = journal_transaction_is_valid(cur_dblock,&oldest_invalid_trans_id, 
													&newest_mount_id)) == 1)
				{
//					PRINTK("d=%d(%d)",cur_dblock,desc->j_len);
					if ( oldest_start == 0 || newest_mount_id < desc->j_mount_id)
						{
							oldest_trans_id = desc->j_trans_id ;
							oldest_start = cur_dblock ;
							newest_mount_id = desc->j_mount_id ;
						}
					else
						{
							if (oldest_trans_id > desc->j_trans_id)  /* one just read was older */
								{
									oldest_trans_id = desc->j_trans_id ;
									oldest_start = cur_dblock ;
								}
						}
					cur_dblock += desc->j_len + 2 ;
				} 
			else
				{
					cur_dblock++ ;
				}
		}
//	PRINTK("\n->%d<-",cur_dblock);
	/* step three, starting at the oldest transaction, replay */
	if (last_flush_start > 0)
		{
			oldest_start = last_flush_start ;
			oldest_trans_id = last_flush_trans_id ;
		} 
	cur_dblock = oldest_start ;

	if (oldest_trans_id)
		{
			if (continue_replay) printk("\nReplying");
			PRINTK("\nclm-2035: Starting replay from offset %lu, trans_id %lu\n",
				   cur_dblock - rs->s_journal_block, oldest_trans_id) ;
		}

	replay_count = 0 ;
	if (!j_table)
		j_table=( struct reiserfs_journal_ptr *)kmalloc(REISERFS_BLOCK_SIZE*8,0);
	if (!j_table)
		{
			printk("\ncan't allocate j_table");
			return -1;
		}
	
	j_tab_count=0;

	PRINTK("%d/%d/%d/",oldest_start, oldest_trans_id, newest_mount_id);
	while(continue_replay && oldest_trans_id > 0)
		{
			printk(".");
			ret = journal_read_transaction(cur_dblock, oldest_start, oldest_trans_id, newest_mount_id) ;
			if (ret < 0)
				{
					continue_replay =0;
				}
			else
				if (ret != 0)        /* ==1 */
					{
						break ;
					}
			cur_dblock = rs->s_journal_block + j_start ;
			replay_count++ ;
		}

	if (oldest_trans_id == 0)
		{
			printk("Journal hasn't valid transactions.\n") ;
		}

	PRINTK("clm-2038: Prepared %d transactions in %lu seconds, mount_id now %lu\n",
		   replay_count, CURRENT_TIME - start, j_mount_id) ;

	
	{  /* sorting j_table by real block number*/
		int i,k,l,n;
		int ss_sort;
		__u32 field_ex;
		k=0;
		l = j_tab_count;
		n=(l+k+1)/2;
		ss_sort = 1;
		while( ss_sort || n>1)
			{
			printk(".");
			PRINTK("\nS");
				ss_sort = 0;
				for(i=0;(i+n)<l;i++)
					{
						if ((j_table+i)->r_BlockNr>(j_table+i+n)->r_BlockNr)
							{
								PRINTK("s");
								field_ex                 = (j_table+i)->r_BlockNr;
								(j_table+i)->r_BlockNr   = (j_table+i+n)->r_BlockNr;
								(j_table+i+n)->r_BlockNr = field_ex;
								field_ex                 = (j_table+i)->j_BlockNr;
								(j_table+i)->j_BlockNr   = (j_table+i+n)->j_BlockNr;
								(j_table+i+n)->j_BlockNr = field_ex;
								ss_sort = 1;
							}
					}
				n=(n+1)/2;
			}
		for(i=0;i<l;i++)
			{
				PRINTK("\n%3d,(%5d/%5d)",i, (j_table+i)->r_BlockNr,	(j_table+i)->j_BlockNr);
			}
	}

	PRINTK("\n");
	kfree(desc);
	kfree(commit);
	kfree(jh);

	
	return 0 ;
}


static inline int  m__comp_keys (struct m_key * k1, struct m_key * k2, int key_size)
{
  __u32 * p_s_key1, * p_s_key2;

  p_s_key1 = (__u32 *)k1;
  p_s_key2 = (__u32 *)k2;

  if ( *p_s_key1 < *p_s_key2 )  return -1;
  if ( *p_s_key1 > *p_s_key2 )  return 1;

  ++p_s_key1;
  ++p_s_key2;

  if ( *p_s_key1 < *p_s_key2 )  return -1;
  if ( *p_s_key1 > *p_s_key2 )  return 1;
  
  if (key_size)
	  {
		  if (m_le_key_version (k1)== ITEM_VERSION_1)
			  {
				  if (k1->u.k_offset_v1.k_offset < k2->u.k_offset_v2.k_offset) return -1;
				  if (k1->u.k_offset_v1.k_offset > k2->u.k_offset_v2.k_offset) return  1;
				  if (uniqueness2type(k1->u.k_offset_v1.k_uniqueness) < k2->u.k_offset_v2.k_type)   return -1;
				  if (uniqueness2type(k1->u.k_offset_v1.k_uniqueness) > k2->u.k_offset_v2.k_type)   return  1;
			  }
		  else
			  {
				  if (k1->u.k_offset_v2.k_offset < k2->u.k_offset_v2.k_offset) return -1;
				  if (k1->u.k_offset_v2.k_offset > k2->u.k_offset_v2.k_offset) return  1;
				  if (k1->u.k_offset_v2.k_type   < k2->u.k_offset_v2.k_type)   return -1;
				  if (k1->u.k_offset_v2.k_type   > k2->u.k_offset_v2.k_type)   return  1;
			  }
	  }
  return 0;
}


#define MAX_GENERATION_NUMBER  127
#define  MAX_GEN_NUMBER 127

#include <asm/types.h>

extern u32 keyed_hash(const char * msg, int len);
extern u32 yura_hash (const char *msg, int len);
extern u32 r5_hash (const char *msg, int len);

static struct  hash_function_t
{
	int  hash_function_code;
	hashf_t hash_function;
	char hash_function_name[10];
}  hash_function_table[]=
{
	{ TEA_HASH,   keyed_hash, "tea" },
	{ YURA_HASH,  yura_hash,  "rupasov" },
	{ R5_HASH,    r5_hash,     "r5" },
};



static hashf_t hash_function (__u32 tt)
{
	int ret;
	ret=1;
	switch (tt)
		{
		case TEA_HASH:
		case YURA_HASH:
		case R5_HASH:
			ret=tt-1;
		};
	return hash_function_table[ret].hash_function;
}


/* for debug */
#ifdef MM_DEBUG_KEY
#define vv_print_key(a,b)      vv_prn_key(0,(a),(b))
#define vv_print_key_stop(a,b) vv_prn_key(1,(a),(b))
#else
#define vv_print_key(a...)
#define vv_print_key_stop(a...) 
#endif


static inline int vv_prn_key(int opt,char *mes,struct m_key * k)
{
	printk("    %s  key= %u/%u/%lu/%u\n?",
		   mes,
		   k->k_dir_id,
		   k->k_objectid,
		   (long)k->u.k_offset_v2.k_offset,
		   (unsigned int) k->u.k_offset_v2.k_type);
	if (opt) kbd_getc();
	return 0;
		
}




/*make milo_item - copy unaligned space to aligned struct */
static struct milo_item * vv_m_ihead(struct milo_item * p_item, struct m_block_head * p_bh, int p_pos)
{
	int type;
#if 0
	struct
	{
		__u16 sd_mode;
		__u16 sd_reserved;
		__u32 sd_nlink;
		__u64 sd_size;	/* file size */
		__u32 sd_uid;		/* owner */
		__u32 sd_gid;		/* group */
		__u32 sd_atime;	/* time of last access */
		__u32 sd_mtime;	/* time file was last modified  */
		__u32 sd_ctime;	/* time inode (stat data) was last changed (except changes to sd_atime and sd_mtime) */
		__u32 sd_blocks;
		__u32 sd_rdev;
	}__attribute__ ((__packed__)) * sd;
	struct
	{
		__u32 deh_offset;
		__u32 deh_dir_id;
		__u32 deh_objectid;
		__u16 deh_location;
		__u16 deh_state;
	}__attribute__ ((__packed__))  * de;
#endif
	memcpy( p_item, (char *)(p_bh)+M_BLKH_SIZE+M_IH_SIZE*(p_pos), M_IH_SIZE );
			
	memcpy( &(p_item->ih_r_key), &(p_bh->blk_right_delim_key), M_KEY_SIZE);

	memcpy( &p_item->body, (char*)(p_bh)+ p_item->ih_item_location, p_item->ih_item_len );


#if 0
	sd=de=(char*)(p_bh)+ p_item->ih_item_location;
	switch (p_item->ih_key.u.k_offset_v2.k_type)
		{
		case 0:          /*stat data*/
			if ((p_item->body.sd.sd_mode!=sd->sd_mode) ||
				(p_item->body.sd.sd_size!=sd->sd_size) ||
				(p_item->body.sd.sd_uid!=sd->sd_uid) ||
				(p_item->body.sd.sd_gid!=sd->sd_gid) ||
				(p_item->body.sd.sd_atime!=sd->sd_atime) ||
				(p_item->body.sd.sd_mtime!=sd->sd_mtime) ||
				(p_item->body.sd.sd_ctime!=sd->sd_ctime) )
				{
					printk("\nstatdata unaligned trap\n  %p=%d/%ld/%d/%d/%d/%d/%d  %p=%d/%ld/%d/%d/%d/%d/%d\n?",
						   p_item,
						   p_item->body.sd.sd_mode,
						   p_item->body.sd.sd_size,
						   p_item->body.sd.sd_uid,
						   p_item->body.sd.sd_gid,
						   p_item->body.sd.sd_atime,
						   p_item->body.sd.sd_mtime,
						   p_item->body.sd.sd_ctime,
						   sd,
						   sd->sd_mode,
						   sd->sd_size,
						   sd->sd_uid,
						   sd->sd_gid,
						   sd->sd_atime,
						   sd->sd_mtime,
						   sd->sd_ctime);
					kbd_getc();
				}
			break;
		case 1:
			break;
		case 2:
			break;
		case 3:
			{
				int i;
				for ( i=0; i<p_item->u.ih_entry_count ; i++,de++ )
					if ( p_item->body.deh[i].deh_offset != de->deh_offset ||
						 p_item->body.deh[i].deh_dir_id != de->deh_dir_id ||
						 p_item->body.deh[i].deh_objectid != de->deh_objectid ||
						 p_item->body.deh[i].deh_location != de->deh_location )
						{
							printk("\ndir entry unaligned trap\n  %d/%d/%d/%d  %p=%d/%d/%d/%d\n?",
								   p_item->body.deh[i].deh_offset ,
								   p_item->body.deh[i].deh_dir_id ,
								   p_item->body.deh[i].deh_objectid ,
								   p_item->body.deh[i].deh_location ,
								   de,
								   de->deh_offset ,
								   de->deh_dir_id,
								   de->deh_objectid,
								   de->deh_location);
							kbd_getc();
						}
			}
			
			break;
		default:
			printk("\n error unaligned trap type =%d\n?",p_item->ih_key.u.k_offset_v2.k_type);
					kbd_getc();
			
		}
#endif

	if (m_le_key_version ((struct m_key *)p_item)== ITEM_VERSION_1 && p_item->ih_key.u.k_offset_v1.k_uniqueness)
		{
			type                                   = uniqueness2type(p_item->ih_key.u.k_offset_v1.k_uniqueness);
			p_item->ih_key.u.k_offset_v2.k_offset  = p_item->ih_key.u.k_offset_v1.k_offset;
			p_item->ih_key.u.k_offset_v2.k_type    = type;
		}


	
	p_item->it_Number = p_pos;
	p_item->it_Last = (p_pos == (p_bh->blk_nr_item-1)) ;
	vv_print_key("get item head",p_item);
	return p_item;
}


static int reiserfs_m_mount(long device, int quiet)
{
	extern void set_blocksize(kdev_t dev, int size);
	int i;
	long sb_offset;
     /* Initialize the inode table */
	for (i = 0; i < MAX_OPEN_FILES; i++)
		{
			inode_table[i].free = 1;
			inode_table[i].inumber = 0;
		}
	
	set_blocksize(device, REISERFS_BLOCK_SIZE);
	sb_offset = REISERFS_DISK_OFFSET_IN_BYTES;
	if (device_read(fd, (char *) &sb, sizeof(sb), sb_offset) !=  sizeof(sb))
		{
			printk("reiserfs sb read failed");
			device_close(fd);
			return (-1);
		}
	if (!is_reiserfs_magic_string(&sb))
		{
			printk ("reiserfs_read_super: can't find a reiserfs filesystem.\n");
			device_close(fd);		
			return -1;
		}
	blocksize = REISERFS_BLOCK_SIZE;
	sb.s_blocksize = REISERFS_BLOCK_SIZE;
	reiserfs.blocksize = REISERFS_BLOCK_SIZE;
	p_hash_function = hash_function (sb.s_hash_function_code);
	s_dev=device;
	j_tab_count=0;

//	if (m_journal_read(&sb) < 0)
//	m_journal_read is void -=sr=-

	if (m_journal_read() < 0)
		{
			printk("\nJournal failure. Unable replay");
			j_tab_count=0;
		}
	return (0);
}


static long j_device_read(int fd,char * p_buf, int blocksize, __u32 blkno)
{
	unsigned long offset;

	offset = blocksize *  (j_tab_count ? check_j_block(blkno) : blkno);
	return ( device_read( fd, p_buf, blocksize, offset) );
}


static struct milo_item * reiserfs_search_item(struct m_key * p_key, char * p_buf, struct milo_item *p_item)
{
	int i, j, l,h,r=0,s;
	struct m_key right_delim_key={
		-1,
		-1,
		{ (struct offset_m_v1){ -1L, 1}}
	};

	vv_print_key("search item",p_key);	

	if ( m_comp_keys((struct m_key *) p_item, p_key ))
		{
	
			bh = ( struct m_block_head *) p_buf;

			if ( j_device_read( fd, p_buf, blocksize, sb.s_root_block) !=blocksize )
				{
					printk("reiserfs_search_item(read root of tree): read error\n");
					return (NULL);
				}

			while ( bh->blk_level > 1 )  /* Search leaf*/
				{
					l = 0;
					h = VV_ITEMS(bh)-1;
					i = ( l + h )/2;
					s=0;
					while( l < h )
						{
							PRINTK("\n %d? r=%d:(%d<%d<%d)",s,r,l,i,h);
							r = m_comp_keys( VV_KEY(bh,i), p_key );
							if ( !r    ) break;
							if ( s )
								{
									if ( r >= 0 || s++>1 ) break;
									i++;
								}
							else
								{
									if ( r < 0 ) l = i; 
									if ( r > 0 ) h = i;
									if ( (l+1) == h ) { i = l; s = 1;}
									else                i = ( l + h )/2;
								}
						}
					vv_print_key("inner block ",VV_KEY(bh,i));
					if ( r > 0 )
						{
							m1_copy_key( &right_delim_key, VV_KEY(bh,i));
						}
					else
						{
							i++;
							if ((i+1) < VV_ITEMS(bh)) m1_copy_key( &right_delim_key, VV_KEY(bh,i+1));
						}
					PRINTK("prt#%d,%u, ", i, VV_CHILD_NUM(bh,i));
					if ( j_device_read( fd, p_buf, blocksize, VV_CHILD_NUM(bh,i) ) !=blocksize )
						{
							printk("reiserfs_search_item(next read tree): read error\n");
							return (NULL);
						}
				};
			j = 0;
			vv_print_key("right_delim_key",&right_delim_key);
			m_copy_key(&(bh-> blk_right_delim_key), &right_delim_key);
	
			/* search item */
			l = 0;
			h = VV_ITEMS(bh) - 1;
			j=0;
			r=-1;
			s=0;
			while(l<h)
				{
					PRINTK("\n %d? r=%d:(%d<%d<%d)",s,r,l,j,h);
					if ( !r    ) break ;
					if (s)
						{
							if ( r < 0 || s++ > 1) break;
							j--;
						}
					else
						{
							if ( r < 0 ) l = j; 
							if ( r > 0 ) h = j; 
							if ( ( l + 1 ) == h ) { j = h; s = 1; }
							else                    j = ( l + h )/2;
						}
					r =  m_comp_keys( (struct m_key *) vv_m_ihead( p_item, bh, j), p_key );
				}
		}
	vv_print_key_stop("founded key",p_item);
    return (p_item);	
}


static struct reiserfs_m_inode *reiserfs_m_iget( struct m_key * ino_k, struct milo_item * tmp_item)
{
    int i;
	struct reiserfs_m_inode *ip;
	struct inode_table_entry *itp;

	ip = NULL;
	itp = NULL;
    for (i = 0; i < MAX_OPEN_FILES; i++)
		{
			if (inode_table[i].free || inode_table[i].inumber==ino_k->k_objectid)
				{
					itp = &(inode_table[i]);
					ip = &(itp->inode);
					break;
				}
		}
	if ((ip == NULL) || (itp == NULL))
		{
			printk("reiserfs_m_iget: no free inodes\n");
			return (NULL);
		}
	
	itp->inumber = ino_k->k_objectid;
	itp->free = 0;
		
    ip->i_mode = 0;

    ip->i_key.k_dir_id = ino_k->k_dir_id;
	ip->i_key.k_objectid = ino_k->k_objectid;
    ip->i_key.u.k_offset_v2.k_offset = SD_OFFSET;
    ip->i_key.u.k_offset_v2.k_type = TYPE_STAT_DATA;

	reiserfs_search_item( (struct m_key *)ip, (char *)iblkbuf , tmp_item);

	if (tmp_item->ih_version)
		{
			ip->i_mode               = le16_to_cpu (tmp_item->body.sd.sd_mode);
			ip->i_uid                = le32_to_cpu (tmp_item->body.sd.sd_uid);
			ip->i_gid                = le32_to_cpu (tmp_item->body.sd.sd_gid);
			ip->i_size               = le64_to_cpu (tmp_item->body.sd.sd_size);
			ip->i_mtime              = le32_to_cpu (tmp_item->body.sd.sd_mtime);
			ip->i_atime              = le32_to_cpu (tmp_item->body.sd.sd_atime);
			ip->i_ctime              = le32_to_cpu (tmp_item->body.sd.sd_ctime);
			ip->i_blocks             = le32_to_cpu (tmp_item->body.sd.sd_blocks);
		}
	else
		{
			ip->i_mode               = le16_to_cpu (tmp_item->body.sd1.sd_mode);
			ip->i_uid                = le16_to_cpu (tmp_item->body.sd1.sd_uid);
			ip->i_gid                = le16_to_cpu (tmp_item->body.sd1.sd_gid);
			ip->i_size               = le32_to_cpu (tmp_item->body.sd1.sd_size);
			ip->i_mtime              = le32_to_cpu (tmp_item->body.sd1.sd_mtime);
			ip->i_atime              = le32_to_cpu (tmp_item->body.sd1.sd_atime);
			ip->i_ctime              = le32_to_cpu (tmp_item->body.sd1.sd_ctime);
			ip->i_blocks             = le32_to_cpu (tmp_item->body.sd1.sd_blocks);
		}


	switch ( ip->i_mode & S_IFMT )
		{
		case S_IFREG:
			ip->i_key.u.k_offset_v2.k_offset = 1;
			ip->i_key.u.k_offset_v2.k_type   = TYPE_DIRECT;
			reiserfs_search_item( (struct m_key *)ip, (char *)iblkbuf , tmp_item);
//			memcpy(&(ip->i_data),&(tmp_item->body),tmp_item->ih_item_len);
//			ip->i_data_length = tmp_item->ih_item_len/sizeof(__u32);
			break;
		case S_IFDIR:
			ip->i_key.u.k_offset_v2.k_offset = DOT_OFFSET;
			ip->i_key.u.k_offset_v2.k_type = TYPE_DIRENTRY;
			reiserfs_search_item( (struct m_key *)ip, (char *)iblkbuf , tmp_item);
/*
//			memcpy(&(ip->i_data),&(tmp_item->body),tmp_item->ih_item_len);
//			ip->i_data_length = tmp_item->ih_item_len;
*/			
			break;
		case S_IFCHR:
			break;
		case S_IFBLK:
		case S_IFIFO:
		case S_IFSOCK:
			break;
		case S_IFLNK:
			(ip->i_key.u.k_offset_v2.k_offset)= 1;
			ip->i_key.u.k_offset_v2.k_type = TYPE_DIRECT;
			reiserfs_search_item( (struct m_key *)ip, (char *)iblkbuf , tmp_item);
/*
//			memcpy(&(ip->i_data),&(tmp_item->body),tmp_item->ih_item_len);
//			ip->i_data_length = tmp_item->ih_item_len;
*/
			break;
		}
	return (ip);
}

/* Release our hold on an inode.  Since this is a read-only application,
 * don't worry about putting back any changes...
 */
static void reiserfs_m_iput(struct reiserfs_m_inode *ip)
{
	struct inode_table_entry *itp;

	/* Find and free the inode table slot we used... */
	itp = (struct inode_table_entry *) ip;

	itp->inumber = 0;
	itp->free = 1;
}

/* Return the size of the open file */
static long reiserfs_m_fdsize(int fd)
{
	struct reiserfs_m_inode *ip;

	ip = &(inode_table[fd].inode);
	return ip->i_size;
}

/* Read block number "blkno" from the specified file */
static int reiserfs_m_bread(int fd, long blkno, char *buffer)
{
	struct reiserfs_m_inode *ip;
	static struct inode_table_entry * itp;
	ip = &(inode_table[fd].inode);
	itp = (struct inode_table_entry *)ip;
	if (itp->free)
		{
			printk("file %d not open", fd);
			return -1;
		}
	reiserfs_m_breadi(ip, blkno, buffer);
	return 0;
}

#ifndef IOBUF_SIZE
#define IOBUF_SIZE 1024
#endif

static void reiserfs_m_breadi(struct reiserfs_m_inode *ip, long p_blkno, char *buffer)
{
	long dev_blkno;
	struct m_key pos_key;
	unsigned long offset, offset_l;
	unsigned long offset_min;
	unsigned long offset_max;
	unsigned int	n_pos;
	unsigned long r_size,c_size;

	r_size=IOBUF_SIZE;
	offset = p_blkno * IOBUF_SIZE   /*1024*/  ;

	if (offset > ip->i_size)
		{
			printk("\nattempt read %ld belong end of file:%ld",offset,ip->i_size);
			memset(buffer,0,blocksize);
			kbd_getc();
			return;
		}
	
	offset_min = it_open.ih_key.u.k_offset_v2.k_offset-1;
	offset_max = it_open.ih_item_len-1;

	if(it_open.ih_key.u.k_offset_v2.k_type==TYPE_INDIRECT )
		{
			offset_max++;
			offset_max = offset_max * blocksize/ sizeof(__u32) + offset_min;
		}
	PRINTK("\nitem min=%d len=%d,max=%d offset=%d.", offset_min, it_open.ih_item_len, offset_max, offset);

	if ( offset < offset_min || offset_max < offset )
		{
			m_copy_key (&pos_key, ip);
			pos_key.u.k_offset_v2.k_offset = offset;
			reiserfs_search_item( &pos_key, buffer, &it_open);
			vv_print_key("\nbread: finded   item",&it_open);
		}

	offset_l = offset - ( it_open.ih_key.u.k_offset_v2.k_offset - 1 );
	switch(it_open.ih_key.u.k_offset_v2.k_type)
		{
		case TYPE_INDIRECT :
			n_pos    = offset_l / blocksize;
			offset_l = offset_l % blocksize;
			dev_blkno = it_open.body.item[n_pos];
			PRINTK("%d ",dev_blkno);
			
			if (j_tab_count) dev_blkno=check_j_block(dev_blkno);

			offset_l += dev_blkno * blocksize;
			
			PRINTK("%d.",dev_blkno);

			if ( device_read (fd, buffer,   IOBUF_SIZE ,  offset_l) != IOBUF_SIZE )
				{
					printk("reiserfs_bread: read error");
				}
			break;
		case  TYPE_DIRECT :
			c_size = ( (ip->i_size - offset ) < IOBUF_SIZE ) ? (ip->i_size - offset ) : IOBUF_SIZE  ;
			while ( c_size>0 )
				{
					r_size = IOBUF_SIZE;
					if (r_size > c_size) 	r_size = c_size;
					if (r_size > it_open.ih_item_len - offset_l ) 	r_size = it_open.ih_item_len - offset_l ;

					memcpy( buffer, (char *)(it_open.body.txt)+offset_l, r_size);

					c_size -= r_size;
					if ( (c_size)>0)
						{
							offset+=r_size;
							m_copy_key (&pos_key, ip);
							pos_key.u.k_offset_v2.k_offset = offset;
							reiserfs_search_item( &pos_key, buffer, &it_open);
							vv_print_key(" bread: find new item",&pos_key);
							offset_l = offset - ( it_open.ih_key.u.k_offset_v2.k_offset - 1 );
							
							if (it_open.ih_key.u.k_offset_v2.k_type !=TYPE_DIRECT)
								{
									return;
								}
						}
				}
			break;
		}
	return;
}


static __u32 m_get_third_component ( const char * name, int len)
{
	__u32 res;
	if (!len || (len == 1 && name[0] == '.'))           return DOT_OFFSET;
	if (len == 2 && name[0] == '.' && name[1] == '.')   return DOT_DOT_OFFSET;
	res = p_hash_function (name, len);
	res = M_GET_HASH_VALUE(res);
	if (res == 0) 	res = 128;
	return res +  MAX_GENERATION_NUMBER /*MAX_GEN_NUMBER*/;
}


static struct reiserfs_m_inode *reiserfs_m_namei(char *name, struct milo_item *n_item)
{
	char namebuf[512];
	char *component;
	struct reiserfs_m_inode
		*dir_inode = NULL,
		*file_inode;
	int i;
	int prev_offset;
	struct reiserfs_m_de_head * de;
	int need_find;	

	struct m_key pos_key;	/* key of current position in the directory (key of directory entry) */
	int link_count=0;
	
	strcpy(namebuf, name);
	
  abs:
	file_inode =  reiserfs_m_iget ( &m_root_key, n_item);

  rel:
	if (!file_inode)
		return NULL;

	component = strtok(namebuf, "/");
	while (component)
		{
			int component_length;

			if (dir_inode)  reiserfs_m_iput(dir_inode);
			dir_inode = file_inode;
			file_inode = NULL;
			
			component_length = strlen(component);
			
#ifdef MILO_SYMLINK_DIRS
			if (S_ISLNK(dir_inode->i_mode))
				{
					PRINTK("     symlink     ");
					if (++link_count > 10)
						printk("%s: possibly circular symlink\n", name);
					else
						{
							char *t;
							reiserfs_search_item( (struct m_key *)dir_inode, (char *)iblkbuf, n_item);
							memcpy( blkbuf, n_item->body.txt, n_item->ih_item_len );
							t = blkbuf + strlen(blkbuf);
							while (component != NULL)
								{
									t = stpcpy(stpcpy(t, "/"), component);
									component = strtok(NULL, "/");
								}

							/* Put it back to namebuf */
							strcpy(namebuf, blkbuf);

							if (*namebuf == '/')
								{
									/* Absolute symlink */
									if (dir_inode)
										reiserfs_m_iput(dir_inode);
									if (file_inode)
										reiserfs_m_iput(file_inode);
									goto abs;
								}
							else
								{
									/* Relative symlink */
									if (file_inode)
										reiserfs_m_iput(file_inode);
									file_inode = dir_inode;
									goto rel;
								}
						}
				}
#endif				/* MILO_SYMLINK_DIRS */
			
			if (!S_ISDIR(dir_inode->i_mode))
				{
					printk("%s: path component is not a directory\n", name);
					break;
				}

			m_copy_key (&pos_key, dir_inode);
			pos_key.u.k_offset_v2.k_offset = m_get_third_component( component, component_length);
			need_find=1;
			while (need_find)
				{
					need_find=0;
					reiserfs_search_item( (struct m_key *)dir_inode, (char *)iblkbuf , n_item);
					de = n_item->body.deh;
					i = n_item->u.ih_entry_count;
					prev_offset = n_item->ih_item_len;
					while ( i &&
							M_GET_HASH_VALUE(de->deh_offset)<M_GET_HASH_VALUE(pos_key.u.k_offset_v2.k_offset ))
						{
							prev_offset = de->deh_location;
							de++;
							i--;
						}
					while ( i  &&
							M_GET_HASH_VALUE( de->deh_offset )==M_GET_HASH_VALUE(pos_key.u.k_offset_v2.k_offset ) &&
							strncmp( n_item->body.txt + de->deh_location , component, prev_offset - de->deh_location ) )
						{
							prev_offset = de->deh_location;
							de++;
							i--;
							if (!i &&
								it_buf.it_Last &&
								!m_comp_short_keys(&pos_key, &(n_item->ih_r_key))&&
								M_GET_HASH_VALUE(pos_key.u.k_offset_v2.k_offset)==M_GET_HASH_VALUE( n_item->ih_r_key.u.k_offset_v2.k_offset ))
								{
									copy_key(dir_inode,&(n_item->ih_r_key));
									need_find=1;
								}
						}
				}

			if ( strncmp( n_item->body.txt + de->deh_location , component, prev_offset - de->deh_location ) )
				{
					reiserfs_m_iput(dir_inode);
					return NULL;
				}
			
			pos_key.k_dir_id    = de->deh_dir_id;
			pos_key.k_objectid  = de->deh_objectid;
			pos_key.u.k_offset_v2.k_offset    = DOT_OFFSET;
			pos_key.u.k_offset_v2.k_type      = TYPE_DIRENTRY;

			file_inode = reiserfs_m_iget(&pos_key,n_item);
			
			component = strtok(NULL, "/");
		}

	if (dir_inode)
		reiserfs_m_iput(dir_inode);
	return (file_inode);
}

/*
** read next byte
*/

static char vv_next(long count, struct reiserfs_m_inode *ip,char * blkh, long f_size)
{
	unsigned int blkno;
	unsigned int offs;
	
	if ( count < f_size )
		{
			blkno = count / IOBUF_SIZE;
			offs  = count % IOBUF_SIZE;
			if ( !offs )
					reiserfs_m_breadi( ip, blkno, blkh);
			return (*(blkh+offs));
		}
	else return 0;
}


/*
** print file  
*/
static void reiserfs_m_cat(struct reiserfs_m_inode *ip, char * buf)
	/*char *filename, char * dev)*/
{
	long f_size;
	__u32 blkno;
	int fd;
	int i,j;
	char * blkh;
	struct inode_table_entry * itp;
	char cc;
	reiserfs_search_item( (struct m_key *)ip, buf, &it_open);

	blkh = buf;

	itp = (struct inode_table_entry *) ip;
	fd = (itp - inode_table);

	f_size=reiserfs_m_fdsize(fd);
	printk( "file size=%ld\n", f_size);
	blkno=0;
	i=0;
	j=0;
	printk("\n%3d:",++i);
	while( (cc = vv_next( j++, ip, blkh, f_size)) )
		{
			if ( cc == '\n' )
				{
					if( !( i%20 ) )
						if ( kbd_getc() == 'q' )
							break;
					printk( "\n%3d:", ++i );
				}
			else
				printk( "%c",(/*isprint(cc)*/  cc >=' ')? cc:'.' );

		}

	printk("\n");
	reiserfs_m_close(fd);
}

/*
** ls (*dirname, void * devname)
** devname - not use, no need: milo read from 1 device, opened befor
** find dirname, and list it contains.
**   if finded name is file- trie print it
*/
static void reiserfs_m_ls(char *dirname, char *devname)
{
	char filename[512];
	int filenamelen;
	char format_string[16];
	struct reiserfs_m_inode *dir_inode;
	struct reiserfs_m_inode *file_inode;
	long entr_count=0;
	int columns;
	int col;
	char typetag;
	struct m_block_head * blkh;
	struct reiserfs_m_de_head * de;

	struct m_key pos_key;	/* key of current position in the directory (key of directory entry) */
    char small_buf[REISERFS_BLOCK_SIZE] ;
	int i;
	__u32 prev_loc;

#define NOT_EOD NotEOD
	int NotEOD;



	strcpy(format_string,"   %s");
                                                /* Get the inode for the specified directory */
	dir_inode = reiserfs_m_namei(dirname,&it_buf);

	printk("\nls %s",dirname);
	if (!dir_inode)
		{
			printk("%s: is no  file or directory\n", dirname);
			return;
		}

	/* If this inode exists but is *not* a directory, just cat it here */
	if ((dir_inode->i_mode & S_IFMT) != S_IFDIR)
		{
			reiserfs_m_cat(dir_inode, (char *)(&it_buf));
			return;
		}

	col = 0;
	columns = 4;
	NOT_EOD = 1;
	blkh = (struct m_block_head *)&small_buf;
	
	while ( NOT_EOD )
		{
			NOT_EOD = 0;

			reiserfs_search_item( (struct m_key *)dir_inode, (void *)blkh, &it_buf);
			de = it_buf.body.deh;
			i = it_buf.u.ih_entry_count;
			prev_loc = it_buf.ih_item_len;
//			de++;
//			prev_loc = (de++)->deh_location;
//			i-=2;
			printk("\n");


			while ( i  )  /* loop by dir_item*/
				{
					entr_count++;
					filenamelen          = prev_loc - de->deh_location;
					memcpy( filename, it_buf.body.txt + de->deh_location ,filenamelen);
					filename[filenamelen] = '\0';
					printk(format_string, filename);
					pos_key.k_dir_id     = de->deh_dir_id;
					pos_key.k_objectid   = de->deh_objectid;
					pos_key.u.k_offset_v2.k_offset     = 1;
					pos_key.u.k_offset_v2.k_type = TYPE_DIRENTRY;

					file_inode = reiserfs_m_iget(&pos_key,&n_item_buf);

					vv_print_key("pos_key",&pos_key);

					if (file_inode)
						{

/*
							typetag = ' ';
							if ( file_inode->i_mode & S_IFMT == S_IFDIR) typetag = '/';
							if ( file_inode->i_mode & S_IFMT == S_IFLNK) typetag = '@';
							if ( file_inode->i_mode & S_IFMT == S_IFIFO) typetag = '=';
*/							
							switch ( file_inode->i_mode & S_IFMT )
								{
								case S_IFLNK:
									typetag = '@';
									break;
								case S_IFDIR:
									typetag = '/';
									break;
								case S_IFIFO:
								case S_IFSOCK:
									typetag = '=';
									break;
								default:
									typetag = ' ';
									break;
									}
							reiserfs_m_iput(file_inode);
						}
					else
						{
							typetag = '!';
						}
					printk("%c", typetag);
					
					if ( !(++col % columns) )  printk("\n");
					
					prev_loc = de->deh_location;

					de++;
					i--;
				}

			if ( it_buf.it_Last ) /* This item is the last in block */
				{
					copy_key(&pos_key,&(it_buf.ih_r_key));
					vv_print_key    ("  tri new pos",&pos_key);
					reiserfs_search_item( &pos_key, (char *)blkh, &it_buf);
					vv_print_key_stop     ("  founded pos",&it_buf.ih_key);
					
					if (!m_comp_short_keys(&(dir_inode->i_key),&it_buf.ih_key) &&
//						it_buf.ih_key.u.k_offset_v2.k_offset>m_get_third_component(filename,filenamelen) &&
						it_buf.ih_key.u.k_offset_v2.k_type==TYPE_DIRENTRY)
						{                                      	/*directory continues in next item*/
							copy_key(&(dir_inode->i_key),&it_buf.ih_key);
							NOT_EOD = 1;
						}
				}
		}			/* end-while */
	
	if ((col % columns))
		{
			printk("\n");
		}
	printk("\ntotal %ld entries\n",entr_count);

	/* We're done with the directory now... */
	reiserfs_m_iput(dir_inode);

}

static int reiserfs_m_open(char *filename)
{
	/* Unix-like open routine.  Returns a small integer (actually an index into
	 * the inode table... */
	struct reiserfs_m_inode *ip;
	ip = reiserfs_m_namei(filename,&it_open);

	if (ip)
		{
			struct inode_table_entry *itp;

			reiserfs_search_item( (struct m_key *)ip, (char *)(&n_item_buf), &it_open);
			PRINTK("\n open: %s size:%d itemsize:%d fist block:%d",
				   filename, ip->i_size,  it_open.ih_item_len,it_open.body.item[0]);
			itp = (struct inode_table_entry *) ip;
			return (itp - inode_table);
		}
	else
		{
			printk("not opened");
			return (-1);
		}
}


static void reiserfs_m_close(int fd)
{
	reiserfs_m_iput(&(inode_table[fd].inode));
	device_close(fd);
}

void copy_key (void * to, void * from)
{
  memcpy (to, from, KEY_SIZE);
}

