{$X+,B-,V-} {essential compiler directives} UNIT nwLock; { nwLock unit as of 950301 / NwTP 0.6 API. (c) 1993,1995, R. Spronk This unit was based on units by a. Scott A. Lewis, 36 Maythorpe Drive, Windsor, CT 06095, U.S.A. Note: (1987) 76515,135@Compuserve.Com b. Erik van Heyningen, Hague Consulting Group, The Hague, the Netherlands. Note: (1994) hcg@hacktick.nl } { Function: Interrupt: Notes: Physical File locking/unlocking ------------------------------- * LogPhysicalFile EB (6) -> F203 * LockPhysicalFileSet F204 * ReleasePhysicalFile EC -> F205 * ReleasePhysicalFileSet CD -> F206 * ClearPhysicalFile ED (6) -> F207 * ClearPhysicalFileSet CF -> F208 Logical File Locking -------------------- + LogLogicalFile (5) + LogLogicalFileSet (5) + ReleaseLogicalFile (5) + ReleaseLogicalFileSet (5) + ClearLogicalFile (5) + ClearLogicalFileSet (5) Logical record locking/unlocking -------------------------------- * LogLogicalRecord D0 -> F209 * LockLogicalRecordSet D1 -> F20A * ReleaseLogicalRecord D2 -> F20C * ReleaseLogicalRecordSet D3 -> F20D * ClearLogicalRecord D4 -> F20B * ClearLogicalRecordSet D5 -> F20E GetLogicalRecordInformation F217/F0 (3) GetLogicalRecordsByConnection F217/EF (3) Physical record locking/unlocking --------------------------------- . LogPhysicalRecord BC -> F21A . LockPhysicalRecordSet C2 -> F21B . ReleasePhysicalRecord BD -> F21C . ReleasePhysicalRecordSet C3 -> F21D . ClearPhysicalRecord BE -> F21E . ClearPhysicalRecordSet C4 -> F21F GetPhysRecLocksByConnectionAndFile F217/ED (3) GetPhysRecLocksByFile F217/EE (3) - ControlRecordAccess 5C (DOS) (4) Not Implemented --------------- - GetLockMode C600 (1) - SetLockMode C601 (1) - BeginLogicalFileLocking C8 / F201 (2) - EndLogicalFileLocking C9 / F202 (2) Notes: -Semaphores can be found in the nwSema Unit (1) Obsolete (2) Not supported by (all) 3.x versions (3) Supported by NW 3.x and upwards (4) Generic physical record locking call, DOS 3.1+ Equivalent to: I . LockPhysicalRecord (without logging) II. ReleasePhysicalrecord (5) Use the equivalent LogicalRecordLocking calls to emulate LogicalFileLocking. NOTE: remember that there's only ONE Log. (6) Includes VLM fix for filenames (GetTrueEntryName in the nwFile unit is called) -> F2xx To be rewritten to the F2 interface. } INTERFACE Uses nwIntr,nwMisc; CONST { Log Resource } LD_LOG = 0; LD_LOG_LOCK = 1; { Deny all access to file/record } LD_LOG_LOCK_RO = 3; { Allow read / deny write (record locking only)} { Lock Resource } LD_lOCK = 0; { Deny all access to file/record } LD_LOCK_RO = 1; { Allow read / deny write (record locking only)} Var Result:word; {------------------- PHYSICAL FILE LOCKING OPERATIONS -----------------------} {F204 [2.15c+]} FUNCTION LockPhysicalFileSet(TimeoutLimit : Word) : Boolean; {Lock a set of files that were logged by the LogFile function } {CD.. [1.0+]} FUNCTION ReleasePhysicalFileSet:boolean; { Release lock on set of files in logged table, files remain logged } {CF [1.0+]} FUNCTION ClearPhysicalFileSet : Boolean; { Unlock and UnLog the entire logged file set } {EB.. [1.0+]} FUNCTION LogPhysicalFile(FileName : String; LockDirective : Byte; TimeoutLimit : Word) : Boolean; {Log files for later use } {EC.. [1.0+]} FUNCTION ReleasePhysicalFile(FileName : String) : boolean; {Release file lock, but keep logged in the table } {ED.. [1.0+]} FUNCTION ClearPhysicalFile(FileName : String) : boolean; {Release a file from the file log table, unlock the file if it is locked } { ------------------- LOGICAL RECORD LOCKING OPERATIONS --------------------} {D0 [1.0+]} FUNCTION LogLogicalRecord(Name:string; LockDirective:Byte; Timeout: Word) : Boolean; {Add a record to the lockable logical record table } {D1.. [1.0+]} FUNCTION LockLogicalRecordSet(LockDirective:Byte; TimeoutLimit : Word) : Boolean; {Lock all logged records } {D2.. [1.0+]} FUNCTION ReleaseLogicalRecord(Name : String) : Boolean; {Unlock a record, keep record in logtable } {D3.. [1.0+]} FUNCTION ReleaseLogicalRecordSet : Boolean; {Unlock all locked records, keep records logged } {D4.. [1.0+]} FUNCTION ClearLogicalRecord(Name : String) : Boolean; {Unlock and UnLog a record } {D5.. [1.0+]} FUNCTION ClearLogicalRecordSet : Boolean; {Unlocks and UnLogs all logged records } {F217/EF [2.1x+]} Function GetLogicalRecordLocksByConnection(ConnNbr:word; {i/o} Var NextRecNbr:word; Var TaskNbr:word; Var LockStatus:Byte; Var LockName:String):Boolean; { You need console operator rights to use this function } {----------------------- PHYSICAL RECORD LOCKING OPERATION -----------------} {BC.. [1.0+]} function LogPhysicalRecord(Handle:Word; LockDirective:Byte; RecordOffset,RecordLength:Longint; TimeOutLimit:Word): boolean; {Add a record to the lockable physical record logtable } {BD.. [1.0+]} function ReleasePhysicalRecord( Handle:Word; RecordOffset,RecordLength:Longint) : boolean; {Unlock record, keep record logged } {BE.. [1.0+]} function ClearPhysicalRecord(Handle:Word; RecordOffset,RecordLength:Longint): boolean; {Unlock and Unlog a record } {C2.. [1.0+]} function LockPhysicalRecordSet(LockDirective: byte; TimeoutLimit : Word): boolean; {Lock all logged records } {C3.. [1.0+]} function ReleasePhysicalRecordSet : boolean; {Unlock all logged records, keep records logged } {C4.. [1.0+]} function ClearPhysicalRecordSet : boolean; {Unlocks and unLogs all logged records } IMPLEMENTATION{==============================================================} uses nwFile; Var regs:TTRegisters; Procedure SetLockMode(mode:Byte); begin regs.AH:=$c6; regs.al:=mode; { 0 or 1 } RealModeIntr($21,regs); end; (* THE FOLLOWING PROCEDURES ARE FOR LOGGING AND LOCKING/RELEASING FILE SETS *) (* File locking by set can be very effective in avoiding deadly embrace *) {F204 [3.x+]} FUNCTION LockPhysicalFileSet(TimeoutLimit : Word) : Boolean; Type Treq=record _TimeOutLimit:Word; end; TPreq=^Treq; BEGIN With TPreq(GlobalReqBuf)^ do begin _TimeoutLimit:=swap(TimeoutLimit); end; F2SystemCall($04,SizeOf(Treq),0,result); LockPhysicalFileSet:=(result=0); { 00 Successful FF Fail FE Timeout } END; {CD.. [1.0+]} FUNCTION ReleasePhysicalFileSet:boolean; { Release lock on set of files in logged table, files remain logged } { These files remain open but cannot be accessed without an error } { To reuse them, send another lock file set } Type Treq=record end; BEGIN WITH Regs DO BEGIN AH := $CD; RealModeIntr($21,Regs); result:=0; END; ReleasePhysicalFileSet:=true; END; {CF [2.0+]} FUNCTION ClearPhysicalFileSet : Boolean; { Unlock and UnLog the entire personal file set (all files are closed) } BEGIN WITH Regs DO BEGIN AH := $CF; RealModeIntr($21,Regs); result:=0; END; ClearPhysicalFileSet:=true; END; {EB.. [2.0+] } FUNCTION LogPhysicalFile(FileName : String; LockDirective : Byte; TimeoutLimit : Word) : Boolean; { This function allows a station to log files for later personal use } { After the desired files are logged, function CBh can be used to lock } { the entire set of files } { !! There is a known problem with lock directive 3 (log and lock shareable) use 1 instead. } Type Treq=record LockDirective:Byte; TimeOutLimit:Word; FileName:string[255]; { or Asciiz ? } end; Var temp1,temp2:word; TEname:string; BEGIN GetTrueEntryName(FileName,TEname); { also UpCases string } { IF this function isn't included and VLMs are used, this call will *appear* to be successful. No error code is returned, the call is however unsuccessful. } WITH Regs DO BEGIN AH := $EB; AL := LockDirective; { 0 = Log Only, 1 Log and Lock } BP := TimeoutLimit; { in 1/18 seconds, 0 = No wait } TEname := TEName+#0; { Terminate with a nul for asciiz } Move(TEname[1],GlobalReqBuf^,ord(TEname[0])); GetGlobalBufferAddress(DS,DX,temp1,temp2); { DS:DX real mode pointer to buffer in realmode-range holding Filename } RealModeIntr($21,Regs); Result:=AL; LogPhysicalFile := (Result = 0); END; { FE Timeout FF hardware error } END; {EC.. [1.0+]} FUNCTION ReleasePhysicalFile(FileName : String) : boolean; { Release file lock, but keep logged in the table } Var temp1,temp2:word; TEname:string; BEGIN GetTrueEntryName(FileName,TEname); { also UpCases string } { IF this function isn't included and VLMs are used, this call will *appear* to be successful. No error code is returned, the call is however unsuccessful. } WITH Regs DO BEGIN AH := $EC; UpString(FileName); TEName := TEName+#0; { null terminate } Move(TEname[1],GlobalReqBuf^,ord(TEname[0])); GetGlobalBufferAddress(DS,DX,temp1,temp2); { DS:DX real mode pointer to buffer in realmode-range holding Filename } RealModeIntr($21,Regs); result:=AL; ReleasePhysicalFile:=(result=0); END; {FF File not found } END; {ED.. [1.0+]} FUNCTION ClearPhysicalFile(FileName : String) : boolean; { Release a file from the file log table, unlock the file if it is locked } Var temp1,temp2:word; BEGIN WITH Regs DO BEGIN AH := $ED; UpString(FileName); FileName := FileName+#0; { null terminate } Move(Filename[1],GlobalReqBuf^,ord(Filename[0])); GetGlobalBufferAddress(DS,DX,temp1,temp2); { DS:DX real mode pointer to buffer in realmode-range holding Filename } RealModeIntr($21,Regs); Result:=AL; ClearPhysicalFile := (Result = 0); { 0 means OK FF File not found} END; END; (* THE FOLLOWING FUNCTIONS ARE FOR LOGICAL LOCKING OPERATIONS *) (* Logical locks work only if all software accessing the files use the *) (* same logical synchronization scheme. Logical locks are much easier *) (* and faster to implement than physical locks. *) {D0 [1.0+]} FUNCTION LogLogicalRecord(Name:String; LockDirective:Byte; Timeout: Word) : Boolean; { This function will log the specified record string in the record log table } { of the requesting station. } { Max length of name: 99 chars } { LockDirective LD_LOG = 0; LD_LOG_LOCK = 1; Deny all access to file/record LD_LOG_LOCK_RO = 3; Allow read / deny write } { TimeOut=0 means NoWait } Var temp1,temp2:word; BEGIN WITH Regs DO BEGIN AH := $D0; AL := LockDirective; UpString(Name); Move(Name,GlobalReqBuf^,ord(Name[0])+1); GetGlobalBufferAddress(DS,DX,temp1,temp2); { DS:DX real mode pointer to buffer in realmode-range holding Filename } BP := Timeout; { In 1/18th seconds (use only with lock bit set } RealModeIntr($21,Regs); Result:=AL; LogLogicalRecord := (Result=0); { FFh fail } { FEh timeout } { 96h No dynamic memory for file } END; END; {D1 [1.0+]} FUNCTION LockLogicalRecordSet(LockDirective:Byte; TimeoutLimit : Word) : Boolean; { Call this to lock all records logged with Log_Logical_Record } { LockDirective LD_LOCK = 0; Deny all access to file/record LD_LOCK_RO = 1; Allow read / deny write } BEGIN WITH Regs DO BEGIN AH := $D1; AL := LockDirective; BP := TimeoutLimit; { In 1/18th seconds, 0 = No wait } RealModeIntr($21,Regs); Result:=AL; LockLogicalRecordSet := (Result=0); {00 - Success FF - fail, FE - timeout } END; END; {D2.. [1.0+]} FUNCTION ReleaseLogicalRecord(Name : String) : Boolean; { Call this to release a logical record lock without removing the rec } { from the table } Var temp1,temp2:word; BEGIN WITH Regs DO BEGIN AH := $D2; UpString(Name); Move(Name,GlobalReqBuf^,ord(Name[0])+1); GetGlobalBufferAddress(DS,DX,temp1,temp2); { DS:DX real mode pointer to buffer in realmode-range holding Filename } RealModeIntr($21,Regs); Result:=AL; ReleaseLogicalRecord := (Result=0); { FF No record found } END; END; {D3.. [1.0+]} FUNCTION ReleaseLogicalRecordSet : Boolean; { release all locked logical records, doesn't remove them from the table } BEGIN WITH Regs DO BEGIN AH := $D3; RealModeIntr($21,Regs); Result:=0; ReleaseLogicalRecordSet := True; END; END; {D4.. [1.0+]} FUNCTION ClearLogicalRecord(Name : String) : Boolean; { This call unlocks and removes the Logical Record lock from the table } Var temp1,temp2:word; BEGIN WITH Regs DO BEGIN AH := $D4; UpString(Name); Move(Name,GlobalReqBuf^,ord(Name[0])+1); GetGlobalBufferAddress(DS,DX,temp1,temp2); { DS:DX real mode pointer to buffer in realmode-range holding Filename } RealModeIntr($21,Regs); Result:=AL; ClearLogicalRecord := (Result=0); { FF No record Found } END; END; {D5.. [1.0+]} FUNCTION ClearLogicalRecordSet : Boolean; { Unlocks and removes from the table all of the stations logical record locks } BEGIN WITH Regs DO BEGIN AH := $D5; RealModeIntr($21,Regs); Result:=0; ClearLogicalRecordSet := True; END; END; (************* THE FOLLOWING ARE PHYSICAL RECORD LOCK CALLS ****************) {F:BC..:Lock (& Log) records in a file} function LogPhysicalRecord(Handle:Word; LockDirective:Byte; RecordOffset,RecordLength:Longint; TimeOutLimit:Word): boolean; { Max length of name: 99 chars } { LockDirective LD_LOG = 0; LD_LOG_LOCK = 1; Deny all access to file/record LD_LOG_LOCK_RO = 3; Allow read / deny write } { TimeOut=0 means NoWait; TimeOut not valid if logging only } { Handle is the file handle } begin with regs do begin AH := $BC; AL := LockDirective; BX := Handle; CX := HiLong(RecordOffset); DX := LowLong(RecordOffset); BP := TimeOutLimit; SI := HiLong(RecordLength); DI := LowLong(RecordLength); RealModeIntr($21,Regs); Result:=AL; LogPhysicalRecord := (Result=0); { $FF = fail, $FE Timeout, $96 = No dynamic memory } end; end; {BD.. [1.0+]} function ReleasePhysicalRecord( Handle:Word; RecordOffset,RecordLength:Longint) : boolean; { When a record is released, it is unlocked for use by someone else, but } { it remains in the log table } { Handle is the file handle, Start_Hi and Start_Lo are the boundaries of } { the locked region to be released } begin with regs do begin AH := $BD; BX := Handle; CX := HiLong(RecordOffset); DX := LowLong(RecordOffset); SI := HiLong(RecordLength); DI := LowLong(RecordLength); RealModeIntr($21,Regs); Result:=AL; ReleasePhysicalRecord := (Result=0); { $FF = No locked record found} end; end; {BE.. [1.0+]} function ClearPhysicalRecord(Handle: Word; RecordOffset,RecordLength:Longint): boolean; { Handle is the file handle, Start_Hi and Start_Lo are the boundaries } { of the file region to be locked. Clearing a record will unlock it } { and remove it from the log table. } begin with regs do begin AH := $BE; BX := Handle; CX := HiLong(RecordOffset); DX := LowLong(RecordOffset); SI := HiLong(RecordLength); DI := LowLong(RecordLength); RealModeIntr($21,Regs); Result:=AL; ClearPhysicalRecord := (Result=0); { $FF No locked record found } end; end; {C2.. [1.0+]} function LockPhysicalRecordSet(LockDirective: byte; TimeoutLimit: Word): boolean; { flgs are the lock flags: bit 1 set means shared (non-exclusive) lock } { Timeout is in 1/18 seconds, 0 = no wait, -1 means indefinite wait } { This function attempts to lock all of the records logged in the station's } { log table. } { LockDirective LD_LOCK = 0; Deny all access to file/record LD_LOCK_RO = 1; Allow read / deny write } { !! There is known problem when the locking directive equals 1. } begin with regs do begin AH := $C2; AL := LockDirective; BP := TimeOutLimit; RealModeIntr($21,Regs); Result:=AL; LockPhysicalRecordSet := (Result=0); { $FF = fail, $FE = timeout fail } end; end; {C3.. [1.0+]} function ReleasePhysicalRecordSet : boolean; { unlocks the entire record log table of the station. records remain in } { the log table. } begin regs.AH := $C3; RealModeIntr($21,Regs); Result:=0; ReleasePhysicalRecordSet := True; end; {C4.. [1.0+]} function ClearPhysicalRecordSet : boolean; { unlocks and removes from the log table any records logged and locked } begin regs.AH := $C4; RealModeIntr($21,Regs); Result:=0; ClearPhysicalRecordSet := True; end; {F217/EF [2.1x+]} Function GetLogicalRecordLocksByConnection(ConnNbr:word; {i/o} Var NextRecNbr:word; Var TaskNbr:word; Var LockStatus:Byte; Var LockName:String):Boolean; { You need console operator rights to use this function } Type Treq=record len :Word; subFunc :Byte; _ConnNbr :word; {lo-hi} { !! Invalid numbers may cause an abend } _LastRecSeen:word; {lo-hi} end; Trep=record _LastRecSeen :word; {lo-hi} _NbrOfRecords:word; {lo-hi} _LockInfo :array[1..508] of byte; end; TPreq=^Treq; TPrep=^Trep; Begin WITH TPreq(GlobalReqBuf)^ do begin subFunc:=$EF; _ConnNbr:=ConnNbr; _LastRecSeen:=NextRecNbr; len:=SizeOf(Treq)-2; end; F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); With TPrep(GlobalReplyBuf)^ do begin Move(_LastRecSeen,NextRecNbr,2); end; GetLogicalRecordLocksByConnection:=(result=0) { Valid completion codes: $00 Success $FF Failure } end; {$IFDEF xxxx} {F217/ [2.1x+]} Function ( ):Boolean; Type Treq=record len:Word; subFunc:Byte; end; Trep=record end; TPreq=^Treq; TPrep=^Trep; Begin WITH TPreq(GlobalReqBuf)^ do begin subFunc:=$ len:=SizeOf(Treq)-2; end; F2SystemCall($17,SizeOf(Treq),SizeOf(Trep),result); With TPrep(GlobalReplyBuf)^ do begin end; :=(result=0) { Valid completion codes: $00 Success $FF Failure. } end; {$ENDIF} Begin SetLockMode(1); END.