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