@k13engineering/linux-fuse 0.0.2 → 0.0.3

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.
@@ -0,0 +1,13 @@
1
+ type TDirentFileType = "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" | "DT_SOCK" | "DT_UNKNOWN";
2
+ type TDirent = {
3
+ inode: bigint;
4
+ nextOffset: bigint;
5
+ fileType: TDirentFileType;
6
+ name: string;
7
+ };
8
+ declare const direntsToBuffer: ({ entries, maxLength }: {
9
+ entries: TDirent[];
10
+ maxLength: number;
11
+ }) => Uint8Array;
12
+ export { direntsToBuffer };
13
+ export type { TDirent };
@@ -0,0 +1,128 @@
1
+ /*type TDirentFileType = "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" | "DT_SOCK" | "DT_UNKNOWN";*/
2
+
3
+ /*type TDirent = {
4
+ inode: bigint;
5
+ nextOffset: bigint;
6
+ fileType: TDirentFileType;
7
+ name: string;
8
+ };*/
9
+
10
+ /*type TNativeDirent = {
11
+ inode: bigint;
12
+ nextOffset: bigint;
13
+ fileTypeNative: number;
14
+ encodedName: Uint8Array;
15
+ };*/
16
+
17
+ const align = ({ value, alignment }/*: { value: number; alignment: number }*/)/*: number*/ => {
18
+ const remainder = value % alignment;
19
+ if (remainder === 0) {
20
+ return value;
21
+ }
22
+
23
+ return value + (alignment - remainder);
24
+ };
25
+
26
+ // eslint-disable-next-line complexity
27
+ const direntFileTypeToNative = ({ fileType }/*: { fileType: TDirentFileType }*/)/*: number*/ => {
28
+ switch (fileType) {
29
+ case "DT_FIFO": return 1;
30
+ case "DT_CHR": return 2;
31
+ case "DT_DIR": return 4;
32
+ case "DT_BLK": return 6;
33
+ case "DT_REG": return 8;
34
+ case "DT_LNK": return 10;
35
+ case "DT_SOCK": return 12;
36
+ default: return 0;
37
+ }
38
+ };
39
+
40
+ const determineAlignedDirentRecordLength = ({ nameLength }/*: { nameLength: number }*/)/*: number*/ => {
41
+ const headerLength = 24;
42
+ const recordLength = headerLength + nameLength;
43
+ return align({ value: recordLength, alignment: 8 });
44
+ };
45
+
46
+ const textEncoder = new TextEncoder();
47
+ let direntBuffer = new Uint8Array(0);
48
+
49
+ const encodeName = ({ name }/*: { name: string }*/)/*: Uint8Array*/ => {
50
+ if (name.includes("\0")) {
51
+ throw Error("name may not contain null bytes");
52
+ }
53
+
54
+ if (name.includes("/")) {
55
+ throw Error("name may not contain slashes");
56
+ }
57
+
58
+ return textEncoder.encode(name);
59
+ };
60
+
61
+ const direntToNative = ({ entry }/*: { entry: TDirent }*/)/*: TNativeDirent*/ => {
62
+ return {
63
+ inode: entry.inode,
64
+ nextOffset: entry.nextOffset,
65
+ fileTypeNative: direntFileTypeToNative({ fileType: entry.fileType }),
66
+ encodedName: encodeName({ name: entry.name })
67
+ };
68
+ };
69
+
70
+ // eslint-disable-next-line max-statements
71
+ const direntsToBuffer = ({ entries, maxLength }/*: { entries: TDirent[], maxLength: number }*/)/*: Uint8Array*/ => {
72
+
73
+ if (maxLength > direntBuffer.length) {
74
+ direntBuffer = new Uint8Array(maxLength);
75
+ }
76
+
77
+ let offset = 0;
78
+
79
+ for (const entry of entries) {
80
+
81
+ const nativeEntry = direntToNative({ entry });
82
+
83
+ const view = new DataView(direntBuffer.buffer, direntBuffer.byteOffset + offset);
84
+
85
+ const recordLength = determineAlignedDirentRecordLength({ nameLength: nativeEntry.encodedName.length });
86
+
87
+ const newOffset = offset + recordLength;
88
+
89
+ if (newOffset > maxLength) {
90
+ break;
91
+ }
92
+
93
+ // struct fuse_dirent {
94
+
95
+ // uint64_t ino
96
+ view.setBigUint64(0, nativeEntry.inode, true);
97
+
98
+ // uint64_t off
99
+ // this is filesystem-interal offset and not used for offset calculation etc.
100
+ // this parameter will be provided by seek / read offset of readdir
101
+ view.setBigUint64(8, nativeEntry.nextOffset, true);
102
+
103
+ // uint32_t namelen
104
+ view.setUint32(16, nativeEntry.encodedName.length, true);
105
+
106
+ // uint32_t type
107
+ view.setUint32(20, nativeEntry.fileTypeNative, true);
108
+
109
+ // char[] name
110
+ direntBuffer.set(nativeEntry.encodedName, offset + 24);
111
+
112
+ // }
113
+
114
+ direntBuffer.fill(0, offset + 24 + nativeEntry.encodedName.length, newOffset);
115
+
116
+ offset = newOffset;
117
+ };
118
+
119
+ return direntBuffer.slice(0, offset);
120
+ };
121
+
122
+ export {
123
+ direntsToBuffer
124
+ };
125
+
126
+ /*export type {
127
+ TDirent
128
+ };*/
@@ -3,6 +3,9 @@ import { openFuseFd, defaultFusePath } from "./linux.js";
3
3
  import { createConvenienceRequestHandler } from "./convenience.js";
