do _G.fs = {} local dfsattr = {read = "*", write = "*"} local fsattr = {} local fT,hT = {},{["_c"]=0} local function parseacl(acl) acl = acl or "" local tacl = {} if acl == "*" then setmetatable(tacl,{__index = function() return true end}) else for n in acl:gmatch("[^,]+") do tacl[n] = true log(n) end end return tacl end local function canread(fn) fn = fs.canonical(fn) if os.getuid() == "nobody" then return false end if os.getuid() ~= "superuser" and fsattr[fn] then if not parseacl(fsattr[fn].read or "")[os.getuid()] then return false end end return true end local function canwrite(fn) fn = fs.canonical(fn) if os.getuid() == "nobody" then return false end if os.getuid() ~= "superuser" and fsattr[fn] then if not parseacl(fsattr[fn].write)[os.getuid()] then return false end end return true end fs.parseacl = parseacl fs.canread = canread fs.canwrite = canwrite local function getattr(fn,k) fn = fs.canonical(fn) if not canread(fn) then return false end if fsattr[fn] then return fsattr[fn][k] else return dfsattr[k] end end local function setattr(fn,k,v) fn = fs.canonical(fn) if not canwrite(fn) then return false end if k:find("\t") or v:find("\t") then return false end log(parseacl(getattr(fn,"write"))[os.getuid()]) if not fsattr[fn] then fsattr[fn] = {} setmetatable(fsattr[fn],{__index=dfsattr}) end fsattr[fn][k] = tostring(v) end function fs.mount(mp,pr) fT[mp] = pr end function fs.simplify(p) local pt,npt,rp = {},{},"" for P in p:gmatch("[^/]+") do pt[#pt+1] = P end for k,v in pairs(pt) do if v == ".." then npt[#npt] = nil elseif v ~= "." then npt[#npt+1] = v end end for k,v in pairs(npt) do rp=rp.."/"..v end if p:sub(1,1) ~= "/" then rp = rp:sub(2) end return rp end function fs.resolve(p) if p:sub(1,1) ~= "/" then -- absolute/relative path p=(os.getenv("PWD") or "").."/"..p end p=fs.simplify(p) local pt,spt = {},"" for P in p:gmatch("[^/]+") do pt[#pt+1] = P end for i = 2, #pt do spt=spt.."/"..pt[i] end return pt, pt[1], spt end function fs.canonical(p) local _,d,rp = fs.resolve(p) return fs.simplify("/"..d.."/"..rp) end function fs.exec(fc,m,...) return fT[fc][m](...) end function fs.open(p,m) if m:sub(1,1) == "r" then if not canread(p) then return false end elseif m:sub(1,1) == "w" or m:sub(1,1) == "a" then if not canwrite(p) then return false end end local _,d,p = fs.resolve(p) local d = fT[d] if d then local f,C=d.open(p,m),hT._c if f then hT._c = C + 1 hT[C] = {d,f} return C end return false end end function fs.close(h) if hT[h] then hT[h][1].close(hT[h][2]) end return false end function fs.read(h,n) if hT[h] then return hT[h][1].read(hT[h][2],n) end return false end function fs.readall(f) local s="" while true do c=fs.read(f,2048) if not c then break end s=s..c end return s end function fs.write(h,d) if hT[h] then return hT[h][1].write(hT[h][2],d) end return false end function fs.list(s) s=s or "" local _,d,p = fs.resolve(s) if not d then local kt = {} for k,v in pairs(fT) do kt[#kt+1] = k end return kt end return fT[d].list(p or "/") end function fs.mkdir(s) local _,d,p = fs.resolve(s) return fT[d].makeDirectory(p or "/") end function fs.rm(s) local _,d,p = fs.resolve(s) return fT[d].remove(p) end function fs.exists(s) local _,d,p = fs.resolve(s) if d then return fT[d].exists(p) end end function fs.isdir(s) local _,d,p = fs.resolve(s) return fT[d].isDirectory(p) end local function flushattr() local f = fs.open("/boot/sys/fsattr.dat","wb") if f then for k,v in pairs(fsattr) do fs.write(f,k.."\t") for l,m in pairs(v) do fs.write(f,"\t"..l.."="..m) end fs.write(f,"\n") end fs.close(f) return true end return false end local function readattr() local f=fs.open("/boot/sys/fsattr.dat","rb") if not f then return false end local C=fs.readall(f) fs.close(f) for line in C:gmatch("[^\n]+") do local ifn = true local fn = "" for kv in line:gmatch("[^\t]+") do if ifn then fn = kv ifn = false else local k,v = kv:match("(.+)%=(.+)") setattr(fn,k,v) end end end end spawn("read fs attributes",readattr) function fs.getattr(fn,k) if fs.exists(fn) then return getattr(fn,k) end return false end function fs.setattr(fn,k,v) if fs.exists(fn) then local res={setattr(fn,k,v)} flushattr() return table.unpack(res) end return false end end function fs.cd(p) if p:sub(1,1) ~= "/" then p=(os.getenv("PWD") or "").."/"..p end p=fs.simplify(p) if fs.exists(p) and fs.isdir(p) then os.setenv("PWD",p) else error("non-existent/not a dir") end end function fs.cp(s,d) local df = fs.open(d,"wb") local sf = fs.open(s,"rb") if df and sf then fs.write(df,fs.readall(sf)) fs.close(df) fs.close(sf) return true end return false end function fs.mv(s,d) fs.cp(s,d) fs.rm(s) end function loadfile(fn) local f=io.open(fn,"rb") if not f then return false, "cannot read file" end local S=f:read("*a") f:close() return load(S,"=("..fn..")","bt",os.genenv()) end function run(fn,...) local lf = loadfile(fn) if not lf then return false, "cannot load file" end local r = {pcall(lf,...)} if r[1] == true then table.remove(r,1) end print(table.unpack(r)) end function srun(fn,...) local lf = loadfile(fn) if not lf then return false, "cannot load file" end spawn(fn,log(pcall(lf,...))) end