/*
 * Copyright (c) 1996 The University of Utah and
 * the Computer Systems Laboratory at the University of Utah (CSL).
 *
 * This file is part of Flick, the Flexible IDL Compiler Kit.
 *
 * Flick is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Flick is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Flick; see the file COPYING.  If not, write to
 * the Free Software Foundation, 59 Temple Place #330, Boston, MA 02111, USA.
 */

/*******************************************************************************
********************************************************************************
*
*  file.c			Low level file operations
*
********************************************************************************
*******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <string.h>
#include "disk.h"
#include "file.h"
#include "cache.h"

#define bzero(a, b)	memset(a, 0, b)

/*******************************************************************************
* Data space for in-memory copy of on-disk structures
*******************************************************************************/

/* Base block buffer area */

char BaseBlockBuffer[512];

/* File handle number is placed first */

int * FileHandle 	= (int *) (BaseBlockBuffer);
int * Time 		= (int *) (BaseBlockBuffer) + 1;
/* Dir == 1 when it's a dir */
int * Dir 		= (int *) (BaseBlockBuffer) + 2;  
int * DirEntryCount 	= (int *) (BaseBlockBuffer) + 3;
int * FileSize 		= (int *) (BaseBlockBuffer) + 4;
int * LinkCount		= (int *) (BaseBlockBuffer) + 5;
int * Spare1 		= (int *) (BaseBlockBuffer) + 6;
int * Spare2 		= (int *) (BaseBlockBuffer) + 7;

/* We allow the first 31 blocks to hold the miscellanous information */
/*   above.  The Direct block addresses begin at the 33rd byte, and */
/*   the Indirect block addresses begin at the 57th byte. */

int * Map 		= (int *) (BaseBlockBuffer + 32);
int * Direct		= (int *) (BaseBlockBuffer + 32);
int * Indirect		= (int *) (BaseBlockBuffer + 32) + 24;

/* The data of the file starts at the 257th byte. */

char * FirstData 	= BaseBlockBuffer + 256;
int * FirstDataInt	= (int *) (BaseBlockBuffer + 256);


/* Indirect block mapping buffer area */

char IndirectBuffer [512];

int * IndirectDirect = (int *) IndirectBuffer;


/* Temporary block buffer */

char Block [512];


/* Temporary data buffer */

char Data [2048];
int * DataInt = (int *) Data;


/*******************************************************************************
* In memory free list, handle list, and other local data
*******************************************************************************/

int UniqueNumber;

Handle HandleList [4096];

int FreeBlockCount;
int FreeBlocks[4096];
int UsedBlocks[4096];

/*******************************************************************************
* int BadHandle (Handle h)
*
*	This function validates a file handle.
*******************************************************************************/

int BadHandle (Handle h)
{
  if (h == HandleList[h % 4096])
    return 0;
    
  YfsError = EBADF;
  return -1;
}


/*******************************************************************************
* int Access (Handle h)
*
*	Sets up a file for low level operations by reading in its file
* information and disk mappings into the BaseBlockBuffer.
*******************************************************************************/

int Access (Handle h)
{
 
/* Make sure the file handle is valid */
  
  if (BadHandle (h))
    return -1;

/* If we have already loaded the handle, return */
    
  if (h == *FileHandle)
    return 0;

/* Attempt to load the new file handle */
    
  if (CacheReadDisk (h % 4096, BaseBlockBuffer, 1, h))
  {
    *FileHandle = 0;
    return -1;
  }

/* Return success */
  
  return 0;
}

/*******************************************************************************
* int Update ()
*
*        This function writes the in-memory block back onto the disk (cache).
*******************************************************************************/

int Update ()
{
  
/* Make sure that we have a valid file handle in memory */

    if (BadHandle (*FileHandle))
      return -1;

/* Attempt to write the file handle */
    
  if (CacheWriteDisk (*FileHandle % 4096, BaseBlockBuffer, 1, *FileHandle))
    return -1;

/* Return success */
  
  return 0;
}

/*******************************************************************************
* Handle MakeFile ()
*
*        This function creates a Handle for a file; however, it does not
* fill in any file specific information except the FileNumber.
*******************************************************************************/

