feat: add readSymlinkTarget and lookupFrom to sfs

This commit is contained in:
dualfroz committed 2026-09-07 17:29:13 +02:00
1 parent 39520e6183
commit 6fdab2178f
1 file changed
+161
+161
View File
@@ -793,3 +793,164 @@ static int32 createNode(const char* path, uint32 type)
if (newInode < 0) if (newInode < 0)
return -1; 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);