view src/fs.c @ 98:28c230f0700e

Started very early work on the shell, fnameCmp will now return 0 if a null character is reached in one of the strings at the same location as a space in the other
author Jonathan Pevarnek <pevarnj@gmail.com>
date Sat, 14 May 2011 17:12:12 -0400
parents 917b70f168b0
children
line wrap: on
line source

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

static void* readFSBlock(u32 blkno);
static ErrCode writeFSBlock(u32 blkno, void *ptr);
static ErrCode sync(u32 blkno);
static ErrCode getBlockData(u32 blkno, void* ptr);
static ErrCode setFileSize(u32 fid, size_t size);
static ErrCode isBlockAt(u32 block, short *b);
static ErrCode setBlockAt(u32 block, short isBlock);
static ErrCode nextFreeBlock(u32 start, u32 *loc);
static u32 getCacheLoc(u32 blkno);
static u32 cacheAdd(u32 blkno);
static ErrCode setTod(Inode *inPtr);

static u32 DevID;
static u32 CacheCap; //how many blocks the cache can hold
static u32 CacheSize; //the current number of items 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;
static u32 MaxBlocks;

ErrCode 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 mkError(MODFS, ALLOCFAIL, ERROR);
	CacheLocs = malloc(CacheCap);
	if(!CacheLocs) return mkError(MODFS, ALLOCFAIL, ERROR);
	CacheSize = 0;
	CacheNext = 0;

	DevID = find_dev(0x100);
	Superblock *sb;
	sb = readFSBlock(1);
	if(!sb) return ERR_FSBLKREADFAIL;
	if(sb->magic != FSMAGICNUM) return mkError(MODFS, MUGGLE, ERROR);
	MaxBlocks = sb->nblocks;
	rootLoc = sb->root_inode;

	return 0;
}

ErrCode getFInfo(u32 n, void* de)
{
	Inode *root = readFSBlock(rootLoc);
	if(!root) return ERR_FSBLKREADFAIL;
	if(n >= root->size/DEPBLK) return mkError(MODFS, OUTOFRANGE, WARN);
	Direntry *dp = readFSBlock(root->blocks[n/DEPBLK]);
	if(!dp) return ERR_FSBLKREADFAIL;
	memcpy(de, dp + n%DEPBLK, sizeof(Direntry));
	return 0;
}

//This sets *fid to be equal to the fileid of the file with name fname
ErrCode lookupFile(char *fname, u32 *fid)
{
	size_t i;
	for(i = 0; 1; i++) {
		Direntry de;
		ErrCode err = getFInfo(i, &de);
		if(err == mkError(MODFS, OUTOFRANGE, WARN)) break; //have iterated through all files
		else if(isError(err)) return err; //propogate error
		if(!fnameCmp(fname, de.fname)) {
			*fid = de.inode;
			return 0;
		}
	}
	return mkError(MODFS, NOTFILE, ERROR);
}

//makes an empty file with name fname, sets fid to the fid for the new file
//If the file already exists, an error is returned but fid will be set to the
//fid of that file
ErrCode makeFile(char *fname, u32 *fid)
{
	ErrCode err;
	if(!isError(err = lookupFile(fname, fid)))
		return mkError(MODFS, EXISTS, ERROR); //the file should not exist
	if(err != mkError(MODFS, NOTFILE, ERROR)) return err;
	if(isError(err = nextFreeBlock(0, fid))) return err;

	Inode nin;
	nin.size = 0;
	nin.nblocks = 0;
	if(isError(err = setTod(&nin))) return err;
	nin.ctime = nin.mtime;
	nin._pad0 = nin._pad1 = nin._pad2 = 0; //I am not sure if this is needed...
	if(isError(err = writeFSBlock(*fid, &nin))) return err;

	Direntry de;
	de.inode = *fid;
	memcpy(de.fname, fname, FNAMELEN);
	if(isError(err = fileAppend(rootLoc, &de, sizeof(Direntry)))) return err;

	return 0;
}