Handle MakeFile ()
{
  
/* Make sure there is space on the disk */

  if (FreeBlockCount == 0)
  {
    YfsError = ENOSPC;
    return -1;
  }

/* Clean up the base block */
  
  bzero (BaseBlockBuffer, 512);
  
/* Allocate a file handle (disk block) for this file */
  
  *FileHandle = ((++UniqueNumber) * 4096) + FreeBlocks[--FreeBlockCount];
  
  HandleList[*FileHandle % 4096] = *FileHandle;
  
/* Return success */
  
  return *FileHandle;
}

/*******************************************************************************
* int DeleteFile ()
*
*       This function deletes the file whose handle information is currently
* in memory.  It returns success if delete succeeded, or if the number of
* links was greater than 1.
*******************************************************************************/

int DeleteFile ()
{
  int pool, loop;
  
 
/* Make sure that we have a valid file handle in memory */

  if (BadHandle (*FileHandle))
    return -1;
      
/* decrement the link count, and return success */

  if ( (*LinkCount)-- > 1 )    
    return 0;

/* Remove the file from the HandleList */

  HandleList[(*FileHandle) % 4096] = 0;

/* Traverse through the Direct and Indirect Mappings.  Add all the blocks */
/*   that the file uses to the FreeBlocks array and increment the */
/*   FreeBlockCount */

  loop = -1;
  while ( Direct[++loop] && loop < 24 )
    FreeBlocks[FreeBlockCount++] = Direct[loop];

  loop = -1;
  while ( Indirect[++loop] && loop < 32 )
  {
    if ( CacheReadDisk( Indirect[loop], IndirectBuffer, 1, *FileHandle) == -1 )
    {
      FreeBlocks[FreeBlockCount++] = (*FileHandle) % 4096;
      *FileHandle = 0;
      return -1;
    }
    pool = -1;
    while ( IndirectDirect[++pool] && pool < 128 )
      FreeBlocks[FreeBlockCount++] = IndirectDirect[pool];
    FreeBlocks[FreeBlockCount++] = Indirect[loop];
  }

/* Add the BaseBlockBuffer to the list of free blocks */

  FreeBlocks[FreeBlockCount++] = (*FileHandle) % 4096;

/* Invalidate the filehandle */

  CacheClean (*FileHandle);
  *FileHandle = 0;

  return 0;
}

/*******************************************************************************
* Handle FindFilename (char * name)
*
*       This function searches the in-memory directory for a file named name.
*******************************************************************************/

Handle FindFilename (char * name)
{

/* A directory entry size is determined in the following way: */
/*   1 byte for the Null character, 4 bytes for the length, 4 bytes for the */
/*   Handle, and 3 bytes for correct round off. */

  int SearchSize = ((strlen (name) + 1 + 4 + 4 + 15) / 16) * 16;
  int pos = 0;

/* Make sure that we have a valid file handle in memory */

  if (BadHandle (*FileHandle))
    return -1;

/* Traverse the directory looking for the entry.  Read in 128+20 bytes, which */
/* is more than necessary. */
  
  while (pos < *FileSize)
  {
    if (FileRead (160, pos, Data) == -1)
      return -1;
    if ( DataInt[0] == 0 )
      break;
    if (SearchSize == DataInt[0] && DataInt[1] && strcmp (Data + 8, name) == 0)
      return DataInt[1];
    pos += DataInt[0];
  }  
  
  YfsError = ENOENT;
  return -1;
}


/*******************************************************************************
* int AddFilename (Handle h, char * name)
*
*
*******************************************************************************/

int AddFilename (Handle h, char * name)
{
 
/* A directory entry size is determined in the following way: */
/*   1 byte for the Null character, 4 bytes for the length, 4 bytes for the */
/*   Handle, and 3 bytes for correct round off. */

  int SearchSize = ((strlen (name) + 1 + 4 + 4 + 15) / 16) * 16;
  int pos = 0;

/* Make sure that we have a valid file handle in memory */

  if (BadHandle (h))
    return -1;

/* Traverse the directory looking for the entry.  Read in 128+20 bytes, which */
/* is more than necessary. */
  
  while (pos < *FileSize)
  {
    if (FileRead (128 + 20, pos, Data) == -1)
      return -1;
    if ( DataInt[0] == 0 || (DataInt[1] == 0 && DataInt[0] == SearchSize))
      break;
    pos += DataInt[0];
  }  

/* Add the entry */
  
  DataInt[0] = SearchSize;
  DataInt[1] = h;
  bzero ( Data + 8, SearchSize - 8);
  strcpy (Data + 8, name);
  
  if (FileWrite (DataInt[0], pos, Data) == -1)
    return -1;   
    
  (*DirEntryCount)++;  

  return 0; 
}


