/home/user owned by user, user starts in cwd /home/user

This commit is contained in:
2026-02-23 23:05:13 -06:00
parent b015d5880a
commit 457eddd5b2
11 changed files with 199 additions and 46 deletions

View File

@@ -64,12 +64,18 @@ local newGid = resolveGid(groupStr)
local function chgrpPath(path)
local stat = syscall.stat(path)
if not stat then
print(name .. ": cannot stat '" .. path .. "': No such file or directory")
print(name .. ": cannot stat '" .. path .. "': no such file or directory")
return false
end
local ok, err = pcall(syscall.chown, path, stat.owner, newGid)
if not ok then
print(name .. ": cannot change group of '" .. path .. "': " .. tostring(err))
local msg = tostring(err)
if msg:find("EPERM") or msg:find("EACCES") then
msg = "operation not permitted (must be root)"
elseif msg:find("ENOENT") then
msg = "no such file or directory"
end
print(name .. ": cannot change group of '" .. path .. "': " .. msg)
return false
end
return true

View File

@@ -220,7 +220,13 @@ local function chmodPath(path)
local ok, cerr = pcall(syscall.chmod, path, newPerms)
if not ok then
print(name .. ": cannot change permissions of '" .. path .. "': " .. tostring(cerr))
local msg = tostring(cerr)
if msg:find("EACCES") or msg:find("EPERM") then
msg = "permission denied"
elseif msg:find("ENOENT") then
msg = "no such file or directory"
end
print(name .. ": cannot change permissions of '" .. path .. "': " .. msg)
return false
end
return true

View File

@@ -95,14 +95,20 @@ end
local function chownPath(path)
local stat = syscall.stat(path)
if not stat then
print(name .. ": cannot stat '" .. path .. "': No such file or directory")
print(name .. ": cannot stat '" .. path .. "': no such file or directory")
return false
end
local uid = newUid ~= nil and newUid or stat.owner
local gid = newGid ~= nil and newGid or stat.group
local ok, err = pcall(syscall.chown, path, uid, gid)
if not ok then
print(name .. ": cannot change owner of '" .. path .. "': " .. tostring(err))
local msg = tostring(err)
if msg:find("EPERM") or msg:find("EACCES") then
msg = "operation not permitted (must be root)"
elseif msg:find("ENOENT") then
msg = "no such file or directory"
end
print(name .. ": cannot change owner of '" .. path .. "': " .. msg)
return false
end
return true

View File

@@ -17,7 +17,13 @@ local commandHistory = {}
local terminate = false
syscall.setEnviron("SHELL","rtbash")
syscall.setEnviron("PATH","/bin/")
syscall.chdir("/")
local _home = syscall.getEnviron("HOME")
if _home and _home ~= "" then
local ok = pcall(syscall.chdir, _home)
if not ok then syscall.chdir("/") end
else
syscall.chdir("/")
end
local oldWD = ""
for i = 1, 16 do

View File

@@ -96,6 +96,9 @@ local function spawnShell(username, uid, shell, homedir)
end
local chdirOk, chdirErr = pcall(syscall.chdir, homedir)
if not chdirOk then
pcall(syscall.chdir, "/")
end
local ok, err = pcall(syscall.execspawn, shell, username .. ":shell")
if not ok then

View File

@@ -14,6 +14,9 @@ for i,v in pairs(kernel.processes) do
end, i)
end
if not fs.exists("/bin/startup") then
fs.mkdir("/bin/startup")
end
local files = fs.list("/bin/startup")
if not files then error("Failed to list /bin/startup") end
for i,v in ipairs(files) do

View File

