local source, output, outside = arg[1], arg[2], arg[3]
local function replace_literal(text, needle, with)
local parts, from = {}, 1
while true do
local start, stop = string.find(text, needle, from, true)
if not start then
break
end
parts[#parts + 1] = string.sub(text, from, start - 1)
parts[#parts + 1] = with
from = stop + 1
end
parts[#parts + 1] = string.sub(text, from)
return table.concat(parts)
end
local function stable(message)
message = string.match(message, "^[^\n]*") or message
message = replace_literal(message, source, "<source>")
message = replace_literal(message, output, "<output>")
return replace_literal(message, outside, "<outside>")
end
local function expect_refusal(label, body)
local ok, err = pcall(body)
assert(not ok, label .. " was permitted, and should not have been")
print(label .. ": " .. stable(tostring(err)))
end
local notes = airsstack.path.join(source, "notes.txt")
local body = airsstack.fs.read(notes)
print("read notes.txt: " .. #body .. " bytes")
local meta = airsstack.fs.stat(notes)
print("stat notes.txt: kind=" .. meta.kind .. " size=" .. meta.size)
airsstack.fs.write(airsstack.path.join(output, "notes.txt"), body)
print("copied to <output>/notes.txt")
airsstack.fs.atomic_write(
airsstack.path.join(output, "index.json"),
airsstack.json.encode_pretty({ source = "notes.txt", bytes = #body })
)
print("atomic_write wrote <output>/index.json")
local claim = airsstack.path.join(output, "lock")
print("create_exclusive, first call: " .. tostring(airsstack.fs.create_exclusive(claim, "held")))
print("create_exclusive, second call: " .. tostring(airsstack.fs.create_exclusive(claim, "held")))
print("<source> holds: " .. table.concat(airsstack.fs.list(source), ", "))
expect_refusal("writing into the read root", function()
airsstack.fs.write(notes, "overwritten")
end)
expect_refusal("reading back from the write root", function()
airsstack.fs.read(airsstack.path.join(output, "notes.txt"))
end)
expect_refusal("listing the write root", function()
airsstack.fs.list(output)
end)
expect_refusal("reading outside every root", function()
airsstack.fs.read(airsstack.path.join(outside, "secret.txt"))
end)