@k13engineering/linux-fuse 0.0.6 → 0.1.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/dist/lib/abi.d.ts +1133 -5
- package/dist/lib/abi.js +705 -535
- package/dist/lib/constants.d.ts +5 -3
- package/dist/lib/constants.js +10 -8
- package/dist/lib/convenience.js +344 -344
- package/dist/lib/dirent.js +24 -8
- package/dist/lib/filesystem.d.ts +41 -0
- package/dist/lib/filesystem.js +344 -0
- package/dist/lib/fuse-lowlevel.d.ts +3 -1
- package/dist/lib/fuse-lowlevel.js +17 -4
- package/dist/lib/index.d.ts +4 -33
- package/dist/lib/index.js +13 -284
- package/dist/lib/kernel.d.ts +9 -0
- package/dist/lib/kernel.js +21 -0
- package/dist/lib/linux-fuse.d.ts +21 -0
- package/dist/lib/linux-fuse.js +38 -0
- package/dist/lib/linux.d.ts +3 -1
- package/dist/lib/linux.js +16 -9
- package/dist/lib/message.js +6 -0
- package/dist/lib/mount.d.ts +5 -2
- package/dist/lib/mount.js +16 -4
- package/dist/lib/protocol.d.ts +6 -0
- package/dist/lib/protocol.js +861 -830
- package/dist/lib/server.js +33 -0
- package/dist/lib/syscalls.d.ts +66 -38
- package/dist/lib/syscalls.js +281 -198
- package/package.json +4 -4
package/dist/lib/dirent.js
CHANGED
|
@@ -26,14 +26,30 @@ const align = ({ value, alignment }/*: { value: number; alignment: number }*/)/*
|
|
|
26
26
|
// eslint-disable-next-line complexity
|
|
27
27
|
const direntFileTypeToNative = ({ fileType }/*: { fileType: TDirentFileType }*/)/*: number*/ => {
|
|
28
28
|
switch (fileType) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
29
|
+
case "DT_FIFO": {
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
case "DT_CHR": {
|
|
33
|
+
return 2;
|
|
34
|
+
}
|
|
35
|
+
case "DT_DIR": {
|
|
36
|
+
return 4;
|
|
37
|
+
}
|
|
38
|
+
case "DT_BLK": {
|
|
39
|
+
return 6;
|
|
40
|
+
}
|
|
41
|
+
case "DT_REG": {
|
|
42
|
+
return 8;
|
|
43
|
+
}
|
|
44
|
+
case "DT_LNK": {
|
|
45
|
+
return 10;
|
|
46
|
+
}
|
|
47
|
+
case "DT_SOCK": {
|
|
48
|
+
return 12;
|
|
49
|
+
}
|
|
50
|
+
default: {
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
37
53
|
}
|
|
38
54
|
};
|
|
39
55
|
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type TRequestHandler } from "./server.js";
|
|
2
|
+
import { type TMountAttributes, type TRequiredFuseMountOptions } from "./mount.js";
|
|
3
|
+
import type { TKernel } from "./kernel.js";
|
|
4
|
+
import type { TSyscalls } from "./syscalls.js";
|
|
5
|
+
type TFuseFileSystemOptions = {
|
|
6
|
+
fuseFd: number;
|
|
7
|
+
requestHandler: TRequestHandler;
|
|
8
|
+
sniff?: {
|
|
9
|
+
rawMessageFromKernel: (args: {
|
|
10
|
+
data: Uint8Array;
|
|
11
|
+
}) => void;
|
|
12
|
+
rawMessageToKernel: (args: {
|
|
13
|
+
data: Uint8Array;
|
|
14
|
+
}) => void;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
type TFsMountResult = {
|
|
18
|
+
error: Error;
|
|
19
|
+
mountFd: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
error: undefined;
|
|
22
|
+
mountFd: number;
|
|
23
|
+
};
|
|
24
|
+
type TMountOptions = {
|
|
25
|
+
otherOptions: {
|
|
26
|
+
default_permissions?: boolean;
|
|
27
|
+
allow_other?: boolean;
|
|
28
|
+
allow_root?: boolean;
|
|
29
|
+
} | Record<string, string | boolean>;
|
|
30
|
+
requiredOptions: TRequiredFuseMountOptions;
|
|
31
|
+
mountAttributes: TMountAttributes;
|
|
32
|
+
};
|
|
33
|
+
declare const createFuseFileSystem: ({ syscalls, createPoller, fuseFd, requestHandler, sniff, }: TFuseFileSystemOptions & {
|
|
34
|
+
syscalls: TSyscalls;
|
|
35
|
+
createPoller: TKernel["createPoller"];
|
|
36
|
+
}) => {
|
|
37
|
+
mountDetached: ({ otherOptions, requiredOptions, mountAttributes, }: TMountOptions) => TFsMountResult;
|
|
38
|
+
close: () => void;
|
|
39
|
+
};
|
|
40
|
+
export { createFuseFileSystem, };
|
|
41
|
+
export type { TFuseFileSystemOptions, };
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { createFuseLowLevel } from "./fuse-lowlevel.js";
|
|
2
|
+
import { createFuseServer, /*type TRequestHandler */} from "./server.js";
|
|
3
|
+
import {
|
|
4
|
+
FSCONFIG_CMD_CREATE,
|
|
5
|
+
FSCONFIG_SET_FLAG,
|
|
6
|
+
FSCONFIG_SET_STRING,
|
|
7
|
+
FSMOUNT_CLOEXEC,
|
|
8
|
+
FSOPEN_CLOEXEC,
|
|
9
|
+
FUSE_MIN_READ_BUFFER,
|
|
10
|
+
} from "./constants.js";
|
|
11
|
+
import {
|
|
12
|
+
mountAttributesToBitmask,
|
|
13
|
+
/*type TMountAttributes,
|
|
14
|
+
*//*type TRequiredFuseMountOptions
|
|
15
|
+
*/} from "./mount.js";
|
|
16
|
+
/*import type { TKernel } from "./kernel.ts";*/
|
|
17
|
+
/*import type { TSyscalls } from "./syscalls.ts";*/
|
|
18
|
+
|
|
19
|
+
const UV_EBADF = -9;
|
|
20
|
+
|
|
21
|
+
const MAX_ALLOWED_WRITE = 32n * 1024n * 1024n;
|
|
22
|
+
|
|
23
|
+
/*type TFuseFileSystemOptions = {
|
|
24
|
+
fuseFd: number;
|
|
25
|
+
requestHandler: TRequestHandler;
|
|
26
|
+
|
|
27
|
+
sniff?: {
|
|
28
|
+
rawMessageFromKernel: (args: { data: Uint8Array }) => void;
|
|
29
|
+
rawMessageToKernel: (args: { data: Uint8Array }) => void;
|
|
30
|
+
}
|
|
31
|
+
};*/
|
|
32
|
+
|
|
33
|
+
/*type TFsMountResult = {
|
|
34
|
+
error: Error;
|
|
35
|
+
mountFd: undefined;
|
|
36
|
+
} | {
|
|
37
|
+
error: undefined;
|
|
38
|
+
mountFd: number;
|
|
39
|
+
};*/
|
|
40
|
+
|
|
41
|
+
/*type TMountOptions = {
|
|
42
|
+
otherOptions: {
|
|
43
|
+
default_permissions?: boolean;
|
|
44
|
+
allow_other?: boolean;
|
|
45
|
+
allow_root?: boolean;
|
|
46
|
+
} | Record<string, string | boolean>;
|
|
47
|
+
requiredOptions: TRequiredFuseMountOptions;
|
|
48
|
+
mountAttributes: TMountAttributes;
|
|
49
|
+
};*/
|
|
50
|
+
|
|
51
|
+
const forbiddenOptions = ["fd", "rootmode", "user_id", "group_id"];
|
|
52
|
+
|
|
53
|
+
const closeFdOrThrow = ({ syscalls, fd }/*: { syscalls: Pick<TSyscalls, "close">; fd: number }*/) => {
|
|
54
|
+
const { errno } = syscalls.close({ fd });
|
|
55
|
+
if (errno !== undefined) {
|
|
56
|
+
throw Error(`close syscall failed with errno ${errno}`);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const createFuseFileSystem = ({
|
|
61
|
+
syscalls,
|
|
62
|
+
createPoller,
|
|
63
|
+
fuseFd,
|
|
64
|
+
requestHandler,
|
|
65
|
+
sniff,
|
|
66
|
+
}/*: TFuseFileSystemOptions & {
|
|
67
|
+
syscalls: TSyscalls;
|
|
68
|
+
createPoller: TKernel["createPoller"];
|
|
69
|
+
}*/) => {
|
|
70
|
+
|
|
71
|
+
let closed = false;
|
|
72
|
+
|
|
73
|
+
let lowLevel = createFuseLowLevel({
|
|
74
|
+
syscalls,
|
|
75
|
+
fuseFd,
|
|
76
|
+
maxMessageSize: 1 * 1024 * 1024 + 4096,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const server = createFuseServer({
|
|
80
|
+
requestHandler: {
|
|
81
|
+
// eslint-disable-next-line complexity
|
|
82
|
+
handleRequest: async ({ request }) => {
|
|
83
|
+
const response = await requestHandler.handleRequest({ request });
|
|
84
|
+
|
|
85
|
+
if (response !== undefined && response.forOpcode === "INIT" && response.errorCode === undefined) {
|
|
86
|
+
|
|
87
|
+
if (response.result.maxWrite >= MAX_ALLOWED_WRITE) {
|
|
88
|
+
throw Error(`maxWrite of ${response.result.maxWrite} is too large, refusing to operate`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
lowLevel = createFuseLowLevel({
|
|
92
|
+
syscalls,
|
|
93
|
+
fuseFd,
|
|
94
|
+
// the kernel rejects reads into buffers smaller than FUSE_MIN_READ_BUFFER
|
|
95
|
+
maxMessageSize: Math.max(FUSE_MIN_READ_BUFFER, Number(response.result.maxWrite) + 4096),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return response;
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
sendMessageToKernel: ({ rawMessageToKernel }) => {
|
|
103
|
+
if (closed) {
|
|
104
|
+
// a request finished after close, its file descriptor number may already be reused
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
sniff?.rawMessageToKernel({ data: rawMessageToKernel });
|
|
109
|
+
lowLevel.writeOutgoingMessage({
|
|
110
|
+
rawMessage: rawMessageToKernel
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
116
|
+
let ended = false;
|
|
117
|
+
|
|
118
|
+
const poller = createPoller({
|
|
119
|
+
fd: fuseFd
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const handlePollError = ({ errorCode }/*: { errorCode: number }*/) => {
|
|
123
|
+
if (errorCode === UV_EBADF) {
|
|
124
|
+
// EBADF is raised when the kernel unmounts the filesystem
|
|
125
|
+
ended = true;
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw Error(`poll error with code ${errorCode}`);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
// reads and processes one message, returns whether the fuse device should be polled further
|
|
133
|
+
// eslint-disable-next-line complexity
|
|
134
|
+
const processIncomingMessage = () => {
|
|
135
|
+
|
|
136
|
+
if (closed) {
|
|
137
|
+
throw Error("BUG: should not be called after close");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const { error: readError, incomingMessage, end } = lowLevel.tryReadIncomingMessage();
|
|
141
|
+
if (readError !== undefined) {
|
|
142
|
+
throw readError;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (end) {
|
|
146
|
+
ended = true;
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (incomingMessage !== undefined) {
|
|
151
|
+
sniff?.rawMessageFromKernel({ data: incomingMessage });
|
|
152
|
+
server.processRequest({
|
|
153
|
+
rawIncomingRequest: incomingMessage
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return true;
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const armPoller = () => {
|
|
161
|
+
poller.armOnce({
|
|
162
|
+
error: ({ errorCode }) => {
|
|
163
|
+
handlePollError({ errorCode });
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
readable: () => {
|
|
167
|
+
if (processIncomingMessage()) {
|
|
168
|
+
armPoller();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
let polling = false;
|
|
175
|
+
|
|
176
|
+
// the kernel reports a fuse device that is not mounted yet as erroneous to poll, which looks the
|
|
177
|
+
// same as an unmount, so polling only starts once the device is connected by mountDetached
|
|
178
|
+
const startPolling = () => {
|
|
179
|
+
if (polling) {
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
polling = true;
|
|
184
|
+
armPoller();
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const configureAndMount = ({
|
|
188
|
+
fsfd,
|
|
189
|
+
otherOptions,
|
|
190
|
+
requiredOptions,
|
|
191
|
+
mountAttributesBitmask,
|
|
192
|
+
}/*: Omit<TMountOptions, "mountAttributes"> & {
|
|
193
|
+
fsfd: number;
|
|
194
|
+
mountAttributesBitmask: bigint;
|
|
195
|
+
}*/)/*: TFsMountResult*/ => {
|
|
196
|
+
|
|
197
|
+
const configureString = ({ key, value }/*: { key: string; value: string }*/) => {
|
|
198
|
+
const { errno } = syscalls.fsconfig({
|
|
199
|
+
fd: fsfd,
|
|
200
|
+
command: FSCONFIG_SET_STRING,
|
|
201
|
+
key,
|
|
202
|
+
value,
|
|
203
|
+
auxiliaryValue: 0,
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (errno !== undefined) {
|
|
207
|
+
throw Error(`fsconfig syscall failed with errno ${errno} when setting ${key}=${value}`);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const configureFlag = ({ key, value }/*: { key: string; value: boolean }*/) => {
|
|
212
|
+
if (!value) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const { errno } = syscalls.fsconfig({
|
|
217
|
+
fd: fsfd,
|
|
218
|
+
command: FSCONFIG_SET_FLAG,
|
|
219
|
+
key,
|
|
220
|
+
value: undefined,
|
|
221
|
+
auxiliaryValue: 0,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
if (errno !== undefined) {
|
|
225
|
+
throw Error(`fsconfig syscall failed with errno ${errno} when setting flag ${key}`);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
Object.keys(otherOptions).forEach((key) => {
|
|
230
|
+
const value = otherOptions[key /*as keyof typeof otherOptions*/];
|
|
231
|
+
|
|
232
|
+
if (value === undefined) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (typeof value === "boolean") {
|
|
237
|
+
configureFlag({ key, value });
|
|
238
|
+
} else {
|
|
239
|
+
configureString({ key, value });
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
configureString({ key: "fd", value: `${fuseFd}` });
|
|
244
|
+
configureString({ key: "rootmode", value: `${requiredOptions.rootmode.toString(8)}` });
|
|
245
|
+
configureString({ key: "user_id", value: `${requiredOptions.user_id}` });
|
|
246
|
+
configureString({ key: "group_id", value: `${requiredOptions.group_id}` });
|
|
247
|
+
|
|
248
|
+
const { errno: createErrno } = syscalls.fsconfig({
|
|
249
|
+
fd: fsfd,
|
|
250
|
+
command: FSCONFIG_CMD_CREATE,
|
|
251
|
+
key: undefined,
|
|
252
|
+
value: undefined,
|
|
253
|
+
auxiliaryValue: 0,
|
|
254
|
+
});
|
|
255
|
+
if (createErrno !== undefined) {
|
|
256
|
+
return {
|
|
257
|
+
error: Error(`fsconfig CMD_CREATE syscall failed with errno ${createErrno}`),
|
|
258
|
+
mountFd: undefined,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const { errno: mountErrno, mountFd } = syscalls.fsmount({
|
|
263
|
+
fd: fsfd,
|
|
264
|
+
flags: FSMOUNT_CLOEXEC,
|
|
265
|
+
mountAttributesBitmask,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
if (mountErrno !== undefined) {
|
|
269
|
+
return {
|
|
270
|
+
error: Error(`fsmount syscall failed with errno ${mountErrno}`),
|
|
271
|
+
mountFd: undefined,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
error: undefined,
|
|
277
|
+
mountFd,
|
|
278
|
+
};
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
const mountDetached = ({
|
|
282
|
+
otherOptions,
|
|
283
|
+
requiredOptions,
|
|
284
|
+
mountAttributes,
|
|
285
|
+
}/*: TMountOptions*/)/*: TFsMountResult*/ => {
|
|
286
|
+
|
|
287
|
+
// validate everything that can be validated before creating the filesystem context
|
|
288
|
+
Object.keys(otherOptions).forEach((key) => {
|
|
289
|
+
if (forbiddenOptions.includes(key)) {
|
|
290
|
+
throw Error(`option ${key} is not allowed as it is required for correct mounting`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
const mountAttributesBitmask = mountAttributesToBitmask({ mountAttributes });
|
|
295
|
+
|
|
296
|
+
const { errno: fsopenErrno, fsfd } = syscalls.fsopen({
|
|
297
|
+
fileSystemName: "fuse",
|
|
298
|
+
flags: FSOPEN_CLOEXEC
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
if (fsopenErrno !== undefined) {
|
|
302
|
+
return {
|
|
303
|
+
error: Error(`fsopen syscall failed with errno ${fsopenErrno}`),
|
|
304
|
+
mountFd: undefined,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const result = configureAndMount({ fsfd, otherOptions, requiredOptions, mountAttributesBitmask });
|
|
310
|
+
if (result.error === undefined) {
|
|
311
|
+
startPolling();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return result;
|
|
315
|
+
} finally {
|
|
316
|
+
// the mount keeps its own reference, so the filesystem context is not needed afterwards
|
|
317
|
+
closeFdOrThrow({ syscalls, fd: fsfd });
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const close = () => {
|
|
322
|
+
if (closed) {
|
|
323
|
+
throw Error("already closed");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
closed = true;
|
|
327
|
+
|
|
328
|
+
poller.close();
|
|
329
|
+
closeFdOrThrow({ syscalls, fd: fuseFd });
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
mountDetached,
|
|
334
|
+
close,
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
export {
|
|
339
|
+
createFuseFileSystem,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
/*export type {
|
|
343
|
+
TFuseFileSystemOptions,
|
|
344
|
+
};*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TSyscalls } from "./syscalls.js";
|
|
1
2
|
type TIncomingRequest = {
|
|
2
3
|
header: {
|
|
3
4
|
opcode: bigint;
|
|
@@ -29,7 +30,8 @@ type TTryReadIncomingMessageResult = {
|
|
|
29
30
|
incomingMessage: undefined;
|
|
30
31
|
end: false | true;
|
|
31
32
|
};
|
|
32
|
-
declare const createFuseLowLevel: ({ fuseFd, maxMessageSize }: {
|
|
33
|
+
declare const createFuseLowLevel: ({ syscalls, fuseFd, maxMessageSize }: {
|
|
34
|
+
syscalls: Pick<TSyscalls, "read" | "write">;
|
|
33
35
|
fuseFd: number;
|
|
34
36
|
maxMessageSize: number;
|
|
35
37
|
}) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
/*import type { TSyscalls } from "./syscalls.ts";*/
|
|
2
2
|
|
|
3
3
|
/*type TIncomingRequest = {
|
|
4
4
|
header: {
|
|
@@ -35,13 +35,16 @@ import { read, write } from "./syscalls.js";
|
|
|
35
35
|
};*/
|
|
36
36
|
|
|
37
37
|
const EPERM = 1;
|
|
38
|
+
const ENOENT = 2;
|
|
38
39
|
const EAGAIN = 11;
|
|
39
40
|
const ENODEV = 19;
|
|
40
41
|
|
|
41
42
|
const createFuseLowLevel = ({
|
|
43
|
+
syscalls,
|
|
42
44
|
fuseFd,
|
|
43
45
|
maxMessageSize
|
|
44
46
|
}/*: {
|
|
47
|
+
syscalls: Pick<TSyscalls, "read" | "write">;
|
|
45
48
|
fuseFd: number;
|
|
46
49
|
maxMessageSize: number;
|
|
47
50
|
}*/) => {
|
|
@@ -51,7 +54,7 @@ const createFuseLowLevel = ({
|
|
|
51
54
|
// eslint-disable-next-line complexity
|
|
52
55
|
const tryReadIncomingMessage = ()/*: TTryReadIncomingMessageResult*/ => {
|
|
53
56
|
|
|
54
|
-
const { errno, ret } = read({
|
|
57
|
+
const { errno, ret } = syscalls.read({
|
|
55
58
|
fd: fuseFd,
|
|
56
59
|
buffer: incomingBuffer
|
|
57
60
|
});
|
|
@@ -89,7 +92,11 @@ const createFuseLowLevel = ({
|
|
|
89
92
|
};
|
|
90
93
|
}
|
|
91
94
|
|
|
92
|
-
|
|
95
|
+
return {
|
|
96
|
+
error: Error(`read syscall failed with errno ${errno}`),
|
|
97
|
+
incomingMessage: undefined,
|
|
98
|
+
end: undefined
|
|
99
|
+
};
|
|
93
100
|
}
|
|
94
101
|
|
|
95
102
|
const incomingMessage = incomingBuffer.slice(0, Number(ret));
|
|
@@ -103,11 +110,17 @@ const createFuseLowLevel = ({
|
|
|
103
110
|
|
|
104
111
|
const writeOutgoingMessage = ({ rawMessage }/*: { rawMessage: Uint8Array }*/) => {
|
|
105
112
|
|
|
106
|
-
const { errno, ret } = write({
|
|
113
|
+
const { errno, ret } = syscalls.write({
|
|
107
114
|
fd: fuseFd,
|
|
108
115
|
buffer: rawMessage
|
|
109
116
|
});
|
|
110
117
|
|
|
118
|
+
if (errno === ENOENT) {
|
|
119
|
+
// the kernel no longer waits for this reply, e.g. because the request was
|
|
120
|
+
// interrupted or the filesystem got unmounted while it was processed
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
111
124
|
if (errno !== undefined) {
|
|
112
125
|
throw Error(`write syscall failed with errno ${errno}`);
|
|
113
126
|
}
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,43 +1,14 @@
|
|
|
1
|
-
import { type TRequestHandler } from "./server.js";
|
|
2
|
-
import { openFuseFd, defaultFusePath } from "./linux.js";
|
|
3
1
|
import { createConvenienceRequestHandler } from "./convenience.js";
|
|
4
|
-
import {
|
|
2
|
+
import { defaultFusePath } from "./linux.js";
|
|
3
|
+
import type { TMountAttributes, TRequiredFuseMountOptions } from "./mount.js";
|
|
5
4
|
import type { TFuseResponse } from "./protocol.js";
|
|
6
5
|
import type { TFuseServerInterface } from "./convenience.js";
|
|
7
6
|
import type { TDirent } from "./dirent.js";
|
|
8
7
|
import { direntsToBuffer } from "./dirent.js";
|
|
9
|
-
declare const createFuseFileSystem:
|
|
10
|
-
fuseFd: number;
|
|
11
|
-
requestHandler: TRequestHandler;
|
|
12
|
-
sniff?: {
|
|
13
|
-
rawMessageFromKernel: (args: {
|
|
14
|
-
data: Uint8Array;
|
|
15
|
-
}) => void;
|
|
16
|
-
rawMessageToKernel: (args: {
|
|
17
|
-
data: Uint8Array;
|
|
18
|
-
}) => void;
|
|
19
|
-
};
|
|
20
|
-
}) => {
|
|
21
|
-
mountDetached: ({ otherOptions, requiredOptions, mountAttributes, }: {
|
|
22
|
-
otherOptions: {
|
|
23
|
-
default_permissions?: boolean;
|
|
24
|
-
allow_other?: boolean;
|
|
25
|
-
allow_root?: boolean;
|
|
26
|
-
} | Record<string, string | boolean>;
|
|
27
|
-
requiredOptions: TRequiredFuseMountOptions;
|
|
28
|
-
mountAttributes: TMountAttributes;
|
|
29
|
-
}) => {
|
|
30
|
-
error: Error;
|
|
31
|
-
mountFd: undefined;
|
|
32
|
-
} | {
|
|
33
|
-
error: undefined;
|
|
34
|
-
mountFd: number;
|
|
35
|
-
};
|
|
36
|
-
close: () => void;
|
|
37
|
-
};
|
|
8
|
+
declare const createFuseFileSystem: any, openFuseFd: any, moveMount: any, unmount: any, fchdir: any;
|
|
38
9
|
declare const createDefaultInitResult: () => Extract<TFuseResponse, {
|
|
39
10
|
forOpcode: "INIT";
|
|
40
11
|
errorCode: undefined;
|
|
41
12
|
}>["result"];
|
|
42
|
-
export { createFuseFileSystem, openFuseFd, defaultFusePath, createConvenienceRequestHandler, moveMount, unmount, createDefaultInitResult, direntsToBuffer };
|
|
13
|
+
export { createFuseFileSystem, openFuseFd, defaultFusePath, createConvenienceRequestHandler, moveMount, unmount, createDefaultInitResult, direntsToBuffer, fchdir, };
|
|
43
14
|
export type { TRequiredFuseMountOptions, TMountAttributes, TFuseServerInterface, TDirent, };
|