Skip to content

Commit d360fa6

Browse files
committed
MDEV-30162 Fix occasional "Permission denied" on Windows caused by buggy 3rd party
Add retry logic for CreateFile, DeleteFile, or MoveFile when GetLastError() is ERROR_SHARING_VIOLATION.
1 parent 2beede9 commit d360fa6

File tree

6 files changed

+194
-107
lines changed

6 files changed

+194
-107
lines changed

mysql-test/main/windows_debug.result

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
# mdev-23741 sharing violation when renaming .frm file in ALTER
2-
CREATE TABLE t(i int);
3-
SET STATEMENT debug_dbug='+d,rename_sharing_violation' FOR ALTER TABLE t ADD PRIMARY KEY (i);
2+
SET @saved_dbug = @@SESSION.debug_dbug;
3+
SET debug_dbug='+d,file_sharing_violation';
4+
CREATE TABLE t(i int) ENGINE=ARIA;
5+
ALTER TABLE t ADD PRIMARY KEY (i);
6+
FLUSH TABLES t;
7+
SELECT * FROM t;
8+
i
49
DROP TABLE t;
10+
SET debug_dbug=@saved_dbug;

mysql-test/main/windows_debug.test

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,16 @@
44
--source include/windows.inc
55

66
--echo # mdev-23741 sharing violation when renaming .frm file in ALTER
7-
CREATE TABLE t(i int);
8-
SET STATEMENT debug_dbug='+d,rename_sharing_violation' FOR ALTER TABLE t ADD PRIMARY KEY (i);
7+
8+
SET @saved_dbug = @@SESSION.debug_dbug;
9+
SET debug_dbug='+d,file_sharing_violation';
10+
11+
CREATE TABLE t(i int) ENGINE=ARIA;
12+
ALTER TABLE t ADD PRIMARY KEY (i);
13+
FLUSH TABLES t;
14+
SELECT * FROM t;
915
DROP TABLE t;
1016

17+
SET debug_dbug=@saved_dbug;
18+
1119
#End of 10.3 tests

mysys/my_delete.c

