Skip to content

Commit 3f4eea4

Browse files
committed
feat: 🎸 implement locking operations
1 parent 606e00c commit 3f4eea4

File tree

4 files changed

+330
-45
lines changed

4 files changed

+330
-45
lines changed

src/nfs/v4/builder.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,67 @@ export const nfs = {
269269
return new msg.Nfsv4OpenattrRequest(createdir);
270270
},
271271

272+
/**
273+
* SECINFO - Get security information for a file.
274+
* @param name - Filename to get security info for
275+
*/
276+
SECINFO(name: string): msg.Nfsv4SecinfoRequest {
277+
return new msg.Nfsv4SecinfoRequest(name);
278+
},
279+
280+
/**
281+
* DELEGPURGE - Purge delegations (not supported).
282+
* @param clientid - Client ID
283+
*/
284+
DELEGPURGE(clientid: bigint): msg.Nfsv4DelegpurgeRequest {
285+
return new msg.Nfsv4DelegpurgeRequest(clientid);
286+
},
287+
288+
/**
289+
* DELEGRETURN - Return delegation (not supported).
290+
* @param stateid - Delegation stateid
291+
*/
292+
DELEGRETURN(stateid: structs.Nfsv4Stateid): msg.Nfsv4DelegreturnRequest {
293+
return new msg.Nfsv4DelegreturnRequest(stateid);
294+
},
295+
296+
/**
297+
* LOCKT - Test for conflicting lock (non-blocking).
298+
* @param locktype - Lock type (READ_LT or WRITE_LT)
299+
* @param offset - Starting byte offset
300+
* @param length - Length in bytes (0xFFFFFFFFFFFFFFFF for EOF)
301+
* @param owner - Lock owner
302+
*/
303+
LOCKT(locktype: number, offset: bigint, length: bigint, owner: structs.Nfsv4LockOwner): msg.Nfsv4LocktRequest {
304+
return new msg.Nfsv4LocktRequest(locktype, offset, length, owner);
305+
},
306+
307+
/**
308+
* LOCKU - Unlock byte range.
309+
* @param locktype - Lock type (READ_LT or WRITE_LT)
310+
* @param seqid - Sequence number
311+
* @param lockStateid - Lock stateid from LOCK operation
312+
* @param offset - Starting byte offset
313+
* @param length - Length in bytes
314+
*/
315+
LOCKU(
316+
locktype: number,
317+
seqid: number,
318+
lockStateid: structs.Nfsv4Stateid,
319+
offset: bigint,
320+
length: bigint,
321+
): msg.Nfsv4LockuRequest {
322+
return new msg.Nfsv4LockuRequest(locktype, seqid, lockStateid, offset, length);
323+
},
324+
325+
/**
326+
* RELEASE_LOCKOWNER - Release all locks for a lock-owner.
327+
* @param lockOwner - Lock owner to release
328+
*/
329+
RELEASE_LOCKOWNER(lockOwner: structs.Nfsv4LockOwner): msg.Nfsv4ReleaseLockOwnerRequest {
330+
return new msg.Nfsv4ReleaseLockOwnerRequest(lockOwner);
331+
},
332+
272333
/**
273334
* Create an Nfsv4Verifier (8-byte opaque data).
274335
* @param data - 8-byte Uint8Array, defaults to zeros
@@ -340,4 +401,13 @@ export const nfs = {
340401
OpenClaimNull(filename: string): structs.Nfsv4OpenClaim {
341402
return new structs.Nfsv4OpenClaim(0, new structs.Nfsv4OpenClaimNull(filename));
342403
},
404+
405+
/**
406+
* Create Nfsv4LockOwner (lock owner identifier).
407+
* @param clientid - Client ID
408+
* @param owner - Owner bytes (unique identifier)
409+
*/
410+
LockOwner(clientid: bigint, owner: Uint8Array): structs.Nfsv4LockOwner {
411+
return new structs.Nfsv4LockOwner(clientid, owner);
412+
},
343413
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type * as struct from '../../structs';
2+
3+
/**
4+
* Byte-range lock record for NFSv4 LOCK operations.
5+
* Represents a single byte-range lock held by a lock-owner on a file.
6+
*/
7+
export class ByteRangeLock {
8+
constructor(
9+
/**
10+
* Stateid associated with this lock.
11+
* Used by client to identify this lock in subsequent operations (LOCKU, etc.).
12+
*/
13+
public readonly stateid: struct.Nfsv4Stateid,
14+
15+
/**
16+
* Absolute file system path of the locked file.
17+
* Used to identify which file this lock applies to.
18+
*/
19+
public readonly path: string,
20+
21+
/**
22+
* Lock type - READ or WRITE lock.
23+
* READ locks (shared) can coexist with other READ locks.
24+
* WRITE locks (exclusive) conflict with all other locks.
25+
*/
26+
public readonly locktype: number,
27+
28+
/**
29+
* Starting byte offset of the locked range.
30+
* 0-based offset from start of file.
31+
*/
32+
public readonly offset: bigint,
33+
34+
/**
35+
* Length of the locked range in bytes.
36+
* Special value 0xFFFFFFFFFFFFFFFF means "to end of file".
37+
*/
38+
public readonly length: bigint,
39+
40+
/**
41+
* Key identifying the lock-owner that holds this lock.
42+
* Format: `${clientid}:${base64(owner)}`.
43+
* Links this lock back to the owner for cleanup and conflict checking.
44+
*/
45+
public readonly lockOwnerKey: string,
46+
) {}
47+
48+
/**
49+
* Check if this lock overlaps with a given byte range.
50+
* @param offset - Start offset to check
51+
* @param length - Length to check
52+
* @returns true if ranges overlap
53+
*/
54+
public overlaps(offset: bigint, length: bigint): boolean {
55+
const MAX_UINT64 = BigInt('0xFFFFFFFFFFFFFFFF');
56+
const thisEnd = this.length === MAX_UINT64 ? MAX_UINT64 : this.offset + this.length;
57+
const otherEnd = length === MAX_UINT64 ? MAX_UINT64 : offset + length;
58+
return this.offset < otherEnd && offset < thisEnd;
59+
}
60+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Lock-owner state record for NFSv4 LOCK operations.
3+
* A lock-owner represents a single entity (process, thread) on a client
4+
* that can acquire byte-range locks on files. Tracks all locks held by this owner.
5+
*/
6+
export class LockOwnerState {
7+
constructor(
8+
/**
9+
* Client ID that owns this lock-owner.
10+
* Links the owner back to the specific NFS client that created it.
11+
*/
12+
public readonly clientid: bigint,
13+
14+
/**
15+
* Opaque lock-owner identifier provided by the client.
16+
* Typically represents a process or thread ID on the client.
17+
* Combined with clientid, uniquely identifies this lock-owner.
18+
*/
19+
public readonly owner: Uint8Array,
20+
21+
/**
22+
* Sequence number for operations from this lock-owner.
23+
* Used to serialize LOCK/LOCKU operations.
24+
* Incremented after each successful stateful operation.
25+
* Server rejects operations with incorrect sequence numbers to prevent replays.
26+
*/
27+
public seqid: number,
28+
29+
/**
30+
* Set of lock keys for all byte-range locks currently held by this owner.
31+
* Format: lock keys are `${stateid}:${offset}:${length}`.
32+
* Used to track all active locks and clean them up if the owner goes away.
33+
*/
34+
public readonly locks: Set<string> = new Set(),
35+
) {}
36+
}

0 commit comments

Comments
 (0)