view src/fs.c @ 64:72d87920de94

Direntries are no longer cached separately from everything else Main thing: direntries are no longer cached separately from everything else, this was done to make future changes with creating/deleting a file much easier There is currently a TON of code duplication in listFiles() and lookupFile(fname), I am still trying to think of a better way to do this...
author Jonathan Pevarnek <pevarnj@gmail.com>
date Thu, 21 Apr 2011 21:59:51 -0400
parents f0d58047305c
children cd108c0a9030
line wrap: on
line source

#include <fs.h>
#include <std.h>
#include <tod.h>

static u32 setTod(Inode *inPtr);
static u32 getCacheLoc(u32 blkno);
static u32 cacheAdd(u32 blkno);
static int setFileSize(u32 fid, size_t size);
static int setBlockAt(u32 block, short isBlock);
static void* readFSBlock(u32 blkno);
static u32 getBlockData(u32 blkno, void* ptr);
static int writeFSBlock(u32 blkno, void *ptr);
static int sync(u32 blkno);

static u32 DevID;
static u32 CacheCap; //how many blocks the cache can hold
static u32 CacheSize; //the current number of elts in the cache
static FSBlk *FS_Cache = NULL; //A pointer to the actual cache
static u32 *CacheLocs; //what location each block points to
static u32 CacheNext; //the next location to toss a cached block into
static u32 rootLoc;

int init_fs(u32 devnum, u64 __memsize)
{
	//Create the cache
	CacheCap = ((__memsize/100)/FSBLKSIZE)*CACHESPACE;
	//number of blocks to store in the cache
	FS_Cache = malloc(CacheCap*FSBLKSIZE);
	if(!FS_Cache) return -1;
	CacheLocs = malloc(CacheCap);
	if(!CacheLocs) return -1;
	CacheSize = 0;
	CacheNext = 0;

	DevID = find_dev(0x100);
	Superblock *sb;
	sb = readFSBlock(1);
	if(!sb) return -1;
	if(sb->magic != 0x42420374) return -1;
	rootLoc = sb->root_inode;

	return 0;
}

//this lists all the files
u32 listFiles()
{
	size_t i;
	Inode *rootINode = malloc(sizeof(Inode));
	if(!rootINode) return -1;
	if(getBlockData(rootLoc, rootINode)) {
		free(rootINode);
		return -1;
	}

	u32 NFiles = rootINode->size/sizeof(Direntry);

	Direntry *dp = NULL;
	for(i = 0; i < NFiles; i++, dp++) {
		if(!(i%DEPBLK)) {
			dp = readFSBlock(rootINode->blocks[i/DEPBLK]);
			if(!dp) {
				free(rootINode);
				return -1;
			}
		}
		printFname(dp->fname), putline("\n", 1);
	}
	free(rootINode);
	return 0;
}

//This returns the "file ID" of a specific file
u32 lookupFile(char *fname)
{
	//XXX XXX XXX TODO TODO XXX XXX XXX
	//XXX MASSIVE CODE DUPLICATION XXX
	size_t i;
	Inode *rootINode = malloc(sizeof(Inode));
	if(!rootINode) return -1;
	if(getBlockData(rootLoc, rootINode)) {
		free(rootINode);
		return -1;
	}

	u32 NFiles = rootINode->size/sizeof(Direntry);

	Direntry *dp = NULL;
	for(i = 0; i < NFiles; i++, dp++) {
		if(!(i%DEPBLK)) {
			dp = readFSBlock(rootINode->blocks[i/DEPBLK]);
			if(!dp) {
				free(rootINode);
				return -1;
			}
		}
		if(!fnameCmp(fname, dp->fname)) return dp->inode; //XXX THIS IS THE ONLY NON DUPLICATED LINE XXX
	}
	free(rootINode);
	return 0;
}

//PRECON:  fid is a valid file id
//This sets *size equal to the size of the file.  Will return 0 an success
int getFileSize(u32 fid, u32 *size)
{
	Inode *ptr = readFSBlock(fid);
	if(!ptr) return -1;
	*size = ptr->size;
	return 0;
}

