Skip to content

Commit 5023a71

Browse files
committed
Integrate syscall override injection
1 parent 6094bbf commit 5023a71

File tree

10 files changed

+157
-778
lines changed

10 files changed

+157
-778
lines changed

packages/php-wasm/compile/php/phpwasm-emscripten-library-file-locking-for-node.js

Lines changed: 6 additions & 747 deletions
Large diffs are not rendered by default.

packages/php-wasm/compile/php/phpwasm-emscripten-library.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,69 @@ const LibraryExample = {
2929
// emscripten_O_DIRECT |
3030
// emscripten_O_NOATIME
3131
init: function (phpWasmInitOptions) {
32+
// TODO: Consider limiting this to Node.js builds.
33+
if (phpWasmInitOptions.bindUserSpace) {
34+
phpWasmInitOptions.bindUserSpace({
35+
// TODO: Require PID instead of defaulting to 42.
36+
pid: PHPLoader.processId ?? 42,
37+
// TODO: When receiving this context, validate that all these fields exist.
38+
constants: {
39+
F_RDLCK: Number('{{{cDefs.F_RDLCK}}}'),
40+
F_WRLCK: Number('{{{cDefs.F_WRLCK}}}'),
41+
F_UNLCK: Number('{{{cDefs.F_UNLCK}}}'),
42+
F_GETFL: Number('{{{cDefs.F_GETFL}}}'),
43+
O_ACCMODE: Number('{{{cDefs.O_ACCMODE}}}'),
44+
O_RDONLY: Number('{{{cDefs.O_RDONLY}}}'),
45+
O_WRONLY: Number('{{{cDefs.O_WRONLY}}}'),
46+
O_APPEND: Number('{{{cDefs.O_APPEND}}}'),
47+
O_NONBLOCK: Number('{{{cDefs.O_NONBLOCK}}}'),
48+
F_SETFL: Number('{{{cDefs.F_SETFL}}}'),
49+
F_GETLK: Number('{{{cDefs.F_GETLK}}}'),
50+
F_SETLK: Number('{{{cDefs.F_SETLK}}}'),
51+
F_SETLKW: Number('{{{cDefs.F_SETLKW}}}'),
52+
SEEK_SET: Number('{{{cDefs.SEEK_SET}}}'),
53+
SEEK_CUR: Number('{{{cDefs.SEEK_CUR}}}'),
54+
SEEK_END: Number('{{{cDefs.SEEK_END}}}'),
55+
F_GETFL: Number('{{{cDefs.F_GETFL}}}'),
56+
O_ACCMODE: Number('{{{cDefs.O_ACCMODE}}}'),
57+
O_RDONLY: Number('{{{cDefs.O_RDONLY}}}'),
58+
O_WRONLY: Number('{{{cDefs.O_WRONLY}}}'),
59+
O_APPEND: Number('{{{cDefs.O_APPEND}}}'),
60+
O_NONBLOCK: Number('{{{cDefs.O_NONBLOCK}}}'),
61+
F_SETFL: Number('{{{cDefs.F_SETFL}}}'),
62+
F_GETLK: Number('{{{cDefs.F_GETLK}}}'),
63+
F_SETLK: Number('{{{cDefs.F_SETLK}}}'),
64+
F_SETLKW: Number('{{{cDefs.F_SETLKW}}}'),
65+
SEEK_SET: Number('{{{cDefs.SEEK_SET}}}'),
66+
SEEK_CUR: Number('{{{cDefs.SEEK_CUR}}}'),
67+
SEEK_END: Number('{{{cDefs.SEEK_END}}}'),
68+
LOCK_SH: 1,
69+
LOCK_EX: 2,
70+
LOCK_NB: 4,
71+
LOCK_UN: 8,
72+
},
73+
errnoCodes: ERRNO_CODES,
74+
memory: {
75+
HEAP8,
76+
HEAPU8,
77+
HEAP16,
78+
HEAPU16,
79+
HEAP32,
80+
HEAPU32,
81+
HEAPF32,
82+
HEAP64,
83+
HEAPU64,
84+
HEAPF64,
85+
},
86+
wasmImports,
87+
wasmExports,
88+
syscalls: SYSCALLS,
89+
FS: typeof Emscripten.FS,
90+
PROXYFS,
91+
NODEFS,
92+
});
93+
}
94+
3295
Module['ENV'] = Module['ENV'] || {};
3396
// Ensure a platform-level bin directory for a fallback `php` binary.
3497
Module['ENV']['PATH'] = [

packages/php-wasm/node/src/lib/load-runtime.ts

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type {
22
SupportedPHPVersion,
33
EmscriptenOptions,
44
PHPRuntime,
5-
RemoteAPI,
6-
FileLockManager,
5+
OSUserSpaceAPI,
6+
OSUserSpaceContext,
77
} from '@php-wasm/universal';
88
import { loadPHPRuntime, FSHelpers } from '@php-wasm/universal';
99
import fs from 'fs';
@@ -12,7 +12,6 @@ import { withNetworking } from './networking/with-networking';
1212
import { withXdebug, type XdebugOptions } from './xdebug/with-xdebug';
1313
import { withIntl } from './extensions/intl/with-intl';
1414
import { joinPaths } from '@php-wasm/util';
15-
import type { Promised } from '@php-wasm/util';
1615
import { dirname } from 'path';
1716

1817
export interface PHPLoaderOptions {
@@ -35,20 +34,21 @@ type PHPLoaderOptionsForNode = PHPLoaderOptions & {
3534
*/
3635
processId?: number;
3736

38-
/**
39-
* An optional file lock manager to use for the PHP runtime.
40-
*
41-
* The lock manager is optional when running a single php-wasm process.
42-
*
43-
* When running with JSPI, both synchronous and asynchronous
44-
* file lock managers are supported.
45-
* When running with Asyncify, the file lock manager must be synchronous.
46-
*/
47-
fileLockManager?:
48-
| RemoteAPI<FileLockManager>
49-
// Allow promised type for testing without providing true RemoteAPI.
50-
| Promised<FileLockManager>
51-
| FileLockManager;
37+
// TODO: Remove this.
38+
// /**
39+
// * An optional file lock manager to use for the PHP runtime.
40+
// *
41+
// * The lock manager is optional when running a single php-wasm process.
42+
// *
43+
// * When running with JSPI, both synchronous and asynchronous
44+
// * file lock managers are supported.
45+
// * When running with Asyncify, the file lock manager must be synchronous.
46+
// */
47+
// fileLockManager?:
48+
// | RemoteAPI<FileLockManager>
49+
// // Allow promised type for testing without providing true RemoteAPI.
50+
// | Promised<FileLockManager>
51+
// | FileLockManager;
5252

5353
/**
5454
* An optional function to collect trace messages.
@@ -69,6 +69,10 @@ type PHPLoaderOptionsForNode = PHPLoaderOptions & {
6969
*/
7070
phpWasmInitOptions?: {
7171
nativeInternalDirPath?: string;
72+
// TODO: Document this.
73+
bindUserSpace?: (
74+
userSpaceContext: OSUserSpaceContext
75+
) => OSUserSpaceAPI;
7276
};
7377
};
7478
};

packages/php-wasm/node/src/test/file-lock-manager-for-node.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { FileLockManagerForNode } from '../lib/file-lock-manager-for-node';
33
import { fork } from 'child_process';
44
import type { ChildProcess } from 'child_process';
55
import { join } from 'path';
6-
import type { WholeFileLockOp } from '../lib/file-lock-manager';
6+
import { type WholeFileLockOp } from '@php-wasm/universal';
77
import { flockSync as nativeFlockSync } from 'fs-ext';
88

99
const TEST_FILE1 = new URL('test1.txt', import.meta.url).pathname;

packages/php-wasm/node/src/test/php-file-locking.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
proxyFileSystem,
88
type SupportedPHPVersion,
99
} from '@php-wasm/universal';
10-
import { SupportedPHPVersions } from '@php-wasm/universal';
10+
import {
11+
SupportedPHPVersions,
12+
type FileLockManager,
13+
} from '@php-wasm/universal';
1114
import {
1215
createNodeFsMountHandler,
1316
FileLockManagerForNode,
@@ -19,7 +22,6 @@ import {
1922
type Promised,
2023
} from '@php-wasm/util';
2124
import { jspi } from 'wasm-feature-detect';
22-
import type { FileLockManager } from '../lib/file-lock-manager';
2325

2426
const phpVersionsToTest =
2527
'PHP' in process.env

packages/php-wasm/universal/src/lib/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,6 @@ export * from './api';
9090
export type { WithAPIState as WithIsReady } from './api';
9191

9292
export type * from './file-lock-manager';
93+
// TODO: Review exported names and improve if needed.
94+
export * from './os-user-space';
95+
export type * from './os-user-space';

packages/php-wasm/universal/src/lib/os-kernel-space.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
// TODO: Consider merging FileLockManager file with os-kernel-space.
2+
import type { Promised } from '@php-wasm/util';
23
import type { FileLockManager } from './file-lock-manager';
4+
import type { RemoteAPI } from './api';
35

46
// TODO: Consider merging FileLockManager into this type.
57
export class OSKernelSpace {
6-
readonly fileLockManager: FileLockManager;
8+
readonly fileLockManager:
9+
| RemoteAPI<FileLockManager>
10+
// Allow promised type for testing without providing true RemoteAPI.
11+
| Promised<FileLockManager>
12+
| FileLockManager;
713

814
constructor(fileLockManager: FileLockManager) {
915
this.fileLockManager = fileLockManager;

packages/php-wasm/universal/src/lib/os-user-space.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,13 @@ export type OSUserSpaceContext = {
9595
};
9696
};
9797

98+
export type OSUserSpaceAPI = {
99+
fcntl64: (fd: number, cmd: number, varargs?: number) => Promise<number>;
100+
flock: (fd: number, op: number) => Promise<number>;
101+
fd_close: (fd: number) => Promise<number>;
102+
js_release_file_locks: () => Promise<void>;
103+
};
104+
98105
export function bindUserSpace(
99106
{ fileLockManager }: OSKernelSpace,
100107
{
@@ -130,7 +137,7 @@ export function bindUserSpace(
130137
PROXYFS,
131138
NODEFS,
132139
}: OSUserSpaceContext
133-
) {
140+
): OSUserSpaceAPI {
134141
class VarArgsAccessor {
135142
argsAddr: number;
136143

@@ -180,7 +187,7 @@ export function bindUserSpace(
180187
// TODO: Do we still need to support PROXYFS now that Playground CLI uses NODEFS everywhere?
181188
// This looks like a PROXYFS node. Let's try a lookup.
182189
const nodePath = PROXYFS.realPath(node);
183-
const backingFs = node?.mount?.opts?.fs;
190+
const backingFs = node?.mount?.opts?.['fs'];
184191
if (backingFs) {
185192
// Tolerate ENOENT because looking up a MEMFS node by path always fails.
186193
const { node: backingNode } = backingFs.lookupPath(nodePath, {
@@ -214,7 +221,7 @@ export function bindUserSpace(
214221
} else if (node.mount.type === PROXYFS) {
215222
// TODO: Tolerate ENOENT here?
216223
const { node: backingNode, path: backingPath } =
217-
node.mount.opts.fs.lookupPath(vfsPath);
224+
node.mount.opts['fs'].lookupPath(vfsPath);
218225
js_wasm_trace(
219226
'backingNode for %s: %s',
220227
vfsPath,
@@ -487,7 +494,7 @@ export function bindUserSpace(
487494
const nativeFilePath =
488495
locking.get_native_path_from_vfs_path(vfsPath);
489496
const conflictingLock =
490-
fileLockManager.findFirstConflictingByteRangeLock(
497+
await fileLockManager.findFirstConflictingByteRangeLock(
491498
nativeFilePath,
492499
{
493500
type: requestedLockType,

packages/playground/cli/src/blueprints-v1/worker-thread-v1.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import type { FileLockManager } from '@php-wasm/node';
1+
import type { FileLockManager } from '@php-wasm/universal';
22
import { loadNodeRuntime } from '@php-wasm/node';
33
import { EmscriptenDownloadMonitor } from '@php-wasm/progress';
44
import type { RemoteAPI, SupportedPHPVersion } from '@php-wasm/universal';
55
import {
66
PHPWorker,
7+
bindUserSpace,
78
consumeAPI,
89
consumeAPISync,
910
exposeAPI,
@@ -176,7 +177,18 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
176177
fileLockManager: this.fileLockManager!,
177178
processId,
178179
trace: trace ? tracePhpWasm : undefined,
179-
phpWasmInitOptions: { nativeInternalDirPath },
180+
phpWasmInitOptions: {
181+
nativeInternalDirPath,
182+
bindUserSpace: (userSpaceContext) => {
183+
return bindUserSpace(
184+
{
185+
fileLockManager:
186+
this.fileLockManager!,
187+
},
188+
userSpaceContext
189+
);
190+
},
191+
},
180192
},
181193
followSymlinks,
182194
withXdebug,
@@ -192,7 +204,7 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
192204
? new File(
193205
[sqliteIntegrationPluginZip],
194206
'sqlite-integration-plugin.zip'
195-
)
207+
)
196208
: undefined,
197209
sapiName: 'cli',
198210
createFiles: {
@@ -279,7 +291,18 @@ export class PlaygroundCliBlueprintV1Worker extends PHPWorker {
279291
ENV: {
280292
DOCROOT: '/wordpress',
281293
},
282-
phpWasmInitOptions: { nativeInternalDirPath },
294+
phpWasmInitOptions: {
295+
nativeInternalDirPath,
296+
bindUserSpace: (userSpaceContext) => {
297+
return bindUserSpace(
298+
{
299+
fileLockManager:
300+
this.fileLockManager!,
301+
},
302+
userSpaceContext
303+
);
304+
},
305+
},
283306
},
284307
followSymlinks,
285308
withXdebug,

packages/playground/cli/src/blueprints-v2/worker-thread-v2.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { errorLogPath, logger } from '@php-wasm/logger';
2-
import type { FileLockManager } from '@php-wasm/node';
2+
import type { FileLockManager } from '@php-wasm/universal';
33
import { createNodeFsMountHandler, loadNodeRuntime } from '@php-wasm/node';
44
import { EmscriptenDownloadMonitor } from '@php-wasm/progress';
55
import type {
@@ -12,6 +12,7 @@ import {
1212
PHPExecutionFailureError,
1313
PHPResponse,
1414
PHPWorker,
15+
bindUserSpace,
1516
consumeAPI,
1617
consumeAPISync,
1718
exposeAPI,
@@ -456,7 +457,18 @@ export class PlaygroundCliBlueprintV2Worker extends PHPWorker {
456457
ENV: {
457458
DOCROOT: '/wordpress',
458459
},
459-
phpWasmInitOptions: { nativeInternalDirPath },
460+
phpWasmInitOptions: {
461+
nativeInternalDirPath,
462+
bindUserSpace: (userSpaceContext) => {
463+
return bindUserSpace(
464+
{
465+
fileLockManager:
466+
this.fileLockManager!,
467+
},
468+
userSpaceContext
469+
);
470+
},
471+
},
460472
},
461473
followSymlinks: allow?.includes('follow-symlinks'),
462474
withXdebug,

0 commit comments

Comments
 (0)