Files: 65191f9a9664f013ebe726bb95a1a54b086e1bd5 / lib / producer.js
7108 bytesRaw
1 | import { STORE_BLOB, STORE_CONTENT, snapshotify } from '../prelude/common.js'; |
2 | import { log, wasReported } from './log.js'; |
3 | import Multistream from 'multistream'; |
4 | import assert from 'assert'; |
5 | import { fabricateTwice } from './fabricator.js'; |
6 | import fs from 'fs'; |
7 | import intoStream from 'into-stream'; |
8 | import streamMeter from 'stream-meter'; |
9 | |
10 | function discoverPlaceholder (binaryBuffer, searchString, padder) { |
11 | const placeholder = Buffer.from(searchString); |
12 | const position = binaryBuffer.indexOf(placeholder); |
13 | if (position === -1) return { notFound: true }; |
14 | return { position, size: placeholder.length, padder }; |
15 | } |
16 | |
17 | function injectPlaceholder (fd, placeholder, value, cb) { |
18 | const { notFound, position, size, padder } = placeholder; |
19 | if (notFound) assert(false, 'Placeholder for not found'); |
20 | if (typeof value === 'number') value = value.toString(); |
21 | if (typeof value === 'string') value = Buffer.from(value); |
22 | const padding = Buffer.from(padder.repeat(size - value.length)); |
23 | value = Buffer.concat([ value, padding ]); |
24 | fs.write(fd, value, 0, value.length, position, cb); |
25 | } |
26 | |
27 | function discoverPlaceholders (binaryBuffer) { |
28 | return { |
29 | BAKERY: discoverPlaceholder(binaryBuffer, '\0' + '// BAKERY '.repeat(20), '\0'), |
30 | PAYLOAD_POSITION: discoverPlaceholder(binaryBuffer, '// PAYLOAD_POSITION //', ' '), |
31 | PAYLOAD_SIZE: discoverPlaceholder(binaryBuffer, '// PAYLOAD_SIZE //', ' '), |
32 | PRELUDE_POSITION: discoverPlaceholder(binaryBuffer, '// PRELUDE_POSITION //', ' '), |
33 | PRELUDE_SIZE: discoverPlaceholder(binaryBuffer, '// PRELUDE_SIZE //', ' ') |
34 | }; |
35 | } |
36 | |
37 | function injectPlaceholders (fd, placeholders, values, cb) { |
38 | injectPlaceholder(fd, placeholders.BAKERY, values.BAKERY, (error) => { |
39 | if (error) return cb(error); |
40 | injectPlaceholder(fd, placeholders.PAYLOAD_POSITION, values.PAYLOAD_POSITION, (error2) => { |
41 | if (error2) return cb(error2); |
42 | injectPlaceholder(fd, placeholders.PAYLOAD_SIZE, values.PAYLOAD_SIZE, (error3) => { |
43 | if (error3) return cb(error3); |
44 | injectPlaceholder(fd, placeholders.PRELUDE_POSITION, values.PRELUDE_POSITION, (error4) => { |
45 | if (error4) return cb(error4); |
46 | injectPlaceholder(fd, placeholders.PRELUDE_SIZE, values.PRELUDE_SIZE, cb); |
47 | }); |
48 | }); |
49 | }); |
50 | }); |
51 | } |
52 | |
53 | function makeBakeryValueFromBakes (bakes) { |
54 | const parts = []; |
55 | if (bakes.length) { |
56 | for (let i = 0; i < bakes.length; i += 1) { |
57 | parts.push(Buffer.from(bakes[i])); |
58 | parts.push(Buffer.alloc(1)); |
59 | } |
60 | parts.push(Buffer.alloc(1)); |
61 | } |
62 | return Buffer.concat(parts); |
63 | } |
64 | |
65 | function replaceDollarWise (s, sf, st) { |
66 | return s.replace(sf, () => st); |
67 | } |
68 | |
69 | function makePreludeBufferFromPrelude (prelude) { |
70 | return Buffer.from( |
71 | '(function(process, require, console, EXECPATH_FD, PAYLOAD_POSITION, PAYLOAD_SIZE) { ' + |
72 | prelude + |
73 | '\n})' // dont remove \n |
74 | ); |
75 | } |
76 | |
77 | export default function ({ backpack, bakes, slash, target }) { |
78 | return new Promise((resolve, reject) => { |
79 | if (!Buffer.alloc) { |
80 | throw wasReported('Your node.js does not have Buffer.alloc. Please upgrade!'); |
81 | } |
82 | |
83 | let { prelude, entrypoint, stripes } = backpack; |
84 | entrypoint = snapshotify(entrypoint, slash); |
85 | stripes = stripes.slice(); |
86 | |
87 | const vfs = {}; |
88 | for (const stripe of stripes) { |
89 | let { snap } = stripe; |
90 | snap = snapshotify(snap, slash); |
91 | if (!vfs[snap]) vfs[snap] = {}; |
92 | } |
93 | |
94 | let meter; |
95 | let count = 0; |
96 | |
97 | function pipeToNewMeter (s) { |
98 | meter = streamMeter(); |
99 | return s.pipe(meter); |
100 | } |
101 | |
102 | function next (s) { |
103 | count += 1; |
104 | return pipeToNewMeter(s); |
105 | } |
106 | |
107 | const binaryBuffer = fs.readFileSync(target.binaryPath); |
108 | const placeholders = discoverPlaceholders(binaryBuffer); |
109 | |
110 | let track = 0; |
111 | let prevStripe; |
112 | |
113 | let payloadPosition; |
114 | let payloadSize; |
115 | let preludePosition; |
116 | let preludeSize; |
117 | |
118 | new Multistream((cb) => { |
119 | if (count === 0) { |
120 | return cb(undefined, next( |
121 | intoStream(binaryBuffer) |
122 | )); |
123 | } else |
124 | if (count === 1) { |
125 | payloadPosition = meter.bytes; |
126 | return cb(undefined, next( |
127 | intoStream(Buffer.alloc(0)) |
128 | )); |
129 | } else |
130 | if (count === 2) { |
131 | if (prevStripe && !prevStripe.skip) { |
132 | let { snap, store } = prevStripe; |
133 | snap = snapshotify(snap, slash); |
134 | vfs[snap][store] = [ track, meter.bytes ]; |
135 | track += meter.bytes; |
136 | } |
137 | |
138 | if (stripes.length) { |
139 | // clone to prevent 'skip' propagate |
140 | // to other targets, since same stripe |
141 | // is used for several targets |
142 | const stripe = Object.assign({}, stripes.shift()); |
143 | prevStripe = stripe; |
144 | |
145 | if (stripe.buffer) { |
146 | if (stripe.store === STORE_BLOB) { |
147 | const snap = snapshotify(stripe.snap, slash); |
148 | return fabricateTwice(bakes, target.fabricator, snap, stripe.buffer, (error, buffer) => { |
149 | if (error) { |
150 | log.warn(error.message); |
151 | stripe.skip = true; |
152 | return cb(undefined, intoStream(Buffer.alloc(0))); |
153 | } |
154 | |
155 | cb(undefined, pipeToNewMeter(intoStream(buffer))); |
156 | }); |
157 | } else { |
158 | return cb(undefined, pipeToNewMeter(intoStream(stripe.buffer))); |
159 | } |
160 | } else |
161 | if (stripe.file) { |
162 | if (stripe.file === target.output) { |
163 | return cb(wasReported( |
164 | 'Trying to take executable into executable', stripe.file |
165 | )); |
166 | } |
167 | |
168 | assert.equal(stripe.store, STORE_CONTENT); // others must be buffers from walker |
169 | return cb(undefined, pipeToNewMeter(fs.createReadStream(stripe.file))); |
170 | } else { |
171 | assert(false, 'producer: bad stripe'); |
172 | } |
173 | } else { |
174 | payloadSize = track; |
175 | preludePosition = payloadPosition + payloadSize; |
176 | return cb(undefined, next( |
177 | intoStream(makePreludeBufferFromPrelude( |
178 | replaceDollarWise( |
179 | replaceDollarWise(prelude, '%VIRTUAL_FILESYSTEM%', JSON.stringify(vfs)), |
180 | '%DEFAULT_ENTRYPOINT%', JSON.stringify(entrypoint)) |
181 | )) |
182 | )); |
183 | } |
184 | } else { |
185 | return cb(); |
186 | } |
187 | }).on('error', (error) => { |
188 | reject(error); |
189 | }).pipe( |
190 | fs.createWriteStream(target.output) |
191 | ).on('error', (error) => { |
192 | reject(error); |
193 | }).on('close', () => { |
194 | preludeSize = meter.bytes; |
195 | fs.open(target.output, 'r+', (error, fd) => { |
196 | if (error) return reject(error); |
197 | injectPlaceholders(fd, placeholders, { |
198 | BAKERY: makeBakeryValueFromBakes(bakes), |
199 | PAYLOAD_POSITION: payloadPosition, |
200 | PAYLOAD_SIZE: payloadSize, |
201 | PRELUDE_POSITION: preludePosition, |
202 | PRELUDE_SIZE: preludeSize |
203 | }, (error2) => { |
204 | if (error2) return reject(error2); |
205 | fs.close(fd, (error3) => { |
206 | if (error3) return reject(error3); |
207 | resolve(); |
208 | }); |
209 | }); |
210 | }); |
211 | }); |
212 | }); |
213 | } |
214 |
Built with git-ssb-web