From 0627d902dd3499ff0dd8424624410cd9d585ef73 Mon Sep 17 00:00:00 2001 From: Leonid Yuriev Date: Fri, 9 Oct 2020 22:43:14 +0300 Subject: [PATCH] mdbx: add `mdbx_env_delete()`. Resolves https://github.com/erthink/libmdbx/issues/119 Related to https://github.com/Kerollmops/heed/issues/58 Change-Id: Iec5bf5978e45bb6843f3ed8dd06ea4d34f2895cb --- ChangeLog.md | 1 + mdbx.h | 42 ++++++++++++++++++++++++++ src/core.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++ src/lck-posix.c | 8 +++++ src/lck-windows.c | 9 ++++++ src/osal.c | 23 +++++++++++++++ src/osal.h | 5 +++- 7 files changed, 162 insertions(+), 1 deletion(-) diff --git a/ChangeLog.md b/ChangeLog.md index c7f4e94a..fe436def 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,7 @@ TODO: Added features: - Provided package for [buildroot](https://buildroot.org/). + - Added `mdbx_env_delete()` for deletion an environment files in a proper and multiprocess-safe way. Fixes: diff --git a/mdbx.h b/mdbx.h index a4282474..de540016 100644 --- a/mdbx.h +++ b/mdbx.h @@ -1801,6 +1801,48 @@ LIBMDBX_API int mdbx_env_create(MDBX_env **penv); LIBMDBX_API int mdbx_env_open(MDBX_env *env, const char *pathname, MDBX_env_flags_t flags, mdbx_mode_t mode); +/** \brief Deletion modes for \ref mdbx_env_delete() + * \ingroup c_extra + * \see mdbx_env_delete() */ +enum MDBX_env_delete_mode_t { + /** \brief Just delete the environment's files and directory if any. + * \note On POSIX systems, processes already working with the database will + * continue to work without interference until it close the environment. + * \note On Windows, the behavior of `MDB_ENV_JUST_DELETE` is different + * because the system does not support deleting files that are currently + * memory mapped. */ + MDBX_ENV_JUST_DELETE = 0, + /** Make sure that the environment is not being used by other processes, + * or return an error otherwise. */ + MDBX_ENV_ENSURE_UNUSED = 1, + /** Wait until other processes closes the environment before deletion. */ + MDBX_ENV_WAIT_FOR_UNUSED = 2, +}; +#ifndef __cplusplus +/** \c_extra c_statinfo */ +typedef enum MDBX_env_delete_mode_t MDBX_env_delete_mode_t; +#endif + +/** \brief Delete the environment files in a proper and multiprocess-safe way. + * \ingroup c_extra + * + * \param [in] pathname The pathname for the database or the directory in which + * the database files reside. + * + * \param [in] mode Special deletion mode for the environment. This + * parameter must be set to one of the values described + * above in the \ref MDBX_env_delete_mode_t section. + * + * \note The \ref MDBX_ENV_JUST_DELETE don't supported on Windows since system + * unable to delete a memory-mapped files. + * + * \returns A non-zero error value on failure and 0 on success, + * some possible errors are: + * \retval MDBX_RESULT_TRUE No corresponding files or directories were found, + * so no deletion was performed. */ +LIBMDBX_API int mdbx_env_delete(const char *pathname, + MDBX_env_delete_mode_t mode); + /** \brief Copy an MDBX environment to the specified path, with options. * \ingroup c_extra * diff --git a/src/core.c b/src/core.c index 15e53fcb..ae54192f 100644 --- a/src/core.c +++ b/src/core.c @@ -10467,6 +10467,81 @@ __cold static int mdbx_handle_env_pathname(MDBX_handle_env_pathname *result, return MDBX_SUCCESS; } +__cold int mdbx_env_delete(const char *pathname, MDBX_env_delete_mode_t mode) { + switch (mode) { + default: + return MDBX_EINVAL; + case MDBX_ENV_JUST_DELETE: + case MDBX_ENV_ENSURE_UNUSED: + case MDBX_ENV_WAIT_FOR_UNUSED: + break; + } + + MDBX_env dummy_env; + memset(&dummy_env, 0, sizeof(dummy_env)); + dummy_env.me_flags = + (mode == MDBX_ENV_ENSURE_UNUSED) ? MDBX_EXCLUSIVE : MDBX_ENV_DEFAULTS; + dummy_env.me_psize = dummy_env.me_os_psize = (unsigned)mdbx_syspagesize(); + dummy_env.me_path = (char *)pathname; + + MDBX_handle_env_pathname env_pathname; + STATIC_ASSERT(sizeof(dummy_env.me_flags) == sizeof(MDBX_env_flags_t)); + int rc = MDBX_RESULT_TRUE, + err = mdbx_handle_env_pathname( + &env_pathname, pathname, (MDBX_env_flags_t *)&dummy_env.me_flags, 0); + if (likely(err == MDBX_SUCCESS)) { + mdbx_filehandle_t clk_handle = INVALID_HANDLE_VALUE, + dxb_handle = INVALID_HANDLE_VALUE; + if (mode > MDBX_ENV_JUST_DELETE) { + err = mdbx_openfile(MDBX_OPEN_DELETE, &dummy_env, env_pathname.dxb, + &dxb_handle, 0); + err = (err == MDBX_ENOFILE) ? MDBX_SUCCESS : err; + if (err == MDBX_SUCCESS) { + err = mdbx_openfile(MDBX_OPEN_DELETE, &dummy_env, env_pathname.lck, + &clk_handle, 0); + err = (err == MDBX_ENOFILE) ? MDBX_SUCCESS : err; + } + if (err == MDBX_SUCCESS && clk_handle != INVALID_HANDLE_VALUE) + err = mdbx_lockfile(clk_handle, mode == MDBX_ENV_WAIT_FOR_UNUSED); + if (err == MDBX_SUCCESS && dxb_handle != INVALID_HANDLE_VALUE) + err = mdbx_lockfile(dxb_handle, mode == MDBX_ENV_WAIT_FOR_UNUSED); + } + + if (err == MDBX_SUCCESS) { + err = mdbx_removefile(env_pathname.dxb); + if (err == MDBX_SUCCESS) + rc = MDBX_SUCCESS; + else if (err == MDBX_ENOFILE) + err = MDBX_SUCCESS; + } + + if (err == MDBX_SUCCESS) { + err = mdbx_removefile(env_pathname.lck); + if (err == MDBX_SUCCESS) + rc = MDBX_SUCCESS; + else if (err == MDBX_ENOFILE) + err = MDBX_SUCCESS; + } + + if (err == MDBX_SUCCESS && !(dummy_env.me_flags & MDBX_NOSUBDIR)) { + err = mdbx_removedirectory(pathname); + if (err == MDBX_SUCCESS) + rc = MDBX_SUCCESS; + else if (err == MDBX_ENOFILE) + err = MDBX_SUCCESS; + } + + if (dxb_handle != INVALID_HANDLE_VALUE) + mdbx_closefile(dxb_handle); + if (clk_handle != INVALID_HANDLE_VALUE) + mdbx_closefile(clk_handle); + } else if (err == MDBX_ENOFILE) + err = MDBX_SUCCESS; + + mdbx_free(env_pathname.buffer_for_free); + return (err == MDBX_SUCCESS) ? rc : err; +} + __cold int mdbx_env_open(MDBX_env *env, const char *pathname, MDBX_env_flags_t flags, mdbx_mode_t mode) { int rc = check_env(env); diff --git a/src/lck-posix.c b/src/lck-posix.c index f879e4a9..dda7490b 100644 --- a/src/lck-posix.c +++ b/src/lck-posix.c @@ -190,6 +190,14 @@ static int lck_op(mdbx_filehandle_t fd, int cmd, int lck, off_t offset, } } +MDBX_INTERNAL_FUNC int mdbx_lockfile(mdbx_filehandle_t fd, bool wait) { +#if MDBX_USE_OFDLOCKS + if (unlikely(op_setlk == 0)) + choice_fcntl(); +#endif /* MDBX_USE_OFDLOCKS */ + return lck_op(fd, wait ? op_setlkw : op_setlk, F_WRLCK, 0, OFF_T_MAX); +} + MDBX_INTERNAL_FUNC int mdbx_rpid_set(MDBX_env *env) { assert(env->me_lfd != INVALID_HANDLE_VALUE); assert(env->me_pid > 0); diff --git a/src/lck-windows.c b/src/lck-windows.c index 68c93b31..0ddd8c7d 100644 --- a/src/lck-windows.c +++ b/src/lck-windows.c @@ -204,6 +204,15 @@ MDBX_INTERNAL_FUNC void mdbx_rdt_unlock(MDBX_env *env) { mdbx_srwlock_ReleaseShared(&env->me_remap_guard); } +MDBX_INTERNAL_FUNC int mdbx_lockfile(mdbx_filehandle_t fd, bool wait) { + return flock(fd, + wait ? LCK_EXCLUSIVE | LCK_WAITFOR + : LCK_EXCLUSIVE | LCK_DONTWAIT, + 0, LCK_MAXLEN) + ? MDBX_SUCCESS + : GetLastError(); +} + static int suspend_and_append(mdbx_handle_array_t **array, const DWORD ThreadId) { const unsigned limit = (*array)->limit; diff --git a/src/osal.c b/src/osal.c index 5c4dd2b6..92bcb79f 100644 --- a/src/osal.c +++ b/src/osal.c @@ -521,6 +521,20 @@ MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname) { #endif } +MDBX_INTERNAL_FUNC int mdbx_removedirectory(const char *pathname) { +#if defined(_WIN32) || defined(_WIN64) + const size_t wlen = mbstowcs(nullptr, pathname, INT_MAX); + if (wlen < 1 || wlen > /* MAX_PATH */ INT16_MAX) + return ERROR_INVALID_NAME; + wchar_t *const pathnameW = _alloca((wlen + 1) * sizeof(wchar_t)); + if (wlen != mbstowcs(pathnameW, pathname, wlen + 1)) + return ERROR_INVALID_NAME; + return RemoveDirectoryW(pathnameW) ? MDBX_SUCCESS : GetLastError(); +#else + return rmdir(pathname) ? errno : MDBX_SUCCESS; +#endif +} + MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, const MDBX_env *env, const char *pathname, mdbx_filehandle_t *fd, @@ -571,6 +585,12 @@ MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, FlagsAndAttributes |= (env->me_psize < env->me_os_psize) ? 0 : FILE_FLAG_NO_BUFFERING; break; + case MDBX_OPEN_DELETE: + CreationDisposition = OPEN_EXISTING; + ShareMode |= FILE_SHARE_DELETE; + DesiredAccess = + FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | DELETE | SYNCHRONIZE; + break; } *fd = CreateFileW(pathnameW, DesiredAccess, ShareMode, NULL, @@ -619,6 +639,9 @@ MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, flags |= O_FSYNC; #endif break; + case MDBX_OPEN_DELETE: + flags = O_RDWR; + break; } const bool direct_nocache_for_copy = diff --git a/src/osal.h b/src/osal.h index 8fdf9790..b47c2426 100644 --- a/src/osal.h +++ b/src/osal.h @@ -626,7 +626,8 @@ enum mdbx_openfile_purpose { MDBX_OPEN_DXB_LAZY = 1, MDBX_OPEN_DXB_DSYNC = 2, MDBX_OPEN_LCK = 3, - MDBX_OPEN_COPY = 4 + MDBX_OPEN_COPY = 4, + MDBX_OPEN_DELETE = 5 }; MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, @@ -635,7 +636,9 @@ MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, mdbx_mode_t unix_mode_bits); MDBX_INTERNAL_FUNC int mdbx_closefile(mdbx_filehandle_t fd); MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname); +MDBX_INTERNAL_FUNC int mdbx_removedirectory(const char *pathname); MDBX_INTERNAL_FUNC int mdbx_is_pipe(mdbx_filehandle_t fd); +MDBX_INTERNAL_FUNC int mdbx_lockfile(mdbx_filehandle_t fd, bool wait); #define MMAP_OPTION_TRUNCATE 1 #define MMAP_OPTION_SEMAPHORE 2