@k13engineering/linux-fuse 0.0.1 → 0.0.2

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,463 @@
1
+ /*import type { TFuseRequest, TFuseResponse } from "./protocol.ts";*/
2
+ /*import type { TRequestHandler } from "./server.ts";*/
3
+
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
+ /*type DistributiveOmit<T, K extends PropertyKey> = T extends any ? Omit<T, K> : never;*/
6
+
7
+ /*type TConvenienceRequest<T extends string> = Extract<TFuseRequest, { opcode: T }>;*/
8
+ /*type TConvenienceResponse<T extends string> = DistributiveOmit<Extract<TFuseResponse, { forOpcode: T }>, "unique">;*/
9
+
10
+ /*type TFuseServerInterface = {
11
+ init: (req: TConvenienceRequest<"INIT">) => Promise<TConvenienceResponse<"INIT">>;
12
+ forget: (req: TConvenienceRequest<"FORGET">) => Promise<void>;
13
+ batchForget: (req: TConvenienceRequest<"BATCH_FORGET">) => Promise<void>;
14
+ getattr: (req: Extract<TConvenienceRequest<"GETATTR">, { getattrMode: "node" }>) => Promise<TConvenienceResponse<"GETATTR">>;
15
+ fgetattr: (req: Extract<TConvenienceRequest<"GETATTR">, { getattrMode: "fileHandle" }>) => Promise<TConvenienceResponse<"GETATTR">>;
16
+ setattr: (req: TConvenienceRequest<"SETATTR">) => Promise<TConvenienceResponse<"SETATTR">>;
17
+ listxattr: (req: TConvenienceRequest<"LISTXATTR">) => Promise<TConvenienceResponse<"LISTXATTR">>;
18
+ getxattr: (req: TConvenienceRequest<"GETXATTR">) => Promise<TConvenienceResponse<"GETXATTR">>;
19
+ readlink: (req: TConvenienceRequest<"READLINK">) => Promise<TConvenienceResponse<"READLINK">>;
20
+ symlink: (req: TConvenienceRequest<"SYMLINK">) => Promise<TConvenienceResponse<"SYMLINK">>;
21
+ mknod: (req: TConvenienceRequest<"MKNOD">) => Promise<TConvenienceResponse<"MKNOD">>;
22
+ mkdir: (req: TConvenienceRequest<"MKDIR">) => Promise<TConvenienceResponse<"MKDIR">>;
23
+ unlink: (req: TConvenienceRequest<"UNLINK">) => Promise<TConvenienceResponse<"UNLINK">>;
24
+ rmdir: (req: TConvenienceRequest<"RMDIR">) => Promise<TConvenienceResponse<"RMDIR">>;
25
+ rename: (req: TConvenienceRequest<"RENAME">) => Promise<TConvenienceResponse<"RENAME">>;
26
+ link: (req: TConvenienceRequest<"LINK">) => Promise<TConvenienceResponse<"LINK">>;
27
+ opendir: (req: TConvenienceRequest<"OPENDIR">) => Promise<TConvenienceResponse<"OPENDIR">>;
28
+ readdir: (req: TConvenienceRequest<"READDIR">) => Promise<TConvenienceResponse<"READDIR">>;
29
+ release: (req: TConvenienceRequest<"RELEASE">) => Promise<TConvenienceResponse<"RELEASE">>;
30
+ releasedir: (req: TConvenienceRequest<"RELEASEDIR">) => Promise<TConvenienceResponse<"RELEASEDIR">>;
31
+ fsyncdir: (req: TConvenienceRequest<"FSYNCDIR">) => Promise<TConvenienceResponse<"FSYNCDIR">>;
32
+ statfs: (req: TConvenienceRequest<"STATFS">) => Promise<TConvenienceResponse<"STATFS">>;
33
+ lookup: (req: TConvenienceRequest<"LOOKUP">) => Promise<TConvenienceResponse<"LOOKUP">>;
34
+ open: (req: TConvenienceRequest<"OPEN">) => Promise<TConvenienceResponse<"OPEN">>;
35
+ read: (req: TConvenienceRequest<"READ">) => Promise<TConvenienceResponse<"READ">>;
36
+ write: (req: TConvenienceRequest<"WRITE">) => Promise<TConvenienceResponse<"WRITE">>;
37
+ flush: (req: TConvenienceRequest<"FLUSH">) => Promise<TConvenienceResponse<"FLUSH">>;
38
+ fsync: (req: TConvenienceRequest<"FSYNC">) => Promise<TConvenienceResponse<"FSYNC">>;
39
+ access: (req: TConvenienceRequest<"ACCESS">) => Promise<TConvenienceResponse<"ACCESS">>;
40
+ setxattr: (req: TConvenienceRequest<"SETXATTR">) => Promise<TConvenienceResponse<"SETXATTR">>;
41
+ removexattr: (req: TConvenienceRequest<"REMOVEXATTR">) => Promise<TConvenienceResponse<"REMOVEXATTR">>;
42
+ create: (req: TConvenienceRequest<"CREATE">) => Promise<TConvenienceResponse<"CREATE">>;
43
+ destroy: (req: TConvenienceRequest<"DESTROY">) => Promise<TConvenienceResponse<"DESTROY">>;
44
+ getlk: (req: TConvenienceRequest<"GETLK">) => Promise<TConvenienceResponse<"GETLK">>;
45
+ setlk: (req: TConvenienceRequest<"SETLK">) => Promise<TConvenienceResponse<"SETLK">>;
46
+ setlkw: (req: TConvenienceRequest<"SETLKW">) => Promise<TConvenienceResponse<"SETLKW">>;
47
+ interrupt: (req: TConvenienceRequest<"INTERRUPT">) => Promise<void>;
48
+ bmap: (req: TConvenienceRequest<"BMAP">) => Promise<TConvenienceResponse<"BMAP">>;
49
+ ioctl: (req: TConvenienceRequest<"IOCTL">) => Promise<TConvenienceResponse<"IOCTL">>;
50
+ poll: (req: TConvenienceRequest<"POLL">) => Promise<TConvenienceResponse<"POLL">>;
51
+ notifyReply: (req: TConvenienceRequest<"NOTIFY_REPLY">) => Promise<void>;
52
+ fallocate: (req: TConvenienceRequest<"FALLOCATE">) => Promise<TConvenienceResponse<"FALLOCATE">>;
53
+ readdirplus: (req: TConvenienceRequest<"READDIRPLUS">) => Promise<TConvenienceResponse<"READDIRPLUS">>;
54
+ };*/
55
+
56
+ const createConvenienceRequestHandler = ({
57
+ serverInterface
58
+ }/*: {
59
+ serverInterface: TFuseServerInterface
60
+ }*/)/*: TRequestHandler*/ => {
61
+ // eslint-disable-next-line max-statements, complexity
62
+ const handleRequest/*: TRequestHandler["handleRequest"]*/ = async ({ request }) => {
63
+ switch (request.opcode) {
64
+ case "INIT": {
65
+ const response = await serverInterface.init(request);
66
+
67
+ return {
68
+ ...response,
69
+ unique: request.unique,
70
+ };
71
+ }
72
+
73
+ case "FORGET": {
74
+ await serverInterface.forget(request);
75
+ return undefined;
76
+ }
77
+
78
+ case "BATCH_FORGET": {
79
+ await serverInterface.batchForget(request);
80
+ return undefined;
81
+ }
82
+
83
+ case "GETATTR": {
84
+
85
+ switch (request.getattrMode) {
86
+ case "node": {
87
+ const response = await serverInterface.getattr(request);
88
+
89
+ return {
90
+ ...response,
91
+ unique: request.unique,
92
+ };
93
+ }
94
+
95
+ case "fileHandle": {
96
+ const response = await serverInterface.fgetattr(request);
97
+
98
+ return {
99
+ ...response,
100
+ unique: request.unique,
101
+ };
102
+ }
103
+
104
+ default: {
105
+ // @ts-expect-error exhaustive check
106
+ throw Error(`unsupported getattr mode: ${request.getattrMode}`);
107
+ }
108
+ }
109
+ }
110
+
111
+ case "SETATTR": {
112
+ const response = await serverInterface.setattr(request);
113
+
114
+ return {
115
+ ...response,
116
+ unique: request.unique,
117
+ };
118
+ }
119
+
120
+ case "LISTXATTR": {
121
+ const response = await serverInterface.listxattr(request);
122
+
123
+ return {
124
+ ...response,
125
+ unique: request.unique,
126
+ };
127
+ }
128
+
129
+ case "GETXATTR": {
130
+ const response = await serverInterface.getxattr(request);
131
+
132
+ return {
133
+ ...response,
134
+ unique: request.unique,
135
+ };
136
+ }
137
+
138
+ case "READLINK": {
139
+ const response = await serverInterface.readlink(request);
140
+
141
+ return {
142
+ ...response,
143
+ unique: request.unique,
144
+ };
145
+ }
146
+
147
+ case "SYMLINK": {
148
+ const response = await serverInterface.symlink(request);
149
+
150
+ return {
151
+ ...response,
152
+ unique: request.unique,
153
+ };
154
+ }
155
+
156
+ case "MKNOD": {
157
+ const response = await serverInterface.mknod(request);
158
+
159
+ return {
160
+ ...response,
161
+ unique: request.unique,
162
+ };
163
+ }
164
+
165
+ case "MKDIR": {
166
+ const response = await serverInterface.mkdir(request);
167
+
168
+ return {
169
+ ...response,
170
+ unique: request.unique,
171
+ };
172
+ }
173
+
174
+ case "UNLINK": {
175
+ const response = await serverInterface.unlink(request);
176
+
177
+ return {
178
+ ...response,
179
+ unique: request.unique,
180
+ };
181
+ }
182
+
183
+ case "RMDIR": {
184
+ const response = await serverInterface.rmdir(request);
185
+
186
+ return {
187
+ ...response,
188
+ unique: request.unique,
189
+ };
190
+ }
191
+
192
+ case "RENAME": {
193
+ const response = await serverInterface.rename(request);
194
+
195
+ return {
196
+ ...response,
197
+ unique: request.unique,
198
+ };
199
+ }
200
+
201
+ case "LINK": {
202
+ const response = await serverInterface.link(request);
203
+
204
+ return {
205
+ ...response,
206
+ unique: request.unique,
207
+ };
208
+ }
209
+
210
+ case "OPENDIR": {
211
+ const response = await serverInterface.opendir(request);
212
+
213
+ return {
214
+ ...response,
215
+ unique: request.unique,
216
+ };
217
+ }
218
+
219
+ case "READDIR": {
220
+ const response = await serverInterface.readdir(request);
221
+
222
+ return {
223
+ ...response,
224
+ unique: request.unique,
225
+ };
226
+ }
227
+
228
+ case "RELEASE": {
229
+ const response = await serverInterface.release(request);
230
+
231
+ return {
232
+ ...response,
233
+ unique: request.unique,
234
+ };
235
+ }
236
+
237
+ case "RELEASEDIR": {
238
+ const response = await serverInterface.releasedir(request);
239
+
240
+ return {
241
+ ...response,
242
+ unique: request.unique,
243
+ };
244
+ }
245
+
246
+ case "FSYNCDIR": {
247
+ const response = await serverInterface.fsyncdir(request);
248
+
249
+ return {
250
+ ...response,
251
+ unique: request.unique,
252
+ };
253
+ }
254
+
255
+ case "STATFS": {
256
+ const response = await serverInterface.statfs(request);
257
+
258
+ return {
259
+ ...response,
260
+ unique: request.unique,
261
+ };
262
+ }
263
+
264
+ case "LOOKUP": {
265
+ const response = await serverInterface.lookup(request);
266
+
267
+ return {
268
+ ...response,
269
+ unique: request.unique,
270
+ };
271
+ }
272
+
273
+ case "OPEN": {
274
+ const response = await serverInterface.open(request);
275
+
276
+ return {
277
+ ...response,
278
+ unique: request.unique,
279
+ };
280
+ }
281
+
282
+ case "READ": {
283
+ const response = await serverInterface.read(request);
284
+
285
+ return {
286
+ ...response,
287
+ unique: request.unique,
288
+ };
289
+ }
290
+
291
+ case "WRITE": {
292
+ const response = await serverInterface.write(request);
293
+
294
+ return {
295
+ ...response,
296
+ unique: request.unique,
297
+ };
298
+ }
299
+
300
+ case "FLUSH": {
301
+ const response = await serverInterface.flush(request);
302
+
303
+ return {
304
+ ...response,
305
+ unique: request.unique,
306
+ };
307
+ }
308
+
309
+ case "FSYNC": {
310
+ const response = await serverInterface.fsync(request);
311
+
312
+ return {
313
+ ...response,
314
+ unique: request.unique,
315
+ };
316
+ }
317
+
318
+ case "ACCESS": {
319
+ const response = await serverInterface.access(request);
320
+
321
+ return {
322
+ ...response,
323
+ unique: request.unique,
324
+ };
325
+ }
326
+
327
+ case "SETXATTR": {
328
+ const response = await serverInterface.setxattr(request);
329
+
330
+ return {
331
+ ...response,
332
+ unique: request.unique,
333
+ };
334
+ }
335
+
336
+ case "REMOVEXATTR": {
337
+ const response = await serverInterface.removexattr(request);
338
+
339
+ return {
340
+ ...response,
341
+ unique: request.unique,
342
+ };
343
+ }
344
+
345
+ case "CREATE": {
346
+ const response = await serverInterface.create(request);
347
+
348
+ return {
349
+ ...response,
350
+ unique: request.unique,
351
+ };
352
+ }
353
+
354
+ case "DESTROY": {
355
+ const response = await serverInterface.destroy(request);
356
+
357
+ return {
358
+ ...response,
359
+ unique: request.unique,
360
+ };
361
+ }
362
+
363
+ case "GETLK": {
364
+ const response = await serverInterface.getlk(request);
365
+
366
+ return {
367
+ ...response,
368
+ unique: request.unique,
369
+ };
370
+ }
371
+
372
+ case "SETLK": {
373
+ const response = await serverInterface.setlk(request);
374
+
375
+ return {
376
+ ...response,
377
+ unique: request.unique,
378
+ };
379
+ }
380
+
381
+ case "SETLKW": {
382
+ const response = await serverInterface.setlkw(request);
383
+
384
+ return {
385
+ ...response,
386
+ unique: request.unique,
387
+ };
388
+ }
389
+
390
+ case "INTERRUPT": {
391
+ await serverInterface.interrupt(request);
392
+ return undefined;
393
+ }
394
+
395
+ case "BMAP": {
396
+ const response = await serverInterface.bmap(request);
397
+
398
+ return {
399
+ ...response,
400
+ unique: request.unique,
401
+ };
402
+ }
403
+
404
+ case "IOCTL": {
405
+ const response = await serverInterface.ioctl(request);
406
+
407
+ return {
408
+ ...response,
409
+ unique: request.unique,
410
+ };
411
+ }
412
+
413
+ case "POLL": {
414
+ const response = await serverInterface.poll(request);
415
+
416
+ return {
417
+ ...response,
418
+ unique: request.unique,
419
+ };
420
+ }
421
+
422
+ case "NOTIFY_REPLY": {
423
+ await serverInterface.notifyReply(request);
424
+ return undefined;
425
+ }
426
+
427
+ case "FALLOCATE": {
428
+ const response = await serverInterface.fallocate(request);
429
+
430
+ return {
431
+ ...response,
432
+ unique: request.unique,
433
+ };
434
+ }
435
+
436
+ case "READDIRPLUS": {
437
+ const response = await serverInterface.readdirplus(request);
438
+
439
+ return {
440
+ ...response,
441
+ unique: request.unique,
442
+ };
443
+ }
444
+
445
+ default: {
446
+ // @ts-expect-error exhaustive check
447
+ throw Error(`unsupported opcode: ${request.opcode}`);
448
+ }
449
+ }
450
+ };
451
+
452
+ return {
453
+ handleRequest
454
+ };
455
+ };
456
+
457
+ export {
458
+ createConvenienceRequestHandler
459
+ };
460
+
461
+ /*export type {
462
+ TFuseServerInterface,
463
+ };*/
@@ -0,0 +1,42 @@
1
+ type TIncomingRequest = {
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 TOutgoingResponse = {
13
+ header: {
14
+ error: bigint;
15
+ unique: bigint;
16
+ };
17
+ payload: Uint8Array;
18
+ };
19
+ type TTryReadIncomingMessageResult = {
20
+ error: Error;
21
+ incomingMessage: undefined;
22
+ end: undefined;
23
+ } | {
24
+ error: undefined;
25
+ incomingMessage: Uint8Array;
26
+ end: false;
27
+ } | {
28
+ error: undefined;
29
+ incomingMessage: undefined;
30
+ end: false | true;
31
+ };
32
+ declare const createFuseLowLevel: ({ fuseFd, maxMessageSize }: {
33
+ fuseFd: number;
34
+ maxMessageSize: number;
35
+ }) => {
36
+ tryReadIncomingMessage: () => TTryReadIncomingMessageResult;
37
+ writeOutgoingMessage: ({ rawMessage }: {
38
+ rawMessage: Uint8Array;
39
+ }) => void;
40
+ };
41
+ export { createFuseLowLevel };
42
+ export type { TIncomingRequest, TOutgoingResponse };
@@ -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,40 @@
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
+ declare const createFuseFileSystem: ({ fuseFd, requestHandler, sniff, }: {
7
+ fuseFd: number;
8
+ requestHandler: TRequestHandler;
9
+ sniff?: {
10
+ rawMessageFromKernel: (args: {
11
+ data: Uint8Array;
12
+ }) => void;
13
+ rawMessageToKernel: (args: {
14
+ data: Uint8Array;
15
+ }) => void;
16
+ };
17
+ }) => {
18
+ mountDetached: ({ otherOptions, requiredOptions, mountAttributes, }: {
19
+ otherOptions: {
20
+ default_permissions?: boolean;
21
+ allow_other?: boolean;
22
+ allow_root?: boolean;
23
+ } | Record<string, string | boolean>;
24
+ requiredOptions: TRequiredFuseMountOptions;
25
+ mountAttributes: TMountAttributes;
26
+ }) => {
27
+ error: Error;
28
+ mountFd: undefined;
29
+ } | {
30
+ error: undefined;
31
+ mountFd: number;
32
+ };
33
+ close: () => void;
34
+ };
35
+ declare const createDefaultInitResult: () => Extract<TFuseResponse, {
36
+ forOpcode: "INIT";
37
+ errorCode: undefined;
38
+ }>["result"];
39
+ export { createFuseFileSystem, openFuseFd, defaultFusePath, createConvenienceRequestHandler, moveMount, unmount, createDefaultInitResult };
40
+ export type { TRequiredFuseMountOptions, TMountAttributes, };