temporarily disable elisp exception tests
[bpt/guile.git] / lib / flock.c
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
9 Copyright (C) 2008-2014 Free Software Foundation, Inc.
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 /* LockFileEx */
30 # define WIN32_LEAN_AND_MEAN
31 # include <windows.h>
32
33 # include <errno.h>
34
35 /* _get_osfhandle */
36 # include "msvc-nothrow.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)
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 }
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 # include <fcntl.h>
167
168 # ifdef HAVE_UNISTD_H
169 # include <unistd.h>
170 # endif
171
172 # include <errno.h>
173 # include <string.h>
174
175 int
176 flock (int fd, int operation)
177 {
178 int cmd, r;
179 struct flock fl;
180
181 if (operation & LOCK_NB)
182 cmd = F_SETLK;
183 else
184 cmd = F_SETLKW;
185 operation &= ~LOCK_NB;
186
187 memset (&fl, 0, sizeof fl);
188 fl.l_whence = SEEK_SET;
189 /* l_start & l_len are 0, which as a special case means "whole file". */
190
191 switch (operation)
192 {
193 case LOCK_SH:
194 fl.l_type = F_RDLCK;
195 break;
196 case LOCK_EX:
197 fl.l_type = F_WRLCK;
198 break;
199 case LOCK_UN:
200 fl.l_type = F_UNLCK;
201 break;
202 default:
203 errno = EINVAL;
204 return -1;
205 }
206
207 r = fcntl (fd, cmd, &fl);
208 if (r == -1 && errno == EACCES)
209 errno = EAGAIN;
210
211 return r;
212 }
213
214 # else /* !HAVE_STRUCT_FLOCK_L_TYPE */
215
216 # error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
217
218 # endif /* !HAVE_STRUCT_FLOCK_L_TYPE */
219
220 #endif /* !Windows */