From 8f2ecdb2d49836a64d6eab60d2fddf4b239e812a Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 7 Sep 2026 15:04:25 +0200 Subject: [PATCH] feat: add inodeLocation and inodeNumInRange to sfs --- src/sfs.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/src/sfs.c b/src/sfs.c index cfd4dd0..266c772 100644 --- a/src/sfs.c +++ b/src/sfs.c @@ -156,3 +156,154 @@ static bool flushSuperblock(void) return blockWrite(0, buf); } +static void inodeLocation(int32 inodeNum, uint32* blockAbs, uint32* index) +{ + uint32 perBlock = sfsBlockSize / sizeof(SfsInode); + *blockAbs = sb.inodeTableStart + (uint32)inodeNum / perBlock; + *index = (uint32)inodeNum % perBlock; +} + +/* Shared bound check for readInode/writeInodeRaw below: every REAL inode + number allocInodeNum ever hands out satisfies 1 <= n < sb.inodeCount (the + loop above never even considers slot 0, reserved as an eternal sentinel). + Every one of this file's own internal callers already passes a value it + got from allocInodeNum/sfsLookup/dirFind, so this never rejects a + legitimate call -- it exists to stop an out-of-range inodeNum (a caller + bug, or a deliberately-invalid probe like sfsModeOf(9999)) from silently + computing a block address past the real inode table and reading/writing + whatever data block happens to live there instead of failing cleanly. */ +static bool inodeNumInRange(int32 inodeNum) +{ + return inodeNum > 0 && (uint32)inodeNum < sb.inodeCount; +} + +static bool readInode(int32 inodeNum, SfsInode* out) +{ + if (!inodeNumInRange(inodeNum)) + return false; + uint32 blk, idx; + inodeLocation(inodeNum, &blk, &idx); + uint8 buf[sfsBlockSize]; + if (!blockRead(blk, buf)) + return false; + memcpy(out, buf + (uint64)idx * sizeof(SfsInode), sizeof(SfsInode)); + return true; +} + +static bool writeInodeRaw(int32 inodeNum, const SfsInode* in) +{ + if (!inodeNumInRange(inodeNum)) + return false; + uint32 blk, idx; + inodeLocation(inodeNum, &blk, &idx); + uint8 buf[sfsBlockSize]; + if (!blockRead(blk, buf)) + return false; + memcpy(buf + (uint64)idx * sizeof(SfsInode), in, sizeof(SfsInode)); + return blockWrite(blk, buf); +} + +static int32 allocInodeNum(void) +{ + for (uint32 i = 1; i < sb.inodeCount; i++) { + if (!bitTest(inodeBitmap, i)) { + bitSet(inodeBitmap, i); + flushInodeBitmap(); + return (int32)i; + } + } + return -1; +} + +static void freeInodeNum(int32 inodeNum) +{ + bitClear(inodeBitmap, (uint32)inodeNum); + flushInodeBitmap(); +} + +static int32 allocDataBlockAbs(void) +{ + uint32 dataBlockCount = sb.totalBlocks - sb.dataStart; + for (uint32 i = 0; i < dataBlockCount; i++) { + if (!bitTest(blockBitmap, i)) { + bitSet(blockBitmap, i); + flushBlockBitmap(); + uint32 abs = sb.dataStart + i; + uint8 zero[sfsBlockSize]; + memset(zero, 0, sizeof(zero)); + blockWrite(abs, zero); + return (int32)abs; + } + } + return -1; +} + +static void freeDataBlockAbs(uint32 blockAbs) +{ + bitClear(blockBitmap, blockAbs - sb.dataStart); + flushBlockBitmap(); +} + +/* Allocates (if needed) blockAbs's holderAbs on first use and returns the + holder block's contents in buf, zeroed for a fresh allocation. Shared by + the single-indirect and each level of the double-indirect path below, so + the "allocate a pointer block, zero it, write it back" dance is written + once. *holderAbs is updated (and inode->blockCount bumped) when a new + block is allocated. */ +static bool loadOrAllocPointerBlock(SfsInode* inode, uint32* holderAbs, uint8* buf) +{ + if (*holderAbs == 0) { + int32 nb = allocDataBlockAbs(); + if (nb < 0) + return false; + *holderAbs = (uint32)nb; + inode->blockCount++; + memset(buf, 0, sfsBlockSize); + return blockWrite(*holderAbs, buf); + } + return blockRead(*holderAbs, buf); +} + +static bool getOrAllocBlockForWrite(SfsInode* inode, uint32 logicalIndex, uint32* outAbs) +{ + if (logicalIndex < sfsDirectBlocks) { + if (inode->direct[logicalIndex] == 0) { + int32 nb = allocDataBlockAbs(); + if (nb < 0) + return false; + inode->direct[logicalIndex] = (uint32)nb; + inode->blockCount++; + } + *outAbs = inode->direct[logicalIndex]; + return true; + } + + uint32 indIndex = logicalIndex - sfsDirectBlocks; + if (indIndex < sfsPtrsPerBlock) { + uint8 indBuf[sfsBlockSize]; + if (!loadOrAllocPointerBlock(inode, &inode->indirect, indBuf)) + return false; + + uint32* ptrs = (uint32*)indBuf; + if (ptrs[indIndex] == 0) { + int32 nb = allocDataBlockAbs(); + if (nb < 0) + return false; + ptrs[indIndex] = (uint32)nb; + inode->blockCount++; + if (!blockWrite(inode->indirect, indBuf)) + return false; + } + *outAbs = ptrs[indIndex]; + return true; + } + + uint32 doubleIndex = indIndex - sfsPtrsPerBlock; + if (doubleIndex < sfsPtrsPerBlock * sfsPtrsPerBlock) { + uint32 outerIndex = doubleIndex / sfsPtrsPerBlock; + uint32 innerIndex = doubleIndex % sfsPtrsPerBlock; + + uint8 outerBuf[sfsBlockSize]; + if (!loadOrAllocPointerBlock(inode, &inode->doubleIndirect, outerBuf)) + return false; +