diff --git a/src/elements/core.c b/src/elements/core.c index b657e2cf..54ff61c1 100644 --- a/src/elements/core.c +++ b/src/elements/core.c @@ -8597,8 +8597,7 @@ static int __cold mdbx_setup_lck(MDBX_env *env, char *lck_pathname, mdbx_assert(env, env->me_fd != INVALID_HANDLE_VALUE); mdbx_assert(env, env->me_lfd == INVALID_HANDLE_VALUE); - int err = mdbx_openfile(lck_pathname, O_RDWR | O_CREAT, mode, &env->me_lfd, - (env->me_flags & MDBX_EXCLUSIVE) ? true : false); + int err = mdbx_openfile(MDBX_OPEN_LCK, env, lck_pathname, &env->me_lfd, mode); if (err != MDBX_SUCCESS) { if (!(err == MDBX_ENOFILE && (env->me_flags & MDBX_EXCLUSIVE)) && !((err == MDBX_EROFS || err == MDBX_EACCESS || err == MDBX_EPERM) && @@ -8908,9 +8907,9 @@ __cold int mdbx_is_readahead_reasonable(size_t volume, intptr_t redundancy) { #error "Persistent DB flags & env flags overlap, but both go in mm_flags" #endif -int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags, +int __cold mdbx_env_open(MDBX_env *env, const char *pathname, unsigned flags, mode_t mode) { - if (unlikely(!env || !path)) + if (unlikely(!env || !pathname)) return MDBX_EINVAL; if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) @@ -8923,7 +8922,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags, (env->me_flags & MDBX_ENV_ACTIVE) != 0) return MDBX_EPERM; - size_t len_full, len = strlen(path); + size_t len_full, len = strlen(pathname); if (flags & MDBX_NOSUBDIR) { len_full = len + sizeof(MDBX_LOCK_SUFFIX) + len + 1; } else { @@ -8936,12 +8935,12 @@ int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags, char *dxb_pathname; if (flags & MDBX_NOSUBDIR) { dxb_pathname = lck_pathname + len + sizeof(MDBX_LOCK_SUFFIX); - sprintf(lck_pathname, "%s" MDBX_LOCK_SUFFIX, path); - strcpy(dxb_pathname, path); + sprintf(lck_pathname, "%s" MDBX_LOCK_SUFFIX, pathname); + strcpy(dxb_pathname, pathname); } else { dxb_pathname = lck_pathname + len + sizeof(MDBX_LOCKNAME); - sprintf(lck_pathname, "%s" MDBX_LOCKNAME, path); - sprintf(dxb_pathname, "%s" MDBX_DATANAME, path); + sprintf(lck_pathname, "%s" MDBX_LOCKNAME, pathname); + sprintf(dxb_pathname, "%s" MDBX_DATANAME, pathname); } int rc = MDBX_SUCCESS; @@ -8978,7 +8977,7 @@ int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags, if (rc) goto bailout; - env->me_path = mdbx_strdup(path); + env->me_path = mdbx_strdup(pathname); env->me_dbxs = mdbx_calloc(env->me_maxdbs, sizeof(MDBX_dbx)); env->me_dbflags = mdbx_calloc(env->me_maxdbs, sizeof(env->me_dbflags[0])); env->me_dbiseqs = mdbx_calloc(env->me_maxdbs, sizeof(env->me_dbiseqs[0])); @@ -8989,37 +8988,39 @@ int __cold mdbx_env_open(MDBX_env *env, const char *path, unsigned flags, env->me_dbxs[FREE_DBI].md_cmp = mdbx_cmp_int_align4; /* aligned MDBX_INTEGERKEY */ - int oflags; - if (F_ISSET(flags, MDBX_RDONLY)) - oflags = O_RDONLY; - else if (mode != 0) { - if ((flags & MDBX_NOSUBDIR) == 0) { + if ((flags & (MDBX_RDONLY | MDBX_NOSUBDIR)) == 0 && mode != 0) { #if defined(_WIN32) || defined(_WIN64) - if (!CreateDirectoryA(path, nullptr)) { - rc = GetLastError(); - if (rc != ERROR_ALREADY_EXISTS) - goto bailout; - } -#else - const mode_t dir_mode = - (/* inherit read/write permissions for group and others */ mode & - (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) | - /* always add read/write/search for owner */ S_IRWXU | - ((mode & S_IRGRP) ? /* +search if readable by group */ S_IXGRP : 0) | - ((mode & S_IROTH) ? /* +search if readable by others */ S_IXOTH : 0); - if (mkdir(path, dir_mode)) { - rc = errno; - if (rc != EEXIST) - goto bailout; - } -#endif + 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)) { + rc = ERROR_INVALID_NAME; + goto bailout; } - oflags = O_RDWR | O_CREAT; - } else - oflags = O_RDWR; + if (!CreateDirectoryW(pathnameW, nullptr)) { + rc = GetLastError(); + if (rc != ERROR_ALREADY_EXISTS) + goto bailout; + } +#else + const mode_t dir_mode = + (/* inherit read/write permissions for group and others */ mode & + (S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) | + /* always add read/write/search for owner */ S_IRWXU | + ((mode & S_IRGRP) ? /* +search if readable by group */ S_IXGRP : 0) | + ((mode & S_IROTH) ? /* +search if readable by others */ S_IXOTH : 0); + if (mkdir(pathname, dir_mode)) { + rc = errno; + if (rc != EEXIST) + goto bailout; + } +#endif + } - rc = mdbx_openfile(dxb_pathname, oflags, mode, &env->me_fd, - (env->me_flags & MDBX_EXCLUSIVE) ? true : false); + rc = mdbx_openfile(F_ISSET(flags, MDBX_RDONLY) ? MDBX_OPEN_DXB_READ + : MDBX_OPEN_DXB_LAZY, + env, dxb_pathname, &env->me_fd, mode); if (rc != MDBX_SUCCESS) goto bailout; @@ -14443,49 +14444,28 @@ int __cold mdbx_env_copy(MDBX_env *env, const char *dest_path, unsigned flags) { if (unlikely(env->me_signature != MDBX_ME_SIGNATURE)) return MDBX_EBADSIGN; - char *dxb_pathname; - mdbx_filehandle_t newfd = INVALID_HANDLE_VALUE; - - if (env->me_flags & MDBX_NOSUBDIR) { - dxb_pathname = (char *)dest_path; - } else { - size_t len = strlen(dest_path); - len += sizeof(MDBX_DATANAME); - dxb_pathname = mdbx_malloc(len); - if (!dxb_pathname) - return MDBX_ENOMEM; - sprintf(dxb_pathname, "%s" MDBX_DATANAME, dest_path); - } - /* The destination path must exist, but the destination file must not. * We don't want the OS to cache the writes, since the source data is * already in the OS cache. */ - int rc = mdbx_openfile(dxb_pathname, O_WRONLY | O_CREAT | O_EXCL, 0640, - &newfd, true); - if (rc == MDBX_SUCCESS) { - if (env->me_psize >= env->me_os_psize) { -#ifdef F_NOCACHE /* __APPLE__ */ - (void)fcntl(newfd, F_NOCACHE, 1); -#elif defined(O_DIRECT) && defined(F_GETFL) - /* Set O_DIRECT if the file system supports it */ - if ((rc = fcntl(newfd, F_GETFL)) != -1) - (void)fcntl(newfd, F_SETFL, rc | O_DIRECT); + mdbx_filehandle_t newfd; + int rc = mdbx_openfile(MDBX_OPEN_COPY, env, dest_path, &newfd, +#if defined(_WIN32) || defined(_WIN64) + (mode_t)-1 +#else + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP #endif - } + ); + if (rc == MDBX_SUCCESS) rc = mdbx_env_copy2fd(env, newfd, flags); - } if (newfd != INVALID_HANDLE_VALUE) { int err = mdbx_closefile(newfd); if (rc == MDBX_SUCCESS && err != rc) rc = err; if (rc != MDBX_SUCCESS) - (void)mdbx_removefile(dxb_pathname); + (void)mdbx_removefile(dest_path); } - if (dxb_pathname != dest_path) - mdbx_free(dxb_pathname); - return rc; } diff --git a/src/elements/osal.c b/src/elements/osal.c index db47c3ee..222a901f 100644 --- a/src/elements/osal.c +++ b/src/elements/osal.c @@ -512,106 +512,156 @@ MDBX_INTERNAL_FUNC int mdbx_fastmutex_release(mdbx_fastmutex_t *fastmutex) { MDBX_INTERNAL_FUNC int mdbx_removefile(const char *pathname) { #if defined(_WIN32) || defined(_WIN64) - return DeleteFileA(pathname) ? MDBX_SUCCESS : GetLastError(); + 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 DeleteFileW(pathnameW) ? MDBX_SUCCESS : GetLastError(); #else return unlink(pathname) ? errno : MDBX_SUCCESS; #endif } -MDBX_INTERNAL_FUNC int mdbx_openfile(const char *pathname, int flags, - mode_t mode, mdbx_filehandle_t *fd, - bool exclusive) { +MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, + const MDBX_env *env, const char *pathname, + mdbx_filehandle_t *fd, + mode_t unix_mode_bits) { *fd = INVALID_HANDLE_VALUE; + #if defined(_WIN32) || defined(_WIN64) - (void)mode; - size_t wlen = mbstowcs(nullptr, pathname, INT_MAX); + 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; - DWORD DesiredAccess, ShareMode; - DWORD FlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; - switch (flags & (O_RDONLY | O_WRONLY | O_RDWR)) { + DWORD CreationDisposition = unix_mode_bits ? OPEN_ALWAYS : OPEN_EXISTING; + DWORD FlagsAndAttributes = + FILE_FLAG_POSIX_SEMANTICS | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + DWORD DesiredAccess = FILE_READ_ATTRIBUTES; + DWORD ShareMode = (env->me_flags & MDBX_EXCLUSIVE) + ? 0 + : (FILE_SHARE_READ | FILE_SHARE_WRITE); + + switch (purpose) { default: return ERROR_INVALID_PARAMETER; - case O_RDONLY: - DesiredAccess = GENERIC_READ; - ShareMode = - exclusive ? FILE_SHARE_READ : (FILE_SHARE_READ | FILE_SHARE_WRITE); + case MDBX_OPEN_LCK: + CreationDisposition = OPEN_ALWAYS; + DesiredAccess |= GENERIC_READ | GENERIC_WRITE; + FlagsAndAttributes |= FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_TEMPORARY; break; - case O_WRONLY: /* assume for MDBX_env_copy() and friends output */ - DesiredAccess = GENERIC_WRITE; - ShareMode = 0; + case MDBX_OPEN_DXB_READ: + CreationDisposition = OPEN_EXISTING; + DesiredAccess |= GENERIC_READ; + ShareMode |= FILE_SHARE_READ; + break; + case MDBX_OPEN_DXB_LAZY: + DesiredAccess |= GENERIC_READ | GENERIC_WRITE; + break; + case MDBX_OPEN_DXB_DSYNC: + CreationDisposition = OPEN_EXISTING; + DesiredAccess |= GENERIC_WRITE; FlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH; break; - case O_RDWR: - DesiredAccess = GENERIC_READ | GENERIC_WRITE; - ShareMode = exclusive ? 0 : (FILE_SHARE_READ | FILE_SHARE_WRITE); - break; - } - - DWORD CreationDisposition; - switch (flags & (O_EXCL | O_CREAT)) { - default: - return ERROR_INVALID_PARAMETER; - case 0: - CreationDisposition = OPEN_EXISTING; - break; - case O_EXCL | O_CREAT: + case MDBX_OPEN_COPY: CreationDisposition = CREATE_NEW; - FlagsAndAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; - break; - case O_CREAT: - CreationDisposition = OPEN_ALWAYS; - FlagsAndAttributes |= FILE_ATTRIBUTE_NOT_CONTENT_INDEXED; + ShareMode = 0; + DesiredAccess |= GENERIC_WRITE; + FlagsAndAttributes |= + (env->me_psize < env->me_os_psize) ? 0 : FILE_FLAG_NO_BUFFERING; break; } *fd = CreateFileW(pathnameW, DesiredAccess, ShareMode, NULL, CreationDisposition, FlagsAndAttributes, NULL); - if (*fd == INVALID_HANDLE_VALUE) return GetLastError(); - if ((flags & O_CREAT) && GetLastError() != ERROR_ALREADY_EXISTS) { - /* set FILE_ATTRIBUTE_NOT_CONTENT_INDEXED for new file */ - DWORD FileAttributes = GetFileAttributesA(pathname); - if (FileAttributes == INVALID_FILE_ATTRIBUTES || - !SetFileAttributesA(pathname, FileAttributes | - FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)) { - int rc = GetLastError(); - CloseHandle(*fd); - *fd = INVALID_HANDLE_VALUE; - return rc; - } + + BY_HANDLE_FILE_INFORMATION info; + if (!GetFileInformationByHandle(*fd, &info)) { + int err = GetLastError(); + CloseHandle(*fd); + *fd = INVALID_HANDLE_VALUE; + return err; } + const DWORD AttributesDiff = + (info.dwFileAttributes ^ FlagsAndAttributes) & + (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | + FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_COMPRESSED); + if (AttributesDiff) + (void)SetFileAttributesW(pathnameW, info.dwFileAttributes ^ AttributesDiff); + #else - (void)exclusive; + int flags = unix_mode_bits ? O_CREAT : 0; + switch (purpose) { + default: + return EINVAL; + case MDBX_OPEN_LCK: + flags |= O_RDWR; + break; + case MDBX_OPEN_DXB_READ: + flags = O_RDONLY; + break; + case MDBX_OPEN_DXB_LAZY: + flags |= O_RDWR; + break; + case MDBX_OPEN_COPY: + flags = O_CREAT | O_WRONLY | O_EXCL; + break; + case MDBX_OPEN_DXB_DSYNC: + flags |= O_WRONLY; +#if defined(O_DSYNC) + flags |= O_DSYNC; +#elif defined(O_SYNC) + flags |= O_SYNC; +#elif defined(O_FSYNC) + flags |= O_FSYNC; +#endif + break; + } + + const bool direct_nocache_for_copy = + env->me_psize >= env->me_os_psize && purpose == MDBX_OPEN_COPY; + if (direct_nocache_for_copy) { +#if defined(O_DIRECT) + flags |= O_DIRECT; +#endif /* O_DIRECT */ +#if defined(O_NOCACHE) + flags |= O_NOCACHE; +#endif /* O_NOCACHE */ + } + #ifdef O_CLOEXEC flags |= O_CLOEXEC; #endif /* O_CLOEXEC */ - *fd = open(pathname, flags, mode); + + *fd = open(pathname, flags, unix_mode_bits); +#if defined(O_DIRECT) + if (*fd < 0 && (flags & O_DIRECT) && + (errno == EINVAL || errno == EAFNOSUPPORT)) { + flags &= ~(O_DIRECT | O_EXCL); + *fd = open(pathname, flags, unix_mode_bits); + } +#endif /* O_DIRECT */ if (*fd < 0) return errno; #if defined(FD_CLOEXEC) && !defined(O_CLOEXEC) - int fd_flags = fcntl(*fd, F_GETFD); + const int fd_flags = fcntl(*fd, F_GETFD); if (fd_flags != -1) (void)fcntl(*fd, F_SETFD, fd_flags | FD_CLOEXEC); #endif /* FD_CLOEXEC && !O_CLOEXEC */ - if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY) { - /* assume for MDBX_env_copy() and friends output */ -#if defined(O_DIRECT) - int fd_flags = fcntl(*fd, F_GETFD); - if (fd_flags != -1) - (void)fcntl(*fd, F_SETFL, fd_flags | O_DIRECT); -#endif /* O_DIRECT */ -#if defined(F_NOCACHE) + if (direct_nocache_for_copy) { +#if defined(F_NOCACHE) && !defined(O_NOCACHE) (void)fcntl(*fd, F_NOCACHE, 1); #endif /* F_NOCACHE */ } + #endif return MDBX_SUCCESS; diff --git a/src/elements/osal.h b/src/elements/osal.h index 0f57cdaa..51ed5b62 100644 --- a/src/elements/osal.h +++ b/src/elements/osal.h @@ -566,9 +566,19 @@ MDBX_INTERNAL_FUNC int mdbx_filesync(mdbx_filehandle_t fd, MDBX_INTERNAL_FUNC int mdbx_ftruncate(mdbx_filehandle_t fd, uint64_t length); MDBX_INTERNAL_FUNC int mdbx_fseek(mdbx_filehandle_t fd, uint64_t pos); MDBX_INTERNAL_FUNC int mdbx_filesize(mdbx_filehandle_t fd, uint64_t *length); -MDBX_INTERNAL_FUNC int mdbx_openfile(const char *pathname, int flags, - mode_t mode, mdbx_filehandle_t *fd, - bool exclusive); + +enum mdbx_openfile_purpose { + MDBX_OPEN_DXB_READ = 0, + MDBX_OPEN_DXB_LAZY = 1, + MDBX_OPEN_DXB_DSYNC = 2, + MDBX_OPEN_LCK = 3, + MDBX_OPEN_COPY = 4 +}; + +MDBX_INTERNAL_FUNC int mdbx_openfile(const enum mdbx_openfile_purpose purpose, + const MDBX_env *env, const char *pathname, + mdbx_filehandle_t *fd, + 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_is_pipe(mdbx_filehandle_t fd);