163 lines
4.7 KiB
Plaintext
163 lines
4.7 KiB
Plaintext
--: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)
|