@php-wasm/node 3.0.54 → 3.1.1
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/index.cjs +1188 -673
- package/index.js +1163 -638
- package/lib/file-lock-manager-for-posix.d.ts +14 -0
- package/lib/file-lock-manager-for-windows.d.ts +15 -0
- package/lib/index.d.ts +4 -2
- package/lib/load-runtime.d.ts +16 -21
- package/lib/wasm-kernel-space.d.ts +9 -0
- package/lib/wasm-user-space.d.ts +81 -0
- package/package.json +15 -17
- package/test/file-lock-manager-composite-with-posix--test-process.d.ts +1 -0
- package/test/file-lock-manager-composite-with-posix--test-worker-thread.d.ts +1 -0
- package/test/file-lock-manager-composite-with-windows--test-process.d.ts +1 -0
- package/test/file-lock-manager-composite-with-windows--test-worker-thread.d.ts +1 -0
- package/test/file-lock-manager-for-posix--test-process.d.ts +1 -0
- package/test/file-lock-manager-for-windows--test-process.d.ts +1 -0
- package/test/file-lock-manager-test-utils.d.ts +20 -0
- package/test/file-lock-manager-tests.d.ts +6 -0
- package/lib/file-lock-manager-for-node.d.ts +0 -164
- package/lib/file-lock-manager.d.ts +0 -96
- package/lib/syscalls-for-node.d.ts +0 -9
package/index.cjs
CHANGED
|
@@ -30,8 +30,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// packages/php-wasm/node/src/index.ts
|
|
31
31
|
var src_exports = {};
|
|
32
32
|
__export(src_exports, {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
FileLockManagerForPosix: () => FileLockManagerForPosix,
|
|
34
|
+
FileLockManagerForWindows: () => FileLockManagerForWindows,
|
|
35
|
+
bindUserSpace: () => bindUserSpace,
|
|
35
36
|
createNodeFsMountHandler: () => createNodeFsMountHandler,
|
|
36
37
|
getPHPLoaderModule: () => getPHPLoaderModule,
|
|
37
38
|
loadNodeRuntime: () => loadNodeRuntime,
|
|
@@ -423,16 +424,1152 @@ async function withNetworking(phpModuleArgs = {}) {
|
|
|
423
424
|
}
|
|
424
425
|
|
|
425
426
|
// packages/php-wasm/node/src/lib/load-runtime.ts
|
|
426
|
-
var
|
|
427
|
+
var import_universal12 = require("@php-wasm/universal");
|
|
428
|
+
|
|
429
|
+
// packages/php-wasm/node/src/lib/wasm-user-space.ts
|
|
430
|
+
var import_promises = require("dns/promises");
|
|
431
|
+
function bindUserSpace({ fileLockManager }, {
|
|
432
|
+
pid,
|
|
433
|
+
memory: { HEAP16, HEAP64, HEAP32 },
|
|
434
|
+
constants: {
|
|
435
|
+
F_RDLCK,
|
|
436
|
+
F_WRLCK,
|
|
437
|
+
F_UNLCK,
|
|
438
|
+
F_GETFL,
|
|
439
|
+
O_ACCMODE,
|
|
440
|
+
O_RDONLY,
|
|
441
|
+
O_WRONLY,
|
|
442
|
+
O_APPEND,
|
|
443
|
+
O_NONBLOCK,
|
|
444
|
+
F_SETFL,
|
|
445
|
+
F_GETLK,
|
|
446
|
+
F_SETLK,
|
|
447
|
+
F_SETLKW,
|
|
448
|
+
SEEK_SET,
|
|
449
|
+
SEEK_CUR,
|
|
450
|
+
SEEK_END,
|
|
451
|
+
LOCK_SH,
|
|
452
|
+
LOCK_EX,
|
|
453
|
+
LOCK_NB,
|
|
454
|
+
LOCK_UN
|
|
455
|
+
},
|
|
456
|
+
errnoCodes: { EBADF, EINVAL, EAGAIN, EWOULDBLOCK },
|
|
457
|
+
wasmImports: { builtin_fcntl64, builtin_fd_close, js_wasm_trace },
|
|
458
|
+
wasmExports: { wasm_get_end_offset },
|
|
459
|
+
syscalls: { getStreamFromFD },
|
|
460
|
+
FS,
|
|
461
|
+
PROXYFS,
|
|
462
|
+
NODEFS
|
|
463
|
+
}) {
|
|
464
|
+
class VarArgsAccessor {
|
|
465
|
+
constructor(argsAddr) {
|
|
466
|
+
this.argsAddr = argsAddr;
|
|
467
|
+
}
|
|
468
|
+
getNextAsPointer() {
|
|
469
|
+
return this.getNextAsInt();
|
|
470
|
+
}
|
|
471
|
+
getNextAsInt() {
|
|
472
|
+
const fourByteOffset = this.argsAddr >> 2;
|
|
473
|
+
const value = HEAP32[fourByteOffset];
|
|
474
|
+
this.argsAddr += 4;
|
|
475
|
+
return value;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const locking = {
|
|
479
|
+
/*
|
|
480
|
+
* This is a set of possibly locked file descriptors.
|
|
481
|
+
*
|
|
482
|
+
* When a file descriptor is closed, we need to release any associated held by this process.
|
|
483
|
+
* Instead of trying remember and forget file descriptors as they are locked and unlocked,
|
|
484
|
+
* we just track file descriptors we have locked before and try an unlock when they are closed.
|
|
485
|
+
*/
|
|
486
|
+
maybeLockedFds: /* @__PURE__ */ new Set(),
|
|
487
|
+
lockStateToFcntl: {
|
|
488
|
+
shared: F_RDLCK,
|
|
489
|
+
exclusive: F_WRLCK,
|
|
490
|
+
unlocked: F_UNLCK
|
|
491
|
+
},
|
|
492
|
+
fcntlToLockState: {
|
|
493
|
+
[F_RDLCK]: "shared",
|
|
494
|
+
[F_WRLCK]: "exclusive",
|
|
495
|
+
[F_UNLCK]: "unlocked"
|
|
496
|
+
},
|
|
497
|
+
is_path_to_shared_fs(path2) {
|
|
498
|
+
const { node } = FS.lookupPath(path2, { noent_okay: true });
|
|
499
|
+
if (!node) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
if (node.mount.type !== PROXYFS) {
|
|
503
|
+
return !!node.isSharedFS;
|
|
504
|
+
}
|
|
505
|
+
const nodePath = PROXYFS.realPath(node);
|
|
506
|
+
const backingFs = node?.mount?.opts?.["fs"];
|
|
507
|
+
if (backingFs) {
|
|
508
|
+
const { node: backingNode } = backingFs.lookupPath(nodePath, {
|
|
509
|
+
noent_okay: true
|
|
510
|
+
});
|
|
511
|
+
return !!backingNode?.isSharedFS;
|
|
512
|
+
}
|
|
513
|
+
return false;
|
|
514
|
+
},
|
|
515
|
+
get_fd_access_mode(fd) {
|
|
516
|
+
return builtin_fcntl64(fd, F_GETFL) & O_ACCMODE;
|
|
517
|
+
},
|
|
518
|
+
get_vfs_path_from_fd(fd) {
|
|
519
|
+
try {
|
|
520
|
+
return [FS.readlink(`/proc/self/fd/${fd}`), 0];
|
|
521
|
+
} catch {
|
|
522
|
+
return [null, EBADF];
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
get_native_path_from_vfs_path(vfsPath) {
|
|
526
|
+
const { node } = FS.lookupPath(vfsPath, {
|
|
527
|
+
noent_okay: true
|
|
528
|
+
});
|
|
529
|
+
if (!node) {
|
|
530
|
+
throw new Error(`No node found for VFS path ${vfsPath}`);
|
|
531
|
+
}
|
|
532
|
+
if (node.mount.type === NODEFS) {
|
|
533
|
+
return NODEFS.realPath(node);
|
|
534
|
+
} else if (node.mount.type === PROXYFS) {
|
|
535
|
+
const { node: backingNode, path: backingPath } = node.mount.opts["fs"].lookupPath(vfsPath);
|
|
536
|
+
js_wasm_trace(
|
|
537
|
+
"backingNode for %s: %s",
|
|
538
|
+
vfsPath,
|
|
539
|
+
backingPath,
|
|
540
|
+
backingNode
|
|
541
|
+
);
|
|
542
|
+
return backingNode.mount.type.realPath(backingNode);
|
|
543
|
+
} else {
|
|
544
|
+
throw new Error(
|
|
545
|
+
`Unsupported filesystem type for path ${vfsPath}`
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
},
|
|
549
|
+
get_native_fd_from_emscripten_fd(fd) {
|
|
550
|
+
try {
|
|
551
|
+
const stream = getStreamFromFD(fd);
|
|
552
|
+
if (stream.nfd === void 0) {
|
|
553
|
+
return [null, EBADF];
|
|
554
|
+
}
|
|
555
|
+
return [stream.nfd, 0];
|
|
556
|
+
} catch {
|
|
557
|
+
return [null, EBADF];
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
check_lock_params(fd, l_type) {
|
|
561
|
+
const accessMode = locking.get_fd_access_mode(fd);
|
|
562
|
+
if (l_type === F_WRLCK && accessMode === O_RDONLY || l_type === F_RDLCK && accessMode === O_WRONLY) {
|
|
563
|
+
js_wasm_trace(
|
|
564
|
+
"check_lock_params(%d, %d, %d) EBADF",
|
|
565
|
+
fd,
|
|
566
|
+
l_type,
|
|
567
|
+
accessMode
|
|
568
|
+
);
|
|
569
|
+
return EBADF;
|
|
570
|
+
}
|
|
571
|
+
return 0;
|
|
572
|
+
}
|
|
573
|
+
};
|
|
574
|
+
const emscripten_flock_l_type_offset = 0;
|
|
575
|
+
const emscripten_flock_l_whence_offset = 2;
|
|
576
|
+
const emscripten_flock_l_start_offset = 8;
|
|
577
|
+
const emscripten_flock_l_len_offset = 16;
|
|
578
|
+
const emscripten_flock_l_pid_offset = 24;
|
|
579
|
+
function readFlockStruct(flockStructAddress) {
|
|
580
|
+
return {
|
|
581
|
+
l_type: HEAP16[
|
|
582
|
+
// Shift right by 1 to divide by 2^1.
|
|
583
|
+
flockStructAddress + emscripten_flock_l_type_offset >> 1
|
|
584
|
+
],
|
|
585
|
+
l_whence: HEAP16[
|
|
586
|
+
// Shift right by 1 to divide by 2^1.
|
|
587
|
+
flockStructAddress + emscripten_flock_l_whence_offset >> 1
|
|
588
|
+
],
|
|
589
|
+
l_start: HEAP64[
|
|
590
|
+
// Shift right by 3 to divide by 2^3.
|
|
591
|
+
flockStructAddress + emscripten_flock_l_start_offset >> 3
|
|
592
|
+
],
|
|
593
|
+
l_len: HEAP64[
|
|
594
|
+
// Shift right by 3 to divide by 2^3.
|
|
595
|
+
flockStructAddress + emscripten_flock_l_len_offset >> 3
|
|
596
|
+
],
|
|
597
|
+
l_pid: HEAP32[
|
|
598
|
+
// Shift right by 2 to divide by 2^2.
|
|
599
|
+
flockStructAddress + emscripten_flock_l_pid_offset >> 2
|
|
600
|
+
]
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function updateFlockStruct(flockStructAddress, fields) {
|
|
604
|
+
if (fields.l_type !== void 0) {
|
|
605
|
+
HEAP16[
|
|
606
|
+
// Shift right by 1 to divide by 2^1.
|
|
607
|
+
flockStructAddress + emscripten_flock_l_type_offset >> 1
|
|
608
|
+
] = fields.l_type;
|
|
609
|
+
}
|
|
610
|
+
if (fields.l_whence !== void 0) {
|
|
611
|
+
HEAP16[
|
|
612
|
+
// Shift right by 1 to divide by 2^1.
|
|
613
|
+
flockStructAddress + emscripten_flock_l_whence_offset >> 1
|
|
614
|
+
] = fields.l_whence;
|
|
615
|
+
}
|
|
616
|
+
if (fields.l_start !== void 0) {
|
|
617
|
+
HEAP64[
|
|
618
|
+
// Shift right by 3 to divide by 2^3.
|
|
619
|
+
flockStructAddress + emscripten_flock_l_start_offset >> 3
|
|
620
|
+
] = fields.l_start;
|
|
621
|
+
}
|
|
622
|
+
if (fields.l_len !== void 0) {
|
|
623
|
+
HEAP64[
|
|
624
|
+
// Shift right by 3 to divide by 2^3.
|
|
625
|
+
flockStructAddress + emscripten_flock_l_len_offset >> 3
|
|
626
|
+
] = fields.l_len;
|
|
627
|
+
}
|
|
628
|
+
if (fields.l_pid !== void 0) {
|
|
629
|
+
HEAP32[
|
|
630
|
+
// Shift right by 2 to divide by 2^2.
|
|
631
|
+
flockStructAddress + emscripten_flock_l_pid_offset >> 2
|
|
632
|
+
] = fields.l_pid;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
function getBaseAddress(fd, whence, startOffset) {
|
|
636
|
+
let baseAddress;
|
|
637
|
+
switch (whence) {
|
|
638
|
+
case SEEK_SET:
|
|
639
|
+
baseAddress = 0n;
|
|
640
|
+
break;
|
|
641
|
+
case SEEK_CUR:
|
|
642
|
+
try {
|
|
643
|
+
const stream = getStreamFromFD(fd);
|
|
644
|
+
baseAddress = FS.llseek(stream, 0, whence);
|
|
645
|
+
} catch (e) {
|
|
646
|
+
js_wasm_trace(
|
|
647
|
+
"get_base_address(%d, %d, %d) getStreamFromFD error %s",
|
|
648
|
+
fd,
|
|
649
|
+
whence,
|
|
650
|
+
startOffset,
|
|
651
|
+
e
|
|
652
|
+
);
|
|
653
|
+
return [null, EINVAL];
|
|
654
|
+
}
|
|
655
|
+
break;
|
|
656
|
+
case SEEK_END:
|
|
657
|
+
baseAddress = wasm_get_end_offset(fd);
|
|
658
|
+
break;
|
|
659
|
+
default:
|
|
660
|
+
return [null, EINVAL];
|
|
661
|
+
}
|
|
662
|
+
if (baseAddress == -1) {
|
|
663
|
+
return [null, EBADF];
|
|
664
|
+
}
|
|
665
|
+
const resolvedOffset = baseAddress + startOffset;
|
|
666
|
+
if (resolvedOffset < 0) {
|
|
667
|
+
return [null, EINVAL];
|
|
668
|
+
}
|
|
669
|
+
return [resolvedOffset, 0];
|
|
670
|
+
}
|
|
671
|
+
function fcntl64(fd, cmd, varargs) {
|
|
672
|
+
js_wasm_trace("fcntl64(%d, %d)", fd, cmd);
|
|
673
|
+
if (!fileLockManager) {
|
|
674
|
+
js_wasm_trace(
|
|
675
|
+
"fcntl64(%d, %d) file lock manager is not available. delegate to Emscripten builtin fcntl64.",
|
|
676
|
+
fd,
|
|
677
|
+
cmd
|
|
678
|
+
);
|
|
679
|
+
return builtin_fcntl64(fd, cmd, varargs);
|
|
680
|
+
}
|
|
681
|
+
switch (cmd) {
|
|
682
|
+
case F_GETLK: {
|
|
683
|
+
const reportUnlockedFileByDefault = function reportUnlockedFileByDefault2() {
|
|
684
|
+
updateFlockStruct(flockStructAddr, {
|
|
685
|
+
l_type: F_UNLCK
|
|
686
|
+
});
|
|
687
|
+
return 0;
|
|
688
|
+
};
|
|
689
|
+
js_wasm_trace("fcntl(%d, F_GETLK)", fd);
|
|
690
|
+
const [vfsPath, vfsPathErrno] = locking.get_vfs_path_from_fd(fd);
|
|
691
|
+
if (vfsPathErrno !== 0) {
|
|
692
|
+
js_wasm_trace(
|
|
693
|
+
"fcntl(%d, F_GETLK) %s get_vfs_path_from_fd errno %d",
|
|
694
|
+
fd,
|
|
695
|
+
vfsPath,
|
|
696
|
+
vfsPathErrno
|
|
697
|
+
);
|
|
698
|
+
return -EBADF;
|
|
699
|
+
}
|
|
700
|
+
const varArgsAccessor = new VarArgsAccessor(varargs);
|
|
701
|
+
const flockStructAddr = varArgsAccessor.getNextAsPointer();
|
|
702
|
+
if (!locking.is_path_to_shared_fs(vfsPath) || fileLockManager === void 0) {
|
|
703
|
+
js_wasm_trace(
|
|
704
|
+
"fcntl(%d, F_GETLK) locking is not implemented for non-NodeFS path '%s'",
|
|
705
|
+
fd,
|
|
706
|
+
vfsPath
|
|
707
|
+
);
|
|
708
|
+
return reportUnlockedFileByDefault();
|
|
709
|
+
}
|
|
710
|
+
const flockStruct = readFlockStruct(flockStructAddr);
|
|
711
|
+
if (!(flockStruct.l_type in locking.fcntlToLockState)) {
|
|
712
|
+
return -EINVAL;
|
|
713
|
+
}
|
|
714
|
+
const paramsCheckErrno = locking.check_lock_params(
|
|
715
|
+
fd,
|
|
716
|
+
flockStruct.l_type
|
|
717
|
+
);
|
|
718
|
+
if (paramsCheckErrno !== 0) {
|
|
719
|
+
js_wasm_trace(
|
|
720
|
+
"fcntl(%d, F_GETLK) %s check_lock_params errno %d",
|
|
721
|
+
fd,
|
|
722
|
+
vfsPath,
|
|
723
|
+
paramsCheckErrno
|
|
724
|
+
);
|
|
725
|
+
return -EINVAL;
|
|
726
|
+
}
|
|
727
|
+
const requestedLockType = locking.fcntlToLockState[flockStruct.l_type];
|
|
728
|
+
const [absoluteStartOffset, baseAddressErrno] = getBaseAddress(
|
|
729
|
+
fd,
|
|
730
|
+
flockStruct.l_whence,
|
|
731
|
+
flockStruct.l_start
|
|
732
|
+
);
|
|
733
|
+
if (baseAddressErrno !== 0) {
|
|
734
|
+
js_wasm_trace(
|
|
735
|
+
"fcntl(%d, F_GETLK) %s get_base_address errno %d",
|
|
736
|
+
fd,
|
|
737
|
+
vfsPath,
|
|
738
|
+
baseAddressErrno
|
|
739
|
+
);
|
|
740
|
+
return -EINVAL;
|
|
741
|
+
}
|
|
742
|
+
const [nativeFd, nativeFdErrno] = locking.get_native_fd_from_emscripten_fd(fd);
|
|
743
|
+
if (nativeFdErrno !== 0) {
|
|
744
|
+
js_wasm_trace(
|
|
745
|
+
"fcntl(%d, F_GETLK) get_native_fd_from_emscripten_fd errno %d",
|
|
746
|
+
fd,
|
|
747
|
+
nativeFdErrno
|
|
748
|
+
);
|
|
749
|
+
return -nativeFdErrno;
|
|
750
|
+
}
|
|
751
|
+
try {
|
|
752
|
+
const nativeFilePath = locking.get_native_path_from_vfs_path(vfsPath);
|
|
753
|
+
const conflictingLock = fileLockManager.findFirstConflictingByteRangeLock(
|
|
754
|
+
nativeFilePath,
|
|
755
|
+
{
|
|
756
|
+
type: requestedLockType,
|
|
757
|
+
start: absoluteStartOffset,
|
|
758
|
+
end: absoluteStartOffset + flockStruct.l_len,
|
|
759
|
+
pid,
|
|
760
|
+
fd: nativeFd
|
|
761
|
+
}
|
|
762
|
+
);
|
|
763
|
+
if (conflictingLock === void 0) {
|
|
764
|
+
js_wasm_trace(
|
|
765
|
+
"fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=unlocked start=0x%x end=0x%x",
|
|
766
|
+
fd,
|
|
767
|
+
vfsPath,
|
|
768
|
+
absoluteStartOffset,
|
|
769
|
+
absoluteStartOffset + flockStruct.l_len
|
|
770
|
+
);
|
|
771
|
+
updateFlockStruct(flockStructAddr, {
|
|
772
|
+
l_type: F_UNLCK
|
|
773
|
+
});
|
|
774
|
+
return 0;
|
|
775
|
+
}
|
|
776
|
+
js_wasm_trace(
|
|
777
|
+
"fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock type=%s start=0x%x end=0x%x conflictingLock %d",
|
|
778
|
+
fd,
|
|
779
|
+
vfsPath,
|
|
780
|
+
conflictingLock.type,
|
|
781
|
+
conflictingLock.start,
|
|
782
|
+
conflictingLock.end,
|
|
783
|
+
conflictingLock.pid
|
|
784
|
+
);
|
|
785
|
+
const fcntlLockState = locking.lockStateToFcntl[conflictingLock.type];
|
|
786
|
+
updateFlockStruct(flockStructAddr, {
|
|
787
|
+
l_type: fcntlLockState,
|
|
788
|
+
l_whence: SEEK_SET,
|
|
789
|
+
l_start: conflictingLock.start,
|
|
790
|
+
l_len: BigInt(
|
|
791
|
+
conflictingLock.end - conflictingLock.start
|
|
792
|
+
),
|
|
793
|
+
l_pid: conflictingLock.pid
|
|
794
|
+
});
|
|
795
|
+
return 0;
|
|
796
|
+
} catch (e) {
|
|
797
|
+
js_wasm_trace(
|
|
798
|
+
"fcntl(%d, F_GETLK) %s findFirstConflictingByteRangeLock error %s",
|
|
799
|
+
fd,
|
|
800
|
+
vfsPath,
|
|
801
|
+
e
|
|
802
|
+
);
|
|
803
|
+
return -EINVAL;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
case F_SETLKW:
|
|
807
|
+
case F_SETLK: {
|
|
808
|
+
js_wasm_trace("fcntl(%d, F_SETLK)", fd);
|
|
809
|
+
const [vfsPath, vfsPathErrno] = locking.get_vfs_path_from_fd(fd);
|
|
810
|
+
if (vfsPathErrno !== 0) {
|
|
811
|
+
js_wasm_trace(
|
|
812
|
+
"fcntl(%d, F_SETLK) %s get_vfs_path_from_fd errno %d",
|
|
813
|
+
fd,
|
|
814
|
+
vfsPath,
|
|
815
|
+
vfsPathErrno
|
|
816
|
+
);
|
|
817
|
+
return -vfsPathErrno;
|
|
818
|
+
}
|
|
819
|
+
if (!locking.is_path_to_shared_fs(vfsPath)) {
|
|
820
|
+
js_wasm_trace(
|
|
821
|
+
"fcntl(%d, F_SETLK) locking is not implemented for non-NodeFS path %s",
|
|
822
|
+
fd,
|
|
823
|
+
vfsPath
|
|
824
|
+
);
|
|
825
|
+
return 0;
|
|
826
|
+
}
|
|
827
|
+
const varArgsAccessor = new VarArgsAccessor(varargs);
|
|
828
|
+
const flockStructAddr = varArgsAccessor.getNextAsPointer();
|
|
829
|
+
const flockStruct = readFlockStruct(flockStructAddr);
|
|
830
|
+
const [absoluteStartOffset, baseAddressErrno] = getBaseAddress(
|
|
831
|
+
fd,
|
|
832
|
+
flockStruct.l_whence,
|
|
833
|
+
flockStruct.l_start
|
|
834
|
+
);
|
|
835
|
+
if (baseAddressErrno !== 0) {
|
|
836
|
+
js_wasm_trace(
|
|
837
|
+
"fcntl(%d, F_SETLK) %s get_base_address errno %d",
|
|
838
|
+
fd,
|
|
839
|
+
vfsPath,
|
|
840
|
+
baseAddressErrno
|
|
841
|
+
);
|
|
842
|
+
return -EINVAL;
|
|
843
|
+
}
|
|
844
|
+
if (!(flockStruct.l_type in locking.fcntlToLockState)) {
|
|
845
|
+
js_wasm_trace(
|
|
846
|
+
"fcntl(%d, F_SETLK) %s invalid lock type %d",
|
|
847
|
+
fd,
|
|
848
|
+
vfsPath,
|
|
849
|
+
flockStruct.l_type
|
|
850
|
+
);
|
|
851
|
+
return -EINVAL;
|
|
852
|
+
}
|
|
853
|
+
const paramsCheckErrno = locking.check_lock_params(
|
|
854
|
+
fd,
|
|
855
|
+
flockStruct.l_type
|
|
856
|
+
);
|
|
857
|
+
if (paramsCheckErrno !== 0) {
|
|
858
|
+
js_wasm_trace(
|
|
859
|
+
"fcntl(%d, F_SETLK) %s check_lock_params errno %d",
|
|
860
|
+
fd,
|
|
861
|
+
vfsPath,
|
|
862
|
+
paramsCheckErrno
|
|
863
|
+
);
|
|
864
|
+
return -paramsCheckErrno;
|
|
865
|
+
}
|
|
866
|
+
const [nativeFd, nativeFdErrno] = locking.get_native_fd_from_emscripten_fd(fd);
|
|
867
|
+
if (nativeFdErrno !== 0) {
|
|
868
|
+
js_wasm_trace(
|
|
869
|
+
"fcntl(%d, F_SETLK) get_native_fd_from_emscripten_fd errno %d",
|
|
870
|
+
fd,
|
|
871
|
+
nativeFdErrno
|
|
872
|
+
);
|
|
873
|
+
return -nativeFdErrno;
|
|
874
|
+
}
|
|
875
|
+
const requestedLockType = locking.fcntlToLockState[flockStruct.l_type];
|
|
876
|
+
const rangeLock = {
|
|
877
|
+
type: requestedLockType,
|
|
878
|
+
start: absoluteStartOffset,
|
|
879
|
+
end: absoluteStartOffset + flockStruct.l_len,
|
|
880
|
+
pid,
|
|
881
|
+
fd: nativeFd
|
|
882
|
+
};
|
|
883
|
+
try {
|
|
884
|
+
const nativeFilePath = locking.get_native_path_from_vfs_path(vfsPath);
|
|
885
|
+
js_wasm_trace(
|
|
886
|
+
"fcntl(%d, F_SETLK) %s calling lockFileByteRange for range lock %s",
|
|
887
|
+
fd,
|
|
888
|
+
vfsPath,
|
|
889
|
+
rangeLock
|
|
890
|
+
);
|
|
891
|
+
const waitForLock = cmd === F_SETLKW;
|
|
892
|
+
const succeeded = fileLockManager.lockFileByteRange(
|
|
893
|
+
nativeFilePath,
|
|
894
|
+
rangeLock,
|
|
895
|
+
waitForLock
|
|
896
|
+
);
|
|
897
|
+
if (succeeded) {
|
|
898
|
+
locking.maybeLockedFds.add(nativeFd);
|
|
899
|
+
}
|
|
900
|
+
js_wasm_trace(
|
|
901
|
+
"fcntl(%d, F_SETLK) %s lockFileByteRange returned %d for range lock %s",
|
|
902
|
+
fd,
|
|
903
|
+
vfsPath,
|
|
904
|
+
succeeded,
|
|
905
|
+
rangeLock
|
|
906
|
+
);
|
|
907
|
+
return succeeded ? 0 : -EAGAIN;
|
|
908
|
+
} catch (e) {
|
|
909
|
+
js_wasm_trace(
|
|
910
|
+
"fcntl(%d, F_SETLK) %s lockFileByteRange error %s for range lock %s",
|
|
911
|
+
fd,
|
|
912
|
+
vfsPath,
|
|
913
|
+
e,
|
|
914
|
+
rangeLock
|
|
915
|
+
);
|
|
916
|
+
return -EINVAL;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
case F_SETFL: {
|
|
920
|
+
let arg = 0;
|
|
921
|
+
if (varargs !== void 0) {
|
|
922
|
+
const varArgsAccessor = new VarArgsAccessor(varargs);
|
|
923
|
+
arg = varArgsAccessor.getNextAsInt();
|
|
924
|
+
}
|
|
925
|
+
const stream = getStreamFromFD(fd);
|
|
926
|
+
const SETFL_MASK = O_APPEND | O_NONBLOCK;
|
|
927
|
+
stream.flags = arg & SETFL_MASK | stream.flags & ~SETFL_MASK;
|
|
928
|
+
return 0;
|
|
929
|
+
}
|
|
930
|
+
default:
|
|
931
|
+
return builtin_fcntl64(fd, cmd, varargs);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
function flock(fd, op) {
|
|
935
|
+
js_wasm_trace("flock(%d, %d)", fd, op);
|
|
936
|
+
if (!fileLockManager) {
|
|
937
|
+
js_wasm_trace(
|
|
938
|
+
"flock(%d, %d) file lock manager is not available. succeed by default as Emscripten does.",
|
|
939
|
+
fd,
|
|
940
|
+
op
|
|
941
|
+
);
|
|
942
|
+
return 0;
|
|
943
|
+
}
|
|
944
|
+
const flockToLockOpType = {
|
|
945
|
+
[LOCK_SH]: "shared",
|
|
946
|
+
[LOCK_EX]: "exclusive",
|
|
947
|
+
[LOCK_UN]: "unlock"
|
|
948
|
+
};
|
|
949
|
+
const [vfsPath, vfsPathErrno] = locking.get_vfs_path_from_fd(fd);
|
|
950
|
+
if (vfsPathErrno !== 0) {
|
|
951
|
+
js_wasm_trace(
|
|
952
|
+
"flock(%d, %d) get_vfs_path_from_fd errno %d",
|
|
953
|
+
fd,
|
|
954
|
+
op,
|
|
955
|
+
vfsPath,
|
|
956
|
+
vfsPathErrno
|
|
957
|
+
);
|
|
958
|
+
return -vfsPathErrno;
|
|
959
|
+
}
|
|
960
|
+
if (!locking.is_path_to_shared_fs(vfsPath)) {
|
|
961
|
+
js_wasm_trace(
|
|
962
|
+
"flock(%d, %d) locking is not implemented for non-NodeFS path %s",
|
|
963
|
+
fd,
|
|
964
|
+
op,
|
|
965
|
+
vfsPath
|
|
966
|
+
);
|
|
967
|
+
return 0;
|
|
968
|
+
}
|
|
969
|
+
const paramsCheckErrno = locking.check_lock_params(fd, op);
|
|
970
|
+
if (paramsCheckErrno !== 0) {
|
|
971
|
+
js_wasm_trace(
|
|
972
|
+
"flock(%d, %d) %s check_lock_params errno %d",
|
|
973
|
+
fd,
|
|
974
|
+
op,
|
|
975
|
+
vfsPath,
|
|
976
|
+
paramsCheckErrno
|
|
977
|
+
);
|
|
978
|
+
return -paramsCheckErrno;
|
|
979
|
+
}
|
|
980
|
+
const maskedOp = op & (LOCK_SH | LOCK_EX | LOCK_UN);
|
|
981
|
+
const waitForLock = (op & LOCK_NB) === 0;
|
|
982
|
+
if (maskedOp === 0) {
|
|
983
|
+
js_wasm_trace("flock(%d, %d) invalid flock() operation", fd, op);
|
|
984
|
+
return -EINVAL;
|
|
985
|
+
}
|
|
986
|
+
const lockOpType = flockToLockOpType[maskedOp];
|
|
987
|
+
if (lockOpType === void 0) {
|
|
988
|
+
js_wasm_trace("flock(%d, %d) invalid flock() operation", fd, op);
|
|
989
|
+
return -EINVAL;
|
|
990
|
+
}
|
|
991
|
+
const [nativeFd, nativeFdErrno] = locking.get_native_fd_from_emscripten_fd(fd);
|
|
992
|
+
if (nativeFdErrno !== 0) {
|
|
993
|
+
js_wasm_trace(
|
|
994
|
+
"js_flock(%d, %d) get_native_fd_from_emscripten_fd errno %d",
|
|
995
|
+
fd,
|
|
996
|
+
op,
|
|
997
|
+
nativeFdErrno
|
|
998
|
+
);
|
|
999
|
+
return -nativeFdErrno;
|
|
1000
|
+
}
|
|
1001
|
+
try {
|
|
1002
|
+
const nativeFilePath = locking.get_native_path_from_vfs_path(vfsPath);
|
|
1003
|
+
const succeeded = fileLockManager.lockWholeFile(nativeFilePath, {
|
|
1004
|
+
type: lockOpType,
|
|
1005
|
+
pid,
|
|
1006
|
+
fd: nativeFd,
|
|
1007
|
+
waitForLock
|
|
1008
|
+
});
|
|
1009
|
+
js_wasm_trace(
|
|
1010
|
+
"flock(%d, %d) lockWholeFile %s returned %d",
|
|
1011
|
+
fd,
|
|
1012
|
+
op,
|
|
1013
|
+
vfsPath,
|
|
1014
|
+
succeeded
|
|
1015
|
+
);
|
|
1016
|
+
if (succeeded) {
|
|
1017
|
+
locking.maybeLockedFds.add(nativeFd);
|
|
1018
|
+
}
|
|
1019
|
+
return succeeded ? 0 : -EWOULDBLOCK;
|
|
1020
|
+
} catch (e) {
|
|
1021
|
+
js_wasm_trace("flock(%d, %d) lockWholeFile error %s", fd, op, e);
|
|
1022
|
+
return -EINVAL;
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function fd_close(fd) {
|
|
1026
|
+
if (!fileLockManager) {
|
|
1027
|
+
js_wasm_trace(
|
|
1028
|
+
"fd_close(%d) file lock manager is not available. delegate to Emscripten builtin fd_close.",
|
|
1029
|
+
fd
|
|
1030
|
+
);
|
|
1031
|
+
return builtin_fd_close(fd);
|
|
1032
|
+
}
|
|
1033
|
+
const [vfsPath, vfsPathResolutionErrno] = locking.get_vfs_path_from_fd(fd);
|
|
1034
|
+
const [nativeFd, nativeFdErrno] = locking.get_native_fd_from_emscripten_fd(fd);
|
|
1035
|
+
const fdCloseResult = builtin_fd_close(fd);
|
|
1036
|
+
if (fdCloseResult !== 0) {
|
|
1037
|
+
js_wasm_trace(
|
|
1038
|
+
"fd_close(%d) %s result %d",
|
|
1039
|
+
fd,
|
|
1040
|
+
vfsPath,
|
|
1041
|
+
fdCloseResult
|
|
1042
|
+
);
|
|
1043
|
+
return fdCloseResult;
|
|
1044
|
+
}
|
|
1045
|
+
if (!locking.maybeLockedFds.has(nativeFd)) {
|
|
1046
|
+
js_wasm_trace(
|
|
1047
|
+
"fd_close(%d) not in maybe-locked-list %s result %d",
|
|
1048
|
+
fd,
|
|
1049
|
+
vfsPath,
|
|
1050
|
+
fdCloseResult
|
|
1051
|
+
);
|
|
1052
|
+
return fdCloseResult;
|
|
1053
|
+
}
|
|
1054
|
+
if (vfsPathResolutionErrno !== 0) {
|
|
1055
|
+
js_wasm_trace(
|
|
1056
|
+
"fd_close(%d) get_vfs_path_from_fd error %d",
|
|
1057
|
+
fd,
|
|
1058
|
+
vfsPathResolutionErrno
|
|
1059
|
+
);
|
|
1060
|
+
return fdCloseResult;
|
|
1061
|
+
}
|
|
1062
|
+
if (nativeFdErrno !== 0) {
|
|
1063
|
+
js_wasm_trace(
|
|
1064
|
+
"fd_close(%d) %s get_native_fd_from_emscripten_fd error %d",
|
|
1065
|
+
fd,
|
|
1066
|
+
vfsPath,
|
|
1067
|
+
nativeFdErrno
|
|
1068
|
+
);
|
|
1069
|
+
return fdCloseResult;
|
|
1070
|
+
}
|
|
1071
|
+
if (!locking.is_path_to_shared_fs(vfsPath)) {
|
|
1072
|
+
return fdCloseResult;
|
|
1073
|
+
}
|
|
1074
|
+
try {
|
|
1075
|
+
js_wasm_trace("fd_close(%d) %s release locks", fd, vfsPath);
|
|
1076
|
+
const nativeFilePath = locking.get_native_path_from_vfs_path(vfsPath);
|
|
1077
|
+
fileLockManager.releaseLocksOnFdClose(
|
|
1078
|
+
pid,
|
|
1079
|
+
nativeFd,
|
|
1080
|
+
nativeFilePath
|
|
1081
|
+
);
|
|
1082
|
+
js_wasm_trace("fd_close(%d) %s release locks success", fd, vfsPath);
|
|
1083
|
+
} catch (e) {
|
|
1084
|
+
js_wasm_trace("fd_close(%d) %s error '%s'", fd, vfsPath, e);
|
|
1085
|
+
}
|
|
1086
|
+
return fdCloseResult;
|
|
1087
|
+
}
|
|
1088
|
+
function js_release_file_locks() {
|
|
1089
|
+
js_wasm_trace("js_release_file_locks()");
|
|
1090
|
+
if (pid === void 0) {
|
|
1091
|
+
js_wasm_trace("js_release_file_locks pid is undefined");
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
if (fileLockManager === void 0) {
|
|
1095
|
+
js_wasm_trace(
|
|
1096
|
+
"js_release_file_locks file lock manager is undefined"
|
|
1097
|
+
);
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
try {
|
|
1101
|
+
fileLockManager.releaseLocksForProcess(pid);
|
|
1102
|
+
js_wasm_trace("js_release_file_locks succeeded");
|
|
1103
|
+
} catch (e) {
|
|
1104
|
+
js_wasm_trace("js_release_file_locks error %s", e);
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
async function gethostbyname(hostname) {
|
|
1108
|
+
const { address } = await (0, import_promises.lookup)(hostname, {
|
|
1109
|
+
family: 4,
|
|
1110
|
+
verbatim: false
|
|
1111
|
+
});
|
|
1112
|
+
return address;
|
|
1113
|
+
}
|
|
1114
|
+
return {
|
|
1115
|
+
fcntl64,
|
|
1116
|
+
flock,
|
|
1117
|
+
fd_close,
|
|
1118
|
+
js_release_file_locks,
|
|
1119
|
+
gethostbyname
|
|
1120
|
+
};
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
// packages/php-wasm/node/src/lib/load-runtime.ts
|
|
427
1124
|
var import_fs5 = __toESM(require("fs"), 1);
|
|
428
1125
|
|
|
1126
|
+
// packages/php-wasm/node/src/lib/file-lock-manager-for-posix.ts
|
|
1127
|
+
var import_universal2 = require("@php-wasm/universal");
|
|
1128
|
+
var import_fs_ext_extra_prebuilt = require("fs-ext-extra-prebuilt");
|
|
1129
|
+
var import_logger2 = require("@php-wasm/logger");
|
|
1130
|
+
function isLockDenialError(e) {
|
|
1131
|
+
if (e && typeof e === "object" && "code" in e) {
|
|
1132
|
+
const code = e.code;
|
|
1133
|
+
return code === "EWOULDBLOCK" || code === "EAGAIN" || code === "EACCES";
|
|
1134
|
+
}
|
|
1135
|
+
return false;
|
|
1136
|
+
}
|
|
1137
|
+
var FileLockManagerForPosix = class {
|
|
1138
|
+
constructor() {
|
|
1139
|
+
this.wholeFileLockMap = /* @__PURE__ */ new Map();
|
|
1140
|
+
this.rangeLockedFds = /* @__PURE__ */ new Map();
|
|
1141
|
+
}
|
|
1142
|
+
lockWholeFile(path2, op) {
|
|
1143
|
+
const opType = op.type === "unlock" ? "un" : op.waitForLock ? op.type === "exclusive" ? "ex" : "sh" : op.type === "exclusive" ? "exnb" : "shnb";
|
|
1144
|
+
try {
|
|
1145
|
+
(0, import_fs_ext_extra_prebuilt.flockSync)(op.fd, opType);
|
|
1146
|
+
if (op.type === "unlock") {
|
|
1147
|
+
this.wholeFileLockMap.get(op.pid)?.delete(op.fd);
|
|
1148
|
+
} else {
|
|
1149
|
+
if (!this.wholeFileLockMap.has(op.pid)) {
|
|
1150
|
+
this.wholeFileLockMap.set(op.pid, /* @__PURE__ */ new Map());
|
|
1151
|
+
}
|
|
1152
|
+
this.wholeFileLockMap.get(op.pid).set(op.fd, {
|
|
1153
|
+
...op,
|
|
1154
|
+
path: path2
|
|
1155
|
+
});
|
|
1156
|
+
}
|
|
1157
|
+
return true;
|
|
1158
|
+
} catch (e) {
|
|
1159
|
+
if (!isLockDenialError(e)) {
|
|
1160
|
+
import_logger2.logger.warn("flock(): unexpected error", e);
|
|
1161
|
+
}
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
lockFileByteRange(path2, op, waitForLock) {
|
|
1166
|
+
if (op.start === op.end) {
|
|
1167
|
+
op = {
|
|
1168
|
+
...op,
|
|
1169
|
+
end: import_universal2.MAX_ADDRESSABLE_FILE_OFFSET
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
const fcntlCmd = waitForLock ? "setlkw" : "setlk";
|
|
1173
|
+
const fcntlOp = op.type === "unlocked" ? import_fs_ext_extra_prebuilt.constants.F_UNLCK : op.type === "exclusive" ? import_fs_ext_extra_prebuilt.constants.F_WRLCK : import_fs_ext_extra_prebuilt.constants.F_RDLCK;
|
|
1174
|
+
try {
|
|
1175
|
+
(0, import_fs_ext_extra_prebuilt.fcntlSync)(
|
|
1176
|
+
op.fd,
|
|
1177
|
+
fcntlCmd,
|
|
1178
|
+
fcntlOp,
|
|
1179
|
+
Number(op.start),
|
|
1180
|
+
Number(op.end - op.start)
|
|
1181
|
+
);
|
|
1182
|
+
if (!this.rangeLockedFds.has(op.pid)) {
|
|
1183
|
+
this.rangeLockedFds.set(op.pid, /* @__PURE__ */ new Map());
|
|
1184
|
+
}
|
|
1185
|
+
const pidMap = this.rangeLockedFds.get(op.pid);
|
|
1186
|
+
if (!pidMap.has(path2)) {
|
|
1187
|
+
pidMap.set(path2, /* @__PURE__ */ new Set());
|
|
1188
|
+
}
|
|
1189
|
+
pidMap.get(path2).add(op.fd);
|
|
1190
|
+
return true;
|
|
1191
|
+
} catch (e) {
|
|
1192
|
+
if (!isLockDenialError(e)) {
|
|
1193
|
+
import_logger2.logger.warn("fcntl(): unexpected error", e);
|
|
1194
|
+
}
|
|
1195
|
+
return false;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
findFirstConflictingByteRangeLock(path2, op) {
|
|
1199
|
+
if (op.type === "unlocked") {
|
|
1200
|
+
return void 0;
|
|
1201
|
+
}
|
|
1202
|
+
const obtainedLock = this.lockFileByteRange(path2, op, false);
|
|
1203
|
+
if (obtainedLock) {
|
|
1204
|
+
this.lockFileByteRange(path2, { ...op, type: "unlocked" }, true);
|
|
1205
|
+
return void 0;
|
|
1206
|
+
}
|
|
1207
|
+
return {
|
|
1208
|
+
type: "exclusive",
|
|
1209
|
+
start: 0n,
|
|
1210
|
+
end: 0xffffffffffffffffn,
|
|
1211
|
+
pid: -1
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
releaseLocksForProcess(targetPid) {
|
|
1215
|
+
const fdMap = this.wholeFileLockMap.get(targetPid);
|
|
1216
|
+
if (fdMap) {
|
|
1217
|
+
for (const storedLock of fdMap.values()) {
|
|
1218
|
+
try {
|
|
1219
|
+
this.lockWholeFile(storedLock.path, {
|
|
1220
|
+
...storedLock,
|
|
1221
|
+
type: "unlock"
|
|
1222
|
+
});
|
|
1223
|
+
} catch (e) {
|
|
1224
|
+
import_logger2.logger.error(
|
|
1225
|
+
`releaseLocksForProcess: failed to unlock whole-file lock for pid=${targetPid} fd=${storedLock.fd}`,
|
|
1226
|
+
e
|
|
1227
|
+
);
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
this.wholeFileLockMap.delete(targetPid);
|
|
1231
|
+
}
|
|
1232
|
+
for (const [path2, fdSet] of this.rangeLockedFds.get(targetPid) ?? []) {
|
|
1233
|
+
for (const fd of fdSet) {
|
|
1234
|
+
try {
|
|
1235
|
+
this.lockFileByteRange(
|
|
1236
|
+
path2,
|
|
1237
|
+
{
|
|
1238
|
+
pid: targetPid,
|
|
1239
|
+
fd,
|
|
1240
|
+
type: "unlocked",
|
|
1241
|
+
start: 0n,
|
|
1242
|
+
end: import_universal2.MAX_ADDRESSABLE_FILE_OFFSET
|
|
1243
|
+
},
|
|
1244
|
+
false
|
|
1245
|
+
);
|
|
1246
|
+
} catch (e) {
|
|
1247
|
+
import_logger2.logger.error(
|
|
1248
|
+
`releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${fd} path=${path2}`,
|
|
1249
|
+
e
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
this.rangeLockedFds.delete(targetPid);
|
|
1255
|
+
}
|
|
1256
|
+
releaseLocksOnFdClose(targetPid, targetFd, targetPath) {
|
|
1257
|
+
this.wholeFileLockMap.get(targetPid)?.delete(targetFd);
|
|
1258
|
+
this.rangeLockedFds.get(targetPid)?.delete(targetPath);
|
|
1259
|
+
}
|
|
1260
|
+
};
|
|
1261
|
+
|
|
1262
|
+
// packages/php-wasm/node/src/lib/file-lock-manager-for-windows.ts
|
|
1263
|
+
var import_fs_ext_extra_prebuilt2 = require("fs-ext-extra-prebuilt");
|
|
1264
|
+
var import_logger3 = require("@php-wasm/logger");
|
|
1265
|
+
var import_universal3 = require("@php-wasm/universal");
|
|
1266
|
+
function toLowAndHigh32BitNumbers(num) {
|
|
1267
|
+
const low = Number(num & 0xffffffffn);
|
|
1268
|
+
const high = Number(num >> 32n & 0xffffffffn);
|
|
1269
|
+
return [low, high];
|
|
1270
|
+
}
|
|
1271
|
+
function isErrnoError(e) {
|
|
1272
|
+
return e !== null && typeof e === "object" && ("errno" in e || "code" in e || "syscall" in e);
|
|
1273
|
+
}
|
|
1274
|
+
function tryLockFileExSync(fd, flags, start, end) {
|
|
1275
|
+
const [offsetLow, offsetHigh] = toLowAndHigh32BitNumbers(start);
|
|
1276
|
+
const [lengthLow, lengthHigh] = toLowAndHigh32BitNumbers(end - start);
|
|
1277
|
+
try {
|
|
1278
|
+
(0, import_fs_ext_extra_prebuilt2.lockFileExSync)(fd, flags, offsetLow, offsetHigh, lengthLow, lengthHigh);
|
|
1279
|
+
return true;
|
|
1280
|
+
} catch (e) {
|
|
1281
|
+
if (!isErrnoError(e)) {
|
|
1282
|
+
throw e;
|
|
1283
|
+
}
|
|
1284
|
+
return false;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
function tryUnlockFileExSync(fd, start, end) {
|
|
1288
|
+
const [offsetLow, offsetHigh] = toLowAndHigh32BitNumbers(start);
|
|
1289
|
+
const [lengthLow, lengthHigh] = toLowAndHigh32BitNumbers(end - start);
|
|
1290
|
+
try {
|
|
1291
|
+
(0, import_fs_ext_extra_prebuilt2.unlockFileExSync)(fd, offsetLow, offsetHigh, lengthLow, lengthHigh);
|
|
1292
|
+
return true;
|
|
1293
|
+
} catch (e) {
|
|
1294
|
+
if (!isErrnoError(e)) {
|
|
1295
|
+
throw e;
|
|
1296
|
+
}
|
|
1297
|
+
return false;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
var FileLockManagerForWindows = class {
|
|
1301
|
+
constructor() {
|
|
1302
|
+
this.wholeFileLockMap = /* @__PURE__ */ new Map();
|
|
1303
|
+
this.rangeLockedFds = /* @__PURE__ */ new Map();
|
|
1304
|
+
}
|
|
1305
|
+
lockWholeFile(path2, op) {
|
|
1306
|
+
const start = 0n;
|
|
1307
|
+
const end = 2n ** 64n - 1n;
|
|
1308
|
+
if (op.type === "unlock") {
|
|
1309
|
+
const success2 = tryUnlockFileExSync(op.fd, start, end);
|
|
1310
|
+
if (success2) {
|
|
1311
|
+
this.wholeFileLockMap.get(op.pid)?.delete(op.fd);
|
|
1312
|
+
if (this.wholeFileLockMap.get(op.pid)?.size === 0) {
|
|
1313
|
+
this.wholeFileLockMap.delete(op.pid);
|
|
1314
|
+
}
|
|
1315
|
+
} else {
|
|
1316
|
+
import_logger3.logger.warn(
|
|
1317
|
+
`lockWholeFile: unlock failed for pid=${op.pid} fd=${op.fd} path=${path2}`
|
|
1318
|
+
);
|
|
1319
|
+
}
|
|
1320
|
+
return success2;
|
|
1321
|
+
}
|
|
1322
|
+
const preexistingLock = this.wholeFileLockMap.get(op.pid)?.get(op.fd);
|
|
1323
|
+
if (op.type === preexistingLock?.type) {
|
|
1324
|
+
return true;
|
|
1325
|
+
}
|
|
1326
|
+
let flags = 0;
|
|
1327
|
+
if (!op.waitForLock) {
|
|
1328
|
+
flags |= import_fs_ext_extra_prebuilt2.constants.LOCKFILE_FAIL_IMMEDIATELY;
|
|
1329
|
+
}
|
|
1330
|
+
let success = false;
|
|
1331
|
+
if (op.type === "shared") {
|
|
1332
|
+
success = tryLockFileExSync(op.fd, flags, start, end);
|
|
1333
|
+
if (success && preexistingLock?.type === "exclusive") {
|
|
1334
|
+
const exclusiveUnlockSuccess = tryUnlockFileExSync(
|
|
1335
|
+
op.fd,
|
|
1336
|
+
start,
|
|
1337
|
+
end
|
|
1338
|
+
);
|
|
1339
|
+
if (!exclusiveUnlockSuccess) {
|
|
1340
|
+
const message = "Failed to unlock preexisting exclusive lock after failing to obtain shared lock";
|
|
1341
|
+
import_logger3.logger.error(message);
|
|
1342
|
+
throw new Error(message);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
} else if (op.type === "exclusive") {
|
|
1346
|
+
flags |= import_fs_ext_extra_prebuilt2.constants.LOCKFILE_EXCLUSIVE_LOCK;
|
|
1347
|
+
let sharedUnlockSuccess;
|
|
1348
|
+
if (preexistingLock?.type === "shared") {
|
|
1349
|
+
sharedUnlockSuccess = tryUnlockFileExSync(op.fd, start, end);
|
|
1350
|
+
if (!sharedUnlockSuccess) {
|
|
1351
|
+
import_logger3.logger.warn(
|
|
1352
|
+
`lockWholeFile: failed to release shared lock before exclusive upgrade for pid=${op.pid} fd=${op.fd}`
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
success = tryLockFileExSync(op.fd, flags, start, end);
|
|
1357
|
+
if (!success) {
|
|
1358
|
+
import_logger3.logger.debug(
|
|
1359
|
+
`lockWholeFile: failed to obtain exclusive lock for pid=${op.pid} fd=${op.fd}`
|
|
1360
|
+
);
|
|
1361
|
+
}
|
|
1362
|
+
if (!success && sharedUnlockSuccess) {
|
|
1363
|
+
const sharedReLockResult = tryLockFileExSync(
|
|
1364
|
+
op.fd,
|
|
1365
|
+
// Wait to restore the shared lock.
|
|
1366
|
+
0,
|
|
1367
|
+
start,
|
|
1368
|
+
end
|
|
1369
|
+
);
|
|
1370
|
+
if (!sharedReLockResult) {
|
|
1371
|
+
const message = "Failed to re-lock preexisting shared lock after failing to obtain exclusive lock";
|
|
1372
|
+
import_logger3.logger.error(message);
|
|
1373
|
+
throw new Error(message);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
} else {
|
|
1377
|
+
throw new Error(`Unexpected wholeFileLock() op: '${op.type}'`);
|
|
1378
|
+
}
|
|
1379
|
+
if (success) {
|
|
1380
|
+
if (!this.wholeFileLockMap.has(op.pid)) {
|
|
1381
|
+
this.wholeFileLockMap.set(op.pid, /* @__PURE__ */ new Map());
|
|
1382
|
+
}
|
|
1383
|
+
this.wholeFileLockMap.get(op.pid).set(op.fd, {
|
|
1384
|
+
...op,
|
|
1385
|
+
path: path2
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
return success;
|
|
1389
|
+
}
|
|
1390
|
+
lockFileByteRange(path2, op, waitForLock) {
|
|
1391
|
+
if (op.start === op.end) {
|
|
1392
|
+
op = {
|
|
1393
|
+
...op,
|
|
1394
|
+
end: import_universal3.MAX_ADDRESSABLE_FILE_OFFSET
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
if (!this.rangeLockedFds.has(path2)) {
|
|
1398
|
+
this.rangeLockedFds.set(path2, new import_universal3.FileLockIntervalTree());
|
|
1399
|
+
}
|
|
1400
|
+
const lockedRangeTree = this.rangeLockedFds.get(path2);
|
|
1401
|
+
const overlappingLocks = lockedRangeTree.findOverlapping(op);
|
|
1402
|
+
let preexistingLock;
|
|
1403
|
+
if (overlappingLocks.length === 1 && overlappingLocks[0].pid === op.pid && // NOTE: FD shouldn't matter for fcntl() F_SETLK because it is a process-level lock,
|
|
1404
|
+
// but it matters for Windows where locks are fd-specific.
|
|
1405
|
+
overlappingLocks[0].fd === op.fd && overlappingLocks[0].start === op.start && overlappingLocks[0].end === op.end) {
|
|
1406
|
+
preexistingLock = overlappingLocks[0];
|
|
1407
|
+
}
|
|
1408
|
+
if (op.type === preexistingLock?.type) {
|
|
1409
|
+
return true;
|
|
1410
|
+
}
|
|
1411
|
+
let flags = 0;
|
|
1412
|
+
if (!waitForLock) {
|
|
1413
|
+
flags |= import_fs_ext_extra_prebuilt2.constants.LOCKFILE_FAIL_IMMEDIATELY;
|
|
1414
|
+
}
|
|
1415
|
+
if (op.type === "shared") {
|
|
1416
|
+
const success = tryLockFileExSync(op.fd, flags, op.start, op.end);
|
|
1417
|
+
if (!success) {
|
|
1418
|
+
return false;
|
|
1419
|
+
}
|
|
1420
|
+
if (preexistingLock?.type === "exclusive") {
|
|
1421
|
+
const releasedPreexistingExclusiveLock = tryUnlockFileExSync(
|
|
1422
|
+
preexistingLock.fd,
|
|
1423
|
+
preexistingLock.start,
|
|
1424
|
+
preexistingLock.end
|
|
1425
|
+
);
|
|
1426
|
+
if (!releasedPreexistingExclusiveLock) {
|
|
1427
|
+
const message = "Failed to unlock preexisting exclusive lock after obtaining a shared lock";
|
|
1428
|
+
import_logger3.logger.error(message);
|
|
1429
|
+
throw new Error(message);
|
|
1430
|
+
}
|
|
1431
|
+
lockedRangeTree.remove(preexistingLock);
|
|
1432
|
+
}
|
|
1433
|
+
lockedRangeTree.insert(op);
|
|
1434
|
+
return true;
|
|
1435
|
+
} else if (op.type === "exclusive") {
|
|
1436
|
+
let sharedUnlockSuccess;
|
|
1437
|
+
if (preexistingLock?.type === "shared") {
|
|
1438
|
+
sharedUnlockSuccess = tryUnlockFileExSync(
|
|
1439
|
+
op.fd,
|
|
1440
|
+
op.start,
|
|
1441
|
+
op.end
|
|
1442
|
+
);
|
|
1443
|
+
}
|
|
1444
|
+
if (op.type === "exclusive") {
|
|
1445
|
+
flags |= import_fs_ext_extra_prebuilt2.constants.LOCKFILE_EXCLUSIVE_LOCK;
|
|
1446
|
+
}
|
|
1447
|
+
const success = tryLockFileExSync(op.fd, flags, op.start, op.end);
|
|
1448
|
+
if (!success) {
|
|
1449
|
+
if (preexistingLock && sharedUnlockSuccess) {
|
|
1450
|
+
const sharedRelockSuccess = tryLockFileExSync(
|
|
1451
|
+
op.fd,
|
|
1452
|
+
0,
|
|
1453
|
+
op.start,
|
|
1454
|
+
op.end
|
|
1455
|
+
);
|
|
1456
|
+
if (!sharedRelockSuccess) {
|
|
1457
|
+
const message = "Failed to re-lock preexisting shared lock after failing to obtain exclusive lock";
|
|
1458
|
+
import_logger3.logger.error(message);
|
|
1459
|
+
throw new Error(message);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
return false;
|
|
1463
|
+
}
|
|
1464
|
+
lockedRangeTree.insert(op);
|
|
1465
|
+
return true;
|
|
1466
|
+
} else {
|
|
1467
|
+
const intersectingLocksForThisProcess = overlappingLocks.filter((lock) => lock.pid === op.pid).filter((lock) => lock.start >= op.start && lock.end <= op.end);
|
|
1468
|
+
for (const lock of intersectingLocksForThisProcess) {
|
|
1469
|
+
const success = tryUnlockFileExSync(
|
|
1470
|
+
lock.fd,
|
|
1471
|
+
lock.start,
|
|
1472
|
+
lock.end
|
|
1473
|
+
);
|
|
1474
|
+
if (!success) {
|
|
1475
|
+
import_logger3.logger.warn(
|
|
1476
|
+
`lockFileByteRange: unlock failed for pid=${op.pid} fd=${lock.fd} range=[${lock.start},${lock.end}]`
|
|
1477
|
+
);
|
|
1478
|
+
return false;
|
|
1479
|
+
}
|
|
1480
|
+
lockedRangeTree.remove(lock);
|
|
1481
|
+
}
|
|
1482
|
+
return true;
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
findFirstConflictingByteRangeLock(path2, op) {
|
|
1486
|
+
if (op.type === "unlocked") {
|
|
1487
|
+
return void 0;
|
|
1488
|
+
}
|
|
1489
|
+
const obtainedLock = !!this.lockFileByteRange(path2, op, false);
|
|
1490
|
+
this.lockFileByteRange(path2, { ...op, type: "unlocked" }, false);
|
|
1491
|
+
if (obtainedLock) {
|
|
1492
|
+
return void 0;
|
|
1493
|
+
}
|
|
1494
|
+
return {
|
|
1495
|
+
type: "exclusive",
|
|
1496
|
+
start: 0n,
|
|
1497
|
+
end: 0xffffffffffffffffn,
|
|
1498
|
+
pid: -1
|
|
1499
|
+
};
|
|
1500
|
+
}
|
|
1501
|
+
releaseLocksForProcess(targetPid) {
|
|
1502
|
+
const fdMap = this.wholeFileLockMap.get(targetPid);
|
|
1503
|
+
if (fdMap) {
|
|
1504
|
+
for (const storedLock of fdMap.values()) {
|
|
1505
|
+
try {
|
|
1506
|
+
this.lockWholeFile(storedLock.path, {
|
|
1507
|
+
...storedLock,
|
|
1508
|
+
type: "unlock"
|
|
1509
|
+
});
|
|
1510
|
+
} catch (e) {
|
|
1511
|
+
import_logger3.logger.error(
|
|
1512
|
+
`releaseLocksForProcess: failed to unlock whole-file lock for pid=${targetPid} fd=${storedLock.fd}`,
|
|
1513
|
+
e
|
|
1514
|
+
);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
this.wholeFileLockMap.delete(targetPid);
|
|
1518
|
+
}
|
|
1519
|
+
for (const [path2, lockedRangeTree] of this.rangeLockedFds.entries()) {
|
|
1520
|
+
const rangesLockedByTargetPid = lockedRangeTree.findLocksForProcess(targetPid);
|
|
1521
|
+
for (const op of rangesLockedByTargetPid) {
|
|
1522
|
+
try {
|
|
1523
|
+
this.lockFileByteRange(
|
|
1524
|
+
path2,
|
|
1525
|
+
{ ...op, type: "unlocked" },
|
|
1526
|
+
false
|
|
1527
|
+
);
|
|
1528
|
+
} catch (e) {
|
|
1529
|
+
import_logger3.logger.error(
|
|
1530
|
+
`releaseLocksForProcess: failed to unlock byte range for pid=${targetPid} fd=${op.fd} path=${path2}`,
|
|
1531
|
+
e
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1534
|
+
lockedRangeTree.remove(op);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
releaseLocksOnFdClose(targetPid, targetFd, targetPath) {
|
|
1539
|
+
const storedLock = this.wholeFileLockMap.get(targetPid)?.get(targetFd);
|
|
1540
|
+
if (storedLock) {
|
|
1541
|
+
this.lockWholeFile(storedLock.path, {
|
|
1542
|
+
...storedLock,
|
|
1543
|
+
type: "unlock"
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
this.wholeFileLockMap.get(targetPid)?.delete(targetFd);
|
|
1547
|
+
const lockedRangeTree = this.rangeLockedFds.get(targetPath);
|
|
1548
|
+
for (const op of lockedRangeTree?.findLocksForProcess(targetPid) ?? []) {
|
|
1549
|
+
this.lockFileByteRange(
|
|
1550
|
+
targetPath,
|
|
1551
|
+
{
|
|
1552
|
+
...op,
|
|
1553
|
+
type: "unlocked",
|
|
1554
|
+
// Use a dummy FD because we're releasing locks for
|
|
1555
|
+
// all FDs on this file (POSIX semantics), not just
|
|
1556
|
+
// the one being closed.
|
|
1557
|
+
fd: -1
|
|
1558
|
+
},
|
|
1559
|
+
false
|
|
1560
|
+
);
|
|
1561
|
+
lockedRangeTree.remove(op);
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
};
|
|
1565
|
+
|
|
429
1566
|
// packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
|
|
430
|
-
var
|
|
1567
|
+
var import_universal5 = require("@php-wasm/universal");
|
|
431
1568
|
var import_fs = __toESM(require("fs"), 1);
|
|
432
1569
|
|
|
433
1570
|
// packages/php-wasm/node/src/lib/extensions/xdebug/get-xdebug-extension-module.ts
|
|
434
|
-
var
|
|
435
|
-
async function getXdebugExtensionModule(version =
|
|
1571
|
+
var import_universal4 = require("@php-wasm/universal");
|
|
1572
|
+
async function getXdebugExtensionModule(version = import_universal4.LatestSupportedPHPVersion) {
|
|
436
1573
|
switch (version) {
|
|
437
1574
|
case "8.5":
|
|
438
1575
|
return (await import("@php-wasm/node-8-5")).getXdebugExtensionPath();
|
|
@@ -453,7 +1590,7 @@ async function getXdebugExtensionModule(version = import_universal2.LatestSuppor
|
|
|
453
1590
|
}
|
|
454
1591
|
|
|
455
1592
|
// packages/php-wasm/node/src/lib/extensions/xdebug/with-xdebug.ts
|
|
456
|
-
async function withXdebug(version =
|
|
1593
|
+
async function withXdebug(version = import_universal5.LatestSupportedPHPVersion, options, xdebugOptions) {
|
|
457
1594
|
const fileName = "xdebug.so";
|
|
458
1595
|
const filePath = await getXdebugExtensionModule(version);
|
|
459
1596
|
const extension = import_fs.default.readFileSync(filePath);
|
|
@@ -467,13 +1604,13 @@ async function withXdebug(version = import_universal3.LatestSupportedPHPVersion,
|
|
|
467
1604
|
if (options.onRuntimeInitialized) {
|
|
468
1605
|
options.onRuntimeInitialized(phpRuntime);
|
|
469
1606
|
}
|
|
470
|
-
if (!
|
|
1607
|
+
if (!import_universal5.FSHelpers.fileExists(
|
|
471
1608
|
phpRuntime.FS,
|
|
472
1609
|
"/internal/shared/extensions"
|
|
473
1610
|
)) {
|
|
474
1611
|
phpRuntime.FS.mkdirTree("/internal/shared/extensions");
|
|
475
1612
|
}
|
|
476
|
-
if (!
|
|
1613
|
+
if (!import_universal5.FSHelpers.fileExists(
|
|
477
1614
|
phpRuntime.FS,
|
|
478
1615
|
`/internal/shared/extensions/${fileName}`
|
|
479
1616
|
)) {
|
|
@@ -482,7 +1619,7 @@ async function withXdebug(version = import_universal3.LatestSupportedPHPVersion,
|
|
|
482
1619
|
new Uint8Array(extension)
|
|
483
1620
|
);
|
|
484
1621
|
}
|
|
485
|
-
if (!
|
|
1622
|
+
if (!import_universal5.FSHelpers.fileExists(
|
|
486
1623
|
phpRuntime.FS,
|
|
487
1624
|
"/internal/shared/extensions/xdebug.ini"
|
|
488
1625
|
)) {
|
|
@@ -512,13 +1649,13 @@ async function withXdebug(version = import_universal3.LatestSupportedPHPVersion,
|
|
|
512
1649
|
}
|
|
513
1650
|
|
|
514
1651
|
// packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
|
|
515
|
-
var
|
|
1652
|
+
var import_universal7 = require("@php-wasm/universal");
|
|
516
1653
|
var import_fs2 = __toESM(require("fs"), 1);
|
|
517
1654
|
var import_path = __toESM(require("path"), 1);
|
|
518
1655
|
|
|
519
1656
|
// packages/php-wasm/node/src/lib/extensions/intl/get-intl-extension-module.ts
|
|
520
|
-
var
|
|
521
|
-
async function getIntlExtensionModule(version =
|
|
1657
|
+
var import_universal6 = require("@php-wasm/universal");
|
|
1658
|
+
async function getIntlExtensionModule(version = import_universal6.LatestSupportedPHPVersion) {
|
|
522
1659
|
switch (version) {
|
|
523
1660
|
case "8.5":
|
|
524
1661
|
return (await import("@php-wasm/node-8-5")).getIntlExtensionPath();
|
|
@@ -540,7 +1677,7 @@ async function getIntlExtensionModule(version = import_universal4.LatestSupporte
|
|
|
540
1677
|
|
|
541
1678
|
// packages/php-wasm/node/src/lib/extensions/intl/with-intl.ts
|
|
542
1679
|
var import_meta = {};
|
|
543
|
-
async function withIntl(version =
|
|
1680
|
+
async function withIntl(version = import_universal7.LatestSupportedPHPVersion, options) {
|
|
544
1681
|
const extensionName = "intl.so";
|
|
545
1682
|
const extensionPath = await getIntlExtensionModule(version);
|
|
546
1683
|
const extension = import_fs2.default.readFileSync(extensionPath);
|
|
@@ -559,13 +1696,13 @@ async function withIntl(version = import_universal5.LatestSupportedPHPVersion, o
|
|
|
559
1696
|
if (options.onRuntimeInitialized) {
|
|
560
1697
|
options.onRuntimeInitialized(phpRuntime);
|
|
561
1698
|
}
|
|
562
|
-
if (!
|
|
1699
|
+
if (!import_universal7.FSHelpers.fileExists(
|
|
563
1700
|
phpRuntime.FS,
|
|
564
1701
|
"/internal/shared/extensions"
|
|
565
1702
|
)) {
|
|
566
1703
|
phpRuntime.FS.mkdirTree("/internal/shared/extensions");
|
|
567
1704
|
}
|
|
568
|
-
if (!
|
|
1705
|
+
if (!import_universal7.FSHelpers.fileExists(
|
|
569
1706
|
phpRuntime.FS,
|
|
570
1707
|
`/internal/shared/extensions/${extensionName}`
|
|
571
1708
|
)) {
|
|
@@ -574,7 +1711,7 @@ async function withIntl(version = import_universal5.LatestSupportedPHPVersion, o
|
|
|
574
1711
|
new Uint8Array(extension)
|
|
575
1712
|
);
|
|
576
1713
|
}
|
|
577
|
-
if (!
|
|
1714
|
+
if (!import_universal7.FSHelpers.fileExists(
|
|
578
1715
|
phpRuntime.FS,
|
|
579
1716
|
"/internal/shared/extensions/intl.ini"
|
|
580
1717
|
)) {
|
|
@@ -585,7 +1722,7 @@ async function withIntl(version = import_universal5.LatestSupportedPHPVersion, o
|
|
|
585
1722
|
].join("\n")
|
|
586
1723
|
);
|
|
587
1724
|
}
|
|
588
|
-
if (!
|
|
1725
|
+
if (!import_universal7.FSHelpers.fileExists(
|
|
589
1726
|
phpRuntime.FS,
|
|
590
1727
|
`${phpRuntime.ENV.ICU_DATA}/${dataName}`
|
|
591
1728
|
)) {
|
|
@@ -600,12 +1737,12 @@ async function withIntl(version = import_universal5.LatestSupportedPHPVersion, o
|
|
|
600
1737
|
}
|
|
601
1738
|
|
|
602
1739
|
// packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
|
|
603
|
-
var
|
|
1740
|
+
var import_universal9 = require("@php-wasm/universal");
|
|
604
1741
|
var import_fs3 = __toESM(require("fs"), 1);
|
|
605
1742
|
|
|
606
1743
|
// packages/php-wasm/node/src/lib/extensions/redis/get-redis-extension-module.ts
|
|
607
|
-
var
|
|
608
|
-
async function getRedisExtensionModule(version =
|
|
1744
|
+
var import_universal8 = require("@php-wasm/universal");
|
|
1745
|
+
async function getRedisExtensionModule(version = import_universal8.LatestSupportedPHPVersion) {
|
|
609
1746
|
switch (version) {
|
|
610
1747
|
case "8.5":
|
|
611
1748
|
return (await import("@php-wasm/node-8-5")).getRedisExtensionPath();
|
|
@@ -626,7 +1763,7 @@ async function getRedisExtensionModule(version = import_universal6.LatestSupport
|
|
|
626
1763
|
}
|
|
627
1764
|
|
|
628
1765
|
// packages/php-wasm/node/src/lib/extensions/redis/with-redis.ts
|
|
629
|
-
async function withRedis(version =
|
|
1766
|
+
async function withRedis(version = import_universal9.LatestSupportedPHPVersion, options) {
|
|
630
1767
|
const extensionName = "redis.so";
|
|
631
1768
|
const extensionPath = await getRedisExtensionModule(version);
|
|
632
1769
|
const extension = import_fs3.default.readFileSync(extensionPath);
|
|
@@ -640,13 +1777,13 @@ async function withRedis(version = import_universal7.LatestSupportedPHPVersion,
|
|
|
640
1777
|
if (options.onRuntimeInitialized) {
|
|
641
1778
|
options.onRuntimeInitialized(phpRuntime);
|
|
642
1779
|
}
|
|
643
|
-
if (!
|
|
1780
|
+
if (!import_universal9.FSHelpers.fileExists(
|
|
644
1781
|
phpRuntime.FS,
|
|
645
1782
|
"/internal/shared/extensions"
|
|
646
1783
|
)) {
|
|
647
1784
|
phpRuntime.FS.mkdirTree("/internal/shared/extensions");
|
|
648
1785
|
}
|
|
649
|
-
if (!
|
|
1786
|
+
if (!import_universal9.FSHelpers.fileExists(
|
|
650
1787
|
phpRuntime.FS,
|
|
651
1788
|
`/internal/shared/extensions/${extensionName}`
|
|
652
1789
|
)) {
|
|
@@ -655,7 +1792,7 @@ async function withRedis(version = import_universal7.LatestSupportedPHPVersion,
|
|
|
655
1792
|
new Uint8Array(extension)
|
|
656
1793
|
);
|
|
657
1794
|
}
|
|
658
|
-
if (!
|
|
1795
|
+
if (!import_universal9.FSHelpers.fileExists(
|
|
659
1796
|
phpRuntime.FS,
|
|
660
1797
|
"/internal/shared/extensions/redis.ini"
|
|
661
1798
|
)) {
|
|
@@ -671,12 +1808,12 @@ async function withRedis(version = import_universal7.LatestSupportedPHPVersion,
|
|
|
671
1808
|
}
|
|
672
1809
|
|
|
673
1810
|
// packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
|
|
674
|
-
var
|
|
1811
|
+
var import_universal11 = require("@php-wasm/universal");
|
|
675
1812
|
var import_fs4 = __toESM(require("fs"), 1);
|
|
676
1813
|
|
|
677
1814
|
// packages/php-wasm/node/src/lib/extensions/memcached/get-memcached-extension-module.ts
|
|
678
|
-
var
|
|
679
|
-
async function getMemcachedExtensionModule(version =
|
|
1815
|
+
var import_universal10 = require("@php-wasm/universal");
|
|
1816
|
+
async function getMemcachedExtensionModule(version = import_universal10.LatestSupportedPHPVersion) {
|
|
680
1817
|
switch (version) {
|
|
681
1818
|
case "8.5":
|
|
682
1819
|
return (await import("@php-wasm/node-8-5")).getMemcachedExtensionPath();
|
|
@@ -697,7 +1834,7 @@ async function getMemcachedExtensionModule(version = import_universal8.LatestSup
|
|
|
697
1834
|
}
|
|
698
1835
|
|
|
699
1836
|
// packages/php-wasm/node/src/lib/extensions/memcached/with-memcached.ts
|
|
700
|
-
async function withMemcached(version =
|
|
1837
|
+
async function withMemcached(version = import_universal11.LatestSupportedPHPVersion, options) {
|
|
701
1838
|
const extensionName = "memcached.so";
|
|
702
1839
|
const extensionPath = await getMemcachedExtensionModule(version);
|
|
703
1840
|
const extension = import_fs4.default.readFileSync(extensionPath);
|
|
@@ -711,13 +1848,13 @@ async function withMemcached(version = import_universal9.LatestSupportedPHPVersi
|
|
|
711
1848
|
if (options.onRuntimeInitialized) {
|
|
712
1849
|
options.onRuntimeInitialized(phpRuntime);
|
|
713
1850
|
}
|
|
714
|
-
if (!
|
|
1851
|
+
if (!import_universal11.FSHelpers.fileExists(
|
|
715
1852
|
phpRuntime.FS,
|
|
716
1853
|
"/internal/shared/extensions"
|
|
717
1854
|
)) {
|
|
718
1855
|
phpRuntime.FS.mkdirTree("/internal/shared/extensions");
|
|
719
1856
|
}
|
|
720
|
-
if (!
|
|
1857
|
+
if (!import_universal11.FSHelpers.fileExists(
|
|
721
1858
|
phpRuntime.FS,
|
|
722
1859
|
`/internal/shared/extensions/${extensionName}`
|
|
723
1860
|
)) {
|
|
@@ -726,7 +1863,7 @@ async function withMemcached(version = import_universal9.LatestSupportedPHPVersi
|
|
|
726
1863
|
new Uint8Array(extension)
|
|
727
1864
|
);
|
|
728
1865
|
}
|
|
729
|
-
if (!
|
|
1866
|
+
if (!import_universal11.FSHelpers.fileExists(
|
|
730
1867
|
phpRuntime.FS,
|
|
731
1868
|
"/internal/shared/extensions/memcached.ini"
|
|
732
1869
|
)) {
|
|
@@ -743,6 +1880,7 @@ async function withMemcached(version = import_universal9.LatestSupportedPHPVersi
|
|
|
743
1880
|
|
|
744
1881
|
// packages/php-wasm/node/src/lib/load-runtime.ts
|
|
745
1882
|
var import_util = require("@php-wasm/util");
|
|
1883
|
+
var import_os = require("os");
|
|
746
1884
|
async function loadNodeRuntime(phpVersion, options = {}) {
|
|
747
1885
|
let emscriptenOptions = {
|
|
748
1886
|
/**
|
|
@@ -753,6 +1891,14 @@ async function loadNodeRuntime(phpVersion, options = {}) {
|
|
|
753
1891
|
quit: function(code, error) {
|
|
754
1892
|
throw error;
|
|
755
1893
|
},
|
|
1894
|
+
bindUserSpace: (userSpaceContext) => {
|
|
1895
|
+
const nativeFileLockManager = (0, import_os.platform)() === "win32" ? new FileLockManagerForWindows() : new FileLockManagerForPosix();
|
|
1896
|
+
const fileLockManager = options.fileLockManager ? new import_universal12.FileLockManagerComposite({
|
|
1897
|
+
nativeLockManager: nativeFileLockManager,
|
|
1898
|
+
wasmLockManager: options.fileLockManager
|
|
1899
|
+
}) : nativeFileLockManager;
|
|
1900
|
+
return bindUserSpace({ fileLockManager }, userSpaceContext);
|
|
1901
|
+
},
|
|
756
1902
|
...options.emscriptenOptions || {},
|
|
757
1903
|
onRuntimeInitialized: (phpRuntime) => {
|
|
758
1904
|
if (options?.followSymlinks === true) {
|
|
@@ -768,7 +1914,7 @@ async function loadNodeRuntime(phpVersion, options = {}) {
|
|
|
768
1914
|
normalizedPath
|
|
769
1915
|
);
|
|
770
1916
|
if (import_fs5.default.existsSync(absoluteSourcePath)) {
|
|
771
|
-
if (!
|
|
1917
|
+
if (!import_universal12.FSHelpers.fileExists(
|
|
772
1918
|
phpRuntime.FS,
|
|
773
1919
|
symlinkMountPath
|
|
774
1920
|
)) {
|
|
@@ -824,7 +1970,7 @@ async function loadNodeRuntime(phpVersion, options = {}) {
|
|
|
824
1970
|
}
|
|
825
1971
|
emscriptenOptions = await withNetworking(emscriptenOptions);
|
|
826
1972
|
const phpLoaderModule = await getPHPLoaderModule(phpVersion);
|
|
827
|
-
const runtimeId = await (0,
|
|
1973
|
+
const runtimeId = await (0, import_universal12.loadPHPRuntime)(phpLoaderModule, emscriptenOptions);
|
|
828
1974
|
return runtimeId;
|
|
829
1975
|
}
|
|
830
1976
|
|
|
@@ -832,14 +1978,14 @@ async function loadNodeRuntime(phpVersion, options = {}) {
|
|
|
832
1978
|
var import_node_fs = require("node:fs");
|
|
833
1979
|
|
|
834
1980
|
// packages/php-wasm/node/src/lib/node-fs-mount.ts
|
|
835
|
-
var
|
|
1981
|
+
var import_universal13 = require("@php-wasm/universal");
|
|
836
1982
|
var import_util2 = require("@php-wasm/util");
|
|
837
1983
|
var import_fs6 = require("fs");
|
|
838
1984
|
var import_path2 = require("path");
|
|
839
1985
|
function createNodeFsMountHandler(localPath) {
|
|
840
1986
|
return function(php, FS, vfsMountPoint) {
|
|
841
1987
|
let removeVfsNode = false;
|
|
842
|
-
if (!
|
|
1988
|
+
if (!import_universal13.FSHelpers.fileExists(FS, vfsMountPoint)) {
|
|
843
1989
|
const lstat = (0, import_fs6.lstatSync)(localPath);
|
|
844
1990
|
if (lstat.isFile() || lstat.isSymbolicLink()) {
|
|
845
1991
|
FS.mkdirTree((0, import_path2.dirname)(vfsMountPoint));
|
|
@@ -853,9 +1999,9 @@ function createNodeFsMountHandler(localPath) {
|
|
|
853
1999
|
}
|
|
854
2000
|
removeVfsNode = true;
|
|
855
2001
|
}
|
|
856
|
-
let
|
|
2002
|
+
let lookup3;
|
|
857
2003
|
try {
|
|
858
|
-
|
|
2004
|
+
lookup3 = FS.lookupPath(vfsMountPoint);
|
|
859
2005
|
} catch (e) {
|
|
860
2006
|
const error = e;
|
|
861
2007
|
if (error.errno === 44) {
|
|
@@ -869,7 +2015,7 @@ function createNodeFsMountHandler(localPath) {
|
|
|
869
2015
|
return () => {
|
|
870
2016
|
FS.unmount(vfsMountPoint);
|
|
871
2017
|
if (removeVfsNode) {
|
|
872
|
-
if (FS.isDir(
|
|
2018
|
+
if (FS.isDir(lookup3.node.mode)) {
|
|
873
2019
|
if ((0, import_util2.isParentOf)(vfsMountPoint, FS.cwd())) {
|
|
874
2020
|
throw new Error(
|
|
875
2021
|
`Cannot remove the VFS directory "${vfsMountPoint}" on umount cleanup \u2013 it is a parent of the CWD "${FS.cwd()}". Change CWD before unmounting or explicitly disable post-unmount node cleanup with createNodeFsMountHandler(path, {cleanupNodesOnUnmount: false}).`
|
|
@@ -923,642 +2069,11 @@ function statPathFollowSymlinks(path2) {
|
|
|
923
2069
|
}
|
|
924
2070
|
return stat;
|
|
925
2071
|
}
|
|
926
|
-
|
|
927
|
-
// packages/php-wasm/node/src/lib/file-lock-manager-for-node.ts
|
|
928
|
-
var import_logger2 = require("@php-wasm/logger");
|
|
929
|
-
var import_fs7 = require("fs");
|
|
930
|
-
var MAX_64BIT_OFFSET = BigInt(2n ** 64n - 1n);
|
|
931
|
-
var FileLockManagerForNode = class {
|
|
932
|
-
/**
|
|
933
|
-
* Create a new FileLockManagerForNode instance.
|
|
934
|
-
*
|
|
935
|
-
* @param nativeFlockSync A synchronous flock() function to lock files via the host OS.
|
|
936
|
-
*/
|
|
937
|
-
constructor(nativeFlockSync = function flockSyncNoOp() {
|
|
938
|
-
}) {
|
|
939
|
-
this.nativeFlockSync = nativeFlockSync;
|
|
940
|
-
this.locks = /* @__PURE__ */ new Map();
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Lock the whole file.
|
|
944
|
-
*
|
|
945
|
-
* @param path The path to the file to lock. This should be the path
|
|
946
|
-
* of the file in the native filesystem.
|
|
947
|
-
* @param op The whole file lock operation to perform.
|
|
948
|
-
* @returns True if the lock was granted, false otherwise.
|
|
949
|
-
*/
|
|
950
|
-
lockWholeFile(path2, op) {
|
|
951
|
-
if (this.locks.get(path2) === void 0) {
|
|
952
|
-
if (op.type === "unlock") {
|
|
953
|
-
return true;
|
|
954
|
-
}
|
|
955
|
-
const maybeLock = FileLock.maybeCreate(
|
|
956
|
-
path2,
|
|
957
|
-
op.type,
|
|
958
|
-
this.nativeFlockSync
|
|
959
|
-
);
|
|
960
|
-
if (maybeLock === void 0) {
|
|
961
|
-
return false;
|
|
962
|
-
}
|
|
963
|
-
this.locks.set(path2, maybeLock);
|
|
964
|
-
}
|
|
965
|
-
const lock = this.locks.get(path2);
|
|
966
|
-
const result = lock.lockWholeFile(op);
|
|
967
|
-
this.forgetPathIfUnlocked(path2);
|
|
968
|
-
return result;
|
|
969
|
-
}
|
|
970
|
-
/**
|
|
971
|
-
* Lock a byte range.
|
|
972
|
-
*
|
|
973
|
-
* @param path The path to the file to lock. This should be the path
|
|
974
|
-
* of the file in the native filesystem.
|
|
975
|
-
* @param requestedLock The byte range lock to perform.
|
|
976
|
-
* @returns True if the lock was granted, false otherwise.
|
|
977
|
-
*/
|
|
978
|
-
lockFileByteRange(path2, requestedLock) {
|
|
979
|
-
if (!this.locks.has(path2)) {
|
|
980
|
-
if (requestedLock.type === "unlocked") {
|
|
981
|
-
return true;
|
|
982
|
-
}
|
|
983
|
-
const maybeLock = FileLock.maybeCreate(
|
|
984
|
-
path2,
|
|
985
|
-
requestedLock.type,
|
|
986
|
-
this.nativeFlockSync
|
|
987
|
-
);
|
|
988
|
-
if (maybeLock === void 0) {
|
|
989
|
-
return false;
|
|
990
|
-
}
|
|
991
|
-
this.locks.set(path2, maybeLock);
|
|
992
|
-
}
|
|
993
|
-
const lock = this.locks.get(path2);
|
|
994
|
-
return lock.lockFileByteRange(requestedLock);
|
|
995
|
-
}
|
|
996
|
-
/**
|
|
997
|
-
* Find the first conflicting byte range lock.
|
|
998
|
-
*
|
|
999
|
-
* @param path The path to the file to find the conflicting lock for.
|
|
1000
|
-
* @param desiredLock The desired byte range lock.
|
|
1001
|
-
* @returns The first conflicting byte range lock, or undefined if no conflicting lock exists.
|
|
1002
|
-
*/
|
|
1003
|
-
findFirstConflictingByteRangeLock(path2, desiredLock) {
|
|
1004
|
-
const lock = this.locks.get(path2);
|
|
1005
|
-
if (lock === void 0) {
|
|
1006
|
-
return void 0;
|
|
1007
|
-
}
|
|
1008
|
-
return lock.findFirstConflictingByteRangeLock(desiredLock);
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Release all locks for the given process.
|
|
1012
|
-
*
|
|
1013
|
-
* @param pid The process ID to release locks for.
|
|
1014
|
-
*/
|
|
1015
|
-
releaseLocksForProcess(pid) {
|
|
1016
|
-
for (const [path2, lock] of this.locks.entries()) {
|
|
1017
|
-
lock.releaseLocksForProcess(pid);
|
|
1018
|
-
this.forgetPathIfUnlocked(path2);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
/**
|
|
1022
|
-
* Release all locks for the given process and file descriptor.
|
|
1023
|
-
*
|
|
1024
|
-
* @param pid The process ID to release locks for.
|
|
1025
|
-
* @param fd The file descriptor to release locks for.
|
|
1026
|
-
* @param path The path to the file to release locks for.
|
|
1027
|
-
*/
|
|
1028
|
-
releaseLocksForProcessFd(pid, fd, nativePath) {
|
|
1029
|
-
const lock = this.locks.get(nativePath);
|
|
1030
|
-
if (!lock) {
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
lock.releaseLocksForProcessFd(pid, fd);
|
|
1034
|
-
this.forgetPathIfUnlocked(nativePath);
|
|
1035
|
-
}
|
|
1036
|
-
/**
|
|
1037
|
-
* Forget the path if it is unlocked.
|
|
1038
|
-
*
|
|
1039
|
-
* @param path The path to the file to forget.
|
|
1040
|
-
*/
|
|
1041
|
-
forgetPathIfUnlocked(path2) {
|
|
1042
|
-
const lock = this.locks.get(path2);
|
|
1043
|
-
if (!lock) {
|
|
1044
|
-
return;
|
|
1045
|
-
}
|
|
1046
|
-
if (lock.isUnlocked()) {
|
|
1047
|
-
lock.dispose();
|
|
1048
|
-
this.locks.delete(path2);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
};
|
|
1052
|
-
var FileLock = class _FileLock {
|
|
1053
|
-
/**
|
|
1054
|
-
* Create a new FileLock instance for the given file and mode.
|
|
1055
|
-
* Fail if the underlying native file lock cannot be acquired.
|
|
1056
|
-
*
|
|
1057
|
-
* @param path The path to the file to lock
|
|
1058
|
-
* @param mode The type of lock to acquire
|
|
1059
|
-
* @returns A FileLock instance if the lock was acquired, undefined otherwise
|
|
1060
|
-
*/
|
|
1061
|
-
static maybeCreate(path2, mode, nativeFlockSync) {
|
|
1062
|
-
let fd;
|
|
1063
|
-
try {
|
|
1064
|
-
fd = (0, import_fs7.openSync)(path2, "a+");
|
|
1065
|
-
const flockFlags = mode === "exclusive" ? "exnb" : "shnb";
|
|
1066
|
-
nativeFlockSync(fd, flockFlags);
|
|
1067
|
-
const nativeLock = { fd, mode, nativeFlockSync };
|
|
1068
|
-
return new _FileLock(nativeLock);
|
|
1069
|
-
} catch {
|
|
1070
|
-
if (fd !== void 0) {
|
|
1071
|
-
try {
|
|
1072
|
-
(0, import_fs7.closeSync)(fd);
|
|
1073
|
-
} catch (error) {
|
|
1074
|
-
import_logger2.logger.error(
|
|
1075
|
-
"Error closing locking file descriptor",
|
|
1076
|
-
error
|
|
1077
|
-
);
|
|
1078
|
-
}
|
|
1079
|
-
}
|
|
1080
|
-
return void 0;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
constructor(nativeLock) {
|
|
1084
|
-
this.nativeLock = nativeLock;
|
|
1085
|
-
this.rangeLocks = new FileLockIntervalTree();
|
|
1086
|
-
this.wholeFileLock = { type: "unlocked" };
|
|
1087
|
-
}
|
|
1088
|
-
/**
|
|
1089
|
-
* Close the file descriptor and release the native lock.
|
|
1090
|
-
*
|
|
1091
|
-
* @TODO Replace this with a Symbol.dispose property once supported by all JS runtimes.
|
|
1092
|
-
*/
|
|
1093
|
-
dispose() {
|
|
1094
|
-
try {
|
|
1095
|
-
(0, import_fs7.closeSync)(this.nativeLock.fd);
|
|
1096
|
-
} catch (error) {
|
|
1097
|
-
import_logger2.logger.error("Error closing locking file descriptor", error);
|
|
1098
|
-
}
|
|
1099
|
-
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Lock the whole file.
|
|
1102
|
-
*
|
|
1103
|
-
* This method corresponds to the flock() function.
|
|
1104
|
-
*
|
|
1105
|
-
* @param op The whole file lock operation to perform.
|
|
1106
|
-
* @returns True if the lock was granted, false otherwise.
|
|
1107
|
-
*/
|
|
1108
|
-
lockWholeFile(op) {
|
|
1109
|
-
if (op.type === "unlock") {
|
|
1110
|
-
const originalType = this.wholeFileLock.type;
|
|
1111
|
-
if (originalType === "unlocked") {
|
|
1112
|
-
} else if (this.wholeFileLock.type === "exclusive" && this.wholeFileLock.pid === op.pid && this.wholeFileLock.fd === op.fd) {
|
|
1113
|
-
this.wholeFileLock = { type: "unlocked" };
|
|
1114
|
-
} else if (this.wholeFileLock.type === "shared" && this.wholeFileLock.pidFds.has(op.pid) && this.wholeFileLock.pidFds.get(op.pid).has(op.fd)) {
|
|
1115
|
-
this.wholeFileLock.pidFds.get(op.pid).delete(op.fd);
|
|
1116
|
-
if (this.wholeFileLock.pidFds.get(op.pid).size === 0) {
|
|
1117
|
-
this.wholeFileLock.pidFds.delete(op.pid);
|
|
1118
|
-
}
|
|
1119
|
-
if (this.wholeFileLock.pidFds.size === 0) {
|
|
1120
|
-
this.wholeFileLock = { type: "unlocked" };
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
if (!this.ensureCompatibleNativeLock()) {
|
|
1124
|
-
import_logger2.logger.error(
|
|
1125
|
-
"Unable to update native lock after removing a whole file lock."
|
|
1126
|
-
);
|
|
1127
|
-
}
|
|
1128
|
-
return true;
|
|
1129
|
-
}
|
|
1130
|
-
if (this.isThereAConflictWithRequestedWholeFileLock(op)) {
|
|
1131
|
-
return false;
|
|
1132
|
-
}
|
|
1133
|
-
if (!this.ensureCompatibleNativeLock({
|
|
1134
|
-
overrideWholeFileLockType: op.type
|
|
1135
|
-
})) {
|
|
1136
|
-
return false;
|
|
1137
|
-
}
|
|
1138
|
-
if (op.type === "exclusive") {
|
|
1139
|
-
this.wholeFileLock = {
|
|
1140
|
-
type: "exclusive",
|
|
1141
|
-
pid: op.pid,
|
|
1142
|
-
fd: op.fd
|
|
1143
|
-
};
|
|
1144
|
-
return true;
|
|
1145
|
-
}
|
|
1146
|
-
if (op.type === "shared") {
|
|
1147
|
-
if (this.wholeFileLock.type !== "shared") {
|
|
1148
|
-
this.wholeFileLock = {
|
|
1149
|
-
type: "shared",
|
|
1150
|
-
pidFds: /* @__PURE__ */ new Map()
|
|
1151
|
-
};
|
|
1152
|
-
}
|
|
1153
|
-
const sharedLock = this.wholeFileLock;
|
|
1154
|
-
if (!sharedLock.pidFds.has(op.pid)) {
|
|
1155
|
-
sharedLock.pidFds.set(op.pid, /* @__PURE__ */ new Set());
|
|
1156
|
-
}
|
|
1157
|
-
sharedLock.pidFds.get(op.pid).add(op.fd);
|
|
1158
|
-
return true;
|
|
1159
|
-
}
|
|
1160
|
-
throw new Error(`Unexpected wholeFileLock() op: '${op.type}'`);
|
|
1161
|
-
}
|
|
1162
|
-
/**
|
|
1163
|
-
* Lock a byte range.
|
|
1164
|
-
*
|
|
1165
|
-
* This method corresponds to the fcntl() F_SETLK command.
|
|
1166
|
-
*
|
|
1167
|
-
* @param requestedLock The byte range lock to perform.
|
|
1168
|
-
* @returns True if the lock was granted, false otherwise.
|
|
1169
|
-
*/
|
|
1170
|
-
lockFileByteRange(requestedLock) {
|
|
1171
|
-
if (requestedLock.start === requestedLock.end) {
|
|
1172
|
-
requestedLock = {
|
|
1173
|
-
...requestedLock,
|
|
1174
|
-
end: MAX_64BIT_OFFSET
|
|
1175
|
-
};
|
|
1176
|
-
}
|
|
1177
|
-
if (requestedLock.type === "unlocked") {
|
|
1178
|
-
const overlappingLocksBySameProcess = this.rangeLocks.findOverlapping(requestedLock).filter((lock) => lock.pid === requestedLock.pid);
|
|
1179
|
-
for (const overlappingLock of overlappingLocksBySameProcess) {
|
|
1180
|
-
this.rangeLocks.remove(overlappingLock);
|
|
1181
|
-
if (overlappingLock.start < requestedLock.start) {
|
|
1182
|
-
this.rangeLocks.insert({
|
|
1183
|
-
...overlappingLock,
|
|
1184
|
-
end: requestedLock.start
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
if (overlappingLock.end > requestedLock.end) {
|
|
1188
|
-
this.rangeLocks.insert({
|
|
1189
|
-
...overlappingLock,
|
|
1190
|
-
start: requestedLock.end
|
|
1191
|
-
});
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
if (!this.ensureCompatibleNativeLock()) {
|
|
1195
|
-
import_logger2.logger.error(
|
|
1196
|
-
"Unable to update native lock after removing a byte range lock."
|
|
1197
|
-
);
|
|
1198
|
-
}
|
|
1199
|
-
return true;
|
|
1200
|
-
}
|
|
1201
|
-
if (this.isThereAConflictWithRequestedRangeLock(requestedLock)) {
|
|
1202
|
-
return false;
|
|
1203
|
-
}
|
|
1204
|
-
if (!this.ensureCompatibleNativeLock({
|
|
1205
|
-
overrideRangeLockType: requestedLock.type
|
|
1206
|
-
})) {
|
|
1207
|
-
return false;
|
|
1208
|
-
}
|
|
1209
|
-
const overlappingLocksFromSameProcess = this.rangeLocks.findOverlapping(requestedLock).filter((lock) => lock.pid === requestedLock.pid);
|
|
1210
|
-
let minStart = requestedLock.start;
|
|
1211
|
-
let maxEnd = requestedLock.end;
|
|
1212
|
-
for (const overlappingLock of overlappingLocksFromSameProcess) {
|
|
1213
|
-
this.rangeLocks.remove(overlappingLock);
|
|
1214
|
-
if (overlappingLock.start < minStart) {
|
|
1215
|
-
minStart = overlappingLock.start;
|
|
1216
|
-
}
|
|
1217
|
-
if (overlappingLock.end > maxEnd) {
|
|
1218
|
-
maxEnd = overlappingLock.end;
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
const mergedLock = {
|
|
1222
|
-
...requestedLock,
|
|
1223
|
-
start: minStart,
|
|
1224
|
-
end: maxEnd
|
|
1225
|
-
};
|
|
1226
|
-
this.rangeLocks.insert(mergedLock);
|
|
1227
|
-
return true;
|
|
1228
|
-
}
|
|
1229
|
-
/**
|
|
1230
|
-
* Find the first conflicting byte range lock.
|
|
1231
|
-
*
|
|
1232
|
-
* This method corresponds to the fcntl() F_GETLK command.
|
|
1233
|
-
*
|
|
1234
|
-
* @param desiredLock The desired byte range lock.
|
|
1235
|
-
* @returns The first conflicting byte range lock, or undefined if no conflicting lock exists.
|
|
1236
|
-
*/
|
|
1237
|
-
findFirstConflictingByteRangeLock(desiredLock) {
|
|
1238
|
-
const overlappingLocks = this.rangeLocks.findOverlapping(desiredLock);
|
|
1239
|
-
const firstConflictingRangeLock = overlappingLocks.find(
|
|
1240
|
-
(lock) => lock.pid !== desiredLock.pid && (desiredLock.type === "exclusive" || lock.type === "exclusive")
|
|
1241
|
-
);
|
|
1242
|
-
if (firstConflictingRangeLock) {
|
|
1243
|
-
return firstConflictingRangeLock;
|
|
1244
|
-
}
|
|
1245
|
-
if (this.wholeFileLock.type === "unlocked") {
|
|
1246
|
-
return void 0;
|
|
1247
|
-
}
|
|
1248
|
-
const wfl = this.wholeFileLock;
|
|
1249
|
-
if (wfl.type === "exclusive" || desiredLock.type === "exclusive") {
|
|
1250
|
-
return {
|
|
1251
|
-
type: this.wholeFileLock.type,
|
|
1252
|
-
start: 0n,
|
|
1253
|
-
end: 0n,
|
|
1254
|
-
pid: -1
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
return void 0;
|
|
1258
|
-
}
|
|
1259
|
-
/**
|
|
1260
|
-
* Release all locks for the given process.
|
|
1261
|
-
*
|
|
1262
|
-
* @param pid The process ID to release locks for.
|
|
1263
|
-
*/
|
|
1264
|
-
releaseLocksForProcess(pid) {
|
|
1265
|
-
for (const rangeLock of this.rangeLocks.findLocksForProcess(pid)) {
|
|
1266
|
-
this.lockFileByteRange({
|
|
1267
|
-
...rangeLock,
|
|
1268
|
-
type: "unlocked"
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
if (this.wholeFileLock.type === "exclusive" && this.wholeFileLock.pid === pid) {
|
|
1272
|
-
this.lockWholeFile({
|
|
1273
|
-
pid,
|
|
1274
|
-
fd: this.wholeFileLock.fd,
|
|
1275
|
-
type: "unlock"
|
|
1276
|
-
});
|
|
1277
|
-
} else if (this.wholeFileLock.type === "shared" && this.wholeFileLock.pidFds.has(pid)) {
|
|
1278
|
-
for (const fd of this.wholeFileLock.pidFds.get(pid)) {
|
|
1279
|
-
this.lockWholeFile({
|
|
1280
|
-
pid,
|
|
1281
|
-
fd,
|
|
1282
|
-
type: "unlock"
|
|
1283
|
-
});
|
|
1284
|
-
}
|
|
1285
|
-
}
|
|
1286
|
-
}
|
|
1287
|
-
/**
|
|
1288
|
-
* Release all locks for the given process and file descriptor.
|
|
1289
|
-
*
|
|
1290
|
-
* @param pid The process ID to release locks for.
|
|
1291
|
-
* @param fd The file descriptor to release locks for.
|
|
1292
|
-
*/
|
|
1293
|
-
releaseLocksForProcessFd(pid, fd) {
|
|
1294
|
-
for (const rangeLock of this.rangeLocks.findLocksForProcess(pid)) {
|
|
1295
|
-
this.lockFileByteRange({
|
|
1296
|
-
...rangeLock,
|
|
1297
|
-
type: "unlocked"
|
|
1298
|
-
});
|
|
1299
|
-
}
|
|
1300
|
-
this.lockWholeFile({
|
|
1301
|
-
pid,
|
|
1302
|
-
fd,
|
|
1303
|
-
type: "unlock"
|
|
1304
|
-
});
|
|
1305
|
-
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Check if the file lock is unlocked.
|
|
1308
|
-
*
|
|
1309
|
-
* @returns True if the file lock is unlocked, false otherwise.
|
|
1310
|
-
*/
|
|
1311
|
-
isUnlocked() {
|
|
1312
|
-
return this.wholeFileLock.type === "unlocked" && this.rangeLocks.isEmpty();
|
|
1313
|
-
}
|
|
1314
|
-
/**
|
|
1315
|
-
* Ensure that the native lock is compatible with the php-wasm lock,
|
|
1316
|
-
* upgrading or downgrading as needed.
|
|
1317
|
-
*
|
|
1318
|
-
* @param overrideWholeFileLockType If provided, use this type for the whole file lock.
|
|
1319
|
-
* @param overrideRangeLockType If provided, use this type for the range lock.
|
|
1320
|
-
* @returns True if the native lock was upgraded or downgraded, false otherwise.
|
|
1321
|
-
*/
|
|
1322
|
-
ensureCompatibleNativeLock({
|
|
1323
|
-
overrideWholeFileLockType,
|
|
1324
|
-
overrideRangeLockType
|
|
1325
|
-
} = {}) {
|
|
1326
|
-
const wholeFileLockType = overrideWholeFileLockType ?? this.wholeFileLock.type;
|
|
1327
|
-
const rangeLockType = overrideRangeLockType ?? this.rangeLocks.findStrictestExistingLockType();
|
|
1328
|
-
let requiredNativeLockType;
|
|
1329
|
-
if (wholeFileLockType === "exclusive" || rangeLockType === "exclusive") {
|
|
1330
|
-
requiredNativeLockType = "exclusive";
|
|
1331
|
-
} else if (wholeFileLockType === "shared" || rangeLockType === "shared") {
|
|
1332
|
-
requiredNativeLockType = "shared";
|
|
1333
|
-
} else {
|
|
1334
|
-
requiredNativeLockType = "unlock";
|
|
1335
|
-
}
|
|
1336
|
-
if (this.nativeLock.mode === requiredNativeLockType) {
|
|
1337
|
-
return true;
|
|
1338
|
-
}
|
|
1339
|
-
const flockFlags = requiredNativeLockType === "exclusive" && "exnb" || requiredNativeLockType === "shared" && "shnb" || "un";
|
|
1340
|
-
try {
|
|
1341
|
-
this.nativeLock.nativeFlockSync(this.nativeLock.fd, flockFlags);
|
|
1342
|
-
this.nativeLock.mode = requiredNativeLockType;
|
|
1343
|
-
return true;
|
|
1344
|
-
} catch {
|
|
1345
|
-
return false;
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
/**
|
|
1349
|
-
* Check if a lock exists that conflicts with the requested range lock.
|
|
1350
|
-
*
|
|
1351
|
-
* @param requestedLock The desired byte range lock.
|
|
1352
|
-
* @returns True if a conflicting lock exists, false otherwise.
|
|
1353
|
-
*/
|
|
1354
|
-
isThereAConflictWithRequestedRangeLock(requestedLock) {
|
|
1355
|
-
return this.findFirstConflictingByteRangeLock(requestedLock) !== void 0;
|
|
1356
|
-
}
|
|
1357
|
-
/**
|
|
1358
|
-
* Check if a lock exists that conflicts with the requested whole-file lock.
|
|
1359
|
-
*
|
|
1360
|
-
* @param requestedLock The desired whole-file lock.
|
|
1361
|
-
* @returns True if a conflicting lock exists, false otherwise.
|
|
1362
|
-
*/
|
|
1363
|
-
isThereAConflictWithRequestedWholeFileLock(requestedLock) {
|
|
1364
|
-
if (requestedLock.type === "exclusive") {
|
|
1365
|
-
if (this.wholeFileLock.type === "exclusive" && (this.wholeFileLock.fd !== requestedLock.fd || this.wholeFileLock.pid !== requestedLock.pid)) {
|
|
1366
|
-
return true;
|
|
1367
|
-
}
|
|
1368
|
-
if (this.wholeFileLock.type === "shared" && Array.from(this.wholeFileLock.pidFds).some(
|
|
1369
|
-
([pid]) => pid !== requestedLock.pid
|
|
1370
|
-
)) {
|
|
1371
|
-
return true;
|
|
1372
|
-
}
|
|
1373
|
-
const overlappingLocks = this.rangeLocks.findOverlapping({
|
|
1374
|
-
type: "unlocked",
|
|
1375
|
-
start: 0n,
|
|
1376
|
-
end: MAX_64BIT_OFFSET,
|
|
1377
|
-
pid: -1
|
|
1378
|
-
});
|
|
1379
|
-
if (overlappingLocks.length > 0) {
|
|
1380
|
-
return true;
|
|
1381
|
-
}
|
|
1382
|
-
return false;
|
|
1383
|
-
}
|
|
1384
|
-
if (requestedLock.type === "shared") {
|
|
1385
|
-
if (this.wholeFileLock.type === "exclusive" && this.wholeFileLock.pid !== requestedLock.pid) {
|
|
1386
|
-
return true;
|
|
1387
|
-
}
|
|
1388
|
-
const overlappingLocks = this.rangeLocks.findOverlapping({
|
|
1389
|
-
type: "unlocked",
|
|
1390
|
-
start: 0n,
|
|
1391
|
-
end: MAX_64BIT_OFFSET,
|
|
1392
|
-
pid: -1
|
|
1393
|
-
});
|
|
1394
|
-
const exclusiveRangeLocks = overlappingLocks.filter(
|
|
1395
|
-
(lock) => lock.type === "exclusive"
|
|
1396
|
-
);
|
|
1397
|
-
if (exclusiveRangeLocks.length > 0) {
|
|
1398
|
-
return true;
|
|
1399
|
-
}
|
|
1400
|
-
return false;
|
|
1401
|
-
}
|
|
1402
|
-
return false;
|
|
1403
|
-
}
|
|
1404
|
-
};
|
|
1405
|
-
var IntervalNode = class {
|
|
1406
|
-
constructor(range) {
|
|
1407
|
-
this.left = null;
|
|
1408
|
-
this.right = null;
|
|
1409
|
-
this.range = range;
|
|
1410
|
-
this.max = range.end;
|
|
1411
|
-
}
|
|
1412
|
-
};
|
|
1413
|
-
var FileLockIntervalTree = class {
|
|
1414
|
-
constructor() {
|
|
1415
|
-
this.root = null;
|
|
1416
|
-
}
|
|
1417
|
-
isEmpty() {
|
|
1418
|
-
return this.root === null;
|
|
1419
|
-
}
|
|
1420
|
-
/**
|
|
1421
|
-
* Insert a new locked range into the tree
|
|
1422
|
-
*/
|
|
1423
|
-
insert(range) {
|
|
1424
|
-
this.root = this.insertNode(this.root, range);
|
|
1425
|
-
}
|
|
1426
|
-
/**
|
|
1427
|
-
* Find all ranges that overlap with the given range
|
|
1428
|
-
*/
|
|
1429
|
-
findOverlapping(range) {
|
|
1430
|
-
const result = [];
|
|
1431
|
-
this.findOverlappingRanges(this.root, range, result);
|
|
1432
|
-
return result;
|
|
1433
|
-
}
|
|
1434
|
-
/**
|
|
1435
|
-
* Remove a lock range from the tree
|
|
1436
|
-
*/
|
|
1437
|
-
remove(range) {
|
|
1438
|
-
this.root = this.removeNode(this.root, range);
|
|
1439
|
-
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Find all ranges locked by the given process.
|
|
1442
|
-
*
|
|
1443
|
-
* @param pid The process ID to find locks for.
|
|
1444
|
-
* @returns All locked ranges for the given process.
|
|
1445
|
-
*/
|
|
1446
|
-
findLocksForProcess(pid) {
|
|
1447
|
-
const result = [];
|
|
1448
|
-
this.findLocksForProcessInNode(this.root, pid, result);
|
|
1449
|
-
return result;
|
|
1450
|
-
}
|
|
1451
|
-
/**
|
|
1452
|
-
* Find the strictest existing lock type in the range lock tree.
|
|
1453
|
-
*
|
|
1454
|
-
* @returns The strictest existing lock type, or 'unlocked' if no locks exist.
|
|
1455
|
-
*/
|
|
1456
|
-
findStrictestExistingLockType() {
|
|
1457
|
-
let maxType = "unlocked";
|
|
1458
|
-
const traverse = (node) => {
|
|
1459
|
-
if (!node) {
|
|
1460
|
-
return;
|
|
1461
|
-
}
|
|
1462
|
-
if (node.range.type === "exclusive") {
|
|
1463
|
-
maxType = "exclusive";
|
|
1464
|
-
return;
|
|
1465
|
-
}
|
|
1466
|
-
if (node.range.type === "shared") {
|
|
1467
|
-
maxType = "shared";
|
|
1468
|
-
}
|
|
1469
|
-
traverse(node.left);
|
|
1470
|
-
traverse(node.right);
|
|
1471
|
-
};
|
|
1472
|
-
traverse(this.root);
|
|
1473
|
-
return maxType;
|
|
1474
|
-
}
|
|
1475
|
-
insertNode(node, range) {
|
|
1476
|
-
if (!node) {
|
|
1477
|
-
return new IntervalNode(range);
|
|
1478
|
-
}
|
|
1479
|
-
if (range.start < node.range.start) {
|
|
1480
|
-
node.left = this.insertNode(node.left, range);
|
|
1481
|
-
} else {
|
|
1482
|
-
node.right = this.insertNode(node.right, range);
|
|
1483
|
-
}
|
|
1484
|
-
node.max = this.bigintMax(node.max, range.end);
|
|
1485
|
-
return node;
|
|
1486
|
-
}
|
|
1487
|
-
bigintMax(...args) {
|
|
1488
|
-
return args.reduce((max, current) => {
|
|
1489
|
-
return current > max ? current : max;
|
|
1490
|
-
}, args[0]);
|
|
1491
|
-
}
|
|
1492
|
-
findOverlappingRanges(node, range, result) {
|
|
1493
|
-
if (!node) {
|
|
1494
|
-
return;
|
|
1495
|
-
}
|
|
1496
|
-
if (this.doRangesOverlap(node.range, range)) {
|
|
1497
|
-
result.push(node.range);
|
|
1498
|
-
}
|
|
1499
|
-
if (node.left && node.left.max >= range.start) {
|
|
1500
|
-
this.findOverlappingRanges(node.left, range, result);
|
|
1501
|
-
}
|
|
1502
|
-
if (node.right && node.range.start <= range.end) {
|
|
1503
|
-
this.findOverlappingRanges(node.right, range, result);
|
|
1504
|
-
}
|
|
1505
|
-
}
|
|
1506
|
-
doRangesOverlap(a, b) {
|
|
1507
|
-
return a.start < b.end && b.start < a.end;
|
|
1508
|
-
}
|
|
1509
|
-
removeNode(node, range) {
|
|
1510
|
-
if (!node) {
|
|
1511
|
-
return null;
|
|
1512
|
-
}
|
|
1513
|
-
if (this.areRangesEqual(node.range, range)) {
|
|
1514
|
-
if (!node.left) {
|
|
1515
|
-
return node.right;
|
|
1516
|
-
}
|
|
1517
|
-
if (!node.right) {
|
|
1518
|
-
return node.left;
|
|
1519
|
-
}
|
|
1520
|
-
const successor = this.findMin(node.right);
|
|
1521
|
-
node.range = successor.range;
|
|
1522
|
-
node.right = this.removeNode(node.right, successor.range);
|
|
1523
|
-
} else if (range.start < node.range.start) {
|
|
1524
|
-
node.left = this.removeNode(node.left, range);
|
|
1525
|
-
} else {
|
|
1526
|
-
node.right = this.removeNode(node.right, range);
|
|
1527
|
-
}
|
|
1528
|
-
node.max = node.range.end;
|
|
1529
|
-
if (node.left) {
|
|
1530
|
-
node.max = this.bigintMax(node.max, node.left.max);
|
|
1531
|
-
}
|
|
1532
|
-
if (node.right) {
|
|
1533
|
-
node.max = this.bigintMax(node.max, node.right.max);
|
|
1534
|
-
}
|
|
1535
|
-
return node;
|
|
1536
|
-
}
|
|
1537
|
-
findMin(node) {
|
|
1538
|
-
let current = node;
|
|
1539
|
-
while (current.left) {
|
|
1540
|
-
current = current.left;
|
|
1541
|
-
}
|
|
1542
|
-
return current;
|
|
1543
|
-
}
|
|
1544
|
-
areRangesEqual(a, b) {
|
|
1545
|
-
return a.start === b.start && a.end === b.end && a.pid === b.pid;
|
|
1546
|
-
}
|
|
1547
|
-
findLocksForProcessInNode(node, pid, result) {
|
|
1548
|
-
if (!node) {
|
|
1549
|
-
return;
|
|
1550
|
-
}
|
|
1551
|
-
if (node.range.pid === pid) {
|
|
1552
|
-
result.push(node.range);
|
|
1553
|
-
}
|
|
1554
|
-
this.findLocksForProcessInNode(node.left, pid, result);
|
|
1555
|
-
this.findLocksForProcessInNode(node.right, pid, result);
|
|
1556
|
-
}
|
|
1557
|
-
};
|
|
1558
2072
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1559
2073
|
0 && (module.exports = {
|
|
1560
|
-
|
|
1561
|
-
|
|
2074
|
+
FileLockManagerForPosix,
|
|
2075
|
+
FileLockManagerForWindows,
|
|
2076
|
+
bindUserSpace,
|
|
1562
2077
|
createNodeFsMountHandler,
|
|
1563
2078
|
getPHPLoaderModule,
|
|
1564
2079
|
loadNodeRuntime,
|