@machinen/runtime 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API.md +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +10 -4
- package/dist/index.js.map +1 -1
- package/dist/mount-server-bin.js +738 -14
- package/dist/mount-server-bin.js.map +1 -1
- package/package.json +7 -7
package/dist/mount-server-bin.js
CHANGED
|
@@ -8,9 +8,13 @@ import { renameSync, writeFileSync, unlinkSync } from "fs";
|
|
|
8
8
|
|
|
9
9
|
// src/mount-server.ts
|
|
10
10
|
import { createServer } from "net";
|
|
11
|
+
import { execFile as execFileCb } from "child_process";
|
|
11
12
|
import {
|
|
12
13
|
chmod,
|
|
14
|
+
lchmod,
|
|
15
|
+
lchown,
|
|
13
16
|
link,
|
|
17
|
+
lutimes,
|
|
14
18
|
mkdir,
|
|
15
19
|
open as fsOpen,
|
|
16
20
|
lstat,
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
} from "fs/promises";
|
|
28
32
|
import { constants as fsConstants } from "fs";
|
|
29
33
|
import { join as join2 } from "path";
|
|
34
|
+
import { promisify } from "util";
|
|
30
35
|
import debug from "debug";
|
|
31
36
|
|
|
32
37
|
// src/mount-resolver.ts
|
|
@@ -91,17 +96,36 @@ var FUSE_OP = {
|
|
|
91
96
|
STATFS: 17,
|
|
92
97
|
RELEASE: 18,
|
|
93
98
|
FSYNC: 20,
|
|
99
|
+
SETXATTR: 21,
|
|
100
|
+
GETXATTR: 22,
|
|
101
|
+
LISTXATTR: 23,
|
|
102
|
+
REMOVEXATTR: 24,
|
|
94
103
|
FLUSH: 25,
|
|
95
104
|
INIT: 26,
|
|
96
105
|
OPENDIR: 27,
|
|
97
106
|
READDIR: 28,
|
|
98
107
|
RELEASEDIR: 29,
|
|
108
|
+
FSYNCDIR: 30,
|
|
109
|
+
GETLK: 31,
|
|
110
|
+
SETLK: 32,
|
|
111
|
+
SETLKW: 33,
|
|
99
112
|
ACCESS: 34,
|
|
100
113
|
CREATE: 35,
|
|
101
114
|
INTERRUPT: 36,
|
|
102
115
|
DESTROY: 38,
|
|
103
116
|
BATCH_FORGET: 42,
|
|
104
|
-
|
|
117
|
+
FALLOCATE: 43,
|
|
118
|
+
READDIRPLUS: 44,
|
|
119
|
+
LSEEK: 46,
|
|
120
|
+
COPY_FILE_RANGE: 47
|
|
121
|
+
};
|
|
122
|
+
var F_LCK = {
|
|
123
|
+
RDLCK: 0,
|
|
124
|
+
WRLCK: 1,
|
|
125
|
+
UNLCK: 2
|
|
126
|
+
};
|
|
127
|
+
var FUSE_LK_FLAGS = {
|
|
128
|
+
FLOCK: 1 << 0
|
|
105
129
|
};
|
|
106
130
|
var FATTR = {
|
|
107
131
|
MODE: 1 << 0,
|
|
@@ -408,6 +432,59 @@ function readLinkIn(buf, off = 0) {
|
|
|
408
432
|
oldnodeid: dv.getBigUint64(0, true)
|
|
409
433
|
};
|
|
410
434
|
}
|
|
435
|
+
var FUSE_FALLOCATE_IN_SIZE = 32;
|
|
436
|
+
function readFallocateIn(buf, off = 0) {
|
|
437
|
+
const dv = viewOf(buf, off, FUSE_FALLOCATE_IN_SIZE);
|
|
438
|
+
return {
|
|
439
|
+
fh: dv.getBigUint64(0, true),
|
|
440
|
+
offset: dv.getBigUint64(8, true),
|
|
441
|
+
length: dv.getBigUint64(16, true),
|
|
442
|
+
mode: dv.getUint32(24, true)
|
|
443
|
+
// padding at 28
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
var FALLOC_FL = {
|
|
447
|
+
KEEP_SIZE: 1,
|
|
448
|
+
PUNCH_HOLE: 2,
|
|
449
|
+
COLLAPSE_RANGE: 8,
|
|
450
|
+
ZERO_RANGE: 16,
|
|
451
|
+
INSERT_RANGE: 32
|
|
452
|
+
};
|
|
453
|
+
var FUSE_LSEEK_IN_SIZE = 24;
|
|
454
|
+
function readLseekIn(buf, off = 0) {
|
|
455
|
+
const dv = viewOf(buf, off, FUSE_LSEEK_IN_SIZE);
|
|
456
|
+
return {
|
|
457
|
+
fh: dv.getBigUint64(0, true),
|
|
458
|
+
offset: dv.getBigUint64(8, true),
|
|
459
|
+
whence: dv.getUint32(16, true)
|
|
460
|
+
// padding at 20
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
function buildLseekOut(o) {
|
|
464
|
+
const buf = new Uint8Array(8);
|
|
465
|
+
new DataView(buf.buffer).setBigUint64(0, o.offset, true);
|
|
466
|
+
return buf;
|
|
467
|
+
}
|
|
468
|
+
var SEEK = {
|
|
469
|
+
SET: 0,
|
|
470
|
+
CUR: 1,
|
|
471
|
+
END: 2,
|
|
472
|
+
DATA: 3,
|
|
473
|
+
HOLE: 4
|
|
474
|
+
};
|
|
475
|
+
var FUSE_COPY_FILE_RANGE_IN_SIZE = 56;
|
|
476
|
+
function readCopyFileRangeIn(buf, off = 0) {
|
|
477
|
+
const dv = viewOf(buf, off, FUSE_COPY_FILE_RANGE_IN_SIZE);
|
|
478
|
+
return {
|
|
479
|
+
fh_in: dv.getBigUint64(0, true),
|
|
480
|
+
off_in: dv.getBigUint64(8, true),
|
|
481
|
+
nodeid_out: dv.getBigUint64(16, true),
|
|
482
|
+
fh_out: dv.getBigUint64(24, true),
|
|
483
|
+
off_out: dv.getBigUint64(32, true),
|
|
484
|
+
len: dv.getBigUint64(40, true),
|
|
485
|
+
flags: dv.getBigUint64(48, true)
|
|
486
|
+
};
|
|
487
|
+
}
|
|
411
488
|
var FUSE_RENAME_IN_SIZE = 8;
|
|
412
489
|
function readRenameIn(buf, off = 0) {
|
|
413
490
|
const dv = viewOf(buf, off, FUSE_RENAME_IN_SIZE);
|
|
@@ -415,6 +492,66 @@ function readRenameIn(buf, off = 0) {
|
|
|
415
492
|
newdir: dv.getBigUint64(0, true)
|
|
416
493
|
};
|
|
417
494
|
}
|
|
495
|
+
var FUSE_FILE_LOCK_SIZE = 24;
|
|
496
|
+
function readFileLock(buf, offset) {
|
|
497
|
+
const dv = viewOf(buf, offset, FUSE_FILE_LOCK_SIZE);
|
|
498
|
+
return {
|
|
499
|
+
start: dv.getBigUint64(0, true),
|
|
500
|
+
end: dv.getBigUint64(8, true),
|
|
501
|
+
type: dv.getUint32(16, true),
|
|
502
|
+
pid: dv.getUint32(20, true)
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
function writeFileLock(buf, offset, lk) {
|
|
506
|
+
const dv = viewOf(buf, offset, FUSE_FILE_LOCK_SIZE);
|
|
507
|
+
dv.setBigUint64(0, lk.start, true);
|
|
508
|
+
dv.setBigUint64(8, lk.end, true);
|
|
509
|
+
dv.setUint32(16, lk.type, true);
|
|
510
|
+
dv.setUint32(20, lk.pid, true);
|
|
511
|
+
}
|
|
512
|
+
var FUSE_LK_IN_SIZE = 48;
|
|
513
|
+
function readLkIn(buf, offset = 0) {
|
|
514
|
+
const dv = viewOf(buf, offset, FUSE_LK_IN_SIZE);
|
|
515
|
+
return {
|
|
516
|
+
fh: dv.getBigUint64(0, true),
|
|
517
|
+
owner: dv.getBigUint64(8, true),
|
|
518
|
+
lk: readFileLock(buf, offset + 16),
|
|
519
|
+
lk_flags: dv.getUint32(40, true)
|
|
520
|
+
// padding at offset 44
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
var FUSE_LK_OUT_SIZE = FUSE_FILE_LOCK_SIZE;
|
|
524
|
+
function buildLkOut(lk) {
|
|
525
|
+
const buf = new Uint8Array(FUSE_LK_OUT_SIZE);
|
|
526
|
+
writeFileLock(buf, 0, lk);
|
|
527
|
+
return buf;
|
|
528
|
+
}
|
|
529
|
+
var FUSE_SETXATTR_IN_SIZE = 8;
|
|
530
|
+
function readSetxattrIn(buf, off = 0) {
|
|
531
|
+
const dv = viewOf(buf, off, FUSE_SETXATTR_IN_SIZE);
|
|
532
|
+
return {
|
|
533
|
+
size: dv.getUint32(0, true),
|
|
534
|
+
flags: dv.getUint32(4, true)
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
var XATTR = {
|
|
538
|
+
CREATE: 1,
|
|
539
|
+
REPLACE: 2
|
|
540
|
+
};
|
|
541
|
+
var FUSE_GETXATTR_IN_SIZE = 8;
|
|
542
|
+
function readGetxattrIn(buf, off = 0) {
|
|
543
|
+
const dv = viewOf(buf, off, FUSE_GETXATTR_IN_SIZE);
|
|
544
|
+
return {
|
|
545
|
+
size: dv.getUint32(0, true)
|
|
546
|
+
// padding at offset 4
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
var FUSE_GETXATTR_OUT_SIZE = 8;
|
|
550
|
+
function buildGetxattrOut(size) {
|
|
551
|
+
const buf = new Uint8Array(FUSE_GETXATTR_OUT_SIZE);
|
|
552
|
+
new DataView(buf.buffer).setUint32(0, size, true);
|
|
553
|
+
return buf;
|
|
554
|
+
}
|
|
418
555
|
function viewOf(buf, offset, len) {
|
|
419
556
|
if (offset < 0 || len < 0 || offset + len > buf.length) {
|
|
420
557
|
throw new Error(
|
|
@@ -431,21 +568,35 @@ function payloadOf(message) {
|
|
|
431
568
|
}
|
|
432
569
|
|
|
433
570
|
// src/mount-server.ts
|
|
571
|
+
var execFile = promisify(execFileCb);
|
|
434
572
|
var log = debug("machinen:mount-server");
|
|
435
573
|
var ERRNO = {
|
|
436
574
|
EPERM: 1,
|
|
437
575
|
ENOENT: 2,
|
|
576
|
+
EINTR: 4,
|
|
438
577
|
EIO: 5,
|
|
578
|
+
ENXIO: 6,
|
|
439
579
|
EBADF: 9,
|
|
580
|
+
EAGAIN: 11,
|
|
440
581
|
EACCES: 13,
|
|
441
582
|
EBUSY: 16,
|
|
442
583
|
EEXIST: 17,
|
|
443
584
|
ENOTDIR: 20,
|
|
444
585
|
EISDIR: 21,
|
|
445
586
|
EINVAL: 22,
|
|
587
|
+
ERANGE: 34,
|
|
446
588
|
EROFS: 30,
|
|
447
589
|
ENOSYS: 38,
|
|
448
590
|
ENOTEMPTY: 39,
|
|
591
|
+
// "Attribute not found" — Linux errno for getxattr/removexattr on a
|
|
592
|
+
// name that doesn't exist. macOS calls the same condition ENOATTR
|
|
593
|
+
// (errno 93) and our shell-out translates it to ENODATA on the wire.
|
|
594
|
+
ENODATA: 61,
|
|
595
|
+
// ENOTSUP / EOPNOTSUPP share value 95 on Linux. Some macOS-host
|
|
596
|
+
// ops (lchmod on filesystems without symlink-mode support, lutimes
|
|
597
|
+
// on older APFS) throw ENOTSUP; pass it through verbatim so tar
|
|
598
|
+
// and friends see a soft failure rather than the EIO catch-all.
|
|
599
|
+
ENOTSUP: 95,
|
|
449
600
|
ESTALE: 116
|
|
450
601
|
};
|
|
451
602
|
async function serveLiveMount(udsPath, opts) {
|
|
@@ -475,7 +626,9 @@ function createState(rootAbs, mode) {
|
|
|
475
626
|
handles: /* @__PURE__ */ new Map(),
|
|
476
627
|
nextHandle: 1n,
|
|
477
628
|
socket: null,
|
|
478
|
-
bytesServedOnPagesImg: 0
|
|
629
|
+
bytesServedOnPagesImg: 0,
|
|
630
|
+
locks: /* @__PURE__ */ new Map(),
|
|
631
|
+
lockWaiters: []
|
|
479
632
|
};
|
|
480
633
|
}
|
|
481
634
|
var PAGES_IMG_RE = /(?:^|\/)pages-\d+\.img$/;
|
|
@@ -492,6 +645,8 @@ async function shutdown(server, state) {
|
|
|
492
645
|
}
|
|
493
646
|
}
|
|
494
647
|
state.handles.clear();
|
|
648
|
+
cancelAllWaiters(state);
|
|
649
|
+
state.locks.clear();
|
|
495
650
|
await new Promise((done) => server.close(() => done()));
|
|
496
651
|
}
|
|
497
652
|
async function handleConnection(sock, state) {
|
|
@@ -502,6 +657,8 @@ async function handleConnection(sock, state) {
|
|
|
502
657
|
state.socket = sock;
|
|
503
658
|
sock.on("close", () => {
|
|
504
659
|
state.socket = null;
|
|
660
|
+
cancelAllWaiters(state);
|
|
661
|
+
state.locks.clear();
|
|
505
662
|
});
|
|
506
663
|
sock.on("error", (err) => log("socket error: %s", err.message));
|
|
507
664
|
try {
|
|
@@ -603,6 +760,28 @@ async function handle(hdr, msg, state) {
|
|
|
603
760
|
return onReleasedir(hdr, msg, state);
|
|
604
761
|
case FUSE_OP.FSYNC:
|
|
605
762
|
return await onFsync(hdr, msg, state);
|
|
763
|
+
case FUSE_OP.FSYNCDIR:
|
|
764
|
+
return await onFsyncdir(hdr, msg, state);
|
|
765
|
+
case FUSE_OP.FALLOCATE:
|
|
766
|
+
return await onFallocate(hdr, msg, state);
|
|
767
|
+
case FUSE_OP.LSEEK:
|
|
768
|
+
return await onLseek(hdr, msg, state);
|
|
769
|
+
case FUSE_OP.COPY_FILE_RANGE:
|
|
770
|
+
return await onCopyFileRange(hdr, msg, state);
|
|
771
|
+
case FUSE_OP.SETXATTR:
|
|
772
|
+
return await onSetxattr(hdr, msg, state);
|
|
773
|
+
case FUSE_OP.GETXATTR:
|
|
774
|
+
return await onGetxattr(hdr, msg, state);
|
|
775
|
+
case FUSE_OP.LISTXATTR:
|
|
776
|
+
return await onListxattr(hdr, msg, state);
|
|
777
|
+
case FUSE_OP.REMOVEXATTR:
|
|
778
|
+
return await onRemovexattr(hdr, msg, state);
|
|
779
|
+
case FUSE_OP.GETLK:
|
|
780
|
+
return onGetlk(hdr, msg, state);
|
|
781
|
+
case FUSE_OP.SETLK:
|
|
782
|
+
return onSetlk(hdr, msg, state);
|
|
783
|
+
case FUSE_OP.SETLKW:
|
|
784
|
+
return await onSetlkw(hdr, msg, state);
|
|
606
785
|
case FUSE_OP.FLUSH:
|
|
607
786
|
case FUSE_OP.ACCESS:
|
|
608
787
|
return buildErrorResponse(hdr.unique, 0);
|
|
@@ -613,7 +792,7 @@ async function handle(hdr, msg, state) {
|
|
|
613
792
|
function onInit(hdr, msg) {
|
|
614
793
|
const init = readInitIn(payloadOf(msg));
|
|
615
794
|
const minor = Math.min(init.minor, FUSE_KERNEL_MINOR_VERSION);
|
|
616
|
-
const supported = FUSE_CAP.ASYNC_READ | FUSE_CAP.BIG_WRITES | FUSE_CAP.EXPORT_SUPPORT | FUSE_CAP.MAX_PAGES | FUSE_CAP.PARALLEL_DIROPS;
|
|
795
|
+
const supported = FUSE_CAP.ASYNC_READ | FUSE_CAP.BIG_WRITES | FUSE_CAP.EXPORT_SUPPORT | FUSE_CAP.MAX_PAGES | FUSE_CAP.PARALLEL_DIROPS | FUSE_CAP.POSIX_LOCKS;
|
|
617
796
|
const flags = init.flags & supported;
|
|
618
797
|
const out = buildInitOut({
|
|
619
798
|
major: FUSE_KERNEL_VERSION,
|
|
@@ -728,7 +907,8 @@ async function onOpen(hdr, msg, state) {
|
|
|
728
907
|
state.handles.set(id, {
|
|
729
908
|
kind: "file",
|
|
730
909
|
fh,
|
|
731
|
-
lazyPagesContrib: isPagesImgPath(entry.relPath)
|
|
910
|
+
lazyPagesContrib: isPagesImgPath(entry.relPath),
|
|
911
|
+
nodeid: hdr.nodeid
|
|
732
912
|
});
|
|
733
913
|
return buildResponse(hdr.unique, buildOpenOut({ fh: id, open_flags: 0 }));
|
|
734
914
|
}
|
|
@@ -746,10 +926,11 @@ async function onRead(hdr, msg, state) {
|
|
|
746
926
|
return buildResponse(hdr.unique, new Uint8Array(buf.buffer, buf.byteOffset, bytesRead));
|
|
747
927
|
}
|
|
748
928
|
async function onRelease(hdr, msg, state) {
|
|
749
|
-
const
|
|
750
|
-
const entry = state.handles.get(fh);
|
|
929
|
+
const rel = readReleaseIn(payloadOf(msg));
|
|
930
|
+
const entry = state.handles.get(rel.fh);
|
|
751
931
|
if (entry && entry.kind === "file") {
|
|
752
|
-
state.handles.delete(fh);
|
|
932
|
+
state.handles.delete(rel.fh);
|
|
933
|
+
dropLocksFor(state, entry.nodeid, rel.lock_owner);
|
|
753
934
|
await entry.fh.close().catch(() => {
|
|
754
935
|
});
|
|
755
936
|
}
|
|
@@ -842,7 +1023,8 @@ async function onCreate(hdr, msg, state) {
|
|
|
842
1023
|
state.handles.set(id, {
|
|
843
1024
|
kind: "file",
|
|
844
1025
|
fh,
|
|
845
|
-
lazyPagesContrib: isPagesImgPath(childRel)
|
|
1026
|
+
lazyPagesContrib: isPagesImgPath(childRel),
|
|
1027
|
+
nodeid: ino
|
|
846
1028
|
});
|
|
847
1029
|
return buildResponse(
|
|
848
1030
|
hdr.unique,
|
|
@@ -1009,8 +1191,13 @@ async function onSetattr(hdr, msg, state) {
|
|
|
1009
1191
|
return buildErrorResponse(hdr.unique, -ERRNO.EROFS);
|
|
1010
1192
|
}
|
|
1011
1193
|
const entry = requireInode(state, hdr.nodeid);
|
|
1012
|
-
const abs = await
|
|
1194
|
+
const abs = await absPathForLstat(state, entry);
|
|
1195
|
+
const st0 = await lstat(abs);
|
|
1196
|
+
const isSymlink = (st0.mode & 61440) === 40960;
|
|
1013
1197
|
if (req.valid & FATTR.SIZE) {
|
|
1198
|
+
if (isSymlink) {
|
|
1199
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1200
|
+
}
|
|
1014
1201
|
if (req.valid & FATTR.FH) {
|
|
1015
1202
|
const handle2 = state.handles.get(req.fh);
|
|
1016
1203
|
if (!handle2 || handle2.kind !== "file") {
|
|
@@ -1022,14 +1209,40 @@ async function onSetattr(hdr, msg, state) {
|
|
|
1022
1209
|
}
|
|
1023
1210
|
}
|
|
1024
1211
|
if (req.valid & FATTR.MODE) {
|
|
1025
|
-
|
|
1212
|
+
const mode = req.mode & 4095;
|
|
1213
|
+
if (isSymlink) {
|
|
1214
|
+
try {
|
|
1215
|
+
await lchmod(abs, mode);
|
|
1216
|
+
} catch (err) {
|
|
1217
|
+
if (!isUnsupportedFsErr(err)) {
|
|
1218
|
+
throw err;
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
} else {
|
|
1222
|
+
await chmod(abs, mode);
|
|
1223
|
+
}
|
|
1026
1224
|
}
|
|
1027
1225
|
if (req.valid & (FATTR.ATIME | FATTR.MTIME | FATTR.ATIME_NOW | FATTR.MTIME_NOW)) {
|
|
1028
|
-
const st2 = await lstat(abs);
|
|
1029
1226
|
const now = Date.now() / 1e3;
|
|
1030
|
-
const a = req.valid & FATTR.ATIME_NOW ? now : req.valid & FATTR.ATIME ? Number(req.atime) + req.atimensec / 1e9 :
|
|
1031
|
-
const m = req.valid & FATTR.MTIME_NOW ? now : req.valid & FATTR.MTIME ? Number(req.mtime) + req.mtimensec / 1e9 :
|
|
1032
|
-
|
|
1227
|
+
const a = req.valid & FATTR.ATIME_NOW ? now : req.valid & FATTR.ATIME ? Number(req.atime) + req.atimensec / 1e9 : st0.atimeMs / 1e3;
|
|
1228
|
+
const m = req.valid & FATTR.MTIME_NOW ? now : req.valid & FATTR.MTIME ? Number(req.mtime) + req.mtimensec / 1e9 : st0.mtimeMs / 1e3;
|
|
1229
|
+
if (isSymlink) {
|
|
1230
|
+
await lutimes(abs, a, m);
|
|
1231
|
+
} else {
|
|
1232
|
+
await utimes(abs, a, m);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (req.valid & (FATTR.UID | FATTR.GID)) {
|
|
1236
|
+
if (isSymlink) {
|
|
1237
|
+
try {
|
|
1238
|
+
await lchown(abs, req.uid, req.gid);
|
|
1239
|
+
} catch (err) {
|
|
1240
|
+
const code = err.code;
|
|
1241
|
+
if (code !== "EPERM" && code !== "ENOSYS" && !isUnsupportedFsErr(err)) {
|
|
1242
|
+
throw err;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1033
1246
|
}
|
|
1034
1247
|
const st = await lstat(abs);
|
|
1035
1248
|
return buildResponse(
|
|
@@ -1041,6 +1254,10 @@ async function onSetattr(hdr, msg, state) {
|
|
|
1041
1254
|
})
|
|
1042
1255
|
);
|
|
1043
1256
|
}
|
|
1257
|
+
function isUnsupportedFsErr(err) {
|
|
1258
|
+
const code = err?.code;
|
|
1259
|
+
return code === "ENOTSUP" || code === "EOPNOTSUPP" || code === "ENOSYS";
|
|
1260
|
+
}
|
|
1044
1261
|
async function onFsync(hdr, msg, state) {
|
|
1045
1262
|
const body = payloadOf(msg);
|
|
1046
1263
|
if (body.length < 8) {
|
|
@@ -1055,6 +1272,510 @@ async function onFsync(hdr, msg, state) {
|
|
|
1055
1272
|
await handle2.fh.sync();
|
|
1056
1273
|
return buildErrorResponse(hdr.unique, 0);
|
|
1057
1274
|
}
|
|
1275
|
+
async function onFsyncdir(hdr, msg, state) {
|
|
1276
|
+
const body = payloadOf(msg);
|
|
1277
|
+
if (body.length < 8) {
|
|
1278
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1279
|
+
}
|
|
1280
|
+
const dv = new DataView(body.buffer, body.byteOffset, body.length);
|
|
1281
|
+
const fh = dv.getBigUint64(0, true);
|
|
1282
|
+
const handle2 = state.handles.get(fh);
|
|
1283
|
+
if (!handle2 || handle2.kind !== "dir") {
|
|
1284
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EBADF);
|
|
1285
|
+
}
|
|
1286
|
+
const entry = requireInode(state, hdr.nodeid);
|
|
1287
|
+
const abs = await absPathForTraversal(state, entry);
|
|
1288
|
+
const dirFh = await fsOpen(abs, fsConstants.O_RDONLY);
|
|
1289
|
+
try {
|
|
1290
|
+
await dirFh.sync();
|
|
1291
|
+
} finally {
|
|
1292
|
+
await dirFh.close().catch(() => {
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1296
|
+
}
|
|
1297
|
+
async function onFallocate(hdr, msg, state) {
|
|
1298
|
+
if (state.mode === "ro") {
|
|
1299
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EROFS);
|
|
1300
|
+
}
|
|
1301
|
+
const req = readFallocateIn(payloadOf(msg));
|
|
1302
|
+
const handle2 = state.handles.get(req.fh);
|
|
1303
|
+
if (!handle2 || handle2.kind !== "file") {
|
|
1304
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EBADF);
|
|
1305
|
+
}
|
|
1306
|
+
const unsupported = FALLOC_FL.PUNCH_HOLE | FALLOC_FL.COLLAPSE_RANGE | FALLOC_FL.ZERO_RANGE | FALLOC_FL.INSERT_RANGE;
|
|
1307
|
+
if (req.mode & unsupported) {
|
|
1308
|
+
return buildErrorResponse(hdr.unique, -ERRNO.ENOSYS);
|
|
1309
|
+
}
|
|
1310
|
+
if (!(req.mode & FALLOC_FL.KEEP_SIZE)) {
|
|
1311
|
+
const target = Number(req.offset + req.length);
|
|
1312
|
+
const st = await handle2.fh.stat();
|
|
1313
|
+
if (target > st.size) {
|
|
1314
|
+
await handle2.fh.truncate(target);
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1318
|
+
}
|
|
1319
|
+
async function onLseek(hdr, msg, state) {
|
|
1320
|
+
const req = readLseekIn(payloadOf(msg));
|
|
1321
|
+
const handle2 = state.handles.get(req.fh);
|
|
1322
|
+
if (!handle2 || handle2.kind !== "file") {
|
|
1323
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EBADF);
|
|
1324
|
+
}
|
|
1325
|
+
let resolved;
|
|
1326
|
+
switch (req.whence) {
|
|
1327
|
+
case SEEK.SET:
|
|
1328
|
+
resolved = req.offset;
|
|
1329
|
+
break;
|
|
1330
|
+
case SEEK.CUR:
|
|
1331
|
+
resolved = req.offset;
|
|
1332
|
+
break;
|
|
1333
|
+
case SEEK.END: {
|
|
1334
|
+
const st = await handle2.fh.stat();
|
|
1335
|
+
resolved = BigInt(st.size) + req.offset;
|
|
1336
|
+
break;
|
|
1337
|
+
}
|
|
1338
|
+
case SEEK.HOLE:
|
|
1339
|
+
case SEEK.DATA:
|
|
1340
|
+
return buildErrorResponse(hdr.unique, -ERRNO.ENOSYS);
|
|
1341
|
+
default:
|
|
1342
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1343
|
+
}
|
|
1344
|
+
if (resolved < 0n) {
|
|
1345
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1346
|
+
}
|
|
1347
|
+
return buildResponse(hdr.unique, buildLseekOut({ offset: resolved }));
|
|
1348
|
+
}
|
|
1349
|
+
var COPY_FILE_RANGE_CHUNK = 1 << 20;
|
|
1350
|
+
async function onCopyFileRange(hdr, msg, state) {
|
|
1351
|
+
if (state.mode === "ro") {
|
|
1352
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EROFS);
|
|
1353
|
+
}
|
|
1354
|
+
const req = readCopyFileRangeIn(payloadOf(msg));
|
|
1355
|
+
if (req.flags !== 0n) {
|
|
1356
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1357
|
+
}
|
|
1358
|
+
const src = state.handles.get(req.fh_in);
|
|
1359
|
+
const dst = state.handles.get(req.fh_out);
|
|
1360
|
+
if (!src || src.kind !== "file" || !dst || dst.kind !== "file") {
|
|
1361
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EBADF);
|
|
1362
|
+
}
|
|
1363
|
+
let copied = 0;
|
|
1364
|
+
let srcOff = Number(req.off_in);
|
|
1365
|
+
let dstOff = Number(req.off_out);
|
|
1366
|
+
const total = Number(req.len);
|
|
1367
|
+
const buf = Buffer.allocUnsafe(Math.min(total, COPY_FILE_RANGE_CHUNK));
|
|
1368
|
+
while (copied < total) {
|
|
1369
|
+
const want = Math.min(total - copied, buf.length);
|
|
1370
|
+
const { bytesRead } = await src.fh.read(buf, 0, want, srcOff);
|
|
1371
|
+
if (bytesRead === 0) {
|
|
1372
|
+
break;
|
|
1373
|
+
}
|
|
1374
|
+
const { bytesWritten } = await dst.fh.write(buf, 0, bytesRead, dstOff);
|
|
1375
|
+
copied += bytesWritten;
|
|
1376
|
+
srcOff += bytesRead;
|
|
1377
|
+
dstOff += bytesWritten;
|
|
1378
|
+
if (bytesWritten < bytesRead) {
|
|
1379
|
+
break;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
return buildResponse(hdr.unique, buildWriteOut({ size: copied }));
|
|
1383
|
+
}
|
|
1384
|
+
var IS_DARWIN = process.platform === "darwin";
|
|
1385
|
+
function isXattrError(e) {
|
|
1386
|
+
return typeof e === "object" && e !== null && "errno" in e && typeof e.errno === "number";
|
|
1387
|
+
}
|
|
1388
|
+
function decodeXattrStderr(stderr) {
|
|
1389
|
+
if (/no such (xattr|attribute)|no data available/i.test(stderr)) {
|
|
1390
|
+
return ERRNO.ENODATA;
|
|
1391
|
+
}
|
|
1392
|
+
if (/operation not permitted|permission denied/i.test(stderr)) {
|
|
1393
|
+
return ERRNO.EACCES;
|
|
1394
|
+
}
|
|
1395
|
+
if (/not supported|operation not supported/i.test(stderr)) {
|
|
1396
|
+
return ERRNO.ENOTSUP;
|
|
1397
|
+
}
|
|
1398
|
+
if (/file name too long|argument list too long/i.test(stderr)) {
|
|
1399
|
+
return ERRNO.EINVAL;
|
|
1400
|
+
}
|
|
1401
|
+
return ERRNO.EIO;
|
|
1402
|
+
}
|
|
1403
|
+
function xattrErrorFromExec(err) {
|
|
1404
|
+
const e = err;
|
|
1405
|
+
if (e.code === "ENOENT") {
|
|
1406
|
+
return { errno: ERRNO.ENOTSUP };
|
|
1407
|
+
}
|
|
1408
|
+
const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString("utf8") ?? "";
|
|
1409
|
+
return { errno: decodeXattrStderr(stderr) };
|
|
1410
|
+
}
|
|
1411
|
+
async function hostGetxattr(absPath, name) {
|
|
1412
|
+
try {
|
|
1413
|
+
if (IS_DARWIN) {
|
|
1414
|
+
const { stdout: stdout2 } = await execFile("xattr", ["-p", "-s", "-x", name, absPath], {
|
|
1415
|
+
encoding: "utf8"
|
|
1416
|
+
});
|
|
1417
|
+
return Buffer.from(stdout2.replace(/\s+/g, ""), "hex");
|
|
1418
|
+
}
|
|
1419
|
+
const { stdout } = await execFile(
|
|
1420
|
+
"getfattr",
|
|
1421
|
+
["--no-dereference", "--absolute-names", "--only-values", "-n", name, absPath],
|
|
1422
|
+
{ encoding: "buffer" }
|
|
1423
|
+
);
|
|
1424
|
+
return stdout;
|
|
1425
|
+
} catch (err) {
|
|
1426
|
+
const xe = xattrErrorFromExec(err);
|
|
1427
|
+
if (xe.errno === ERRNO.ENODATA) {
|
|
1428
|
+
return null;
|
|
1429
|
+
}
|
|
1430
|
+
throw xe;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
async function hostXattrExists(absPath, name) {
|
|
1434
|
+
return await hostGetxattr(absPath, name) !== null;
|
|
1435
|
+
}
|
|
1436
|
+
async function hostSetxattr(absPath, name, value) {
|
|
1437
|
+
try {
|
|
1438
|
+
if (IS_DARWIN) {
|
|
1439
|
+
const hex = Buffer.from(value.buffer, value.byteOffset, value.length).toString("hex");
|
|
1440
|
+
await execFile("xattr", ["-w", "-s", "-x", name, hex, absPath]);
|
|
1441
|
+
return;
|
|
1442
|
+
}
|
|
1443
|
+
const b64 = Buffer.from(value.buffer, value.byteOffset, value.length).toString("base64");
|
|
1444
|
+
await execFile("setfattr", ["--no-dereference", "-n", name, "-v", `0s${b64}`, absPath]);
|
|
1445
|
+
} catch (err) {
|
|
1446
|
+
throw xattrErrorFromExec(err);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
async function hostListxattr(absPath) {
|
|
1450
|
+
try {
|
|
1451
|
+
if (IS_DARWIN) {
|
|
1452
|
+
const { stdout: stdout2 } = await execFile("xattr", ["-s", absPath], { encoding: "utf8" });
|
|
1453
|
+
return stdout2.split("\n").filter((n) => n.length > 0);
|
|
1454
|
+
}
|
|
1455
|
+
const { stdout } = await execFile(
|
|
1456
|
+
"getfattr",
|
|
1457
|
+
["--no-dereference", "--absolute-names", "--match=.", absPath],
|
|
1458
|
+
{ encoding: "utf8" }
|
|
1459
|
+
);
|
|
1460
|
+
return stdout.split("\n").map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
1461
|
+
} catch (err) {
|
|
1462
|
+
const xe = xattrErrorFromExec(err);
|
|
1463
|
+
if (xe.errno === ERRNO.ENODATA) {
|
|
1464
|
+
return [];
|
|
1465
|
+
}
|
|
1466
|
+
throw xe;
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
async function hostRemovexattr(absPath, name) {
|
|
1470
|
+
try {
|
|
1471
|
+
if (IS_DARWIN) {
|
|
1472
|
+
await execFile("xattr", ["-d", "-s", name, absPath]);
|
|
1473
|
+
return;
|
|
1474
|
+
}
|
|
1475
|
+
await execFile("setfattr", ["--no-dereference", "-x", name, absPath]);
|
|
1476
|
+
} catch (err) {
|
|
1477
|
+
throw xattrErrorFromExec(err);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
function packListxattrNames(names) {
|
|
1481
|
+
const encoded = names.map((n) => Buffer.from(`${n}\0`, "utf8"));
|
|
1482
|
+
const total = encoded.reduce((sum, b) => sum + b.length, 0);
|
|
1483
|
+
const out = new Uint8Array(total);
|
|
1484
|
+
let cursor = 0;
|
|
1485
|
+
for (const b of encoded) {
|
|
1486
|
+
out.set(b, cursor);
|
|
1487
|
+
cursor += b.length;
|
|
1488
|
+
}
|
|
1489
|
+
return out;
|
|
1490
|
+
}
|
|
1491
|
+
async function onSetxattr(hdr, msg, state) {
|
|
1492
|
+
if (state.mode === "ro") {
|
|
1493
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EROFS);
|
|
1494
|
+
}
|
|
1495
|
+
const body = payloadOf(msg);
|
|
1496
|
+
const req = readSetxattrIn(body);
|
|
1497
|
+
const after = body.subarray(FUSE_SETXATTR_IN_SIZE);
|
|
1498
|
+
const nulAt = after.indexOf(0);
|
|
1499
|
+
if (nulAt < 0) {
|
|
1500
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1501
|
+
}
|
|
1502
|
+
const name = new TextDecoder("utf-8", { fatal: false }).decode(after.subarray(0, nulAt));
|
|
1503
|
+
if (name === "" || name.length > 255) {
|
|
1504
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1505
|
+
}
|
|
1506
|
+
const valueStart = nulAt + 1;
|
|
1507
|
+
if (valueStart + req.size > after.length) {
|
|
1508
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1509
|
+
}
|
|
1510
|
+
const value = after.subarray(valueStart, valueStart + req.size);
|
|
1511
|
+
const entry = requireInode(state, hdr.nodeid);
|
|
1512
|
+
const abs = await absPathForLstat(state, entry);
|
|
1513
|
+
if (req.flags & XATTR.CREATE && await hostXattrExists(abs, name)) {
|
|
1514
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EEXIST);
|
|
1515
|
+
}
|
|
1516
|
+
if (req.flags & XATTR.REPLACE && !await hostXattrExists(abs, name)) {
|
|
1517
|
+
return buildErrorResponse(hdr.unique, -ERRNO.ENODATA);
|
|
1518
|
+
}
|
|
1519
|
+
try {
|
|
1520
|
+
await hostSetxattr(abs, name, value);
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
if (isXattrError(err)) {
|
|
1523
|
+
return buildErrorResponse(hdr.unique, -err.errno);
|
|
1524
|
+
}
|
|
1525
|
+
throw err;
|
|
1526
|
+
}
|
|
1527
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1528
|
+
}
|
|
1529
|
+
async function onGetxattr(hdr, msg, state) {
|
|
1530
|
+
const body = payloadOf(msg);
|
|
1531
|
+
const req = readGetxattrIn(body);
|
|
1532
|
+
const after = body.subarray(8);
|
|
1533
|
+
const nulAt = after.indexOf(0);
|
|
1534
|
+
if (nulAt < 0) {
|
|
1535
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1536
|
+
}
|
|
1537
|
+
const name = new TextDecoder("utf-8", { fatal: false }).decode(after.subarray(0, nulAt));
|
|
1538
|
+
if (name === "" || name.length > 255) {
|
|
1539
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1540
|
+
}
|
|
1541
|
+
const entry = requireInode(state, hdr.nodeid);
|
|
1542
|
+
const abs = await absPathForLstat(state, entry);
|
|
1543
|
+
let value;
|
|
1544
|
+
try {
|
|
1545
|
+
value = await hostGetxattr(abs, name);
|
|
1546
|
+
} catch (err) {
|
|
1547
|
+
if (isXattrError(err)) {
|
|
1548
|
+
return buildErrorResponse(hdr.unique, -err.errno);
|
|
1549
|
+
}
|
|
1550
|
+
throw err;
|
|
1551
|
+
}
|
|
1552
|
+
if (value === null) {
|
|
1553
|
+
return buildErrorResponse(hdr.unique, -ERRNO.ENODATA);
|
|
1554
|
+
}
|
|
1555
|
+
if (req.size === 0) {
|
|
1556
|
+
return buildResponse(hdr.unique, buildGetxattrOut(value.length));
|
|
1557
|
+
}
|
|
1558
|
+
if (value.length > req.size) {
|
|
1559
|
+
return buildErrorResponse(hdr.unique, -ERRNO.ERANGE);
|
|
1560
|
+
}
|
|
1561
|
+
return buildResponse(hdr.unique, new Uint8Array(value.buffer, value.byteOffset, value.length));
|
|
1562
|
+
}
|
|
1563
|
+
async function onListxattr(hdr, msg, state) {
|
|
1564
|
+
const req = readGetxattrIn(payloadOf(msg));
|
|
1565
|
+
const entry = requireInode(state, hdr.nodeid);
|
|
1566
|
+
const abs = await absPathForLstat(state, entry);
|
|
1567
|
+
let names;
|
|
1568
|
+
try {
|
|
1569
|
+
names = await hostListxattr(abs);
|
|
1570
|
+
} catch (err) {
|
|
1571
|
+
if (isXattrError(err)) {
|
|
1572
|
+
return buildErrorResponse(hdr.unique, -err.errno);
|
|
1573
|
+
}
|
|
1574
|
+
throw err;
|
|
1575
|
+
}
|
|
1576
|
+
const packed = packListxattrNames(names);
|
|
1577
|
+
if (req.size === 0) {
|
|
1578
|
+
return buildResponse(hdr.unique, buildGetxattrOut(packed.length));
|
|
1579
|
+
}
|
|
1580
|
+
if (packed.length > req.size) {
|
|
1581
|
+
return buildErrorResponse(hdr.unique, -ERRNO.ERANGE);
|
|
1582
|
+
}
|
|
1583
|
+
return buildResponse(hdr.unique, packed);
|
|
1584
|
+
}
|
|
1585
|
+
async function onRemovexattr(hdr, msg, state) {
|
|
1586
|
+
if (state.mode === "ro") {
|
|
1587
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EROFS);
|
|
1588
|
+
}
|
|
1589
|
+
const name = decodeName(payloadOf(msg));
|
|
1590
|
+
if (name === "" || name.length > 255) {
|
|
1591
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1592
|
+
}
|
|
1593
|
+
const entry = requireInode(state, hdr.nodeid);
|
|
1594
|
+
const abs = await absPathForLstat(state, entry);
|
|
1595
|
+
try {
|
|
1596
|
+
await hostRemovexattr(abs, name);
|
|
1597
|
+
} catch (err) {
|
|
1598
|
+
if (isXattrError(err)) {
|
|
1599
|
+
return buildErrorResponse(hdr.unique, -err.errno);
|
|
1600
|
+
}
|
|
1601
|
+
throw err;
|
|
1602
|
+
}
|
|
1603
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1604
|
+
}
|
|
1605
|
+
function rangeFromLkIn(lk, owner) {
|
|
1606
|
+
if (lk.type !== F_LCK.RDLCK && lk.type !== F_LCK.WRLCK) {
|
|
1607
|
+
return null;
|
|
1608
|
+
}
|
|
1609
|
+
return { start: lk.start, end: lk.end, type: lk.type, owner, pid: lk.pid };
|
|
1610
|
+
}
|
|
1611
|
+
function rangesOverlap(a, b) {
|
|
1612
|
+
return a.start <= b.end && b.start <= a.end;
|
|
1613
|
+
}
|
|
1614
|
+
function findConflict(state, nodeid, req) {
|
|
1615
|
+
const ranges = state.locks.get(nodeid);
|
|
1616
|
+
if (!ranges) {
|
|
1617
|
+
return null;
|
|
1618
|
+
}
|
|
1619
|
+
for (const r of ranges) {
|
|
1620
|
+
if (r.owner === req.owner) {
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
if (!rangesOverlap(r, req)) {
|
|
1624
|
+
continue;
|
|
1625
|
+
}
|
|
1626
|
+
if (req.type === F_LCK.WRLCK || r.type === F_LCK.WRLCK) {
|
|
1627
|
+
return r;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
return null;
|
|
1631
|
+
}
|
|
1632
|
+
function addLock(state, nodeid, req) {
|
|
1633
|
+
let ranges = state.locks.get(nodeid);
|
|
1634
|
+
if (!ranges) {
|
|
1635
|
+
ranges = [];
|
|
1636
|
+
state.locks.set(nodeid, ranges);
|
|
1637
|
+
}
|
|
1638
|
+
let merged = { ...req };
|
|
1639
|
+
const kept = [];
|
|
1640
|
+
for (const r of ranges) {
|
|
1641
|
+
if (r.owner !== merged.owner || !rangesOverlap(r, merged)) {
|
|
1642
|
+
kept.push(r);
|
|
1643
|
+
continue;
|
|
1644
|
+
}
|
|
1645
|
+
if (r.type === merged.type) {
|
|
1646
|
+
const start = r.start < merged.start ? r.start : merged.start;
|
|
1647
|
+
const end = r.end > merged.end ? r.end : merged.end;
|
|
1648
|
+
merged = { ...merged, start, end };
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
kept.push(merged);
|
|
1652
|
+
state.locks.set(nodeid, kept);
|
|
1653
|
+
}
|
|
1654
|
+
function removeLockRange(state, nodeid, owner, req) {
|
|
1655
|
+
const ranges = state.locks.get(nodeid);
|
|
1656
|
+
if (!ranges) {
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
const out = [];
|
|
1660
|
+
for (const r of ranges) {
|
|
1661
|
+
if (r.owner !== owner || !rangesOverlap(r, req)) {
|
|
1662
|
+
out.push(r);
|
|
1663
|
+
continue;
|
|
1664
|
+
}
|
|
1665
|
+
if (r.start < req.start) {
|
|
1666
|
+
out.push({ ...r, end: req.start - 1n });
|
|
1667
|
+
}
|
|
1668
|
+
if (r.end > req.end) {
|
|
1669
|
+
out.push({ ...r, start: req.end + 1n });
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
if (out.length === 0) {
|
|
1673
|
+
state.locks.delete(nodeid);
|
|
1674
|
+
} else {
|
|
1675
|
+
state.locks.set(nodeid, out);
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
function dropLocksFor(state, nodeid, owner) {
|
|
1679
|
+
const ranges = state.locks.get(nodeid);
|
|
1680
|
+
if (!ranges) {
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
const out = ranges.filter((r) => r.owner !== owner);
|
|
1684
|
+
if (out.length === 0) {
|
|
1685
|
+
state.locks.delete(nodeid);
|
|
1686
|
+
} else {
|
|
1687
|
+
state.locks.set(nodeid, out);
|
|
1688
|
+
}
|
|
1689
|
+
wakeWaiters(state);
|
|
1690
|
+
}
|
|
1691
|
+
function wakeWaiters(state) {
|
|
1692
|
+
for (; ; ) {
|
|
1693
|
+
const idx = state.lockWaiters.findIndex(
|
|
1694
|
+
(w2) => findConflict(state, w2.nodeid, w2.request) === null
|
|
1695
|
+
);
|
|
1696
|
+
if (idx < 0) {
|
|
1697
|
+
return;
|
|
1698
|
+
}
|
|
1699
|
+
const [w] = state.lockWaiters.splice(idx, 1);
|
|
1700
|
+
if (!w) {
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
addLock(state, w.nodeid, w.request);
|
|
1704
|
+
w.resolve();
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
function cancelAllWaiters(state) {
|
|
1708
|
+
const waiters = state.lockWaiters;
|
|
1709
|
+
state.lockWaiters = [];
|
|
1710
|
+
for (const w of waiters) {
|
|
1711
|
+
w.cancel();
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
function onGetlk(hdr, msg, state) {
|
|
1715
|
+
const req = readLkIn(payloadOf(msg));
|
|
1716
|
+
const probe = rangeFromLkIn(req.lk, req.owner);
|
|
1717
|
+
if (!probe) {
|
|
1718
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1719
|
+
}
|
|
1720
|
+
const conflict = findConflict(state, hdr.nodeid, probe);
|
|
1721
|
+
if (!conflict) {
|
|
1722
|
+
return buildResponse(
|
|
1723
|
+
hdr.unique,
|
|
1724
|
+
buildLkOut({ start: req.lk.start, end: req.lk.end, type: F_LCK.UNLCK, pid: 0 })
|
|
1725
|
+
);
|
|
1726
|
+
}
|
|
1727
|
+
return buildResponse(
|
|
1728
|
+
hdr.unique,
|
|
1729
|
+
buildLkOut({
|
|
1730
|
+
start: conflict.start,
|
|
1731
|
+
end: conflict.end,
|
|
1732
|
+
type: conflict.type,
|
|
1733
|
+
pid: conflict.pid
|
|
1734
|
+
})
|
|
1735
|
+
);
|
|
1736
|
+
}
|
|
1737
|
+
function onSetlk(hdr, msg, state) {
|
|
1738
|
+
const req = readLkIn(payloadOf(msg));
|
|
1739
|
+
if (req.lk.type === F_LCK.UNLCK) {
|
|
1740
|
+
removeLockRange(state, hdr.nodeid, req.owner, { start: req.lk.start, end: req.lk.end });
|
|
1741
|
+
wakeWaiters(state);
|
|
1742
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1743
|
+
}
|
|
1744
|
+
const want = rangeFromLkIn(req.lk, req.owner);
|
|
1745
|
+
if (!want) {
|
|
1746
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1747
|
+
}
|
|
1748
|
+
if (findConflict(state, hdr.nodeid, want)) {
|
|
1749
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EAGAIN);
|
|
1750
|
+
}
|
|
1751
|
+
addLock(state, hdr.nodeid, want);
|
|
1752
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1753
|
+
}
|
|
1754
|
+
async function onSetlkw(hdr, msg, state) {
|
|
1755
|
+
const req = readLkIn(payloadOf(msg));
|
|
1756
|
+
if (req.lk.type === F_LCK.UNLCK) {
|
|
1757
|
+
removeLockRange(state, hdr.nodeid, req.owner, { start: req.lk.start, end: req.lk.end });
|
|
1758
|
+
wakeWaiters(state);
|
|
1759
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1760
|
+
}
|
|
1761
|
+
const want = rangeFromLkIn(req.lk, req.owner);
|
|
1762
|
+
if (!want) {
|
|
1763
|
+
return buildErrorResponse(hdr.unique, -ERRNO.EINVAL);
|
|
1764
|
+
}
|
|
1765
|
+
if (!findConflict(state, hdr.nodeid, want)) {
|
|
1766
|
+
addLock(state, hdr.nodeid, want);
|
|
1767
|
+
return buildErrorResponse(hdr.unique, 0);
|
|
1768
|
+
}
|
|
1769
|
+
return await new Promise((done) => {
|
|
1770
|
+
const waiter = {
|
|
1771
|
+
nodeid: hdr.nodeid,
|
|
1772
|
+
request: want,
|
|
1773
|
+
resolve: () => done(buildErrorResponse(hdr.unique, 0)),
|
|
1774
|
+
cancel: () => done(buildErrorResponse(hdr.unique, -ERRNO.EINTR))
|
|
1775
|
+
};
|
|
1776
|
+
state.lockWaiters.push(waiter);
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1058
1779
|
function decodeName(body) {
|
|
1059
1780
|
const nulAt = body.indexOf(0);
|
|
1060
1781
|
const end = nulAt >= 0 ? nulAt : body.length;
|
|
@@ -1215,6 +1936,9 @@ function mapErrorToErrno(err) {
|
|
|
1215
1936
|
if (!code) {
|
|
1216
1937
|
return -ERRNO.EIO;
|
|
1217
1938
|
}
|
|
1939
|
+
if (code === "EOPNOTSUPP") {
|
|
1940
|
+
return -ERRNO.ENOTSUP;
|
|
1941
|
+
}
|
|
1218
1942
|
const key = code;
|
|
1219
1943
|
if (key in ERRNO) {
|
|
1220
1944
|
return -ERRNO[key];
|