@@ -22,6 +22,7 @@ import {OpenFileState} from '../OpenFileState';
2222import { OpenOwnerState } from '../OpenOwnerState' ;
2323import { LockOwnerState } from '../LockOwnerState' ;
2424import { ByteRangeLock } from '../ByteRangeLock' ;
25+ import { LockStateid } from '../LockStateid' ;
2526import { FilesystemStats } from '../FilesystemStats' ;
2627import { FileHandleMapper , ROOT_FH } from './fh' ;
2728import { isErrCode , normalizeNodeFsError } from './util' ;
@@ -103,6 +104,8 @@ export class Nfsv4OperationsNode implements Nfsv4Operations {
103104 protected locks : Map < string , ByteRangeLock > = new Map ( ) ;
104105 /** Map from lock-owner key to lock-owner state. */
105106 protected lockOwners : Map < string , LockOwnerState > = new Map ( ) ;
107+ /** Map from lock stateid 'other' field to lock stateid state. Per RFC 7530, one stateid per lock-owner per file. */
108+ protected lockStateids : Map < string , LockStateid > = new Map ( ) ;
106109
107110 /**
108111 * Server-wide monotonic change counter for directory change_info.
@@ -246,6 +249,28 @@ export class Nfsv4OperationsNode implements Nfsv4Operations {
246249 return `${ this . makeStateidKey ( stateid ) } :${ offset } :${ length } ` ;
247250 }
248251
252+ protected makeLockStateidKey ( lockOwnerKey : string , path : string ) : string {
253+ return `${ lockOwnerKey } :${ path } ` ;
254+ }
255+
256+ protected getOrCreateLockStateid ( lockOwnerKey : string , path : string ) : LockStateid {
257+ const key = this . makeLockStateidKey ( lockOwnerKey , path ) ;
258+ let lockStateid = this . lockStateids . get ( key ) ;
259+ if ( ! lockStateid ) {
260+ const other = randomBytes ( 12 ) ;
261+ lockStateid = new LockStateid ( other , 1 , lockOwnerKey , path ) ;
262+ this . lockStateids . set ( key , lockStateid ) ;
263+ const otherKey = Buffer . from ( other ) . toString ( 'hex' ) ;
264+ this . lockStateids . set ( otherKey , lockStateid ) ;
265+ }
266+ return lockStateid ;
267+ }
268+
269+ protected findLockStateidByOther ( other : Uint8Array ) : LockStateid | undefined {
270+ const otherKey = Buffer . from ( other ) . toString ( 'hex' ) ;
271+ return this . lockStateids . get ( otherKey ) ;
272+ }
273+
249274 protected hasConflictingLock (
250275 path : string ,
251276 locktype : number ,
@@ -950,7 +975,8 @@ export class Nfsv4OperationsNode implements Nfsv4Operations {
950975 const denied = new msg . Nfsv4LockResDenied ( offset , length , locktype , conflictOwner ) ;
951976 return new msg . Nfsv4LockResponse ( Nfsv4Stat . NFS4ERR_LOCKED , undefined , denied ) ;
952977 }
953- const stateid = this . createStateid ( ) ;
978+ const lockStateid = this . getOrCreateLockStateid ( existingLockOwnerKey , currentPath ) ;
979+ const stateid = lockStateid . incrementAndGetStateid ( ) ;
954980 const lock = new ByteRangeLock ( stateid , currentPath , locktype , offset , length , existingLockOwnerKey ) ;
955981 const lockKey = this . makeLockKey ( stateid , offset , length ) ;
956982 this . locks . set ( lockKey , lock ) ;
@@ -1027,7 +1053,8 @@ export class Nfsv4OperationsNode implements Nfsv4Operations {
10271053 }
10281054 }
10291055 lockOwnerState . seqid = openToLock . lockSeqid ;
1030- const stateid = this . createStateid ( ) ;
1056+ const lockStateid = this . getOrCreateLockStateid ( lockOwnerKey , currentPath ) ;
1057+ const stateid = lockStateid . incrementAndGetStateid ( ) ;
10311058 const lock = new ByteRangeLock ( stateid , currentPath , locktype , offset , length , lockOwnerKey ) ;
10321059 const lockKey = this . makeLockKey ( stateid , offset , length ) ;
10331060 this . locks . set ( lockKey , lock ) ;
@@ -1053,43 +1080,16 @@ export class Nfsv4OperationsNode implements Nfsv4Operations {
10531080
10541081 public async LOCKU ( request : msg . Nfsv4LockuRequest , ctx : Nfsv4OperationCtx ) : Promise < msg . Nfsv4LockuResponse > {
10551082 const { lockStateid, offset, length, seqid} = request ;
1056- const lockKey = this . makeLockKey ( lockStateid , offset , length ) ;
1057- const stateidKey = this . makeStateidKey ( lockStateid ) ;
1058- const lock = this . locks . get ( lockKey ) ;
1059- let ownerKey : string | undefined ;
1060- let lockOwnerState : LockOwnerState | undefined ;
1061- if ( lock ) {
1062- ownerKey = lock . lockOwnerKey ;
1063- lockOwnerState = this . lockOwners . get ( ownerKey ) ;
1064- if ( ! lockOwnerState ) throw Nfsv4Stat . NFS4ERR_BAD_STATEID ;
1065- this . renewClientLease ( lockOwnerState . clientid ) ;
1066- } else {
1067- const suffix = `:${ stateidKey } :${ offset . toString ( ) } :${ length . toString ( ) } :${ seqid } ` ;
1068- for ( const [ candidateKey , candidateState ] of this . lockOwners . entries ( ) ) {
1069- const lastKey = candidateState . lastRequestKey ;
1070- if ( lastKey && lastKey . startsWith ( 'LOCKU:' ) && lastKey . endsWith ( suffix ) ) {
1071- lockOwnerState = candidateState ;
1072- ownerKey = candidateKey ;
1073- break ;
1074- }
1075- }
1076- if ( ! lockOwnerState ) throw Nfsv4Stat . NFS4ERR_BAD_STATEID ;
1077- this . renewClientLease ( lockOwnerState . clientid ) ;
1078- }
1079- const requestKey = this . makeLockuRequestKey ( ownerKey ! , lockStateid , offset , length , seqid ) ;
1083+ const lockStateidState = this . findLockStateidByOther ( lockStateid . other ) ;
1084+ if ( ! lockStateidState ) throw Nfsv4Stat . NFS4ERR_BAD_STATEID ;
1085+ const ownerKey = lockStateidState . lockOwnerKey ;
1086+ const lockOwnerState = this . lockOwners . get ( ownerKey ) ;
1087+ if ( ! lockOwnerState ) throw Nfsv4Stat . NFS4ERR_BAD_STATEID ;
1088+ this . renewClientLease ( lockOwnerState . clientid ) ;
1089+ const currentPath = this . fh . currentPath ( ctx ) ;
1090+ if ( lockStateidState . path !== currentPath ) throw Nfsv4Stat . NFS4ERR_BAD_STATEID ;
1091+ const requestKey = this . makeLockuRequestKey ( ownerKey , lockStateid , offset , length , seqid ) ;
10801092 const seqidValidation = this . validateSeqid ( seqid , lockOwnerState . seqid ) ;
1081- if ( ! lock ) {
1082- if ( seqidValidation === 'replay' ) {
1083- if ( lockOwnerState . lastRequestKey === requestKey && lockOwnerState . lastResponse ) {
1084- return lockOwnerState . lastResponse ;
1085- }
1086- throw Nfsv4Stat . NFS4ERR_BAD_SEQID ;
1087- }
1088- if ( seqidValidation === 'invalid' ) {
1089- throw Nfsv4Stat . NFS4ERR_BAD_SEQID ;
1090- }
1091- throw Nfsv4Stat . NFS4ERR_BAD_STATEID ;
1092- }
10931093 if ( seqidValidation === 'invalid' ) {
10941094 throw Nfsv4Stat . NFS4ERR_BAD_SEQID ;
10951095 }
@@ -1100,9 +1100,13 @@ export class Nfsv4OperationsNode implements Nfsv4Operations {
11001100 throw Nfsv4Stat . NFS4ERR_BAD_SEQID ;
11011101 }
11021102 lockOwnerState . seqid = seqid ;
1103- this . locks . delete ( lockKey ) ;
1104- lockOwnerState . locks . delete ( lockKey ) ;
1105- const stateid = this . createStateid ( ) ;
1103+ const lockKey = this . makeLockKey ( lockStateid , offset , length ) ;
1104+ const lock = this . locks . get ( lockKey ) ;
1105+ if ( lock ) {
1106+ this . locks . delete ( lockKey ) ;
1107+ lockOwnerState . locks . delete ( lockKey ) ;
1108+ }
1109+ const stateid = lockStateidState . incrementAndGetStateid ( ) ;
11061110 const resok = new msg . Nfsv4LockuResOk ( stateid ) ;
11071111 const response = new msg . Nfsv4LockuResponse ( Nfsv4Stat . NFS4_OK , resok ) ;
11081112 lockOwnerState . lastResponse = response ;
0 commit comments