/*******************************************************************************
* int RemoveFilename (char *)
*
*
*******************************************************************************/

int RemoveFilename (char *name)
{
  int pos = 0;
  

/* Make sure that we have a valid file handle in memory */

  if (BadHandle (*FileHandle))
    return -1;
  
/* Traverse the directory looking for the entry.  Read in 128+20 bytes, which */
/* is more than necessary. */
  
   while (pos < *FileSize)
  {
    if (FileRead (128 + 20, pos, Data) == -1)
      return -1;
    if ( DataInt[0] == 0 )
      break;

/* Delete the entry by zeroing out the Handle field */

    if (!strcmp( name, Data+8))
    {
      DataInt[1] = 0;
      if (FileWrite (DataInt[0], pos, Data) == -1)
        return -1;
      return 0;
    }
    pos += DataInt[0];
  }   
 
  YfsError = ENOENT;  
  return -1;
}


/*******************************************************************************
* int TimeStamp ()
*
*	This function returns the time stamp for the open file.
*******************************************************************************/

int TimeStamp ()
{
   
/* Make sure that we have a valid file handle in memory */

    if (BadHandle (*FileHandle))
      return -1;

/* Stamp in the time */

  time((time_t *) Time);
  
  return 0;
}

/*******************************************************************************
* int Xlate ( int offset, int allocate ) 
*
*	This function will return 0 on success.  It associates a block
* with an offset, and if necessary, it allocates a block(s).
* If the disk read failed, return -1.  If no block is mapped in, return 1
* or allocate a block.
*******************************************************************************/

int OffsetBlock, OffsetByte;

int Xlate ( int offset, int allocate ) 
{
  int TempBlock, Temp;

/* offset located in the Handle block (in the latter 256 byte part) */
  
  if ( offset < 0x100 )
  {
    OffsetBlock = 0;
    OffsetByte = offset + 0x100;
    return 0;
  }
  
  offset = offset - 0x100;  
  Temp = offset / BLOCK_SIZE;

/* See if the offset is located in one of the direct blocks */
  
  if ( offset < 24 * BLOCK_SIZE )
  {
    if ( Direct[Temp] == 0 && allocate && FreeBlockCount )
    {
      Direct[Temp] = FreeBlocks[--FreeBlockCount];
   }
    OffsetBlock = Direct[Temp];
    OffsetByte = offset % BLOCK_SIZE;
    return ( OffsetBlock == 0 ? -1 : 0 );
  }
  
  offset = offset - 24 * BLOCK_SIZE;
  Temp = offset / (128 * BLOCK_SIZE);

/* The offset is located in one of the blocks with one level of indirection. */
/*    Just a little more calculations are necessary. */
  
  if ( Indirect[Temp] == 0 && allocate  && FreeBlockCount )
  {
    Indirect[Temp] = FreeBlocks[--FreeBlockCount];
    bzero( IndirectBuffer, BLOCK_SIZE);
    if ( CacheWriteDisk( Indirect[Temp], IndirectBuffer, 1, *FileHandle) == -1 )
      return -1;
  }  

  TempBlock = Indirect[Temp];
  if ( TempBlock == 0 )
    return -1;
  
  if ( CacheReadDisk( TempBlock, IndirectBuffer, 1, *FileHandle) == -1 )
    return -1;
  
  offset = offset % ( 128 * BLOCK_SIZE );
  Temp = offset / BLOCK_SIZE; 
  
  if ( IndirectDirect[Temp] == 0 && allocate && FreeBlockCount )
  {
    IndirectDirect[Temp] = FreeBlocks[--FreeBlockCount];
    if ( CacheWriteDisk( TempBlock, IndirectBuffer, 1, *FileHandle) == -1 )
      return -1;
  }
      
  OffsetBlock = IndirectDirect[Temp];
  OffsetByte = offset % BLOCK_SIZE;
  return ( OffsetBlock == 0 ? 1 : 0 );
}

/*******************************************************************************
* int FileRead (int count, int offset, char *buffer)
*
*      This function reads count bytes from the file whose inode information
* is currently in memory ( BaseBlockBuffer).  It starts at the offset byte,
* in the file, and reads in the data into the buffer.  If offset+count > size,
* read in only size-offset bytes.
*******************************************************************************/