Lines changed: 94 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -79,107 +79,122 @@ int my_delete(const char *name, myf MyFlags)
7979
a file to unique name.
8080
8181
Symbolic link are deleted without renaming. Directories are not deleted.
82-
*/
82+
*/
83+
8384
static int my_win_unlink(const char *name)
8485
{
8586
HANDLE handle= INVALID_HANDLE_VALUE;
8687
DWORD attributes;
8788
uint last_error;
8889
char unique_filename[MAX_PATH + 35];
8990
unsigned long long tsc; /* time stamp counter, for unique filename*/
90-
91+
int retries;
9192
DBUG_ENTER("my_win_unlink");
92-
attributes= GetFileAttributes(name);
93-
if (attributes == INVALID_FILE_ATTRIBUTES)
94-
{
95-
last_error= GetLastError();
96-
DBUG_PRINT("error",("GetFileAttributes(%s) failed with %u\n", name, last_error));
97-
goto error;
98-
}
9993

100-
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
101-
{
102-
DBUG_PRINT("error",("can't remove %s - it is a directory\n", name));
103-
errno= EINVAL;
104-
DBUG_RETURN(-1);
105-
}
106-
107-
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
94+
DBUG_INJECT_FILE_SHARING_VIOLATION(name);
95+
96+
for (retries= FILE_SHARING_VIOLATION_RETRIES; ; retries--)
10897
{
109-
/* Symbolic link. Delete link, the not target */
110-
if (!DeleteFile(name))
98+
attributes= GetFileAttributes(name);
99+
if (attributes == INVALID_FILE_ATTRIBUTES)
111100
{
112-
last_error= GetLastError();
113-
DBUG_PRINT("error",("DeleteFile(%s) failed with %u\n", name,last_error));
114-
goto error;
101+
last_error= GetLastError();
102+
DBUG_PRINT("error",
103+
("GetFileAttributes(%s) failed with %u\n", name, last_error));
104+
goto error;
115105
}
116-
DBUG_RETURN(0);
117-
}
118106

119-
/*
120-
Try Windows 10 method, delete with "posix semantics" (file is not visible, and creating
121-
a file with the same name won't fail, even if it the fiile was open)
122-
*/
123-
struct
124-
{
125-
DWORD _Flags;
126-
} disp={0x3};
127-
/* 0x3 = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS */
107+
if (attributes & FILE_ATTRIBUTE_DIRECTORY)
108+
{
109+
DBUG_PRINT("error", ("can't remove %s - it is a directory\n", name));
110+
errno= EINVAL;
111+
DBUG_RETURN(-1);
112+
}
128113

129-
handle= CreateFile(name, DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
130-
NULL, OPEN_EXISTING, 0, NULL);
131-
if (handle != INVALID_HANDLE_VALUE)
132-
{
133-
BOOL ok= SetFileInformationByHandle(handle,
134-
(FILE_INFO_BY_HANDLE_CLASS) 21, &disp, sizeof(disp));
135-
CloseHandle(handle);
136-
if (ok)
114+
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
115+
{
116+
/* Symbolic link. Delete link, the not target */
117+
if (!DeleteFile(name))
118+
{
119+
last_error= GetLastError();
120+
DBUG_PRINT("error",
121+
("DeleteFile(%s) failed with %u\n", name, last_error));
122+
goto error;
123+
}
137124
DBUG_RETURN(0);
138-
}
125+
}
139126

140-
handle= CreateFile(name, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
141-
if (handle != INVALID_HANDLE_VALUE)
142-
{
143127
/*
144-
We opened file without sharing flags (exclusive), no one else has this file
145-
opened, thus it is save to close handle to remove it. No renaming is
146-
necessary.
128+
Try Windows 10 method, delete with "posix semantics" (file is not
129+
visible, and creating a file with the same name won't fail, even if it
130+
the file was open)
147131
*/
148-
CloseHandle(handle);
149-
DBUG_RETURN(0);
150-
}
132+
handle= CreateFile(name, DELETE,
133+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
134+
NULL, OPEN_EXISTING, 0, NULL);
135+
if (handle != INVALID_HANDLE_VALUE)
136+
{
137+
/* 0x3 = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS */
138+
struct {DWORD _Flags;} disp= {0x3};
139+
BOOL ok= SetFileInformationByHandle(
140+
handle, (FILE_INFO_BY_HANDLE_CLASS) 21, &disp, sizeof(disp));
141+
CloseHandle(handle);
142+
if (ok)
143+
DBUG_RETURN(0);
144+
}
151145

152-
/*
153-
Can't open file exclusively, hence the file must be already opened by
154-
someone else. Open it for delete (with all FILE_SHARE flags set),
155-
rename to unique name, close.
156-
*/
157-
handle= CreateFile(name, DELETE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
158-
NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
159-
if (handle == INVALID_HANDLE_VALUE)
160-
{
161-
last_error= GetLastError();
162-
DBUG_PRINT("error",
163-
("CreateFile(%s) with FILE_FLAG_DELETE_ON_CLOSE failed with %u\n",
164-
name,last_error));
165-
goto error;
166-
}
146+
handle= CreateFile(name, DELETE, 0, NULL, OPEN_EXISTING,
147+
FILE_FLAG_DELETE_ON_CLOSE, NULL);
148+
if (handle != INVALID_HANDLE_VALUE)
149+
{
150+
/*
151+
We opened file without sharing flags (exclusive), no one else has this
152+
file opened, thus it is safe to close handle to remove it. No renaming
153+
is necessary.
154+
*/
155+
CloseHandle(handle);
156+
DBUG_RETURN(0);
157+
}
167158

168-
tsc= __rdtsc();
169-
my_snprintf(unique_filename,sizeof(unique_filename),"%s.%llx.deleted",
170-
name, tsc);
171-
if (!MoveFile(name, unique_filename))
172-
{
173-
DBUG_PRINT("warning", ("moving %s to unique filename failed, error %lu\n",
174-
name,GetLastError()));
175-
}
159+
/*
160+
Can't open file exclusively, hence the file must be already opened by
161+
someone else. Open it for delete (with all FILE_SHARE flags set),
162+
rename to unique name, close.
163+
*/
164+
handle= CreateFile(name, DELETE,
165+
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
166+
NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
167+
if (handle == INVALID_HANDLE_VALUE)
168+
{
169+
last_error= GetLastError();
170+
DBUG_PRINT(
171+
"error",
172+
("CreateFile(%s) with FILE_FLAG_DELETE_ON_CLOSE failed with %u\n",
173+
name, last_error));
174+
goto error;
175+
}
176+
177+
tsc= __rdtsc();
178+
my_snprintf(unique_filename, sizeof(unique_filename), "%s.%llx.deleted",
179+
name, tsc);
180+
if (!MoveFile(name, unique_filename))
181+
{
182+
DBUG_PRINT("warning",
183+
("moving %s to unique filename failed, error %lu\n", name,
184+
GetLastError()));
185+
}
186+
CloseHandle(handle);
187+
DBUG_RETURN(0);
176188

177-
CloseHandle(handle);
178-
DBUG_RETURN(0);
179-
180189
error:
181-
my_osmaperr(last_error);
182-
DBUG_RETURN(-1);
190+
if (last_error != ERROR_SHARING_VIOLATION || retries == 0)
191+
{
192+
my_osmaperr(last_error);
193+
DBUG_RETURN(-1);
194+
}
195+
DBUG_CLEAR_FILE_SHARING_VIOLATION();
196+
Sleep(FILE_SHARING_VIOLATION_DELAY_MS);
197+
}
183198
}
184199
#endif
185200

mysys/my_rename.c

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,41 +34,24 @@
3434
*/
3535
static BOOL win_rename_with_retries(const char *from, const char *to)
3636
{
37-
#ifndef DBUG_OFF
38-
FILE *fp = NULL;
39-
DBUG_EXECUTE_IF("rename_sharing_violation",
40-
{
41-
fp= fopen(from, "r");
42-
DBUG_ASSERT(fp);
43-
}
44-
);
45-
#endif
37+
DBUG_INJECT_FILE_SHARING_VIOLATION(from);
4638

47-
for (int retry= RENAME_MAX_RETRIES; retry--;)
39+
for (int retry= FILE_SHARING_VIOLATION_RETRIES; retry--;)
4840
{
4941
BOOL ret= MoveFileEx(from, to,
5042
MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING);
5143

44+
DBUG_CLEAR_FILE_SHARING_VIOLATION();
45+
5246
if (ret)
5347
return ret;
5448

5549
DWORD last_error= GetLastError();
50+
5651
if (last_error == ERROR_SHARING_VIOLATION ||
5752
last_error == ERROR_ACCESS_DENIED)
5853
{
59-
#ifndef DBUG_OFF
60-
/*
61-
If error was injected in via DBUG_EXECUTE_IF, close the file
62-
that is causing ERROR_SHARING_VIOLATION, so that retry succeeds.
63-
*/
64-
if (fp)
65-
{
66-
fclose(fp);
67-
fp= NULL;
68-
}
69-
#endif
70-
71-
Sleep(10);
54+
Sleep(FILE_SHARING_VIOLATION_DELAY_MS);
7255
}
7356
else
7457
return ret;

mysys/my_winfile.c

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,42 @@ static int my_get_open_flags(File fd)
102102
DBUG_RETURN(my_file_info[fd].oflag);
103103
}
104104

105+
/*
106+
CreateFile with retry logic.
107+
108+
Uses retries, to avoid or reduce CreateFile errors
109+
with ERROR_SHARING_VIOLATION, in case the file is opened
110+
by another process, which used incompatible sharing
111+
flags when opening.
112+
113+
See Windows' CreateFile() documentation for details.
114+
*/
115+
static HANDLE my_create_file_with_retries(
116+
LPCSTR lpFileName, DWORD dwDesiredAccess,
117+
DWORD dwShareMode,
118+
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
119+
DWORD dwCreationDisposition,
120+
DWORD dwFlagsAndAttributes,
121+
HANDLE hTemplateFile)
122+
{
123+
int retries;
124+
DBUG_INJECT_FILE_SHARING_VIOLATION(lpFileName);
125+
126+
for (retries = FILE_SHARING_VIOLATION_RETRIES;;)
127+
{
128+
HANDLE h= CreateFile(lpFileName, dwDesiredAccess, dwShareMode,
129+
lpSecurityAttributes, dwCreationDisposition,
130+
dwFlagsAndAttributes, hTemplateFile);
131+
DBUG_CLEAR_FILE_SHARING_VIOLATION();
132+
133+
if (h != INVALID_HANDLE_VALUE ||
134+
GetLastError() != ERROR_SHARING_VIOLATION || --retries == 0)
135+
return h;
136+
137+
Sleep(FILE_SHARING_VIOLATION_DELAY_MS);
138+
}
139+
return INVALID_HANDLE_VALUE;
140+
}
105141

106142
/*
107143
Open a file with sharing. Similar to _sopen() from libc, but allows managing
@@ -247,7 +283,7 @@ File my_win_sopen(const char *path, int oflag, int shflag, int pmode)
247283
fileattrib|= FILE_FLAG_RANDOM_ACCESS;
248284

249285
/* try to open/create the file */
250-
if ((osfh= CreateFile(path, fileaccess, fileshare, &SecurityAttributes,
286+
if ((osfh= my_create_file_with_retries(path, fileaccess, fileshare, &SecurityAttributes,
251287
filecreate, fileattrib, NULL)) == INVALID_HANDLE_VALUE)
252288
{
253289
DWORD last_error= GetLastError();

mysys/mysys_priv.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,45 @@ extern int my_win_fsync(File fd);
176176
extern File my_win_dup(File fd);
177177
extern File my_win_sopen(const char *path, int oflag, int shflag, int perm);
178178
extern File my_open_osfhandle(HANDLE handle, int oflag);
179+
180+
181+
/*
182+
The following constants are related to retries when file operation fails with
183+
ERROR_FILE_SHARING_VIOLATION
184+
*/
185+
#define FILE_SHARING_VIOLATION_RETRIES 50
186+
#define FILE_SHARING_VIOLATION_DELAY_MS 10
187+
188+
189+
/* DBUG injecting of ERROR_FILE_SHARING_VIOLATION */
190+
#ifndef DBUG_OFF
191+
/* Open file, without sharing. if specific DBUG keyword is set */
192+
#define DBUG_INJECT_FILE_SHARING_VIOLATION(filename) \
193+
FILE *fp= NULL; \
194+
do \
195+
{ \
196+
DBUG_EXECUTE_IF("file_sharing_violation", \
197+
fp= _fsopen(filename, "r", _SH_DENYRW);); \
198+
} while (0)
199+
200+
/* Close the file that causes ERROR_FILE_SHARING_VIOLATION.*/
201+
#define DBUG_CLEAR_FILE_SHARING_VIOLATION() \
202+
do \
203+
{ \
204+
if (fp) \
205+
{ \
206+
DWORD tmp_err= GetLastError(); \
207+
fclose(fp); \
208+
SetLastError(tmp_err); \
209+
fp= NULL; \
210+
} \
211+
} while (0)
212+
213+
#else
214+
#define DBUG_INJECT_FILE_SHARING_VIOLATION(filename) do {} while (0)
215+
#define DBUG_CLEAR_FILE_SHARING_VIOLATION() do {} while (0)
216+
#endif
217+
179218
#endif
180219

181220
C_MODE_END

0 commit comments

Comments
 (0)