feat: add readSymlinkTarget and lookupFrom to sfs
This commit is contained in:
1 parent
39520e6183
commit
6fdab2178f
1 file changed
+161
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user