@jsonjoy.com/json-pack 1.19.0 → 1.21.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/lib/JsonPackMpint.d.ts +8 -0
- package/lib/JsonPackMpint.js +74 -0
- package/lib/JsonPackMpint.js.map +1 -0
- package/lib/json/JsonEncoder.js +9 -3
- package/lib/json/JsonEncoder.js.map +1 -1
- package/lib/nfs/v4/Nfsv4Decoder.d.ts +14 -10
- package/lib/nfs/v4/Nfsv4Decoder.js +156 -74
- package/lib/nfs/v4/Nfsv4Decoder.js.map +1 -1
- package/lib/nfs/v4/Nfsv4Encoder.d.ts +4 -91
- package/lib/nfs/v4/Nfsv4Encoder.js +7 -823
- package/lib/nfs/v4/Nfsv4Encoder.js.map +1 -1
- package/lib/nfs/v4/Nfsv4FullEncoder.d.ts +32 -0
- package/lib/nfs/v4/Nfsv4FullEncoder.js +72 -0
- package/lib/nfs/v4/Nfsv4FullEncoder.js.map +1 -0
- package/lib/nfs/v4/attributes.d.ts +17 -0
- package/lib/nfs/v4/attributes.js +203 -0
- package/lib/nfs/v4/attributes.js.map +1 -0
- package/lib/nfs/v4/builder.d.ts +60 -0
- package/lib/nfs/v4/builder.js +187 -0
- package/lib/nfs/v4/builder.js.map +1 -0
- package/lib/nfs/v4/client/NfsFsDir.d.ts +20 -0
- package/lib/nfs/v4/client/NfsFsDir.js +129 -0
- package/lib/nfs/v4/client/NfsFsDir.js.map +1 -0
- package/lib/nfs/v4/client/NfsFsDirent.d.ts +14 -0
- package/lib/nfs/v4/client/NfsFsDirent.js +32 -0
- package/lib/nfs/v4/client/NfsFsDirent.js.map +1 -0
- package/lib/nfs/v4/client/NfsFsFileHandle.d.ts +34 -0
- package/lib/nfs/v4/client/NfsFsFileHandle.js +268 -0
- package/lib/nfs/v4/client/NfsFsFileHandle.js.map +1 -0
- package/lib/nfs/v4/client/NfsFsStats.d.ts +31 -0
- package/lib/nfs/v4/client/NfsFsStats.js +49 -0
- package/lib/nfs/v4/client/NfsFsStats.js.map +1 -0
- package/lib/nfs/v4/client/Nfsv4FsClient.d.ts +54 -0
- package/lib/nfs/v4/client/Nfsv4FsClient.js +812 -0
- package/lib/nfs/v4/client/Nfsv4FsClient.js.map +1 -0
- package/lib/nfs/v4/client/Nfsv4TcpClient.d.ts +41 -0
- package/lib/nfs/v4/client/Nfsv4TcpClient.js +216 -0
- package/lib/nfs/v4/client/Nfsv4TcpClient.js.map +1 -0
- package/lib/nfs/v4/client/types.d.ts +9 -0
- package/lib/nfs/v4/client/types.js +3 -0
- package/lib/nfs/v4/client/types.js.map +1 -0
- package/lib/nfs/v4/constants.d.ts +3 -0
- package/lib/nfs/v4/constants.js +4 -0
- package/lib/nfs/v4/constants.js.map +1 -1
- package/lib/nfs/v4/format.d.ts +23 -0
- package/lib/nfs/v4/format.js +870 -0
- package/lib/nfs/v4/format.js.map +1 -0
- package/lib/nfs/v4/index.d.ts +3 -1
- package/lib/nfs/v4/index.js +3 -1
- package/lib/nfs/v4/index.js.map +1 -1
- package/lib/nfs/v4/messages.d.ts +208 -90
- package/lib/nfs/v4/messages.js +585 -1
- package/lib/nfs/v4/messages.js.map +1 -1
- package/lib/nfs/v4/server/Nfsv4CompoundProcCtx.d.ts +11 -0
- package/lib/nfs/v4/server/Nfsv4CompoundProcCtx.js +155 -0
- package/lib/nfs/v4/server/Nfsv4CompoundProcCtx.js.map +1 -0
- package/lib/nfs/v4/server/Nfsv4Connection.d.ts +42 -0
- package/lib/nfs/v4/server/Nfsv4Connection.js +160 -0
- package/lib/nfs/v4/server/Nfsv4Connection.js.map +1 -0
- package/lib/nfs/v4/server/Nfsv4TcpServer.d.ts +26 -0
- package/lib/nfs/v4/server/Nfsv4TcpServer.js +91 -0
- package/lib/nfs/v4/server/Nfsv4TcpServer.js.map +1 -0
- package/lib/nfs/v4/server/operations/ByteRangeLock.d.ts +11 -0
- package/lib/nfs/v4/server/operations/ByteRangeLock.js +21 -0
- package/lib/nfs/v4/server/operations/ByteRangeLock.js.map +1 -0
- package/lib/nfs/v4/server/operations/ClientRecord.d.ts +13 -0
- package/lib/nfs/v4/server/operations/ClientRecord.js +17 -0
- package/lib/nfs/v4/server/operations/ClientRecord.js.map +1 -0
- package/lib/nfs/v4/server/operations/FilesystemStats.d.ts +9 -0
- package/lib/nfs/v4/server/operations/FilesystemStats.js +15 -0
- package/lib/nfs/v4/server/operations/FilesystemStats.js.map +1 -0
- package/lib/nfs/v4/server/operations/LockOwnerState.d.ts +9 -0
- package/lib/nfs/v4/server/operations/LockOwnerState.js +15 -0
- package/lib/nfs/v4/server/operations/LockOwnerState.js.map +1 -0
- package/lib/nfs/v4/server/operations/LockStateid.d.ts +10 -0
- package/lib/nfs/v4/server/operations/LockStateid.js +22 -0
- package/lib/nfs/v4/server/operations/LockStateid.js.map +1 -0
- package/lib/nfs/v4/server/operations/Nfsv4Operations.d.ts +44 -0
- package/lib/nfs/v4/server/operations/Nfsv4Operations.js +3 -0
- package/lib/nfs/v4/server/operations/Nfsv4Operations.js.map +1 -0
- package/lib/nfs/v4/server/operations/Nfsv4OperationsNotImpl.d.ts +42 -0
- package/lib/nfs/v4/server/operations/Nfsv4OperationsNotImpl.js +159 -0
- package/lib/nfs/v4/server/operations/Nfsv4OperationsNotImpl.js.map +1 -0
- package/lib/nfs/v4/server/operations/OpenFileState.d.ts +14 -0
- package/lib/nfs/v4/server/operations/OpenFileState.js +17 -0
- package/lib/nfs/v4/server/operations/OpenFileState.js.map +1 -0
- package/lib/nfs/v4/server/operations/OpenOwnerState.d.ts +9 -0
- package/lib/nfs/v4/server/operations/OpenOwnerState.js +15 -0
- package/lib/nfs/v4/server/operations/OpenOwnerState.js.map +1 -0
- package/lib/nfs/v4/server/operations/node/Nfsv4OperationsNode.d.ts +99 -0
- package/lib/nfs/v4/server/operations/node/Nfsv4OperationsNode.js +1400 -0
- package/lib/nfs/v4/server/operations/node/Nfsv4OperationsNode.js.map +1 -0
- package/lib/nfs/v4/server/operations/node/attrs.d.ts +5 -0
- package/lib/nfs/v4/server/operations/node/attrs.js +262 -0
- package/lib/nfs/v4/server/operations/node/attrs.js.map +1 -0
- package/lib/nfs/v4/server/operations/node/fh.d.ts +28 -0
- package/lib/nfs/v4/server/operations/node/fh.js +147 -0
- package/lib/nfs/v4/server/operations/node/fh.js.map +1 -0
- package/lib/nfs/v4/server/operations/node/util.d.ts +4 -0
- package/lib/nfs/v4/server/operations/node/util.js +17 -0
- package/lib/nfs/v4/server/operations/node/util.js.map +1 -0
- package/lib/nfs/v4/server/types.d.ts +4 -0
- package/lib/nfs/v4/server/types.js +3 -0
- package/lib/nfs/v4/server/types.js.map +1 -0
- package/lib/nfs/v4/server/util.d.ts +6 -0
- package/lib/nfs/v4/server/util.js +184 -0
- package/lib/nfs/v4/server/util.js.map +1 -0
- package/lib/nfs/v4/structs.d.ts +106 -51
- package/lib/nfs/v4/structs.js +237 -16
- package/lib/nfs/v4/structs.js.map +1 -1
- package/lib/rm/RmRecordDecoder.js.map +1 -1
- package/lib/rm/RmRecordEncoder.d.ts +2 -0
- package/lib/rm/RmRecordEncoder.js +25 -0
- package/lib/rm/RmRecordEncoder.js.map +1 -1
- package/lib/rpc/RpcMessageDecoder.js +3 -1
- package/lib/rpc/RpcMessageDecoder.js.map +1 -1
- package/lib/ssh/SshDecoder.d.ts +20 -0
- package/lib/ssh/SshDecoder.js +87 -0
- package/lib/ssh/SshDecoder.js.map +1 -0
- package/lib/ssh/SshEncoder.d.ts +27 -0
- package/lib/ssh/SshEncoder.js +144 -0
- package/lib/ssh/SshEncoder.js.map +1 -0
- package/lib/ssh/index.d.ts +2 -0
- package/lib/ssh/index.js +6 -0
- package/lib/ssh/index.js.map +1 -0
- package/lib/xdr/types.d.ts +6 -0
- package/package.json +4 -2
- package/lib/nfs/v4/FullNfsv4Encoder.d.ts +0 -28
- package/lib/nfs/v4/FullNfsv4Encoder.js +0 -73
- package/lib/nfs/v4/FullNfsv4Encoder.js.map +0 -1
|
@@ -0,0 +1,812 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Nfsv4FsClient = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const builder_1 = require("../builder");
|
|
6
|
+
const structs = tslib_1.__importStar(require("../structs"));
|
|
7
|
+
const Writer_1 = require("@jsonjoy.com/buffers/lib/Writer");
|
|
8
|
+
const Reader_1 = require("@jsonjoy.com/buffers/lib/Reader");
|
|
9
|
+
const XdrEncoder_1 = require("../../../xdr/XdrEncoder");
|
|
10
|
+
const XdrDecoder_1 = require("../../../xdr/XdrDecoder");
|
|
11
|
+
const NfsFsStats_1 = require("./NfsFsStats");
|
|
12
|
+
const NfsFsDir_1 = require("./NfsFsDir");
|
|
13
|
+
const NfsFsDirent_1 = require("./NfsFsDirent");
|
|
14
|
+
const NfsFsFileHandle_1 = require("./NfsFsFileHandle");
|
|
15
|
+
class Nfsv4FsClient {
|
|
16
|
+
constructor(fs) {
|
|
17
|
+
this.fs = fs;
|
|
18
|
+
this.openOwnerSeqids = new Map();
|
|
19
|
+
this.defaultOpenOwnerId = new Uint8Array([1, 2, 3, 4]);
|
|
20
|
+
this.closeStateid = async (openOwner, stateid) => {
|
|
21
|
+
const key = this.makeOpenOwnerKey(openOwner);
|
|
22
|
+
const previousSeqid = this.openOwnerSeqids.get(key);
|
|
23
|
+
const seqid = this.nextOpenOwnerSeqid(openOwner);
|
|
24
|
+
const response = await this.fs.compound([builder_1.nfs.CLOSE(seqid, stateid)]);
|
|
25
|
+
if (response.status !== 0) {
|
|
26
|
+
if (previousSeqid !== undefined) {
|
|
27
|
+
this.openOwnerSeqids.set(key, previousSeqid);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
this.openOwnerSeqids.delete(key);
|
|
31
|
+
}
|
|
32
|
+
throw new Error(`Failed to close file: ${response.status}`);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
this.readFile = async (id, options) => {
|
|
36
|
+
const encoding = typeof options === 'string' ? options : options?.encoding;
|
|
37
|
+
const path = typeof id === 'string' ? id : id.toString();
|
|
38
|
+
const parts = this.parsePath(path);
|
|
39
|
+
const operations = this.navigateToParent(parts);
|
|
40
|
+
const filename = parts[parts.length - 1];
|
|
41
|
+
const openOwner = this.createDefaultOpenOwner();
|
|
42
|
+
const claim = builder_1.nfs.OpenClaimNull(filename);
|
|
43
|
+
const openSeqid = this.nextOpenOwnerSeqid(openOwner);
|
|
44
|
+
operations.push(builder_1.nfs.OPEN(openSeqid, 1, 0, openOwner, builder_1.nfs.OpenHowNoCreate(), claim));
|
|
45
|
+
const openResponse = await this.fs.compound(operations);
|
|
46
|
+
if (openResponse.status !== 0) {
|
|
47
|
+
throw new Error(`Failed to open file: ${openResponse.status}`);
|
|
48
|
+
}
|
|
49
|
+
const openRes = openResponse.resarray[openResponse.resarray.length - 1];
|
|
50
|
+
if (openRes.status !== 0 || !openRes.resok) {
|
|
51
|
+
throw new Error(`Failed to open file: ${openRes.status}`);
|
|
52
|
+
}
|
|
53
|
+
const stateid = openRes.resok.stateid;
|
|
54
|
+
const chunks = [];
|
|
55
|
+
let offset = BigInt(0);
|
|
56
|
+
const chunkSize = 65536;
|
|
57
|
+
try {
|
|
58
|
+
while (true) {
|
|
59
|
+
const readResponse = await this.fs.compound([builder_1.nfs.READ(offset, chunkSize, stateid)]);
|
|
60
|
+
if (readResponse.status !== 0) {
|
|
61
|
+
throw new Error(`Failed to read file: ${readResponse.status}`);
|
|
62
|
+
}
|
|
63
|
+
const readRes = readResponse.resarray[0];
|
|
64
|
+
if (readRes.status !== 0 || !readRes.resok) {
|
|
65
|
+
throw new Error(`Failed to read file: ${readRes.status}`);
|
|
66
|
+
}
|
|
67
|
+
if (readRes.resok.data.length > 0) {
|
|
68
|
+
chunks.push(readRes.resok.data);
|
|
69
|
+
offset += BigInt(readRes.resok.data.length);
|
|
70
|
+
}
|
|
71
|
+
if (readRes.resok.eof)
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
finally {
|
|
76
|
+
await this.closeStateid(openOwner, stateid);
|
|
77
|
+
}
|
|
78
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
79
|
+
const result = new Uint8Array(totalLength);
|
|
80
|
+
let position = 0;
|
|
81
|
+
for (const chunk of chunks) {
|
|
82
|
+
result.set(chunk, position);
|
|
83
|
+
position += chunk.length;
|
|
84
|
+
}
|
|
85
|
+
return this.decodeData(result, encoding);
|
|
86
|
+
};
|
|
87
|
+
this.writeFile = async (id, data, options) => {
|
|
88
|
+
const path = typeof id === 'string' ? id : id.toString();
|
|
89
|
+
const parts = this.parsePath(path);
|
|
90
|
+
const operations = this.navigateToParent(parts);
|
|
91
|
+
const filename = parts[parts.length - 1];
|
|
92
|
+
const openOwner = this.createDefaultOpenOwner();
|
|
93
|
+
const claim = builder_1.nfs.OpenClaimNull(filename);
|
|
94
|
+
const openSeqid = this.nextOpenOwnerSeqid(openOwner);
|
|
95
|
+
operations.push(builder_1.nfs.OPEN(openSeqid, 2, 0, openOwner, builder_1.nfs.OpenHowCreateUnchecked(), claim));
|
|
96
|
+
const writer = new Writer_1.Writer(16);
|
|
97
|
+
const xdr = new XdrEncoder_1.XdrEncoder(writer);
|
|
98
|
+
xdr.writeUnsignedHyper(BigInt(0));
|
|
99
|
+
const attrVals = writer.flush();
|
|
100
|
+
const truncateAttrs = builder_1.nfs.Fattr([4], attrVals);
|
|
101
|
+
const stateid = builder_1.nfs.Stateid(0, new Uint8Array(12));
|
|
102
|
+
operations.push(builder_1.nfs.SETATTR(stateid, truncateAttrs));
|
|
103
|
+
const openResponse = await this.fs.compound(operations);
|
|
104
|
+
if (openResponse.status !== 0) {
|
|
105
|
+
throw new Error(`Failed to open file: ${openResponse.status}`);
|
|
106
|
+
}
|
|
107
|
+
const openRes = openResponse.resarray[openResponse.resarray.length - 2];
|
|
108
|
+
if (openRes.status !== 0 || !openRes.resok) {
|
|
109
|
+
throw new Error(`Failed to open file: ${openRes.status}`);
|
|
110
|
+
}
|
|
111
|
+
const openStateid = openRes.resok.stateid;
|
|
112
|
+
const buffer = this.encodeData(data);
|
|
113
|
+
const chunkSize = 65536;
|
|
114
|
+
try {
|
|
115
|
+
let offset = BigInt(0);
|
|
116
|
+
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
117
|
+
const chunk = buffer.slice(i, Math.min(i + chunkSize, buffer.length));
|
|
118
|
+
const writeResponse = await this.fs.compound([
|
|
119
|
+
builder_1.nfs.WRITE(openStateid, offset, 2, chunk),
|
|
120
|
+
]);
|
|
121
|
+
if (writeResponse.status !== 0) {
|
|
122
|
+
throw new Error(`Failed to write file: ${writeResponse.status}`);
|
|
123
|
+
}
|
|
124
|
+
const writeRes = writeResponse.resarray[0];
|
|
125
|
+
if (writeRes.status !== 0 || !writeRes.resok) {
|
|
126
|
+
throw new Error(`Failed to write file: ${writeRes.status}`);
|
|
127
|
+
}
|
|
128
|
+
offset += BigInt(writeRes.resok.count);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
await this.closeStateid(openOwner, openStateid);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
this.stat = async (path, options) => {
|
|
136
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
137
|
+
const parts = this.parsePath(pathStr);
|
|
138
|
+
const operations = this.navigateToPath(parts);
|
|
139
|
+
const attrNums = [
|
|
140
|
+
1,
|
|
141
|
+
4,
|
|
142
|
+
20,
|
|
143
|
+
33,
|
|
144
|
+
35,
|
|
145
|
+
45,
|
|
146
|
+
47,
|
|
147
|
+
53,
|
|
148
|
+
52,
|
|
149
|
+
];
|
|
150
|
+
const attrMask = this.attrNumsToBitmap(attrNums);
|
|
151
|
+
operations.push(builder_1.nfs.GETATTR(attrMask));
|
|
152
|
+
const response = await this.fs.compound(operations);
|
|
153
|
+
if (response.status !== 0) {
|
|
154
|
+
throw new Error(`Failed to stat file: ${response.status}`);
|
|
155
|
+
}
|
|
156
|
+
const getattrRes = response.resarray[response.resarray.length - 1];
|
|
157
|
+
if (getattrRes.status !== 0 || !getattrRes.resok) {
|
|
158
|
+
throw new Error(`Failed to get attributes: ${getattrRes.status}`);
|
|
159
|
+
}
|
|
160
|
+
const fattr = getattrRes.resok.objAttributes;
|
|
161
|
+
const reader = new Reader_1.Reader();
|
|
162
|
+
reader.reset(fattr.attrVals);
|
|
163
|
+
const xdr = new XdrDecoder_1.XdrDecoder(reader);
|
|
164
|
+
let fileType = 1;
|
|
165
|
+
let size = 0;
|
|
166
|
+
let fileid = 0;
|
|
167
|
+
let mode = 0;
|
|
168
|
+
let nlink = 1;
|
|
169
|
+
let spaceUsed = 0;
|
|
170
|
+
let atime = new Date(0);
|
|
171
|
+
let mtime = new Date(0);
|
|
172
|
+
let ctime = new Date(0);
|
|
173
|
+
const returnedMask = fattr.attrmask.mask;
|
|
174
|
+
for (let i = 0; i < returnedMask.length; i++) {
|
|
175
|
+
const word = returnedMask[i];
|
|
176
|
+
if (!word)
|
|
177
|
+
continue;
|
|
178
|
+
for (let bit = 0; bit < 32; bit++) {
|
|
179
|
+
if (!(word & (1 << bit)))
|
|
180
|
+
continue;
|
|
181
|
+
const attrNum = i * 32 + bit;
|
|
182
|
+
switch (attrNum) {
|
|
183
|
+
case 1:
|
|
184
|
+
fileType = xdr.readUnsignedInt();
|
|
185
|
+
break;
|
|
186
|
+
case 4:
|
|
187
|
+
size = Number(xdr.readUnsignedHyper());
|
|
188
|
+
break;
|
|
189
|
+
case 20:
|
|
190
|
+
fileid = Number(xdr.readUnsignedHyper());
|
|
191
|
+
break;
|
|
192
|
+
case 33:
|
|
193
|
+
mode = xdr.readUnsignedInt();
|
|
194
|
+
break;
|
|
195
|
+
case 35:
|
|
196
|
+
nlink = xdr.readUnsignedInt();
|
|
197
|
+
break;
|
|
198
|
+
case 45:
|
|
199
|
+
spaceUsed = Number(xdr.readUnsignedHyper());
|
|
200
|
+
break;
|
|
201
|
+
case 47: {
|
|
202
|
+
const seconds = Number(xdr.readHyper());
|
|
203
|
+
const nseconds = xdr.readUnsignedInt();
|
|
204
|
+
atime = new Date(seconds * 1000 + nseconds / 1000000);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
case 53: {
|
|
208
|
+
const seconds = Number(xdr.readHyper());
|
|
209
|
+
const nseconds = xdr.readUnsignedInt();
|
|
210
|
+
mtime = new Date(seconds * 1000 + nseconds / 1000000);
|
|
211
|
+
break;
|
|
212
|
+
}
|
|
213
|
+
case 52: {
|
|
214
|
+
const seconds = Number(xdr.readHyper());
|
|
215
|
+
const nseconds = xdr.readUnsignedInt();
|
|
216
|
+
ctime = new Date(seconds * 1000 + nseconds / 1000000);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const blocks = Math.ceil(spaceUsed / 512);
|
|
223
|
+
return new NfsFsStats_1.NfsFsStats(0, 0, 0, 4096, fileid, size, blocks, atime, mtime, ctime, mtime, atime.getTime(), mtime.getTime(), ctime.getTime(), mtime.getTime(), 0, mode, nlink, fileType);
|
|
224
|
+
};
|
|
225
|
+
this.lstat = async (path, options) => {
|
|
226
|
+
return this.stat(path, options);
|
|
227
|
+
};
|
|
228
|
+
this.mkdir = async (path, options) => {
|
|
229
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
230
|
+
const parts = this.parsePath(pathStr);
|
|
231
|
+
if (parts.length === 0) {
|
|
232
|
+
throw new Error('Cannot create root directory');
|
|
233
|
+
}
|
|
234
|
+
const operations = this.navigateToParent(parts);
|
|
235
|
+
const dirname = parts[parts.length - 1];
|
|
236
|
+
const createType = builder_1.nfs.CreateTypeDir();
|
|
237
|
+
const emptyAttrs = builder_1.nfs.Fattr([], new Uint8Array(0));
|
|
238
|
+
operations.push(builder_1.nfs.CREATE(createType, dirname, emptyAttrs));
|
|
239
|
+
const response = await this.fs.compound(operations);
|
|
240
|
+
if (response.status !== 0) {
|
|
241
|
+
throw new Error(`Failed to create directory: ${response.status}`);
|
|
242
|
+
}
|
|
243
|
+
const createRes = response.resarray[response.resarray.length - 1];
|
|
244
|
+
if (createRes.status !== 0) {
|
|
245
|
+
throw new Error(`Failed to create directory: ${createRes.status}`);
|
|
246
|
+
}
|
|
247
|
+
return undefined;
|
|
248
|
+
};
|
|
249
|
+
this.readdir = async (path, options) => {
|
|
250
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
251
|
+
const withFileTypes = typeof options === 'object' && options?.withFileTypes;
|
|
252
|
+
const encoding = typeof options === 'string' ? options : options?.encoding;
|
|
253
|
+
const parts = this.parsePath(pathStr);
|
|
254
|
+
const operations = this.navigateToPath(parts);
|
|
255
|
+
const attrNums = withFileTypes ? [1] : [];
|
|
256
|
+
const attrMask = this.attrNumsToBitmap(attrNums);
|
|
257
|
+
operations.push(builder_1.nfs.READDIR(attrMask));
|
|
258
|
+
const response = await this.fs.compound(operations);
|
|
259
|
+
if (response.status !== 0) {
|
|
260
|
+
throw new Error(`Failed to read directory: ${response.status}`);
|
|
261
|
+
}
|
|
262
|
+
const readdirRes = response.resarray[response.resarray.length - 1];
|
|
263
|
+
if (readdirRes.status !== 0 || !readdirRes.resok) {
|
|
264
|
+
throw new Error(`Failed to read directory: ${readdirRes.status}`);
|
|
265
|
+
}
|
|
266
|
+
const entries = [];
|
|
267
|
+
const dirents = [];
|
|
268
|
+
const entryList = readdirRes.resok.entries;
|
|
269
|
+
for (let i = 0; i < entryList.length; i++) {
|
|
270
|
+
const entry = entryList[i];
|
|
271
|
+
const name = entry.name;
|
|
272
|
+
if (withFileTypes) {
|
|
273
|
+
const fattr = entry.attrs;
|
|
274
|
+
const reader = new Reader_1.Reader();
|
|
275
|
+
reader.reset(fattr.attrVals);
|
|
276
|
+
const xdr = new XdrDecoder_1.XdrDecoder(reader);
|
|
277
|
+
let fileType = 1;
|
|
278
|
+
const returnedMask = fattr.attrmask.mask;
|
|
279
|
+
for (let i = 0; i < returnedMask.length; i++) {
|
|
280
|
+
const word = returnedMask[i];
|
|
281
|
+
if (!word)
|
|
282
|
+
continue;
|
|
283
|
+
for (let bit = 0; bit < 32; bit++) {
|
|
284
|
+
if (!(word & (1 << bit)))
|
|
285
|
+
continue;
|
|
286
|
+
const attrNum = i * 32 + bit;
|
|
287
|
+
if (attrNum === 1) {
|
|
288
|
+
fileType = xdr.readUnsignedInt();
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
dirents.push(new NfsFsDirent_1.NfsFsDirent(name, fileType));
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
entries.push(name);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (withFileTypes) {
|
|
299
|
+
return dirents;
|
|
300
|
+
}
|
|
301
|
+
if (encoding && encoding !== 'utf8') {
|
|
302
|
+
return entries.map((name) => Buffer.from(name, 'utf8'));
|
|
303
|
+
}
|
|
304
|
+
return entries;
|
|
305
|
+
};
|
|
306
|
+
this.appendFile = async (path, data, options) => {
|
|
307
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
308
|
+
const parts = this.parsePath(pathStr);
|
|
309
|
+
const operations = this.navigateToParent(parts);
|
|
310
|
+
const filename = parts[parts.length - 1];
|
|
311
|
+
const openOwner = this.createDefaultOpenOwner();
|
|
312
|
+
const claim = builder_1.nfs.OpenClaimNull(filename);
|
|
313
|
+
const openSeqid = this.nextOpenOwnerSeqid(openOwner);
|
|
314
|
+
operations.push(builder_1.nfs.OPEN(openSeqid, 2, 0, openOwner, builder_1.nfs.OpenHowNoCreate(), claim));
|
|
315
|
+
const attrNums = [4];
|
|
316
|
+
const attrMask = this.attrNumsToBitmap(attrNums);
|
|
317
|
+
operations.push(builder_1.nfs.GETATTR(attrMask));
|
|
318
|
+
const openResponse = await this.fs.compound(operations);
|
|
319
|
+
if (openResponse.status !== 0) {
|
|
320
|
+
throw new Error(`Failed to open file: ${openResponse.status}`);
|
|
321
|
+
}
|
|
322
|
+
const openRes = openResponse.resarray[openResponse.resarray.length - 2];
|
|
323
|
+
if (openRes.status !== 0 || !openRes.resok) {
|
|
324
|
+
throw new Error(`Failed to open file: ${openRes.status}`);
|
|
325
|
+
}
|
|
326
|
+
const getattrRes = openResponse.resarray[openResponse.resarray.length - 1];
|
|
327
|
+
if (getattrRes.status !== 0 || !getattrRes.resok) {
|
|
328
|
+
throw new Error(`Failed to get attributes: ${getattrRes.status}`);
|
|
329
|
+
}
|
|
330
|
+
const fattr = getattrRes.resok.objAttributes;
|
|
331
|
+
const reader = new Reader_1.Reader();
|
|
332
|
+
reader.reset(fattr.attrVals);
|
|
333
|
+
const xdr = new XdrDecoder_1.XdrDecoder(reader);
|
|
334
|
+
const currentSize = Number(xdr.readUnsignedHyper());
|
|
335
|
+
const openStateid = openRes.resok.stateid;
|
|
336
|
+
const buffer = this.encodeData(data);
|
|
337
|
+
const chunkSize = 65536;
|
|
338
|
+
try {
|
|
339
|
+
let offset = BigInt(currentSize);
|
|
340
|
+
for (let i = 0; i < buffer.length; i += chunkSize) {
|
|
341
|
+
const chunk = buffer.slice(i, Math.min(i + chunkSize, buffer.length));
|
|
342
|
+
const writeResponse = await this.fs.compound([
|
|
343
|
+
builder_1.nfs.WRITE(openStateid, offset, 2, chunk),
|
|
344
|
+
]);
|
|
345
|
+
if (writeResponse.status !== 0) {
|
|
346
|
+
throw new Error(`Failed to write file: ${writeResponse.status}`);
|
|
347
|
+
}
|
|
348
|
+
const writeRes = writeResponse.resarray[0];
|
|
349
|
+
if (writeRes.status !== 0 || !writeRes.resok) {
|
|
350
|
+
throw new Error(`Failed to write file: ${writeRes.status}`);
|
|
351
|
+
}
|
|
352
|
+
offset += BigInt(writeRes.resok.count);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
finally {
|
|
356
|
+
await this.closeStateid(openOwner, openStateid);
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
this.truncate = async (path, len = 0) => {
|
|
360
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
361
|
+
const parts = this.parsePath(pathStr);
|
|
362
|
+
const operations = this.navigateToPath(parts);
|
|
363
|
+
const writer = new Writer_1.Writer(16);
|
|
364
|
+
const xdr = new XdrEncoder_1.XdrEncoder(writer);
|
|
365
|
+
xdr.writeUnsignedHyper(BigInt(len));
|
|
366
|
+
const attrVals = writer.flush();
|
|
367
|
+
const sizeAttrs = builder_1.nfs.Fattr([4], attrVals);
|
|
368
|
+
const stateid = builder_1.nfs.Stateid(0, new Uint8Array(12));
|
|
369
|
+
operations.push(builder_1.nfs.SETATTR(stateid, sizeAttrs));
|
|
370
|
+
const response = await this.fs.compound(operations);
|
|
371
|
+
if (response.status !== 0) {
|
|
372
|
+
throw new Error(`Failed to truncate file: ${response.status}`);
|
|
373
|
+
}
|
|
374
|
+
const setattrRes = response.resarray[response.resarray.length - 1];
|
|
375
|
+
if (setattrRes.status !== 0) {
|
|
376
|
+
throw new Error(`Failed to truncate file: ${setattrRes.status}`);
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
this.unlink = async (path) => {
|
|
380
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
381
|
+
const parts = this.parsePath(pathStr);
|
|
382
|
+
if (parts.length === 0) {
|
|
383
|
+
throw new Error('Cannot unlink root directory');
|
|
384
|
+
}
|
|
385
|
+
const operations = this.navigateToParent(parts);
|
|
386
|
+
const filename = parts[parts.length - 1];
|
|
387
|
+
operations.push(builder_1.nfs.REMOVE(filename));
|
|
388
|
+
const response = await this.fs.compound(operations);
|
|
389
|
+
if (response.status !== 0) {
|
|
390
|
+
throw new Error(`Failed to unlink file: ${response.status}`);
|
|
391
|
+
}
|
|
392
|
+
const removeRes = response.resarray[response.resarray.length - 1];
|
|
393
|
+
if (removeRes.status !== 0) {
|
|
394
|
+
throw new Error(`Failed to unlink file: ${removeRes.status}`);
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
this.rmdir = async (path, options) => {
|
|
398
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
399
|
+
const parts = this.parsePath(pathStr);
|
|
400
|
+
if (parts.length === 0) {
|
|
401
|
+
throw new Error('Cannot remove root directory');
|
|
402
|
+
}
|
|
403
|
+
const operations = this.navigateToParent(parts);
|
|
404
|
+
const dirname = parts[parts.length - 1];
|
|
405
|
+
operations.push(builder_1.nfs.REMOVE(dirname));
|
|
406
|
+
const response = await this.fs.compound(operations);
|
|
407
|
+
if (response.status !== 0) {
|
|
408
|
+
throw new Error(`Failed to remove directory: ${response.status}`);
|
|
409
|
+
}
|
|
410
|
+
const removeRes = response.resarray[response.resarray.length - 1];
|
|
411
|
+
if (removeRes.status !== 0) {
|
|
412
|
+
throw new Error(`Failed to remove directory: ${removeRes.status}`);
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
this.rm = async (path, options) => {
|
|
416
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
417
|
+
const parts = this.parsePath(pathStr);
|
|
418
|
+
if (parts.length === 0) {
|
|
419
|
+
throw new Error('Cannot remove root directory');
|
|
420
|
+
}
|
|
421
|
+
const force = options?.force ?? false;
|
|
422
|
+
const recursive = options?.recursive ?? false;
|
|
423
|
+
if (recursive) {
|
|
424
|
+
try {
|
|
425
|
+
const stats = await this.stat(path);
|
|
426
|
+
if (stats.isDirectory()) {
|
|
427
|
+
const entries = await this.readdir(path);
|
|
428
|
+
for (const entry of entries) {
|
|
429
|
+
const entryPath = pathStr + '/' + entry;
|
|
430
|
+
await this.rm(entryPath, options);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
catch (err) {
|
|
435
|
+
if (!force)
|
|
436
|
+
throw err;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
try {
|
|
441
|
+
const operations = this.navigateToParent(parts);
|
|
442
|
+
const name = parts[parts.length - 1];
|
|
443
|
+
operations.push(builder_1.nfs.REMOVE(name));
|
|
444
|
+
const response = await this.fs.compound(operations);
|
|
445
|
+
if (response.status !== 0) {
|
|
446
|
+
if (!force)
|
|
447
|
+
throw new Error(`Failed to remove: ${response.status}`);
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
const removeRes = response.resarray[response.resarray.length - 1];
|
|
451
|
+
if (removeRes.status !== 0) {
|
|
452
|
+
if (!force)
|
|
453
|
+
throw new Error(`Failed to remove: ${removeRes.status}`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch (err) {
|
|
457
|
+
if (!force)
|
|
458
|
+
throw err;
|
|
459
|
+
}
|
|
460
|
+
};
|
|
461
|
+
this.access = async (path, mode = 0) => {
|
|
462
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
463
|
+
const parts = this.parsePath(pathStr);
|
|
464
|
+
const operations = this.navigateToPath(parts);
|
|
465
|
+
let accessMask = 0;
|
|
466
|
+
if (mode === 0) {
|
|
467
|
+
accessMask = 1;
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
if (mode & 4)
|
|
471
|
+
accessMask |= 1;
|
|
472
|
+
if (mode & 2)
|
|
473
|
+
accessMask |= 4;
|
|
474
|
+
if (mode & 1)
|
|
475
|
+
accessMask |= 32;
|
|
476
|
+
}
|
|
477
|
+
operations.push(builder_1.nfs.ACCESS(accessMask));
|
|
478
|
+
const response = await this.fs.compound(operations);
|
|
479
|
+
if (response.status !== 0) {
|
|
480
|
+
throw new Error(`Access denied: ${response.status}`);
|
|
481
|
+
}
|
|
482
|
+
const accessRes = response.resarray[response.resarray.length - 1];
|
|
483
|
+
if (accessRes.status !== 0) {
|
|
484
|
+
throw new Error(`Access denied: ${accessRes.status}`);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
this.rename = async (oldPath, newPath) => {
|
|
488
|
+
const oldPathStr = typeof oldPath === 'string' ? oldPath : oldPath.toString();
|
|
489
|
+
const newPathStr = typeof newPath === 'string' ? newPath : newPath.toString();
|
|
490
|
+
const oldParts = this.parsePath(oldPathStr);
|
|
491
|
+
const newParts = this.parsePath(newPathStr);
|
|
492
|
+
if (oldParts.length === 0 || newParts.length === 0) {
|
|
493
|
+
throw new Error('Cannot rename root directory');
|
|
494
|
+
}
|
|
495
|
+
const operations = [];
|
|
496
|
+
operations.push(builder_1.nfs.PUTROOTFH());
|
|
497
|
+
for (const part of oldParts.slice(0, -1)) {
|
|
498
|
+
operations.push(builder_1.nfs.LOOKUP(part));
|
|
499
|
+
}
|
|
500
|
+
operations.push(builder_1.nfs.SAVEFH());
|
|
501
|
+
operations.push(builder_1.nfs.PUTROOTFH());
|
|
502
|
+
for (const part of newParts.slice(0, -1)) {
|
|
503
|
+
operations.push(builder_1.nfs.LOOKUP(part));
|
|
504
|
+
}
|
|
505
|
+
const oldname = oldParts[oldParts.length - 1];
|
|
506
|
+
const newname = newParts[newParts.length - 1];
|
|
507
|
+
operations.push(builder_1.nfs.RENAME(oldname, newname));
|
|
508
|
+
const response = await this.fs.compound(operations);
|
|
509
|
+
if (response.status !== 0) {
|
|
510
|
+
throw new Error(`Failed to rename: ${response.status}`);
|
|
511
|
+
}
|
|
512
|
+
const renameRes = response.resarray[response.resarray.length - 1];
|
|
513
|
+
if (renameRes.status !== 0) {
|
|
514
|
+
throw new Error(`Failed to rename: ${renameRes.status}`);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
this.copyFile = async (src, dest, flags) => {
|
|
518
|
+
const data = await this.readFile(src);
|
|
519
|
+
await this.writeFile(dest, data);
|
|
520
|
+
};
|
|
521
|
+
this.realpath = async (path, options) => {
|
|
522
|
+
const encoding = typeof options === 'string' ? options : options?.encoding;
|
|
523
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
524
|
+
const normalized = '/' + this.parsePath(pathStr).join('/');
|
|
525
|
+
if (!encoding || encoding === 'utf8') {
|
|
526
|
+
return normalized;
|
|
527
|
+
}
|
|
528
|
+
return Buffer.from(normalized, 'utf8');
|
|
529
|
+
};
|
|
530
|
+
this.link = async (existingPath, newPath) => {
|
|
531
|
+
const existingPathStr = typeof existingPath === 'string' ? existingPath : existingPath.toString();
|
|
532
|
+
const newPathStr = typeof newPath === 'string' ? newPath : newPath.toString();
|
|
533
|
+
const existingParts = this.parsePath(existingPathStr);
|
|
534
|
+
const newParts = this.parsePath(newPathStr);
|
|
535
|
+
if (newParts.length === 0) {
|
|
536
|
+
throw new Error('Cannot create link at root');
|
|
537
|
+
}
|
|
538
|
+
const operations = this.navigateToPath(existingParts);
|
|
539
|
+
operations.push(builder_1.nfs.SAVEFH());
|
|
540
|
+
operations.push(builder_1.nfs.PUTROOTFH());
|
|
541
|
+
for (const part of newParts.slice(0, -1)) {
|
|
542
|
+
operations.push(builder_1.nfs.LOOKUP(part));
|
|
543
|
+
}
|
|
544
|
+
const newname = newParts[newParts.length - 1];
|
|
545
|
+
operations.push(builder_1.nfs.LINK(newname));
|
|
546
|
+
const response = await this.fs.compound(operations);
|
|
547
|
+
if (response.status !== 0) {
|
|
548
|
+
throw new Error(`Failed to create link: ${response.status}`);
|
|
549
|
+
}
|
|
550
|
+
const linkRes = response.resarray[response.resarray.length - 1];
|
|
551
|
+
if (linkRes.status !== 0) {
|
|
552
|
+
throw new Error(`Failed to create link: ${linkRes.status}`);
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
this.symlink = async (target, path, type) => {
|
|
556
|
+
const targetStr = typeof target === 'string' ? target : target.toString();
|
|
557
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
558
|
+
const parts = this.parsePath(pathStr);
|
|
559
|
+
if (parts.length === 0) {
|
|
560
|
+
throw new Error('Cannot create symlink at root');
|
|
561
|
+
}
|
|
562
|
+
const operations = this.navigateToParent(parts);
|
|
563
|
+
const linkname = parts[parts.length - 1];
|
|
564
|
+
const createType = new structs.Nfsv4CreateType(5, new structs.Nfsv4CreateTypeLink(targetStr));
|
|
565
|
+
const emptyAttrs = builder_1.nfs.Fattr([], new Uint8Array(0));
|
|
566
|
+
operations.push(builder_1.nfs.CREATE(createType, linkname, emptyAttrs));
|
|
567
|
+
const response = await this.fs.compound(operations);
|
|
568
|
+
if (response.status !== 0) {
|
|
569
|
+
throw new Error(`Failed to create symlink: ${response.status}`);
|
|
570
|
+
}
|
|
571
|
+
const createRes = response.resarray[response.resarray.length - 1];
|
|
572
|
+
if (createRes.status !== 0) {
|
|
573
|
+
throw new Error(`Failed to create symlink: ${createRes.status}`);
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
this.utimes = async (path, atime, mtime) => {
|
|
577
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
578
|
+
const parts = this.parsePath(pathStr);
|
|
579
|
+
const operations = this.navigateToPath(parts);
|
|
580
|
+
const atimeMs = typeof atime === 'number' ? atime : atime instanceof Date ? atime.getTime() : Date.now();
|
|
581
|
+
const mtimeMs = typeof mtime === 'number' ? mtime : mtime instanceof Date ? mtime.getTime() : Date.now();
|
|
582
|
+
const writer = new Writer_1.Writer(64);
|
|
583
|
+
const xdr = new XdrEncoder_1.XdrEncoder(writer);
|
|
584
|
+
xdr.writeUnsignedInt(1);
|
|
585
|
+
xdr.writeHyper(BigInt(Math.floor(atimeMs / 1000)));
|
|
586
|
+
xdr.writeUnsignedInt((atimeMs % 1000) * 1000000);
|
|
587
|
+
xdr.writeUnsignedInt(1);
|
|
588
|
+
xdr.writeHyper(BigInt(Math.floor(mtimeMs / 1000)));
|
|
589
|
+
xdr.writeUnsignedInt((mtimeMs % 1000) * 1000000);
|
|
590
|
+
const attrVals = writer.flush();
|
|
591
|
+
const timeAttrs = builder_1.nfs.Fattr([48, 54], attrVals);
|
|
592
|
+
const stateid = builder_1.nfs.Stateid(0, new Uint8Array(12));
|
|
593
|
+
operations.push(builder_1.nfs.SETATTR(stateid, timeAttrs));
|
|
594
|
+
const response = await this.fs.compound(operations);
|
|
595
|
+
if (response.status !== 0) {
|
|
596
|
+
throw new Error(`Failed to set times: ${response.status}`);
|
|
597
|
+
}
|
|
598
|
+
const setattrRes = response.resarray[response.resarray.length - 1];
|
|
599
|
+
if (setattrRes.status !== 0) {
|
|
600
|
+
throw new Error(`Failed to set times: ${setattrRes.status}`);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
this.readlink = async (path, options) => {
|
|
604
|
+
const encoding = typeof options === 'string' ? options : options?.encoding;
|
|
605
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
606
|
+
const parts = this.parsePath(pathStr);
|
|
607
|
+
const operations = this.navigateToPath(parts);
|
|
608
|
+
operations.push(builder_1.nfs.READLINK());
|
|
609
|
+
const response = await this.fs.compound(operations);
|
|
610
|
+
if (response.status !== 0) {
|
|
611
|
+
throw new Error(`Failed to read link: ${response.status}`);
|
|
612
|
+
}
|
|
613
|
+
const readlinkRes = response.resarray[response.resarray.length - 1];
|
|
614
|
+
if (readlinkRes.status !== 0 || !readlinkRes.resok) {
|
|
615
|
+
throw new Error(`Failed to read link: ${readlinkRes.status}`);
|
|
616
|
+
}
|
|
617
|
+
if (!encoding || encoding === 'utf8') {
|
|
618
|
+
return readlinkRes.resok.link;
|
|
619
|
+
}
|
|
620
|
+
return Buffer.from(readlinkRes.resok.link, 'utf8');
|
|
621
|
+
};
|
|
622
|
+
this.opendir = async (path, options) => {
|
|
623
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
624
|
+
const parts = this.parsePath(pathStr);
|
|
625
|
+
const operations = this.navigateToPath(parts);
|
|
626
|
+
return new NfsFsDir_1.NfsFsDir(pathStr, this.fs, operations);
|
|
627
|
+
};
|
|
628
|
+
this.mkdtemp = async (prefix, options) => {
|
|
629
|
+
const encoding = typeof options === 'string' ? options : options?.encoding;
|
|
630
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
631
|
+
const dirName = prefix + randomSuffix;
|
|
632
|
+
await this.mkdir(dirName);
|
|
633
|
+
if (!encoding || encoding === 'utf8')
|
|
634
|
+
return dirName;
|
|
635
|
+
return Buffer.from(dirName, 'utf8');
|
|
636
|
+
};
|
|
637
|
+
this.chmod = async (path, mode) => {
|
|
638
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
639
|
+
const parts = this.parsePath(pathStr);
|
|
640
|
+
const operations = this.navigateToPath(parts);
|
|
641
|
+
const modeValue = typeof mode === 'number' ? mode : parseInt(mode.toString(), 8);
|
|
642
|
+
const writer = new Writer_1.Writer(8);
|
|
643
|
+
const xdr = new XdrEncoder_1.XdrEncoder(writer);
|
|
644
|
+
xdr.writeUnsignedInt(modeValue);
|
|
645
|
+
const attrVals = writer.flush();
|
|
646
|
+
const attrs = builder_1.nfs.Fattr([33], attrVals);
|
|
647
|
+
const stateid = builder_1.nfs.Stateid(0, new Uint8Array(12));
|
|
648
|
+
operations.push(builder_1.nfs.SETATTR(stateid, attrs));
|
|
649
|
+
const response = await this.fs.compound(operations);
|
|
650
|
+
if (response.status !== 0) {
|
|
651
|
+
throw new Error(`Failed to chmod: ${response.status}`);
|
|
652
|
+
}
|
|
653
|
+
const setattrRes = response.resarray[response.resarray.length - 1];
|
|
654
|
+
if (setattrRes.status !== 0) {
|
|
655
|
+
throw new Error(`Failed to chmod: ${setattrRes.status}`);
|
|
656
|
+
}
|
|
657
|
+
};
|
|
658
|
+
this.chown = async (path, uid, gid) => {
|
|
659
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
660
|
+
const parts = this.parsePath(pathStr);
|
|
661
|
+
const operations = this.navigateToPath(parts);
|
|
662
|
+
const writer = new Writer_1.Writer(64);
|
|
663
|
+
const xdr = new XdrEncoder_1.XdrEncoder(writer);
|
|
664
|
+
xdr.writeStr(uid.toString());
|
|
665
|
+
xdr.writeStr(gid.toString());
|
|
666
|
+
const attrVals = writer.flush();
|
|
667
|
+
const attrs = builder_1.nfs.Fattr([36, 37], attrVals);
|
|
668
|
+
const stateid = builder_1.nfs.Stateid(0, new Uint8Array(12));
|
|
669
|
+
operations.push(builder_1.nfs.SETATTR(stateid, attrs));
|
|
670
|
+
const response = await this.fs.compound(operations);
|
|
671
|
+
if (response.status !== 0) {
|
|
672
|
+
throw new Error(`Failed to chown: ${response.status}`);
|
|
673
|
+
}
|
|
674
|
+
const setattrRes = response.resarray[response.resarray.length - 1];
|
|
675
|
+
if (setattrRes.status !== 0) {
|
|
676
|
+
throw new Error(`Failed to chown: ${setattrRes.status}`);
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
this.lchmod = async (path, mode) => {
|
|
680
|
+
return this.chmod(path, mode);
|
|
681
|
+
};
|
|
682
|
+
this.lchown = async (path, uid, gid) => {
|
|
683
|
+
return this.chown(path, uid, gid);
|
|
684
|
+
};
|
|
685
|
+
this.lutimes = async (path, atime, mtime) => {
|
|
686
|
+
return this.utimes(path, atime, mtime);
|
|
687
|
+
};
|
|
688
|
+
this.open = async (path, flags, mode) => {
|
|
689
|
+
const pathStr = typeof path === 'string' ? path : path.toString();
|
|
690
|
+
const parts = this.parsePath(pathStr);
|
|
691
|
+
const operations = this.navigateToParent(parts);
|
|
692
|
+
const filename = parts[parts.length - 1];
|
|
693
|
+
const openOwner = this.createDefaultOpenOwner();
|
|
694
|
+
const claim = builder_1.nfs.OpenClaimNull(filename);
|
|
695
|
+
let access = 1;
|
|
696
|
+
const openSeqid = this.nextOpenOwnerSeqid(openOwner);
|
|
697
|
+
if (typeof flags === 'string') {
|
|
698
|
+
if (flags.includes('r') && flags.includes('+')) {
|
|
699
|
+
access = 3;
|
|
700
|
+
}
|
|
701
|
+
else if (flags.includes('w') || flags.includes('a')) {
|
|
702
|
+
access = 2;
|
|
703
|
+
if (flags.includes('+')) {
|
|
704
|
+
access = 3;
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
else if (typeof flags === 'number') {
|
|
709
|
+
const O_RDONLY = 0;
|
|
710
|
+
const O_WRONLY = 1;
|
|
711
|
+
const O_RDWR = 2;
|
|
712
|
+
const O_ACCMODE = 3;
|
|
713
|
+
const accessMode = flags & O_ACCMODE;
|
|
714
|
+
switch (accessMode) {
|
|
715
|
+
case O_RDONLY:
|
|
716
|
+
access = 1;
|
|
717
|
+
break;
|
|
718
|
+
case O_WRONLY:
|
|
719
|
+
access = 2;
|
|
720
|
+
break;
|
|
721
|
+
case O_RDWR:
|
|
722
|
+
access = 3;
|
|
723
|
+
break;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
operations.push(builder_1.nfs.OPEN(openSeqid, access, 0, openOwner, builder_1.nfs.OpenHowNoCreate(), claim));
|
|
727
|
+
const openResponse = await this.fs.compound(operations);
|
|
728
|
+
if (openResponse.status !== 0) {
|
|
729
|
+
throw new Error(`Failed to open file: ${openResponse.status}`);
|
|
730
|
+
}
|
|
731
|
+
const openRes = openResponse.resarray[openResponse.resarray.length - 1];
|
|
732
|
+
if (openRes.status !== 0 || !openRes.resok) {
|
|
733
|
+
throw new Error(`Failed to open file: ${openRes.status}`);
|
|
734
|
+
}
|
|
735
|
+
const stateid = openRes.resok.stateid;
|
|
736
|
+
const fd = Math.floor(Math.random() * 1000000);
|
|
737
|
+
return new NfsFsFileHandle_1.NfsFsFileHandle(fd, pathStr, this, stateid, openOwner);
|
|
738
|
+
};
|
|
739
|
+
this.statfs = (path, options) => {
|
|
740
|
+
throw new Error('Not implemented.');
|
|
741
|
+
};
|
|
742
|
+
this.watch = (filename, options) => {
|
|
743
|
+
throw new Error('Not implemented.');
|
|
744
|
+
};
|
|
745
|
+
this.glob = (pattern, options) => {
|
|
746
|
+
throw new Error('Not implemented.');
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
makeOpenOwnerKey(owner) {
|
|
750
|
+
return `${owner.clientid}:${Buffer.from(owner.owner).toString('hex')}`;
|
|
751
|
+
}
|
|
752
|
+
nextOpenOwnerSeqid(owner) {
|
|
753
|
+
const key = this.makeOpenOwnerKey(owner);
|
|
754
|
+
const last = this.openOwnerSeqids.get(key);
|
|
755
|
+
const next = last === undefined ? 0 : last === 0xffffffff ? 1 : (last + 1) >>> 0;
|
|
756
|
+
this.openOwnerSeqids.set(key, next);
|
|
757
|
+
return next;
|
|
758
|
+
}
|
|
759
|
+
createDefaultOpenOwner() {
|
|
760
|
+
return builder_1.nfs.OpenOwner(BigInt(1), new Uint8Array(this.defaultOpenOwnerId));
|
|
761
|
+
}
|
|
762
|
+
attrNumsToBitmap(attrNums) {
|
|
763
|
+
const bitmap = [];
|
|
764
|
+
for (const attrNum of attrNums) {
|
|
765
|
+
const wordIndex = Math.floor(attrNum / 32);
|
|
766
|
+
const bitIndex = attrNum % 32;
|
|
767
|
+
while (bitmap.length <= wordIndex) {
|
|
768
|
+
bitmap.push(0);
|
|
769
|
+
}
|
|
770
|
+
bitmap[wordIndex] |= 1 << bitIndex;
|
|
771
|
+
}
|
|
772
|
+
return bitmap;
|
|
773
|
+
}
|
|
774
|
+
parsePath(path) {
|
|
775
|
+
const normalized = path.replace(/^\/+/, '').replace(/\/+$/, '');
|
|
776
|
+
if (!normalized)
|
|
777
|
+
return [];
|
|
778
|
+
return normalized.split('/').filter((part) => part.length > 0);
|
|
779
|
+
}
|
|
780
|
+
navigateToParent(parts) {
|
|
781
|
+
const operations = [builder_1.nfs.PUTROOTFH()];
|
|
782
|
+
for (const part of parts.slice(0, -1)) {
|
|
783
|
+
operations.push(builder_1.nfs.LOOKUP(part));
|
|
784
|
+
}
|
|
785
|
+
return operations;
|
|
786
|
+
}
|
|
787
|
+
navigateToPath(parts) {
|
|
788
|
+
const operations = [builder_1.nfs.PUTROOTFH()];
|
|
789
|
+
for (const part of parts) {
|
|
790
|
+
operations.push(builder_1.nfs.LOOKUP(part));
|
|
791
|
+
}
|
|
792
|
+
return operations;
|
|
793
|
+
}
|
|
794
|
+
encodeData(data) {
|
|
795
|
+
if (data instanceof Uint8Array)
|
|
796
|
+
return data;
|
|
797
|
+
if (data instanceof ArrayBuffer)
|
|
798
|
+
return new Uint8Array(data);
|
|
799
|
+
if (typeof data === 'string')
|
|
800
|
+
return new TextEncoder().encode(data);
|
|
801
|
+
if (Buffer.isBuffer(data))
|
|
802
|
+
return new Uint8Array(data);
|
|
803
|
+
throw new Error('Unsupported data type');
|
|
804
|
+
}
|
|
805
|
+
decodeData(data, encoding) {
|
|
806
|
+
if (!encoding || encoding === 'buffer')
|
|
807
|
+
return Buffer.from(data);
|
|
808
|
+
return new TextDecoder(encoding).decode(data);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
exports.Nfsv4FsClient = Nfsv4FsClient;
|
|
812
|
+
//# sourceMappingURL=Nfsv4FsClient.js.map
|