@modelprofile.com/browser-runtime 3.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/changelog.md +17 -0
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.artifactstore.d.ts +4 -1
- package/dist_ts/classes.artifactstore.js +42 -11
- package/dist_ts/classes.runtime.d.ts +8 -5
- package/dist_ts/classes.runtime.js +126 -48
- package/dist_ts/classes.runtimeownership.d.ts +102 -0
- package/dist_ts/classes.runtimeownership.js +1230 -0
- package/dist_ts/internal.testing.d.ts +2 -0
- package/dist_ts/internal.testing.js +1 -1
- package/dist_ts/plugins.d.ts +2 -1
- package/dist_ts/plugins.js +3 -2
- package/package.json +3 -2
- package/readme.hints.md +7 -0
- package/readme.md +23 -4
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.artifactstore.ts +51 -14
- package/ts/classes.runtime.ts +132 -48
- package/ts/classes.runtimeownership.ts +1393 -0
- package/ts/internal.testing.ts +2 -0
- package/ts/plugins.ts +2 -1
|
@@ -0,0 +1,1230 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { BrowserRuntimeError } from './errors.js';
|
|
3
|
+
import { randomId } from './utils.js';
|
|
4
|
+
const metadataSchema = 1;
|
|
5
|
+
const maximumMetadataBytes = 4096;
|
|
6
|
+
const maximumProcFileBytes = 2 * 1024 * 1024;
|
|
7
|
+
const anchorInitializationAttempts = 200;
|
|
8
|
+
const generationIdPattern = /^[A-Za-z0-9_-]{32}$/u;
|
|
9
|
+
const hashPattern = /^[a-f0-9]{64}$/u;
|
|
10
|
+
const bootIdPattern = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/u;
|
|
11
|
+
const unsignedIntegerPattern = /^(?:0|[1-9][0-9]{0,24})$/u;
|
|
12
|
+
const isMissingError = (errorArg) => (errorArg.code === 'ENOENT'
|
|
13
|
+
|| errorArg.code === 'ESRCH');
|
|
14
|
+
const normalizeOwnershipError = (errorArg) => (errorArg instanceof BrowserRuntimeError
|
|
15
|
+
? errorArg
|
|
16
|
+
: new BrowserRuntimeError('FENCED', 'runtime ownership could not be verified'));
|
|
17
|
+
const statIdentityMatches = (leftArg, rightArg) => leftArg.dev === rightArg.dev && leftArg.ino === rightArg.ino;
|
|
18
|
+
const parseProcessStartTicks = (statTextArg, expectedPidArg) => {
|
|
19
|
+
if (plugins.Buffer.byteLength(statTextArg, 'utf8') === 0
|
|
20
|
+
|| plugins.Buffer.byteLength(statTextArg, 'utf8') > 16 * 1024
|
|
21
|
+
|| /[\u0000\r]/u.test(statTextArg))
|
|
22
|
+
throw new BrowserRuntimeError('FENCED', 'process identity is malformed');
|
|
23
|
+
const statText = statTextArg.endsWith('\n') ? statTextArg.slice(0, -1) : statTextArg;
|
|
24
|
+
if (statText.includes('\n')) {
|
|
25
|
+
throw new BrowserRuntimeError('FENCED', 'process identity is malformed');
|
|
26
|
+
}
|
|
27
|
+
const commandStart = statText.indexOf(' (');
|
|
28
|
+
const commandEnd = statText.lastIndexOf(') ');
|
|
29
|
+
if (commandStart <= 0 || commandEnd < commandStart + 2) {
|
|
30
|
+
throw new BrowserRuntimeError('FENCED', 'process identity is malformed');
|
|
31
|
+
}
|
|
32
|
+
const pidText = statText.slice(0, commandStart);
|
|
33
|
+
const fieldsAfterCommand = statText.slice(commandEnd + 2).split(' ');
|
|
34
|
+
const processStartTicks = fieldsAfterCommand[19];
|
|
35
|
+
if (!/^[1-9][0-9]{0,9}$/u.test(pidText)
|
|
36
|
+
|| fieldsAfterCommand.length < 20
|
|
37
|
+
|| !processStartTicks
|
|
38
|
+
|| !unsignedIntegerPattern.test(processStartTicks))
|
|
39
|
+
throw new BrowserRuntimeError('FENCED', 'process identity is malformed');
|
|
40
|
+
if (expectedPidArg !== undefined && Number(pidText) !== expectedPidArg) {
|
|
41
|
+
throw new BrowserRuntimeError('FENCED', 'process identity is malformed');
|
|
42
|
+
}
|
|
43
|
+
return processStartTicks;
|
|
44
|
+
};
|
|
45
|
+
export class BrowserRuntimeOwnership {
|
|
46
|
+
runtimeDirectory;
|
|
47
|
+
lockPath;
|
|
48
|
+
anchorDirectory;
|
|
49
|
+
generationsRoot;
|
|
50
|
+
legacyProfileRoot;
|
|
51
|
+
legacyArtifactRoot;
|
|
52
|
+
uid;
|
|
53
|
+
procRoot;
|
|
54
|
+
now;
|
|
55
|
+
beforeMetadataPublication;
|
|
56
|
+
beforeGenerationRemoval;
|
|
57
|
+
onOwnershipLost;
|
|
58
|
+
directoryHandle;
|
|
59
|
+
directoryIdentity;
|
|
60
|
+
anchorDirectoryHandle;
|
|
61
|
+
anchorDirectoryIdentity;
|
|
62
|
+
anchorIdentity;
|
|
63
|
+
namedMutex;
|
|
64
|
+
namedMutexLease;
|
|
65
|
+
lockHandle;
|
|
66
|
+
lockIdentity;
|
|
67
|
+
createdLock = false;
|
|
68
|
+
leaseLost = false;
|
|
69
|
+
metadata;
|
|
70
|
+
generation;
|
|
71
|
+
acquirePromise;
|
|
72
|
+
releasePromise;
|
|
73
|
+
constructor(optionsArg) {
|
|
74
|
+
this.runtimeDirectory = plugins.path.resolve(optionsArg.runtimeDirectory);
|
|
75
|
+
this.lockPath = plugins.path.join(this.runtimeDirectory, 'runtime.lock');
|
|
76
|
+
this.anchorDirectory = plugins.path.join(this.runtimeDirectory, 'runtime.mutex');
|
|
77
|
+
this.generationsRoot = plugins.path.join(this.runtimeDirectory, 'generations');
|
|
78
|
+
this.legacyProfileRoot = plugins.path.join(this.runtimeDirectory, 'profiles');
|
|
79
|
+
this.legacyArtifactRoot = plugins.path.join(this.runtimeDirectory, 'artifacts');
|
|
80
|
+
this.uid = optionsArg.uid;
|
|
81
|
+
this.procRoot = optionsArg.testing?.procRoot ?? '/proc';
|
|
82
|
+
this.now = optionsArg.testing?.now ?? Date.now;
|
|
83
|
+
this.beforeMetadataPublication = optionsArg.testing?.beforeMetadataPublication;
|
|
84
|
+
this.beforeGenerationRemoval = optionsArg.testing?.beforeGenerationRemoval;
|
|
85
|
+
this.onOwnershipLost = optionsArg.onOwnershipLost;
|
|
86
|
+
}
|
|
87
|
+
get owned() {
|
|
88
|
+
return Boolean(this.metadata?.state === 'active'
|
|
89
|
+
&& this.generation
|
|
90
|
+
&& this.namedMutexLease?.state === 'active'
|
|
91
|
+
&& !this.leaseLost);
|
|
92
|
+
}
|
|
93
|
+
get cleanupPending() {
|
|
94
|
+
return Boolean(this.lockHandle
|
|
95
|
+
|| this.anchorDirectoryHandle
|
|
96
|
+
|| this.directoryHandle
|
|
97
|
+
|| (this.namedMutexLease && this.namedMutexLease.state !== 'released'));
|
|
98
|
+
}
|
|
99
|
+
acquire() {
|
|
100
|
+
if (this.owned)
|
|
101
|
+
return Promise.resolve({ ...this.generation });
|
|
102
|
+
if (this.acquirePromise)
|
|
103
|
+
return this.acquirePromise;
|
|
104
|
+
if (this.releasePromise)
|
|
105
|
+
return this.releasePromise.then(() => this.acquire());
|
|
106
|
+
const operation = this.acquireInternal().finally(() => {
|
|
107
|
+
if (this.acquirePromise === operation)
|
|
108
|
+
this.acquirePromise = undefined;
|
|
109
|
+
});
|
|
110
|
+
this.acquirePromise = operation;
|
|
111
|
+
return operation;
|
|
112
|
+
}
|
|
113
|
+
release(reasonArg = 'stop') {
|
|
114
|
+
if (this.releasePromise)
|
|
115
|
+
return this.releasePromise;
|
|
116
|
+
if (!this.namedMutexLease
|
|
117
|
+
&& !this.lockHandle
|
|
118
|
+
&& !this.anchorDirectoryHandle
|
|
119
|
+
&& !this.directoryHandle)
|
|
120
|
+
return Promise.resolve();
|
|
121
|
+
const operation = this.releaseInternal(reasonArg).catch((error) => {
|
|
122
|
+
throw normalizeOwnershipError(error);
|
|
123
|
+
}).finally(() => {
|
|
124
|
+
if (this.releasePromise === operation)
|
|
125
|
+
this.releasePromise = undefined;
|
|
126
|
+
});
|
|
127
|
+
this.releasePromise = operation;
|
|
128
|
+
return operation;
|
|
129
|
+
}
|
|
130
|
+
/** @internal */
|
|
131
|
+
async assertCurrentOwnership() {
|
|
132
|
+
if (!this.owned)
|
|
133
|
+
throw new BrowserRuntimeError('FENCED', 'runtime ownership is unavailable');
|
|
134
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
135
|
+
await this.assertNativeAnchorCurrent();
|
|
136
|
+
await this.assertLockPathCurrent();
|
|
137
|
+
}
|
|
138
|
+
async acquireInternal() {
|
|
139
|
+
try {
|
|
140
|
+
await this.openAndValidateRuntimeDirectory();
|
|
141
|
+
await this.openAndValidateAnchorDirectory();
|
|
142
|
+
await this.acquireNativeLease();
|
|
143
|
+
const lockKind = await this.openAndClassifyLock();
|
|
144
|
+
if (lockKind.kind === 'new') {
|
|
145
|
+
await this.adoptCreatedLockLayout();
|
|
146
|
+
}
|
|
147
|
+
else if (lockKind.kind === 'legacy') {
|
|
148
|
+
await this.adoptLegacyLock();
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
await this.reclaimMetadataGeneration(lockKind.metadata);
|
|
152
|
+
}
|
|
153
|
+
const generation = await this.publishGeneration();
|
|
154
|
+
return { ...generation };
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
const normalized = normalizeOwnershipError(error);
|
|
158
|
+
if (this.metadata?.state === 'active' && this.generation) {
|
|
159
|
+
try {
|
|
160
|
+
await this.release('startup-failure');
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
throw new BrowserRuntimeError('FENCED', 'runtime startup cleanup is incomplete');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
if (this.createdLock) {
|
|
168
|
+
let cleanupError;
|
|
169
|
+
try {
|
|
170
|
+
await this.unlinkCreatedLock();
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
cleanupError = error;
|
|
174
|
+
}
|
|
175
|
+
if (cleanupError && this.createdLock) {
|
|
176
|
+
throw new BrowserRuntimeError('FENCED', 'runtime startup cleanup is incomplete');
|
|
177
|
+
}
|
|
178
|
+
if (cleanupError) {
|
|
179
|
+
await this.closePassiveHandles().catch(() => {
|
|
180
|
+
throw new BrowserRuntimeError('FENCED', 'runtime startup cleanup is incomplete');
|
|
181
|
+
});
|
|
182
|
+
throw new BrowserRuntimeError('FENCED', 'runtime startup cleanup is incomplete');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
await this.closePassiveHandles();
|
|
186
|
+
}
|
|
187
|
+
throw normalized;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
async openAndValidateRuntimeDirectory() {
|
|
191
|
+
const createdPath = await plugins.fsPromises.mkdir(this.runtimeDirectory, {
|
|
192
|
+
recursive: true,
|
|
193
|
+
mode: 0o700,
|
|
194
|
+
});
|
|
195
|
+
let pathStat = await plugins.fsPromises.lstat(this.runtimeDirectory, { bigint: true });
|
|
196
|
+
if (!pathStat.isDirectory()
|
|
197
|
+
|| pathStat.isSymbolicLink()
|
|
198
|
+
|| pathStat.uid !== BigInt(this.uid))
|
|
199
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory is not private');
|
|
200
|
+
if (createdPath !== undefined) {
|
|
201
|
+
await plugins.fsPromises.chmod(this.runtimeDirectory, 0o700);
|
|
202
|
+
pathStat = await plugins.fsPromises.lstat(this.runtimeDirectory, { bigint: true });
|
|
203
|
+
}
|
|
204
|
+
if ((pathStat.mode & 511n) !== 448n) {
|
|
205
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory is not private');
|
|
206
|
+
}
|
|
207
|
+
const realPath = await plugins.fsPromises.realpath(this.runtimeDirectory);
|
|
208
|
+
if (realPath !== this.runtimeDirectory) {
|
|
209
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory path is not canonical');
|
|
210
|
+
}
|
|
211
|
+
const handle = await plugins.fsPromises.open(this.runtimeDirectory, plugins.fs.constants.O_RDONLY
|
|
212
|
+
| plugins.fs.constants.O_DIRECTORY
|
|
213
|
+
| plugins.fs.constants.O_NOFOLLOW);
|
|
214
|
+
this.directoryHandle = handle;
|
|
215
|
+
let handleStat = await handle.stat({ bigint: true });
|
|
216
|
+
if (createdPath !== undefined) {
|
|
217
|
+
await handle.chmod(0o700);
|
|
218
|
+
await plugins.fsPromises.chmod(this.runtimeDirectory, 0o700);
|
|
219
|
+
[pathStat, handleStat] = await Promise.all([
|
|
220
|
+
plugins.fsPromises.lstat(this.runtimeDirectory, { bigint: true }),
|
|
221
|
+
handle.stat({ bigint: true }),
|
|
222
|
+
]);
|
|
223
|
+
}
|
|
224
|
+
if (!handleStat.isDirectory()
|
|
225
|
+
|| handleStat.uid !== BigInt(this.uid)
|
|
226
|
+
|| (handleStat.mode & 511n) !== 448n
|
|
227
|
+
|| !statIdentityMatches(pathStat, handleStat)) {
|
|
228
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory identity changed');
|
|
229
|
+
}
|
|
230
|
+
const pathHash = plugins.crypto.createHash('sha256')
|
|
231
|
+
.update(this.runtimeDirectory, 'utf8')
|
|
232
|
+
.digest('hex');
|
|
233
|
+
const device = String(handleStat.dev);
|
|
234
|
+
const inode = String(handleStat.ino);
|
|
235
|
+
const hash = plugins.crypto.createHash('sha256')
|
|
236
|
+
.update(`${this.uid}\0${device}\0${inode}\0${pathHash}`, 'utf8')
|
|
237
|
+
.digest('hex');
|
|
238
|
+
this.directoryIdentity = { uid: this.uid, device, inode, hash };
|
|
239
|
+
}
|
|
240
|
+
async openAndValidateAnchorDirectory() {
|
|
241
|
+
await this.ensurePrivateDirectory(this.anchorDirectory, this.runtimeDirectory);
|
|
242
|
+
const handle = await plugins.fsPromises.open(this.anchorDirectory, plugins.fs.constants.O_RDONLY
|
|
243
|
+
| plugins.fs.constants.O_DIRECTORY
|
|
244
|
+
| plugins.fs.constants.O_NOFOLLOW);
|
|
245
|
+
try {
|
|
246
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
247
|
+
plugins.fsPromises.lstat(this.anchorDirectory, { bigint: true }),
|
|
248
|
+
handle.stat({ bigint: true }),
|
|
249
|
+
]);
|
|
250
|
+
if (!pathStat.isDirectory()
|
|
251
|
+
|| pathStat.isSymbolicLink()
|
|
252
|
+
|| pathStat.uid !== BigInt(this.uid)
|
|
253
|
+
|| (pathStat.mode & 511n) !== 448n
|
|
254
|
+
|| !statIdentityMatches(pathStat, handleStat)
|
|
255
|
+
|| String(handleStat.dev) !== this.directoryIdentity.device)
|
|
256
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor directory is unsafe');
|
|
257
|
+
this.anchorDirectoryHandle = handle;
|
|
258
|
+
this.anchorDirectoryIdentity = handleStat;
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
await handle.close().catch(() => undefined);
|
|
262
|
+
throw error;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async acquireNativeLease() {
|
|
266
|
+
const identity = this.directoryIdentity;
|
|
267
|
+
let mutex;
|
|
268
|
+
try {
|
|
269
|
+
mutex = new plugins.smartipc.NamedMutex(`@modelprofile.com/browser-runtime:v1:${identity.uid}:${identity.device}:${identity.inode}:${identity.hash}`, { directoryPath: this.anchorDirectory });
|
|
270
|
+
}
|
|
271
|
+
catch {
|
|
272
|
+
throw new BrowserRuntimeError('FENCED', 'runtime native ownership backend is unavailable');
|
|
273
|
+
}
|
|
274
|
+
await this.ensurePermanentAnchor(mutex.anchorPath);
|
|
275
|
+
let lease;
|
|
276
|
+
try {
|
|
277
|
+
lease = await mutex.tryAcquire();
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
throw new BrowserRuntimeError('FENCED', 'runtime native ownership could not be verified');
|
|
281
|
+
}
|
|
282
|
+
if (!lease)
|
|
283
|
+
throw new BrowserRuntimeError('LOCKED');
|
|
284
|
+
this.namedMutex = mutex;
|
|
285
|
+
this.namedMutexLease = lease;
|
|
286
|
+
this.leaseLost = false;
|
|
287
|
+
this.anchorIdentity = await plugins.fsPromises.lstat(mutex.anchorPath, { bigint: true });
|
|
288
|
+
await this.assertNativeAnchorCurrent();
|
|
289
|
+
}
|
|
290
|
+
async ensurePermanentAnchor(anchorPathArg) {
|
|
291
|
+
if (!this.pathIsWithin(this.anchorDirectory, anchorPathArg)
|
|
292
|
+
|| plugins.path.dirname(anchorPathArg) !== this.anchorDirectory)
|
|
293
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor escaped containment');
|
|
294
|
+
const anchorName = plugins.path.basename(anchorPathArg);
|
|
295
|
+
const entries = await plugins.fsPromises.readdir(this.anchorDirectory, { withFileTypes: true });
|
|
296
|
+
if (entries.some((entry) => entry.name !== anchorName)) {
|
|
297
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor directory is ambiguous');
|
|
298
|
+
}
|
|
299
|
+
let handle;
|
|
300
|
+
try {
|
|
301
|
+
handle = await plugins.fsPromises.open(anchorPathArg, plugins.fs.constants.O_RDWR
|
|
302
|
+
| plugins.fs.constants.O_CREAT
|
|
303
|
+
| plugins.fs.constants.O_EXCL
|
|
304
|
+
| plugins.fs.constants.O_NOFOLLOW, 0o600);
|
|
305
|
+
}
|
|
306
|
+
catch (error) {
|
|
307
|
+
if (error.code !== 'EEXIST')
|
|
308
|
+
throw error;
|
|
309
|
+
for (let attempt = 0; attempt < anchorInitializationAttempts; attempt += 1) {
|
|
310
|
+
const stat = await plugins.fsPromises.lstat(anchorPathArg, { bigint: true });
|
|
311
|
+
if (stat.isFile()
|
|
312
|
+
&& !stat.isSymbolicLink()
|
|
313
|
+
&& stat.uid === BigInt(this.uid)
|
|
314
|
+
&& stat.nlink === 1n
|
|
315
|
+
&& stat.size === 0n
|
|
316
|
+
&& String(stat.dev) === this.directoryIdentity.device) {
|
|
317
|
+
const mode = stat.mode & 511n;
|
|
318
|
+
if (mode === 384n)
|
|
319
|
+
return;
|
|
320
|
+
if ((mode & 127n) !== 0n) {
|
|
321
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor is unsafe');
|
|
322
|
+
}
|
|
323
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor is unsafe');
|
|
327
|
+
}
|
|
328
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor initialization is incomplete');
|
|
329
|
+
}
|
|
330
|
+
try {
|
|
331
|
+
await handle.chmod(0o600);
|
|
332
|
+
await plugins.fsPromises.chmod(anchorPathArg, 0o600);
|
|
333
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
334
|
+
plugins.fsPromises.lstat(anchorPathArg, { bigint: true }),
|
|
335
|
+
handle.stat({ bigint: true }),
|
|
336
|
+
]);
|
|
337
|
+
this.assertPrivateAnchorStat(pathStat);
|
|
338
|
+
this.assertPrivateAnchorStat(handleStat);
|
|
339
|
+
if (!statIdentityMatches(pathStat, handleStat)) {
|
|
340
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor identity changed');
|
|
341
|
+
}
|
|
342
|
+
await handle.sync();
|
|
343
|
+
await this.anchorDirectoryHandle.sync();
|
|
344
|
+
}
|
|
345
|
+
finally {
|
|
346
|
+
await handle.close();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
assertPrivateAnchorStat(statArg) {
|
|
350
|
+
if (!statArg.isFile()
|
|
351
|
+
|| statArg.isSymbolicLink()
|
|
352
|
+
|| statArg.uid !== BigInt(this.uid)
|
|
353
|
+
|| (statArg.mode & 511n) !== 384n
|
|
354
|
+
|| statArg.nlink !== 1n
|
|
355
|
+
|| statArg.size !== 0n
|
|
356
|
+
|| String(statArg.dev) !== this.directoryIdentity.device)
|
|
357
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor is unsafe');
|
|
358
|
+
}
|
|
359
|
+
async assertNativeAnchorCurrent() {
|
|
360
|
+
if (!this.namedMutex
|
|
361
|
+
|| !this.namedMutexLease
|
|
362
|
+
|| this.namedMutexLease.state !== 'active'
|
|
363
|
+
|| !this.anchorIdentity
|
|
364
|
+
|| !this.anchorDirectoryHandle
|
|
365
|
+
|| !this.anchorDirectoryIdentity
|
|
366
|
+
|| this.leaseLost)
|
|
367
|
+
throw new BrowserRuntimeError('FENCED', 'runtime native ownership is unavailable');
|
|
368
|
+
const [directoryPathStat, directoryHandleStat, anchorStat] = await Promise.all([
|
|
369
|
+
plugins.fsPromises.lstat(this.anchorDirectory, { bigint: true }),
|
|
370
|
+
this.anchorDirectoryHandle.stat({ bigint: true }),
|
|
371
|
+
plugins.fsPromises.lstat(this.namedMutex.anchorPath, { bigint: true }),
|
|
372
|
+
]);
|
|
373
|
+
if (!directoryPathStat.isDirectory()
|
|
374
|
+
|| directoryPathStat.isSymbolicLink()
|
|
375
|
+
|| directoryPathStat.uid !== BigInt(this.uid)
|
|
376
|
+
|| (directoryPathStat.mode & 511n) !== 448n
|
|
377
|
+
|| !statIdentityMatches(this.anchorDirectoryIdentity, directoryPathStat)
|
|
378
|
+
|| !statIdentityMatches(this.anchorDirectoryIdentity, directoryHandleStat)
|
|
379
|
+
|| String(directoryHandleStat.dev) !== this.directoryIdentity.device)
|
|
380
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor directory identity changed');
|
|
381
|
+
this.assertPrivateAnchorStat(anchorStat);
|
|
382
|
+
if (!statIdentityMatches(this.anchorIdentity, anchorStat)) {
|
|
383
|
+
throw new BrowserRuntimeError('FENCED', 'runtime mutex anchor identity changed');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/** @internal */
|
|
387
|
+
async forceOwnershipLossForTesting() {
|
|
388
|
+
if (!this.namedMutexLease || this.leaseLost)
|
|
389
|
+
return;
|
|
390
|
+
let releaseError;
|
|
391
|
+
try {
|
|
392
|
+
await this.namedMutexLease.release();
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
releaseError = error;
|
|
396
|
+
}
|
|
397
|
+
this.leaseLost = true;
|
|
398
|
+
this.onOwnershipLost?.();
|
|
399
|
+
if (releaseError) {
|
|
400
|
+
throw new BrowserRuntimeError('FENCED', 'runtime native ownership release is uncertain');
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
async openAndClassifyLock() {
|
|
404
|
+
let handle;
|
|
405
|
+
try {
|
|
406
|
+
handle = await plugins.fsPromises.open(this.lockPath, plugins.fs.constants.O_RDWR
|
|
407
|
+
| plugins.fs.constants.O_CREAT
|
|
408
|
+
| plugins.fs.constants.O_EXCL
|
|
409
|
+
| plugins.fs.constants.O_NOFOLLOW, 0o600);
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
if (error.code !== 'EEXIST')
|
|
413
|
+
throw error;
|
|
414
|
+
const pathStat = await this.readPrivateLockPathStat();
|
|
415
|
+
handle = await plugins.fsPromises.open(this.lockPath, plugins.fs.constants.O_RDWR | plugins.fs.constants.O_NOFOLLOW);
|
|
416
|
+
let handleStat;
|
|
417
|
+
try {
|
|
418
|
+
handleStat = await this.validateLockHandle(handle);
|
|
419
|
+
}
|
|
420
|
+
catch (validationError) {
|
|
421
|
+
await handle.close().catch(() => undefined);
|
|
422
|
+
throw validationError;
|
|
423
|
+
}
|
|
424
|
+
if (!statIdentityMatches(pathStat, handleStat)) {
|
|
425
|
+
await handle.close();
|
|
426
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock identity changed');
|
|
427
|
+
}
|
|
428
|
+
this.lockHandle = handle;
|
|
429
|
+
this.lockIdentity = handleStat;
|
|
430
|
+
await this.assertLockPathCurrent();
|
|
431
|
+
if (handleStat.size === 0n)
|
|
432
|
+
return { kind: 'legacy' };
|
|
433
|
+
return { kind: 'metadata', metadata: await this.readMetadata() };
|
|
434
|
+
}
|
|
435
|
+
this.lockHandle = handle;
|
|
436
|
+
this.createdLock = true;
|
|
437
|
+
await handle.chmod(0o600);
|
|
438
|
+
const initialHandleStat = await handle.stat({ bigint: true });
|
|
439
|
+
const initialPathStat = await plugins.fsPromises.lstat(this.lockPath, { bigint: true });
|
|
440
|
+
if (!initialHandleStat.isFile()
|
|
441
|
+
|| !initialPathStat.isFile()
|
|
442
|
+
|| initialPathStat.isSymbolicLink()
|
|
443
|
+
|| !statIdentityMatches(initialHandleStat, initialPathStat)
|
|
444
|
+
|| initialHandleStat.uid !== BigInt(this.uid)
|
|
445
|
+
|| initialHandleStat.nlink !== 1n
|
|
446
|
+
|| initialHandleStat.size !== 0n)
|
|
447
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock is unsafe');
|
|
448
|
+
this.lockIdentity = initialHandleStat;
|
|
449
|
+
await plugins.fsPromises.chmod(this.lockPath, 0o600);
|
|
450
|
+
await handle.chmod(0o600);
|
|
451
|
+
this.lockIdentity = await this.validateLockHandle(handle);
|
|
452
|
+
await this.assertLockPathCurrent();
|
|
453
|
+
await this.directoryHandle.sync();
|
|
454
|
+
return { kind: 'new' };
|
|
455
|
+
}
|
|
456
|
+
async readPrivateLockPathStat() {
|
|
457
|
+
let stat;
|
|
458
|
+
try {
|
|
459
|
+
stat = await plugins.fsPromises.lstat(this.lockPath, { bigint: true });
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
if (isMissingError(error)) {
|
|
463
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock identity changed');
|
|
464
|
+
}
|
|
465
|
+
throw error;
|
|
466
|
+
}
|
|
467
|
+
this.assertPrivateLockStat(stat);
|
|
468
|
+
return stat;
|
|
469
|
+
}
|
|
470
|
+
async validateLockHandle(handleArg) {
|
|
471
|
+
const stat = await handleArg.stat({ bigint: true });
|
|
472
|
+
this.assertPrivateLockStat(stat);
|
|
473
|
+
return stat;
|
|
474
|
+
}
|
|
475
|
+
assertPrivateLockStat(statArg) {
|
|
476
|
+
if (!statArg.isFile()
|
|
477
|
+
|| statArg.isSymbolicLink()
|
|
478
|
+
|| statArg.uid !== BigInt(this.uid)
|
|
479
|
+
|| (statArg.mode & 511n) !== 384n
|
|
480
|
+
|| statArg.nlink !== 1n
|
|
481
|
+
|| statArg.size > BigInt(maximumMetadataBytes))
|
|
482
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock is unsafe');
|
|
483
|
+
}
|
|
484
|
+
async assertLockPathCurrent() {
|
|
485
|
+
if (!this.lockHandle || !this.lockIdentity) {
|
|
486
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock is unavailable');
|
|
487
|
+
}
|
|
488
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
489
|
+
this.readPrivateLockPathStat(),
|
|
490
|
+
this.validateLockHandle(this.lockHandle),
|
|
491
|
+
]);
|
|
492
|
+
if (!statIdentityMatches(this.lockIdentity, pathStat)
|
|
493
|
+
|| !statIdentityMatches(this.lockIdentity, handleStat))
|
|
494
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock identity changed');
|
|
495
|
+
}
|
|
496
|
+
async adoptCreatedLockLayout() {
|
|
497
|
+
await this.assertRuntimeEntries(new Set([
|
|
498
|
+
'runtime.lock',
|
|
499
|
+
'runtime.mutex',
|
|
500
|
+
'profiles',
|
|
501
|
+
'artifacts',
|
|
502
|
+
]));
|
|
503
|
+
await this.validateOptionalPrivateDirectory(this.legacyProfileRoot);
|
|
504
|
+
await this.validateOptionalPrivateDirectory(this.legacyArtifactRoot);
|
|
505
|
+
const [profileInspection, profileEntries, artifactEntries] = await Promise.all([
|
|
506
|
+
this.inspectProfileProcesses([this.legacyProfileRoot]),
|
|
507
|
+
this.readOptionalDirectoryEntries(this.legacyProfileRoot),
|
|
508
|
+
this.readOptionalDirectoryEntries(this.legacyArtifactRoot),
|
|
509
|
+
]);
|
|
510
|
+
if (profileInspection === 'held')
|
|
511
|
+
throw new BrowserRuntimeError('LOCKED');
|
|
512
|
+
if (profileInspection === 'indeterminate') {
|
|
513
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime process ownership is indeterminate');
|
|
514
|
+
}
|
|
515
|
+
if (profileEntries.length > 0 || artifactEntries.length > 0) {
|
|
516
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime roots are not empty');
|
|
517
|
+
}
|
|
518
|
+
await this.assertLockPathCurrent();
|
|
519
|
+
await this.assertNativeAnchorCurrent();
|
|
520
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
521
|
+
const [currentProfileEntries, currentArtifactEntries] = await Promise.all([
|
|
522
|
+
this.readOptionalDirectoryEntries(this.legacyProfileRoot),
|
|
523
|
+
this.readOptionalDirectoryEntries(this.legacyArtifactRoot),
|
|
524
|
+
]);
|
|
525
|
+
if (currentProfileEntries.length > 0 || currentArtifactEntries.length > 0) {
|
|
526
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime roots changed');
|
|
527
|
+
}
|
|
528
|
+
await this.removePrivateDirectory(this.legacyProfileRoot);
|
|
529
|
+
await this.removePrivateDirectory(this.legacyArtifactRoot);
|
|
530
|
+
await this.directoryHandle.sync();
|
|
531
|
+
}
|
|
532
|
+
async unlinkCreatedLock() {
|
|
533
|
+
if (!this.createdLock)
|
|
534
|
+
return;
|
|
535
|
+
if (!this.lockHandle) {
|
|
536
|
+
throw new BrowserRuntimeError('FENCED', 'created runtime lock is unavailable');
|
|
537
|
+
}
|
|
538
|
+
await this.assertNativeAnchorCurrent();
|
|
539
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
540
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
541
|
+
plugins.fsPromises.lstat(this.lockPath, { bigint: true }),
|
|
542
|
+
this.lockHandle.stat({ bigint: true }),
|
|
543
|
+
]);
|
|
544
|
+
if (!pathStat.isFile()
|
|
545
|
+
|| pathStat.isSymbolicLink()
|
|
546
|
+
|| !handleStat.isFile()
|
|
547
|
+
|| pathStat.uid !== BigInt(this.uid)
|
|
548
|
+
|| handleStat.uid !== BigInt(this.uid)
|
|
549
|
+
|| pathStat.nlink !== 1n
|
|
550
|
+
|| handleStat.nlink !== 1n
|
|
551
|
+
|| String(pathStat.dev) !== this.directoryIdentity.device
|
|
552
|
+
|| !statIdentityMatches(pathStat, handleStat)
|
|
553
|
+
|| (this.lockIdentity && !statIdentityMatches(this.lockIdentity, handleStat)))
|
|
554
|
+
throw new BrowserRuntimeError('FENCED', 'created runtime lock identity changed');
|
|
555
|
+
await plugins.fsPromises.unlink(this.lockPath);
|
|
556
|
+
this.createdLock = false;
|
|
557
|
+
await this.directoryHandle.sync();
|
|
558
|
+
}
|
|
559
|
+
async adoptLegacyLock() {
|
|
560
|
+
await this.assertRuntimeEntries(new Set([
|
|
561
|
+
'runtime.lock',
|
|
562
|
+
'runtime.mutex',
|
|
563
|
+
'profiles',
|
|
564
|
+
'artifacts',
|
|
565
|
+
]));
|
|
566
|
+
await this.validateOptionalPrivateDirectory(this.legacyProfileRoot);
|
|
567
|
+
await this.validateOptionalPrivateDirectory(this.legacyArtifactRoot);
|
|
568
|
+
const [descriptorInspection, profileInspection, predatesBoot] = await Promise.all([
|
|
569
|
+
this.inspectLegacyLockDescriptors(),
|
|
570
|
+
this.inspectProfileProcesses([this.legacyProfileRoot]),
|
|
571
|
+
this.legacyLockPredatesCurrentBoot(),
|
|
572
|
+
]);
|
|
573
|
+
if (descriptorInspection === 'held' || profileInspection === 'held') {
|
|
574
|
+
throw new BrowserRuntimeError('LOCKED');
|
|
575
|
+
}
|
|
576
|
+
if (descriptorInspection === 'indeterminate' || profileInspection === 'indeterminate') {
|
|
577
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime ownership is indeterminate');
|
|
578
|
+
}
|
|
579
|
+
if (!predatesBoot) {
|
|
580
|
+
throw new BrowserRuntimeError('FENCED', 'same-boot legacy runtime ownership is unsafe');
|
|
581
|
+
}
|
|
582
|
+
await this.assertLockPathCurrent();
|
|
583
|
+
if (!await this.legacyLockPredatesCurrentBoot()) {
|
|
584
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime lock timestamp changed');
|
|
585
|
+
}
|
|
586
|
+
await this.assertNativeAnchorCurrent();
|
|
587
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
588
|
+
await this.removePrivateDirectory(this.legacyProfileRoot);
|
|
589
|
+
await this.removePrivateDirectory(this.legacyArtifactRoot);
|
|
590
|
+
await this.directoryHandle.sync();
|
|
591
|
+
}
|
|
592
|
+
async legacyLockPredatesCurrentBoot() {
|
|
593
|
+
const bootTimeNs = await this.readBootTimeNs();
|
|
594
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
595
|
+
this.readPrivateLockPathStat(),
|
|
596
|
+
this.validateLockHandle(this.lockHandle),
|
|
597
|
+
]);
|
|
598
|
+
if (!statIdentityMatches(this.lockIdentity, pathStat)
|
|
599
|
+
|| !statIdentityMatches(this.lockIdentity, handleStat))
|
|
600
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime lock identity changed');
|
|
601
|
+
const pathTimestamps = [pathStat.birthtimeNs, pathStat.ctimeNs, pathStat.mtimeNs];
|
|
602
|
+
const handleTimestamps = [handleStat.birthtimeNs, handleStat.ctimeNs, handleStat.mtimeNs];
|
|
603
|
+
if (pathTimestamps.some((timestamp, index) => (timestamp <= 0n || timestamp !== handleTimestamps[index])))
|
|
604
|
+
throw new BrowserRuntimeError('FENCED', 'legacy runtime lock timestamps are ambiguous');
|
|
605
|
+
return pathTimestamps.every((timestamp) => timestamp < bootTimeNs);
|
|
606
|
+
}
|
|
607
|
+
async readBootTimeNs() {
|
|
608
|
+
const procStat = await this.readBoundedFile(plugins.path.join(this.procRoot, 'stat'), maximumProcFileBytes);
|
|
609
|
+
if (/\u0000|\r/u.test(procStat)) {
|
|
610
|
+
throw new BrowserRuntimeError('FENCED', 'boot timestamp is malformed');
|
|
611
|
+
}
|
|
612
|
+
const bootTimeLines = procStat.split('\n').filter((line) => line.startsWith('btime'));
|
|
613
|
+
if (bootTimeLines.length !== 1) {
|
|
614
|
+
throw new BrowserRuntimeError('FENCED', 'boot timestamp is malformed');
|
|
615
|
+
}
|
|
616
|
+
const match = /^btime ([1-9][0-9]{0,19})$/u.exec(bootTimeLines[0]);
|
|
617
|
+
if (!match)
|
|
618
|
+
throw new BrowserRuntimeError('FENCED', 'boot timestamp is malformed');
|
|
619
|
+
const bootTimeNs = BigInt(match[1]) * 1000000000n;
|
|
620
|
+
const now = this.now();
|
|
621
|
+
if (!Number.isSafeInteger(now) || now < 0) {
|
|
622
|
+
throw new BrowserRuntimeError('FENCED', 'current timestamp is unavailable');
|
|
623
|
+
}
|
|
624
|
+
const nowNs = BigInt(now) * 1000000n;
|
|
625
|
+
if (bootTimeNs > nowNs) {
|
|
626
|
+
throw new BrowserRuntimeError('FENCED', 'boot timestamp is in the future');
|
|
627
|
+
}
|
|
628
|
+
return bootTimeNs;
|
|
629
|
+
}
|
|
630
|
+
async reclaimMetadataGeneration(metadataArg) {
|
|
631
|
+
this.assertMetadataDirectoryIdentity(metadataArg);
|
|
632
|
+
await this.assertRuntimeEntries(new Set([
|
|
633
|
+
'runtime.lock',
|
|
634
|
+
'runtime.mutex',
|
|
635
|
+
'generations',
|
|
636
|
+
]));
|
|
637
|
+
await this.validateOptionalPrivateDirectory(this.generationsRoot);
|
|
638
|
+
const generationDirectory = this.generationPath(metadataArg.generationId);
|
|
639
|
+
const entries = await this.readOptionalDirectoryEntries(this.generationsRoot);
|
|
640
|
+
if (entries.some((entry) => entry.name !== metadataArg.generationId)) {
|
|
641
|
+
throw new BrowserRuntimeError('FENCED', 'runtime generation ownership is ambiguous');
|
|
642
|
+
}
|
|
643
|
+
await this.validateOptionalGeneration(generationDirectory);
|
|
644
|
+
const profileInspection = await this.inspectProfileProcesses([
|
|
645
|
+
plugins.path.join(generationDirectory, 'profiles'),
|
|
646
|
+
]);
|
|
647
|
+
if (profileInspection === 'held')
|
|
648
|
+
throw new BrowserRuntimeError('LOCKED');
|
|
649
|
+
if (profileInspection === 'indeterminate') {
|
|
650
|
+
throw new BrowserRuntimeError('FENCED', 'runtime process ownership is indeterminate');
|
|
651
|
+
}
|
|
652
|
+
await this.assertLockPathCurrent();
|
|
653
|
+
await this.assertNativeAnchorCurrent();
|
|
654
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
655
|
+
await this.beforeGenerationRemoval?.('stale');
|
|
656
|
+
await this.assertLockPathCurrent();
|
|
657
|
+
await this.assertNativeAnchorCurrent();
|
|
658
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
659
|
+
await this.removePrivateDirectory(generationDirectory);
|
|
660
|
+
await this.syncOptionalDirectory(this.generationsRoot);
|
|
661
|
+
}
|
|
662
|
+
async publishGeneration() {
|
|
663
|
+
const identity = this.directoryIdentity;
|
|
664
|
+
const generationId = randomId(24);
|
|
665
|
+
const metadata = {
|
|
666
|
+
schema: metadataSchema,
|
|
667
|
+
state: 'active',
|
|
668
|
+
uid: this.uid,
|
|
669
|
+
runtimeDirectoryDevice: identity.device,
|
|
670
|
+
runtimeDirectoryInode: identity.inode,
|
|
671
|
+
runtimeDirectoryHash: identity.hash,
|
|
672
|
+
bootId: await this.readBootId(),
|
|
673
|
+
pid: process.pid,
|
|
674
|
+
processStartTicks: parseProcessStartTicks(await this.readBoundedFile(plugins.path.join(this.procRoot, 'self', 'stat'), 16 * 1024), process.pid),
|
|
675
|
+
generationId,
|
|
676
|
+
nonce: randomId(24),
|
|
677
|
+
};
|
|
678
|
+
const generationDirectory = this.generationPath(generationId);
|
|
679
|
+
const generation = {
|
|
680
|
+
generationId,
|
|
681
|
+
generationDirectory,
|
|
682
|
+
profileRoot: plugins.path.join(generationDirectory, 'profiles'),
|
|
683
|
+
artifactRoot: plugins.path.join(generationDirectory, 'artifacts'),
|
|
684
|
+
};
|
|
685
|
+
await this.beforeMetadataPublication?.();
|
|
686
|
+
await this.writeMetadata(metadata);
|
|
687
|
+
this.metadata = metadata;
|
|
688
|
+
this.generation = generation;
|
|
689
|
+
await this.ensurePrivateDirectory(this.generationsRoot, this.runtimeDirectory);
|
|
690
|
+
const entries = await this.readOptionalDirectoryEntries(this.generationsRoot);
|
|
691
|
+
if (entries.length > 0) {
|
|
692
|
+
throw new BrowserRuntimeError('FENCED', 'runtime generation root is not empty');
|
|
693
|
+
}
|
|
694
|
+
await this.ensurePrivateDirectory(generationDirectory, this.generationsRoot);
|
|
695
|
+
await this.syncOptionalDirectory(this.generationsRoot);
|
|
696
|
+
return generation;
|
|
697
|
+
}
|
|
698
|
+
async releaseInternal(reasonArg) {
|
|
699
|
+
if (this.leaseLost) {
|
|
700
|
+
await this.closePassiveHandles().catch(() => undefined);
|
|
701
|
+
throw new BrowserRuntimeError('FENCED', 'runtime ownership was lost');
|
|
702
|
+
}
|
|
703
|
+
if (this.createdLock) {
|
|
704
|
+
await this.unlinkCreatedLock();
|
|
705
|
+
await this.closeOwnershipHandles();
|
|
706
|
+
this.metadata = undefined;
|
|
707
|
+
this.generation = undefined;
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
const metadata = this.metadata;
|
|
711
|
+
const generation = this.generation;
|
|
712
|
+
if (metadata?.state === 'active' && generation) {
|
|
713
|
+
await this.assertLockPathCurrent();
|
|
714
|
+
await this.assertNativeAnchorCurrent();
|
|
715
|
+
await this.validateOptionalGeneration(generation.generationDirectory);
|
|
716
|
+
await this.assertLockPathCurrent();
|
|
717
|
+
await this.assertNativeAnchorCurrent();
|
|
718
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
719
|
+
await this.beforeGenerationRemoval?.(reasonArg);
|
|
720
|
+
await this.assertLockPathCurrent();
|
|
721
|
+
await this.assertNativeAnchorCurrent();
|
|
722
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
723
|
+
await this.removePrivateDirectory(generation.generationDirectory);
|
|
724
|
+
await this.syncOptionalDirectory(this.generationsRoot);
|
|
725
|
+
const relinquished = { ...metadata, state: 'relinquished' };
|
|
726
|
+
await this.writeMetadata(relinquished);
|
|
727
|
+
this.metadata = relinquished;
|
|
728
|
+
}
|
|
729
|
+
await this.closeOwnershipHandles();
|
|
730
|
+
this.metadata = undefined;
|
|
731
|
+
this.generation = undefined;
|
|
732
|
+
}
|
|
733
|
+
async closePassiveHandles() {
|
|
734
|
+
try {
|
|
735
|
+
await this.closeOwnershipHandles();
|
|
736
|
+
}
|
|
737
|
+
finally {
|
|
738
|
+
this.metadata = undefined;
|
|
739
|
+
this.generation = undefined;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
async closeOwnershipHandles() {
|
|
743
|
+
const errors = [];
|
|
744
|
+
if (this.lockHandle) {
|
|
745
|
+
await this.lockHandle.close().then(() => {
|
|
746
|
+
this.lockHandle = undefined;
|
|
747
|
+
this.lockIdentity = undefined;
|
|
748
|
+
}, (error) => errors.push(error));
|
|
749
|
+
}
|
|
750
|
+
if (this.anchorDirectoryHandle) {
|
|
751
|
+
await this.anchorDirectoryHandle.close().then(() => {
|
|
752
|
+
this.anchorDirectoryHandle = undefined;
|
|
753
|
+
this.anchorDirectoryIdentity = undefined;
|
|
754
|
+
}, (error) => errors.push(error));
|
|
755
|
+
}
|
|
756
|
+
if (this.directoryHandle) {
|
|
757
|
+
await this.directoryHandle.close().then(() => {
|
|
758
|
+
this.directoryHandle = undefined;
|
|
759
|
+
this.directoryIdentity = undefined;
|
|
760
|
+
}, (error) => errors.push(error));
|
|
761
|
+
}
|
|
762
|
+
if (this.namedMutexLease) {
|
|
763
|
+
await this.namedMutexLease.release().then(() => {
|
|
764
|
+
this.namedMutexLease = undefined;
|
|
765
|
+
this.namedMutex = undefined;
|
|
766
|
+
this.anchorIdentity = undefined;
|
|
767
|
+
this.leaseLost = false;
|
|
768
|
+
}, (error) => errors.push(error));
|
|
769
|
+
}
|
|
770
|
+
if (errors.length > 0) {
|
|
771
|
+
throw new BrowserRuntimeError('FENCED', 'runtime ownership handles could not be closed');
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
async readMetadata() {
|
|
775
|
+
const stat = await this.lockHandle.stat({ bigint: true });
|
|
776
|
+
if (stat.size <= 0n || stat.size > BigInt(maximumMetadataBytes)) {
|
|
777
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is malformed');
|
|
778
|
+
}
|
|
779
|
+
const bytes = plugins.Buffer.alloc(Number(stat.size));
|
|
780
|
+
let bytesRead = 0;
|
|
781
|
+
while (bytesRead < bytes.byteLength) {
|
|
782
|
+
const result = await this.lockHandle.read(bytes, bytesRead, bytes.byteLength - bytesRead, bytesRead);
|
|
783
|
+
if (result.bytesRead <= 0) {
|
|
784
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is unreadable');
|
|
785
|
+
}
|
|
786
|
+
bytesRead += result.bytesRead;
|
|
787
|
+
}
|
|
788
|
+
let text;
|
|
789
|
+
try {
|
|
790
|
+
text = new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
791
|
+
}
|
|
792
|
+
catch {
|
|
793
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is malformed');
|
|
794
|
+
}
|
|
795
|
+
if (!text.endsWith('\n') || text.slice(0, -1).includes('\n')) {
|
|
796
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is malformed');
|
|
797
|
+
}
|
|
798
|
+
const serialized = text.slice(0, -1);
|
|
799
|
+
let parsed;
|
|
800
|
+
try {
|
|
801
|
+
parsed = JSON.parse(serialized);
|
|
802
|
+
}
|
|
803
|
+
catch {
|
|
804
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is malformed');
|
|
805
|
+
}
|
|
806
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
807
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is malformed');
|
|
808
|
+
}
|
|
809
|
+
const record = parsed;
|
|
810
|
+
const expectedKeys = [
|
|
811
|
+
'schema',
|
|
812
|
+
'state',
|
|
813
|
+
'uid',
|
|
814
|
+
'runtimeDirectoryDevice',
|
|
815
|
+
'runtimeDirectoryInode',
|
|
816
|
+
'runtimeDirectoryHash',
|
|
817
|
+
'bootId',
|
|
818
|
+
'pid',
|
|
819
|
+
'processStartTicks',
|
|
820
|
+
'generationId',
|
|
821
|
+
'nonce',
|
|
822
|
+
];
|
|
823
|
+
if (Object.keys(record).length !== expectedKeys.length
|
|
824
|
+
|| expectedKeys.some((key) => !(key in record))
|
|
825
|
+
|| record.schema !== metadataSchema
|
|
826
|
+
|| (record.state !== 'active' && record.state !== 'relinquished')
|
|
827
|
+
|| !Number.isSafeInteger(record.uid)
|
|
828
|
+
|| record.uid < 1
|
|
829
|
+
|| typeof record.runtimeDirectoryDevice !== 'string'
|
|
830
|
+
|| !unsignedIntegerPattern.test(record.runtimeDirectoryDevice)
|
|
831
|
+
|| typeof record.runtimeDirectoryInode !== 'string'
|
|
832
|
+
|| !unsignedIntegerPattern.test(record.runtimeDirectoryInode)
|
|
833
|
+
|| typeof record.runtimeDirectoryHash !== 'string'
|
|
834
|
+
|| !hashPattern.test(record.runtimeDirectoryHash)
|
|
835
|
+
|| typeof record.bootId !== 'string'
|
|
836
|
+
|| !bootIdPattern.test(record.bootId)
|
|
837
|
+
|| !Number.isSafeInteger(record.pid)
|
|
838
|
+
|| record.pid < 1
|
|
839
|
+
|| typeof record.processStartTicks !== 'string'
|
|
840
|
+
|| !unsignedIntegerPattern.test(record.processStartTicks)
|
|
841
|
+
|| typeof record.generationId !== 'string'
|
|
842
|
+
|| !generationIdPattern.test(record.generationId)
|
|
843
|
+
|| typeof record.nonce !== 'string'
|
|
844
|
+
|| !generationIdPattern.test(record.nonce)
|
|
845
|
+
|| JSON.stringify(record) !== serialized)
|
|
846
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata is malformed');
|
|
847
|
+
return record;
|
|
848
|
+
}
|
|
849
|
+
async writeMetadata(metadataArg) {
|
|
850
|
+
await this.assertLockPathCurrent();
|
|
851
|
+
await this.assertNativeAnchorCurrent();
|
|
852
|
+
const bytes = plugins.Buffer.from(`${JSON.stringify(metadataArg)}\n`, 'utf8');
|
|
853
|
+
if (bytes.byteLength > maximumMetadataBytes) {
|
|
854
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata exceeds its bound');
|
|
855
|
+
}
|
|
856
|
+
let written = 0;
|
|
857
|
+
while (written < bytes.byteLength) {
|
|
858
|
+
const result = await this.lockHandle.write(bytes, written, bytes.byteLength - written, written);
|
|
859
|
+
if (result.bytesWritten <= 0) {
|
|
860
|
+
throw new BrowserRuntimeError('FENCED', 'runtime lock metadata write was incomplete');
|
|
861
|
+
}
|
|
862
|
+
written += result.bytesWritten;
|
|
863
|
+
}
|
|
864
|
+
await this.lockHandle.truncate(bytes.byteLength);
|
|
865
|
+
await this.lockHandle.sync();
|
|
866
|
+
this.createdLock = false;
|
|
867
|
+
await this.assertLockPathCurrent();
|
|
868
|
+
await this.assertNativeAnchorCurrent();
|
|
869
|
+
}
|
|
870
|
+
assertMetadataDirectoryIdentity(metadataArg) {
|
|
871
|
+
const identity = this.directoryIdentity;
|
|
872
|
+
if (metadataArg.uid !== identity.uid
|
|
873
|
+
|| metadataArg.runtimeDirectoryDevice !== identity.device
|
|
874
|
+
|| metadataArg.runtimeDirectoryInode !== identity.inode
|
|
875
|
+
|| metadataArg.runtimeDirectoryHash !== identity.hash)
|
|
876
|
+
throw new BrowserRuntimeError('FENCED', 'runtime metadata belongs to another directory');
|
|
877
|
+
}
|
|
878
|
+
async inspectLegacyLockDescriptors() {
|
|
879
|
+
const lockIdentity = this.lockIdentity;
|
|
880
|
+
let indeterminate = false;
|
|
881
|
+
for (const pid of await this.listOwnedProcessIds()) {
|
|
882
|
+
let descriptorNames;
|
|
883
|
+
try {
|
|
884
|
+
descriptorNames = await plugins.fsPromises.readdir(plugins.path.join(this.procRoot, String(pid), 'fd'));
|
|
885
|
+
}
|
|
886
|
+
catch {
|
|
887
|
+
const uidState = await this.processStillExists(pid);
|
|
888
|
+
if (uidState === 'missing' || uidState === 'other')
|
|
889
|
+
continue;
|
|
890
|
+
indeterminate = true;
|
|
891
|
+
continue;
|
|
892
|
+
}
|
|
893
|
+
for (const descriptorName of descriptorNames) {
|
|
894
|
+
if (!/^(?:0|[1-9][0-9]*)$/u.test(descriptorName)) {
|
|
895
|
+
indeterminate = true;
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
const descriptor = Number(descriptorName);
|
|
899
|
+
if (pid === process.pid && descriptor === this.lockHandle.fd)
|
|
900
|
+
continue;
|
|
901
|
+
try {
|
|
902
|
+
const stat = await plugins.fsPromises.stat(plugins.path.join(this.procRoot, String(pid), 'fd', descriptorName), { bigint: true });
|
|
903
|
+
if (stat.dev === lockIdentity.dev && stat.ino === lockIdentity.ino)
|
|
904
|
+
return 'held';
|
|
905
|
+
}
|
|
906
|
+
catch (error) {
|
|
907
|
+
if (isMissingError(error))
|
|
908
|
+
continue;
|
|
909
|
+
indeterminate = true;
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return indeterminate ? 'indeterminate' : 'clear';
|
|
914
|
+
}
|
|
915
|
+
async inspectProfileProcesses(profileRootsArg) {
|
|
916
|
+
const roots = profileRootsArg.map((root) => plugins.path.resolve(root));
|
|
917
|
+
let indeterminate = false;
|
|
918
|
+
for (const pid of await this.listOwnedProcessIds()) {
|
|
919
|
+
let cmdline;
|
|
920
|
+
try {
|
|
921
|
+
cmdline = await this.readBoundedBuffer(plugins.path.join(this.procRoot, String(pid), 'cmdline'), maximumProcFileBytes);
|
|
922
|
+
}
|
|
923
|
+
catch {
|
|
924
|
+
const uidState = await this.processStillExists(pid);
|
|
925
|
+
if (uidState === 'missing' || uidState === 'other')
|
|
926
|
+
continue;
|
|
927
|
+
indeterminate = true;
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (cmdline.byteLength === 0) {
|
|
931
|
+
if (!await this.processIsZombie(pid))
|
|
932
|
+
indeterminate = true;
|
|
933
|
+
continue;
|
|
934
|
+
}
|
|
935
|
+
let cmdlineText;
|
|
936
|
+
try {
|
|
937
|
+
cmdlineText = new TextDecoder('utf-8', { fatal: true }).decode(cmdline);
|
|
938
|
+
}
|
|
939
|
+
catch {
|
|
940
|
+
indeterminate = true;
|
|
941
|
+
continue;
|
|
942
|
+
}
|
|
943
|
+
const argumentsList = cmdlineText.split('\0');
|
|
944
|
+
if (argumentsList.at(-1) === '')
|
|
945
|
+
argumentsList.pop();
|
|
946
|
+
for (let index = 0; index < argumentsList.length; index += 1) {
|
|
947
|
+
const argument = argumentsList[index];
|
|
948
|
+
let profilePath;
|
|
949
|
+
if (argument === '--user-data-dir') {
|
|
950
|
+
profilePath = argumentsList[index + 1];
|
|
951
|
+
}
|
|
952
|
+
else if (argument.startsWith('--user-data-dir=')) {
|
|
953
|
+
profilePath = argument.slice('--user-data-dir='.length);
|
|
954
|
+
}
|
|
955
|
+
if (!profilePath || !plugins.path.isAbsolute(profilePath))
|
|
956
|
+
continue;
|
|
957
|
+
const resolved = plugins.path.resolve(profilePath);
|
|
958
|
+
if (roots.some((root) => resolved === root || this.pathIsWithin(root, resolved))) {
|
|
959
|
+
return 'held';
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return indeterminate ? 'indeterminate' : 'clear';
|
|
964
|
+
}
|
|
965
|
+
async listOwnedProcessIds() {
|
|
966
|
+
let entries;
|
|
967
|
+
try {
|
|
968
|
+
entries = await plugins.fsPromises.readdir(this.procRoot, { withFileTypes: true });
|
|
969
|
+
}
|
|
970
|
+
catch {
|
|
971
|
+
throw new BrowserRuntimeError('FENCED', 'process inspection is unavailable');
|
|
972
|
+
}
|
|
973
|
+
const processIds = [];
|
|
974
|
+
let indeterminate = false;
|
|
975
|
+
for (const entry of entries) {
|
|
976
|
+
if (!/^[1-9][0-9]{0,9}$/u.test(entry.name))
|
|
977
|
+
continue;
|
|
978
|
+
const pid = Number(entry.name);
|
|
979
|
+
try {
|
|
980
|
+
const stat = await plugins.fsPromises.lstat(plugins.path.join(this.procRoot, entry.name), { bigint: true });
|
|
981
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()) {
|
|
982
|
+
indeterminate = true;
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
catch (error) {
|
|
987
|
+
if (isMissingError(error))
|
|
988
|
+
continue;
|
|
989
|
+
indeterminate = true;
|
|
990
|
+
continue;
|
|
991
|
+
}
|
|
992
|
+
const uidState = await this.processStillExists(pid);
|
|
993
|
+
if (uidState === 'same')
|
|
994
|
+
processIds.push(pid);
|
|
995
|
+
else if (uidState === 'indeterminate')
|
|
996
|
+
indeterminate = true;
|
|
997
|
+
}
|
|
998
|
+
if (indeterminate) {
|
|
999
|
+
throw new BrowserRuntimeError('FENCED', 'process inspection is indeterminate');
|
|
1000
|
+
}
|
|
1001
|
+
return processIds.sort((left, right) => {
|
|
1002
|
+
if (left === process.pid)
|
|
1003
|
+
return -1;
|
|
1004
|
+
if (right === process.pid)
|
|
1005
|
+
return 1;
|
|
1006
|
+
return left - right;
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
async processStillExists(pidArg) {
|
|
1010
|
+
const processPath = plugins.path.join(this.procRoot, String(pidArg));
|
|
1011
|
+
try {
|
|
1012
|
+
const status = await this.readBoundedFile(plugins.path.join(processPath, 'status'), 64 * 1024);
|
|
1013
|
+
return this.parseProcessUidState(status);
|
|
1014
|
+
}
|
|
1015
|
+
catch (error) {
|
|
1016
|
+
if (!isMissingError(error))
|
|
1017
|
+
return 'indeterminate';
|
|
1018
|
+
try {
|
|
1019
|
+
await plugins.fsPromises.lstat(processPath, { bigint: true });
|
|
1020
|
+
return 'indeterminate';
|
|
1021
|
+
}
|
|
1022
|
+
catch (statError) {
|
|
1023
|
+
return isMissingError(statError) ? 'missing' : 'indeterminate';
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
parseProcessUidState(statusArg) {
|
|
1028
|
+
if (/\u0000|\r/u.test(statusArg))
|
|
1029
|
+
return 'indeterminate';
|
|
1030
|
+
const uidLines = statusArg.split('\n').filter((line) => line.startsWith('Uid:'));
|
|
1031
|
+
if (uidLines.length !== 1)
|
|
1032
|
+
return 'indeterminate';
|
|
1033
|
+
const match = /^Uid:\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*$/u.exec(uidLines[0]);
|
|
1034
|
+
if (!match)
|
|
1035
|
+
return 'indeterminate';
|
|
1036
|
+
const ids = match.slice(1).map(Number);
|
|
1037
|
+
if (ids.some((id) => !Number.isSafeInteger(id) || id < 0 || id > 0xffff_ffff)) {
|
|
1038
|
+
return 'indeterminate';
|
|
1039
|
+
}
|
|
1040
|
+
if (ids.every((id) => id === this.uid))
|
|
1041
|
+
return 'same';
|
|
1042
|
+
if (ids.every((id) => id !== this.uid))
|
|
1043
|
+
return 'other';
|
|
1044
|
+
return 'indeterminate';
|
|
1045
|
+
}
|
|
1046
|
+
async processIsZombie(pidArg) {
|
|
1047
|
+
try {
|
|
1048
|
+
const status = await this.readBoundedFile(plugins.path.join(this.procRoot, String(pidArg), 'status'), 64 * 1024);
|
|
1049
|
+
return /^State:\s+Z(?:\s|$)/mu.test(status);
|
|
1050
|
+
}
|
|
1051
|
+
catch {
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
async readBootId() {
|
|
1056
|
+
const rawBootId = await this.readBoundedFile(plugins.path.join(this.procRoot, 'sys', 'kernel', 'random', 'boot_id'), 64);
|
|
1057
|
+
const bootId = rawBootId.endsWith('\n') ? rawBootId.slice(0, -1) : rawBootId;
|
|
1058
|
+
if (bootId.includes('\n') || bootId.includes('\r') || !bootIdPattern.test(bootId)) {
|
|
1059
|
+
throw new BrowserRuntimeError('FENCED', 'boot identity is malformed');
|
|
1060
|
+
}
|
|
1061
|
+
return bootId;
|
|
1062
|
+
}
|
|
1063
|
+
async readBoundedFile(pathArg, maximumBytesArg) {
|
|
1064
|
+
const bytes = await this.readBoundedBuffer(pathArg, maximumBytesArg);
|
|
1065
|
+
try {
|
|
1066
|
+
return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
|
|
1067
|
+
}
|
|
1068
|
+
catch {
|
|
1069
|
+
throw new BrowserRuntimeError('FENCED', 'process metadata is malformed');
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
async readBoundedBuffer(pathArg, maximumBytesArg) {
|
|
1073
|
+
const bytes = await plugins.fsPromises.readFile(pathArg);
|
|
1074
|
+
if (bytes.byteLength > maximumBytesArg) {
|
|
1075
|
+
throw new BrowserRuntimeError('FENCED', 'process metadata exceeds its bound');
|
|
1076
|
+
}
|
|
1077
|
+
return bytes;
|
|
1078
|
+
}
|
|
1079
|
+
generationPath(generationIdArg) {
|
|
1080
|
+
if (!generationIdPattern.test(generationIdArg)) {
|
|
1081
|
+
throw new BrowserRuntimeError('FENCED', 'runtime generation identity is malformed');
|
|
1082
|
+
}
|
|
1083
|
+
const generationPath = plugins.path.join(this.generationsRoot, generationIdArg);
|
|
1084
|
+
if (!this.pathIsWithin(this.generationsRoot, generationPath)) {
|
|
1085
|
+
throw new BrowserRuntimeError('FENCED', 'runtime generation escaped containment');
|
|
1086
|
+
}
|
|
1087
|
+
return generationPath;
|
|
1088
|
+
}
|
|
1089
|
+
pathIsWithin(rootArg, candidateArg) {
|
|
1090
|
+
const relative = plugins.path.relative(rootArg, candidateArg);
|
|
1091
|
+
return relative.length > 0 && relative !== '..'
|
|
1092
|
+
&& !relative.startsWith(`..${plugins.path.sep}`)
|
|
1093
|
+
&& !plugins.path.isAbsolute(relative);
|
|
1094
|
+
}
|
|
1095
|
+
async assertRuntimeEntries(allowedNamesArg) {
|
|
1096
|
+
await this.assertRuntimeDirectoryCurrent();
|
|
1097
|
+
const entries = await plugins.fsPromises.readdir(this.runtimeDirectory, { withFileTypes: true });
|
|
1098
|
+
if (entries.some((entry) => !allowedNamesArg.has(entry.name))) {
|
|
1099
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory contains unknown state');
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
async assertRuntimeDirectoryCurrent() {
|
|
1103
|
+
if (!this.directoryHandle || !this.directoryIdentity) {
|
|
1104
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory is unavailable');
|
|
1105
|
+
}
|
|
1106
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
1107
|
+
plugins.fsPromises.lstat(this.runtimeDirectory, { bigint: true }),
|
|
1108
|
+
this.directoryHandle.stat({ bigint: true }),
|
|
1109
|
+
]);
|
|
1110
|
+
if (!pathStat.isDirectory()
|
|
1111
|
+
|| pathStat.isSymbolicLink()
|
|
1112
|
+
|| pathStat.uid !== BigInt(this.uid)
|
|
1113
|
+
|| (pathStat.mode & 511n) !== 448n
|
|
1114
|
+
|| !statIdentityMatches(pathStat, handleStat)
|
|
1115
|
+
|| String(handleStat.dev) !== this.directoryIdentity.device
|
|
1116
|
+
|| String(handleStat.ino) !== this.directoryIdentity.inode)
|
|
1117
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory identity changed');
|
|
1118
|
+
}
|
|
1119
|
+
async ensurePrivateDirectory(pathArg, parentArg) {
|
|
1120
|
+
if (!this.pathIsWithin(parentArg, pathArg)) {
|
|
1121
|
+
throw new BrowserRuntimeError('FENCED', 'runtime path escaped containment');
|
|
1122
|
+
}
|
|
1123
|
+
let created = false;
|
|
1124
|
+
try {
|
|
1125
|
+
await plugins.fsPromises.mkdir(pathArg, { recursive: false, mode: 0o700 });
|
|
1126
|
+
created = true;
|
|
1127
|
+
}
|
|
1128
|
+
catch (error) {
|
|
1129
|
+
if (error.code !== 'EEXIST')
|
|
1130
|
+
throw error;
|
|
1131
|
+
}
|
|
1132
|
+
if (created) {
|
|
1133
|
+
await plugins.fsPromises.chmod(pathArg, 0o700);
|
|
1134
|
+
const handle = await plugins.fsPromises.open(pathArg, plugins.fs.constants.O_RDONLY
|
|
1135
|
+
| plugins.fs.constants.O_DIRECTORY
|
|
1136
|
+
| plugins.fs.constants.O_NOFOLLOW);
|
|
1137
|
+
try {
|
|
1138
|
+
await handle.chmod(0o700);
|
|
1139
|
+
await plugins.fsPromises.chmod(pathArg, 0o700);
|
|
1140
|
+
const [pathStat, handleStat] = await Promise.all([
|
|
1141
|
+
plugins.fsPromises.lstat(pathArg, { bigint: true }),
|
|
1142
|
+
handle.stat({ bigint: true }),
|
|
1143
|
+
]);
|
|
1144
|
+
if (!pathStat.isDirectory()
|
|
1145
|
+
|| pathStat.isSymbolicLink()
|
|
1146
|
+
|| pathStat.uid !== BigInt(this.uid)
|
|
1147
|
+
|| (pathStat.mode & 511n) !== 448n
|
|
1148
|
+
|| !statIdentityMatches(pathStat, handleStat))
|
|
1149
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory state is unsafe');
|
|
1150
|
+
await handle.sync();
|
|
1151
|
+
}
|
|
1152
|
+
finally {
|
|
1153
|
+
await handle.close();
|
|
1154
|
+
}
|
|
1155
|
+
await this.syncDirectory(parentArg);
|
|
1156
|
+
}
|
|
1157
|
+
await this.validateOptionalPrivateDirectory(pathArg, false);
|
|
1158
|
+
}
|
|
1159
|
+
async validateOptionalPrivateDirectory(pathArg, allowMissingArg = true) {
|
|
1160
|
+
let stat;
|
|
1161
|
+
try {
|
|
1162
|
+
stat = await plugins.fsPromises.lstat(pathArg, { bigint: true });
|
|
1163
|
+
}
|
|
1164
|
+
catch (error) {
|
|
1165
|
+
if (allowMissingArg && isMissingError(error))
|
|
1166
|
+
return false;
|
|
1167
|
+
throw error;
|
|
1168
|
+
}
|
|
1169
|
+
if (!stat.isDirectory()
|
|
1170
|
+
|| stat.isSymbolicLink()
|
|
1171
|
+
|| stat.uid !== BigInt(this.uid)
|
|
1172
|
+
|| (stat.mode & 511n) !== 448n)
|
|
1173
|
+
throw new BrowserRuntimeError('FENCED', 'runtime directory state is unsafe');
|
|
1174
|
+
return true;
|
|
1175
|
+
}
|
|
1176
|
+
async validateOptionalGeneration(generationDirectoryArg) {
|
|
1177
|
+
if (!await this.validateOptionalPrivateDirectory(generationDirectoryArg))
|
|
1178
|
+
return;
|
|
1179
|
+
const entries = await plugins.fsPromises.readdir(generationDirectoryArg, { withFileTypes: true });
|
|
1180
|
+
if (entries.some((entry) => entry.name !== 'profiles' && entry.name !== 'artifacts')) {
|
|
1181
|
+
throw new BrowserRuntimeError('FENCED', 'runtime generation contains unknown state');
|
|
1182
|
+
}
|
|
1183
|
+
await this.validateOptionalPrivateDirectory(plugins.path.join(generationDirectoryArg, 'profiles'));
|
|
1184
|
+
await this.validateOptionalPrivateDirectory(plugins.path.join(generationDirectoryArg, 'artifacts'));
|
|
1185
|
+
}
|
|
1186
|
+
async removePrivateDirectory(pathArg) {
|
|
1187
|
+
if (!this.pathIsWithin(this.runtimeDirectory, pathArg)) {
|
|
1188
|
+
throw new BrowserRuntimeError('FENCED', 'runtime cleanup escaped containment');
|
|
1189
|
+
}
|
|
1190
|
+
if (!await this.validateOptionalPrivateDirectory(pathArg))
|
|
1191
|
+
return;
|
|
1192
|
+
await plugins.fsPromises.rm(pathArg, {
|
|
1193
|
+
recursive: true,
|
|
1194
|
+
force: false,
|
|
1195
|
+
maxRetries: 3,
|
|
1196
|
+
retryDelay: 50,
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1199
|
+
async readOptionalDirectoryEntries(pathArg) {
|
|
1200
|
+
try {
|
|
1201
|
+
return await plugins.fsPromises.readdir(pathArg, { withFileTypes: true });
|
|
1202
|
+
}
|
|
1203
|
+
catch (error) {
|
|
1204
|
+
if (isMissingError(error))
|
|
1205
|
+
return [];
|
|
1206
|
+
throw error;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
async syncOptionalDirectory(pathArg) {
|
|
1210
|
+
try {
|
|
1211
|
+
await this.syncDirectory(pathArg);
|
|
1212
|
+
}
|
|
1213
|
+
catch (error) {
|
|
1214
|
+
if (!isMissingError(error))
|
|
1215
|
+
throw error;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
async syncDirectory(pathArg) {
|
|
1219
|
+
const handle = await plugins.fsPromises.open(pathArg, plugins.fs.constants.O_RDONLY
|
|
1220
|
+
| plugins.fs.constants.O_DIRECTORY
|
|
1221
|
+
| plugins.fs.constants.O_NOFOLLOW);
|
|
1222
|
+
try {
|
|
1223
|
+
await handle.sync();
|
|
1224
|
+
}
|
|
1225
|
+
finally {
|
|
1226
|
+
await handle.close();
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5ydW50aW1lb3duZXJzaGlwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5ydW50aW1lb3duZXJzaGlwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUNsRCxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRXRDLE1BQU0sY0FBYyxHQUFHLENBQUMsQ0FBQztBQUN6QixNQUFNLG9CQUFvQixHQUFHLElBQUksQ0FBQztBQUNsQyxNQUFNLG9CQUFvQixHQUFHLENBQUMsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBQzdDLE1BQU0sNEJBQTRCLEdBQUcsR0FBRyxDQUFDO0FBQ3pDLE1BQU0sbUJBQW1CLEdBQUcsc0JBQXNCLENBQUM7QUFDbkQsTUFBTSxXQUFXLEdBQUcsaUJBQWlCLENBQUM7QUFDdEMsTUFBTSxhQUFhLEdBQUcsNEVBQTRFLENBQUM7QUFDbkcsTUFBTSxzQkFBc0IsR0FBRywyQkFBMkIsQ0FBQztBQWtEM0QsTUFBTSxjQUFjLEdBQUcsQ0FBQyxRQUFpQixFQUFXLEVBQUUsQ0FBQyxDQUNwRCxRQUFrQyxDQUFDLElBQUksS0FBSyxRQUFRO09BQ2pELFFBQWtDLENBQUMsSUFBSSxLQUFLLE9BQU8sQ0FDeEQsQ0FBQztBQUVGLE1BQU0sdUJBQXVCLEdBQUcsQ0FBQyxRQUFpQixFQUF1QixFQUFFLENBQUMsQ0FDMUUsUUFBUSxZQUFZLG1CQUFtQjtJQUNyQyxDQUFDLENBQUMsUUFBUTtJQUNWLENBQUMsQ0FBQyxJQUFJLG1CQUFtQixDQUFDLFFBQVEsRUFBRSx5Q0FBeUMsQ0FBQyxDQUNqRixDQUFDO0FBRUYsTUFBTSxtQkFBbUIsR0FBRyxDQUMxQixPQUErQixFQUMvQixRQUFnQyxFQUN2QixFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcs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
|