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