//PRECON:  ptr points to a large enough location to hold all the data in file
//         fid
int getFileData(u32 fid, void *ptr)
{
	int i;
	Inode *inPtr = malloc(sizeof(Inode));
	if(!inPtr) return -1;
	if(getBlockData(fid, inPtr)) {
		free(inPtr);
		return -1;
	}

	u32 size = inPtr->size;
	for(i = 0; i < inPtr->nblocks; i++) {
		Inode *filePtr = readFSBlock(inPtr->blocks[i]);
		if(!filePtr) {
			free(inPtr);
			return -1;
		}
		memcpy(ptr, filePtr, Min_u32(size, FSBLKSIZE));
		sync(inPtr->blocks[i]); //update the content of the actual file on disk
		ptr += FSBLKSIZE;
		size -= FSBLKSIZE;
	}
	if(writeFSBlock(fid, inPtr)) {
		free(inPtr);
		return -1;
	}
	free(inPtr);
	return 0;
}

int setFileData(u32 fid, const void *ptr, size_t size)
{
	unsigned int i;
	if(setFileSize(fid, size)) return -1; //TODO efficiency (talk to Jeff)
	Inode *inPtr = malloc(sizeof(Inode));
	if(!inPtr) return -1;
	if(getBlockData(fid, inPtr)) {
		free(inPtr);
		return -1;
	}

	for(i = 0; i < inPtr->nblocks; i++) {
		Inode *filePtr = readFSBlock(inPtr->blocks[i]);
		if(!filePtr) {
			free(inPtr);
			return -1;
		}
		memcpy(filePtr, ptr, Min_u32(size, FSBLKSIZE));
		ptr += FSBLKSIZE;
		size -= FSBLKSIZE;
	}

	if(setTod(inPtr)) { //sadly, this needs to be updated...
		free(inPtr);
		return -1;
	}
	if(writeFSBlock(fid, inPtr)) {
		free(inPtr);
		return -1;
	}
	free(inPtr);
	return 0;
}

//sets the file to have a size equal to size.  The data will be preserved if
//the file is currently larger than that or will be jibberish if the file is
//currently smaller
static int setFileSize(u32 fid, size_t size)
{
	unsigned int i, j;
	u16 neededBlocks = (size + FSBLKSIZE - 1)/FSBLKSIZE;
	if(neededBlocks > MAXBLOCKS) return -1;
	Inode *inPtr = malloc(sizeof(Inode));
	if(!inPtr) return -1;
	if(getBlockData(fid, inPtr)) {
		free(inPtr);
		return -1;
	}

	if(inPtr->nblocks >= neededBlocks) { //get rid of some blocks
		for(i = neededBlocks; i < inPtr->nblocks; i++) { //if it is equal, will skip this loop
			if(setBlockAt(inPtr->blocks[i], 0)) {
				free(inPtr);
				return -1;
			}
		}
	} else { //there are fewer blocks
		for(i = inPtr->nblocks; i < neededBlocks; i++) {
			inPtr->blocks[i] = 0;
			for(j = inPtr->blocks[i - 1]; inPtr->blocks[i] == 0; j++)
				if(!isBlockAt(j)) inPtr->blocks[i] = j;
			if(setBlockAt(inPtr->blocks[i], 1)) {
				free(inPtr);
				return -1;
			}
		}
	}
	inPtr->nblocks = neededBlocks;
	inPtr->size = size;
	if(setTod(inPtr)) {
		free(inPtr);
		return -1;
	}
	if(writeFSBlock(fid, inPtr)) {
		free(inPtr);
		return -1;
	}
	free(inPtr);
	return 0;
}

short isBlockAt(u32 block)
{ //TODO reconsider return value
	u32 inBlock = block/(8*FSBLKSIZE) + 2; //which block the bit is in
	u32 blockSpot = (block%(8*FSBLKSIZE))/8; //which u8 the bit is in
	u32 spotSpot = 7 - block%8; //which bit the bit is
	u8 *ptr = readFSBlock(inBlock);
//	if(!ptr) return -1; //TODO return value
	return (ptr[blockSpot] >> spotSpot) & 1;
}

