Hyperion v1.2.0
This commit is contained in:
162
Src/Hyperion-bash/bin/chattr
Normal file
162
Src/Hyperion-bash/bin/chattr
Normal file
@@ -0,0 +1,162 @@
|
||||
--:Minify:--
|
||||
-- supports +i/-i (immutable) stored in the file's cmeta/xattr field
|
||||
local name = syscall.getTask(syscall.getpid()).name
|
||||
local cloptions = { R = false, help = false }
|
||||
local args = {}
|
||||
local modeStr = nil
|
||||
|
||||
for _, v in ipairs({ ... }) do
|
||||
if v:sub(1, 2) == "--" then
|
||||
local opt = v:sub(3)
|
||||
if cloptions[opt] == nil then
|
||||
print(name .. ": unrecognized option '" .. v .. "'")
|
||||
print("try '" .. name .. " --help' for more information.")
|
||||
syscall.exit(1); return
|
||||
end
|
||||
cloptions[opt] = true
|
||||
elseif v:sub(1, 1) == "-" and not v:match("^%-[%+%-]") then
|
||||
local isFlag = true
|
||||
for i = 2, #v do
|
||||
local c = v:sub(i,i)
|
||||
if cloptions[c] ~= nil then
|
||||
cloptions[c] = true
|
||||
else
|
||||
isFlag = false; break
|
||||
end
|
||||
end
|
||||
if not isFlag then
|
||||
modeStr = v
|
||||
end
|
||||
elseif v:sub(1,1) == "+" or (v:sub(1,1) == "-" and v:match("^%-[a-zA-Z]")) then
|
||||
modeStr = v
|
||||
else
|
||||
table.insert(args, v)
|
||||
end
|
||||
end
|
||||
|
||||
if cloptions.help then
|
||||
print("Usage: " .. name .. " [OPTION]... +-= ATTRS FILE...")
|
||||
print("Change file attributes on a filesystem.")
|
||||
print("")
|
||||
print("Attributes:")
|
||||
print(" i immutable: file cannot be modified, renamed, or deleted")
|
||||
print(" a append-only: file can only be appended to")
|
||||
print("")
|
||||
print("Operators: +attr add, -attr remove")
|
||||
print("Example: " .. name .. " +i /etc/passwd")
|
||||
print("")
|
||||
print("Options:")
|
||||
print(" -R operate on files and directories recursively")
|
||||
print(" --help display this help and exit")
|
||||
return
|
||||
end
|
||||
|
||||
if not modeStr or #args < 1 then
|
||||
print(name .. ": missing operand")
|
||||
print("try '" .. name .. " --help' for more information.")
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
local op = modeStr:sub(1, 1)
|
||||
local attrs = modeStr:sub(2)
|
||||
|
||||
if op ~= "+" and op ~= "-" then
|
||||
print(name .. ": invalid operator '" .. op .. "' (use + or -)")
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
local function getXattr(path)
|
||||
local stat = pcall(function() return syscall.stat(path) end) and syscall.stat(path)
|
||||
if stat then return stat.xattr or "" end
|
||||
return ""
|
||||
end
|
||||
|
||||
local IMMUTABLE_TAG = "|i"
|
||||
local APPENDONLY_TAG = "|a"
|
||||
|
||||
local function attrTag(c)
|
||||
if c == "i" then return IMMUTABLE_TAG
|
||||
elseif c == "a" then return APPENDONLY_TAG
|
||||
else return nil end
|
||||
end
|
||||
|
||||
local function chattrPath(path)
|
||||
local stat = syscall.stat(path)
|
||||
if not stat then
|
||||
print(name .. ": cannot stat '" .. path .. "': No such file or directory")
|
||||
return false
|
||||
end
|
||||
if stat.etype == 0x01 then
|
||||
return true
|
||||
end
|
||||
|
||||
if not syscall.setxattr then
|
||||
print(name .. ": kernel does not expose setxattr syscall; cannot modify attributes")
|
||||
syscall.exit(1); return false
|
||||
end
|
||||
|
||||
local xattr = stat.xattr or ""
|
||||
|
||||
for i = 1, #attrs do
|
||||
local c = attrs:sub(i, i)
|
||||
local tag = attrTag(c)
|
||||
if not tag then
|
||||
print(name .. ": unsupported attribute '" .. c .. "'")
|
||||
syscall.exit(1); return false
|
||||
end
|
||||
local hasTag = xattr:find(tag, 1, true)
|
||||
if op == "+" and not hasTag then
|
||||
xattr = xattr .. tag
|
||||
elseif op == "-" and hasTag then
|
||||
xattr = xattr:gsub(tag:gsub("|", "%%|"), "")
|
||||
end
|
||||
end
|
||||
|
||||
local ok, err = pcall(syscall.setxattr, path, xattr)
|
||||
if not ok then
|
||||
print(name .. ": cannot set attributes on '" .. path .. "': " .. tostring(err))
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function chattrRecursive(path)
|
||||
if not chattrPath(path) then return end
|
||||
if syscall.type(path) == "directory" then
|
||||
local ok, list = pcall(syscall.listdir, path)
|
||||
if ok then
|
||||
for _, entry in ipairs(list) do
|
||||
local child = path
|
||||
if child:sub(-1) ~= "/" then child = child .. "/" end
|
||||
chattrRecursive(child .. entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local cwd = syscall.getcwd()
|
||||
local function absPath(p)
|
||||
if p:sub(1,1) ~= "/" then p = cwd .. "/" .. p end
|
||||
return p
|
||||
end
|
||||
|
||||
if not syscall.setxattr then
|
||||
print(name .. ": kernel does not expose setxattr; attributes cannot be persisted")
|
||||
print(name .. ": add sys[\"setxattr\"] = vfs.setxattr to 10_vfs.kmod to enable this")
|
||||
syscall.exit(1); return
|
||||
end
|
||||
|
||||
local exitCode = 0
|
||||
for i = 1, #args do
|
||||
local path = absPath(args[i])
|
||||
if not syscall.exists(path) then
|
||||
print(name .. ": cannot access '" .. args[i] .. "': No such file or directory")
|
||||
exitCode = 1
|
||||
elseif cloptions.R then
|
||||
chattrRecursive(path)
|
||||
else
|
||||
if not chattrPath(path) then exitCode = 1 end
|
||||
end
|
||||
end
|
||||
|
||||
syscall.exit(exitCode)
|
||||
Reference in New Issue
Block a user