@@ -164,6 +164,9 @@ local function resolveMount(normalPath)
return vfs.disks[mountId], diskPath
end
-- Expose parser for use by other modules (e.g. permissions seeder)
vfs._parseMetafile = parseMetafile
local function readMetaEntry(disk, parentDiskPath, filename)
if filename == ".meta" then error("Cannot open metafile") end
local mp
@@ -177,6 +180,15 @@ local function readMetaEntry(disk, parentDiskPath, filename)
if not ok or not f then return nil end
local raw = f.read(65535)
if f.close then f.close() end
-- Auto-upgrade: if this .meta file is not v2, rewrite it as v2 now
if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then
local upgraded = makeMetafile(parseMetafile(raw))
local wok, wf = pcall(function() return disk:open(mp, "w") end)
if wok and wf then wf.write(upgraded); if wf.close then wf.close() end end
raw = upgraded
end
local parsed = parseMetafile(raw)
return parsed[filename]
end
@@ -418,6 +430,8 @@ function vfs.open(path, mode)
if not disk then error("NODISK") end
local meta = getFileMeta(path)
local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath)
checkperms(meta, mode == "r" and "r" or "w")
local handle
@@ -426,6 +440,22 @@ function vfs.open(path, mode)
if type(handle) ~= "table" then error("ENFILE") end
end
-- If this is a newly created file, stamp it with the creating user's ownership
if isNew then
local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0
local norm = normalizePath(path)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local name = norm:match("[^/]+$")
if name then
local entry = { etype=0x00, owner=euid, group=egid,
perms=vfs.PERM.RW_R_R, cmeta="" }
pcall(writeMetaEntry, parent, name, entry, false)
meta = entry
end
end
local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath))
if mode == "r" and bit_is_set(meta.perms, 6) then
fobj.suid_owner = meta.owner
@@ -592,15 +622,33 @@ function vfs.listdir(path)
end
function vfs.mkdir(path)
local norm = normalizePath(path)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w")
local disk, diskPath = resolvePath(path)
local meta = getFileMeta(path)
checkperms(meta, "w")
disk:makeDirectory(diskPath)
-- Stamp the new directory with the creating user's ownership
local task = kernel.currentTask
local euid = (task and (task.euid or task.uid)) or kernel.uid
local egid = (task and task.gid) or 0
local name = norm:match("[^/]+$")
if name then
local entry = { etype=0x00, owner=euid, group=egid,
perms=vfs.PERM.RWX_RX, cmeta="" }
pcall(writeMetaEntry, parent, name, entry, false)
end
end
function vfs.remove(path)
local norm = resolveSymlinks(path, true)
local parent = norm:match("^(.*)/[^/]+$") or "/"
if parent == "" then parent = "/" end
local parentMeta = getFileMeta(parent)
checkperms(parentMeta, "w")
local meta = getFileMeta(path, true)
checkperms(meta, "w")
if kernel.unixSockets and kernel.unixSockets[path] then
kernel.unixSockets[path] = nil

View File

@@ -50,10 +50,10 @@ local function readonly(tbl)
__metatable = false
})
end
local origLoad = load
--local origLoad = load
kernel._U = readonly(kernel._G)
kernel.allowGlobalOverwrites = true
kernel._U._G = kernel._U
kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
--kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end
kernel.allowGlobalOverwrites = false

View File

@@ -394,6 +394,8 @@ function auth.newUser(username, password, gid, homedir, shell)
if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then
kernel.vfs.mkdir(homedir)
-- Homedir must be owned by the new user, not root
pcall(kernel.vfs.chown, homedir, uid, uid)
end
kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid))

View File