ErrCode deleteFile(u32 fid)
{
	ErrCode err;
	if(isError(err = setFileSize(fid, 0))) return err;
	Direntry de;
	u32 fileNo;
	for(fileNo = 0; 1; fileNo++) {
		if(isError(err = getFInfo(fileNo, &de))) return err;
		else if(err == mkError(MODFS, OUTOFRANGE, WARN)) return mkError(MODFS, NOTFILE, ERROR);
		if(de.inode == fid) break;
	}
	Inode *root = readFSBlock(rootLoc);
	if(!root) return ERR_FSBLKREADFAIL;
	u32 rootSize = root->size;
	u32 numDE = rootSize/sizeof(Direntry);
	u32 toBlkNo = root->blocks[fileNo/DEPBLK]; //block that the DE used to occupy
	u32 endBlkNo = root->blocks[numDE/DEPBLK]; //block for the last DE
	Direntry *td = readFSBlock(root->blocks[fileNo/DEPBLK]);
	if(!td) return ERR_FSBLKREADFAIL;
	td += fileNo%DEPBLK;
	Direntry *end = readFSBlock(endBlkNo);
		//this will fail if there is only space for one item in cache and the 2 DEs
		//are in different blocks.  There will be no indication of failure
	if(!end) return ERR_FSBLKREADFAIL;
	end += numDE%DEPBLK - 1;
	*td = *end;
	if(isError(err = setFileSize(rootLoc, rootSize - sizeof(Direntry)))) return err;
	if(isError(err = sync(toBlkNo))) return err; //end does not need to be synchronized
	return 0;
}

ErrCode fileAppend(u32 fid, void *data, u32 length)
{
	ErrCode err;
	u32 fileSize;
	if(isError(err = getFileSize(fid, &fileSize))) return err;
	if(isError(err = fileWrite(fid, data, length, fileSize))) return err;
	return 0;
}

ErrCode fileWrite(u32 fid, void *data, u32 length, u32 offset)
{
	ErrCode err, ret = 0;
	Inode *iptr = readFSBlock(fid);
	if(!iptr) return ERR_FSBLKREADFAIL;
	u32 oldSize = iptr->size;
	u32 dataStop = offset + length;
	if(dataStop > oldSize) {
		if(isError(err = setFileSize(fid, dataStop))) return err;
	}

	Inode in;
	if(isError(err = getBlockData(fid, &in))) return err;

	u32 currPos = offset;
	while(currPos < dataStop) {
		u32 currBlock = in.blocks[currPos/FSBLKSIZE];
		void *ptr = readFSBlock(currBlock);
		if(!ptr) return ERR_FSBLKREADFAIL;
		u32 tcOS = currPos%FSBLKSIZE; //the current offset for the block
		u32 tcSize = Min_u32(dataStop - currPos, FSBLKSIZE); //assume 0 offset
		tcSize = (tcSize + tcOS > FSBLKSIZE)?FSBLKSIZE - tcOS:tcSize; //take into account the offset
		memcpy(ptr + tcOS, data, tcSize);
		if(isError(err = sync(currBlock))) return err;
		data += tcSize;
		currPos += tcSize;
	}

	if(isError(err = setTod(&in))) ret = makeWarn(err);
	if(isError(err = writeFSBlock(fid, &in))) return err;
	return ret;
}

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

//PRECON:  ptr points to a large enough location to hold all the data in file
//         fid
ErrCode getFileData(u32 fid, void *ptr)
{
	ErrCode ret = 0, err;
	int i;
	Inode in;
	if(isError(err = getBlockData(fid, &in))) {
		ret = err;
		goto end;
	}

	u32 size = in.size;
	for(i = 0; i < in.nblocks; i++) {
		Inode *filePtr = readFSBlock(in.blocks[i]);
		if(!filePtr) {
			ret = ERR_FSBLKREADFAIL;
			goto end;
		}
		memcpy(ptr, filePtr, Min_u32(size, FSBLKSIZE));
		ptr += FSBLKSIZE;
		size -= FSBLKSIZE;
	}

end:
	return ret;
}

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) || (*b == ' ' && !*a)) break;
		if(*a - *b) return *a - *b;
	}
	return 0;
}

//XXX HELPER FUNCTIONS XXX

//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);
}

