Some file locking scenarios does not work as expected
The lock tool¶
To reproduce this bug I use the attached simple interactive/scriptable locking application. To compile it run:
gcc -Wall -o lock lock.c
Once the application is started, it understands three sub-commands:
lock r|R|w|W|u|U start len filename test r|w|u start len filename sleep seconds
The lock sub-command tries to lock the specified area (start, len) of the filename for reading (r, R), or writing (w, W), or unlock it (u, U). When the lowercase letter is used (r, w, u), the attempt is non-blocking (F_SETLK). With uppercase (R, W, U), the blocking (F_SETLKW) attempt is performed.
The test sub-command checks for the specified lock type (r, w, u) and area (start, len) of the filename using the F_GETLK.
The sleep sub-command waits for the specified time period in seconds (usable for scripting).
The failing scenario¶
Run two instances of the lock tool in same directory.
In the first instance lock a region of a file for reading:
> lock r 0 2 f
In the second instance try to lock the same region of the file for reading:
> lock W 0 2 f
It will block.
Now, back to the first instance. Do the following:
> lock w 1 1 f > test r 0 2 f F_GETLK: F_UNLCK 0 2 > test w 0 2 f F_GETLK: F_UNLCK 0 2 > lock r 0 2 f fcntl failed > lock R 0 2 f fcntl failed > lock w 0 2 f >
We see that the lock request for read failed, even the preceeding test command showed that it should pass. The similar lock request for write succeeded properly. Apparently, the lock subsystem is unable to replace a pair of read and write locks by one read lock, but similar replacement by one write lock works well.
According to the POSIX fcntl specification, the above scenario should pass:
There shall be at most one type of lock set for each byte in the file. Before a successful return from an F_SETLK or an F_SETLKW request when the calling process has previously existing locks on bytes in the region specified by the request, the previous lock type for each byte in the specified region shall be replaced by the new lock type. As specified above under the descriptions of shared locks and exclusive locks, an F_SETLK or an F_SETLKW request (respectively) shall fail or block when another process has existing locks on bytes in the specified region and the type of any of those locks conflicts with the type specified in the request.