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