//Writes 1024 bytes from *ptr to data block blkno
static ErrCode writeFSBlock(u32 blkno, void *ptr)
{
	u32 cacheLoc = getCacheLoc(blkno);
	ErrCode err;
	if(cacheLoc == CacheCap)
		cacheLoc = cacheAdd(blkno); //need room in cache for data
	FSBlk *nextFSB = FS_Cache + cacheLoc; //TODO abstraction levels?
	memcpy(nextFSB, ptr, FSBLKSIZE); //copy the data to the cache
	DskBlk *nextDSKB = (DskBlk *) nextFSB; //where to get the data from
	if(isError(err = fba_write_blk(DevID, blkno*2, nextDSKB))) return err;
	//load from the cache to the disk
	if(isError(err = fba_write_blk(DevID, blkno*2 + 1, nextDSKB + 1))) return err;
	short ib;
	if(isError(err = isBlockAt(blkno, &ib))) return err;
	if(!ib) {
		if(isError(err = setBlockAt(blkno, 1))) return makeWarn(err);
	}
	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 ErrCode sync(u32 blkno)
{
	u32 cacheLoc = getCacheLoc(blkno);
	if(cacheLoc == CacheCap) return mkError(MODFS, NOTINCACHE, ERROR);
	FSBlk *nextFSB = FS_Cache + cacheLoc;
	DskBlk *nextDSKB = (DskBlk *) nextFSB; //where to get the data from
	if(fba_write_blk(DevID, blkno*2, nextDSKB)) return mkError(MODFS, BLKWRITEFAIL, ERROR);
	if(fba_write_blk(DevID, blkno*2 + 1, nextDSKB + 1)) return mkError(MODFS, BLKWRITEFAIL, ERROR);
	return 0;
}

static ErrCode getBlockData(u32 blkno, void* ptr)
{
	Inode *temp = readFSBlock(blkno);
	if(!temp) return ERR_FSBLKREADFAIL;
	memcpy(ptr, temp, FSBLKSIZE);
	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 ErrCode setFileSize(u32 fid, size_t size) //TODO clean
{
	ErrCode ret = 0, err;
	unsigned int i;
	u16 neededBlocks = (size + FSBLKSIZE - 1)/FSBLKSIZE;
	if(neededBlocks > MAXBLOCKS) {
		ret = mkError(MODFS, SIZETOOLARGE, ERROR);
		goto end;
	}
	Inode in;
	if(isError(err = getBlockData(fid, &in))) {
		ret = err;
		goto end;
	}

	if(in.nblocks >= neededBlocks) { //get rid of some blocks
		for(i = neededBlocks; i < in.nblocks; i++) { //if it is equal, will skip this loop
			if(isError(err = setBlockAt(in.blocks[i], 0))) {
				ret = err;
				goto end;
			}
		}
	} else { //there are fewer blocks
		for(i = in.nblocks; i < neededBlocks; i++) {
			u32 prevLoc = (i)?in.blocks[i - 1]:fid;
			if(isError(err = nextFreeBlock(prevLoc, &in.blocks[i]))) {
				ret = err;
				goto end;
			}
			if(isError(err = setBlockAt(in.blocks[i], 1))) {
				ret = err;
				goto end;
			}
		}
	}
	in.nblocks = neededBlocks;
	in.size = size;
	if(isError(err = setTod(&in))) {
		ret = makeWarn(err);
	}
	if(isError(err = writeFSBlock(fid, &in))) {
		ret = err;
		goto end;
	}

end:
	return ret;
}

static ErrCode isBlockAt(u32 block, short *b)
{
	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 ERR_FSBLKREADFAIL;
	*b = (ptr[blockSpot] >> spotSpot) & 1;
	return 0;
}

//sets the block table to indicate whether there is a block at block
static ErrCode 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 ERR_FSBLKREADFAIL;
	if(isBlock) ptr[blockSpot] |= 1 << spotSpot;
	else ptr[blockSpot] &= ~(1 << spotSpot);
	ErrCode err;
	if(isError(err = sync(inBlock))) return err;
	return 0;
}

static ErrCode nextFreeBlock(u32 start, u32 *loc)
{
	u32 i;
	ErrCode err;
	for(i = start; i < MaxBlocks; i++) {
		short ib;
		if(isError(err = isBlockAt(i, &ib))) return err;
		if(!ib) {
			*loc = i;
			return 0;
		}
	}
	return mkError(MODFS, DISKFULL, ERROR);
}

//TODO ErrCode?
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
//Returns the location in the cache where the data is
//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;
}

//TODO consider what to do in other places if this returns a warning/error
static ErrCode setTod(Inode *inPtr)
{
	u64 tod;
	ErrCode ret;
	ret = get_tod(&tod);
	inPtr->mtime = tod >> 12;
	return ret;
}