@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.
@@ -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,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
+ };*/
@@ -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 };