diff --git a/src/sfs.c b/src/sfs.c new file mode 100644 index 0000000..cfd4dd0 --- /dev/null +++ b/src/sfs.c @@ -0,0 +1,158 @@ +#include "sfs.h" +#include "ata.h" +#include "heap.h" +#include "string.h" +#include "rtc.h" +#include "fs.h" +#include "sched.h" +#include "vfs.h" + +#define sfsDirectBlocks 10 +#define sfsPtrsPerBlock (sfsBlockSize / sizeof(uint32)) /* 128 */ + +/* 88 bytes: 512/88 = 5 inodes per block (72 bytes/block left unused -- + inodeLocation and the format-time inodeTableBlocks math already derive + everything from sizeof(SfsInode) generically, so this shrinking density + is the honest cost of the extra fields, not a layout hack, same story as + 84 bytes/6-per-block was before gid). Addressing is direct (10 blocks) + + single indirect (128 blocks) + double indirect (128 * 128 blocks) + + triple indirect (128 * 128 * 128 blocks): a max file size of roughly + (10 + 128 + 128*128 + 128*128*128) * 512 bytes ~= 1032 MiB, versus ~8 MiB + with direct/single/double alone. mtime/ctime (rtc.h's rtcNow(), seconds + since the Unix epoch, truncated to 32 bits -- valid through year 2106, + same honest scope as rtc.c's own 2000-2099 century limit), mode (fs.h's + fsModeOwner*, real chmod(2) bits), uid (fs.h's FbStat.uid, sched.h's + currentThreadUid() at creation) and now gid (fs.h's FbStat.gid, sched.h's + currentThreadGid() at creation) are the newest fields, genuinely growing + the struct like tripleIndirect did before them -- there is no padding + left to repurpose. */ +typedef struct SfsInode { + uint32 type; + uint32 size; + uint32 linkCount; + uint32 blockCount; + uint32 direct[sfsDirectBlocks]; + uint32 indirect; + uint32 doubleIndirect; + uint32 tripleIndirect; + uint32 mtime; + uint32 ctime; + uint32 mode; + uint32 uid; + uint32 gid; +} SfsInode; + +/* Exactly 64 bytes: 512/64 = 8 directory entries per block. name[56] leaves + room for sfsNameMax (55) chars plus the null terminator. */ +typedef struct SfsDirEntry { + uint32 inode; + uint32 type; + char name[56]; +} SfsDirEntry; + +typedef struct SfsSuperblock { + uint32 magic; + uint32 blockSize; + uint32 totalBlocks; + uint32 inodeCount; + uint32 inodeBitmapStart; + uint32 inodeBitmapBlocks; + uint32 blockBitmapStart; + uint32 blockBitmapBlocks; + uint32 inodeTableStart; + uint32 inodeTableBlocks; + uint32 dataStart; + uint32 rootInode; +} SfsSuperblock; + +#define sfsMagic 0x53465331u /* "SFS1" */ + +static SfsSuperblock sb; +static uint8* inodeBitmap = nullptr; +static uint8* blockBitmap = nullptr; +static bool isMounted = false; + +/* See sfsGeneration()'s doc comment (sfs.h). Starts at 1 (not 0) so a caller + that zero-initializes its own cache can use generation 0 to mean "never + bound to anything yet" without that ever colliding with a real one. */ +static uint64 formatGeneration = 1; + +/* --- internal helpers (forward-declared so definition order can follow the + dependency graph without header noise) --- */ +static bool bitTest(const uint8* map, uint32 bit); +static void bitSet(uint8* map, uint32 bit); +static void bitClear(uint8* map, uint32 bit); +static bool blockRead(uint32 blockAbs, void* buffer); +static bool blockWrite(uint32 blockAbs, const void* buffer); +static bool flushInodeBitmap(void); +static bool flushBlockBitmap(void); +static bool flushSuperblock(void); +static void inodeLocation(int32 inodeNum, uint32* blockAbs, uint32* index); +static bool readInode(int32 inodeNum, SfsInode* out); +static bool writeInodeRaw(int32 inodeNum, const SfsInode* in); +static int32 allocInodeNum(void); +static void freeInodeNum(int32 inodeNum); +static int32 allocDataBlockAbs(void); +static void freeDataBlockAbs(uint32 blockAbs); +static bool loadOrAllocPointerBlock(SfsInode* inode, uint32* holderAbs, uint8* buf); +static bool getOrAllocBlockForWrite(SfsInode* inode, uint32 logicalIndex, uint32* outAbs); +static bool getBlockForRead(const SfsInode* inode, uint32 logicalIndex, uint32* outAbs); +static bool sfsWriteRaw(SfsInode* inode, int32 inodeNum, uint64 position, const void* data, uint64 length); +static bool sfsTruncateInode(SfsInode* inode, int32 inodeNum, uint32 newSize); +static bool splitPath(const char* path, char* parentOut, uint64 parentCap, char* leafOut, uint64 leafCap); +static int32 dirFind(int32 dirInode, const char* name, uint64 nameLen); +static bool dirAdd(int32 dirInode, const char* name, uint64 nameLen, int32 childInode, uint32 type); +static bool dirRemove(int32 dirInode, const char* name, uint64 nameLen); +static int32 createNode(const char* path, uint32 type); +static bool readSymlinkTarget(int32 inodeNum, char* buf, uint64 cap); +static int32 lookupFrom(const char* path, bool followFinal, int depth); + +static bool bitTest(const uint8* map, uint32 bit) +{ + return (map[bit / 8] >> (bit % 8)) & 1u; +} + +static void bitSet(uint8* map, uint32 bit) +{ + map[bit / 8] = (uint8)(map[bit / 8] | (uint8)(1u << (bit % 8))); +} + +static void bitClear(uint8* map, uint32 bit) +{ + map[bit / 8] = (uint8)(map[bit / 8] & (uint8)~(1u << (bit % 8))); +} + +static bool blockRead(uint32 blockAbs, void* buffer) +{ + return ataReadSectors(sfsBaseLba + blockAbs, 1, buffer); +} + +static bool blockWrite(uint32 blockAbs, const void* buffer) +{ + return ataWriteSectors(sfsBaseLba + blockAbs, 1, buffer); +} + +static bool flushInodeBitmap(void) +{ + for (uint32 i = 0; i < sb.inodeBitmapBlocks; i++) + if (!blockWrite(sb.inodeBitmapStart + i, inodeBitmap + (uint64)i * sfsBlockSize)) + return false; + return true; +} + +static bool flushBlockBitmap(void) +{ + for (uint32 i = 0; i < sb.blockBitmapBlocks; i++) + if (!blockWrite(sb.blockBitmapStart + i, blockBitmap + (uint64)i * sfsBlockSize)) + return false; + return true; +} + +static bool flushSuperblock(void) +{ + uint8 buf[sfsBlockSize]; + memset(buf, 0, sizeof(buf)); + memcpy(buf, &sb, sizeof(sb)); + return blockWrite(0, buf); +} +