/*
 * inode.c
 * Part of the !Memphis distribution
 * (c) bdb/nas, 1991-3
 */

/************** Inode Module ***************/
/* #define DEBUG */
/*  debug support*/
#ifdef DEBUG
#define DEBUGf printf
#else
#define DEBUGf 1?(void)0:(void)printf
#endif

/*  includes*/
#include <stdio.h>
#include "interface.h"
#include "core.h"
#include "store.h"
#include "inode.h"
#include "util.h"

/*  errs*/
static DEFERR( mb_root_failed,         0,  "Failed to create root" );
static DEFERR( mb_root_lost,           0,  "Lost root" );

/*  prototypes*/
static _kernel_oserror *Inode_CreateRoot(storeid s);

/*  vars*/
#define NUMINODES 100

static INODE inodetable[NUMINODES];
static int nextinode;
volatile int Inode_flushcount;

/* set to zero on something needing flush
 * incremented each second
 * We flush inodes when it reaches FLUSHTIME
 */
#define FLUSHTIME 3

 INODE *Inode_Find(storeid s,int inode) /*Find an inode in memory*/
{ int i;
  DEBUGf("Inode_Find(%p,%d)\n",s,inode);
  Inode_flushcount=0;
start:
  for (i=0;i<NUMINODES;i++)
    if (inodetable[i].store==s && inodetable[i].inode==inode)
    { inodetable[i].uses++;
      return &inodetable[i];
    }
  for ( ; inodetable[nextinode].uses; ++nextinode >= NUMINODES && (nextinode=0)==0)
    ;
  i=nextinode;
  ++nextinode>=NUMINODES && (nextinode=0)==0;
  if (inodetable[i].changed)
    Store_Write(inodetable[i].store,0,inodetable[i].inode*sizeof(STOREINODE),
                                sizeof(STOREINODE), &inodetable[i]);
  if (Store_Read( s, 0, inode*sizeof(STOREINODE), sizeof(STOREINODE), &inodetable[i]))
  { inodetable[i].inode = -2;
    inodetable[i].uses = 0;
    inodetable[i].changed = 0;
    if (Inode_CreateRoot(s))
      return NULL;
    goto start;
  }
  inodetable[i].store=s;
  inodetable[i].inode = inode;
  inodetable[i].uses = 1; /* NOW CHANGED TO LOCK IT */
  inodetable[i].changed = 0;
  return &inodetable[i];
}

 _kernel_oserror *Inode_EnsureSize( INODE *h, int size )/**/
{ _kernel_oserror *err;
  DEBUGf("Inode_EnsureSize(%p,%d)\n",h,size);
  if ( size!=h->allocated)
  { err = Store_SetLength( h->store, h->inode, size );
    if (err)
      return err;
    h->allocated=size;
    Inode_Changed( h );
  }
  return NULL;
}

INODE *Inode_New( INODE *p )/**/
{ INODE *p0,*d;
  int inode;
  DEBUGf("Inode_New(%p)\n",p);
  Inode_flushcount=0;
  p0 = Inode_Find(p->store,0);
  if (!p0)
    return 0;
  if ( p0->parentinode)
  { inode = p0->parentinode;
    d = Inode_Find(p->store,inode);
    if (!d)
    { Inode_Lose(p0); return 0; }
    p0->parentinode = d->parentinode;
  }
  else
  { inode = p0->allocated/sizeof(STOREINODE);
    p0->allocated+=sizeof(STOREINODE);
    if (Store_SetLength(p0->store,0,p0->allocated))
    { Inode_Lose(p0); return 0; }
    d = Inode_Find(p->store,inode);
  }
  Inode_Changed(p0);
  Inode_Lose(p0);
  if (!d)
    return 0;
  d->d.info.date_type.part_1=0xDEADDEAD;
  d->d.info.date_type.part_2=0xDEADDEAD;
  d->d.length = 0;
  d->allocated = 0;
  d->d.attr = Attr_R | Attr_W;
  d->d.type = 1;
  d->d.buffered = 1;
  d->d.interactive = 0;
  d->d.noosgbpb = 0;
  d->parentinode = p->inode;
  Inode_Changed(d);
#ifdef DEBUG
  printf( "Created inode %d\n", d->inode );
#endif
  return d;
}

 _kernel_oserror *Inode_Delete( INODE *h )/**/
{
  INODE *t0;
  _kernel_oserror *err;
  DEBUGf("Inode_Delete(%p)\n",h);
  Inode_flushcount=0;
  t0 = Inode_Find(h->store,0);
  if (!t0)
    return ERR(mb_root_lost);
  h->parentinode = t0->parentinode;
  err=Store_SetLength( h->store, h->inode, 0 );
  t0->parentinode = h->inode;
  Inode_Changed(t0);
  Inode_Lose(t0);
  Inode_Changed(h);
  Inode_Lose(h);
  return err;
}

 static _kernel_oserror *Inode_CreateRoot(storeid s) /**/
{
    INODE *p0,*inode;
    _kernel_oserror *err;
    DEBUGf("Inode_CreateRoot(%p)\n",s);
    Inode_flushcount=0;
    err = Store_SetLength( s,0,sizeof(STOREINODE));
    if ( err )
      return err;
    p0 = Inode_Find( s,0 );
    if ( !p0 )
      return ERR( mb_root_failed );
    StampInfo( &p0->d.info );
    p0->d.attr=0x12345678;
    p0->d.length=1;
    p0->allocated=sizeof(STOREINODE);
    p0->parentinode=0;   /* chain of free inodes */
    Inode_Changed( p0 );
    inode = Inode_New(p0);
    Inode_Lose(p0);
    if (!inode)
      return ERR( mb_root_failed );
    inode->d.type = 2;
    inode->parentinode = 1;
    StampInfo( &inode->d.info );
    Inode_Changed(inode);
    Inode_Lose(inode);
    return NULL;
}

/*  Inode_Read,Write,Init,Finish,Flush,Ticker*/
_kernel_oserror *Inode_Init(void)
{ int i;
  for (i=0;i<NUMINODES;i++)
  { inodetable[i].inode=-2;
    inodetable[i].uses=0;
    inodetable[i].changed=0;
  }
  Inode_flushcount = FLUSHTIME+1;     /* no need to flush */
  return Store_Init();
}

void Inode_Flush(void)
{ int i;
  DEBUGf("***Flush***");
  for (i=0;i<NUMINODES;i++)
  { if (inodetable[i].changed)
    { Store_Write(inodetable[i].store,0,inodetable[i].inode*sizeof(STOREINODE),
                                sizeof(STOREINODE), &inodetable[i]);
      inodetable[i].changed=0;
    }
    if (!inodetable[i].uses)
      inodetable[i].inode=-2;
  }
  Store_Flush();
}
void Inode_Ticker(void)
{ /* DEBUGf("Tick!"); */
  if (++Inode_flushcount==FLUSHTIME)
    Inode_Flush();
}

_kernel_oserror *Inode_Finish(void)
{ Inode_Flush();
  return Store_Finish();
}

_kernel_oserror *Inode_Read( INODE *p, int offset, int length, void *ptr )
{ return Store_Read( p->store, p->inode, offset, length, ptr );
  Inode_flushcount=0;
}

_kernel_oserror *Inode_Write( INODE *p, int offset, int length, void *ptr )
{ return Store_Write( p->store, p->inode, offset, length, ptr );
  Inode_flushcount=0;
}

