Newer
Older
ubixos-old / src / sys / ubixfsv2 / ubixfs.cpp
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <iostream>

#include "ubixfs.h"
#include "btree.h"

using namespace std;

UbixFS::UbixFS(void) { 
  device = NULL;
  freeBlockList = NULL;
  superBlock = NULL;
} // UbixFS::UbixFS

UbixFS::UbixFS(device_t * dev) {
  device = dev;
  freeBlockList = NULL;
  superBlock = NULL;
} // UbixFS::UbixFS

void
UbixFS::printSuperBlock(void) {
  printf("superBlock->name............%s\n", superBlock->name);
  printf("superBlock->magic1..........%X\n", superBlock->magic1);
  printf("superBlock->fsByteOrder.....%d\n", superBlock->fsByteOrder);
  printf("superBlock->blockSize.......%d\n", superBlock->blockSize);
  printf("superBlock->blockShift......%d\n", superBlock->blockShift);
  printf("superBlock->numBlocks.......%d\n", superBlock->numBlocks);
  printf("superBlock->usedBlocks......%d\n", superBlock->usedBlocks);
  printf("superBlock->batSectors......%d\n", superBlock->batSectors);
  printf("superBlock->inodeCount......%d\n", superBlock->inodeCount);
  printf("superBlock->magic2..........%X\n", superBlock->magic2);
  printf("superBlock->blocksPerAG.....%d\n", superBlock->blocksPerAG);
  printf("superBlock->AGShift.........%d\n", superBlock->AGShift);
  printf("superBlock->numAGs..........%d\n", superBlock->numAGs);
  printf("superBlock->lastUsedAG......%d\n", superBlock->lastUsedAG);
  printf("superBlock->flags...........%X\n", superBlock->flags);
  printf("superBlock->magic3..........%X\n", superBlock->magic3);
  return;
} // UbixFS::printSuperBlock

int 
UbixFS::vfs_init(void) {
assert(device);
  cout << "vfs_init()" << endl;
  assert(device);

  if (device == NULL) return -1;
  if (superBlock != NULL) delete superBlock;
  superBlock = new diskSuperBlock;
assert(superBlock);
  if (superBlock == NULL) return -1;

  // read in the superBlock. It's always the last block on the partition 
cout << "reading in superBlock" << endl;
  device->read(device, superBlock, device->sectors-1, 1);
cout << "done" << endl;

  assert(superBlock->magic1 == UBIXFS_MAGIC1);
  assert(superBlock->magic2 == UBIXFS_MAGIC2);
  assert(superBlock->magic3 == UBIXFS_MAGIC3);
  assert(strcmp(superBlock->name, "UbixFS") == 0);
  assert((1 << superBlock->blockShift) == superBlock->blockSize);
  assert((1 << superBlock->AGShift) == superBlock->blocksPerAG);
  assert(superBlock->flags == UBIXFS_CLEAN);

  if (freeBlockList != NULL) delete [] freeBlockList;
  freeBlockList = new signed char[superBlock->batSectors*512];
assert(freeBlockList);
cout << "reading in freeBlockList" << endl;
  device->read(device, 
               freeBlockList, 
               device->sectors-superBlock->batSectors-1,
               superBlock->batSectors
              ); // device->read()

  printSuperBlock();
                 
  return 0;
} // UbixFS::init

blockRun
UbixFS::getFreeBlock(uInt32 AG) {
  // AG == AllocationGroup
  blockRun br;
  signed char * ptr;
  int32 count;
  int32 subCount = 128;

  br.allocationGroup = 0;
  br.start = 0;
  br.len = 0;
  // Check to make sure neither of these are null
  if (device == NULL || freeBlockList == NULL || superBlock == NULL) return br;

  // Are there any blocks available?
  if (superBlock->numBlocks == superBlock->usedBlocks) return br;

  /* 
   * count is the block from the base of the list.
   * Since we're given a specific AG to look through, we start the count at
   * AG << AGShift, where AGShift is the shift value of the number of blocks
   * in an AG 
   */

  count = (AG << superBlock->AGShift);

  /*
   * The freeBlockList is a bit map of the free/used blocks. 
   * Used = on bit
   * Unused = off bit
   * There are 8 bits per byte (hopefully) and so we have to divide the count
   * by 8 to get our starting byte offset to look from
   */

  ptr = freeBlockList + (count >> 3);

  // Scan through the freeBlockList 

rescan:
  while (*ptr == -1) { 
    ++ptr;
    count += 8;
    if (count+8 > superBlock->numBlocks) break;
  } // while *ptr == -1

  subCount = 128;

  do {
    if ((*ptr & subCount) == 0) break;
    subCount >>= 1;
    ++count;
    if (count == superBlock->numBlocks) {
      count = 0;
      ptr = freeBlockList;
      goto rescan;
    } // if
  } while(subCount > 1);

  *ptr |= subCount;           // mark this block as used
  ++superBlock->usedBlocks;   // increment the number of used blocks

  br.allocationGroup = count / superBlock->blocksPerAG; 
  br.start = count % superBlock->blocksPerAG;
  br.len = 1;
  return br;               // return the allocated block number
} // Ubixfs::getFreeBlock