4
4
  import { moveMount, unmount, type TMountAttributes, type TRequiredFuseMountOptions } from "./mount.js";
5
5
  import type { TFuseResponse } from "./protocol.js";
6
+ import type { TFuseServerInterface } from "./convenience.js";
7
+ import type { TDirent } from "./dirent.js";
8
+ import { direntsToBuffer } from "./dirent.js";
6
9
  declare const createFuseFileSystem: ({ fuseFd, requestHandler, sniff, }: {
7
10
  fuseFd: number;
8
11
  requestHandler: TRequestHandler;
@@ -36,5 +39,5 @@ declare const createDefaultInitResult: () => Extract<TFuseResponse, {
36
39
  forOpcode: "INIT";
37
40
  errorCode: undefined;
38
41
  }>["result"];
39
- export { createFuseFileSystem, openFuseFd, defaultFusePath, createConvenienceRequestHandler, moveMount, unmount, createDefaultInitResult };
40
- export type { TRequiredFuseMountOptions, TMountAttributes, };
42
+ export { createFuseFileSystem, openFuseFd, defaultFusePath, createConvenienceRequestHandler, moveMount, unmount, createDefaultInitResult, direntsToBuffer };
43
+ export type { TRequiredFuseMountOptions, TMountAttributes, TFuseServerInterface, TDirent, };
package/dist/lib/index.js CHANGED
@@ -11,9 +11,18 @@ import nodeFs from "node:fs";
11
11
  import { fsconfig, fsmount, fsopen } from "./syscalls.js";
12
12
  import { openFuseFd, defaultFusePath } from "./linux.js";
13
13
  import { createConvenienceRequestHandler } from "./convenience.js";
14
- import { mountAttributesToBitmask, moveMount, unmount, /*type TMountAttributes, *//*type TRequiredFuseMountOptions */} from "./mount.js";
14
+ import {
15
+ mountAttributesToBitmask,
16
+ moveMount,
17
+ unmount,
18
+ /*type TMountAttributes,
19
+ *//*type TRequiredFuseMountOptions
20
+ */} from "./mount.js";
15
21
  import { createPoller } from "@k13engineering/uv-poll";
16
22
  /*import type { TFuseResponse } from "./protocol.ts";*/
23
+ /*import type { TFuseServerInterface } from "./convenience.ts";*/
24
+ /*import type { TDirent } from "./dirent.ts";*/
25
+ import { direntsToBuffer } from "./dirent.js";
17
26
 
18
27
  const UV_EBADF = -9;
19
28
 
@@ -304,10 +313,13 @@ export {
304
313
  createConvenienceRequestHandler,
305
314
  moveMount,
306
315
  unmount,
307
- createDefaultInitResult
316
+ createDefaultInitResult,
317
+ direntsToBuffer
308
318
  };
309
319
 
310
320
  /*export type {
311
321
  TRequiredFuseMountOptions,
312
322
  TMountAttributes,
323
+ TFuseServerInterface,
324
+ TDirent,
313
325
  };*/
@@ -72,16 +72,15 @@ type TFuseRequest = {
72
72
  } | {
73
73
  opcode: "SETATTR";
74
74
  nodeId: bigint;
75
- valid: bigint;
76
- fh: bigint;
77
- size: bigint;
75
+ fh: bigint | undefined;
76
+ size: bigint | undefined;
78
77
  lockOwner: bigint;
79
- atimeNsec: bigint;
80
- mtimeNsec: bigint;
78
+ atimeNsec: bigint | "now" | undefined;
79
+ mtimeNsec: bigint | "now" | undefined;
81
80
  ctimeNsec: bigint;
82
- mode: bigint;
83
- uid: bigint;
84
- gid: bigint;
81
+ mode: bigint | undefined;
82
+ uid: bigint | undefined;
83
+ gid: bigint | undefined;
85
84
  } | {
86
85
  opcode: "READLINK";
87
86
  nodeId: bigint;
@@ -42,7 +42,22 @@ import {
42
42
  fuseWriteInParser,
43
43
  fuseWriteOutParser,
44
44
  } from "./abi.js";
45
- import { FOPEN_DIRECT_IO, FOPEN_KEEP_CACHE, FOPEN_NONSEEKABLE, FUSE_GETATTR_FH, opcode } from "./constants.js";
45
+ import {
46
+ FATTR_ATIME,
47
+ FATTR_ATIME_NOW,
48
+ FATTR_FH,
49
+ FATTR_GID,
50
+ FATTR_MODE,
51
+ FATTR_MTIME,
52
+ FATTR_MTIME_NOW,
53
+ FATTR_SIZE,
54
+ FATTR_UID,
55
+ FOPEN_DIRECT_IO,
56
+ FOPEN_KEEP_CACHE,
57
+ FOPEN_NONSEEKABLE,
58
+ FUSE_GETATTR_FH,
59
+ opcode,
60
+ } from "./constants.js";
46
61
  /*import type { TParsedMessageFromKernel, TParsedMessageToKernel } from "./message.ts";*/
47
62
 
48
63
  /*type TRequesterInfo = {
@@ -123,16 +138,15 @@ import { FOPEN_DIRECT_IO, FOPEN_KEEP_CACHE, FOPEN_NONSEEKABLE, FUSE_GETATTR_FH,
123
138
  } | {
124
139
  opcode: "SETATTR";
125
140
  nodeId: bigint;
126
- valid: bigint;
127
- fh: bigint;
128
- size: bigint;
141
+ fh: bigint | undefined;
142
+ size: bigint | undefined;
129
143
  lockOwner: bigint;
130
- atimeNsec: bigint;
131
- mtimeNsec: bigint;
144
+ atimeNsec: bigint | "now" | undefined;
145
+ mtimeNsec: bigint | "now" | undefined;
132
146
  ctimeNsec: bigint;
133
- mode: bigint;
134
- uid: bigint;
135
- gid: bigint;
147
+ mode: bigint | undefined;
148
+ uid: bigint | undefined;
149
+ gid: bigint | undefined;
136
150
  } | {
137
151
  opcode: "READLINK";
138
152
  nodeId: bigint;
@@ -891,7 +905,7 @@ const interpretAttrPayload = /*<TForOpcode extends "GETATTR" | "SETATTR">*/(
891
905
  )/*: TGenericInterpretResult<Extract<TFuseResponse, { forOpcode: TForOpcode }>>*/ => {
892
906
  if (payload.length !== fuseAttrOutParser.size) {
893
907
  return {
894
- error: new Error(
908
+ error: Error(
895
909
  `Invalid payload size for ${forOpcode} response. Expected ${fuseAttrOutParser.size} bytes, got ${payload.length} bytes.`
896
910
  ),
897
911
  interpreted: undefined,
@@ -947,7 +961,7 @@ const interpretEntryPayload = /*<TForOpcode extends "LOOKUP" | "SYMLINK" | "MKNO
947
961
  )/*: TGenericInterpretResult<Extract<TFuseResponse, { forOpcode: TForOpcode }>>*/ => {
948
962
  if (payload.length !== fuseEntryOutParser.size) {
949
963
  return {
950
- error: new Error(
964
+ error: Error(
951
965
  `Invalid payload size for ${forOpcode} response. Expected ${fuseEntryOutParser.size} bytes, got ${payload.length} bytes.`
952
966
  ),
953
967
  interpreted: undefined,
@@ -998,7 +1012,7 @@ const interpretOpenPayload = /*<TForOpcode extends "OPEN" | "OPENDIR">*/(
998
1012
  )/*: TGenericInterpretResult<Extract<TFuseResponse, { forOpcode: TForOpcode }>>*/ => {
999
1013
  if (payload.length !== fuseOpenOutParser.size) {
1000
1014
  return {
1001
- error: new Error(
1015
+ error: Error(
1002
1016
  `Invalid payload size for ${forOpcode} response. Expected ${fuseOpenOutParser.size} bytes, got ${payload.length} bytes.`
1003
1017
  ),
1004
1018
  interpreted: undefined,
@@ -1066,7 +1080,7 @@ const interpretXAttrPayload = /*<TForOpcode extends "GETXATTR" | "LISTXATTR">*/(
1066
1080
  if (requestedSize === 0n) {
1067
1081
  if (payload.length !== fuseGetxattrOutParser.size) {
1068
1082
  return {
1069
- error: new Error(
1083
+ error: Error(
1070
1084
  `Invalid payload size for ${forOpcode} size response. Expected ${fuseGetxattrOutParser.size} bytes, got ${payload.length} bytes.`
1071
1085
  ),
1072
1086
  interpreted: undefined,
@@ -1107,7 +1121,7 @@ const createMessageFromKernelInterpreter = () => {
1107
1121
 
1108
1122
  if (payload.length !== fuseInitInParser.size) {
1109
1123
  return {
1110
- error: new Error(`Invalid payload size for INIT request. Expected ${fuseInitInParser.size} bytes, got ${payload.length} bytes.`),
1124
+ error: Error(`Invalid payload size for INIT request. Expected ${fuseInitInParser.size} bytes, got ${payload.length} bytes.`),
1111
1125
  interpreted: undefined,
1112
1126
  };
1113
1127
  }
@@ -1129,7 +1143,7 @@ const createMessageFromKernelInterpreter = () => {
1129
1143
  const interpretGetAttr/*: TInterpretFunc<"GETATTR">*/ = ({ unique, nodeId, requester, payload }) => {
1130
1144
  if (payload.length !== fuseGetattrInParser.size) {
1131
1145
  return {
1132
- error: new Error(
1146
+ error: Error(
1133
1147
  `Invalid payload size for GETATTR request. Expected ${fuseGetattrInParser.size} bytes, got ${payload.length} bytes.`
1134
1148
  ),
1135
1149
  interpreted: undefined,
@@ -1163,24 +1177,10 @@ const createMessageFromKernelInterpreter = () => {
1163
1177
  };
1164
1178
  };
1165
1179
 
1166
- // eslint-disable-next-line @typescript-eslint/no-unused-vars, no-underscore-dangle
1167
- const _interpretListXAttr/*: TInterpretFunc<"LISTXATTR">*/ = ({ unique, nodeId, requester }) => {
1168
- return {
1169
- error: undefined,
1170
- interpreted: {
1171
- unique,
1172
- requester,
1173
- opcode: "LISTXATTR",
1174
- nodeId,
1175
- size: 0n,
1176
- }
1177
- };
1178
- };
1179
-
1180
1180
  const interpretForget/*: TInterpretFunc<"FORGET">*/ = ({ unique, nodeId, requester, payload }) => {
1181
1181
  if (payload.length !== fuseForgetInParser.size) {
1182
1182
  return {
1183
- error: new Error(
1183
+ error: Error(
1184
1184
  `Invalid payload size for FORGET request. Expected ${fuseForgetInParser.size} bytes, got ${payload.length} bytes.`
1185
1185
  ),
1186
1186
  interpreted: undefined,
@@ -1204,7 +1204,7 @@ const createMessageFromKernelInterpreter = () => {
1204
1204
  const interpretBatchForget/*: TInterpretFunc<"BATCH_FORGET">*/ = ({ unique, requester, payload }) => {
1205
1205
  if (payload.length < fuseBatchForgetInParser.size) {
1206
1206
  return {
1207
- error: new Error(
1207
+ error: Error(
1208
1208
  `Invalid payload size for BATCH_FORGET request. ` +
1209
1209
  `Expected at least ${fuseBatchForgetInParser.size} bytes, got ${payload.length} bytes.`
1210
1210
  ),
@@ -1217,7 +1217,7 @@ const createMessageFromKernelInterpreter = () => {
1217
1217
  const expectedSize = fuseBatchForgetInParser.size + (count * fuseForgetOneParser.size);
1218
1218
  if (payload.length !== expectedSize) {
1219
1219
  return {
1220
- error: new Error(`Invalid payload size for BATCH_FORGET request. Expected ${expectedSize} bytes, got ${payload.length} bytes.`),
1220
+ error: Error(`Invalid payload size for BATCH_FORGET request. Expected ${expectedSize} bytes, got ${payload.length} bytes.`),
1221
1221
  interpreted: undefined,
1222
1222
  };
1223
1223
  }
@@ -1246,7 +1246,7 @@ const createMessageFromKernelInterpreter = () => {
1246
1246
  const interpretListXAttrRequest/*: TInterpretFunc<"LISTXATTR">*/ = ({ unique, nodeId, requester, payload }) => {
1247
1247
  if (payload.length !== fuseGetxattrInParser.size) {
1248
1248
  return {
1249
- error: new Error(
1249
+ error: Error(
1250
1250
  `Invalid payload size for LISTXATTR request. Expected ${fuseGetxattrInParser.size} bytes, got ${payload.length} bytes.`
1251
1251
  ),
1252
1252
  interpreted: undefined,
@@ -1270,7 +1270,7 @@ const createMessageFromKernelInterpreter = () => {
1270
1270
  const interpretGetXAttr/*: TInterpretFunc<"GETXATTR">*/ = ({ unique, nodeId, requester, payload }) => {
1271
1271
  if (payload.length < fuseGetxattrInParser.size) {
1272
1272
  return {
1273
- error: new Error(
1273
+ error: Error(
1274
1274
  `Invalid payload size for GETXATTR request. Expected at least ${fuseGetxattrInParser.size} bytes, got ${payload.length} bytes.`
1275
1275
  ),
1276
1276
  interpreted: undefined,
@@ -1295,7 +1295,7 @@ const createMessageFromKernelInterpreter = () => {
1295
1295
  const interpretSetAttr/*: TInterpretFunc<"SETATTR">*/ = ({ unique, nodeId, requester, payload }) => {
1296
1296
  if (payload.length !== fuseSetattrInParser.size) {
1297
1297
  return {
1298
- error: new Error(
1298
+ error: Error(
1299
1299
  `Invalid payload size for SETATTR request. Expected ${fuseSetattrInParser.size} bytes, got ${payload.length} bytes.`
1300
1300
  ),
1301
1301
  interpreted: undefined,
@@ -1303,6 +1303,21 @@ const createMessageFromKernelInterpreter = () => {
1303
1303
  }
1304
1304
 
1305
1305
  const parsed = fuseSetattrInParser.parse({ data: payload });
1306
+ const validFlags = Number(parsed.valid);
1307
+
1308
+ let atimeNsec/*: bigint | "now" | undefined*/ = undefined;
1309
+ if ((validFlags & FATTR_ATIME_NOW) !== 0) {
1310
+ atimeNsec = "now";
1311
+ } else if ((validFlags & FATTR_ATIME) !== 0) {
1312
+ atimeNsec = parsed.atime * 1_000_000_000n + parsed.atimensec;
1313
+ }
1314
+
1315
+ let mtimeNsec/*: bigint | "now" | undefined*/ = undefined;
1316
+ if ((validFlags & FATTR_MTIME_NOW) !== 0) {
1317
+ mtimeNsec = "now";
1318
+ } else if ((validFlags & FATTR_MTIME) !== 0) {
1319
+ mtimeNsec = parsed.mtime * 1_000_000_000n + parsed.mtimensec;
1320
+ }
1306
1321
 
1307
1322
  return {
1308
1323
  error: undefined,
@@ -1311,16 +1326,15 @@ const createMessageFromKernelInterpreter = () => {
1311
1326
  requester,
1312
1327
  opcode: "SETATTR",
1313
1328
  nodeId,
1314
- valid: parsed.valid,
1315
- fh: parsed.fh,
1316
- size: parsed.size,
1329
+ fh: (validFlags & FATTR_FH) === 0 ? undefined : parsed.fh,
1330
+ size: (validFlags & FATTR_SIZE) === 0 ? undefined : parsed.size,
1317
1331
  lockOwner: parsed.lock_owner,
1318
- atimeNsec: parsed.atime * 1_000_000_000n + parsed.atimensec,
1319
- mtimeNsec: parsed.mtime * 1_000_000_000n + parsed.mtimensec,
1332
+ atimeNsec,
1333
+ mtimeNsec,
1320
1334
  ctimeNsec: parsed.ctime * 1_000_000_000n + parsed.ctimensec,
1321
- mode: parsed.mode,
1322
- uid: parsed.uid,
1323
- gid: parsed.gid,
1335
+ mode: (validFlags & FATTR_MODE) === 0 ? undefined : parsed.mode,
1336
+ uid: (validFlags & FATTR_UID) === 0 ? undefined : parsed.uid,
1337
+ gid: (validFlags & FATTR_GID) === 0 ? undefined : parsed.gid,
1324
1338
  }
1325
1339
  };
1326
1340
  };
@@ -1356,7 +1370,7 @@ const createMessageFromKernelInterpreter = () => {
1356
1370
  const interpretMknod/*: TInterpretFunc<"MKNOD">*/ = ({ unique, nodeId, requester, payload }) => {
1357
1371
  if (payload.length < fuseMknodInParser.size) {
1358
1372
  return {
1359
- error: new Error(
1373
+ error: Error(
1360
1374
  `Invalid payload size for MKNOD request. Expected at least ${fuseMknodInParser.size} bytes, got ${payload.length} bytes.`
1361
1375
  ),
1362
1376
  interpreted: undefined,
@@ -1383,7 +1397,7 @@ const createMessageFromKernelInterpreter = () => {
1383
1397
  const interpretMkdir/*: TInterpretFunc<"MKDIR">*/ = ({ unique, nodeId, requester, payload }) => {
1384
1398
  if (payload.length < fuseMkdirInParser.size) {
1385
1399
  return {
1386
- error: new Error(
1400
+ error: Error(
1387
1401
  `Invalid payload size for MKDIR request. Expected at least ${fuseMkdirInParser.size} bytes, got ${payload.length} bytes.`
1388
1402
  ),
1389
1403
  interpreted: undefined,
@@ -1439,7 +1453,7 @@ const createMessageFromKernelInterpreter = () => {
1439
1453
  const headerSize = supportsFlags ? fuseRename2InParser.size : fuseRenameInParser.size;
1440
1454
  if (payload.length < headerSize) {
1441
1455
  return {
1442
- error: new Error(
1456
+ error: Error(
1443
1457
  `Invalid payload size for ${supportsFlags ? "RENAME2" : "RENAME"} request. ` +
1444
1458
  `Expected at least ${headerSize} bytes, got ${payload.length} bytes.`
1445
1459
  ),
@@ -1470,7 +1484,7 @@ const createMessageFromKernelInterpreter = () => {
1470
1484
  const interpretOpenDir/*: TInterpretFunc<"OPENDIR">*/ = ({ unique, nodeId, requester, payload }) => {
1471
1485
  if (payload.length !== fuseOpenInParser.size) {
1472
1486
  return {
1473
- error: new Error(`Invalid payload size for OPENDIR request. Expected ${fuseOpenInParser.size} bytes, got ${payload.length} bytes.`),
1487
+ error: Error(`Invalid payload size for OPENDIR request. Expected ${fuseOpenInParser.size} bytes, got ${payload.length} bytes.`),
1474
1488
  interpreted: undefined,
1475
1489
  };
1476
1490
  }
@@ -1492,7 +1506,7 @@ const createMessageFromKernelInterpreter = () => {
1492
1506
  const interpretLink/*: TInterpretFunc<"LINK">*/ = ({ unique, nodeId, requester, payload }) => {
1493
1507
  if (payload.length < fuseLinkInParser.size) {
1494
1508
  return {
1495
- error: new Error(
1509
+ error: Error(
1496
1510
  `Invalid payload size for LINK request. Expected at least ${fuseLinkInParser.size} bytes, got ${payload.length} bytes.`
1497
1511
  ),
1498
1512
  interpreted: undefined,
@@ -1517,7 +1531,7 @@ const createMessageFromKernelInterpreter = () => {
1517
1531
  const interpretReadDir/*: TInterpretFunc<"READDIR">*/ = ({ unique, nodeId, requester, payload }) => {
1518
1532
  if (payload.length !== fuseReadInParser.size) {
1519
1533
  return {
1520
- error: new Error(`Invalid payload size for READDIR request. Expected ${fuseReadInParser.size} bytes, got ${payload.length} bytes.`),
1534
+ error: Error(`Invalid payload size for READDIR request. Expected ${fuseReadInParser.size} bytes, got ${payload.length} bytes.`),
1521
1535
  interpreted: undefined,
1522
1536
  };
1523
1537
  }
@@ -1544,7 +1558,7 @@ const createMessageFromKernelInterpreter = () => {
1544
1558
  const interpretReleaseDir/*: TInterpretFunc<"RELEASEDIR">*/ = ({ unique, nodeId, requester, payload }) => {
1545
1559
  if (payload.length !== fuseReleaseInParser.size) {
1546
1560
  return {
1547
- error: new Error(
1561
+ error: Error(
1548
1562
  `Invalid payload size for RELEASEDIR request. Expected ${fuseReleaseInParser.size} bytes, got ${payload.length} bytes.`
1549
1563
  ),
1550
1564
  interpreted: undefined,
@@ -1571,7 +1585,7 @@ const createMessageFromKernelInterpreter = () => {
1571
1585
  const interpretRelease/*: TInterpretFunc<"RELEASE">*/ = ({ unique, nodeId, requester, payload }) => {
1572
1586
  if (payload.length !== fuseReleaseInParser.size) {
1573
1587
  return {
1574
- error: new Error(
1588
+ error: Error(
1575
1589
  `Invalid payload size for RELEASE request. Expected ${fuseReleaseInParser.size} bytes, got ${payload.length} bytes.`
1576
1590
  ),
1577
1591
  interpreted: undefined,
@@ -1624,7 +1638,7 @@ const createMessageFromKernelInterpreter = () => {
1624
1638
  const interpretOpen/*: TInterpretFunc<"OPEN">*/ = ({ unique, nodeId, requester, payload }) => {
1625
1639
  if (payload.length !== fuseOpenInParser.size) {
1626
1640
  return {
1627
- error: new Error(`Invalid payload size for OPEN request. Expected ${fuseOpenInParser.size} bytes, got ${payload.length} bytes.`),
1641
+ error: Error(`Invalid payload size for OPEN request. Expected ${fuseOpenInParser.size} bytes, got ${payload.length} bytes.`),
1628
1642
  interpreted: undefined,
1629
1643
  };
1630
1644
  }
@@ -1646,7 +1660,7 @@ const createMessageFromKernelInterpreter = () => {
1646
1660
  const interpretRead/*: TInterpretFunc<"READ">*/ = ({ unique, nodeId, requester, payload }) => {
1647
1661
  if (payload.length !== fuseReadInParser.size) {
1648
1662
  return {
1649
- error: new Error(`Invalid payload size for READ request. Expected ${fuseReadInParser.size} bytes, got ${payload.length} bytes.`),
1663
+ error: Error(`Invalid payload size for READ request. Expected ${fuseReadInParser.size} bytes, got ${payload.length} bytes.`),
1650
1664
  interpreted: undefined,
1651
1665
  };
1652
1666
  }
@@ -1672,7 +1686,7 @@ const createMessageFromKernelInterpreter = () => {
1672
1686
  const interpretWrite/*: TInterpretFunc<"WRITE">*/ = ({ unique, nodeId, requester, payload }) => {
1673
1687
  if (payload.length < fuseWriteInParser.size) {
1674
1688
  return {
1675
- error: new Error(
1689
+ error: Error(
1676
1690
  `Invalid payload size for WRITE request. Expected at least ${fuseWriteInParser.size} bytes, got ${payload.length} bytes.`
1677
1691
  ),
1678
1692
  interpreted: undefined,
@@ -1701,7 +1715,7 @@ const createMessageFromKernelInterpreter = () => {
1701
1715
  const interpretFlush/*: TInterpretFunc<"FLUSH">*/ = ({ unique, nodeId, requester, payload }) => {
1702
1716
  if (payload.length !== fuseFlushInParser.size) {
1703
1717
  return {
1704
- error: new Error(`Invalid payload size for FLUSH request. Expected ${fuseFlushInParser.size} bytes, got ${payload.length} bytes.`),
1718
+ error: Error(`Invalid payload size for FLUSH request. Expected ${fuseFlushInParser.size} bytes, got ${payload.length} bytes.`),
1705
1719
  interpreted: undefined,
1706
1720
  };
1707
1721
  }
@@ -1727,7 +1741,7 @@ const createMessageFromKernelInterpreter = () => {
1727
1741
  )/*: TGenericInterpretResult<Extract<TFuseRequest, { opcode: typeof forOpcode }>>*/ => {
1728
1742
  if (payload.length !== fuseFsyncInParser.size) {
1729
1743
  return {
1730
- error: new Error(
1744
+ error: Error(
1731
1745
  `Invalid payload size for ${forOpcode} request. Expected ${fuseFsyncInParser.size} bytes, got ${payload.length} bytes.`
1732
1746
  ),
1733
1747
  interpreted: undefined,
@@ -1752,7 +1766,7 @@ const createMessageFromKernelInterpreter = () => {
1752
1766
  const interpretAccess/*: TInterpretFunc<"ACCESS">*/ = ({ unique, nodeId, requester, payload }) => {
1753
1767
  if (payload.length !== fuseAccessInParser.size) {
1754
1768
  return {
1755
- error: new Error(
1769
+ error: Error(
1756
1770
  `Invalid payload size for ACCESS request. Expected ${fuseAccessInParser.size} bytes, got ${payload.length} bytes.`
1757
1771
  ),
1758
1772
  interpreted: undefined,
@@ -1776,7 +1790,7 @@ const createMessageFromKernelInterpreter = () => {
1776
1790
  const interpretSetXAttr/*: TInterpretFunc<"SETXATTR">*/ = ({ unique, nodeId, requester, payload }) => {
1777
1791
  if (payload.length < fuseSetxattrInParser.size) {
1778
1792
  return {
1779
- error: new Error(
1793
+ error: Error(
1780
1794
  `Invalid payload size for SETXATTR request. Expected at least ${fuseSetxattrInParser.size} bytes, got ${payload.length} bytes.`
1781
1795
  ),
1782
1796
  interpreted: undefined,
@@ -1788,7 +1802,7 @@ const createMessageFromKernelInterpreter = () => {
1788
1802
  const nameTerminator = trailing.indexOf(0);
1789
1803
  if (nameTerminator === -1) {
1790
1804
  return {
1791
- error: new Error("Invalid payload for SETXATTR request. Expected a nul-terminated attribute name."),
1805
+ error: Error("Invalid payload for SETXATTR request. Expected a nul-terminated attribute name."),
1792
1806
  interpreted: undefined,
1793
1807
  };
1794
1808
  }
@@ -1797,7 +1811,7 @@ const createMessageFromKernelInterpreter = () => {
1797
1811
  const data = trailing.subarray(nameTerminator + 1);
1798
1812
  if (BigInt(data.length) !== size) {
1799
1813
  return {
1800
- error: new Error(`Invalid payload size for SETXATTR request data. Expected ${size} bytes, got ${data.length} bytes.`),
1814
+ error: Error(`Invalid payload size for SETXATTR request data. Expected ${size} bytes, got ${data.length} bytes.`),
1801
1815
  interpreted: undefined,
1802
1816
  };
1803
1817
  }
@@ -1833,7 +1847,7 @@ const createMessageFromKernelInterpreter = () => {
1833
1847
  const interpretCreate/*: TInterpretFunc<"CREATE">*/ = ({ unique, nodeId, requester, payload }) => {
1834
1848
  if (payload.length < fuseCreateInParser.size) {
1835
1849
  return {
1836
- error: new Error(
1850
+ error: Error(
1837
1851
  `Invalid payload size for CREATE request. Expected at least ${fuseCreateInParser.size} bytes, got ${payload.length} bytes.`
1838
1852
  ),
1839
1853
  interpreted: undefined,
@@ -1874,7 +1888,7 @@ const createMessageFromKernelInterpreter = () => {
1874
1888
  )/*: TGenericInterpretResult<Extract<TFuseRequest, { opcode: typeof forOpcode }>>*/ => {
1875
1889
  if (payload.length !== fuseLkInParser.size) {
1876
1890
  return {
1877
- error: new Error(
1891
+ error: Error(
1878
1892
  `Invalid payload size for ${forOpcode} request. Expected ${fuseLkInParser.size} bytes, got ${payload.length} bytes.`
1879
1893
  ),
1880
1894
  interpreted: undefined,
@@ -1904,7 +1918,7 @@ const createMessageFromKernelInterpreter = () => {
1904
1918
  const interpretInterrupt/*: TInterpretFunc<"INTERRUPT">*/ = ({ unique, requester, payload }) => {
1905
1919
  if (payload.length !== fuseInterruptInParser.size) {
1906
1920
  return {
1907
- error: new Error(
1921
+ error: Error(
1908
1922
  `Invalid payload size for INTERRUPT request. Expected ${fuseInterruptInParser.size} bytes, got ${payload.length} bytes.`
1909
1923
  ),
1910
1924
  interpreted: undefined,
@@ -1927,7 +1941,7 @@ const createMessageFromKernelInterpreter = () => {
1927
1941
  const interpretBmap/*: TInterpretFunc<"BMAP">*/ = ({ unique, nodeId, requester, payload }) => {
1928
1942
  if (payload.length !== fuseBmapInParser.size) {
1929
1943
  return {
1930
- error: new Error(`Invalid payload size for BMAP request. Expected ${fuseBmapInParser.size} bytes, got ${payload.length} bytes.`),
1944
+ error: Error(`Invalid payload size for BMAP request. Expected ${fuseBmapInParser.size} bytes, got ${payload.length} bytes.`),
1931
1945
  interpreted: undefined,
1932
1946
  };
1933
1947
  }
@@ -1950,7 +1964,7 @@ const createMessageFromKernelInterpreter = () => {
1950
1964
  const interpretIoctl/*: TInterpretFunc<"IOCTL">*/ = ({ unique, nodeId, requester, payload }) => {
1951
1965
  if (payload.length < fuseIoctlInParser.size) {
1952
1966
  return {
1953
- error: new Error(
1967
+ error: Error(
1954
1968
  `Invalid payload size for IOCTL request. Expected at least ${fuseIoctlInParser.size} bytes, got ${payload.length} bytes.`
1955
1969
  ),
1956
1970
  interpreted: undefined,
@@ -1979,7 +1993,7 @@ const createMessageFromKernelInterpreter = () => {
1979
1993
  const interpretPoll/*: TInterpretFunc<"POLL">*/ = ({ unique, nodeId, requester, payload }) => {
1980
1994
  if (payload.length !== fusePollInParser.size) {
1981
1995
  return {
1982
- error: new Error(`Invalid payload size for POLL request. Expected ${fusePollInParser.size} bytes, got ${payload.length} bytes.`),
1996
+ error: Error(`Invalid payload size for POLL request. Expected ${fusePollInParser.size} bytes, got ${payload.length} bytes.`),
1983
1997
  interpreted: undefined,
1984
1998
  };
1985
1999
  }
@@ -2016,7 +2030,7 @@ const createMessageFromKernelInterpreter = () => {
2016
2030
  const interpretFallocate/*: TInterpretFunc<"FALLOCATE">*/ = ({ unique, nodeId, requester, payload }) => {
2017
2031
  if (payload.length !== fuseFallocateInParser.size) {
2018
2032
  return {
2019
- error: new Error(
2033
+ error: Error(
2020
2034
  `Invalid payload size for FALLOCATE request. Expected ${fuseFallocateInParser.size} bytes, got ${payload.length} bytes.`
2021
2035
  ),
2022
2036
  interpreted: undefined,
@@ -2043,7 +2057,7 @@ const createMessageFromKernelInterpreter = () => {
2043
2057
  const interpretReadDirPlus/*: TInterpretFunc<"READDIRPLUS">*/ = ({ unique, nodeId, requester, payload }) => {
2044
2058
  if (payload.length !== fuseReadInParser.size) {
2045
2059
  return {
2046
- error: new Error(
2060
+ error: Error(
2047
2061
  `Invalid payload size for READDIRPLUS request. Expected ${fuseReadInParser.size} bytes, got ${payload.length} bytes.`
2048
2062
  ),
2049
2063
  interpreted: undefined,
@@ -2482,7 +2496,7 @@ const createMessageFromKernelInterpreter = () => {
2482
2496
  }
2483
2497
 
2484
2498
  return {
2485
- error: new Error(`Unsupported opcode: ${parsedMessageFromKernel.header.opcode}`),
2499
+ error: Error(`Unsupported opcode: ${parsedMessageFromKernel.header.opcode}`),
2486
2500
  interpreted: undefined,
2487
2501
  };
2488
2502
  };
@@ -3079,7 +3093,7 @@ const createMessageToKernelInterpreter = () => {
3079
3093
 
3080
3094
  default: {
3081
3095
  // @ts-expect-error exhaustiveness check
3082
- throw new Error(`Unsupported opcode: ${response.forOpcode}`);
3096
+ throw Error(`Unsupported opcode: ${response.forOpcode}`);
3083
3097
  }
3084
3098
  }
3085
3099
  };
@@ -3087,7 +3101,7 @@ const createMessageToKernelInterpreter = () => {
3087
3101
  const interpretInitResponse/*: TInterpretFunc<"INIT">*/ = ({ unique, payload }) => {
3088
3102
  if (payload.length !== fuseInitOutParser.size) {
3089
3103
  return {
3090
- error: new Error(`Invalid payload size for INIT response. Expected ${fuseInitOutParser.size} bytes, got ${payload.length} bytes.`),
3104
+ error: Error(`Invalid payload size for INIT response. Expected ${fuseInitOutParser.size} bytes, got ${payload.length} bytes.`),
3091
3105
  interpreted: undefined,
3092
3106
  };
3093
3107
  }
@@ -3194,7 +3208,7 @@ const createMessageToKernelInterpreter = () => {
3194
3208
  const interpretWriteResponse/*: TInterpretFunc<"WRITE">*/ = ({ unique, payload }) => {
3195
3209
  if (payload.length !== fuseWriteOutParser.size) {
3196
3210
  return {
3197
- error: new Error(
3211
+ error: Error(
3198
3212
  `Invalid payload size for WRITE response. Expected ${fuseWriteOutParser.size} bytes, got ${payload.length} bytes.`
3199
3213
  ),
3200
3214
  interpreted: undefined,
@@ -3284,7 +3298,7 @@ const createMessageToKernelInterpreter = () => {
3284
3298
  const expectedSize = fuseEntryOutParser.size + fuseOpenOutParser.size;
3285
3299
  if (payload.length !== expectedSize) {
3286
3300
  return {
3287
- error: new Error(`Invalid payload size for CREATE response. Expected ${expectedSize} bytes, got ${payload.length} bytes.`),
3301
+ error: Error(`Invalid payload size for CREATE response. Expected ${expectedSize} bytes, got ${payload.length} bytes.`),
3288
3302
  interpreted: undefined,
3289
3303
  };
3290
3304
  }
@@ -3318,7 +3332,7 @@ const createMessageToKernelInterpreter = () => {
3318
3332
  const interpretGetLkResponse/*: TInterpretFunc<"GETLK">*/ = ({ unique, payload }) => {
3319
3333
  if (payload.length !== fuseLkOutParser.size) {
3320
3334
  return {
3321
- error: new Error(`Invalid payload size for GETLK response. Expected ${fuseLkOutParser.size} bytes, got ${payload.length} bytes.`),
3335
+ error: Error(`Invalid payload size for GETLK response. Expected ${fuseLkOutParser.size} bytes, got ${payload.length} bytes.`),
3322
3336
  interpreted: undefined,
3323
3337
  };
3324
3338
  }
@@ -3352,7 +3366,7 @@ const createMessageToKernelInterpreter = () => {
3352
3366
  const interpretBmapResponse/*: TInterpretFunc<"BMAP">*/ = ({ unique, payload }) => {
3353
3367
  if (payload.length !== fuseBmapOutParser.size) {
3354
3368
  return {
3355
- error: new Error(`Invalid payload size for BMAP response. Expected ${fuseBmapOutParser.size} bytes, got ${payload.length} bytes.`),
3369
+ error: Error(`Invalid payload size for BMAP response. Expected ${fuseBmapOutParser.size} bytes, got ${payload.length} bytes.`),
3356
3370
  interpreted: undefined,
3357
3371
  };
3358
3372
  }
@@ -3375,7 +3389,7 @@ const createMessageToKernelInterpreter = () => {
3375
3389
  const interpretIoctlResponse/*: TInterpretFunc<"IOCTL">*/ = ({ unique, payload }) => {
3376
3390
  if (payload.length < fuseIoctlOutParser.size) {
3377
3391
  return {
3378
- error: new Error(
3392
+ error: Error(
3379
3393
  `Invalid payload size for IOCTL response. Expected at least ${fuseIoctlOutParser.size} bytes, got ${payload.length} bytes.`
3380
3394
  ),
3381
3395
  interpreted: undefined,
@@ -3402,7 +3416,7 @@ const createMessageToKernelInterpreter = () => {
3402
3416
  const interpretPollResponse/*: TInterpretFunc<"POLL">*/ = ({ unique, payload }) => {
3403
3417
  if (payload.length !== fusePollOutParser.size) {
3404
3418
  return {
3405
- error: new Error(`Invalid payload size for POLL response. Expected ${fusePollOutParser.size} bytes, got ${payload.length} bytes.`),
3419
+ error: Error(`Invalid payload size for POLL response. Expected ${fusePollOutParser.size} bytes, got ${payload.length} bytes.`),
3406
3420
  interpreted: undefined,
3407
3421
  };
3408
3422
  }
@@ -3443,7 +3457,7 @@ const createMessageToKernelInterpreter = () => {
3443
3457
  const interpretStatFsResponse/*: TInterpretFunc<"STATFS">*/ = ({ unique, payload }) => {
3444
3458
  if (payload.length !== fuseStatfsOutParser.size) {
3445
3459
  return {
3446
- error: new Error(
3460
+ error: Error(
3447
3461
  `Invalid payload size for STATFS response. Expected ${fuseStatfsOutParser.size} bytes, got ${payload.length} bytes.`
3448
3462
  ),
3449
3463
  interpreted: undefined,
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.2",
2
+ "version": "0.0.3",
3
3
  "name": "@k13engineering/linux-fuse",
4
4
  "type": "module",
5
5
  "description": "linux-fuse",
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "devDependencies": {
16
16
  "@eslint/js": "^10.0.1",
17
- "@k13engineering/eslint-rules": "^0.0.3",
17
+ "@k13engineering/eslint-rules": "^0.0.5",
18
18
  "@k13engineering/releasetool": "^0.0.5",
19
19
  "@types/mocha": "^10.0.10",
20
20
  "@types/node": "^25.6.0",