#include <stddef.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include "ubixfs.h"
#include "btree.h"
UbixFS::UbixFS(device_t * dev) {
device = dev;
freeBlockList = NULL;
} // UbixFS::UbixFS
int
UbixFS::vfs_init(void) {
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
device->read(device, superBlock, device->size-1, 1);
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->BAT.len*4096];
assert(freeBlockList);
device->read(device,
freeBlockList,
((superBlock->BAT.allocationGroup << superBlock->AGShift)
+ superBlock->BAT.start) * 8,
superBlock->BAT.len * 8
); // device->read()
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;
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) {
char sector[512];
uInt32 blocks, batSect, batSize;
if (dev == NULL) return -1;
// zero out the sector
memset(§or, 0, sizeof(sector));
// fill the drive in with zeroed out sectors
for (unsigned int i = 0; i < dev->size; i++) {
dev->write(dev, §or, i, 1);
} // for i
// allocate a new superBlock and clear it
diskSuperBlock *sb = new diskSuperBlock;
if (sb == NULL) return -1;
memset(sb, 0, sizeof(diskSuperBlock));
// dev->size is the size of the device in 512 sectors
blocks = (dev->size-1) / 8; // 4k blocks
batSize = (dev->size-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 = 1; // root dir takes one block
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;
sb->BAT.allocationGroup = batSect / sb->blocksPerAG;
sb->BAT.start = batSect % sb->blocksPerAG;
sb->BAT.len = (batSize+7) / 8;
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 = 2;
sb->rootDir.start = 0;
sb->rootDir.len = 1;
dev->write(dev, sb, dev->size-1, 1);
delete sb;
return 0;
} // UbixFS::vfs_format
UbixFS::~UbixFS(void) {
delete [] freeBlockList;
return;
}