uInt32
UbixFS::getNextAG(void) {

  if (superBlock->lastUsedAG == superBlock->numAGs) 
    superBlock->lastUsedAG = 0;
  else
    superBlock->lastUsedAG++;
  return superBlock->lastUsedAG;

} // UbixFS::getNextAG

/*
 * UbixFS::getFreeBlock(void)
 * upon success returns a free block based on the next AG after the lastUsedAG
 * failure returns -1
 */

blockRun
UbixFS::getFreeBlock(void) {
  return getFreeBlock(getNextAG());
} // UbixFS::getFreeBlock

blockRun
UbixFS::get8FreeBlocks(uInt32 AG) {
  // AG == AllocationGroup
  blockRun br;
  signed char * ptr;
  signed char * endPtr;
  int32 count;

  br.allocationGroup = 0;
  br.start = 0;
  br.len = 0;

  if (device == NULL || freeBlockList == NULL || superBlock == NULL) return br;

  // Are there any blocks available?
  if (superBlock->usedBlocks+8 > superBlock->numBlocks) return br;

  /*
   * count is the block from the base of the list.
   * Since we're given a specific AG to look through, we start the count at
   * AG << AGShift, where AGShift is the shift value of the number of blocks
   * in an AG
   */

  count = (AG << superBlock->AGShift);

  ptr = freeBlockList + (count >> 3);
  
  endPtr = freeBlockList + (superBlock->numBlocks >> 3);

  bool secondTime = false;
  while (*ptr != 0) {
    ++ptr;
    count += 8;
    if (ptr == endPtr) {
      if (secondTime) 
        return br; 
      else 
        secondTime = true;

      count = 0;
      ptr = freeBlockList;      
    } // if
  } // while 

  *ptr = -1;   // mark 8 blocks as taken

  br.allocationGroup = count / superBlock->blocksPerAG;
  br.start = count % superBlock->blocksPerAG;
  br.len = 8;
  return br;
} // UbixFS::get8FreeBlocks

void *
UbixFS::mknod(const char *filename, ubixfsInode * parent) {
  ubixfsInode * inode = NULL;

  inode = new ubixfsInode;
  assert(inode);
  if (inode == NULL) return NULL;
  memset(inode, 0, sizeof(ubixfsInode));

  // inode->magic1 = ;
  if (parent == NULL) {
    inode->inodeNum = getFreeBlock();
    inode->parent.iAddr.allocationGroup = 0;
    inode->parent.iAddr.start = 0;
    inode->parent.iAddr.len = 0;
  } else {
    inode->inodeNum = getFreeBlock(parent->inodeNum.allocationGroup);
    inode->parent.iAddr = parent->inodeNum;
  } // else
   
  strncpy(inode->name, filename, MAX_FILENAME_LENGTH);

  inode->uid = getuid();
  inode->gid = getgid();
  // inode->mode
  // inode->flags
  // inode->createTime
  // inode->lastModifiedTime
  // inode->type 
  inode->inodeSize = superBlock->inodeSize;

  /*
   * next and prev are used in memory to hold pointers to the next/prev
   * inodes in this dir.  On disk they may have another value, but for
   * now they should be set to null.
   */

  inode->next.offset = 0;
  inode->prev.offset = 0;
  ++superBlock->inodeCount;
  return inode;
} // UbixFS::mknod

void *
UbixFS::vfs_mknod(const char *path, mode_t mode) {
  return mknod(path, 0);
} // UbixFS::mknod

