From 6fdab2178fd048622a17147fb625c4c7a602e367 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 7 Sep 2026 17:29:13 +0200 Subject: [PATCH] feat: add readSymlinkTarget and lookupFrom to sfs --- src/sfs.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) diff --git a/src/sfs.c b/src/sfs.c index bc7c4aa..f5384f8 100644 --- a/src/sfs.c +++ b/src/sfs.c @@ -793,3 +793,164 @@ static int32 createNode(const char* path, uint32 type) if (newInode < 0) return -1; + SfsInode inode; + memset(&inode, 0, sizeof(inode)); + inode.type = type; + inode.linkCount = 1; + inode.mtime = inode.ctime = (uint32)rtcNow(); + inode.mode = (type == sfsTypeDir) ? fsModeDefaultDir : fsModeDefaultFile; + inode.uid = currentThreadUid(); + inode.gid = currentThreadGid(); + if (!writeInodeRaw(newInode, &inode)) { + freeInodeNum(newInode); + return -1; + } + + if (!dirAdd(parentInode, leaf, strlen(leaf), newInode, type)) { + freeInodeNum(newInode); + return -1; + } + return newInode; +} + +static bool readSymlinkTarget(int32 inodeNum, char* buf, uint64 cap) +{ + SfsInode inode; + if (!readInode(inodeNum, &inode)) + return false; + if (inode.type != sfsTypeSymlink) + return false; + if (cap == 0 || (uint64)inode.size >= cap) + return false; + + int64 got = sfsReadAt(inodeNum, 0, buf, inode.size); + if (got != (int64)inode.size) + return false; + buf[inode.size] = 0; + return true; +} + +/* Walks path one component at a time from the root, exactly like the old + flat sfsLookup used to, except a symlink component (other than possibly + the last one, depending on followFinal) is resolved by re-running this + same walk on its target and splicing the result back in as `cur` before + continuing with whatever of the original path remains. depth counts hops + already taken across that splicing, so a cycle (or merely a very long + chain) fails cleanly with -1 instead of recursing without bound. */ +static int32 lookupFrom(const char* path, bool followFinal, int depth) +{ + if (!isMounted) + return -1; + if (depth > sfsMaxSymlinkHops) + return -1; + + int32 cur = sfsRootInode; + const char* p = path ? path : ""; + while (*p == '/') + p++; + + while (*p) { + const char* start = p; + while (*p && *p != '/') + p++; + uint64 len = (uint64)(p - start); + if (len == 0 || len >= sfsNameMax) + return -1; + if (sfsTypeOf(cur) != sfsTypeDir) + return -1; + /* Real POSIX search permission on every directory ANCESTOR walked + through, mirroring vfs.c's lookupNode's own equivalent check for + tmpfs -- resolving a name inside `cur` needs the owner-or-other + exec bit for the calling thread's uid, not just "cur is a + directory". */ + if (!vfsPermitted(sfsModeOf(cur), sfsUidOf(cur), sfsGidOf(cur), + currentThreadUid(), currentThreadGid(), fsModeOwnerExec)) + return -1; + int32 child = dirFind(cur, start, len); + if (child < 0) + return -1; + + while (*p == '/') + p++; + bool isLast = (*p == 0); + + if (sfsTypeOf(child) == sfsTypeSymlink && (!isLast || followFinal)) { + char target[sfsSymlinkTargetMax + 1]; + if (!readSymlinkTarget(child, target, sizeof(target))) + return -1; + int32 resolved = lookupFrom(target, true, depth + 1); + if (resolved < 0) + return -1; + child = resolved; + } + + cur = child; + } + return cur; +} + +/* --- public API --- */ + +bool sfsFormat(void) +{ + if (!ataPresent()) + return false; + + uint64 sectors = ataSectorCount(); + if (sectors <= sfsBaseLba + sfsReserveEnd + 64) + return false; + + SfsSuperblock nsb; + memset(&nsb, 0, sizeof(nsb)); + nsb.magic = sfsMagic; + nsb.blockSize = sfsBlockSize; + nsb.totalBlocks = (uint32)(sectors - sfsBaseLba - sfsReserveEnd); + nsb.inodeCount = sfsMaxNodes; + + nsb.inodeBitmapStart = 1; + nsb.inodeBitmapBlocks = (nsb.inodeCount + 8 * sfsBlockSize - 1) / (8 * sfsBlockSize); + if (nsb.inodeBitmapBlocks == 0) + nsb.inodeBitmapBlocks = 1; + + nsb.blockBitmapStart = nsb.inodeBitmapStart + nsb.inodeBitmapBlocks; + nsb.blockBitmapBlocks = (nsb.totalBlocks + 8 * sfsBlockSize - 1) / (8 * sfsBlockSize); + if (nsb.blockBitmapBlocks == 0) + nsb.blockBitmapBlocks = 1; + + nsb.inodeTableStart = nsb.blockBitmapStart + nsb.blockBitmapBlocks; + uint32 inodesPerBlock = sfsBlockSize / sizeof(SfsInode); + nsb.inodeTableBlocks = (nsb.inodeCount + inodesPerBlock - 1) / inodesPerBlock; + + nsb.dataStart = nsb.inodeTableStart + nsb.inodeTableBlocks; + nsb.rootInode = sfsRootInode; + if (nsb.dataStart >= nsb.totalBlocks) + return false; + + /* Committed to actually wiping from here on (every earlier return above + is a rejected format that touched nothing) -- every inode number's + old meaning is about to become garbage, so anything that cached one + from before this point must be told apart from anything cached + after. */ + formatGeneration++; + + if (inodeBitmap) { + kfree(inodeBitmap); + inodeBitmap = nullptr; + } + if (blockBitmap) { + kfree(blockBitmap); + blockBitmap = nullptr; + } + + inodeBitmap = (uint8*)kmalloc((uint64)nsb.inodeBitmapBlocks * sfsBlockSize); + blockBitmap = (uint8*)kmalloc((uint64)nsb.blockBitmapBlocks * sfsBlockSize); + if (!inodeBitmap || !blockBitmap) { + if (inodeBitmap) kfree(inodeBitmap); + if (blockBitmap) kfree(blockBitmap); + inodeBitmap = nullptr; + blockBitmap = nullptr; + return false; + } + memset(inodeBitmap, 0, (uint64)nsb.inodeBitmapBlocks * sfsBlockSize); + memset(blockBitmap, 0, (uint64)nsb.blockBitmapBlocks * sfsBlockSize); +