buildahcker
Version:
Buildahcker is a node.js library to create and run commands in OCI (Open Container Initiative) container images (or docker images), based on Buildah and a hash-based cache. It also contains utilities to easily create a partitioned bootable disk image of a
148 lines (139 loc) • 50.5 kB
JavaScript
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("fs/promises"),mt=require("os"),f=require("path"),_=require("crypto"),L=require("fs"),Ke=require("util"),Ne=require("child_process"),De=require("stream"),yt=require("stream/promises"),D=(e,t)=>{const a=async i=>{let s=[...e];s=await t?.beforeRun?.(i,s)??s,await i.run(s,[...t?.buildahArgs??[],...t?.buildahArgsNoHash??[]])};return a.getCacheKey=async()=>{const i=_.createHash("sha256");return i.update(JSON.stringify(e)),i.update(JSON.stringify(t?.buildahArgs??[])),i.update(JSON.stringify(t?.extraHashData??[])),`RUN-${i.digest("base64url")}`},a},Wt=(e,{apkCache:t}={})=>{const a={};return t&&(a.buildahArgsNoHash=["--volume",`${t}:/etc/apk/cache:rw`],a.extraHashData=["--volume",":/etc/apk/cache:rw"],a.beforeRun=async()=>{await u.mkdir(t,{recursive:!0})}),D(["apk","add",...e],a)};let rt;const ze=()=>(rt||(rt=f.join(mt.homedir(),".buildahcker","cache","apk")),rt),Zt=async(e,t,a=!1)=>{const i=f.basename(t);if(i==="."||i===".."||i==="")throw new Error(`Invalid path: ${t}`);return f.join(await gt(e,f.dirname(t),a),i)},gt=async(e,t,a=!1)=>{const i=new Set,s=f.normalize(t).split(f.sep);for(let r=0;r<s.length;r++){const n=s[r];if(!n||n==="."||r<1&&n===".."&&!a){s.splice(r,1),r--;continue}else if(n===".."){if(r<1)throw new Error(`Invalid path: ${t}`);s.splice(r-1,2),r-=2;continue}const o=f.join(e,...s.slice(0,r+1));if(i.has(o))throw new Error(`Recursive link in path: ${o}`);i.add(o);try{const c=await u.readlink(o,"utf8"),l=f.normalize(c).split(f.sep);f.isAbsolute(c)?(s.splice(0,r+1,...l),r=-1):(s.splice(r,1,...l),r--)}catch{}}return f.join(e,...s)},Ot=()=>({directories:[],files:[]});class Qt{#t=Ot();#e=".";packagesMap=new Map;addLine(t){if(t.length===0){this.#t=Ot(),this.#e=".";return}if(t[1]!=":")throw new Error("Unexpected line syntax!");const a=t[0],i=t.substring(2);switch(a){case"P":{this.#t.packageName=i,this.packagesMap.set(i,this.#t);break}case"F":{this.#e=i,this.#t.directories.push(i);break}case"R":{const s=f.posix.join(this.#e,i);this.#t.files.push(s);break}}}}const Xt=async e=>{const t=await gt(e,"lib/apk/db/installed"),a=await u.open(t);try{const i=new Qt;for await(const s of a.readLines())i.addLine(s);return i}finally{await a.close()}},Yt=async e=>{try{(await u.readdir(e)).length===0&&await u.rmdir(e)}catch(t){if(t.code!=="ENOENT")throw t}},te=async(e,t)=>{try{return await u.readFile(e)}catch(a){if(a.code==="ENOENT"){const i=await t();return await u.mkdir(f.dirname(e),{recursive:!0}),await u.writeFile(e,i),i}throw a}},ee=async(e,t,a)=>(await te(e,async()=>Buffer.from(await t(),a))).toString(a),I=async e=>(e=f.resolve(e),await u.mkdir(f.dirname(e),{recursive:!0}),await u.writeFile(e,""),e),ae=async(e,t)=>typeof e=="number"?e:await new Promise((a,i)=>L.open(e,t,(s,r)=>s?i(s):a(r))),ie=async(e,t)=>typeof t=="number"?void 0:new Promise(a=>L.close(e,a)),Me=Ke.promisify(L.read),je=async(e,t,a)=>{const{buffer:i,bytesRead:s}=await Me(e,{buffer:Buffer.alloc(a),position:t});if(s!==a)throw new Error(`Could not read ${s} bytes from file`);return i},bt=e=>{const t=f.normalize(e).split(f.sep);if(t[t.length-1]===""&&t.pop(),(t[0]==="."||t[0]==="")&&t.shift(),t.length===0||t[0]==="..")throw new Error(`Unsafe path: ${e}`);return t.join(f.sep)},se=([e,t])=>[bt(e),t],Ge=(e,t)=>e<t?-1:e>t?1:0,Je=([e],[t])=>Ge(e,t),tt=e=>new Map(Object.entries(e).map(se).sort(Je)),Et=async e=>{const t=_.createHash("sha256");for(const[a,i]of e){const s=await i.getHash();t.update(`${a.length},${s.length},`),t.update(a),t.update(s)}return t.digest()},qe=e=>(e&u.constants.S_IFMT)===u.constants.S_IFLNK;class vt{async getHash(){const t=_.createHash("sha256"),a=await this.getAttributes();t.update(`${a.mode.toString(8)},${a.uid},${a.gid}`);const i=await this.getContentHash();return t.update(i),t.digest()}async writeTo(t){try{(await u.lstat(t)).isDirectory()||await u.rm(t)}catch(i){if(i.code!=="ENOENT")throw i}await this.writeContentTo(t);const a=await this.getAttributes();await u.lchown(t,a.uid,a.gid),qe(a.mode)||await u.chmod(t,a.mode)}}class re extends vt{#t;async getUnderlyingFile(){return this.#t||(this.#t=await this._getFile()),this.#t}async getAttributes(){return await(await this.getUnderlyingFile()).getAttributes()}async getContentHash(){return await(await this.getUnderlyingFile()).getContentHash()}async writeContentTo(t){return await(await this.getUnderlyingFile()).writeContentTo(t)}}class ne extends vt{_modeAllow=65535;_modeMandatory=0;attributes;constructor(t){super(),this.attributes={uid:0,gid:0,mode:420,...t}}async getAttributes(){return{...this.attributes,mode:this.attributes.mode&this._modeAllow|this._modeMandatory}}}class et extends ne{#t;#e;async getContent(){return this.#t||(this.#t=await this._getContent()),this.#t}async getContentHash(){return this.#e||(this.#e=await this._getContentHash()),this.#e}}class kt extends et{constructor(t){super(t),this._modeAllow=~u.constants.S_IFMT,this._modeMandatory=u.constants.S_IFREG}async _getContentHash(){const t=_.createHash("sha256");return t.update(await this.getContent()),t.digest()}async writeContentTo(t){await u.writeFile(t,await this.getContent())}}class $t extends et{constructor(t){super(t),this._modeAllow=0,this._modeMandatory=u.constants.S_IFLNK|511}async _getContentHash(){const t=_.createHash("sha256");return t.update(await this.getContent()),t.digest()}async writeContentTo(t){await u.symlink(await this.getContent(),t)}}class St extends et{constructor(t){super({mode:493,...t}),this._modeAllow=~u.constants.S_IFMT,this._modeMandatory=u.constants.S_IFDIR}async _getContent(){return tt(await this._getDirectoryContent())}async _getContentHash(){return await Et(await this.getContent())}async writeContentTo(t){try{await u.mkdir(t)}catch(a){if(a.code!=="EEXIST")throw a}for(const[a,i]of await this.getContent())await i.writeTo(f.join(t,a))}}class J extends re{constructor(t,a){super(),this.sourceFilePath=t,this.options=a,this.sourceFilePath=f.resolve(t)}async _getFile(){const t=this.sourceFilePath,a=await u.lstat(t),i={uid:a.uid,gid:a.gid,mode:a.mode,...this.options?.overrideAttributes};if(a.isDirectory())return new _t(t,i,this.options);if(a.isFile())return new j(t,i);if(a.isSymbolicLink())return new oe(t,i);throw new Error(`Unsupported file type ${a.mode.toString(8)}`)}}class _t extends St{constructor(t,a,i){super(a),this.sourceFilePath=t,this.options=i,this.sourceFilePath=f.resolve(t)}async _getDirectoryContent(){const t=this.sourceFilePath,a={},i=await u.readdir(t);for(const s of i)a[s]=new J(f.join(t,s),this.options);return a}}class j extends kt{constructor(t,a){super(a),this.sourceFilePath=t,this.sourceFilePath=f.resolve(t)}async _getContent(){return await u.readFile(this.sourceFilePath)}}class oe extends $t{constructor(t,a){super(a),this.sourceFilePath=t,this.sourceFilePath=f.resolve(t)}async _getContent(){return await u.readlink(this.sourceFilePath)}}const ce=async(e,t)=>{for(const[a,i]of e){const s=await t.resolve(a);await u.mkdir(f.dirname(s),{recursive:!0}),await i.writeTo(s)}},Ve=e=>{const t=new _t(e,{uid:0,gid:0,mode:493}),a=async i=>{await ce(await t.getContent(),i)};return a.getCacheKey=async()=>`ADD-ROOT-DIRECTORY-${(await t.getContentHash()).toString("base64url")}`,a},Ft=e=>{const t=tt(e),a=async i=>{await ce(t,i)};return a.getCacheKey=async()=>`ADD-FILES-${(await Et(t)).toString("base64url")}`,a},Rt=e=>{e=e.map(bt).sort();const t=async a=>{const i=await a.mount();for(const s of e){const r=await Zt(i,s);await u.rm(r,{force:!0,recursive:!0})}};return t.getCacheKey=async()=>{const a=_.createHash("sha256");return a.update(JSON.stringify(e)),`RM-FILES-${a.digest("base64url")}`},t},ue=(e,t)=>{e=e.sort();const a=async i=>{const s=await i.mount(),r=await Xt(s),n=[],o=[];for(const c of e){const l=r.packagesMap.get(c);if(!l){t?.write(`Package not installed: ${c}
`);continue}n.push(...l.files),o.push(...l.directories)}for(const c of n){const l=await i.resolveParent(c);t?.write(`rm ${c}
`),await u.rm(l,{force:!0})}o.sort().reverse();for(const c of o){const l=await i.resolveParent(c);await Yt(l)}};return a.getCacheKey=async()=>{const i=_.createHash("sha256");return i.update(JSON.stringify(e)),`APK-REMOVE-${i.digest("base64url")}`},a},We=(e=[],t)=>[ue(["apk-tools",...e],t),Rt(["var/lib/apk","lib/apk","etc/apk","usr/share/apk","var/cache/apk"])];class ft extends De.Writable{#t=[];promise;constructor(){super(),this.promise=new Promise(t=>{this._final=t}).then(()=>{const t=Buffer.concat(this.#t);return this.#t=[],t})}_write(t,a,i){this.#t.push(Buffer.isBuffer(t)?t:Buffer.from(t,a)),i()}}class le extends Error{constructor(t,a,i,s){super(`Command failed: ${t.join(" ")}
${i?.toString("utf8")??""}
${s?.toString("utf8")??""}`),this.command=t,this.exitCode=a,this.stdout=i,this.stderr=s}}const T=async(e,{logger:t}={},a={})=>{const i=Ne.spawn(e[0],e.slice(1),{...a,stdio:"pipe"});t?.write(`[${i.pid}]$ ${e.join(" ")}
`),t&&(i.stdout.pipe(t,{end:!1}),i.stderr.pipe(t,{end:!1}));const s=new ft;i.stdout.pipe(s);const r=new ft;i.stderr.pipe(r),await new Promise(c=>i.on("exit",c)),t?.write(`[${i.pid}] Exit code: ${i.exitCode}
`);const n=await s.promise,o=await r.promise;if(i.exitCode!==0)throw new le(e,i.exitCode,n,o);return{stdout:n,stderr:o}};class q{constructor(t,a){this.options=a,this.#t=t}#t=null;#e=null;static async from(t,a){const s=(await T(["buildah","from",t],a)).stdout.toString("utf8").trim()||null;if(!s)throw new Error(`Failed to create a container from ${t}.`);return new q(s,a)}get name(){const t=this.#t;if(!t)throw new Error("The container has been destroyed!");return t}get mountPath(){const t=this.#e;if(!t)throw new Error("The container is not mounted!");return t}async mount(){let t=this.#e;if(!t){const a=this.name;if(t=(await T(["buildah","mount",a],this.options)).stdout.toString("utf8").trim()||null,!t)throw new Error(`Could not mount container ${a}`);this.#e=t}return t}toPathInContainer(t){const a=f.relative(this.mountPath,t).split(f.sep);if(a[0]==="..")throw new Error("Path is not in container");return a.unshift(""),a.join(f.sep)}async tempFolder(){const t=await this.resolve("tmp"),a=await u.mkdtemp(f.join(t,"buildahcker-"));return{pathInHost:a,pathInContainer:this.toPathInContainer(a),remove:async()=>{await u.rm(a,{recursive:!0,force:!0})}}}async resolve(t,a){return await this.mount(),await gt(this.mountPath,t,a)}async resolveParent(t,a){return await this.mount(),await Zt(this.mountPath,t,a)}async remove(){const t=this.#t;t&&(this.#t=null,this.#e=null,await T(["buildah","rm",t],this.options))}async run(t,a=[]){return await T(["buildah","run",...a,"--",this.name,...t],this.options)}async commit({timestamp:t}={}){return(await T(["buildah","commit",...t!=null?["--timestamp",`${Math.round(t/1e3)}`]:[],this.name],this.options)).stdout.toString("utf8").trim()}async executeStep(t){if(Array.isArray(t))for(const a of t)await this.executeStep(a);else await t(this)}}const It=async(e,t,a)=>{const i=await q.from(e,a);try{return await t(i)}finally{await i.remove()}},z=async(e,t,a)=>typeof e=="string"?await It(e,t,a):await t(e),he=async({source:e,command:t,buildahRunOptions:a,...i})=>await z(e,async s=>await s.run(t,a),i),Ze=async(e,t,a)=>{const i=await T(["buildah","inspect",...t?["--type",t]:[],"--",e],a);return JSON.parse(i.stdout.toString("utf8"))},wt=async(e,t)=>(await T(["buildah","inspect","--format","{{.FromImageID}}","--type","image","--",e],t)).stdout.toString("utf8").trim();class A{constructor(t,a){this.options=a,this.#t=t}#t;static async from(t,a){const i=t==="scratch"?t:await wt(t,a);if(!i)throw new Error(`Could not get information about image ${t}`);return new A(i,a)}clone(){return new A(this.#t,this.options)}get imageId(){return this.#t}async tag(t,a={}){await T(["buildah","tag",this.#t,t],a)}async#e(t,a){return await It(a??this.#t,async i=>{await i.executeStep(t);const s=await i.commit(this.options?.commitOptions);return a&&(this.#t=s),s},this.options)}async executeStep(t){const a=this.options?.containerCache;if(!a){await this.#e(t);return}if(Array.isArray(t)){for(const n of t)await this.executeStep(n);return}const i=this.#t;let s;const r=await t.getCacheKey?.();if(r&&(s=await a.getEntry(i,r),s))try{s=await wt(s,this.options)}catch{s=void 0}s||(s=await this.#e(t,i),r&&await a.setEntry(i,r,s)),this.#t=s}}const K=async({baseImage:e="alpine",apkPackages:t,commitOptions:a,logger:i,apkCache:s,containerCache:r})=>{const n=await A.from(e,{containerCache:r,commitOptions:a,logger:i});return await n.executeStep(Wt(t,{apkCache:s})),n.imageId},M=async({existingSource:e,command:t,buildahRunOptions:a,...i})=>await he({source:e??await K(i),command:t,buildahRunOptions:a,logger:i.logger});var nt={};var Ht;function Qe(){return Ht||(Ht=1,(function(e){(function(t){t(typeof DO_NOT_EXPORT_CRC>"u"?e:{})})(function(t){t.version="1.2.2";function a(){for(var h=0,F=new Array(256),d=0;d!=256;++d)h=d,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,h=h&1?-306674912^h>>>1:h>>>1,F[d]=h;return typeof Int32Array<"u"?new Int32Array(F):F}var i=a();function s(h){var F=0,d=0,E=0,k=typeof Int32Array<"u"?new Int32Array(4096):new Array(4096);for(E=0;E!=256;++E)k[E]=h[E];for(E=0;E!=256;++E)for(d=h[E],F=256+E;F<4096;F+=256)d=k[F]=d>>>8^h[d&255];var S=[];for(E=1;E!=16;++E)S[E-1]=typeof Int32Array<"u"?k.subarray(E*256,E*256+256):k.slice(E*256,E*256+256);return S}var r=s(i),n=r[0],o=r[1],c=r[2],l=r[3],p=r[4],w=r[5],y=r[6],g=r[7],v=r[8],b=r[9],m=r[10],$=r[11],C=r[12],x=r[13],B=r[14];function R(h,F){for(var d=F^-1,E=0,k=h.length;E<k;)d=d>>>8^i[(d^h.charCodeAt(E++))&255];return~d}function V(h,F){for(var d=F^-1,E=h.length-15,k=0;k<E;)d=B[h[k++]^d&255]^x[h[k++]^d>>8&255]^C[h[k++]^d>>16&255]^$[h[k++]^d>>>24]^m[h[k++]]^b[h[k++]]^v[h[k++]]^g[h[k++]]^y[h[k++]]^w[h[k++]]^p[h[k++]]^l[h[k++]]^c[h[k++]]^o[h[k++]]^n[h[k++]]^i[h[k++]];for(E+=15;k<E;)d=d>>>8^i[(d^h[k++])&255];return~d}function W(h,F){for(var d=F^-1,E=0,k=h.length,S=0,U=0;E<k;)S=h.charCodeAt(E++),S<128?d=d>>>8^i[(d^S)&255]:S<2048?(d=d>>>8^i[(d^(192|S>>6&31))&255],d=d>>>8^i[(d^(128|S&63))&255]):S>=55296&&S<57344?(S=(S&1023)+64,U=h.charCodeAt(E++)&1023,d=d>>>8^i[(d^(240|S>>8&7))&255],d=d>>>8^i[(d^(128|S>>2&63))&255],d=d>>>8^i[(d^(128|U>>6&15|(S&3)<<4))&255],d=d>>>8^i[(d^(128|U&63))&255]):(d=d>>>8^i[(d^(224|S>>12&15))&255],d=d>>>8^i[(d^(128|S>>6&63))&255],d=d>>>8^i[(d^(128|S&63))&255]);return~d}t.table=i,t.bstr=R,t.buf=V,t.str=W})})(nt)),nt}var ot=Qe();const at=async e=>{const t=e.outputFile,a=await ae(t,"r+");try{for(const i of e.partitions){const s=L.createWriteStream("",{fd:a,start:i.output.offset,autoClose:!1});if("inputFile"in i){const r=i.input?.offset??0,n=i.input?.size??(await u.stat(i.inputFile)).size-r;if(n>i.output.size)throw new Error(`Partition too small for content: ${n} > ${i.output.size}`);const o=L.createReadStream(i.inputFile,{start:r,end:r+n,autoClose:!1});try{await yt.pipeline(o,s)}finally{await o.close()}}else{if(i.inputBuffer.length>i.output.size)throw new Error(`Partition too small for content: ${i.inputBuffer.length} > ${i.output.size}`);await new Promise((r,n)=>s.write(i.inputBuffer,o=>o?n(o):r()))}}}finally{await ie(a,t)}};var O=(e=>(e.EfiSystem="C12A7328-F81F-11D2-BA4B-00A0C93EC93B",e.BiosBoot="21686148-6449-6E6F-744E-656564454649",e.LinuxData="0FC63DAF-8483-4772-8E79-3D69D8477DE4",e))(O||{});const ct=(e,t,a)=>{const i=Buffer.from(e.replaceAll("-",""),"hex");if(i.length!=16)throw new Error(`Invalid GUID: ${e}`);i.copy(t,a)},de=async e=>{const t=[],r=Math.ceil(34),n=Buffer.alloc(16384);let o=r,c=0;for(const C of e.partitions){const x=o,B=Math.ceil(C.size/512/2048)*2048;o+=B,t.push({offset:x*512,size:B*512});const R=Buffer.alloc(128);ct(C.type,R,0),ct(C.guid??_.randomUUID(),R,16),R.writeBigInt64LE(BigInt(x),32),R.writeBigInt64LE(BigInt(x+B-1),40),R.write(C.name,56,72,"utf-16le"),R.copy(n,c*128),c++}o+=r;const l=await I(e.outputFile);await u.truncate(l,o*512);const p=[],w=Buffer.alloc(512);w.write("000200",447,"hex"),w.writeUInt8(238,450),w.write("FFFFFF",447,"hex"),w.writeUint32LE(1,454),w.writeUint32LE(Math.min(4294967295,o-1),458),w.writeUint8(85,510),w.writeUint8(170,511);const y=1,g=o-1,v=2,b=o-r,m=Buffer.alloc(512);m.write("EFI PART",0,"ascii"),m.write("00000100",8,"hex"),m.writeUInt32LE(92,12),m.writeBigUInt64LE(BigInt(y),24),m.writeBigUInt64LE(BigInt(g),32),m.writeBigUInt64LE(BigInt(r),40),m.writeBigUInt64LE(BigInt(b-1),48),ct(e.guid??_.randomUUID(),m,56),m.writeBigUInt64LE(BigInt(v),72),m.writeUInt32LE(128,80),m.writeUInt32LE(128,84),m.writeInt32LE(ot.buf(n),88),m.writeInt32LE(ot.buf(m.subarray(0,92)),16);const $=Buffer.from(m);return $.writeBigUInt64LE(BigInt(g),24),$.writeBigUInt64LE(BigInt(y),32),$.writeBigUInt64LE(BigInt(b),72),$.writeUint32LE(0,16),$.writeInt32LE(ot.buf($.subarray(0,92)),16),p.push({inputBuffer:w,output:{offset:0,size:w.length}},{inputBuffer:m,output:{offset:y*512,size:m.length}},{inputBuffer:n,output:{offset:v*512,size:n.length}},{inputBuffer:n,output:{offset:b*512,size:n.length}},{inputBuffer:$,output:{offset:g*512,size:$.length}}),await at({outputFile:l,partitions:p}),t},H=512,pt=9,Lt=3,Xe=90,Ye=92,Kt=440,ta=510,Nt=102,fe=12,X=H-fe,ut=X-fe,ea=2048,Dt=(e,t,a)=>e.writeBigUint64LE(a,t),zt=(e,t,a)=>e.writeUint16LE(a,t+8),Mt=(e,t,a)=>e.writeUint16LE(a,t+10),we=(e,t)=>{const a=e.length%H;a!==0&&(e=Buffer.concat([e,Buffer.alloc(H-a)]));const i=BigInt(t>>pt);return Dt(e,X,i+1n),zt(e,X,(e.length>>pt)-1),Mt(e,X,ea+(H>>4)),Dt(e,ut,0n),zt(e,ut,0),Mt(e,ut,0),e},pe=(e,t,a)=>{if(e.length!==H)throw new Error(`The boot file should have a size of ${H} bytes.`);a?.copy(e,Lt,Lt,Xe),a?.copy(e,Kt,Kt,ta);const i=BigInt(t>>pt);return e.writeBigUint64LE(i,Ye),e.writeUInt8(144,Nt),e.writeUInt8(144,Nt+1),e},me=async e=>{const t=e.imageFile,a=await ae(t,"r+");try{const i=await je(a,0,H),s=await u.readFile(e.bootFile),r=await u.readFile(e.coreFile);await at({outputFile:a,partitions:[{inputBuffer:pe(s,e.partition.offset,i),output:{offset:0,size:H}},{inputBuffer:we(r,e.partition.offset),output:e.partition}]})}finally{await ie(a,t)}},ye=async e=>{const t=await u.readFile(e.bootFile??e.outputBootFile),a=e.existingBootSectorFile?await u.readFile(e.existingBootSectorFile):void 0;await u.writeFile(await I(e.outputBootFile),pe(t,e.diskOffset,a))},ge=async e=>{const t=await u.readFile(e.coreFile??e.outputCoreFile);await u.writeFile(await I(e.outputCoreFile),we(t,e.diskOffset))},be=async({outputCoreFile:e,outputBootFile:t,biosSetupDiskOffset:a,modules:i,prefix:s,config:r,pubkey:n,memdisk:o,disableCli:c,disableShimLock:l,target:p,grubSource:w,containerCache:y,apkCache:g,logger:v})=>{e=await I(e),w||(w=await K({apkPackages:["grub","grub-bios","grub-efi"],containerCache:y,apkCache:g,logger:v})),await z(w,async b=>{const m=await b.tempFolder();try{r&&await u.writeFile(f.join(m.pathInHost,"config.cfg"),r),await b.run(["grub-mkimage","-O",p??"x86_64-efi","-o","core.img",...r?["-c","config.cfg"]:[],...o?["-m","memdisk.img"]:[],...n?["-k","pubkey.key"]:[],...c?["--disable-cli"]:[],...l?["--disable-shim-lock"]:[],...s?["-p",s]:[],"--",...i??[]],["--workingdir",m.pathInContainer,"-v",`${e}:${m.pathInContainer}/core.img:rw`,...o?["-v",`${o}:${m.pathInContainer}/memdisk.img:ro`]:[],...n?["-v",`${n}:${m.pathInContainer}/pubkey.key:ro`]:[]]),t&&(t=await I(t),await u.cp(await b.resolve(`usr/lib/grub/${p}/boot.img`),t)),a&&(await ge({diskOffset:a,outputCoreFile:e}),t&&await ye({diskOffset:a,outputBootFile:t}))}finally{await m.remove()}},{logger:v})},Bt=({outputCoreFile:e,outputBootFile:t,biosSetupDiskOffset:a,modules:i,prefix:s,config:r,memdisk:n,pubkey:o,disableCli:c,disableShimLock:l,target:p,...w})=>{const y=async g=>{const v=await g.resolve(e),b=t?await g.resolve(t):void 0,m=n?await g.resolve(n):void 0,$=o?await g.resolve(o):void 0;await be({outputCoreFile:v,outputBootFile:b,biosSetupDiskOffset:a,modules:i,prefix:s,config:r,memdisk:m,pubkey:$,disableCli:c,disableShimLock:l,target:p,...w})};return y.getCacheKey=async()=>{const g=_.createHash("sha256");return g.update(JSON.stringify({outputCoreFile:e,outputBootFile:t,biosSetupDiskOffset:a,modules:i,prefix:s,config:r,memdisk:n,pubkey:o,disableCli:c,disableShimLock:l,target:p})),`GRUB-MKIMAGE-${g.digest("base64url")}`},y},Ee=async({outputFile:e,variables:t,grubSource:a,containerCache:i,apkCache:s,logger:r})=>{e=await I(e),a||(a=await K({apkPackages:["grub","grub-bios","grub-efi"],containerCache:i,apkCache:s,logger:r})),await z(a,async n=>{const o=await n.tempFolder(),c=["--workingdir",o.pathInContainer];try{await n.run(["grub-editenv","grubenv","create"],c),t&&t.length>0&&await n.run(["grub-editenv","grubenv","set",...t],c),await u.cp(f.join(o.pathInHost,"grubenv"),e)}finally{await o.remove()}},{logger:r})},ve=({outputFile:e,variables:t,...a})=>{const i=async s=>{const r=await s.resolve(e);await Ee({outputFile:r,variables:t,...a})};return i.getCacheKey=async()=>{const s=_.createHash("sha256");return s.update(JSON.stringify({outputFile:e,variables:t})),`GRUB-MKENV-${s.digest("base64url")}`},i},jt=async e=>(await u.stat(e)).size,Gt=async(e,t)=>{const a=L.createReadStream(e),i=L.createWriteStream(t,{flags:"a"});await yt.pipeline(a,i)},Ct=async({file:e,metadataFile:t=`${e}.json`,salt:a="",uuid:i="00000000-0000-0000-0000-000000000000",cryptsetupSource:s,containerCache:r,apkCache:n,logger:o})=>{s||(s=await K({apkPackages:["cryptsetup"],containerCache:r,apkCache:n,logger:o})),await z(s,async c=>{const p=(await c.run(["veritysetup","format","/image","/image.hash","--fec-device=/image.fec",`--salt=${a}`,`--uuid=${i}`],["-v",`${e}:/image:rw`])).stdout.toString("utf8"),[,w]=/Root hash:\s*([0-9a-f]{64})/.exec(p),y=await jt(e),g=await c.resolve("/image.hash");await Gt(g,e);const v=await jt(e),b=await c.resolve("/image.fec");await Gt(b,e),await u.rm(g),await u.rm(b);let m={};try{m=JSON.parse(await u.readFile(t,"utf8"))}catch{}const $={...m,rootHash:w,hashOffset:y,fecOffset:v};await u.writeFile(t,JSON.stringify($))},{logger:o})},ke=async({inputFolder:e,squashfsToolsSource:t,outputFile:a,timestamp:i=0,veritySetup:s,containerCache:r,apkCache:n,logger:o})=>{a=await I(a);const c=f.relative(e,a),l=[];c.startsWith(`..${f.sep}`)||l.push("-e",c),await M({apkPackages:["squashfs-tools"],existingSource:t,command:["mksquashfs","/in","/out","-noappend","-no-xattrs","-mkfs-time",`${i}`,"-all-time",`${i}`,...l],buildahRunOptions:["-v",`${e}:/in:ro`,"-v",`${a}:/out:rw`],containerCache:r,apkCache:n,logger:o}),s&&await Ct({file:a,...s})},At=({inputFolder:e,outputFile:t,...a})=>{const i=async s=>{const r=await s.resolve(e),n=await s.resolve(t);await ke({inputFolder:r,outputFile:n,...a})};return i.getCacheKey=async()=>{const s=_.createHash("sha256");return s.update(JSON.stringify({inputFolder:e,outputFile:t,timestamp:a.timestamp??void 0,veritySetup:a?.veritySetup?{salt:a.veritySetup.salt??void 0,uuid:a.veritySetup.uuid??void 0}:void 0})),`MKSQUASHFS-${s.digest("base64url")}`},i},$e=async({inputFolder:e,mtoolsSource:t,outputFileSize:a,outputFile:i,containerCache:s,apkCache:r,logger:n})=>{t||(t=await K({apkPackages:["mtools"],containerCache:s,apkCache:r,logger:n})),i=await I(i),await u.truncate(i,a),await z(t,async o=>{await o.run(["mformat","-i","/out","-F","::"],["-v",`${i}:/out:rw`]);const c=await u.readdir(e);c.length>0&&await o.run(["mcopy","-i","/out","-s","-b","-p",...c.map(l=>`./${l}`),"::/"],["-v",`${e}:/in:ro`,"-v",`${i}:/out:rw`,"--workingdir","/in"])},{logger:n})},Se=({inputFolder:e,outputFile:t,...a})=>{const i=async s=>{const r=await s.resolve(e),n=await s.resolve(t);await $e({inputFolder:r,outputFile:n,...a})};return i.getCacheKey=async()=>{const s=_.createHash("sha256");return s.update(JSON.stringify({inputFolder:e,outputFile:t,outputFileSize:a.outputFileSize})),`MKVFATFS-${s.digest("base64url")}`},i};class _e extends St{content;constructor({content:t={},...a}={}){super(a),this.content=t}async _getDirectoryContent(){return this.content}}class Y extends kt{content;constructor({content:t="",...a}={}){super(a),this.content=t}async _getContent(){const t=this.content;return Buffer.isBuffer(t)?t:Buffer.from(t,"utf8")}}class aa extends $t{content;constructor({content:t,...a}){super(a),this.content=t}async _getContent(){return this.content}}const ia=e=>{const t=tt(e),a=async i=>{for(const[s,{recursive:r,mode:n,dmode:o=n,uid:c,gid:l}]of t){const p=[await i.resolve(s)];for(;p.length>0;){const w=p.shift(),y=await u.lstat(w);if(y.isDirectory()&&(o!=null&&await u.chmod(w,o),r)){const v=await u.readdir(w);p.push(...v.map(b=>f.join(w,b)))}const g=y.isDirectory()?o:n;g!=null&&!y.isSymbolicLink()&&await u.chmod(w,g),(c!=null||l!=null)&&await u.lchown(w,c??y.uid,l??y.gid)}}};return a.getCacheKey=async()=>{const i=_.createHash("sha256");return i.update(JSON.stringify(e)),`SETATTRIBUTES-${i.digest("base64url")}`},a},Fe=["ed25519"],sa=async({outputFolder:e,opensshSource:t,types:a=Fe,prefix:i="ssh_host_",suffix:s="_key",containerCache:r,apkCache:n,logger:o})=>{const c={};e=f.resolve(e),await u.mkdir(e,{recursive:!0});let l=!1;const p=await Promise.all(a.map(async w=>{const y=`${i}${w}${s}`,g=`${y}.pub`;let v=!1;const b=f.join(e,g),m=f.join(e,y);c[y]=new j(m,{mode:384}),c[g]=new j(b,{mode:384});try{const[$,C]=await Promise.all([u.stat(b),u.stat(m)]);if(!$.isFile()||!C.isFile())throw new Error(`${y}.pub or ${y} exists in ${e} and is not a regular file.`);v=!0}catch($){if($.code!="ENOENT")throw $;l=!0}return{alreadyPresent:v,keyFile:y,type:w}}));return l&&await z(t??await K({apkPackages:["openssh"],containerCache:r,apkCache:n,logger:o}),async w=>{const y=await w.tempFolder();try{for(const{alreadyPresent:g,type:v,keyFile:b}of p)g||await w.run(["ssh-keygen","-t",v,"-f",b,"-N",""],["-v",`${e}:${y.pathInContainer}`,"--workingdir",y.pathInContainer])}finally{await y.remove()}},{logger:o}),c},xt=async(e,t)=>{const a=new Map;try{return await e(async i=>{if(typeof i!="string"){let s=a.get(i.imageId);s||(s=await q.from(i.imageId,t),a.set(i.imageId,s)),i=await s.resolve(i.file)}return i})}finally{for(const i of a.values())await i.remove()}},ra=async(e,t)=>{t=await I(t),await xt(async a=>{await u.cp(await a(e),t)})},na=33*1024*1024,Re=async({grubDiskDevice:e="hd0",grubEnvPartitionIndex:t,grubEnvPath:a="/grubenv",grubExtraConfig:i="",grubSourceImage:s,grubSourcePath:r="/usr/lib/grub",grubTimeout:n=3,linuxDiskDevice:o="/dev/sda",rootPartitionAIndex:c,rootPartitionBIndex:l,rootPartitionGrubCfg:p="/boot/grub.cfg",squashfsToolsSource:w,apkCache:y,containerCache:g,logger:v})=>{const b=await A.from(s,{containerCache:g,logger:v});return await b.executeStep([Ft({[f.join(r,"grub.cfg")]:new Y({content:`
insmod all_video
set envfile=(${e},gpt${t})/${a}
load_env --file $envfile buildahcker_stable buildahcker_new
if [ ( $buildahcker_new == b ) -o ( ( $buildahcker_new != a ) -a ( $buildahcker_stable == b ) ) ] ; then
set default=b
set fallback=a
else
set default=a
set fallback=b
fi
if [ $buildahcker_new != n ] ; then
set buildahcker_new=n
save_env --file $envfile buildahcker_stable buildahcker_new
fi
export buildahcker_params
set timeout=${n}
${i}
menuentry A --id=a {
set root=(${e},gpt${c})
set buildahcker_params="buildahcker_current=a buildahcker_grubenv_device=${o}${t} buildahcker_grubenv=${a} buildahcker_other_root=${o}${l} root=${o}${c}"
configfile ${p}
}
menuentry B --id=b {
set root=(${e},gpt${l})
set buildahcker_params="buildahcker_current=b buildahcker_grubenv_device=${o}${t} buildahcker_grubenv=${a} buildahcker_other_root=${o}${c} root=${o}${l}"
configfile ${p}
}
`})}),At({inputFolder:r,outputFile:"/buildahcker.img",squashfsToolsSource:w,apkCache:y,containerCache:g,logger:v})]),{imageId:b.imageId,file:"buildahcker.img"}},oa=async({grubSourceImage:e,containerCache:t,logger:a})=>{const i=await A.from(e,{containerCache:t,logger:a});return await i.executeStep([D(["grub-editenv","/buildahcker.img","create"]),D(["grub-editenv","/buildahcker.img","set","buildahcker_stable=a","buildahcker_new=n"])]),{imageId:i.imageId,file:"buildahcker.img"}},Ie=async({efiPartitionSize:e,grubDiskDevice:t="hd0",grubEnvPath:a="/grubenv",grubPartitionIndex:i,grubSourceImage:s,mtoolsSource:r,useEfi:n,containerCache:o,apkCache:c,logger:l})=>{const p=await A.from("scratch",{containerCache:o,logger:l});return await p.executeStep([ve({grubSource:s,outputFile:f.join("efi",a),variables:["buildahcker_stable=a","buildahcker_new=n"],containerCache:o,apkCache:c,logger:l})]),n&&await p.executeStep(Bt({outputCoreFile:"efi/EFI/boot/bootx64.efi",grubSource:s,target:"x86_64-efi",modules:["part_gpt","squash4"],prefix:`(${t},gpt${i})/`,containerCache:o,apkCache:c,logger:l})),await p.executeStep(Se({inputFolder:"efi",outputFile:"/buildahcker.img",outputFileSize:e,mtoolsSource:r,apkCache:c,containerCache:o,logger:l})),{imageId:p.imageId,file:"buildahcker.img"}},Be=async({grubDiskDevice:e="hd0",grubPartitionIndex:t,grubSourceImage:a,containerCache:i,apkCache:s,logger:r})=>{const n=await A.from("scratch",{containerCache:i,logger:r});return await n.executeStep([Bt({outputCoreFile:"buildahcker_grub_core.img",outputBootFile:"buildahcker_grub_boot.img",grubSource:a,target:"i386-pc",modules:["biosdisk","part_gpt","squash4"],prefix:`(${e},gpt${t})/`,containerCache:i,apkCache:s,logger:r})]),{core:{imageId:n.imageId,file:"buildahcker_grub_core.img"},boot:{imageId:n.imageId,file:"buildahcker_grub_boot.img"}}},ca=async({biosBootPartitionSize:e,bootType:t="both",efiPartitionSize:a,grubDiskDevice:i,grubEnvPath:s,grubExtraConfig:r,grubSourceImage:n,grubSourcePath:o,grubTimeout:c,linuxDiskDevice:l,mtoolsSource:p,rootPartition:w,rootPartitionGrubCfg:y,rootPartitionSize:g,squashfsToolsSource:v,apkCache:b,containerCache:m,logger:$})=>{const C=t==="bios"||t==="both",x=t==="efi"||t==="both";n||(n=await K({apkPackages:[...x?["grub-efi"]:[],...C?["grub-bios"]:[]],apkCache:b,containerCache:m,logger:$}));const B={};let R=0;const V=C?++R:-1,W=++R,h=++R,F=++R,d=++R,E=C?await Be({grubDiskDevice:i,grubPartitionIndex:h,grubSourceImage:n,apkCache:b,containerCache:m,logger:$}):void 0;E&&(B[V]={type:O.BiosBoot,name:"biosboot",size:e,file:E.core}),a=Math.max(na,a??0),B[W]={name:x?"efi":"grubenv",type:x?O.EfiSystem:O.LinuxData,file:await Ie({efiPartitionSize:a,grubDiskDevice:i,grubEnvPath:s,grubPartitionIndex:h,grubSourceImage:n,mtoolsSource:p,useEfi:x,apkCache:b,containerCache:m,logger:$})},B[h]={name:"grub",type:O.LinuxData,file:await Re({grubDiskDevice:i,grubEnvPartitionIndex:W,grubExtraConfig:r,grubSourceImage:n,grubSourcePath:o,grubTimeout:c,linuxDiskDevice:l,rootPartitionAIndex:F,rootPartitionBIndex:d,rootPartitionGrubCfg:y,squashfsToolsSource:v,apkCache:b,containerCache:m,logger:$})},B[F]={name:"systemA",type:O.LinuxData,size:g,file:w},B[d]={name:"systemB",type:O.LinuxData,size:g};const k=await A.from("scratch",{containerCache:m,logger:$}),S=async U=>await xt(async Z=>{const st=await U.resolve("buildahcker.img"),Tt=[];for(let P=1;P<=R;P++){const N=B[P];Tt.push({name:N.name,size:N.size??(await u.stat(await Z(N.file))).size,type:N.type})}const Ut=await de({partitions:Tt,outputFile:st}),Pt=[];for(let P=1;P<=R;P++){const N=B[P];N.file&&Pt.push({inputFile:await Z(N.file),output:Ut[P-1]})}await at({outputFile:st,partitions:Pt}),E&&await me({imageFile:st,bootFile:await Z(E.boot),coreFile:await Z(E.core),partition:Ut[V-1]})});return S.getCacheKey=async()=>{const U=_.createHash("sha256");return U.update(JSON.stringify(B)),`ABPARTITIONSDISK-${U.digest("base64url")}`},await k.executeStep(S),{imageId:k.imageId,file:"buildahcker.img"}},ua=async({kernelCmdline:e,rootPartitionGrubCfg:t="/boot/grub.cfg",sourceRootImage:a,sourceRootInitrdPath:i="/boot/initramfs-lts",sourceRootKernelPath:s="/boot/vmlinuz-lts",squashfsToolsSource:r,updateToolPath:n="/sbin/buildahckerABTool",apkCache:o,containerCache:c,logger:l})=>{const p=await A.from(a,{containerCache:c,logger:l});return await p.executeStep([Ft({[t]:new Y({content:`linux ${s} $buildahcker_params${e?` ${e}`:""}${i?`
initrd ${i}`:""}
`}),[n]:new Y({content:`#!/bin/sh
set -e
function readCmdline() {
cat /proc/cmdline | sed -nE 's/^.*[[:space:]]'"$1"'=([^[:space:]]*)([[:space:]]|$).*$/\\1/p'
}
BUILDHACKER_CURRENT="$(readCmdline buildahcker_current)"
BUILDHACKER_CURRENT_ROOT="$(readCmdline root)"
BUILDHACKER_OTHER_ROOT="$(readCmdline buildahcker_other_root)"
BUILDHACKER_GRUBENV="$(readCmdline buildahcker_grubenv)"
BUILDHACKER_GRUBENV_DEVICE="$(readCmdline buildahcker_grubenv_device)"
if [ -z "$BUILDHACKER_CURRENT" ] || [ -z "$BUILDHACKER_OTHER_ROOT" ] || [ -z "$BUILDHACKER_GRUBENV" ] || [ -z "$BUILDHACKER_GRUBENV_DEVICE" ] ; then
echo Not running in expected buildahcker A/B partition environment.
exit 1
fi
case "$BUILDHACKER_CURRENT" in
'a')
BUILDHACKER_OTHER=b
;;
'b')
BUILDHACKER_OTHER=a
;;
*)
echo Invalid buildahcker_current value: $BUILDHACKER_CURRENT
exit 1
;;
esac
mkdir -p /run/buildahcker-ab-grubenv
if ! mountpoint /run/buildahcker-ab-grubenv &> /dev/null ; then
mount -o ro -t vfat $BUILDHACKER_GRUBENV_DEVICE /run/buildahcker-ab-grubenv
fi
function readGrubenv() {
grub-editenv "/run/buildahcker-ab-grubenv$BUILDHACKER_GRUBENV" list | sed -nE 's/(^.*[[:space:]]|^)'"$1"'=([^[:space:]]*)([[:space:]]|$).*$/\\2/p'
}
function updateGrubenv() {
mount -o remount,rw /run/buildahcker-ab-grubenv
grub-editenv "/run/buildahcker-ab-grubenv$BUILDHACKER_GRUBENV" set "$@"
mount -o remount,ro /run/buildahcker-ab-grubenv
}
BUILDAHCKER_STABLE=$(readGrubenv buildahcker_stable)
BUILDAHCKER_NEW=$(readGrubenv buildahcker_new)
case "$*" in
'show' | '')
echo Current: "$BUILDHACKER_CURRENT"
echo Current root: "$BUILDHACKER_CURRENT_ROOT"
echo Other: "$BUILDHACKER_OTHER"
echo Other root: "$BUILDHACKER_OTHER_ROOT"
echo Stable: "$BUILDAHCKER_STABLE"
if [ "$BUILDAHCKER_STABLE" != "$BUILDHACKER_CURRENT" ] ; then
echo "Warning: current system is not marked as stable!"
fi
if [ "$BUILDAHCKER_NEW" != "n" ] ; then
echo "Warning: next reboot will be on $BUILDAHCKER_NEW"
fi
;;
'update')
if [ "$BUILDAHCKER_STABLE" != "$BUILDHACKER_CURRENT" ]; then
echo Current system is not marked as stable!
echo Updates should only be done from a stable system.
echo Use mark-stable or reboot the system before updating.
exit 1
fi
echo "Target partition: $BUILDHACKER_OTHER_ROOT"
echo "Update in progress..."
dd of="$BUILDHACKER_OTHER_ROOT"
echo "Finished writing on $BUILDHACKER_OTHER_ROOT"
updateGrubenv buildahcker_new=$BUILDHACKER_OTHER
echo Next reboot will be on "$BUILDHACKER_OTHER"!
echo To cancel, use cancel-update or run update with a different update.
;;
'cancel-update')
if [ "$BUILDAHCKER_NEW" != "n" ]; then
updateGrubenv buildahcker_new=n
echo Update cancelled, next reboot will be on "$BUILDHACKER_STABLE"
else
echo There is no update in progress.
fi
;;
'mark-stable')
if [ "$BUILDAHCKER_STABLE" != "$BUILDHACKER_CURRENT" ]; then
updateGrubenv buildahcker_stable=$BUILDHACKER_CURRENT
echo "Current system successfully marked as stable."
else
echo "Current system was already marked as stable."
fi
;;
'is-stable')
if [ "$BUILDAHCKER_STABLE" == "$BUILDHACKER_CURRENT" ]; then
exit 0
else
exit 1
fi
;;
*)
echo Invalid command: "$*"
exit 1
;;
esac
`,mode:320})}),At({inputFolder:".",outputFile:"/buildahcker.img",squashfsToolsSource:r,apkCache:o,containerCache:c,logger:l})]),{imageId:p.imageId,file:"buildahcker.img"}};class it{#t;#e;#a;constructor(t,a){this.#t=t,this.#e=a,this.#a=new _e({content:{cert:t,key:a}})}async certificatePath(){return this.#t.sourceFilePath}async privateKeyPath(){return this.#e.sourceFilePath}async getHash(){return await this.#a.getContentHash()}static from(t,a){return new it(new j(t,{uid:0,gid:0,mode:384}),new j(a,{uid:0,gid:0,mode:384}))}}const Ce=async({inputFile:e,outputFile:t,key:a,sbsignSource:i,containerCache:s,apkCache:r,logger:n})=>{t||(t=`${e}.signed`),t=await I(t),await M({apkPackages:["sbsigntool"],existingSource:i,command:["sbsign","--key","/in.key","--cert","/in.crt","--output","/out.efi","/in.efi"],buildahRunOptions:["-v",`${e}:/in.efi:ro`,"-v",`${await a.certificatePath()}:/in.crt:ro`,"-v",`${await a.privateKeyPath()}:/in.key:ro`,"-v",`${t}:/out.efi:rw`],containerCache:s,apkCache:r,logger:n})},Ae=({inputFile:e,outputFile:t,key:a,...i})=>{t||(t=`${e}.signed`);const s=async r=>{const n=e===t,o=n?`${e}.unsigned`:e,c=await r.resolve(o),l=await r.resolve(t);n&&(await u.copyFile(l,c),await u.truncate(l)),await Ce({inputFile:c,outputFile:l,key:a,...i}),n&&await u.rm(c)};return s.getCacheKey=async()=>{const r=_.createHash("sha256");return r.update(JSON.stringify({inputFile:e,outputFile:t})),r.update(await a.getHash()),`SBSIGN-${r.digest("base64url")}`},s},G=1024,la=G+12,lt=48,Jt=4,ha=[0,4067132163,3778769143,324072436,3348797215,904991772,648144872,3570033899,2329499855,2024987596,1809983544,2575936315,1296289744,3207089363,2893594407,1578318884,274646895,3795141740,4049975192,51262619,3619967088,632279923,922689671,3298075524,2592579488,1760304291,2075979607,2312596564,1562183871,2943781820,3156637768,1313733451,549293790,3537243613,3246849577,871202090,3878099393,357341890,102525238,4101499445,2858735121,1477399826,1264559846,3107202533,1845379342,2677391885,2361733625,2125378298,820201905,3263744690,3520608582,598981189,4151959214,85089709,373468761,3827903834,3124367742,1213305469,1526817161,2842354314,2107672161,2412447074,2627466902,1861252501,1098587580,3004210879,2688576843,1378610760,2262928035,1955203488,1742404180,2511436119,3416409459,969524848,714683780,3639785095,205050476,4266873199,3976438427,526918040,1361435347,2739821008,2954799652,1114974503,2529119692,1691668175,2005155131,2247081528,3690758684,697762079,986182379,3366744552,476452099,3993867776,4250756596,255256311,1640403810,2477592673,2164122517,1922457750,2791048317,1412925310,1197962378,3037525897,3944729517,427051182,170179418,4165941337,746937522,3740196785,3451792453,1070968646,1905808397,2213795598,2426610938,1657317369,3053634322,1147748369,1463399397,2773627110,4215344322,153784257,444234805,3893493558,1021025245,3467647198,3722505002,797665321,2197175160,1889384571,1674398607,2443626636,1164749927,3070701412,2757221520,1446797203,137323447,4198817972,3910406976,461344835,3484808360,1037989803,781091935,3705997148,2460548119,1623424788,1939049696,2180517859,1429367560,2807687179,3020495871,1180866812,410100952,3927582683,4182430767,186734380,3756733383,763408580,1053836080,3434856499,2722870694,1344288421,1131464017,2971354706,1708204729,2545590714,2229949006,1988219213,680717673,3673779818,3383336350,1002577565,4010310262,493091189,238226049,4233660802,2987750089,1082061258,1395524158,2705686845,1972364758,2279892693,2494862625,1725896226,952904198,3399985413,3656866545,731699698,4283874585,222117402,510512622,3959836397,3280807620,837199303,582374963,3504198960,68661723,4135334616,3844915500,390545967,1230274059,3141532936,2825850620,1510247935,2395924756,2091215383,1878366691,2644384480,3553878443,565732008,854102364,3229815391,340358836,3861050807,4117890627,119113024,1493875044,2875275879,3090270611,1247431312,2660249211,1828433272,2141937292,2378227087,3811616794,291187481,34330861,4032846830,615137029,3603020806,3314634738,939183345,1776939221,2609017814,2295496738,2058945313,2926798794,1545135305,1330124605,3173225534,4084100981,17165430,307568514,3762199681,888469610,3332340585,3587147933,665062302,2042050490,2346497209,2559330125,1793573966,3190661285,1279665062,1595330642,2910671697],da=(e,t)=>{const a=t.length;for(let i=0;i<a;i++)e=ha[(e^t[i])&255]^e>>>8;return e},qt=e=>da(1346745674,e.subarray(8))>>>0,fa=e=>{const t=e.toString("hex");return`${t.substring(0,8)}-${t.substring(8,12)}-${t.substring(12,16)}-${t.substring(16,20)}-${t.substring(20,32)}`},xe=async(e,t)=>{const a=await u.open(e,"r+");try{const i=Buffer.alloc(1);if((await a.read(i,0,i.length,la)).bytesRead!==i.length)throw new Error("Could not read blkszbits byte");let r=1<<i.readUInt8(0);r>G&&(r-=G);const n=Buffer.alloc(r);if((await a.read(n,0,n.length,G)).bytesRead!==n.length)throw new Error("Could not read full superblock");if(n.readUInt32LE(0)!==3774210530)throw new Error("Missing EROFS signature.");const c=n.readUInt32LE(Jt),l=qt(n);if(c!==l)throw new Error(`Original CRC does not match: 0x${c.toString(16)} !== 0x${l.toString(16)}`);return t.copy(n,lt,0,16),n.writeUInt32LE(qt(n),Jt),await a.write(n,0,n.length,G),fa(n.subarray(lt,lt+16))}finally{await a.close()}},Te=async({inputFolder:e,erofsUtilsSource:t,excludeRegex:a,timestamp:i=0,uuid:s="hash",veritySetup:r,outputFile:n,metadataFile:o=`${n}.json`,containerCache:c,apkCache:l,logger:p})=>{n=await I(n);const w=f.relative(e,n),y=[];if(w.startsWith(`..${f.sep}`)||y.push(`--exclude-path=${w}`),a?.forEach(g=>y.push(`--exclude-regex=${g}`)),s!=="random"&&s!=="hash"&&y.push("-U",s),await M({apkPackages:["erofs-utils"],existingSource:t,command:["mkfs.erofs",...y,"-T",`${i}`,"/out","--","/in"],buildahRunOptions:["-v",`${e}:/in:ro`,"-v",`${n}:/out:rw`],containerCache:c,apkCache:l,logger:p}),s==="hash"){const g=L.createReadStream(n),v=_.createHash("sha256");await yt.pipeline(g,v);const b=v.digest();p?.write(`Hash: ${b.toString("hex")}
`);const m=await xe(n,b);await u.writeFile(o,JSON.stringify({uuid:m}))}r&&await Ct({file:n,metadataFile:o,...r})},wa=({inputFolder:e,outputFile:t,metadataFile:a,...i})=>{const s=async r=>{const n=await r.resolve(e),o=await r.resolve(t),c=a?await r.resolve(a):void 0;await Te({inputFolder:n,outputFile:o,metadataFile:c,...i})};return s.getCacheKey=async()=>{const r=_.createHash("sha256");return r.update(JSON.stringify({inputFolder:e,outputFile:t,metadataFile:a,excludeRegex:i.excludeRegex??void 0,uuid:i.uuid??void 0,timestamp:i.timestamp??void 0,veritySetup:i.veritySetup?{salt:i.veritySetup.salt??void 0,uuid:i.veritySetup.uuid??void 0}:void 0})),`MKEROFS-${r.digest("base64url")}`},s},pa=e=>{const t=async a=>{const i=await a.resolve("/etc/veritytab"),s=[];for(const r of e.volumes){const n=JSON.parse(await u.readFile(await a.resolve(r.metadataFile),"utf8")),o=r.device??(n.uuid?`/dev/disk/by-uuid/${n.uuid}`:null);if(!o)throw new Error(`No device specified for ${r.name}`);const c=[`hash-offset=${n.hashOffset}`,`fec-device=${o}`,`fec-offset=${n.fecOffset}`,...r.options??[]];s.push(`${r.name} ${o} ${o} ${n.rootHash} ${c.join(",")}`)}await u.writeFile(i,s.join(`
`)+`
`,{mode:420,flag:e.replace?"w":"a"})};return t.getCacheKey=async()=>{const a=_.createHash("sha256");return a.update(JSON.stringify(e)),`VERITYTAB-${a.digest("base64url")}`},t},ma=({outputFile:e,addModules:t,installFiles:a,mount:i,extraOptions:s=[]})=>{const r=["dracut",e,"--no-hostonly"];return t?.forEach(n=>{r.push("--add",n)}),i?.forEach(n=>{r.push("--mount",n)}),a?.forEach(n=>{r.push("--install",n)}),r.push(...s),D(r,{extraHashData:["--kver","AUTOKERNELVERSION"],async beforeRun(n,o){await u.mkdir(await n.resolve("var/tmp"),{recursive:!0});const c=await u.readdir(await n.resolve("lib/modules"));if(c.length!=1)throw new Error(`Expected exactly one kernel version in lib/modules, found: ${c.join(", ")}`);o.push("--kver",c[0])}})},ya=({outputFile:e,splash:t,initrd:a,cmdline:i,sign:s})=>{const r=["ukify","build",`--output=${e}`];t&&r.push(`--splash=${t}`),a&&r.push(`--initrd=${a}`),i&&r.push(`--cmdline=${i}`);const n=[D(r,{extraHashData:["--linux=/lib/modules/AUTOKERNELVERSION/vmlinuz"],async beforeRun(o,c){const l=await u.readdir(await o.resolve("lib/modules"));if(l.length!=1)throw new Error(`Expected exactly one kernel version in lib/modules, found: ${l.join(", ")}`);c.push(`--linux=/lib/modules/${l[0]}/vmlinuz`)}})];return s&&n.push(Ae({inputFile:e,outputFile:e,...s})),n},Ue=async({certificateFile:e,keyFile:t,keyType:a="rsa:4096",options:{subject:i,validDays:s},opensslSource:r,containerCache:n,apkCache:o,logger:c})=>{e=await I(e),t=await I(t);const l=["openssl","req","-newkey",a,"-nodes","-keyout","/key.key","-new","-x509","-out","/cert.pem","-days",s.toString(),"-subj",i];await M({apkPackages:["openssl"],existingSource:r,command:l,buildahRunOptions:["-v",`${e}:/cert.pem:rw`,"-v",`${t}:/key.key:rw`],containerCache:n,apkCache:o,logger:c})},Pe=async({inputFile:e,outputFile:t,efitoolsSource:a,containerCache:i,apkCache:s,logger:r,guid:n})=>{t=await I(t);const o=["cert-to-efi-sig-list"];n&&o.push("-g",n),o.push("/input.pem","/output.esl"),await M({apkPackages:["efitools"],existingSource:a,command:o,buildahRunOptions:["-v",`${e}:/input.pem:ro`,"-v",`${t}:/output.esl:rw`],containerCache:i,apkCache:s,logger:r})},Oe=async({keyFile:e,certificateFile:t,inputFile:a,outputFile:i,efiVar:s,efitoolsSource:r,containerCache:n,apkCache:o,logger:c,guid:l})=>{i=await I(i);const p=["sign-efi-sig-list"];l&&p.push("-g",l),p.push("-c","/cert.pem","-k","/key.key",s,"/input.esl","/output.auth"),await M({apkPackages:["efitools"],existingSource:r,command:p,buildahRunOptions:["-v",`${e}:/key.key:ro`,"-v",`${t}:/cert.pem:ro`,"-v",`${i}:/output.auth:rw`,"-v",`${a}:/input.esl:ro`],containerCache:n,apkCache:o,logger:c})},Q=async e=>{try{if((await u.lstat(e)).isFile())return!0;throw new Error(`Expected a regular file in ${e}`)}catch(t){if(t.code==="ENOENT")return!1;throw t}},ga=async({outputDirectory:e,defaultCertOptions:t={subject:"/CN=Secure boot key/",validDays:36500},certOptions:a,guid:i=_.randomUUID(),opensslSource:s,efitoolsSource:r,logger:n,apkCache:o,containerCache:c})=>{let l="PK";i=await ee(f.join(e,"guid"),()=>i);let p=!1;for(const w of["PK","KEK","db"]){const y=f.join(e,`${w}.crt`),g=f.join(e,`${w}.key`),v=f.join(e,`${w}.esl`),b=f.join(e,`${w}.auth`),m=await Q(y),$=await Q(g);if(!m!=!$)throw new Error(`${y} and ${g} must both exist or both not exist.`);if(!m&&!$)await Ue({certificateFile:y,keyFile:g,options:a?.[w]??t,opensslSource:s,containerCache:c,apkCache:o,logger:n}),p=!0;else if(p)throw new Error(`${y} and ${g} already exist. Please remove the existing files and try again.`);if(!await Q(v))await Pe({inputFile:y,outputFile:v,guid:i,efitoolsSource:r,containerCache:c,apkCache:o,logger:n}),p=!0;else if(p)throw new Error(`${v} already exists. Please remove the existing file and try again.`);if(!await Q(b))await Oe({keyFile:f.join(e,`${l}.key`),certificateFile:f.join(e,`${l}.crt`),inputFile:v,outputFile:b,efiVar:w,guid:i,efitoolsSource:r,containerCache:c,apkCache:o,logger:n}),p=!0;else if(p)throw new Error(`${b} already exists. Please remove the existing file and try again.`);l=w}return{key:it.from(f.join(e,"db.crt"),f.join(e,"db.key")),authFiles:{"PK.auth":new J(f.join(e,"PK.auth")),"KEK.auth":new J(f.join(e,"KEK.auth")),"db.auth":new J(f.join(e,"db.auth"))}}},ba=(e,{pacmanCache:t}={})=>{const a={};return t&&(a.buildahArgsNoHash=["--volume",`${t}:/var/cache/pacman/pkg:rw`],a.extraHashData=["--volume",":/var/cache/pacman/pkg:rw"],a.beforeRun=async()=>{await u.mkdir(t,{recursive:!0})}),D(["pacman","-Syu","--noconfirm","--",...e],a)};let ht;const Ea=()=>(ht||(ht=f.join(mt.homedir(),".buildahcker","cache","pacman")),ht),He=(e,t)=>{e=e.sort();const a=async i=>{const r=(await i.run(["pacman","-lq","-Q","--",...e])).stdout.toString("utf8").trim().split(`
`),n=[];for(const o of r){if(o.endsWith("/")){n.unshift(o);continue}const c=await i.resolveParent(o);t?.write(`rm ${o}
`),await u.rm(c,{force:!0})}for(const o of n){const c=await i.resolveParent(o);await Yt(c)}};return a.getCacheKey=async()=>{const i=_.createHash("sha256");return i.update(JSON.stringify(e)),`PACMAN-REMOVE-${i.digest("base64url")}`},a},va=(e=[],t)=>[He(["pacman",...e],t),Rt(["var/lib/pacman","var/cache/pacman","usr/share/pacman","usr/share/libalpm","etc/pacman.d"])],Vt=/^[\w-]+$/;class Le{cachePath;constructor(t){this.cachePath=f.resolve(t)}#t(t,a){if(!Vt.test(t)||!Vt.test(a))throw new Error(`Unsafe image id ${JSON.stringify(t)} or operation key ${JSON.stringify(a)}.`);return f.join(this.cachePath,t,a)}async getEntry(t,a){const i=this.#t(t,a);try{return(await u.readFile(i,"utf8")).trim()||void 0}catch{}}async setEntry(t,a,i){const s=this.#t(t,a);await u.mkdir(f.dirname(s),{recursive:!0}),await u.writeFile(s,i,"utf8")}}let dt;const ka=()=>{if(!dt){const e=f.join(mt.homedir(),".buildahcker","cache","containers");dt=new Le(e)}return dt};exports.ApkInstalledDatabaseParser=Qt;exports.BaseDirectory=St;exports.BaseFile=vt;exports.BaseFileWithCachedContent=et;exports.BaseFileWithLoadedAttributes=ne;exports.BaseRegularFile=kt;exports.BaseSymbolicLink=$t;exports.CommandFailed=le;exports.Container=q;exports.DiskDirectory=_t;exports.DiskFile=j;exports.DiskLocation=J;exports.DiskSymLink=oe;exports.FSContainerCache=Le;exports.ImageBuilder=A;exports.MemDirectory=_e;exports.MemFile=Y;exports.MemSymLink=aa;exports.PartitionType=O;exports.ProxyFile=re;exports.SignKey=it;exports.WritableBuffer=ft;exports.abpartitionsBiosPartition=Be;exports.abpartitionsDisk=ca;exports.abpartitionsGrubEnvPartition=oa;exports.abpartitionsGrubPartition=Re;exports.abpartitionsGrubenvAndEfiPartition=Ie;exports.abpartitionsRootPartition=ua;exports.addFiles=Ft;exports.addRootDirectory=Ve;exports.apkAdd=Wt;exports.apkManuallyRemove=ue;exports.apkRemoveApk=We;exports.buildahInspect=Ze;exports.certToEfiSigList=Pe;exports.copyFileInImage=ra;exports.defaultApkCache=ze;exports.defaultContainerCache=ka;exports.defaultPacmanCache=Ea;exports.defaultSSHKeyGenTypes=Fe;exports.dracut=ma;exports.erofsSetUUID=xe;exports.exec=T;exports.getFullImageID=wt;exports.grubBiosSetup=me;exports.grubBiosSetupPrepareBoot=ye;exports.grubBiosSetupPrepareCore=ge;exports.grubMkenv=Ee;exports.grubMkenvStep=ve;exports.grubMkimage=be;exports.grubMkimageStep=Bt;exports.hashDirectoryContent=Et;exports.mkcert=Ue;exports.mkerofs=Te;exports.mkerofsStep=wa;exports.mksquashfs=ke;exports.mksquashfsStep=At;exports.mkvfatfs=$e;exports.mkvfatfsStep=Se;exports.normalizeDirectoryEntries=tt;exports.normalizeEntry=se;exports.normalizeRelativePath=bt;exports.pacmanInstall=ba;exports.pacmanManuallyRemove=He;exports.pacmanRemovePacman=va;exports.parted=de;exports.prepareApkPackages=K;exports.prepareApkPackagesAndRun=M;e