int
UbixFS::vfs_format(device_t * dev) {
  cout << "vfs_format()" << endl;
  char sector[512];
  uInt32 blocks, batSect, batSize;
  if (dev == NULL) return -1; 

  // zero out the sector
  memset(&sector, 0, sizeof(sector));
 
  // fill the drive in with zeroed out sectors
  cout << "dev->sectors: " << dev->sectors << endl;
  cout << "clearing device" << endl;

  for (unsigned int i = 0; i < dev->sectors; i++) {
    dev->write(dev, &sector, i, 1);
  } // for i
  cout << "device clear" << endl;

  // allocate a new superBlock and clear it

  diskSuperBlock *sb = new diskSuperBlock;
  if (sb == NULL) return -1;
  memset(sb, 0, sizeof(diskSuperBlock));

  // dev->sectors is the number of 512 byte sectors

  blocks = (dev->sectors-1) / 8;     // 4k blocks
  batSize = (dev->sectors-1) % 8;    // remainder
  
  // compute the BAT size

  while ((batSize * 4096) < blocks) {
    batSize += 8;
    --blocks;
  } // while
 
  // batSize is in sectors
  batSect = blocks * 8;

  strcpy(sb->name, "UbixFS");
  sb->magic1 = UBIXFS_MAGIC1;
  sb->fsByteOrder = 0;
  sb->blockSize = 4096;
  sb->blockShift = 12;
  sb->numBlocks = blocks;
  sb->usedBlocks = 2;  // root dir takes two blocks (inode + bTree header)
  sb->inodeCount = 1;
  sb->inodeSize = 4096;
  sb->magic2 = UBIXFS_MAGIC2;
  sb->blocksPerAG = 2048;
  sb->AGShift = 11;
  sb->numAGs = (sb->numBlocks+2047) / 2048;
  sb->lastUsedAG = 0;

  // the BAT exists outside our official block count, so no 
  // entries in the BAT need to be set for it
  sb->batSectors = batSize;

  sb->flags = 0x434C454E; // CLEN
  sb->logBlocks.allocationGroup = 0;
  sb->logBlocks.start = 0;
  sb->logBlocks.len = 0;
  sb->logStart = 0;
  sb->logEnd = 0;
  sb->magic3 = UBIXFS_MAGIC3;
  sb->indicies.allocationGroup = 0;
  sb->indicies.start = 0;
  sb->indicies.len = 0;

  sb->rootDir.allocationGroup = 0;
  sb->rootDir.start = 0;
  sb->rootDir.len = 1;

  // write out the superBlock
cout << "writing superBlock" << endl;
  dev->write(dev, sb, dev->sectors-1, 1);

cout << "batSector: " << batSect << endl;
cout << "batSize: " << batSize << endl; 

  // mark the first two 4k blocks used
  sector[0] = (1 << 7) | (1 << 6);
  // write out the first sector of the BAT 
cout << "Writing BAT - 1 - " << endl;
  dev->write(dev, &sector, batSect / 8, 1);
  // clear the rest
  sector[0] = 0;
  // write out the rest of the BAT
cout << "Writing BAT - 2 - " << endl;
  for (unsigned int i = 1; i < batSize; i++) {
    dev->write(dev, &sector, (batSect / 8)+i, 1);
  } // for i
cout << "Finished writing BAT" << endl;
  /* allocate part of the root dir */

  // sanity checks
  assert(sb->blockSize);
  assert((unsigned)sb->blockSize >= sizeof(bTreeHeader));
cout << "allocating bTree header" << endl;
  bTreeHeader * bth = (bTreeHeader *)malloc(sb->blockSize);
  assert(bth);
  memset(bth, 0, sb->blockSize);
  bth->firstDeleted = bth->firstNodeOffset = -1;

  /* create the root dir inode here */
cout << "creating root inode" << endl;
  ubixfsInode * inode = new ubixfsInode;
  assert(inode);
  if (inode == NULL) return NULL;
  memset(inode, 0, sizeof(ubixfsInode));

  // inode->magic1 = ;
  inode->inodeNum.allocationGroup = 0;
  inode->inodeNum.start = 0;
  inode->inodeNum.len = 1;

  inode->parent.iAddr.allocationGroup = 0;
  inode->parent.iAddr.start = 0;
  inode->parent.iAddr.len = 0;

  strcpy(inode->name, "/");
  inode->uid = getuid();
  inode->gid = getgid();
  // inode->mode
  // inode->flags
  // inode->createTime
  // inode->lastModifiedTime
  // inode->type
  inode->inodeSize = sb->inodeSize;

  /*
   * next and prev are used in memory to hold pointers to the next/prev
   * inodes in this dir.  On disk they may have another value, but for
   * now they should be set to null.
   */

  inode->next.offset = 0;
  inode->prev.offset = 0;

  // write out the "root" dir inode

  dev->write(dev, inode, 0, sb->inodeSize / 512);
  // write out the "root" dir
  dev->write(dev, bth, 1, sb->blockSize / 512);
  delete inode;
  free(bth);
  delete sb;
  cout << "format complete" << endl;
  return 0;
} // UbixFS::vfs_format

UbixFS::~UbixFS(void) {
  delete [] freeBlockList;
  return;
}