local fs=require("filesystem") local serialization=require("serialization") local B={} B.CFG="/etc/yaru-backup.cfg" function B.config() local cfg={src="/home",dst=nil,keep=3} local f=io.open(B.CFG) if f then local ok,t=pcall(serialization.unserialize,f:read("*a")) f:close() if ok and type(t)=="table"then for k,v in pairs(t)do cfg[k]=v end end end return cfg end function B.save(cfg) local f=io.open(B.CFG,"w") if f then f:write(serialization.serialize(cfg));f:close()end end function B.snapshots(dst) local r={} local dir=fs.concat(dst or"","Backups") if dst and fs.isDirectory(dir)then for n in fs.list(dir)do if n:sub(-1)=="/"then r[#r+1]=n:sub(1,-2)end end end table.sort(r,function(a,b)return a>b end) return r end local function walk(src,fn) for name in fs.list(src)do local p=fs.concat(src,name) if name:sub(-1)=="/"then if not fs.isLink(p)then walk(p,fn)end else fn(p)end end end function B.run(cfg,progress,stamp) if not cfg.dst or not fs.isDirectory(cfg.dst)then return nil,"no destination drive"end local dst=fs.canonical(cfg.dst) local src=fs.canonical(cfg.src) if(dst.."/"):sub(1,#src+1)==src.."/"then return nil,"destination is inside the source folder"end local files={} walk(src,function(p)files[#files+1]=p end) local snap=fs.concat(dst,"Backups",stamp) fs.makeDirectory(snap) local bytes=0 for i,p in ipairs(files)do local rel=p:sub(#src+2) local target=fs.concat(snap,rel) fs.makeDirectory(fs.path(target)) local ok,err=fs.copy(p,target) if not ok then return nil,rel..": "..tostring(err)end bytes=bytes+(fs.size(p)or 0) if progress and i%5==0 then progress(i,#files,rel)end end local f=io.open(fs.concat(snap,".backup-info"),"w") if f then f:write(serialization.serialize({src=src,files=#files,bytes=bytes}));f:close()end local snaps=B.snapshots(dst) for i=(cfg.keep or 3)+1,#snaps do fs.remove(fs.concat(dst,"Backups",snaps[i]))end return#files,bytes end function B.info(dst,name) local f=io.open(fs.concat(dst,"Backups",name,".backup-info")) if not f then return{}end local ok,t=pcall(serialization.unserialize,f:read("*a")) f:close() return ok and t or{} end function B.restore(dst,name,into,progress) local snap=fs.concat(dst,"Backups",name) local n=0 local files={} walk(snap,function(p)if fs.name(p)~=".backup-info"then files[#files+1]=p end end) for i,p in ipairs(files)do local target=fs.concat(into,p:sub(#snap+2)) fs.makeDirectory(fs.path(target)) fs.copy(p,target) n=n+1 if progress and i%5==0 then progress(i,#files,p)end end return n end return B