diff --git a/src/elements/lck-posix.c b/src/elements/lck-posix.c index 264d81c7..931f4cc5 100644 --- a/src/elements/lck-posix.c +++ b/src/elements/lck-posix.c @@ -220,6 +220,65 @@ MDBX_INTERNAL_FUNC int mdbx_ipclock_destroy(mdbx_ipclock_t *ipc) { } #endif /* MDBX_LOCKING > MDBX_LOCKING_SYSV */ +static int check_fstat(MDBX_env *env) { + struct stat st; + + int rc = MDBX_SUCCESS; + if (fstat(env->me_lazy_fd, &st)) { + rc = errno; + mdbx_error("fstat(%s), err %d", "DXB", rc); + return rc; + } + + if (!S_ISREG(st.st_mode) || st.st_nlink < 1) { +#ifdef EBADFD + rc = EBADFD; +#else + rc = EPERM; +#endif + mdbx_error("%s %s, err %d", "DXB", + (st.st_nlink < 1) ? "file was removed" : "not a regular file", + rc); + return rc; + } + + if (st.st_size < (off_t)(MDBX_MIN_PAGESIZE * NUM_METAS)) { + mdbx_verbose("dxb-file is too short (%u), exclusive-lock needed", + (unsigned)st.st_size); + rc = MDBX_RESULT_TRUE; + } + + //---------------------------------------------------------------------------- + + if (fstat(env->me_lfd, &st)) { + rc = errno; + mdbx_error("fstat(%s), err %d", "LCK", rc); + return rc; + } + + if (!S_ISREG(st.st_mode) || st.st_nlink < 1) { +#ifdef EBADFD + rc = EBADFD; +#else + rc = EPERM; +#endif + mdbx_error("%s %s, err %d", "LCK", + (st.st_nlink < 1) ? "file was removed" : "not a regular file", + rc); + return rc; + } + + /* Checking file size for detect the situation when we got the shared lock + * immediately after mdbx_lck_destroy(). */ + if (st.st_size < (off_t)(sizeof(MDBX_lockinfo) + sizeof(MDBX_reader))) { + mdbx_verbose("lck-file is too short (%u), exclusive-lock needed", + (unsigned)st.st_size); + rc = MDBX_RESULT_TRUE; + } + + return rc; +} + MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) { assert(env->me_lazy_fd != INVALID_HANDLE_VALUE); if (unlikely(mdbx_getpid() != env->me_pid)) @@ -229,7 +288,7 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) { choice_fcntl(); #endif /* MDBX_USE_OFDLOCKS */ - int rc; + int rc = MDBX_SUCCESS; if (env->me_lfd == INVALID_HANDLE_VALUE) { /* LY: without-lck mode (e.g. exclusive or on read-only filesystem) */ rc = @@ -242,11 +301,27 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_seize(MDBX_env *env) { } return MDBX_RESULT_TRUE /* Done: return with exclusive locking. */; } +#if defined(_POSIX_PRIORITY_SCHEDULING) && _POSIX_PRIORITY_SCHEDULING > 0 + sched_yield(); +#endif + +retry: + if (rc == MDBX_RESULT_TRUE) { + rc = lck_op(env->me_lfd, op_setlk, F_UNLCK, 0, 1); + if (rc != MDBX_SUCCESS) { + mdbx_error("%s, err %u", "unlock-before-retry", rc); + mdbx_assert(env, MDBX_IS_ERROR(rc)); + return rc; + } + } -retry_exclusive: /* Firstly try to get exclusive locking. */ rc = lck_op(env->me_lfd, op_setlk, F_WRLCK, 0, 1); if (rc == MDBX_SUCCESS) { + rc = check_fstat(env); + if (MDBX_IS_ERROR(rc)) + return rc; + continue_dxb_exclusive: rc = lck_op(env->me_lazy_fd, op_setlk, @@ -254,6 +329,10 @@ retry_exclusive: if (rc == MDBX_SUCCESS) return MDBX_RESULT_TRUE /* Done: return with exclusive locking. */; + int err = check_fstat(env); + if (MDBX_IS_ERROR(err)) + return err; + /* the cause may be a collision with POSIX's file-lock recovery. */ if (!(rc == EAGAIN || rc == EACCES || rc == EBUSY || rc == EWOULDBLOCK || rc == EDEADLK)) { @@ -263,6 +342,11 @@ retry_exclusive: } /* Fallback to lck-shared */ + } else if (!(rc == EAGAIN || rc == EACCES || rc == EBUSY || + rc == EWOULDBLOCK || rc == EDEADLK)) { + mdbx_error("%s, err %u", "try-exclusive", rc); + mdbx_assert(env, MDBX_IS_ERROR(rc)); + return rc; } /* Here could be one of two: @@ -282,6 +366,14 @@ retry_exclusive: return rc; } + rc = check_fstat(env); + if (rc == MDBX_RESULT_TRUE) + goto retry; + if (rc != MDBX_SUCCESS) { + mdbx_error("%s, err %u", "lck_fstat", rc); + return rc; + } + /* got shared, retry exclusive */ rc = lck_op(env->me_lfd, op_setlk, F_WRLCK, 0, 1); if (rc == MDBX_SUCCESS) @@ -294,27 +386,6 @@ retry_exclusive: return rc; } - /* Checking file size for detect the situation when we got the shared lock - * immediately after mdbx_lck_destroy(). */ - struct stat st; - if (fstat(env->me_lfd, &st)) { - rc = errno; - mdbx_error("%s(%s) failed: errcode %u", __func__, "check-filesize", rc); - mdbx_assert(env, MDBX_IS_ERROR(rc)); - return rc; - } - if (st.st_size < (unsigned)(sizeof(MDBX_lockinfo) + sizeof(MDBX_reader))) { - mdbx_verbose("lck-file is too short (%u), retry exclusive-lock", - (unsigned)st.st_size); - rc = lck_op(env->me_lfd, op_setlk, F_UNLCK, 0, 1); - if (rc != MDBX_SUCCESS) { - mdbx_error("%s(%s) failed: errcode %u", __func__, "retry-exclusive", rc); - mdbx_assert(env, MDBX_IS_ERROR(rc)); - return rc; - } - goto retry_exclusive; - } - /* Lock against another process operating in without-lck or exclusive mode. */ rc = lck_op(env->me_lazy_fd, op_setlk, @@ -357,10 +428,13 @@ MDBX_INTERNAL_FUNC int __cold mdbx_lck_destroy(MDBX_env *env, return MDBX_PANIC; int rc = MDBX_SUCCESS; + struct stat lck_info; if (env->me_lfd != INVALID_HANDLE_VALUE && !inprocess_neighbor && env->me_lck && /* try get exclusive access */ lck_op(env->me_lfd, op_setlk, F_WRLCK, 0, OFF_T_MAX) == 0 && + /* if LCK was not removed */ + fstat(env->me_lfd, &lck_info) == 0 && lck_info.st_nlink > 0 && lck_op(env->me_lazy_fd, op_setlk, (env->me_flags & MDBX_RDONLY) ? F_RDLCK : F_WRLCK, 0, OFF_T_MAX) == 0) {