Mercurial > sos > sos
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; }