forked from Hyperion/HyperionOS
278 lines
7.4 KiB
Plaintext
278 lines
7.4 KiB
Plaintext
local kernel = ...
|
|
local vfs = {}
|
|
vfs.mounts = { ["$"] = "/" }
|
|
local disks = kernel.disks
|
|
|
|
-- Path handling
|
|
local function normalizePath(path)
|
|
local parts = {}
|
|
for part in path:gmatch("[^/]+") do
|
|
if part == ".." then
|
|
if #parts > 0 then table.remove(parts) end
|
|
elseif part ~= "." and part ~= "" then
|
|
table.insert(parts, part)
|
|
end
|
|
end
|
|
return "/" .. table.concat(parts, "/") .. (path:sub(-1) == "/" and "/" or "")
|
|
end
|
|
|
|
local function resolvePath(path)
|
|
local task = kernel.currentTask
|
|
local cwd = task.cwd or "/"
|
|
|
|
if path:sub(1,1) ~= "/" then
|
|
path = cwd .. path
|
|
end
|
|
|
|
path = normalizePath(path)
|
|
|
|
local mountPoint = "/"
|
|
local mountId = "$"
|
|
for k,v in pairs(vfs.mounts) do
|
|
if path:sub(1,#v) == v and #v > #mountPoint then
|
|
mountPoint = v
|
|
mountId = k
|
|
end
|
|
end
|
|
|
|
local diskPath = path:sub(#mountPoint + 1)
|
|
return disks[mountId], diskPath
|
|
end
|
|
|
|
-- File object creation
|
|
local function newFileObject(disk, handle, mode, path)
|
|
return {
|
|
disk = disk,
|
|
handle = handle,
|
|
mode = mode,
|
|
path = path,
|
|
refcount = 1
|
|
}
|
|
end
|
|
|
|
local function allocFD(task)
|
|
local fd = 0
|
|
while task.fd[fd] do fd = fd + 1 end
|
|
return fd
|
|
end
|
|
|
|
local function allocFD(task)
|
|
-- enforce per-task limit
|
|
local count = 0
|
|
for _ in pairs(task.fd) do count = count + 1 end
|
|
if count >= kernel.config.maxFilesPerTask then
|
|
error("EMFILE") -- Too many open files in this task
|
|
end
|
|
|
|
-- find first free FD
|
|
local fd = 0
|
|
while task.fd[fd] do fd = fd + 1 end
|
|
return fd
|
|
end
|
|
|
|
local function checkSystemLimit()
|
|
-- enforce system-wide limit
|
|
local total = 0
|
|
for _, task in pairs(kernel.tasks or {}) do
|
|
for _ in pairs(task.fd) do total = total + 1 end
|
|
end
|
|
if total >= kernel.config.maxOpenFiles then
|
|
error("ENFILE") -- Too many open files in the system
|
|
end
|
|
end
|
|
|
|
-- VFS syscalls
|
|
function vfs.open(path, mode)
|
|
local task = kernel.currentTask
|
|
|
|
-- check limits
|
|
checkSystemLimit()
|
|
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then
|
|
error("No disk mounted for path '"..path.."'")
|
|
end
|
|
|
|
local handle = disk:open(diskPath, mode)
|
|
if not handle then return nil end
|
|
|
|
local file = newFileObject(disk.address, handle, mode, path)
|
|
local fd = allocFD(task)
|
|
task.fd[fd] = file
|
|
|
|
return fd
|
|
end
|
|
|
|
function vfs.close(fd)
|
|
local task = kernel.currentTask
|
|
local file = task.fd[fd]
|
|
if not file then error("EBADF") end
|
|
|
|
task.fd[fd] = nil
|
|
file.refcount = file.refcount - 1
|
|
if file.refcount == 0 then
|
|
file.handle.close()
|
|
end
|
|
return true
|
|
end
|
|
|
|
function vfs.read(fd, count)
|
|
local file = kernel.currentTask.fd[fd]
|
|
if not file then error("EBADF") end
|
|
if not file.mode:find("r") then error("File not open for reading") end
|
|
return file.handle.read(count)
|
|
end
|
|
|
|
function vfs.write(fd, data)
|
|
local file = kernel.currentTask.fd[fd]
|
|
if not file then error("EBADF") end
|
|
if not file.mode:find("w") then error("File not open for writing") end
|
|
return file.handle.write(data)
|
|
end
|
|
|
|
function vfs.whereis(fd)
|
|
local file = kernel.currentTask.fd[fd]
|
|
if not file then error("EBADF") end
|
|
return file.path
|
|
end
|
|
|
|
-- Filesystem operations
|
|
function vfs.mkdir(path)
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then error("No disk mounted") end
|
|
return disk:makeDirectory(diskPath)
|
|
end
|
|
|
|
function vfs.remove(path)
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then error("No disk mounted") end
|
|
return disk:remove(diskPath)
|
|
end
|
|
|
|
function vfs.attributes(path)
|
|
if type(path) == "number" then
|
|
local file = kernel.currentTask.fd[path]
|
|
if not file then error("EBADF") end
|
|
return disks[file.disk]:attributes(file.path)
|
|
end
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then error("No disk mounted") end
|
|
return disk:attributes(diskPath)
|
|
end
|
|
|
|
function vfs.list(path)
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then error("No disk mounted") end
|
|
return disk:list(diskPath)
|
|
end
|
|
|
|
function vfs.exists(path)
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then return false end
|
|
return disk:directoryExists(diskPath) or disk:fileExists(diskPath)
|
|
end
|
|
|
|
function vfs.type(path)
|
|
if type(path) == "number" then
|
|
local file = kernel.currentTask.fd[path]
|
|
if not file then error("EBADF") end
|
|
return disks[file.disk]:type(file.path)
|
|
end
|
|
local disk, diskPath = resolvePath(path)
|
|
if not disk then error("No disk mounted") end
|
|
return disk:type(diskPath)
|
|
end
|
|
|
|
-- CWD
|
|
function vfs.getcwd()
|
|
return kernel.currentTask.cwd
|
|
end
|
|
|
|
function vfs.setcwd(path)
|
|
if path:sub(-1) ~= "/" then path = path .. "/" end
|
|
if path:sub(1,1) ~= "/" then path = "/" .. path end
|
|
kernel.currentTask.cwd = normalizePath(path)
|
|
end
|
|
|
|
-- Mounting
|
|
function vfs.mount(diskId, path)
|
|
if kernel.uid ~= 0 then error("Permission denied") end
|
|
if not disks[diskId] then error("Unknown disk '"..diskId.."'") end
|
|
if path:sub(-1) ~= "/" then path = path .. "/" end
|
|
|
|
for _,v in pairs(vfs.mounts) do
|
|
if v == path then error("Mount point already used") end
|
|
end
|
|
vfs.mounts[diskId] = path
|
|
end
|
|
|
|
function vfs.unmount(path)
|
|
if kernel.uid ~= 0 then error("Permission denied") end
|
|
for k,v in pairs(vfs.mounts) do
|
|
if v == path then
|
|
vfs.mounts[k] = nil
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function vfs.getMounts()
|
|
return vfs.mounts
|
|
end
|
|
|
|
function vfs.virtdisk(obj)
|
|
kernel.disks[obj.address] = obj
|
|
kernel.log("Registered virtual disk at "..obj.address)
|
|
end
|
|
|
|
-- Redirect file operations to VFS
|
|
function vfs.dup(oldfd)
|
|
local task = kernel.currentTask
|
|
local file = task.fd[oldfd]
|
|
if not file then error("EBADF") end
|
|
|
|
local newfd = allocFD(task)
|
|
task.fd[newfd] = file
|
|
file.refcount = file.refcount + 1
|
|
return newfd
|
|
end
|
|
|
|
function vfs.dup2(oldfd, newfd)
|
|
local task = kernel.currentTask
|
|
local file = task.fd[oldfd]
|
|
if not file then error("EBADF") end
|
|
|
|
if oldfd == newfd then return newfd end
|
|
|
|
if task.fd[newfd] then
|
|
vfs.close(newfd)
|
|
end
|
|
|
|
task.fd[newfd] = file
|
|
file.refcount = file.refcount + 1
|
|
return newfd
|
|
end
|
|
|
|
-- Syscall registration
|
|
kernel.vfs = vfs
|
|
kernel.syscalls["VFS_open"] = vfs.open
|
|
kernel.syscalls["VFS_read"] = vfs.read
|
|
kernel.syscalls["VFS_write"] = vfs.write
|
|
kernel.syscalls["VFS_close"] = vfs.close
|
|
kernel.syscalls["VFS_list"] = vfs.list
|
|
kernel.syscalls["VFS_type"] = vfs.type
|
|
kernel.syscalls["VFS_attributes"] = vfs.attributes
|
|
kernel.syscalls["VFS_mkdir"] = vfs.mkdir
|
|
kernel.syscalls["VFS_remove"] = vfs.remove
|
|
kernel.syscalls["VFS_exists"] = vfs.exists
|
|
kernel.syscalls["VFS_mount"] = vfs.mount
|
|
kernel.syscalls["VFS_unmount"] = vfs.unmount
|
|
kernel.syscalls["VFS_getcwd"] = vfs.getcwd
|
|
kernel.syscalls["VFS_setcwd"] = vfs.setcwd
|
|
kernel.syscalls["VFS_whereis"] = vfs.whereis
|
|
kernel.syscalls["VFS_dup"] = vfs.dup
|
|
kernel.syscalls["VFS_dup2"] = vfs.dup2
|
|
|
|
kernel.log("VFS module loaded")
|