local args = {...} local kernel = args[1] local fs = {} local disks = {} local mounts = {["/"]="$"} -- path normaliing local function normalizePath(path) if not path or path == "" then return "/" end if path:sub(1,1) ~= "/" then path = "/" .. path end local parts = {} for part in path:gmatch("[^/]+") do if part == ".." then if #parts > 0 then table.remove(parts) end elseif part ~= "." and part ~= "" then parts[#parts+1] = part end end return "/" .. table.concat(parts, "/") end -- disk resolution local function resolvePath(path) local abs = normalizePath(path) local best = "/" for mount, diskAddr in pairs(mounts) do if abs:sub(1, #mount) == mount and #mount > #best then best = mount end end local newPath = abs:sub(#best + 1) if newPath == "" then newPath = "/" end return disks[mounts[best]], newPath end -- Symlinks -- Format: #!@SYMLINK[target] local SYMLINK_PREFIX = "#!@SYMLINK[" local SYMLINK_SUFFIX = "]" local SYMLINK_MAX_DEPTH = 64 local function isSymlink(disk, path) if not disk:fileExists(path) then return false end local text = disk:readAllText(path) if not text then return false end return text:sub(1, #SYMLINK_PREFIX) == SYMLINK_PREFIX end local function readSymlink(disk, path) local text = disk:readAllText(path) if not text then return nil end if text:sub(1, #SYMLINK_PREFIX) ~= SYMLINK_PREFIX then return nil end local target = text:sub(#SYMLINK_PREFIX + 1) if target:sub(-1) == SYMLINK_SUFFIX then target = target:sub(1, -2) end return target end -- Path resolution local function splitPath(p) local parts = {} for part in p:gmatch("[^/]+") do parts[#parts+1] = part end return parts end local function resolveSymlink(path) path = normalizePath(path) local symlinkDepth = 0 local parts = splitPath(path) local resolved = {} local i = 1 while i <= #parts do local comp = parts[i] if comp == "." then elseif comp == ".." then if #resolved > 0 then table.remove(resolved) end else local currentPath = "/" .. table.concat(resolved, "/") local disk, p = resolvePath(currentPath .. "/" .. comp) if isSymlink(disk, p) then symlinkDepth = symlinkDepth + 1 if symlinkDepth > SYMLINK_MAX_DEPTH then error("Too many levels of symbolic links: " .. path) end local target = readSymlink(disk, p) if not target then resolved[#resolved+1] = comp else if target:sub(1,1) == "/" then resolved = splitPath(normalizePath(target)) else local base = resolved resolved = {} for _, seg in ipairs(base) do resolved[#resolved+1]=seg end for seg in target:gmatch("[^/]+") do if seg == ".." then if #resolved>0 then table.remove(resolved) end elseif seg ~= "." then resolved[#resolved+1]=seg end end end local remaining = {} for j=i+1,#parts do remaining[#remaining+1]=parts[j] end parts = remaining i = 0 end else resolved[#resolved+1]=comp end end i=i+1 end local finalPath="/"..table.concat(resolved,"/") local disk,diskPath=resolvePath(finalPath) return disk,diskPath end -- PUBLIC API: MOUNTING function fs.mount(disk, mountPoint) if kernel.uid ~= 0 then error("Permission Denied") end mounts[mountPoint]=disk end function fs.unmount(mountPoint) if kernel.uid ~= 0 then error("Permission Denied") end mounts[mountPoint]=nil end function fs.virtDisk(diskObj) if kernel.uid ~= 0 then error("Permission Denied") end if disks[diskObj.address] then error("Disk exists") end disks[diskObj.address]=diskObj end function fs.eject(addr) if kernel.uid ~= 0 then error("Permission Denied") end disks[addr]=nil end -- SYMLINK API function fs.symlink(target, linkPath) local disk, p = resolvePath(linkPath) return disk:writeAllText(p, SYMLINK_PREFIX..target..SYMLINK_SUFFIX) end function fs.isLink(path) local disk, p = resolvePath(path) return isSymlink(disk,p) end function fs.readLink(path) local disk, p = resolvePath(path) return readSymlink(disk,p) end -- FILE OPERATIONS function fs.exists(path) local disk, p = resolveSymlink(path) return disk:fileExists(p) or disk:directoryExists(p) end function fs.isFile(path) local disk, p = resolveSymlink(path) if isSymlink(disk, p) then return false end return disk:fileExists(p) end function fs.isDir(path) local disk, p = resolveSymlink(path) if isSymlink(disk, p) then return false end return disk:directoryExists(p) end function fs.list(path) local disk, p = resolveSymlink(path) return disk:list(p) end function fs.makeDir(path) local disk, p = resolveSymlink(path) return disk:makeDirectory(p) end function fs.remove(path) local disk, p = resolvePath(path) if isSymlink(disk, p) then return disk:remove(p) end local d2, p2 = resolveSymlink(path) return d2:remove(p2) end function fs.readAllText(path) local disk, p = resolveSymlink(path) return disk:readAllText(p) end function fs.writeAllText(path, text) local disk, p = resolveSymlink(path) return disk:writeAllText(p, text) end function fs.appendAllText(path, text) local disk, p = resolveSymlink(path) return disk:appendAllText(p, text) end function fs.getSize(path) local disk, p = resolveSymlink(path) return disk:getSize(p) end -- INIT for _,v in kernel.initdisks.list() do fs.virtDisk(v) end kernel.fs=fs kernel.chache.preload.fs=fs kernel.chache.preload.filesystem=fs