--: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)