Imported Upstream version 2.8.3
[platform/upstream/man-db.git] / gnulib / 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 General Public
13    License as published by the Free Software Foundation; either
14    version 3 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    General Public License for more details.
20
21    You should have received a copy of the GNU 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 */