Commit | Line | Data |
---|---|---|
f240aacb LC |
1 | /* Emulate flock on platforms that lack it, primarily Windows and MinGW. |
2 | ||
3 | This is derived from sqlite3 sources. | |
4 | http://www.sqlite.org/cvstrac/rlog?f=sqlite/src/os_win.c | |
5 | http://www.sqlite.org/copyright.html | |
6 | ||
7 | Written by Richard W.M. Jones <rjones.at.redhat.com> | |
8 | ||
61cd9dc9 | 9 | Copyright (C) 2008-2010 Free Software Foundation, Inc. |
f240aacb LC |
10 | |
11 | This library is free software; you can redistribute it and/or | |
12 | modify it under the terms of the GNU Lesser General Public | |
13 | License as published by the Free Software Foundation; either | |
14 | version 2.1 of the License, or (at your option) any later version. | |
15 | ||
16 | This library is distributed in the hope that it will be useful, | |
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
19 | Lesser General Public License for more details. | |
20 | ||
21 | You should have received a copy of the GNU Lesser General Public License | |
22 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
23 | ||
24 | #include <config.h> | |
25 | #include <sys/file.h> | |
26 | ||
27 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | |
28 | ||
29 | /* _get_osfhandle */ | |
30 | #include <io.h> | |
31 | ||
32 | /* LockFileEx */ | |
33 | #define WIN32_LEAN_AND_MEAN | |
34 | #include <windows.h> | |
35 | ||
36 | #include <errno.h> | |
37 | ||
38 | /* Determine the current size of a file. Because the other braindead | |
39 | * APIs we'll call need lower/upper 32 bit pairs, keep the file size | |
40 | * like that too. | |
41 | */ | |
42 | static BOOL | |
43 | file_size (HANDLE h, DWORD * lower, DWORD * upper) | |
44 | { | |
45 | *lower = GetFileSize (h, upper); | |
46 | return 1; | |
47 | } | |
48 | ||
49 | /* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */ | |
50 | #ifndef LOCKFILE_FAIL_IMMEDIATELY | |
51 | # define LOCKFILE_FAIL_IMMEDIATELY 1 | |
52 | #endif | |
53 | ||
54 | /* Acquire a lock. */ | |
55 | static BOOL | |
56 | do_lock (HANDLE h, int non_blocking, int exclusive) | |
57 | { | |
58 | BOOL res; | |
59 | DWORD size_lower, size_upper; | |
60 | OVERLAPPED ovlp; | |
61 | int flags = 0; | |
62 | ||
63 | /* We're going to lock the whole file, so get the file size. */ | |
64 | res = file_size (h, &size_lower, &size_upper); | |
65 | if (!res) | |
66 | return 0; | |
67 | ||
68 | /* Start offset is 0, and also zero the remaining members of this struct. */ | |
69 | memset (&ovlp, 0, sizeof ovlp); | |
70 | ||
71 | if (non_blocking) | |
72 | flags |= LOCKFILE_FAIL_IMMEDIATELY; | |
73 | if (exclusive) | |
74 | flags |= LOCKFILE_EXCLUSIVE_LOCK; | |
75 | ||
76 | return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp); | |
77 | } | |
78 | ||
79 | /* Unlock reader or exclusive lock. */ | |
80 | static BOOL | |
81 | do_unlock (HANDLE h) | |
82 | { | |
83 | int res; | |
84 | DWORD size_lower, size_upper; | |
85 | ||
86 | res = file_size (h, &size_lower, &size_upper); | |
87 | if (!res) | |
88 | return 0; | |
89 | ||
90 | return UnlockFile (h, 0, 0, size_lower, size_upper); | |
91 | } | |
92 | ||
93 | /* Now our BSD-like flock operation. */ | |
94 | int | |
95 | flock (int fd, int operation) | |
96 | { | |
97 | HANDLE h = (HANDLE) _get_osfhandle (fd); | |
98 | DWORD res; | |
99 | int non_blocking; | |
100 | ||
101 | if (h == INVALID_HANDLE_VALUE) | |
102 | { | |
103 | errno = EBADF; | |
104 | return -1; | |
105 | } | |
106 | ||
107 | non_blocking = operation & LOCK_NB; | |
108 | operation &= ~LOCK_NB; | |
109 | ||
110 | switch (operation) | |
111 | { | |
112 | case LOCK_SH: | |
113 | res = do_lock (h, non_blocking, 0); | |
114 | break; | |
115 | case LOCK_EX: | |
116 | res = do_lock (h, non_blocking, 1); | |
117 | break; | |
118 | case LOCK_UN: | |
119 | res = do_unlock (h); | |
120 | break; | |
121 | default: | |
122 | errno = EINVAL; | |
123 | return -1; | |
124 | } | |
125 | ||
126 | /* Map Windows errors into Unix errnos. As usual MSDN fails to | |
127 | * document the permissible error codes. | |
128 | */ | |
129 | if (!res) | |
130 | { | |
131 | DWORD err = GetLastError (); | |
132 | switch (err) | |
1cd4fffc LC |
133 | { |
134 | /* This means someone else is holding a lock. */ | |
135 | case ERROR_LOCK_VIOLATION: | |
136 | errno = EAGAIN; | |
137 | break; | |
138 | ||
139 | /* Out of memory. */ | |
140 | case ERROR_NOT_ENOUGH_MEMORY: | |
141 | errno = ENOMEM; | |
142 | break; | |
143 | ||
144 | case ERROR_BAD_COMMAND: | |
145 | errno = EINVAL; | |
146 | break; | |
147 | ||
148 | /* Unlikely to be other errors, but at least don't lose the | |
149 | * error code. | |
150 | */ | |
151 | default: | |
152 | errno = err; | |
153 | } | |
f240aacb LC |
154 | |
155 | return -1; | |
156 | } | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | #else /* !Windows */ | |
162 | ||
163 | #ifdef HAVE_STRUCT_FLOCK_L_TYPE | |
164 | /* We know how to implement flock in terms of fcntl. */ | |
165 | ||
166 | #ifdef HAVE_FCNTL_H | |
167 | #include <fcntl.h> | |
168 | #endif | |
169 | ||
170 | #ifdef HAVE_UNISTD_H | |
171 | #include <unistd.h> | |
172 | #endif | |
173 | ||
174 | #include <errno.h> | |
175 | #include <string.h> | |
176 | ||
177 | int | |
178 | flock (int fd, int operation) | |
179 | { | |
180 | int cmd, r; | |
181 | struct flock fl; | |
182 | ||
183 | if (operation & LOCK_NB) | |
184 | cmd = F_SETLK; | |
185 | else | |
186 | cmd = F_SETLKW; | |
187 | operation &= ~LOCK_NB; | |
188 | ||
189 | memset (&fl, 0, sizeof fl); | |
190 | fl.l_whence = SEEK_SET; | |
191 | /* l_start & l_len are 0, which as a special case means "whole file". */ | |
192 | ||
193 | switch (operation) | |
194 | { | |
195 | case LOCK_SH: | |
196 | fl.l_type = F_RDLCK; | |
197 | break; | |
198 | case LOCK_EX: | |
199 | fl.l_type = F_WRLCK; | |
200 | break; | |
201 | case LOCK_UN: | |
202 | fl.l_type = F_UNLCK; | |
203 | break; | |
204 | default: | |
205 | errno = EINVAL; | |
206 | return -1; | |
207 | } | |
208 | ||
209 | r = fcntl (fd, cmd, &fl); | |
210 | if (r == -1 && errno == EACCES) | |
211 | errno = EAGAIN; | |
212 | ||
213 | return r; | |
214 | } | |
215 | ||
216 | #else /* !HAVE_STRUCT_FLOCK_L_TYPE */ | |
217 | ||
218 | #error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib." | |
219 | ||
220 | #endif /* !HAVE_STRUCT_FLOCK_L_TYPE */ | |
221 | ||
222 | #endif /* !Windows */ |