Locking and Unlocking File Ranges
A file can be opened with shared read/write permission to allow several users to share the data in the file. When a user needs to modify a portion of a file that has been opened with shared read/write permission, it is usually desirable to make that portion of the file unavailable to other users while the changes are made. You can call thePBLockRange
function to lock a range of bytes before modifying the file and thenPBUnlockRange
to unlock that range after your changes are safely recorded in the file.Locking a range of bytes in a file gives the user exclusive read/write access to that range and makes it inaccessible to other users. Other users can neither write nor read the bytes in that range until you unlock it. If other users attempt to read data from a portion of a file that you have locked, they receive the
fLckdErr
result code.The functions
PBLockRange
andPBUnlockRange
are effective only on files that are located on volumes that are sharable. If you callPBLockRange
on a file that is not located on a remote server volume or that is not currently being shared, no range locking occurs. Moreover,PBLockRange
does not return a result code indicating that no range locking has occurred. As a result, you should usually check whether range locking will be effective on a file before attempting to lock the desired range.Listing 2-9 illustrates how you can check to make sure that calling
PBLockRange
will have the desired effect.Listing 2-9 Determining whether a file can have ranges locked
FUNCTION RangesCanBeLocked (fRefNum: Integer): Boolean; VAR myParmBlk: ParamBlockRec; {basic parameter block} myErr: OSErr; BEGIN WITH myParmBlk DO BEGIN ioRefNum := fRefNum; ioReqCount := 1; {lock a single byte} ioPosMode := fsFromStart; {at the beginning of the file} ioPosOffset := 0; END; myErr := PBLockRange(@myParmBlk, FALSE);{lock the byte; ignore result} myErr := PBLockRange(@myParmBlk, FALSE);{lock the byte again} CASE myErr OF fLckdErr, {byte was locked by another user} afpRangeOverlap, {byte was locked by this user} afpNoMoreLocks: {max number of locks already used} BEGIN RangesCanBeLocked := TRUE; {range locking is supported} IF myErr = afpRangeOverlap THEN {unlock the byte we locked} myErr := PBUnlockRange(@myParmBlk, FALSE); END; OTHERWISE RangesCanBeLocked := FALSE; {range locking is not supported} END; {of CASE} END;The function RangesCanBeLocked takes a file reference number of an open file as
a parameter; this is the reference number of the file in which a range of bytes is to
be locked. The function attempts to locks the first byte in the file and immediately
attempts to lock it again. If the second range locking fails with the result codeafpRangeOverlap
, the first call toPBLockRange
was successful. If the second call toPBLockRange
fails with the result codefLckdErr
, the byte was already locked by another user. Similarly, if the second call toPBLockRange
fails with the result codeafpNoMoreLocks
, the maximum number of range locks has been reached. In these three cases, range locking is supported by the volume containing the specified file. If any other result code (includingnoErr
) is returned, range locking is not supported by that volume or for some reason the capabilities of the volume cannot be determined.
You can unlock a locked range of bytes by calling
- Note
- Local file sharing can be started or stopped (via the Sharing Setup control panel) while your application is running. For this reason, each time you want to lock a range, it's best to check that byte ranges in that file can be locked.
PBUnlockRange
. Note that the range to be unlocked must be the exact same range of bytes that was previously locked usingPBLockRange
. (You can lock and unlock different byte ranges in any order, however.) If for some reason you need to unlock a range of bytes and do not know where the range started or how long the range is, you must close the file to unlock the range. When a file is closed, all locked ranges held by a user are unlocked.If you want to append data to a shared file, you can use
PBLockRange
to lock the range of bytes from the file's current logical end-of-file to the last possible addressable byte of the file. Once you have locked that range, you can write data into it. Listing 2-10 shows how to determine the current logical end-of-file and lock the appropriate range.Listing 2-10 Locking a file range to append data to the file
FUNCTION LockRangeForAppending (fRefNum: Integer; VAR EOF: LongInt): OSErr; VAR myParmBlk: ParamBlockRec; {basic parameter block} myErr: OSErr; myEOF: LongInt; {current EOF} BEGIN myParmBlk.ioCompletion := NIL; myParmBlk.ioRefNum := fRefNum; myErr := PBGetEOF(@myParmBlk, FALSE); {get the current EOF} IF myErr <> noErr THEN BEGIN LockRangeForAppending := myErr; Exit(LockRangeForAppending); {trouble reading EOF} END; myEOF := LongInt(myParmBlk.ioMisc); {save the current EOF} WITH myParmBlk DO BEGIN ioReqCount := -1; {all addressable bytes} ioPosMode := fsFromStart; {start range...} ioPosOffset := myEOF; {...at the current end-of-file} END; myErr := PBLockRange(@myParmBlk, FALSE);{lock the specified range} EOF := myEOF; {return current EOF to caller} LockRangeForAppending := myErr; END;The functionLockRangeForAppending
first determines the current logical end-of-file. It is important to get this value immediately before you attempt to lock a range that depends on it because another user of the shared file might have changed the end-of-file since you last read it. ThenLockRangeForAppending
locks the range beginning at the current end-of-file and extending for the maximum number of bytes (specified using the special value -1).In effect, this technique locks a range where data does not yet exist. Practically speaking, locking the entire addressable range of a file prevents another user from appending data to the file until you unlock that range. Note that
LockRangeForAppending
returns the current logical end-of-file to the caller so that the caller can unlock the correct range of bytes after appending the data.You can also call
PBLockRange
to lock a range of bytes when you want to truncate a file. Locking the end portion of a file to be deleted prevents another user from using that portion during the truncation. Instead of setting theioPosOffset
field of the parameter block to the logical end-of-file (as in Listing 2-10), simply set it to what will be the last byte after the file is truncated. Similarly, you can lock an entire file fork by setting theioPosOffset
field to 0.