exp2l: Work around a NetBSD 10.0/i386 bug.
[gnulib.git] / lib / flock.c
blob53525cf1ba2a5fa2d1eab4051fe8e6e94e2013b1
1 /* Emulate flock on platforms that lack it, primarily Windows and MinGW.
3 This is derived from sqlite3 sources.
4 https://www.sqlite.org/src/finfo?name=src/os_win.c
5 https://www.sqlite.org/copyright.html
7 Written by Richard W.M. Jones <rjones.at.redhat.com>
9 Copyright (C) 2008-2024 Free Software Foundation, Inc.
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.
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.
21 You should have received a copy of the GNU Lesser General Public License
22 along with this program. If not, see <https://www.gnu.org/licenses/>. */
24 #include <config.h>
25 #include <sys/file.h>
27 #if defined _WIN32 && ! defined __CYGWIN__
29 /* LockFileEx */
30 # define WIN32_LEAN_AND_MEAN
31 # include <windows.h>
33 # include <errno.h>
35 /* _get_osfhandle */
36 # if GNULIB_MSVC_NOTHROW
37 # include "msvc-nothrow.h"
38 # else
39 # include <io.h>
40 # endif
42 /* Determine the current size of a file. Because the other braindead
43 * APIs we'll call need lower/upper 32 bit pairs, keep the file size
44 * like that too.
46 static BOOL
47 file_size (HANDLE h, DWORD * lower, DWORD * upper)
49 *lower = GetFileSize (h, upper);
50 return 1;
53 /* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */
54 # ifndef LOCKFILE_FAIL_IMMEDIATELY
55 # define LOCKFILE_FAIL_IMMEDIATELY 1
56 # endif
58 /* Acquire a lock. */
59 static BOOL
60 do_lock (HANDLE h, int non_blocking, int exclusive)
62 BOOL res;
63 DWORD size_lower, size_upper;
64 OVERLAPPED ovlp;
65 int flags = 0;
67 /* We're going to lock the whole file, so get the file size. */
68 res = file_size (h, &size_lower, &size_upper);
69 if (!res)
70 return 0;
72 /* Start offset is 0, and also zero the remaining members of this struct. */
73 memset (&ovlp, 0, sizeof ovlp);
75 if (non_blocking)
76 flags |= LOCKFILE_FAIL_IMMEDIATELY;
77 if (exclusive)
78 flags |= LOCKFILE_EXCLUSIVE_LOCK;
80 return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp);
83 /* Unlock reader or exclusive lock. */
84 static BOOL
85 do_unlock (HANDLE h)
87 int res;
88 DWORD size_lower, size_upper;
90 res = file_size (h, &size_lower, &size_upper);
91 if (!res)
92 return 0;
94 return UnlockFile (h, 0, 0, size_lower, size_upper);
97 /* Now our BSD-like flock operation. */
98 int
99 flock (int fd, int operation)
101 HANDLE h = (HANDLE) _get_osfhandle (fd);
102 DWORD res;
103 int non_blocking;
105 if (h == INVALID_HANDLE_VALUE)
107 errno = EBADF;
108 return -1;
111 non_blocking = operation & LOCK_NB;
112 operation &= ~LOCK_NB;
114 switch (operation)
116 case LOCK_SH:
117 res = do_lock (h, non_blocking, 0);
118 break;
119 case LOCK_EX:
120 res = do_lock (h, non_blocking, 1);
121 break;
122 case LOCK_UN:
123 res = do_unlock (h);
124 break;
125 default:
126 errno = EINVAL;
127 return -1;
130 /* Map Windows errors into Unix errnos. As usual MSDN fails to
131 * document the permissible error codes.
133 if (!res)
135 DWORD err = GetLastError ();
136 switch (err)
138 /* This means someone else is holding a lock. */
139 case ERROR_LOCK_VIOLATION:
140 errno = EAGAIN;
141 break;
143 /* Out of memory. */
144 case ERROR_NOT_ENOUGH_MEMORY:
145 errno = ENOMEM;
146 break;
148 case ERROR_BAD_COMMAND:
149 errno = EINVAL;
150 break;
152 /* Unlikely to be other errors, but at least don't lose the
153 * error code.
155 default:
156 errno = err;
159 return -1;
162 return 0;
165 #else /* !Windows */
167 # ifdef HAVE_STRUCT_FLOCK_L_TYPE
168 /* We know how to implement flock in terms of fcntl. */
170 # include <fcntl.h>
172 # ifdef HAVE_UNISTD_H
173 # include <unistd.h>
174 # endif
176 # include <errno.h>
177 # include <string.h>
180 flock (int fd, int operation)
182 int cmd, r;
183 struct flock fl;
185 if (operation & LOCK_NB)
186 cmd = F_SETLK;
187 else
188 cmd = F_SETLKW;
189 operation &= ~LOCK_NB;
191 memset (&fl, 0, sizeof fl);
192 fl.l_whence = SEEK_SET;
193 /* l_start & l_len are 0, which as a special case means "whole file". */
195 switch (operation)
197 case LOCK_SH:
198 fl.l_type = F_RDLCK;
199 break;
200 case LOCK_EX:
201 fl.l_type = F_WRLCK;
202 break;
203 case LOCK_UN:
204 fl.l_type = F_UNLCK;
205 break;
206 default:
207 errno = EINVAL;
208 return -1;
211 r = fcntl (fd, cmd, &fl);
212 if (r == -1 && errno == EACCES)
213 errno = EAGAIN;
215 return r;
218 # else /* !HAVE_STRUCT_FLOCK_L_TYPE */
220 # error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
222 # endif /* !HAVE_STRUCT_FLOCK_L_TYPE */
224 #endif /* !Windows */