view src/fs.c @ 87:480f5685b3c2

Fixed bugs in delete, moved arrayLookup to sarpn arrayLookup was a specialized function that, though useful, did not belong in the standard library delete was not properly determining the location for the end block and also not actually updating the disk with the re-written table. Both fixed.
author Jonathan Pevarnek <pevarnj@gmail.com>
date Wed, 27 Apr 2011 22:49:00 -0400
parents 2fb2138d8c83
children 7962969a9abd
line wrap: on
line source

#include <fs.h>
#include <std.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;
	*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;
	ErrCode ret = 0;
	Inode *iptr = readFSBlock(fid);
	if(!iptr) return ERR_FSBLKREADFAIL;
	u32 oldSize = iptr->size;
	if(isError(err = setFileSize(fid, oldSize + length))) return err;
	Inode in;
	if(isError(err = getBlockData(fid, &in))) return err;

	//Each iteration here should copy all the data that needs to go into one block...
	u32 maxSize = in.size; //the final size the file should hold
	u32 currSize = oldSize;
	while(currSize < maxSize) {
		u32 currBlock = in.blocks[currSize/FSBLKSIZE];
		void *ptr = readFSBlock(currBlock);
		if(!ptr) return ERR_FSBLKREADFAIL;
		u32 tcOS = currSize%FSBLKSIZE; //the current offset for the block
		u32 tcSize = Min_u32(maxSize - currSize, FSBLKSIZE); //assume 0 offset
		tcSize = (tcSize + tcOS > FSBLKSIZE)?FSBLKSIZE - tcOS:tcSize; //take int account the offset
		memcpy(ptr + tcOS, data, tcSize);
		if(isError(err = sync(currBlock))) return err;
		data += tcSize;
		currSize += tcSize;
	}
	
	if(isError(err = setTod(&in))) ret = makeWarn(err);
	//everything else should still work fine, just a bad time
	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) 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;
}