int FileRead (int count, int offset, char *buffer)
{
  int length, oldcount;
  char * readbuffer;
  
  
/* Make sure that we have a valid file handle in memory */

  if (BadHandle (*FileHandle))
    return -1;
    
/* Clip the length */

  if (offset + count > *FileSize)
    count = *FileSize - offset;
    
  oldcount = count;
      
  while ( count > 0 )
  {
    if ( Xlate ( offset, 0 ) != 0 )
    {
      YfsError = EIO;
      return -1;
    }
   
/* Deal with the three possible cases: */
/*    - read an entire block (512bytes) */
/*    - read part of block starting at offset 0 */
/*    - read block starting at some offset != 0 */

    length = BLOCK_SIZE - OffsetByte > count ? count : BLOCK_SIZE - OffsetByte;
    
    
    readbuffer = length == BLOCK_SIZE ? buffer : Block;

    if (OffsetBlock == 0)
      readbuffer = BaseBlockBuffer;
    else
      if ( CacheReadDisk ( OffsetBlock, readbuffer, 1, *FileHandle ) == -1)
        return -1;
      
    if ( length != BLOCK_SIZE )
      bcopy ( readbuffer + OffsetByte, buffer, length );
    
    count -= length;
    buffer += length;
    offset += length;
  }
  
  return oldcount;
}

/*******************************************************************************
* int FileWrite (int count, int offset, char *buffer)
*
*       This function writes count bytes from the buffer to a to the file
* starting at offset bytes.  The file to be written is currently in memory
* (BaseBlockBuffer).
*******************************************************************************/

int FileWrite (int count, int offset, char *buffer)
{
  int length, oldcount = count;
  int OldUsed, NewUsed;

  
/* Make sure that we have a valid file handle in memory */

  if (BadHandle (*FileHandle))
    return -1;
  
  if ( *FileSize < offset + count )
  {

  
/* the number of data blocks used aside from Handle data */

    OldUsed = (*FileSize - 0x100 ) / BLOCK_SIZE + 1;
    NewUsed = (offset + count - 0x100 ) / BLOCK_SIZE + 1;

/* modified by the number of indirect blocks */

    if ( OldUsed > 24 )
      OldUsed += ( OldUsed - 24 ) / 128 + 1;
    if ( NewUsed > 24 )
      NewUsed += ( NewUsed - 24 ) / 128 + 1; 
      
 
    if (NewUsed - OldUsed > FreeBlockCount)
    {
      YfsError = ENOSPC;
      return -1;
    }
  }
  
/* Assume that we will not get an error due to lack of space */
    
  while ( count > 0 )
  {
    if ( Xlate ( offset, 1 ) )
    {
      YfsError = EIO;
      return -1;
    }
    
    length = BLOCK_SIZE - OffsetByte < count ? BLOCK_SIZE - OffsetByte : count;
          
 
    if ( length != BLOCK_SIZE )
    {
      if (OffsetBlock == 0)
        bcopy ( buffer, BaseBlockBuffer + OffsetByte, length );
      else
      {
        if ( CacheReadDisk ( OffsetBlock, Block, 1, *FileHandle ) == -1)
          return -1;
        bcopy ( buffer, Block + OffsetByte, length );
        if ( CacheWriteDisk ( OffsetBlock, Block, 1, *FileHandle ) == -1)
          return -1;
      }
    }
    else
      if ( CacheWriteDisk ( OffsetBlock, buffer, 1, *FileHandle ) == -1)
        return -1;
       
    count -= length;
    buffer += length;
    offset += length;
  }

  if (offset > *FileSize)
    *FileSize = offset;
  
  return oldcount;
}

/*******************************************************************************
* int Traverse ( Handle h)
*
*        This function traverses through all our blocks and marks them
* as used in the UsedBlocks list.  It is used upon starting the file system.
* Returns -1 on failure.
*******************************************************************************/