@@ -51,12 +51,106 @@ end
local REG = 0x00
if rootDisk:fileExists(".meta") then
kernel.log("Permissions already seeded, skipping.", "INFO")
else
-- All known /bin entries with their permissions
local BIN_ENTRIES = {
{"cat", REG, 0, 0, RWX_RX_RX},
{"chattr", REG, 0, 0, RWX_RX_RX},
{"chgrp", REG, 0, 0, RWX_RX_RX},
{"chmod", REG, 0, 0, RWX_RX_RX},
{"chown", REG, 0, 0, RWX_RX_RX},
{"chroot", REG, 0, 0, RWX_RX_RX},
{"clear", REG, 0, 0, RWX_RX_RX},
{"echo", REG, 0, 0, RWX_RX_RX},
{"hfetch", REG, 0, 0, RWX_RX_RX},
{"help", REG, 0, 0, RWX_RX_RX},
{"hysh", REG, 0, 0, RWX_RX_RX},
{"hyshex", REG, 0, 0, RWX_RX_RX},
{"id", REG, 0, 0, RWX_RX_RX},
{"install", REG, 0, 0, RWX_RX_RX},
{"ln", REG, 0, 0, RWX_RX_RX},
{"login", REG, 0, 0, SUID_755 },
{"loimgcreate", REG, 0, 0, RWX_RX_RX},
{"looptest", REG, 0, 0, RWX_RX_RX},
{"losetup", REG, 0, 0, RWX_RX_RX},
{"ls", REG, 0, 0, RWX_RX_RX},
{"lsusers", REG, 0, 0, RWX_RX_RX},
{"lua", REG, 0, 0, RWX_RX_RX},
{"luaold", REG, 0, 0, RWX_RX_RX},
{"micro", REG, 0, 0, RWX_RX_RX},
{"mkdir", REG, 0, 0, RWX_RX_RX},
{"mount", REG, 0, 0, RWX_RX_RX},
{"passwd", REG, 0, 0, RWX_RX_RX},
{"ps", REG, 0, 0, RWX_RX_RX},
{"pwd", REG, 0, 0, RWX_RX_RX},
{"readlink", REG, 0, 0, RWX_RX_RX},
{"sed", REG, 0, 0, RWX_RX_RX},
{"socktest", REG, 0, 0, RWX_RX_RX},
{"spm", REG, 0, 0, RWX_RX_RX},
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
{"sysdump", REG, 0, 0, RWX_RX_RX},
{"umount", REG, 0, 0, RWX_RX_RX},
{"useradd", REG, 0, 0, RWX_RX_RX},
{"userdel", REG, 0, 0, RWX_RX_RX},
{"usermod", REG, 0, 0, RWX_RX_RX},
{"whoami", REG, 0, 0, RWX_RX_RX},
{"yes", REG, 0, 0, RWX_RX_RX},
{"startup", REG, 0, 0, RWX_RX_RX},
}
-- Merge entries: always ensure all known entries exist with correct permissions.
-- This handles both fresh installs and upgrades (adds missing entries, upgrades
-- the on-disk format to v2 by rewriting).
local function mergeMeta(dir, entries)
local diskDir = dir
if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end
local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta")
-- Read existing meta (may be v1 or v2)
local existing = {}
local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end)
if rok and rf then
local raw = rf.read(65535)
if rf.close then rf.close() end
-- Parse using the VFS parser (handles v0/v1/v2)
existing = kernel.vfs and kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw) or {}
end
-- Add any missing entries (don't overwrite existing customised perms)
for _, e in ipairs(entries) do
if not existing[e[1]] then
existing[e[1]] = {
etype = e[2] or 0x00,
owner = e[3] or 0,
group = e[4] or 0,
perms = e[5] or RWX_RX_RX,
cmeta = e[6] or "",
}
end
end
-- Write back as v2
local data = string.char(META_VERSION)
for name, m in pairs(existing) do
data = data .. makeEntry(name, m.etype or 0x00, m.owner or 0, m.group or 0, m.perms or RWX_RX_RX, m.cmeta or "")
end
local ok, err = pcall(function()
local f = rootDisk:open(metaPath, "w")
f.write(data)
f.close()
end)
if not ok then
kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8)
end
end
local freshInstall = not rootDisk:fileExists(".meta")
if freshInstall then
kernel.log("Seeding filesystem permissions...", "INFO")
-- /
-- / (only on fresh install — these dirs are stable)
writeMeta("/", {
{"bin", REG, 0, 0, RWX_RX_RX},
{"boot", REG, 0, 0, RWX_RX_RX},
@@ -71,38 +165,10 @@ else
{"var", REG, 0, 0, RWX_RX_RX},
})
-- /bin
writeMeta("/bin", {
{"cat", REG, 0, 0, RWX_RX_RX},
{"clear", REG, 0, 0, RWX_RX_RX},
{"echo", REG, 0, 0, RWX_RX_RX},
{"hfetch", REG, 0, 0, RWX_RX_RX},
{"hysh", REG, 0, 0, RWX_RX_RX},
{"hyshex", REG, 0, 0, RWX_RX_RX},
{"install", REG, 0, 0, RWX_RX_RX},
{"login", REG, 0, 0, SUID_755 },
{"ls", REG, 0, 0, RWX_RX_RX},
{"lua", REG, 0, 0, RWX_RX_RX},
{"luaold", REG, 0, 0, RWX_RX_RX},
{"mkdir", REG, 0, 0, RWX_RX_RX},
{"ps", REG, 0, 0, RWX_RX_RX},
{"pwd", REG, 0, 0, RWX_RX_RX},
{"spm", REG, 0, 0, RWX_RX_RX},
{"su", REG, 0, 0, SUID_755 },
{"sudo", REG, 0, 0, SUID_755 },
{"sysdump", REG, 0, 0, RWX_RX_RX},
{"whoami", REG, 0, 0, RWX_RX_RX},
{"yes", REG, 0, 0, RWX_RX_RX},
{"startup", REG, 0, 0, RWX_RX_RX},
{"ln", REG, 0, 0, RWX_RX_RX},
{"readlink", REG, 0, 0, RWX_RX_RX},
})
writeMeta("/bin/startup", {
{"test.lua", REG, 0, 0, RWX_RX_RX},
})
-- /etc
writeMeta("/etc", {
{"passwd", REG, 0, 0, RW_R_R},
{"shadow", REG, 0, 0, RW____},
@@ -113,12 +179,10 @@ else
{"secret", REG, 0, 0, RW____},
})
-- /sbin
writeMeta("/sbin", {
{"init.lua", REG, 0, 0, RWX_RX_RX},
})
-- /boot
writeMeta("/boot", {
{"kernel.lua", REG, 0, 0, RW_R_R },
{"boot.cfg", REG, 0, 0, RW_R_R },
@@ -129,7 +193,6 @@ else
{"oc", REG, 0, 0, RWX_RX_RX},
})
-- /lib
writeMeta("/lib", {
{"sys", REG, 0, 0, RWX_RX_RX},
{"modules", REG, 0, 0, RWX_RX_RX},
@@ -141,6 +204,11 @@ else
})
kernel.log("Filesystem permissions seeded.", "INFO")
else
kernel.log("Permissions already seeded, merging /bin updates...", "INFO")
end
-- Always merge /bin — adds missing entries and upgrades format to v2
mergeMeta("/bin", BIN_ENTRIES)
kernel.log("Permission module loaded.", "INFO")

View File

@@ -0,0 +1,5 @@
local args = {...}
local kernel = args[1]
local origLoad = load
--kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end