diff options
Diffstat (limited to 'mysys/my_delete.c')
-rw-r--r-- | mysys/my_delete.c | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/mysys/my_delete.c b/mysys/my_delete.c new file mode 100644 index 00000000..6854033f --- /dev/null +++ b/mysys/my_delete.c @@ -0,0 +1,261 @@ +/* Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +#include "mysys_priv.h" +#include "mysys_err.h" +#include <my_sys.h> + +#ifdef _WIN32 +#include <direct.h> /* rmdir */ +static int my_win_unlink(const char *name); +#endif + +CREATE_NOSYMLINK_FUNCTION( + unlink_nosymlinks(const char *pathname), + unlinkat(dfd, filename, 0), + unlink(pathname) +); + +int my_delete(const char *name, myf MyFlags) +{ + int err; + DBUG_ENTER("my_delete"); + DBUG_PRINT("my",("name %s MyFlags %lu", name, MyFlags)); + +#ifdef _WIN32 + err = my_win_unlink(name); +#else + if (MyFlags & MY_NOSYMLINKS) + err= unlink_nosymlinks(name); + else + err= unlink(name); +#endif + + if ((MyFlags & MY_IGNORE_ENOENT) && errno == ENOENT) + DBUG_RETURN(0); + + if (err) + { + my_errno= errno; + if (MyFlags & (MY_FAE+MY_WME)) + my_error(EE_DELETE, MYF(ME_BELL), name, errno); + } + else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(name, MyFlags)) + err= -1; + DBUG_RETURN(err); +} /* my_delete */ + + +#if defined (_WIN32) + +/* + Delete file. + + The function also makes best effort to minimize number of errors, + where another program (or thread in the current program) has the the same file + open. + + We're using several tricks to prevent the errors, such as + + - Windows 10 "posix semantics" delete + + - Avoid the error by using CreateFile() with FILE_FLAG_DELETE_ON_CLOSE, instead + of DeleteFile() + + - If file which is deleted (delete on close) but has not entirely gone, + because it is still opened by some app, an attempt to trcreate file with the + same name would result in yet another error. The workaround here is renaming + a file to unique name. + + Symbolic link are deleted without renaming. Directories are not deleted. +*/ +#include <my_rdtsc.h> + +static int my_win_unlink(const char *name) +{ + HANDLE handle= INVALID_HANDLE_VALUE; + DWORD attributes; + uint last_error; + char unique_filename[MAX_PATH + 35]; + unsigned long long tsc; /* time stamp counter, for unique filename*/ + int retries; + DBUG_ENTER("my_win_unlink"); + + DBUG_INJECT_FILE_SHARING_VIOLATION(name); + + for (retries= FILE_SHARING_VIOLATION_RETRIES; ; retries--) + { + attributes= GetFileAttributes(name); + if (attributes == INVALID_FILE_ATTRIBUTES) + { + last_error= GetLastError(); + DBUG_PRINT("error", + ("GetFileAttributes(%s) failed with %u\n", name, last_error)); + goto error; + } + + if (attributes & FILE_ATTRIBUTE_DIRECTORY) + { + DBUG_PRINT("error", ("can't remove %s - it is a directory\n", name)); + errno= EINVAL; + DBUG_RETURN(-1); + } + + if (attributes & FILE_ATTRIBUTE_REPARSE_POINT) + { + /* Symbolic link. Delete link, the not target */ + if (!DeleteFile(name)) + { + last_error= GetLastError(); + DBUG_PRINT("error", + ("DeleteFile(%s) failed with %u\n", name, last_error)); + goto error; + } + DBUG_RETURN(0); + } + + /* + Try Windows 10 method, delete with "posix semantics" (file is not + visible, and creating a file with the same name won't fail, even if it + the file was open) + */ + handle= CreateFile(name, DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, 0, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + /* 0x3 = FILE_DISPOSITION_FLAG_DELETE | FILE_DISPOSITION_FLAG_POSIX_SEMANTICS */ + struct {DWORD _Flags;} disp= {0x3}; + BOOL ok= SetFileInformationByHandle( + handle, (FILE_INFO_BY_HANDLE_CLASS) 21, &disp, sizeof(disp)); + CloseHandle(handle); + if (ok) + DBUG_RETURN(0); + } + + handle= CreateFile(name, DELETE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (handle != INVALID_HANDLE_VALUE) + { + /* + We opened file without sharing flags (exclusive), no one else has this + file opened, thus it is safe to close handle to remove it. No renaming + is necessary. + */ + CloseHandle(handle); + DBUG_RETURN(0); + } + + /* + Can't open file exclusively, hence the file must be already opened by + someone else. Open it for delete (with all FILE_SHARE flags set), + rename to unique name, close. + */ + handle= CreateFile(name, DELETE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); + if (handle == INVALID_HANDLE_VALUE) + { + last_error= GetLastError(); + DBUG_PRINT( + "error", + ("CreateFile(%s) with FILE_FLAG_DELETE_ON_CLOSE failed with %u\n", + name, last_error)); + goto error; + } + + tsc= my_timer_cycles(); + my_snprintf(unique_filename, sizeof(unique_filename), "%s.%llx.deleted", + name, tsc); + if (!MoveFile(name, unique_filename)) + { + DBUG_PRINT("warning", + ("moving %s to unique filename failed, error %lu\n", name, + GetLastError())); + } + CloseHandle(handle); + DBUG_RETURN(0); + +error: + if (last_error != ERROR_SHARING_VIOLATION || retries == 0) + { + my_osmaperr(last_error); + DBUG_RETURN(-1); + } + DBUG_CLEAR_FILE_SHARING_VIOLATION(); + Sleep(FILE_SHARING_VIOLATION_DELAY_MS); + } +} +#endif + +/* + Remove directory recursively. +*/ +int my_rmtree(const char *dir, myf MyFlags) +{ + char path[FN_REFLEN]; + char sep[] = { FN_LIBCHAR, 0 }; + int err = 0; + size_t i; + + MY_DIR *dir_info = my_dir(dir, MYF(MY_DONT_SORT | MY_WANT_STAT)); + if (!dir_info) + return 1; + + for (i = 0; i < dir_info->number_of_files; i++) + { + FILEINFO *file = dir_info->dir_entry + i; + /* Skip "." and ".." */ + if (!strcmp(file->name, ".") || !strcmp(file->name, "..")) + continue; + + strxnmov(path, sizeof(path), dir, sep, file->name, NULL); + + if (!MY_S_ISDIR(file->mystat->st_mode)) + { + err = my_delete(path, MyFlags); +#ifdef _WIN32 + /* + On Windows, check and possible reset readonly attribute. + my_delete(), or DeleteFile does not remove theses files. + */ + if (err) + { + DWORD attr = GetFileAttributes(path); + if (attr != INVALID_FILE_ATTRIBUTES && + (attr & FILE_ATTRIBUTE_READONLY)) + { + SetFileAttributes(path, attr &~FILE_ATTRIBUTE_READONLY); + err = my_delete(path, MyFlags); + } + } +#endif + } + else + err = my_rmtree(path, MyFlags); + + if (err) + break; + } + + my_dirend(dir_info); + + if (!err) + err = rmdir(dir); + + return err; +} + + |