@k13engineering/linux-fuse 0.0.1 → 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.
- package/dist/lib/abi.d.ts +331 -0
- package/dist/lib/abi.js +1019 -0
- package/dist/lib/constants.d.ts +225 -0
- package/dist/lib/constants.js +272 -0
- package/dist/lib/convenience.d.ts +63 -0
- package/dist/lib/convenience.js +463 -0
- package/dist/lib/dirent.d.ts +13 -0
- package/dist/lib/dirent.js +128 -0
- package/dist/lib/fuse-lowlevel.d.ts +42 -0
- package/dist/lib/fuse-lowlevel.js +133 -0
- package/dist/lib/index.d.ts +43 -0
- package/dist/lib/index.js +325 -0
- package/dist/lib/linux.d.ts +12 -0
- package/dist/lib/linux.js +32 -0
- package/dist/lib/message.d.ts +50 -0
- package/dist/lib/message.js +177 -0
- package/dist/lib/mount.d.ts +33 -0
- package/dist/lib/mount.js +149 -0
- package/dist/lib/protocol.d.ts +755 -0
- package/dist/lib/protocol.js +3797 -0
- package/dist/lib/server.d.ts +18 -0
- package/dist/lib/server.js +73 -0
- package/dist/lib/syscalls.d.ts +73 -0
- package/dist/lib/syscalls.js +275 -0
- package/package.json +2 -2
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { read, write } from "./syscalls.js";
|
|
2
|
+
|
|
3
|
+
/*type TIncomingRequest = {
|
|
4
|
+
header: {
|
|
5
|
+
opcode: bigint;
|
|
6
|
+
unique: bigint;
|
|
7
|
+
nodeid: bigint;
|
|
8
|
+
uid: bigint;
|
|
9
|
+
gid: bigint;
|
|
10
|
+
pid: bigint;
|
|
11
|
+
},
|
|
12
|
+
payload: Uint8Array
|
|
13
|
+
};*/
|
|
14
|
+
|
|
15
|
+
/*type TOutgoingResponse = {
|
|
16
|
+
header: {
|
|
17
|
+
error: bigint;
|
|
18
|
+
unique: bigint;
|
|
19
|
+
},
|
|
20
|
+
payload: Uint8Array
|
|
21
|
+
};*/
|
|
22
|
+
|
|
23
|
+
/*type TTryReadIncomingMessageResult = {
|
|
24
|
+
error: Error;
|
|
25
|
+
incomingMessage: undefined;
|
|
26
|
+
end: undefined;
|
|
27
|
+
} | {
|
|
28
|
+
error: undefined;
|
|
29
|
+
incomingMessage: Uint8Array;
|
|
30
|
+
end: false;
|
|
31
|
+
} | {
|
|
32
|
+
error: undefined;
|
|
33
|
+
incomingMessage: undefined;
|
|
34
|
+
end: false | true;
|
|
35
|
+
};*/
|
|
36
|
+
|
|
37
|
+
const EPERM = 1;
|
|
38
|
+
const EAGAIN = 11;
|
|
39
|
+
const ENODEV = 19;
|
|
40
|
+
|
|
41
|
+
const createFuseLowLevel = ({
|
|
42
|
+
fuseFd,
|
|
43
|
+
maxMessageSize
|
|
44
|
+
}/*: {
|
|
45
|
+
fuseFd: number;
|
|
46
|
+
maxMessageSize: number;
|
|
47
|
+
}*/) => {
|
|
48
|
+
|
|
49
|
+
const incomingBuffer = new Uint8Array(maxMessageSize);
|
|
50
|
+
|
|
51
|
+
// eslint-disable-next-line complexity
|
|
52
|
+
const tryReadIncomingMessage = ()/*: TTryReadIncomingMessageResult*/ => {
|
|
53
|
+
|
|
54
|
+
const { errno, ret } = read({
|
|
55
|
+
fd: fuseFd,
|
|
56
|
+
buffer: incomingBuffer
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (errno !== undefined) {
|
|
60
|
+
|
|
61
|
+
if (errno === EPERM) {
|
|
62
|
+
|
|
63
|
+
// most likely the filesystem hasn't been mounted yet
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
error: undefined,
|
|
67
|
+
incomingMessage: undefined,
|
|
68
|
+
end: false
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (errno === EAGAIN) {
|
|
73
|
+
// no message to read
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
error: undefined,
|
|
77
|
+
incomingMessage: undefined,
|
|
78
|
+
end: false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (errno === ENODEV) {
|
|
83
|
+
// the filesystem has been unmounted
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
error: undefined,
|
|
87
|
+
incomingMessage: undefined,
|
|
88
|
+
end: true
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw Error(`errno ${errno} not implemented yet`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const incomingMessage = incomingBuffer.slice(0, Number(ret));
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
error: undefined,
|
|
99
|
+
incomingMessage,
|
|
100
|
+
end: false
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const writeOutgoingMessage = ({ rawMessage }/*: { rawMessage: Uint8Array }*/) => {
|
|
105
|
+
|
|
106
|
+
const { errno, ret } = write({
|
|
107
|
+
fd: fuseFd,
|
|
108
|
+
buffer: rawMessage
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (errno !== undefined) {
|
|
112
|
+
throw Error(`write syscall failed with errno ${errno}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (ret !== BigInt(rawMessage.length)) {
|
|
116
|
+
throw Error(`short write not supported yet`);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
tryReadIncomingMessage,
|
|
122
|
+
writeOutgoingMessage,
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export {
|
|
127
|
+
createFuseLowLevel
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/*export type {
|
|
131
|
+
TIncomingRequest,
|
|
132
|
+
TOutgoingResponse
|
|
133
|
+
};*/
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type TRequestHandler } from "./server.js";
|
|
2
|
+
import { openFuseFd, defaultFusePath } from "./linux.js";
|
|
3
|
+
import { createConvenienceRequestHandler } from "./convenience.js";
|
|
4
|
+
import { moveMount, unmount, type TMountAttributes, type TRequiredFuseMountOptions } from "./mount.js";
|
|
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";
|
|
9
|
+
declare const createFuseFileSystem: ({ fuseFd, requestHandler, sniff, }: {
|
|
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
|
+
};
|
|
38
|
+
declare const createDefaultInitResult: () => Extract<TFuseResponse, {
|
|
39
|
+
forOpcode: "INIT";
|
|
40
|
+
errorCode: undefined;
|
|
41
|
+
}>["result"];
|
|
42
|
+
export { createFuseFileSystem, openFuseFd, defaultFusePath, createConvenienceRequestHandler, moveMount, unmount, createDefaultInitResult, direntsToBuffer };
|
|
43
|
+
export type { TRequiredFuseMountOptions, TMountAttributes, TFuseServerInterface, TDirent, };
|
|
@@ -0,0 +1,325 @@
|
|
|
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
|
+
} from "./constants.js";
|
|
10
|
+
import nodeFs from "node:fs";
|
|
11
|
+
import { fsconfig, fsmount, fsopen } from "./syscalls.js";
|
|
12
|
+
import { openFuseFd, defaultFusePath } from "./linux.js";
|
|
13
|
+
import { createConvenienceRequestHandler } from "./convenience.js";
|
|
14
|
+
import {
|
|
15
|
+
mountAttributesToBitmask,
|
|
16
|
+
moveMount,
|
|
17
|
+
unmount,
|
|
18
|
+
/*type TMountAttributes,
|
|
19
|
+
*//*type TRequiredFuseMountOptions
|
|
20
|
+
*/} from "./mount.js";
|
|
21
|
+
import { createPoller } from "@k13engineering/uv-poll";
|
|
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";
|
|
26
|
+
|
|
27
|
+
const UV_EBADF = -9;
|
|
28
|
+
|
|
29
|
+
const MAX_ALLOWED_WRITE = 32n * 1024n * 1024n;
|
|
30
|
+
|
|
31
|
+
const createFuseFileSystem = ({
|
|
32
|
+
fuseFd,
|
|
33
|
+
requestHandler,
|
|
34
|
+
sniff,
|
|
35
|
+
}/*: {
|
|
36
|
+
fuseFd: number;
|
|
37
|
+
requestHandler: TRequestHandler;
|
|
38
|
+
|
|
39
|
+
sniff?: {
|
|
40
|
+
rawMessageFromKernel: (args: { data: Uint8Array }) => void;
|
|
41
|
+
rawMessageToKernel: (args: { data: Uint8Array }) => void;
|
|
42
|
+
}
|
|
43
|
+
}*/) => {
|
|
44
|
+
|
|
45
|
+
let lowLevel = createFuseLowLevel({
|
|
46
|
+
fuseFd,
|
|
47
|
+
maxMessageSize: 1 * 1024 * 1024 + 4096,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const server = createFuseServer({
|
|
51
|
+
requestHandler: {
|
|
52
|
+
// eslint-disable-next-line complexity
|
|
53
|
+
handleRequest: async ({ request }) => {
|
|
54
|
+
const response = await requestHandler.handleRequest({ request });
|
|
55
|
+
|
|
56
|
+
if (response !== undefined && response.forOpcode === "INIT" && response.errorCode === undefined) {
|
|
57
|
+
|
|
58
|
+
if (response.result.maxWrite >= MAX_ALLOWED_WRITE) {
|
|
59
|
+
throw Error(`maxWrite of ${response.result.maxWrite} is too large, refusing to operate`);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
lowLevel = createFuseLowLevel({
|
|
63
|
+
fuseFd,
|
|
64
|
+
maxMessageSize: Number(response.result.maxWrite) + 4096,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return response;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
sendMessageToKernel: ({ rawMessageToKernel }) => {
|
|
72
|
+
sniff?.rawMessageToKernel({ data: rawMessageToKernel });
|
|
73
|
+
lowLevel.writeOutgoingMessage({
|
|
74
|
+
rawMessage: rawMessageToKernel
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
80
|
+
let ended = false;
|
|
81
|
+
let closed = false;
|
|
82
|
+
|
|
83
|
+
const poller = createPoller({
|
|
84
|
+
fd: fuseFd
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const handlePollError = ({ errorCode }/*: { errorCode: number }*/) => {
|
|
88
|
+
if (errorCode === UV_EBADF) {
|
|
89
|
+
// EBADF is raised when the kernel unmounts the filesystem
|
|
90
|
+
ended = true;
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw Error(`poll error with code ${errorCode}`);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// eslint-disable-next-line complexity
|
|
98
|
+
const next = () => {
|
|
99
|
+
|
|
100
|
+
if (closed) {
|
|
101
|
+
throw Error("BUG: should not be called after close");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { error: readError, incomingMessage, end } = lowLevel.tryReadIncomingMessage();
|
|
105
|
+
if (readError !== undefined) {
|
|
106
|
+
throw readError;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (end) {
|
|
110
|
+
ended = true;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (incomingMessage !== undefined) {
|
|
115
|
+
sniff?.rawMessageFromKernel({ data: incomingMessage });
|
|
116
|
+
server.processRequest({
|
|
117
|
+
rawIncomingRequest: incomingMessage
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
poller.armOnce({
|
|
122
|
+
error: ({ errorCode }) => {
|
|
123
|
+
handlePollError({ errorCode });
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
readable: () => {
|
|
127
|
+
next();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
poller.armOnce({
|
|
133
|
+
error: ({ errorCode }) => {
|
|
134
|
+
handlePollError({ errorCode });
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
readable: () => {
|
|
138
|
+
next();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
/*type TFsMountResult = {
|
|
143
|
+
error: Error;
|
|
144
|
+
mountFd: undefined;
|
|
145
|
+
} | {
|
|
146
|
+
error: undefined;
|
|
147
|
+
mountFd: number;
|
|
148
|
+
};*/
|
|
149
|
+
|
|
150
|
+
// eslint-disable-next-line max-statements
|
|
151
|
+
const mountDetached = ({
|
|
152
|
+
otherOptions,
|
|
153
|
+
requiredOptions,
|
|
154
|
+
mountAttributes,
|
|
155
|
+
}/*: {
|
|
156
|
+
otherOptions: {
|
|
157
|
+
default_permissions?: boolean;
|
|
158
|
+
allow_other?: boolean;
|
|
159
|
+
allow_root?: boolean;
|
|
160
|
+
} | Record<string, string | boolean>;
|
|
161
|
+
requiredOptions: TRequiredFuseMountOptions;
|
|
162
|
+
mountAttributes: TMountAttributes;
|
|
163
|
+
}*/)/*: TFsMountResult*/ => {
|
|
164
|
+
|
|
165
|
+
const { errno: fsopenErrno, fsfd } = fsopen({
|
|
166
|
+
fileSystemName: "fuse",
|
|
167
|
+
flags: FSOPEN_CLOEXEC
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (fsopenErrno !== undefined) {
|
|
171
|
+
return {
|
|
172
|
+
error: Error(`fsopen syscall failed with errno ${fsopenErrno}`),
|
|
173
|
+
mountFd: undefined,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const configureString = ({ key, value }/*: { key: string; value: string }*/) => {
|
|
178
|
+
const { errno } = fsconfig({
|
|
179
|
+
fd: fsfd,
|
|
180
|
+
command: FSCONFIG_SET_STRING,
|
|
181
|
+
key,
|
|
182
|
+
value,
|
|
183
|
+
auxiliaryValue: 0,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (errno !== undefined) {
|
|
187
|
+
throw Error(`fsconfig syscall failed with errno ${errno} when setting ${key}=${value}`);
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const configureFlag = ({ key, value }/*: { key: string; value: boolean }*/) => {
|
|
192
|
+
if (!value) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const { errno } = fsconfig({
|
|
197
|
+
fd: fsfd,
|
|
198
|
+
command: FSCONFIG_SET_FLAG,
|
|
199
|
+
key,
|
|
200
|
+
value: undefined,
|
|
201
|
+
auxiliaryValue: 0,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (errno !== undefined) {
|
|
205
|
+
throw Error(`fsconfig syscall failed with errno ${errno} when setting flag ${key}`);
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const forbiddenOptions = ["fd", "rootmode", "user_id", "group_id"];
|
|
210
|
+
|
|
211
|
+
Object.keys(otherOptions).forEach((key) => {
|
|
212
|
+
const value = otherOptions[key /*as keyof typeof otherOptions*/];
|
|
213
|
+
|
|
214
|
+
if (forbiddenOptions.includes(key)) {
|
|
215
|
+
throw Error(`option ${key} is not allowed as it is required for correct mounting`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (value === undefined) {
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (typeof value === "boolean") {
|
|
223
|
+
configureFlag({ key, value });
|
|
224
|
+
} else {
|
|
225
|
+
configureString({ key, value });
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
configureString({ key: "fd", value: `${fuseFd}` });
|
|
230
|
+
configureString({ key: "rootmode", value: `${requiredOptions.rootmode.toString(8)}` });
|
|
231
|
+
configureString({ key: "user_id", value: `${requiredOptions.user_id}` });
|
|
232
|
+
configureString({ key: "group_id", value: `${requiredOptions.group_id}` });
|
|
233
|
+
|
|
234
|
+
const { errno: createErrno } = fsconfig({
|
|
235
|
+
fd: fsfd,
|
|
236
|
+
command: FSCONFIG_CMD_CREATE,
|
|
237
|
+
key: undefined,
|
|
238
|
+
value: undefined,
|
|
239
|
+
auxiliaryValue: 0,
|
|
240
|
+
});
|
|
241
|
+
if (createErrno !== undefined) {
|
|
242
|
+
|
|
243
|
+
nodeFs.closeSync(fsfd);
|
|
244
|
+
|
|
245
|
+
return {
|
|
246
|
+
error: Error(`fsconfig CMD_CREATE syscall failed with errno ${createErrno}`),
|
|
247
|
+
mountFd: undefined,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const { errno: mountErrno, mountFd } = fsmount({
|
|
252
|
+
fd: fsfd,
|
|
253
|
+
flags: FSMOUNT_CLOEXEC,
|
|
254
|
+
mountAttributesBitmask: mountAttributesToBitmask({
|
|
255
|
+
mountAttributes
|
|
256
|
+
})
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
nodeFs.closeSync(fsfd);
|
|
260
|
+
|
|
261
|
+
if (mountErrno !== undefined) {
|
|
262
|
+
return {
|
|
263
|
+
error: Error(`fsmount syscall failed with errno ${mountErrno}`),
|
|
264
|
+
mountFd: undefined,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
error: undefined,
|
|
270
|
+
mountFd,
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const close = () => {
|
|
275
|
+
if (closed) {
|
|
276
|
+
throw Error("already closed");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
closed = true;
|
|
280
|
+
|
|
281
|
+
poller.close();
|
|
282
|
+
nodeFs.closeSync(fuseFd);
|
|
283
|
+
};
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
mountDetached,
|
|
287
|
+
close,
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const createDefaultInitResult = ()/*: Extract<TFuseResponse, { forOpcode: "INIT", errorCode: undefined }>["result"]*/ => {
|
|
292
|
+
return {
|
|
293
|
+
major: 7n,
|
|
294
|
+
minor: 45n,
|
|
295
|
+
maxReadahead: 4096n,
|
|
296
|
+
flags: 0n,
|
|
297
|
+
maxBackground: 0n,
|
|
298
|
+
congestionThreshold: 0n,
|
|
299
|
+
maxWrite: 1n * 1024n * 1024n,
|
|
300
|
+
timeGran: 1n,
|
|
301
|
+
maxPages: 0n,
|
|
302
|
+
mapAlignment: 0n,
|
|
303
|
+
flags2: 0n,
|
|
304
|
+
maxStackDepth: 0n,
|
|
305
|
+
requestTimeout: 0n,
|
|
306
|
+
};
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
export {
|
|
310
|
+
createFuseFileSystem,
|
|
311
|
+
openFuseFd,
|
|
312
|
+
defaultFusePath,
|
|
313
|
+
createConvenienceRequestHandler,
|
|
314
|
+
moveMount,
|
|
315
|
+
unmount,
|
|
316
|
+
createDefaultInitResult,
|
|
317
|
+
direntsToBuffer
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
/*export type {
|
|
321
|
+
TRequiredFuseMountOptions,
|
|
322
|
+
TMountAttributes,
|
|
323
|
+
TFuseServerInterface,
|
|
324
|
+
TDirent,
|
|
325
|
+
};*/
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type TOpenResult = {
|
|
2
|
+
error: Error;
|
|
3
|
+
fuseFd: undefined;
|
|
4
|
+
} | {
|
|
5
|
+
error: undefined;
|
|
6
|
+
fuseFd: number;
|
|
7
|
+
};
|
|
8
|
+
declare const defaultFusePath = "/dev/fuse";
|
|
9
|
+
declare const openFuseFd: ({ fusePath }: {
|
|
10
|
+
fusePath: string;
|
|
11
|
+
}) => TOpenResult;
|
|
12
|
+
export { defaultFusePath, openFuseFd };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import nodeFs from "node:fs";
|
|
2
|
+
|
|
3
|
+
/*type TOpenResult = {
|
|
4
|
+
error: Error;
|
|
5
|
+
fuseFd: undefined;
|
|
6
|
+
} | {
|
|
7
|
+
error: undefined;
|
|
8
|
+
fuseFd: number;
|
|
9
|
+
};*/
|
|
10
|
+
|
|
11
|
+
const defaultFusePath = "/dev/fuse";
|
|
12
|
+
|
|
13
|
+
const openFuseFd = ({ fusePath }/*: { fusePath: string }*/)/*: TOpenResult*/ => {
|
|
14
|
+
try {
|
|
15
|
+
const fuseFd = nodeFs.openSync(fusePath, nodeFs.constants.O_RDWR | nodeFs.constants.O_NONBLOCK);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
error: undefined,
|
|
19
|
+
fuseFd,
|
|
20
|
+
};
|
|
21
|
+
} catch (err) {
|
|
22
|
+
return {
|
|
23
|
+
error: Error(`failed to open "${defaultFusePath}": ${err}`),
|
|
24
|
+
fuseFd: undefined,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export {
|
|
30
|
+
defaultFusePath,
|
|
31
|
+
openFuseFd
|
|
32
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type TParsedMessageFromKernel = {
|
|
2
|
+
header: {
|
|
3
|
+
opcode: bigint;
|
|
4
|
+
unique: bigint;
|
|
5
|
+
nodeid: bigint;
|
|
6
|
+
uid: bigint;
|
|
7
|
+
gid: bigint;
|
|
8
|
+
pid: bigint;
|
|
9
|
+
};
|
|
10
|
+
payload: Uint8Array;
|
|
11
|
+
};
|
|
12
|
+
type TParsedMessageToKernel = {
|
|
13
|
+
header: {
|
|
14
|
+
error: bigint;
|
|
15
|
+
unique: bigint;
|
|
16
|
+
};
|
|
17
|
+
payload: Uint8Array;
|
|
18
|
+
};
|
|
19
|
+
type TParseMessageFromKernelResult = {
|
|
20
|
+
error: Error;
|
|
21
|
+
parsedMessageFromKernel: undefined;
|
|
22
|
+
} | {
|
|
23
|
+
error: undefined;
|
|
24
|
+
parsedMessageFromKernel: TParsedMessageFromKernel;
|
|
25
|
+
};
|
|
26
|
+
type TParseMessageToKernelResult = {
|
|
27
|
+
error: Error;
|
|
28
|
+
parsedMessageToKernel: undefined;
|
|
29
|
+
} | {
|
|
30
|
+
error: undefined;
|
|
31
|
+
parsedMessageToKernel: TParsedMessageToKernel;
|
|
32
|
+
};
|
|
33
|
+
declare const createMessageFromKernelParser: () => {
|
|
34
|
+
parseMessageFromKernel: ({ data }: {
|
|
35
|
+
data: Uint8Array;
|
|
36
|
+
}) => TParseMessageFromKernelResult;
|
|
37
|
+
formatMessageFromKernel: ({ parsedMessageFromKernel }: {
|
|
38
|
+
parsedMessageFromKernel: TParsedMessageFromKernel;
|
|
39
|
+
}) => Uint8Array;
|
|
40
|
+
};
|
|
41
|
+
declare const createMessageToKernelParser: () => {
|
|
42
|
+
formatMessageToKernel: ({ parsedMessageToKernel }: {
|
|
43
|
+
parsedMessageToKernel: TParsedMessageToKernel;
|
|
44
|
+
}) => Uint8Array;
|
|
45
|
+
parseMessageToKernel: ({ data }: {
|
|
46
|
+
data: Uint8Array;
|
|
47
|
+
}) => TParseMessageToKernelResult;
|
|
48
|
+
};
|
|
49
|
+
export { createMessageFromKernelParser, createMessageToKernelParser };
|
|
50
|
+
export type { TParsedMessageFromKernel, TParsedMessageToKernel };
|