unistr/u*-chr: test multibyte sequences
[gnulib.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-2010 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 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)
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 #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 */