int Traverse ( Handle h )
{
  int loop, pool, pos;

/* Determine the current UniqueNumber */

  if (h / 4096 >= UniqueNumber)
    UniqueNumber = h / 4096 + 1;

/* Mark this handle as used */

  HandleList[ h % 4096 ] = h;
  
/* Read in this file's header */
  
  if ( Access (h) == -1 )
    return -1;

/* Mark the header, direct and indirect block as used */
  
  UsedBlocks[ h % 4096 ]++;
  
  for ( loop = 24; loop--;)
    UsedBlocks[Direct [loop] ]++;
  
  for ( loop = 32; loop--;)
  {
    UsedBlocks[Indirect [loop] ]++;
    if ( Indirect[loop] == 0 )
      continue;
      
    if ( CacheReadDisk( Indirect[loop], IndirectBuffer, 1, h) == -1 )
      return -1;

    for ( pool = 128; pool--; )
      UsedBlocks[ IndirectDirect [pool] ]++;
  }

/* If this file is not a directory, return */
   
  if ( ! *Dir )
    return 0;
  
/* Traverse all the entries in this directory which have not been traversed */

  pos = 0;
  while (pos < *FileSize)
  {
    if (FileRead (128 + 20, pos, Data) == -1)
      return -1;
      
    if ( DataInt[0] == 0 )
      break;
    pos += DataInt[0];
      
    if ( HandleList[ DataInt[1] % 4096 ] == 0 )
    {
      if ( Traverse ( DataInt[1] ) == -1) 	/* This closes file h */
        return -1;
    }

    if ( Access (h) == -1 )			/* Reopen file h */
      return -1; 
  } 
  
  return 0; 
}

/*******************************************************************************
* int StartFileSystem ()
*
*       This function is responsible for correct initialization of all our
* lists and global variables.  It traverses through all blocks.  Returns -1
* on failure.
*******************************************************************************/

int StartFileSystem ()
{
  int loop;
  
 
/* Initialize the unique number and trash the currently loaded file handle */
  
  UniqueNumber = 0;
  *FileHandle = 0;

/* Clean up the free block list and the used handle list */
  
  for ( loop = 4096; loop--;)
    HandleList[loop] = UsedBlocks[loop] = 0;

/* Block 0 can never be used, invalidate the entries */
    
  HandleList[0]--;
  UsedBlocks[0]++;

/* Traverse starting at the root directory */

  if ( Traverse (1) == -1 )
    return -1;

/* Mark any unused blocks as free */

  FreeBlockCount = 0;
  
  for ( loop = 4096; loop--;)
    if ( ! UsedBlocks[loop] )
      FreeBlocks[FreeBlockCount++] = loop;

  return 0;
}


/*******************************************************************************
* int FormatDisk ()
*
*        This function formats our disk into 4096 blocks.  It creates the
* root directory in block 1.
*******************************************************************************/

int FormatDisk ()
{
  int i, j;
  SuperBlock * super_blk = (SuperBlock *) Data;
  char buf[BLOCK_SIZE];

/* Attempt to write every block */

  for (i = 0; i < NUM_OF_BLOCKS; i++)
  {
    if (WriteDisk(i, buf, 1) == -1) 
      return -1;
    printf("%d\b", i); 
    j = i;
    while ( j /= 10 )
      putchar('\b');
    fflush(stdout);
  }
  
  putchar('\n');
  
/* Write the symbolic super block */

  bzero ( Data, BLOCK_SIZE );
  strcpy(&(super_blk->label[0]), DEF_LABEL);
  super_blk->sector_size       = BLOCK_SIZE;
  super_blk->sectors_per_track = DEF_SECTORS_PER_TRACK;
  super_blk->num_cylinders     = DEF_NUM_CYLINDERS;
  super_blk->num_platters      = DEF_NUM_PLATTERS;
  super_blk->checksum          = DEF_CHECKSUM;

  if(WriteDisk(0, Data, 1) == -1)
  {
    printf("Cannot write super block\n");
    return -1;
  }
  
  printf("Wrote super block\n");

/* Create the root directory Handle equal to 1 */

  bzero ( BaseBlockBuffer, BLOCK_SIZE );
  
  *FileHandle = 1;
  time((time_t *) Time);
  *Dir = 1;
  *DirEntryCount = 3;
  *FileSize = 32;
  *LinkCount = 0;
  
  FirstDataInt[0] = FirstDataInt[4] = 16;
  FirstDataInt[1] = FirstDataInt[5] = 1;
  FirstData[8] = FirstData[24] = FirstData[25] = '.';

  if(WriteDisk(1, BaseBlockBuffer, 1) == -1)
  {
    printf("Cannot write root directory\n");
    return -1;
  } 
    
  printf("Wrote root directory\n");  
  printf("Formatted %d blocks of size %d\n", NUM_OF_BLOCKS, BLOCK_SIZE);

  return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1