2.8 Locking Files

2.8.1 Problem

You want to lock files (or portions of them) to prevent two or more processes from accessing them simultaneously.

2.8.2 Solution

Two basic types of locks exist: advisory and mandatory. Unix supports both advisory and, to an extremely limited extent, mandatory locks, while Windows supports only mandatory locks.

2.8.3 Discussion

In the following sections, we will look at the different issues for Unix and Windows. Locking files on Unix

All modern Unix variants support advisory locks. An advisory lock is a lock in which the operating system does not enforce the lock. Instead, programs sharing the same file must cooperate with each other to ensure that locks are properly observed. From a security perspective, advisory locks are of little use because any program is free to perform any action on a file regardless of the state of any advisory locks that other programs may hold on the file.

Support for mandatory locks varies greatly from one Unix variant to another. Both Linux and Solaris support mandatory locks, but Darwin, FreeBSD, NetBSD, and OpenBSD do not, even though they export the interface used by Linux and Solaris to support them. On such systems, this interface creates advisory locks.

Support for mandatory locking does not extend to NFS. In other words, both Linux and Solaris are capable only of using mandatory locks on local filesystems. Further, Linux requires that filesystems be mounted with support for mandatory locking, which is disabled by default. In the end, Solaris is really the only Unix variant on which you can reasonably expect mandatory locking to work, and even then, relying on mandatory locks is like playing with fire.

As if the story for mandatory locking on Unix were not bad enough already, it gets worse. To be able to use mandatory locks on a file, the file must have the setgid bit enabled and the group execute bit disabled in its permissions. Even if a process holds a mandatory lock on a file, another process may remove the setgid bit from the file's permissions, which effectively turns the mandatory lock into an advisory lock!

Essentially, there is no such thing as a mandatory lock on Unix.

Just to add more fuel to the fire, neither Solaris nor Linux fully or properly implement the System V defined semantics for mandatory locks, and both systems differ in where they stray from the System V definitions. The details of the differences are not important here. We strongly recommend that you avoid the Unix mandatory lock debacle altogether. If you want to use advisory locking on Unix, then we recommend using a standalone lock file, as described in Recipe 2.9. Locking files on Windows

Where Unix falls flat on its face with respect to supporting file locking, Windows gets it right. Windows supports only mandatory file locks, and it fully enforces them. If a process has a lock on a file or a portion of a file, another process cannot mistakenly or maliciously steal that lock.

Windows provides four functions for locking and unlocking files. Two functions, LockFile( ) and LockFileEx( ), are provided for engaging locks, and two functions, UnlockFile( ) and UnlockFileEx( ), are provided for removing them.

Neither LockFile( ) nor UnlockFile( ) will return until the lock can be successfully obtained or released, respectively. LockFileEx( ) and UnlockFileEx( ), however, can be called in such a way that they will always return immediately, either returning failure or signalling an event object when the requested operation completes.

Locks can be placed on a file in its entirety or on a portion of a file. A single file may have multiple locks owned by multiple processes so long as none of the locks overlap. When removing a lock, you must specify the exact portion of the file that was locked. For example, two locks covering contiguous portions of a file may not be removed with a single unlock operation that spans the two locks.

When a lock is held on a file, closing the file does not necessarily remove the lock. The behavior is actually undefined and may vary across different filesystems and versions of Windows. Always make sure to remove any locks on a file before closing it.

There are two types of locks on Windows:

Shared lock

This type of lock allows other processes to read from the locked portion of the file, while denying all processes?including the process that obtained the lock?permission to write to the locked portion of the file.

Exclusive lock

This type of lock denies other processes both read and write access to the locked portion of the file, while allowing the locking process to read or write to the locked portion of the file.

Using LockFile( ) to obtain a lock always obtains an exclusive lock. However, LockFileEx( ) obtains a shared lock unless the flag LOCKFILE_EXCLUSIVE_LOCK is specified.

Here are the signatures for LockFile and UnlockFile( ):

BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, 
              DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToLockLow, 
              DWORD nNumberOfBytesToLockHigh); 
BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, 
                DWORD dwFileOffsetHigh, DWORD nNumberOfBytesToUnlockLow, 
                DWORD nNumberOfBytesToUnlockHigh);