1/*
2 * Copyright 2007, Vasilis Kaoutsis, kaoutsis@sch.gr.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <errno.h>
7#include <fcntl.h>
8#include <unistd.h>
9
10#include <errno_private.h>
11
12
13int
14lockf(int fileDescriptor, int function, off_t size)
15{
16	struct flock fileLock;
17	fileLock.l_start = 0;
18	fileLock.l_len = size;
19	fileLock.l_whence = SEEK_CUR;
20
21	if (function == F_ULOCK) {
22		// unlock locked sections
23		fileLock.l_type = F_UNLCK;
24		return fcntl(fileDescriptor, F_SETLK, &fileLock);
25	} else if (function == F_LOCK) {
26		// lock a section for exclusive use
27		fileLock.l_type = F_WRLCK;
28		return fcntl(fileDescriptor, F_SETLKW, &fileLock);
29	} else if (function == F_TLOCK) {
30		// test and lock a section for exclusive use
31		fileLock.l_type = F_WRLCK;
32		return fcntl(fileDescriptor, F_SETLK, &fileLock);
33	} else if (function == F_TEST) {
34		// test a section for locks by other processes
35		fileLock.l_type = F_WRLCK;
36		if (fcntl(fileDescriptor, F_GETLK, &fileLock) == -1)
37			return -1;
38		if (fileLock.l_type == F_UNLCK)
39			return 0;
40
41		__set_errno(EAGAIN);
42		return -1;
43	} else {
44		__set_errno(EINVAL);
45		return -1;
46	}
47
48	// Notes regarding standard compliance (cf. Open Group Base Specs):
49	// * "The interaction between fcntl() and lockf() locks is unspecified."
50	// * fcntl() locking works on a per-process level. The lockf() description
51	//   is a little fuzzy on whether it works the same way. The first quote
52	//   seem to describe per-thread locks (though it might actually mean
53	//   "threads of other processes"), but the others quotes are strongly
54	//   indicating per-process locks:
55	//   - "Calls to lockf() from other threads which attempt to lock the locked
56	//     file section shall either return an error value or block until the
57	//     section becomes unlocked."
58	//   - "All the locks for a process are removed when the process
59	//     terminates."
60	//   - "F_TEST shall detect if a lock by another process is present on the
61	//     specified section."
62	//   - "The sections locked with F_LOCK or F_TLOCK may, in whole or in part,
63	//     contain or be contained by a previously locked section for the same
64	//     process. When this occurs, or if adjacent locked sections would
65	//     occur, the sections shall be combined into a single locked section."
66	// * fcntl() and lockf() handle a 0 size argument differently. The former
67	//   use the file size at the time of the call:
68	//    "If the command is F_SETLKW and the process must wait for another
69	//     process to release a lock, then the range of bytes to be locked shall
70	//     be determined before the fcntl() function blocks. If the file size
71	//     or file descriptor seek offset change while fcntl() is blocked, this
72	//     shall not affect the range of bytes locked."
73	//   lockf(), on the other hand, is supposed to create a lock whose size
74	//   dynamically adjusts to the file size:
75	//    "If size is 0, the section from the current offset through the largest
76	//     possible file offset shall be locked (that is, from the current
77	//     offset through the present or any future end-of-file)."
78	// * The lock release handling when closing descriptors sounds a little
79	//   different, though might actually mean the same.
80	//   For fcntl():
81	//    "All locks associated with a file for a given process shall be removed
82	//     when a file descriptor for that file is closed by that process or the
83	//     process holding that file descriptor terminates."
84	//   For lockf():
85	//    "File locks shall be released on first close by the locking process of
86	//     any file descriptor for the file."
87}
88