The earliest versions of Unix had no way to lock files except to create lock files. The idea is that two or more processes would more or less simultaneously try to create a lock file in exclusive mode, via the O_EXCL flag of the open( ) system call. The operating system would return success to the process that won the race, and a "file exists" error to losing processes. One problem with this scheme is that it relies on the winning process to remove the lock file before it exits. If the process is running buggy software, this might not happen. Some applications mitigate this problem by recording the process ID of the winner into the contents of the lock file. A process that finds that it gets a "file exists" error can then read the lock file to see if the owning process is still running.
Still, lock files can be clumsy. In the 1980s, Unix versions were released with file locking support built into the operating system. The System V branch of Unix offered file locking via the fcntl( ) system call, whereas the BSD branch provided the flock( ) system call. In both cases, when the process that creates the lock dies, the lock will be automatically released.
System V (and therefore Solaris) offers mandatory or enforced locking as an option. This option is enabled if mandatory lock permissions are set on a file. Mandatory lock permissions are an overload of the set group ID execution bit (02000 in octal). If the set group ID execution bit is set, and if the group execution bit is not set, then all reads and writes to the file will use enforced locking. So, for example:
This makes file example readable and writable by the file's owner, and readable by everyone else. The appearance of the l in the first field of the output of the ls command tells you that mandatory locking is enabled. Of course, you can use any combination of read or write permissions for the file's owner, group, and world.% chmod 2644 example % ls -l example -rw-r-lr-- 1 mre staff 9 Dec 28 10:52 example
If the mandatory lock permissions are set on a file, then every write( ) or read( ) system call results in an implicit sequence of:
What if the process has already acquired a lock by an explicit fcntl call? If the range locked is equal to or encompasses the range the read or write is done on, then no implicit pair of fcntl calls are done. If the range explicitly locked partly overlaps the range read or write will do, then implicit fcntl calls are done on the unlocked portion of the range.fcntl(...); /* lock the file at the range we are reading or writing */ read(...); /* or */ write(...); fcntl(...); /* unlock the file at the range locked above */
Mandatory locking seems very useful, but it is open to denial of service attacks. Suppose mandatory lock permissions are set on a file. An attacker named Mallet decides to issue an fcntl call to get an exclusive lock on the entire file. Bob now tries to read the file and finds that his application hangs. A proponent of mandatory locking might point out that the mistake was in allowing the file to be accessible by Mallet (if Mallet can't open the file, he can't lock it). The counter argument is that if you are going to rely on permissions to avoid a denial of service (and restricted permissions are a good thing to have for critical applications), then the set of users who can access the file is limited to those with a vested interest in avoiding denial of service. In that case, mandatory locking is no more useful than advisory locking.
[18]As it turns out, very few Windows programs rely on byte range mandatory locking.
TIP: Share reservations in the Windows world do not interact at all with Windows byte range or whole file locking.
10.5. Printer services | 11.2. NFS and file locking |
Copyright © 2002 O'Reilly & Associates. All rights reserved.