1 /* Emulate flock on platforms that lack it, primarily Windows and MinGW.
2 
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
6 
7    Written by Richard W.M. Jones <rjones.at.redhat.com>
8 
9    Copyright (C) 2008-2021 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 <https://www.gnu.org/licenses/>.  */
23 
24 #include <config.h>
25 #include <sys/file.h>
26 
27 #if 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 # if GNULIB_MSVC_NOTHROW
37 #  include "msvc-nothrow.h"
38 # else
39 #  include <io.h>
40 # endif
41 
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.
45  */
46 static BOOL
file_size(HANDLE h,DWORD * lower,DWORD * upper)47 file_size (HANDLE h, DWORD * lower, DWORD * upper)
48 {
49   *lower = GetFileSize (h, upper);
50   return 1;
51 }
52 
53 /* LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. */
54 # ifndef LOCKFILE_FAIL_IMMEDIATELY
55 #  define LOCKFILE_FAIL_IMMEDIATELY 1
56 # endif
57 
58 /* Acquire a lock. */
59 static BOOL
do_lock(HANDLE h,int non_blocking,int exclusive)60 do_lock (HANDLE h, int non_blocking, int exclusive)
61 {
62   BOOL res;
63   DWORD size_lower, size_upper;
64   OVERLAPPED ovlp;
65   int flags = 0;
66 
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;
71 
72   /* Start offset is 0, and also zero the remaining members of this struct. */
73   memset (&ovlp, 0, sizeof ovlp);
74 
75   if (non_blocking)
76     flags |= LOCKFILE_FAIL_IMMEDIATELY;
77   if (exclusive)
78     flags |= LOCKFILE_EXCLUSIVE_LOCK;
79 
80   return LockFileEx (h, flags, 0, size_lower, size_upper, &ovlp);
81 }
82 
83 /* Unlock reader or exclusive lock. */
84 static BOOL
do_unlock(HANDLE h)85 do_unlock (HANDLE h)
86 {
87   int res;
88   DWORD size_lower, size_upper;
89 
90   res = file_size (h, &size_lower, &size_upper);
91   if (!res)
92     return 0;
93 
94   return UnlockFile (h, 0, 0, size_lower, size_upper);
95 }
96 
97 /* Now our BSD-like flock operation. */
98 int
flock(int fd,int operation)99 flock (int fd, int operation)
100 {
101   HANDLE h = (HANDLE) _get_osfhandle (fd);
102   DWORD res;
103   int non_blocking;
104 
105   if (h == INVALID_HANDLE_VALUE)
106     {
107       errno = EBADF;
108       return -1;
109     }
110 
111   non_blocking = operation & LOCK_NB;
112   operation &= ~LOCK_NB;
113 
114   switch (operation)
115     {
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;
128     }
129 
130   /* Map Windows errors into Unix errnos.  As usual MSDN fails to
131    * document the permissible error codes.
132    */
133   if (!res)
134     {
135       DWORD err = GetLastError ();
136       switch (err)
137         {
138           /* This means someone else is holding a lock. */
139         case ERROR_LOCK_VIOLATION:
140           errno = EAGAIN;
141           break;
142 
143           /* Out of memory. */
144         case ERROR_NOT_ENOUGH_MEMORY:
145           errno = ENOMEM;
146           break;
147 
148         case ERROR_BAD_COMMAND:
149           errno = EINVAL;
150           break;
151 
152           /* Unlikely to be other errors, but at least don't lose the
153            * error code.
154            */
155         default:
156           errno = err;
157         }
158 
159       return -1;
160     }
161 
162   return 0;
163 }
164 
165 #else /* !Windows */
166 
167 # ifdef HAVE_STRUCT_FLOCK_L_TYPE
168 /* We know how to implement flock in terms of fcntl. */
169 
170 #  include <fcntl.h>
171 
172 #  ifdef HAVE_UNISTD_H
173 #   include <unistd.h>
174 #  endif
175 
176 #  include <errno.h>
177 #  include <string.h>
178 
179 int
flock(int fd,int operation)180 flock (int fd, int operation)
181 {
182   int cmd, r;
183   struct flock fl;
184 
185   if (operation & LOCK_NB)
186     cmd = F_SETLK;
187   else
188     cmd = F_SETLKW;
189   operation &= ~LOCK_NB;
190 
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". */
194 
195   switch (operation)
196     {
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;
209     }
210 
211   r = fcntl (fd, cmd, &fl);
212   if (r == -1 && errno == EACCES)
213     errno = EAGAIN;
214 
215   return r;
216 }
217 
218 # else /* !HAVE_STRUCT_FLOCK_L_TYPE */
219 
220 #  error "This platform lacks flock function, and Gnulib doesn't provide a replacement. This is a bug in Gnulib."
221 
222 # endif /* !HAVE_STRUCT_FLOCK_L_TYPE */
223 
224 #endif /* !Windows */
225