#include <stddef.h> #include <stdlib.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; assert(inode); 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 byte 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; // the BAT exists outside our official block count, so no // entries in the BAT need to be set for it 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 = 0; sb->rootDir.start = 0; sb->rootDir.len = 1; // write out the superBlock dev->write(dev, sb, dev->size-1, 1); // mark the first two 4k blocks used sector[0] = (1 << 7) | (1 << 6); // write out the first sector of the BAT dev->write(dev, §or, batSect, 1); // clear the rest sector[0] = 0; // write out the rest of the BAT dev->write(dev, §or, batSect+1, batSize-1); /* allocate part of the root dir */ // sanity checks assert(superBlock->blockSize); assert((unsigned)superBlock->blockSize >= sizeof(bTreeHeader)); bTreeHeader * bth = (bTreeHeader *)malloc(superBlock->blockSize); assert(bth); memset(bth, 0, superBlock->blockSize); bth->firstDeleted = bth->firstNodeOffset = -1; // write out the "root" dir inode ubixfsInode * inode = new ubixfsInode; assert(inode); memset(inode, 0, sizeof(ubixfsInode)); strcpy(inode->name, "/"); dev->write(dev, inode, 0, superBlock->inodeSize / 512); // write out the "root" dir dev->write(dev, bth, 1, superBlock->blockSize / 512); free(bth); delete sb; return 0; } // UbixFS::vfs_format UbixFS::~UbixFS(void) { delete [] freeBlockList; return; }