//sets the block table to indicate whether there is a block at block
int setBlockAt(u32 block, short isBlock)
{
	u32 inBlock = block/(8*FSBLKSIZE) + 2; //which block the bit is in
	u32 blockSpot = (block%(8*FSBLKSIZE))/8; //which u8 the bit is in
	u32 spotSpot = 7 - block%8; //which bit the bit is
	u8 *ptr = readFSBlock(inBlock);
	if(!ptr) return -1;
	if(isBlock) ptr[blockSpot] |= 1 << spotSpot;
	else ptr[blockSpot] &= ~(1 << spotSpot);
	sync(inBlock);
	return 0;
}

void printFname(char *name)
{
	putline(name, 28);
}

//PRECON:  fname points to a location 28 characters long
//Sets fname to contain a filename entered by the user
void getFname(char *fname)
{
	int chars = getline(fname, FNAMELEN);
	for(;chars < FNAMELEN; chars++) {
		fname[chars] = ' ';
	}
}

//Checks whether two file names are equal
int fnameCmp(const char *a, const char *b)
{
	int i;
	for(i = 0; i < FNAMELEN; i++, a++, b++) {
		if(*a - *b) return *a - *b;
	}
	return 0;
}

static u32 setTod(Inode *inPtr)
{
	u64 tod;
	if(get_tod(&tod)) return -1;
	inPtr->mtime = tod;
	return 0;
}

static u32 getCacheLoc(u32 blkno)
{
	int i;
	for(i = 0; i < CacheSize; i++) {
		if(CacheLocs[i] == blkno) return i;
	}
	return CacheCap;
}

//PRECON:   block blkno is not already in the cache
//sets aside space in the cache for some item.  NOTE: this does not read the
//current contents of the disk to the cache
static u32 cacheAdd(u32 blkno)
{
	u32 ret = CacheNext;
	CacheLocs[CacheNext] = blkno;
	if(CacheSize != CacheCap) CacheSize += 1;
	if(CacheNext != CacheCap - 1) CacheNext += 1;
	else CacheNext = 0;
	return ret;
}

//returns a pointer pointing to a location where the data from blkno will be
//stored
static void* readFSBlock(u32 blkno)
{
	u32 cacheLoc = getCacheLoc(blkno); //Do not reload if in cache
	if(cacheLoc == CacheCap) { //It was not found in the cache
		cacheLoc = cacheAdd(blkno);
		FSBlk *nextFSB = FS_Cache + cacheLoc;
		DskBlk *nextDSKB = (DskBlk *) nextFSB; //block to put data in
		if(fba_read_blk(DevID, blkno*2, nextDSKB)) return NULL;
		if(fba_read_blk(DevID, blkno*2 + 1, nextDSKB + 1)) return NULL;
	}
	return (void *)(FS_Cache + cacheLoc);
}

static u32 getBlockData(u32 blkno, void* ptr)
{
	Inode *temp = readFSBlock(blkno);
	if(!temp) return -1;
	memcpy(ptr, temp, FSBLKSIZE);
	return 0;
}

//Writes 1024 bytes from *ptr to data block blkno
static int writeFSBlock(u32 blkno, void *ptr)
{
	u32 cacheLoc = getCacheLoc(blkno);
	if(cacheLoc == CacheCap)
		cacheLoc = cacheAdd(blkno); //need room in cache for data
	FSBlk *nextFSB = FS_Cache + cacheLoc;
	memcpy(nextFSB, ptr, FSBLKSIZE); //copy the data to the cache
	DskBlk *nextDSKB = (DskBlk *) nextFSB; //where to get the data from
	if(fba_write_blk(DevID, blkno*2, nextDSKB)) return -1; //load from the cache to the disk
	
	if(fba_write_blk(DevID, blkno*2 + 1, nextDSKB + 1)) return -1;
	if(!isBlockAt(blkno))
		if(setBlockAt(blkno, 1)) return -1;
	return 0;
}

//updates the data in block blkno based upon the data in the cache.  If block
//blkno is not in the cache, returns non-zero
static int sync(u32 blkno)
{
	u32 cacheLoc = getCacheLoc(blkno);
	if(cacheLoc == CacheCap) return -1;
	FSBlk *nextFSB = FS_Cache + cacheLoc;
	DskBlk *nextDSKB = (DskBlk *) nextFSB; //where to get the data from
	if(fba_write_blk(DevID, blkno*2, nextDSKB)) return -1;
	if(fba_write_blk(DevID, blkno*2 + 1, nextDSKB + 1)) return -1;
	return 0;
}