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 <my_sys.h>
19
20 #ifdef _WIN32
21 #include <direct.h> /* rmdir */
22 static int my_win_unlink(const char *name);
23 #endif
24
25 CREATE_NOSYMLINK_FUNCTION(
26 unlink_nosymlinks(const char *pathname),
27 unlinkat(dfd, filename, 0),
28 unlink(pathname)
29 );
30
my_delete(const char * name,myf MyFlags)31 int my_delete(const char *name, myf MyFlags)
32 {
33 int err;
34 DBUG_ENTER("my_delete");
35 DBUG_PRINT("my",("name %s MyFlags %lu", name, MyFlags));
36
37 #ifdef _WIN32
38 err = my_win_unlink(name);
39 #else
40 if (MyFlags & MY_NOSYMLINKS)
41 err= unlink_nosymlinks(name);
42 else
43 err= unlink(name);
44 #endif
45
46 if ((MyFlags & MY_IGNORE_ENOENT) && errno == ENOENT)
47 DBUG_RETURN(0);
48
49 if (err)
50 {
51 my_errno= errno;
52 if (MyFlags & (MY_FAE+MY_WME))
53 my_error(EE_DELETE, MYF(ME_BELL), name, errno);
54 }
55 else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(name, MyFlags))
56 err= -1;
57 DBUG_RETURN(err);
58 } /* my_delete */
59
60
61 #if defined (_WIN32)
62
63 /*
64 Delete file.
65
66 The function also makes best effort to minimize number of errors,
67 where another program (or thread in the current program) has the the same file
68 open.
69
70 We're using several tricks to prevent the errors, such as
71
72 - Windows 10 "posix semantics" delete
73
74 - Avoid the error by using CreateFile() with FILE_FLAG_DELETE_ON_CLOSE, instead
75 of DeleteFile()
76
77 - If file which is deleted (delete on close) but has not entirely gone,
78 because it is still opened by some app, an attempt to trcreate file with the
79 same name would result in yet another error. The workaround here is renaming
80 a file to unique name.
81
82 Symbolic link are deleted without renaming. Directories are not deleted.
83 */
my_win_unlink(const char * name)84 static int my_win_unlink(const char *name)
85 {
86 HANDLE handle= INVALID_HANDLE_VALUE;
87 DWORD attributes;
88 uint last_error;
89 char unique_filename[MAX_PATH + 35];
90 unsigned long long tsc; /* time stamp counter, for unique filename*/
91
92 DBUG_ENTER("my_win_unlink");
93 attributes= GetFileAttributes(name);
94 if (attributes == INVALID_FILE_ATTRIBUTES)
95 {
96 last_error= GetLastError();
97 DBUG_PRINT("error",("GetFileAttributes(%s) failed with %u\n", name, last_error));
98 goto error;
99 }
100
101 if (attributes & FILE_ATTRIBUTE_DIRECTORY)
102 {
103 DBUG_PRINT("error",("can't remove %s - it is a directory\n", name));
104 errno= EINVAL;
105 DBUG_RETURN(-1);
106 }
107
108 if (attributes & FILE_ATTRIBUTE_REPARSE_POINT)
109 {
110 /* Symbolic link. Delete link, the not target */
111 if (!DeleteFile(name))
112 {
113 last_error= GetLastError();
114 DBUG_PRINT("error",("DeleteFile(%s) failed with %u\n", name,last_error));
115 goto error;
116 }
117 DBUG_RETURN(0);
118 }
119
120 /*
121 Try Windows 10 method, delete with "posix semantics" (file is not visible, and creating
122 a file with the same name won't fail, even if it the fiile was open)
123 */
124 struct
125 {
126 DWORD _Flags;
127 } disp={0x3};
128 /* 0x3 = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS */
129
130 handle= CreateFile(name, DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
131 NULL, OPEN_EXISTING, 0, NULL);
132 if (handle != INVALID_HANDLE_VALUE)
133 {
134 BOOL ok= SetFileInformationByHandle(handle,
135 (FILE_INFO_BY_HANDLE_CLASS) 21, &disp, sizeof(disp));
136 CloseHandle(handle);
137 if (ok)
138 DBUG_RETURN(0);
139 }
140
141 handle= CreateFile(name, DELETE, 0, NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
142 if (handle != INVALID_HANDLE_VALUE)
143 {
144 /*
145 We opened file without sharing flags (exclusive), no one else has this file
146 opened, thus it is save to close handle to remove it. No renaming is
147 necessary.
148 */
149 CloseHandle(handle);
150 DBUG_RETURN(0);
151 }
152
153 /*
154 Can't open file exclusively, hence the file must be already opened by
155 someone else. Open it for delete (with all FILE_SHARE flags set),
156 rename to unique name, close.
157 */
158 handle= CreateFile(name, DELETE, FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
159 NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
160 if (handle == INVALID_HANDLE_VALUE)
161 {
162 last_error= GetLastError();
163 DBUG_PRINT("error",
164 ("CreateFile(%s) with FILE_FLAG_DELETE_ON_CLOSE failed with %u\n",
165 name,last_error));
166 goto error;
167 }
168
169 tsc= __rdtsc();
170 my_snprintf(unique_filename,sizeof(unique_filename),"%s.%llx.deleted",
171 name, tsc);
172 if (!MoveFile(name, unique_filename))
173 {
174 DBUG_PRINT("warning", ("moving %s to unique filename failed, error %lu\n",
175 name,GetLastError()));
176 }
177
178 CloseHandle(handle);
179 DBUG_RETURN(0);
180
181 error:
182 my_osmaperr(last_error);
183 DBUG_RETURN(-1);
184 }
185 #endif
186
187 /*
188 Remove directory recursively.
189 */
my_rmtree(const char * dir,myf MyFlags)190 int my_rmtree(const char *dir, myf MyFlags)
191 {
192 char path[FN_REFLEN];
193 char sep[] = { FN_LIBCHAR, 0 };
194 int err = 0;
195 uint i;
196
197 MY_DIR *dir_info = my_dir(dir, MYF(MY_DONT_SORT | MY_WANT_STAT));
198 if (!dir_info)
199 return 1;
200
201 for (i = 0; i < dir_info->number_of_files; i++)
202 {
203 FILEINFO *file = dir_info->dir_entry + i;
204 /* Skip "." and ".." */
205 if (!strcmp(file->name, ".") || !strcmp(file->name, ".."))
206 continue;
207
208 strxnmov(path, sizeof(path), dir, sep, file->name, NULL);
209
210 if (!MY_S_ISDIR(file->mystat->st_mode))
211 {
212 err = my_delete(path, MyFlags);
213 #ifdef _WIN32
214 /*
215 On Windows, check and possible reset readonly attribute.
216 my_delete(), or DeleteFile does not remove theses files.
217 */
218 if (err)
219 {
220 DWORD attr = GetFileAttributes(path);
221 if (attr != INVALID_FILE_ATTRIBUTES &&
222 (attr & FILE_ATTRIBUTE_READONLY))
223 {
224 SetFileAttributes(path, attr &~FILE_ATTRIBUTE_READONLY);
225 err = my_delete(path, MyFlags);
226 }
227 }
228 #endif
229 }
230 else
231 err = my_rmtree(path, MyFlags);
232
233 if (err)
234 break;
235 }
236
237 my_dirend(dir_info);
238
239 if (!err)
240 err = rmdir(dir);
241
242 return err;
243 }
244
245
246