1 /* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 Without limiting anything contained in the foregoing, this file,
15 which is part of C Driver for MySQL (Connector/C), is also subject to the
16 Universal FOSS Exception, version 1.0, a copy of which can be found at
17 http://oss.oracle.com/licenses/universal-foss-exception.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License, version 2.0, for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
27
28 #include "mysys_priv.h"
29 #include "mysys_err.h"
30 #include <errno.h>
31 #undef MY_HOW_OFTEN_TO_ALARM
32 #define MY_HOW_OFTEN_TO_ALARM ((int) my_time_to_wait_for_lock)
33 #ifdef NO_ALARM_LOOP
34 #undef NO_ALARM_LOOP
35 #endif
36 #include <my_alarm.h>
37
38 #ifdef _WIN32
39 #define WIN_LOCK_INFINITE -1
40 #define WIN_LOCK_SLEEP_MILLIS 100
41
win_lock(File fd,int locktype,my_off_t start,my_off_t length,int timeout_sec)42 static int win_lock(File fd, int locktype, my_off_t start, my_off_t length,
43 int timeout_sec)
44 {
45 LARGE_INTEGER liOffset,liLength;
46 DWORD dwFlags;
47 OVERLAPPED ov= {0};
48 HANDLE hFile= (HANDLE)my_get_osfhandle(fd);
49 DWORD lastError= 0;
50 int i;
51 int timeout_millis= timeout_sec * 1000;
52
53 DBUG_ENTER("win_lock");
54
55 liOffset.QuadPart= start;
56 liLength.QuadPart= length;
57
58 ov.Offset= liOffset.LowPart;
59 ov.OffsetHigh= liOffset.HighPart;
60
61 if (locktype == F_UNLCK)
62 {
63 if (UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov))
64 DBUG_RETURN(0);
65 /*
66 For compatibility with fcntl implementation, ignore error,
67 if region was not locked
68 */
69 if (GetLastError() == ERROR_NOT_LOCKED)
70 {
71 SetLastError(0);
72 DBUG_RETURN(0);
73 }
74 goto error;
75 }
76 else if (locktype == F_RDLCK)
77 /* read lock is mapped to a shared lock. */
78 dwFlags= 0;
79 else
80 /* write lock is mapped to an exclusive lock. */
81 dwFlags= LOCKFILE_EXCLUSIVE_LOCK;
82
83 /*
84 Drop old lock first to avoid double locking.
85 During analyze of Bug#38133 (Myisamlog test fails on Windows)
86 I met the situation that the program myisamlog locked the file
87 exclusively, then additionally shared, then did one unlock, and
88 then blocked on an attempt to lock it exclusively again.
89 Unlocking before every lock fixed the problem.
90 Note that this introduces a race condition. When the application
91 wants to convert an exclusive lock into a shared one, it will now
92 first unlock the file and then lock it shared. A waiting exclusive
93 lock could step in here. For reasons described in Bug#38133 and
94 Bug#41124 (Server hangs on Windows with --external-locking after
95 INSERT...SELECT) and in the review thread at
96 http://lists.mysql.com/commits/60721 it seems to be the better
97 option than not to unlock here.
98 If one day someone notices a way how to do file lock type changes
99 on Windows without unlocking before taking the new lock, please
100 change this code accordingly to fix the race condition.
101 */
102 if (!UnlockFileEx(hFile, 0, liLength.LowPart, liLength.HighPart, &ov) &&
103 (GetLastError() != ERROR_NOT_LOCKED))
104 goto error;
105
106 if (timeout_sec == WIN_LOCK_INFINITE)
107 {
108 if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
109 DBUG_RETURN(0);
110 goto error;
111 }
112
113 dwFlags|= LOCKFILE_FAIL_IMMEDIATELY;
114 timeout_millis= timeout_sec * 1000;
115 /* Try lock in a loop, until the lock is acquired or timeout happens */
116 for(i= 0; ;i+= WIN_LOCK_SLEEP_MILLIS)
117 {
118 if (LockFileEx(hFile, dwFlags, 0, liLength.LowPart, liLength.HighPart, &ov))
119 DBUG_RETURN(0);
120
121 if (GetLastError() != ERROR_LOCK_VIOLATION)
122 goto error;
123
124 if (i >= timeout_millis)
125 break;
126 Sleep(WIN_LOCK_SLEEP_MILLIS);
127 }
128
129 /* timeout */
130 errno= EAGAIN;
131 DBUG_RETURN(-1);
132
133 error:
134 my_osmaperr(GetLastError());
135 DBUG_RETURN(-1);
136 }
137 #endif
138
139
140
141 /*
142 Lock a part of a file
143
144 RETURN VALUE
145 0 Success
146 -1 An error has occured and 'my_errno' is set
147 to indicate the actual error code.
148 */
149
my_lock(File fd,int locktype,my_off_t start,my_off_t length,myf MyFlags)150 int my_lock(File fd, int locktype, my_off_t start, my_off_t length,
151 myf MyFlags)
152 {
153 #ifdef HAVE_FCNTL
154 int value;
155 ALARM_VARIABLES;
156 #endif
157
158 DBUG_ENTER("my_lock");
159 DBUG_PRINT("my",("fd: %d Op: %d start: %ld Length: %ld MyFlags: %d",
160 fd,locktype,(long) start,(long) length,MyFlags));
161 if (my_disable_locking)
162 DBUG_RETURN(0);
163
164 #if defined(_WIN32)
165 {
166 int timeout_sec;
167 if (MyFlags & MY_DONT_WAIT)
168 timeout_sec= 0;
169 else
170 timeout_sec= WIN_LOCK_INFINITE;
171
172 if (win_lock(fd, locktype, start, length, timeout_sec) == 0)
173 DBUG_RETURN(0);
174 }
175 #else
176 #if defined(HAVE_FCNTL)
177 {
178 struct flock lock;
179
180 lock.l_type= (short) locktype;
181 lock.l_whence= SEEK_SET;
182 lock.l_start= (off_t) start;
183 lock.l_len= (off_t) length;
184
185 if (MyFlags & MY_DONT_WAIT)
186 {
187 if (fcntl(fd,F_SETLK,&lock) != -1) /* Check if we can lock */
188 DBUG_RETURN(0); /* Ok, file locked */
189 DBUG_PRINT("info",("Was locked, trying with alarm"));
190 ALARM_INIT;
191 while ((value=fcntl(fd,F_SETLKW,&lock)) && ! ALARM_TEST &&
192 errno == EINTR)
193 { /* Setup again so we don`t miss it */
194 ALARM_REINIT;
195 }
196 ALARM_END;
197 if (value != -1)
198 DBUG_RETURN(0);
199 if (errno == EINTR)
200 errno=EAGAIN;
201 }
202 else if (fcntl(fd,F_SETLKW,&lock) != -1) /* Wait until a lock */
203 DBUG_RETURN(0);
204 }
205 #else
206 if (MyFlags & MY_SEEK_NOT_DONE)
207 {
208 if (my_seek(fd,start,MY_SEEK_SET,MYF(MyFlags & ~MY_SEEK_NOT_DONE))
209 == MY_FILEPOS_ERROR)
210 {
211 /*
212 If an error has occured in my_seek then we will already
213 have an error code in my_errno; Just return error code.
214 */
215 DBUG_RETURN(-1);
216 }
217 }
218 if (lockf(fd,locktype,length) != -1)
219 DBUG_RETURN(0);
220 #endif /* HAVE_FCNTL */
221 #endif /* HAVE_LOCKING */
222
223 /* We got an error. We don't want EACCES errors */
224 my_errno=(errno == EACCES) ? EAGAIN : errno ? errno : -1;
225
226 if (MyFlags & MY_WME)
227 {
228 char errbuf[MYSYS_STRERROR_SIZE];
229 if (locktype == F_UNLCK)
230 my_error(EE_CANTUNLOCK, MYF(ME_BELL+ME_WAITTANG),
231 my_errno, my_strerror(errbuf, sizeof(errbuf), my_errno));
232 else
233 my_error(EE_CANTLOCK, MYF(ME_BELL+ME_WAITTANG),
234 my_errno, my_strerror(errbuf, sizeof(errbuf), my_errno));
235 }
236 DBUG_PRINT("error",("my_errno: %d (%d)",my_errno,errno));
237 DBUG_RETURN(-1);
238 } /* my_lock */
239