8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-02-01 00:03:03 +01:00
firebird-mirror/src/jrd/os/win32/winnt.cpp

1275 lines
32 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: winnt.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Windows NT specific physical IO
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
* conditionals, as the engine now fully supports
* readonly databases.
2001-12-24 03:51:06 +01:00
*
2002-07-01 17:07:18 +02:00
*
* 17-Oct-2001 Mike Nordell: Non-shared file access
*
2001-12-24 03:51:06 +01:00
* 02-Nov-2001 Mike Nordell: Synch with FB1 changes.
*
* 20-Nov-2001 Ann Harrison: Make page count work on db with forced write
*
* 21-Nov-2001 Ann Harrison: Allow read sharing so gstat works
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
2004-03-22 12:38:23 +01:00
#include "../jrd/common.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/ods.h"
#include "../jrd/lck.h"
#include "../jrd/cch.h"
#include "gen/iberror.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/cch_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/isc_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/mov_proto.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio_proto.h"
#include "../jrd/GlobalRWLock.h"
#include "../jrd/thread_proto.h"
2001-05-23 15:26:42 +02:00
#include <windows.h>
namespace Jrd {
FileExtendLock::FileExtendLock(Firebird::MemoryPool& p, size_t lock_len, UCHAR* lock_string)
{
thread_db* tdbb = NULL;
SET_TDBB(tdbb);
// hvlad: logical lock owner better would be a thread, but this is not
// implemented currently. Fortunately only place when we need an exclusive
// lock is PIO_extend and it always called with existing attachment
m_lock = FB_NEW(p) GlobalRWLock(tdbb, p, LCK_file_extend,
lock_len, lock_string,
LCK_OWNER_database, LCK_OWNER_attachment, true);
}
FileExtendLock::~FileExtendLock()
{
delete m_lock;
}
void FileExtendLock::lock(thread_db* tdbb, bool exclusive)
{
const bool in_engine = SCH_thread_enter_check();
if (!in_engine)
THREAD_ENTER();
SET_TDBB(tdbb);
fb_assert(tdbb->tdbb_attachment);
m_lock->lock(tdbb, exclusive ? LCK_write : LCK_read, LCK_WAIT);
if (!in_engine)
THREAD_EXIT();
}
void FileExtendLock::release(thread_db* tdbb, bool exclusive)
{
const bool in_engine = SCH_thread_enter_check();
if (!in_engine)
THREAD_ENTER();
SET_TDBB(tdbb);
fb_assert(tdbb->tdbb_attachment);
m_lock->unlock(tdbb, exclusive ? LCK_write : LCK_read);
if (!in_engine)
THREAD_EXIT();
}
class FileExtendLockGuard
{
public:
FileExtendLockGuard(thread_db* tdbb, FileExtendLock *lock, bool exclusive) :
m_tdbb(tdbb), m_lock(lock), m_exclusive(exclusive)
{
if (exclusive) {
fb_assert(m_lock);
}
if (m_lock) {
m_lock->lock(m_tdbb, m_exclusive);
}
}
~FileExtendLockGuard()
{
if (m_lock) {
m_lock->release(m_tdbb, m_exclusive);
}
}
private:
thread_db* m_tdbb;
FileExtendLock* m_lock;
bool m_exclusive;
};
} // namespace Jrd
2001-05-23 15:26:42 +02:00
using namespace Jrd;
2001-05-23 15:26:42 +02:00
#ifdef TEXT
#undef TEXT
#endif
#define TEXT SCHAR
2004-05-14 01:20:50 +02:00
const USHORT OS_WINDOWS_NT = 1;
const USHORT OS_CHICAGO = 2;
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
static void release_io_event(jrd_file*, OVERLAPPED*);
2001-05-23 15:26:42 +02:00
#endif
static bool maybe_close_file(HANDLE&);
static jrd_file* seek_file(jrd_file*, BufferDesc*, ISC_STATUS*, OVERLAPPED*, OVERLAPPED**);
2005-09-08 18:41:56 +02:00
static jrd_file* setup_file(Database*, const Firebird::PathName&, HANDLE);
static bool nt_error(TEXT*, const jrd_file*, ISC_STATUS, ISC_STATUS*);
2001-05-23 15:26:42 +02:00
static USHORT ostype;
2001-11-02 21:59:38 +01:00
#ifdef SUPERSERVER_V2
2001-12-24 03:51:06 +01:00
static const DWORD g_dwShareFlags = FILE_SHARE_READ; // no write sharing
2001-11-02 21:59:38 +01:00
static const DWORD g_dwExtraFlags = FILE_FLAG_OVERLAPPED |
FILE_FLAG_NO_BUFFERING |
FILE_FLAG_RANDOM_ACCESS;
2002-04-03 10:41:23 +02:00
#else
#ifdef SUPERSERVER
2001-12-24 03:51:06 +01:00
static const DWORD g_dwShareFlags = FILE_SHARE_READ; // no write sharing
2001-11-02 21:59:38 +01:00
static const DWORD g_dwExtraFlags = FILE_FLAG_RANDOM_ACCESS;
#else
static const DWORD g_dwShareFlags = FILE_SHARE_READ | FILE_SHARE_WRITE;
static const DWORD g_dwExtraFlags = FILE_FLAG_RANDOM_ACCESS;
#endif
2002-04-03 10:41:23 +02:00
#endif
2001-11-02 21:59:38 +01:00
2006-05-22 00:07:35 +02:00
static const DWORD g_dwShareTempFlags = FILE_SHARE_READ;
static const DWORD g_dwExtraTempFlags = FILE_ATTRIBUTE_TEMPORARY |
FILE_FLAG_DELETE_ON_CLOSE;
2001-11-02 21:59:38 +01:00
2001-05-23 15:26:42 +02:00
2005-09-08 18:41:56 +02:00
int PIO_add_file(Database* dbb, jrd_file* main_file, const Firebird::PathName& file_name, SLONG start)
2002-11-10 16:38:38 +01:00
{
2001-05-23 15:26:42 +02:00
/**************************************
*
2002-11-10 16:38:38 +01:00
* P I O _ a d d _ f i l e
*
**************************************
*
* Functional description
2001-05-23 15:26:42 +02:00
* Add a file to an existing database. Return the sequence
* number of the new file. If anything goes wrong, return a
* sequence of 0.
*
**************************************/
2007-03-09 08:59:05 +01:00
jrd_file* new_file = PIO_create(dbb, file_name, false, false, false);
2001-05-23 15:26:42 +02:00
if (!new_file) {
return 0;
}
new_file->fil_min_page = start;
USHORT sequence = 1;
2001-05-23 15:26:42 +02:00
jrd_file* file;
2001-05-23 15:26:42 +02:00
for (file = main_file; file->fil_next; file = file->fil_next) {
++sequence;
}
file->fil_max_page = start - 1;
file->fil_next = new_file;
return sequence;
}
void PIO_close(jrd_file* main_file)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ c l o s e
*
**************************************
*
* Functional description
*
**************************************/
for (jrd_file* file = main_file; file; file = file->fil_next)
2001-05-23 15:26:42 +02:00
{
if (maybe_close_file(file->fil_desc))
2001-05-23 15:26:42 +02:00
{
#ifdef SUPERSERVER_V2
2002-04-29 17:05:11 +02:00
for (int i = 0; i < MAX_FILE_IO; i++)
2001-05-23 15:26:42 +02:00
{
if (file->fil_io_events[i])
{
CloseHandle((HANDLE) file->fil_io_events[i]);
2001-11-02 21:59:38 +01:00
file->fil_io_events[i] = 0;
2001-05-23 15:26:42 +02:00
}
}
#endif
}
}
}
2007-03-09 08:59:05 +01:00
jrd_file* PIO_create(Database* dbb, const Firebird::PathName& string,
bool overwrite, bool temporary, bool share_delete)
2002-11-10 16:38:38 +01:00
{
2001-05-23 15:26:42 +02:00
/**************************************
*
2002-11-10 16:38:38 +01:00
* P I O _ c r e a t e
*
**************************************
*
* Functional description
2001-05-23 15:26:42 +02:00
* Create a new database file.
*
**************************************/
2005-09-08 18:41:56 +02:00
const TEXT* file_name = string.c_str();
2001-05-23 15:26:42 +02:00
2007-03-09 08:59:05 +01:00
if (!ISC_is_WinNT())
share_delete = false;
DWORD dwShareMode = (temporary ? g_dwShareTempFlags : g_dwShareFlags);
if (share_delete)
dwShareMode |= FILE_SHARE_DELETE;
DWORD dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL | g_dwExtraFlags;
if (temporary)
dwFlagsAndAttributes |= g_dwExtraTempFlags;
2003-12-31 06:36:12 +01:00
const HANDLE desc = CreateFile(file_name,
2001-05-23 15:26:42 +02:00
GENERIC_READ | GENERIC_WRITE,
2007-03-09 08:59:05 +01:00
dwShareMode,
2001-05-23 15:26:42 +02:00
NULL,
(overwrite ? CREATE_ALWAYS : CREATE_NEW),
2007-03-09 08:59:05 +01:00
dwFlagsAndAttributes,
2001-11-02 21:59:38 +01:00
0);
2001-05-23 15:26:42 +02:00
2001-11-02 21:59:38 +01:00
if (desc == INVALID_HANDLE_VALUE)
{
2001-05-23 15:26:42 +02:00
ERR_post(isc_io_error,
isc_arg_string, "CreateFile (create)",
2006-05-30 15:34:23 +02:00
isc_arg_cstring, string.length(), ERR_cstring(string),
isc_arg_gds, isc_io_create_err, isc_arg_win32, GetLastError(),
0);
2001-05-23 15:26:42 +02:00
}
/* File open succeeded. Now expand the file name. */
2006-05-30 15:34:23 +02:00
/* workspace is the expanded name here */
2001-05-23 15:26:42 +02:00
2005-09-08 18:41:56 +02:00
Firebird::PathName workspace(string);
ISC_expand_filename(workspace, false);
jrd_file *file;
try {
2005-09-08 18:41:56 +02:00
file = setup_file(dbb, workspace, desc);
2006-05-20 05:55:54 +02:00
}
catch (const Firebird::Exception&) {
CloseHandle(desc);
throw;
}
2001-05-23 15:26:42 +02:00
return file;
}
2006-10-29 15:16:29 +01:00
bool PIO_expand(const TEXT* file_name, USHORT file_length, TEXT* expanded_name,
2004-11-08 07:16:19 +01:00
size_t len_expanded)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ e x p a n d
*
**************************************
*
* Functional description
* Fully expand a file name. If the file doesn't exist, do something
* intelligent.
*
**************************************/
return ISC_expand_filename(file_name, file_length,
expanded_name, len_expanded, false);
2001-05-23 15:26:42 +02:00
}
void PIO_extend(jrd_file* main_file, const ULONG extPages, const USHORT pageSize)
{
/**************************************
*
* P I O _ e x t e n d
*
**************************************
*
* Functional description
* Extend file by extPages pages of pageSize size.
*
**************************************/
2007-05-02 08:57:18 +02:00
2007-05-04 03:18:25 +02:00
#if (defined(_MSC_VER) && (_MSC_VER <= 1200)) // || defined(MINGW)
2007-05-02 08:57:18 +02:00
const DWORD INVALID_SET_FILE_POINTER = 0xFFFFFFFF;
#endif
// hvlad: prevent other threads from read\write changing file pointer
// It solved issue CORE-1468 (database file corruption when file extension
// and read\write activity performed simultaneously)
// It looks like a Windows bug as all our ReadFile\WriteFile calls used
// overlapped to set read\write operation offset not touching file pointer
if (!main_file->fil_ext_lock)
return;
FileExtendLockGuard extLock(NULL, main_file->fil_ext_lock, true);
ULONG leftPages = extPages;
for (jrd_file* file = main_file; file && leftPages; file = file->fil_next)
{
const ULONG filePages = PIO_get_number_of_pages(file, pageSize);
const ULONG fileMaxPages = (file->fil_max_page == MAX_ULONG) ? MAX_ULONG :
file->fil_max_page - file->fil_min_page + 1;
if (filePages < fileMaxPages)
{
const ULONG extendBy = MIN(fileMaxPages - filePages + file->fil_fudge, leftPages);
HANDLE hFile = file->fil_desc;
LARGE_INTEGER newSize;
newSize.QuadPart = (ULONGLONG) (filePages + extendBy) * pageSize;
if (ostype == OS_CHICAGO) {
file->fil_mutex.enter();
}
const DWORD ret = SetFilePointer(hFile, newSize.LowPart, &newSize.HighPart, FILE_BEGIN);
if (ret == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) {
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
}
nt_error("SetFilePointer", file, isc_io_write_err, 0);
}
if (!SetEndOfFile(hFile)) {
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
}
nt_error("SetEndOfFile", file, isc_io_write_err, 0);
}
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
}
leftPages -= extendBy;
}
}
}
void PIO_flush(jrd_file* main_file)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ f l u s h
*
**************************************
*
* Functional description
* Flush the operating system cache back to good, solid oxide.
*
**************************************/
for (jrd_file* file = main_file; file; file = file->fil_next)
2001-11-02 21:59:38 +01:00
{
if (ostype == OS_CHICAGO)
2001-11-02 21:59:38 +01:00
{
file->fil_mutex.enter();
}
FlushFileBuffers(file->fil_desc);
if (ostype == OS_CHICAGO)
{
file->fil_mutex.leave();
2001-11-02 21:59:38 +01:00
}
2001-05-23 15:26:42 +02:00
}
}
2007-07-26 03:23:18 +02:00
void PIO_force_write(jrd_file* file, bool forceWrite, bool notUseFSCache)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ f o r c e _ w r i t e
*
**************************************
*
* Functional description
* Set (or clear) force write, if possible, for the database.
*
**************************************/
2007-07-26 03:23:18 +02:00
const bool oldForce = (file->fil_flags & FIL_force_write) != 0;
const bool oldNotUseCache = (file->fil_flags & FIL_no_fs_cache) != 0;
2001-12-28 06:16:58 +01:00
2007-07-26 03:23:18 +02:00
if (forceWrite != oldForce || notUseFSCache != oldNotUseCache)
{
2007-07-26 03:23:18 +02:00
const int force = forceWrite ? FILE_FLAG_WRITE_THROUGH : 0;
const int fsCache = notUseFSCache ? FILE_FLAG_NO_BUFFERING : 0;
const int writeMode = (file->fil_flags & FIL_readonly) ? 0 : GENERIC_WRITE;
2007-07-26 03:23:18 +02:00
HANDLE& hFile = file->fil_desc;
maybe_close_file(hFile);
hFile = CreateFile(file->fil_string,
GENERIC_READ | writeMode,
2001-12-28 06:16:58 +01:00
g_dwShareFlags,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | force | fsCache | g_dwExtraFlags,
2001-12-28 06:16:58 +01:00
0);
if (hFile == INVALID_HANDLE_VALUE)
2001-11-02 21:59:38 +01:00
{
2001-12-28 06:16:58 +01:00
ERR_post(isc_io_error,
isc_arg_string,
2001-12-28 06:16:58 +01:00
"CreateFile (force write)",
isc_arg_cstring,
2001-12-28 06:16:58 +01:00
file->fil_length,
ERR_string(file->fil_string, file->fil_length),
isc_arg_gds, isc_io_access_err,
isc_arg_win32,
2001-12-28 06:16:58 +01:00
GetLastError(),
0);
}
2007-07-26 03:23:18 +02:00
if (forceWrite) {
file->fil_flags |= FIL_force_write;
}
else {
file->fil_flags &= ~FIL_force_write;
}
2007-07-26 03:23:18 +02:00
if (notUseFSCache) {
file->fil_flags |= FIL_no_fs_cache;
}
else {
file->fil_flags &= ~FIL_no_fs_cache;
}
2001-05-23 15:26:42 +02:00
}
}
2004-03-07 08:58:55 +01:00
void PIO_header(Database* dbb, SCHAR * address, int length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ h e a d e r
*
**************************************
*
* Functional description
* Read the page header. This assumes that the file has not been
* repositioned since the file was originally mapped.
* The detail of Win32 implementation is that it doesn't assume
* this fact as seeks to first byte of file initially, but external
* callers should not rely on this behavior
2001-05-23 15:26:42 +02:00
*
**************************************/
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
jrd_file* file = pageSpace->file;
HANDLE desc = file->fil_desc;
2001-05-23 15:26:42 +02:00
2007-07-26 03:23:18 +02:00
OVERLAPPED overlapped;
OVERLAPPED* overlapped_ptr;
2001-11-02 21:59:38 +01:00
if (ostype == OS_CHICAGO)
{
file->fil_mutex.enter();
2003-08-09 21:01:02 +02:00
if (SetFilePointer(desc, 0, NULL, FILE_BEGIN) == (DWORD) -1)
2001-11-02 21:59:38 +01:00
{
file->fil_mutex.leave();
2001-05-23 15:26:42 +02:00
nt_error("SetFilePointer", file, isc_io_read_err, 0);
}
overlapped_ptr = NULL;
}
2001-11-02 21:59:38 +01:00
else
{
2001-05-23 15:26:42 +02:00
memset(&overlapped, 0, sizeof(OVERLAPPED));
overlapped_ptr = &overlapped;
#ifdef SUPERSERVER_V2
if (!(overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
nt_error("Overlapped event", file, isc_io_read_err, 0);
ResetEvent(overlapped.hEvent);
#endif
}
DWORD actual_length;
if (dbb->dbb_encrypt_key.hasData())
2001-11-02 21:59:38 +01:00
{
2001-05-23 15:26:42 +02:00
SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)];
if (!ReadFile(desc, spare_buffer, length, &actual_length, overlapped_ptr)
2001-11-02 21:59:38 +01:00
|| actual_length != (DWORD)length)
{
2001-05-23 15:26:42 +02:00
if (ostype == OS_CHICAGO)
2001-11-02 21:59:38 +01:00
{
file->fil_mutex.leave();
2001-11-02 21:59:38 +01:00
}
2001-05-23 15:26:42 +02:00
nt_error("ReadFile", file, isc_io_read_err, 0);
}
(*dbb->dbb_decrypt) (dbb->dbb_encrypt_key.c_str(),
2001-05-23 15:26:42 +02:00
spare_buffer, length, address);
}
else
{
if (!ReadFile(desc, address, length, &actual_length, overlapped_ptr)
2001-11-02 21:59:38 +01:00
|| actual_length != (DWORD)length)
{
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE)
2001-11-02 21:59:38 +01:00
|| actual_length != length)
{
CloseHandle(overlapped.hEvent);
nt_error("GetOverlappedResult", file, isc_io_read_err, 0);
}
2001-05-23 15:26:42 +02:00
#else
if (ostype == OS_CHICAGO)
2001-11-02 21:59:38 +01:00
{
file->fil_mutex.leave();
2001-11-02 21:59:38 +01:00
}
2001-05-23 15:26:42 +02:00
nt_error("ReadFile", file, isc_io_read_err, 0);
#endif
2001-11-02 21:59:38 +01:00
}
2001-05-23 15:26:42 +02:00
}
#ifdef SUPERSERVER_V2
CloseHandle(overlapped.hEvent);
#endif
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2001-05-23 15:26:42 +02:00
}
}
2004-03-07 08:58:55 +01:00
jrd_file* PIO_open(Database* dbb,
2007-03-09 08:59:05 +01:00
const Firebird::PathName& string,
bool trace_flag,
const Firebird::PathName& file_name,
bool share_delete)
2001-05-23 15:26:42 +02:00
{
2002-11-10 16:38:38 +01:00
/**************************************
*
* P I O _ o p e n
*
**************************************
*
* Functional description
* Open a database file.
2002-11-10 16:38:38 +01:00
*
**************************************/
2005-09-08 18:41:56 +02:00
const TEXT* ptr = (string.hasData() ? string : file_name).c_str();
2007-07-26 03:23:18 +02:00
bool readOnly = false;
2001-05-23 15:26:42 +02:00
2007-03-09 08:59:05 +01:00
if (!ISC_is_WinNT())
share_delete = false;
HANDLE desc = CreateFile(ptr,
2001-05-23 15:26:42 +02:00
GENERIC_READ | GENERIC_WRITE,
2007-03-09 08:59:05 +01:00
g_dwShareFlags | (share_delete ? FILE_SHARE_DELETE : 0),
2001-05-23 15:26:42 +02:00
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL |
2001-11-02 21:59:38 +01:00
g_dwExtraFlags,
0);
2001-05-23 15:26:42 +02:00
if (desc == INVALID_HANDLE_VALUE) {
/* Try opening the database file in ReadOnly mode.
* The database file could be on a RO medium (CD-ROM etc.).
* If this fileopen fails, return error.
*/
desc = CreateFile(ptr,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL |
2001-11-02 21:59:38 +01:00
g_dwExtraFlags, 0);
2001-05-23 15:26:42 +02:00
if (desc == INVALID_HANDLE_VALUE) {
ERR_post(isc_io_error,
isc_arg_string,
2001-05-23 15:26:42 +02:00
"CreateFile (open)",
isc_arg_cstring,
2005-09-08 18:41:56 +02:00
file_name.length(),
ERR_cstring(file_name),
2001-05-23 15:26:42 +02:00
isc_arg_gds,
isc_io_open_err, isc_arg_win32, GetLastError(), 0);
2001-05-23 15:26:42 +02:00
}
else {
2004-03-07 08:58:55 +01:00
/* If this is the primary file, set Database flag to indicate that it is
2001-05-23 15:26:42 +02:00
* being opened ReadOnly. This flag will be used later to compare with
* the Header Page flag setting to make sure that the database is set
* ReadOnly.
*/
2007-07-26 03:23:18 +02:00
readOnly = true;
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
if (!pageSpace->file)
2001-05-23 15:26:42 +02:00
dbb->dbb_flags |= DBB_being_opened_read_only;
}
}
jrd_file *file;
try {
2005-09-08 18:41:56 +02:00
file = setup_file(dbb, string, desc);
2007-07-26 03:23:18 +02:00
if (readOnly)
file->fil_flags |= FIL_readonly;
2006-05-20 05:55:54 +02:00
}
catch (const Firebird::Exception&) {
CloseHandle(desc);
throw;
}
return file;
2001-05-23 15:26:42 +02:00
}
bool PIO_read(jrd_file* file, BufferDesc* bdb, Ods::pag* page, ISC_STATUS* status_vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ r e a d
*
**************************************
*
* Functional description
* Read a data page.
*
**************************************/
2004-03-07 08:58:55 +01:00
Database* dbb = bdb->bdb_dbb;
const DWORD size = dbb->dbb_page_size;
2001-05-23 15:26:42 +02:00
FileExtendLockGuard extLock(NULL,
dbb->dbb_attachments ? file->fil_ext_lock : NULL, false);
OVERLAPPED overlapped, *overlapped_ptr;
2002-04-29 17:05:11 +02:00
if (!(file = seek_file(file, bdb, status_vector, &overlapped, &overlapped_ptr)))
return false;
2001-05-23 15:26:42 +02:00
HANDLE desc = file->fil_desc;
2001-05-23 15:26:42 +02:00
if (dbb->dbb_encrypt_key.hasData())
2001-12-24 03:51:06 +01:00
{
2001-05-23 15:26:42 +02:00
SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)];
DWORD actual_length;
2001-05-23 15:26:42 +02:00
if (!ReadFile(desc, spare_buffer, size, &actual_length, overlapped_ptr)
2001-12-24 03:51:06 +01:00
|| actual_length != size)
{
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
return nt_error("ReadFile", file, isc_io_read_err, status_vector);
}
(*dbb->dbb_decrypt) (dbb->dbb_encrypt_key.c_str(),
2001-05-23 15:26:42 +02:00
spare_buffer, size, page);
}
else
{
DWORD actual_length;
2001-05-23 15:26:42 +02:00
if (!ReadFile(desc, page, size, &actual_length, overlapped_ptr) ||
2001-11-02 21:59:38 +01:00
actual_length != size)
{
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE)
2001-11-02 21:59:38 +01:00
|| actual_length != size)
{
2001-05-23 15:26:42 +02:00
release_io_event(file, overlapped_ptr);
return nt_error("GetOverlappedResult", file, isc_io_read_err,
status_vector);
}
#else
2002-04-29 17:05:11 +02:00
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2001-05-23 15:26:42 +02:00
}
return nt_error("ReadFile", file, isc_io_read_err, status_vector);
2001-05-23 15:26:42 +02:00
#endif
}
}
#ifdef SUPERSERVER_V2
release_io_event(file, overlapped_ptr);
#endif
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2001-05-23 15:26:42 +02:00
}
return true;
2001-05-23 15:26:42 +02:00
}
#ifdef SUPERSERVER_V2
2004-03-07 08:58:55 +01:00
bool PIO_read_ahead(Database* dbb,
2001-12-24 03:51:06 +01:00
SLONG start_page,
SCHAR* buffer,
SLONG pages,
phys_io_blk* piob,
2003-04-10 08:49:16 +02:00
ISC_STATUS* status_vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ r e a d _ a h e a d
*
**************************************
*
* Functional description
* Read a contiguous set of pages. The only
* tricky part is to segment the I/O when crossing
* file boundaries.
*
**************************************/
OVERLAPPED overlapped, *overlapped_ptr;
/* If an I/O status block was passed the caller wants to
queue an asynchronous I/O. */
if (!piob) {
overlapped_ptr = &overlapped;
}
else {
overlapped_ptr = (OVERLAPPED *) & piob->piob_io_event;
piob->piob_flags = 0;
}
BufferDesc bdb;
2001-05-23 15:26:42 +02:00
while (pages) {
/* Setup up a dummy buffer descriptor block for seeking file. */
bdb.bdb_dbb = dbb;
bdb.bdb_page = start_page;
jrd_file* file = seek_file(dbb->dbb_file,
2001-12-24 03:51:06 +01:00
&bdb, status_vector,
overlapped_ptr,
&overlapped_ptr);
2001-05-23 15:26:42 +02:00
if (!file) {
return false;
2001-05-23 15:26:42 +02:00
}
/* Check that every page within the set resides in the same database
file. If not read what you can and loop back for the rest. */
DWORD segmented_length = 0;
2001-05-23 15:26:42 +02:00
while (pages && start_page >= file->fil_min_page
&& start_page <= file->fil_max_page)
{
2001-05-23 15:26:42 +02:00
segmented_length += dbb->dbb_page_size;
++start_page;
--pages;
}
HANDLE desc = file->fil_desc;
2001-05-23 15:26:42 +02:00
DWORD actual_length;
2001-12-24 03:51:06 +01:00
if (ReadFile( desc,
buffer,
segmented_length,
&actual_length,
overlapped_ptr) &&
actual_length == segmented_length)
{
if (piob && !pages) {
2001-05-23 15:26:42 +02:00
piob->piob_flags = PIOB_success;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
else if (piob && !pages) {
piob->piob_flags = PIOB_pending;
piob->piob_desc = reinterpret_cast<SLONG>(desc);
2001-05-23 15:26:42 +02:00
piob->piob_file = file;
piob->piob_io_length = segmented_length;
}
2001-12-24 03:51:06 +01:00
else if (!GetOverlappedResult( desc,
overlapped_ptr,
&actual_length,
TRUE) ||
actual_length != segmented_length)
{
if (piob) {
piob->piob_flags = PIOB_error;
}
2001-05-23 15:26:42 +02:00
release_io_event(file, overlapped_ptr);
return nt_error("GetOverlappedResult", file, isc_io_read_err,
status_vector);
}
if (!piob || (piob->piob_flags & (PIOB_success | PIOB_error)))
release_io_event(file, overlapped_ptr);
buffer += segmented_length;
}
return true;
2001-05-23 15:26:42 +02:00
}
#endif
#ifdef SUPERSERVER_V2
bool PIO_status(phys_io_blk* piob, ISC_STATUS* status_vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ s t a t u s
*
**************************************
*
* Functional description
* Check the status of an asynchronous I/O.
*
**************************************/
if (!(piob->piob_flags & PIOB_success)) {
if (piob->piob_flags & PIOB_error) {
return false;
2001-05-23 15:26:42 +02:00
}
DWORD actual_length;
2001-05-23 15:26:42 +02:00
if (!GetOverlappedResult((HANDLE) piob->piob_desc,
(OVERLAPPED *) & piob->piob_io_event,
&actual_length,
piob->piob_wait) ||
actual_length != piob->piob_io_length)
{
2001-05-23 15:26:42 +02:00
release_io_event(piob->piob_file,
(OVERLAPPED *) & piob->piob_io_event);
return nt_error("GetOverlappedResult", piob->piob_file,
isc_io_error, status_vector);
}
}
release_io_event(piob->piob_file, (OVERLAPPED *) & piob->piob_io_event);
return true;
2001-05-23 15:26:42 +02:00
}
#endif
bool PIO_write(jrd_file* file, BufferDesc* bdb, Ods::pag* page, ISC_STATUS* status_vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* P I O _ w r i t e
*
**************************************
*
* Functional description
* Write a data page.
*
**************************************/
OVERLAPPED overlapped, *overlapped_ptr;
2004-03-07 08:58:55 +01:00
Database* dbb = bdb->bdb_dbb;
const DWORD size = dbb->dbb_page_size;
2001-05-23 15:26:42 +02:00
FileExtendLockGuard extLock(NULL,
dbb->dbb_attachments ? file->fil_ext_lock : NULL, false);
2001-12-24 03:51:06 +01:00
file = seek_file(file, bdb, status_vector, &overlapped,
&overlapped_ptr);
if (!file) {
return false;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
HANDLE desc = file->fil_desc;
2001-05-23 15:26:42 +02:00
if (dbb->dbb_encrypt_key.hasData()) {
2001-05-23 15:26:42 +02:00
SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)];
(*dbb->dbb_encrypt) (dbb->dbb_encrypt_key.c_str(),
2001-05-23 15:26:42 +02:00
page, size, spare_buffer);
DWORD actual_length;
2001-12-24 03:51:06 +01:00
if (!WriteFile(desc, spare_buffer, size, &actual_length, overlapped_ptr)
|| actual_length != size)
{
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2001-12-24 03:51:06 +01:00
}
return nt_error("WriteFile", file, isc_io_write_err, status_vector);
2001-05-23 15:26:42 +02:00
}
}
else
{
DWORD actual_length;
if (!WriteFile(desc, page, size, &actual_length, overlapped_ptr)
|| actual_length != size )
2002-04-29 17:05:11 +02:00
{
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE)
2001-11-02 21:59:38 +01:00
|| actual_length != size)
2002-04-29 17:05:11 +02:00
{
release_io_event(file, overlapped_ptr);
return nt_error("GetOverlappedResult", file, isc_io_write_err,
status_vector);
}
2001-05-23 15:26:42 +02:00
#else
2002-04-29 17:05:11 +02:00
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2002-04-29 17:05:11 +02:00
}
return nt_error("WriteFile", file, isc_io_write_err, status_vector);
2001-05-23 15:26:42 +02:00
#endif
2002-04-29 17:05:11 +02:00
}
2001-05-23 15:26:42 +02:00
}
#ifdef SUPERSERVER_V2
release_io_event(file, overlapped_ptr);
#endif
2001-12-24 03:51:06 +01:00
if (ostype == OS_CHICAGO) {
file->fil_mutex.leave();
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
return true;
2001-05-23 15:26:42 +02:00
}
ULONG PIO_get_number_of_pages(const jrd_file* file, const USHORT pagesize)
2002-11-10 16:38:38 +01:00
{
2001-12-24 03:51:06 +01:00
/**************************************
*
* P I O _ g e t _ n u m b e r _ o f _ p a g e s
2002-11-10 16:38:38 +01:00
*
**************************************
*
* Functional description
2001-12-24 03:51:06 +01:00
* Compute number of pages in file, based only on file size.
*
**************************************/
HANDLE hFile = file->fil_desc;
2001-12-24 03:51:06 +01:00
DWORD dwFileSizeHigh;
const DWORD dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh);
2001-12-24 03:51:06 +01:00
2003-08-09 21:01:02 +02:00
if (dwFileSizeLow == (DWORD) -1) {
2001-12-24 03:51:06 +01:00
nt_error("GetFileSize", file, isc_io_access_err, 0);
}
const ULONGLONG ullFileSize = (((ULONGLONG) dwFileSizeHigh) << 32)
+ dwFileSizeLow;
2001-12-24 03:51:06 +01:00
return (ULONG) ((ullFileSize + pagesize - 1) / pagesize);
}
2001-05-23 15:26:42 +02:00
#ifdef SUPERSERVER_V2
static void release_io_event(jrd_file* file, OVERLAPPED* overlapped)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e l e a s e _ i o _ e v e n t
*
**************************************
*
* Functional description
* Release an overlapped I/O event
* back to the file block.
*
**************************************/
if (!overlapped || !overlapped->hEvent)
return;
file->fil_mutex.enter();
for (int i = 0; i < MAX_FILE_IO; i++)
2001-05-23 15:26:42 +02:00
if (!file->fil_io_events[i]) {
file->fil_io_events[i] = overlapped->hEvent;
overlapped->hEvent = NULL;
break;
}
file->fil_mutex.leave();
2001-05-23 15:26:42 +02:00
if (overlapped->hEvent)
CloseHandle(overlapped->hEvent);
}
#endif
static jrd_file* seek_file(jrd_file* file,
BufferDesc* bdb,
2003-04-10 08:49:16 +02:00
ISC_STATUS* status_vector,
2001-05-23 15:26:42 +02:00
OVERLAPPED* overlapped,
OVERLAPPED** overlapped_ptr)
{
/**************************************
*
* s e e k _ f i l e
*
**************************************
*
* Functional description
* Given a buffer descriptor block, find the appropropriate
* file block and seek to the proper page in that file.
*
**************************************/
2004-03-07 08:58:55 +01:00
Database* dbb = bdb->bdb_dbb;
2006-05-22 00:07:35 +02:00
ULONG page = bdb->bdb_page.getPageNum();
2001-05-23 15:26:42 +02:00
for (;; file = file->fil_next) {
if (!file) {
2003-09-26 13:46:03 +02:00
CORRUPT(158); /* msg 158 database file not available */
2001-05-23 15:26:42 +02:00
}
else if (page >= file->fil_min_page && page <= file->fil_max_page) {
break;
}
}
page -= file->fil_min_page - file->fil_fudge;
LARGE_INTEGER liOffset;
2001-05-23 15:26:42 +02:00
liOffset.QuadPart =
UInt32x32To64((DWORD) page, (DWORD) dbb->dbb_page_size);
if (ostype == OS_CHICAGO) {
file->fil_mutex.enter();
HANDLE desc = file->fil_desc;
2001-05-23 15:26:42 +02:00
if (SetFilePointer(desc,
(LONG) liOffset.LowPart,
&liOffset.HighPart, FILE_BEGIN) == 0xffffffff)
{
file->fil_mutex.leave();
nt_error("SetFilePointer", file, isc_io_access_err, status_vector);
return 0;
2001-05-23 15:26:42 +02:00
}
*overlapped_ptr = NULL;
}
else {
overlapped->Offset = liOffset.LowPart;
overlapped->OffsetHigh = liOffset.HighPart;
overlapped->Internal = 0;
overlapped->InternalHigh = 0;
overlapped->hEvent = (HANDLE) 0;
*overlapped_ptr = overlapped;
#ifdef SUPERSERVER_V2
file->fil_mutex.enter();
2001-12-24 03:51:06 +01:00
for (USHORT i = 0; i < MAX_FILE_IO; i++) {
2001-05-23 15:26:42 +02:00
if (overlapped->hEvent = (HANDLE) file->fil_io_events[i]) {
file->fil_io_events[i] = 0;
break;
}
}
file->fil_mutex.leave();
2001-05-23 15:26:42 +02:00
if (!overlapped->hEvent &&
!(overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
nt_error("CreateEvent", file, isc_io_access_err, status_vector);
return 0;
2001-05-23 15:26:42 +02:00
}
ResetEvent(overlapped->hEvent);
#endif
}
return file;
}
2005-09-08 18:41:56 +02:00
static jrd_file* setup_file(Database* dbb,
const Firebird::PathName& file_name,
HANDLE desc)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e t u p _ f i l e
*
**************************************
*
* Functional description
* Set up file and lock blocks for a file.
*
**************************************/
/* Allocate file block and copy file name string */
2005-09-08 18:41:56 +02:00
jrd_file* file = FB_NEW_RPT(*dbb->dbb_permanent, file_name.length() + 1) jrd_file;
file->fil_desc = desc;
2005-09-08 18:41:56 +02:00
file->fil_length = file_name.length();
2003-08-09 21:01:02 +02:00
file->fil_max_page = (ULONG) -1;
file->fil_ext_lock = NULL;
2002-04-29 17:05:11 +02:00
#ifdef SUPERSERVER_V2
2007-02-02 11:56:53 +01:00
memset(file->fil_io_events, 0, MAX_FILE_IO * sizeof(void*));
2002-04-29 17:05:11 +02:00
#endif
2005-09-08 18:41:56 +02:00
MOVE_FAST(file_name.c_str(), file->fil_string, file_name.length());
file->fil_string[file_name.length()] = 0;
2001-05-23 15:26:42 +02:00
/* If this isn't the primary file, we're done */
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
if (pageSpace && pageSpace->file)
2001-05-23 15:26:42 +02:00
return file;
/* Set a local variable that indicates whether we're running
under Windows/NT or Chicago */
// CVC: local variable to all this unit, it means.
2001-05-23 15:26:42 +02:00
ostype = /*ISC_is_WinNT() ? OS_WINDOWS_NT : */OS_CHICAGO;
2001-05-23 15:26:42 +02:00
/* Build unique lock string for file and construct lock block */
BY_HANDLE_FILE_INFORMATION file_info;
GetFileInformationByHandle(desc, &file_info);
UCHAR lock_string[32];
UCHAR* p = lock_string;
2001-05-23 15:26:42 +02:00
// The identifier is [nFileIndexHigh, nFileIndexLow]
// MSDN says: After a process opens a file, the identifier is constant until
// the file is closed. An application can use this identifier and the
// volume serial number to determine whether two handles refer to the same file.
size_t l = sizeof(file_info.dwVolumeSerialNumber);
2006-02-23 07:52:25 +01:00
memcpy(p, &file_info.dwVolumeSerialNumber, l);
p += l;
2001-05-23 15:26:42 +02:00
l = sizeof(file_info.nFileIndexHigh);
2006-02-23 07:52:25 +01:00
memcpy(p, &file_info.nFileIndexHigh, l);
p += l;
2001-05-23 15:26:42 +02:00
l = sizeof(file_info.nFileIndexLow);
2006-02-23 07:52:25 +01:00
memcpy(p, &file_info.nFileIndexLow, l);
p += l;
2001-05-23 15:26:42 +02:00
// We know p only was incremented, so can use safely size_t instead of ptrdiff_t
2001-05-23 15:26:42 +02:00
l = p - lock_string;
fb_assert(l <= sizeof(lock_string)); // In case we continue adding information.
2001-05-23 15:26:42 +02:00
file->fil_ext_lock = FB_NEW(*dbb->dbb_permanent)
FileExtendLock(*dbb->dbb_permanent, l, lock_string);
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, l) Lock;
dbb->dbb_lock = lock;
2001-05-23 15:26:42 +02:00
lock->lck_type = LCK_database;
2003-09-01 13:08:30 +02:00
lock->lck_owner_handle = LCK_get_owner_handle(NULL, lock->lck_type);
2001-12-24 03:51:06 +01:00
lock->lck_object = reinterpret_cast<blk*>(dbb);
2001-05-23 15:26:42 +02:00
lock->lck_length = l;
lock->lck_dbb = dbb;
lock->lck_ast = CCH_down_grade_dbb;
2001-05-23 15:26:42 +02:00
MOVE_FAST(lock_string, lock->lck_key.lck_string, l);
/* Try to get an exclusive lock on database. If this fails, insist
on at least a shared lock */
dbb->dbb_flags |= DBB_exclusive;
2003-09-01 13:08:30 +02:00
if (!LCK_lock(NULL, lock, LCK_EX, LCK_NO_WAIT)) {
2001-05-23 15:26:42 +02:00
dbb->dbb_flags &= ~DBB_exclusive;
thread_db* tdbb = JRD_get_thread_data();
while (!LCK_lock(tdbb, lock, LCK_SW, -1)) {
tdbb->tdbb_status_vector[0] = 0; // Clean status vector from lock manager error code
// If we are in a single-threaded maintenance mode then clean up and stop waiting
2007-02-02 11:56:53 +01:00
SCHAR spare_memory[MIN_PAGE_SIZE * 2];
SCHAR *header_page_buffer = (SCHAR*) FB_ALIGN((IPTR)spare_memory, MIN_PAGE_SIZE);
2006-08-17 14:08:49 +02:00
pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
try {
2006-05-22 00:07:35 +02:00
pageSpace->file = file;
PIO_header(dbb, header_page_buffer, MIN_PAGE_SIZE);
if ((reinterpret_cast<Ods::header_page*>(header_page_buffer)->hdr_flags & Ods::hdr_shutdown_mask) == Ods::hdr_shutdown_single)
2005-09-08 18:41:56 +02:00
ERR_post(isc_shutdown, isc_arg_cstring, file_name.length(), ERR_cstring(file_name), 0);
2006-05-22 00:07:35 +02:00
pageSpace->file = NULL; // Will be set again later by the caller
2006-05-20 05:55:54 +02:00
}
catch (const Firebird::Exception&) {
delete dbb->dbb_lock;
dbb->dbb_lock = NULL;
delete file;
2006-05-22 00:07:35 +02:00
pageSpace->file = NULL; // Will be set again later by the caller
throw;
}
}
2001-05-23 15:26:42 +02:00
}
return file;
}
static bool maybe_close_file(HANDLE& hFile)
2001-12-24 03:51:06 +01:00
{
/**************************************
*
* M a y b e C l o s e F i l e
*
**************************************
*
* Functional description
* If the file is open, close it.
*
**************************************/
if (hFile != INVALID_HANDLE_VALUE)
2001-12-24 03:51:06 +01:00
{
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
2001-12-24 03:51:06 +01:00
return true;
}
return false;
}
2001-05-23 15:26:42 +02:00
static bool nt_error(TEXT* string,
const jrd_file* file,
2003-04-10 08:49:16 +02:00
ISC_STATUS operation,
ISC_STATUS* status_vector)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* n t _ e r r o r
*
**************************************
*
* Functional description
* Somebody has noticed a file system error and expects error
* to do something about it. Harumph!
*
**************************************/
if (status_vector) {
*status_vector++ = isc_arg_gds;
*status_vector++ = isc_io_error;
*status_vector++ = isc_arg_string;
2003-04-10 08:49:16 +02:00
*status_vector++ = (ISC_STATUS) string;
*status_vector++ = isc_arg_string;
2001-05-23 15:26:42 +02:00
*status_vector++ =
(ISC_STATUS)(U_IPTR) ERR_string(file->fil_string, file->fil_length);
2001-05-23 15:26:42 +02:00
*status_vector++ = isc_arg_gds;
*status_vector++ = operation;
*status_vector++ = isc_arg_win32;
2001-05-23 15:26:42 +02:00
*status_vector++ = GetLastError();
*status_vector++ = isc_arg_end;
return false;
2001-05-23 15:26:42 +02:00
}
ERR_post(isc_io_error,
isc_arg_string, string,
isc_arg_string, ERR_string(file->fil_string, file->fil_length),
isc_arg_gds, operation, isc_arg_win32, GetLastError(), 0);
2001-05-23 15:26:42 +02:00
return true;
2001-05-23 15:26:42 +02:00
}