/* * PROGRAM: JRD Access Method * MODULE: winnt.cpp * 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. * * * 17-Oct-2001 Mike Nordell: Non-shared file access * * 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 */ #include "firebird.h" #include #include "../jrd/common.h" #include "../jrd/jrd.h" #include "../jrd/os/pio.h" #include "../jrd/ods.h" #include "../jrd/lck.h" #include "../jrd/cch.h" #include "gen/iberror.h" #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" #include "../jrd/os/pio_proto.h" #include "../common/classes/init.h" #include "../common/config/config.h" #include namespace Jrd { class FileExtendLockGuard { public: FileExtendLockGuard(Firebird::RWLock* lock, bool exclusive) : m_lock(lock), m_exclusive(exclusive) { if (m_exclusive) { fb_assert(m_lock); } if (m_lock) { if (m_exclusive) m_lock->beginWrite(); else m_lock->beginRead(); } } ~FileExtendLockGuard() { if (m_lock) { if (m_exclusive) m_lock->endWrite(); else m_lock->endRead(); } } private: // copying is prohibited FileExtendLockGuard(const FileExtendLockGuard&); FileExtendLockGuard& operator=(const FileExtendLockGuard&); Firebird::RWLock* const m_lock; const bool m_exclusive; }; } // namespace Jrd using namespace Jrd; using namespace Firebird; #ifdef TEXT #undef TEXT #endif #define TEXT SCHAR #ifdef SUPERSERVER_V2 static void release_io_event(jrd_file*, OVERLAPPED*); #endif static bool maybeCloseFile(HANDLE&); static jrd_file* seek_file(jrd_file*, BufferDesc*, ISC_STATUS*, OVERLAPPED*, OVERLAPPED**); static jrd_file* setup_file(Database*, const Firebird::PathName&, HANDLE, bool); static bool nt_error(const TEXT*, const jrd_file*, ISC_STATUS, ISC_STATUS* const); static void adjustFileSystemCacheSize(); struct AdjustFsCache { static void init() { adjustFileSystemCacheSize(); } }; InitMutex adjustFsCacheOnce; #ifdef SUPERSERVER_V2 static const DWORD g_dwShareFlags = FILE_SHARE_READ; // no write sharing static const DWORD g_dwExtraFlags = FILE_FLAG_OVERLAPPED | FILE_FLAG_NO_BUFFERING | FILE_FLAG_RANDOM_ACCESS; #else #ifdef SUPERSERVER static const DWORD g_dwShareFlags = FILE_SHARE_READ; // no write sharing 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 #endif static const DWORD g_dwShareTempFlags = FILE_SHARE_READ; static const DWORD g_dwExtraTempFlags = FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE; int PIO_add_file(Database* dbb, jrd_file* main_file, const Firebird::PathName& file_name, SLONG start) { /************************************** * * P I O _ a d d _ f i l e * ************************************** * * Functional description * Add a file to an existing database. Return the sequence * number of the new file. If anything goes wrong, return a * sequence of 0. * **************************************/ jrd_file* const new_file = PIO_create(dbb, file_name, false, false, false); if (!new_file) { return 0; } new_file->fil_min_page = start; USHORT sequence = 1; jrd_file* file; 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) { /************************************** * * P I O _ c l o s e * ************************************** * * Functional description * **************************************/ for (jrd_file* file = main_file; file; file = file->fil_next) { if (maybeCloseFile(file->fil_desc)) { #ifdef SUPERSERVER_V2 for (int i = 0; i < MAX_FILE_IO; i++) { if (file->fil_io_events[i]) { CloseHandle((HANDLE) file->fil_io_events[i]); file->fil_io_events[i] = 0; } } #endif } } } jrd_file* PIO_create(Database* dbb, const Firebird::PathName& string, const bool overwrite, const bool temporary, const bool share_delete) { /************************************** * * P I O _ c r e a t e * ************************************** * * Functional description * Create a new database file. * **************************************/ adjustFsCacheOnce.init(); const TEXT* file_name = string.c_str(); 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; const HANDLE desc = CreateFile(file_name, GENERIC_READ | GENERIC_WRITE, dwShareMode, NULL, (overwrite ? CREATE_ALWAYS : CREATE_NEW), dwFlagsAndAttributes, 0); if (desc == INVALID_HANDLE_VALUE) { ERR_post(Arg::Gds(isc_io_error) << Arg::Str("CreateFile (create)") << Arg::Str(string) << Arg::Gds(isc_io_create_err) << Arg::Windows(GetLastError())); } // File open succeeded. Now expand the file name. // workspace is the expanded name here Firebird::PathName workspace(string); ISC_expand_filename(workspace, false); return setup_file(dbb, workspace, desc, false); } bool PIO_expand(const TEXT* file_name, USHORT file_length, TEXT* expanded_name, size_t len_expanded) { /************************************** * * 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); } void PIO_extend(Database* dbb, 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. * **************************************/ // hvlad: prevent other reading\writing threads from changing file pointer. // As we open file without FILE_FLAG_OVERLAPPED, ReadFile\WriteFile calls // will change file pointer we set here and file truncation instead of file // extension will occurs. // It solved issue CORE-1468 (database file corruption when file extension // and read\write activity performed simultaneously) // if file have no extend lock it is better to not extend file than corrupt it if (!main_file->fil_ext_lock) return; Database::Checkout dcoHolder(dbb); FileExtendLockGuard extLock(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; const DWORD ret = SetFilePointer(hFile, newSize.LowPart, &newSize.HighPart, FILE_BEGIN); if (ret == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { nt_error("SetFilePointer", file, isc_io_write_err, 0); } if (!SetEndOfFile(hFile)) { nt_error("SetEndOfFile", file, isc_io_write_err, 0); } leftPages -= extendBy; } } } void PIO_flush(Database* dbb, jrd_file* main_file) { /************************************** * * P I O _ f l u s h * ************************************** * * Functional description * Flush the operating system cache back to good, solid oxide. * **************************************/ Database::Checkout dcoHolder(dbb); for (jrd_file* file = main_file; file; file = file->fil_next) { FlushFileBuffers(file->fil_desc); } } void PIO_force_write(jrd_file* file, const bool forceWrite, const bool notUseFSCache) { /************************************** * * 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. * **************************************/ const bool oldForce = (file->fil_flags & FIL_force_write) != 0; const bool oldNotUseCache = (file->fil_flags & FIL_no_fs_cache) != 0; if (forceWrite != oldForce || notUseFSCache != oldNotUseCache) { 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; HANDLE& hFile = file->fil_desc; maybeCloseFile(hFile); hFile = CreateFile(file->fil_string, GENERIC_READ | writeMode, g_dwShareFlags, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | force | fsCache | g_dwExtraFlags, 0); if (hFile == INVALID_HANDLE_VALUE) { ERR_post(Arg::Gds(isc_io_error) << Arg::Str("CreateFile (force write)") << Arg::Str(file->fil_string) << Arg::Gds(isc_io_access_err) << Arg::Windows(GetLastError())); } if (forceWrite) { file->fil_flags |= FIL_force_write; } else { file->fil_flags &= ~FIL_force_write; } if (notUseFSCache) { file->fil_flags |= FIL_no_fs_cache; } else { file->fil_flags &= ~FIL_no_fs_cache; } } } void PIO_header(Database* dbb, SCHAR* address, int length) { /************************************** * * 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 * **************************************/ PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); jrd_file* file = pageSpace->file; HANDLE desc = file->fil_desc; OVERLAPPED overlapped; OVERLAPPED* overlapped_ptr; 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()) { SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)]; if (!ReadFile(desc, spare_buffer, length, &actual_length, overlapped_ptr) || actual_length != (DWORD) length) { nt_error("ReadFile", file, isc_io_read_err, 0); } (*dbb->dbb_decrypt) (dbb->dbb_encrypt_key.c_str(), spare_buffer, length, address); } else { if (!ReadFile(desc, address, length, &actual_length, overlapped_ptr) || actual_length != (DWORD) length) { #ifdef SUPERSERVER_V2 if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) || actual_length != length) { CloseHandle(overlapped.hEvent); nt_error("GetOverlappedResult", file, isc_io_read_err, 0); } #else nt_error("ReadFile", file, isc_io_read_err, 0); #endif } } #ifdef SUPERSERVER_V2 CloseHandle(overlapped.hEvent); #endif } // we need a class here only to return memory on shutdown and avoid // false memory leak reports static Firebird::InitInstance zeros; USHORT PIO_init_data(Database* dbb, jrd_file* main_file, ISC_STATUS* status_vector, ULONG startPage, USHORT initPages) { /************************************** * * P I O _ i n i t _ d a t a * ************************************** * * Functional description * Initialize tail of file with zeros * **************************************/ const char* const zero_buff = zeros().getBuffer(); const size_t zero_buff_size = zeros().getSize(); Database::Checkout dcoHolder(dbb); FileExtendLockGuard extLock(main_file->fil_ext_lock, false); // Fake buffer, used in seek_file. Page space ID doesn't matter there // as we already know file to work with BufferDesc bdb; bdb.bdb_dbb = dbb; bdb.bdb_page = PageNumber(0, startPage); OVERLAPPED overlapped; OVERLAPPED* overlapped_ptr; jrd_file* file = seek_file(main_file, &bdb, status_vector, &overlapped, &overlapped_ptr); if (!file) return 0; if (file->fil_min_page + 8 > startPage) return 0; USHORT leftPages = initPages; const ULONG initBy = MIN(file->fil_max_page - startPage, leftPages); if (initBy < leftPages) leftPages = initBy; for (ULONG i = startPage; i < startPage + initBy; ) { bdb.bdb_page = PageNumber(0, i); USHORT write_pages = zero_buff_size / dbb->dbb_page_size; if (write_pages > leftPages) write_pages = leftPages; seek_file(main_file, &bdb, status_vector, &overlapped, &overlapped_ptr); const DWORD to_write = (DWORD) write_pages * dbb->dbb_page_size; DWORD written; if (!WriteFile(file->fil_desc, zero_buff, to_write, &written, overlapped_ptr) || to_write != written) { nt_error("WriteFile", file, isc_io_write_err, status_vector); break; } leftPages -= write_pages; i += write_pages; } return (initPages - leftPages); } jrd_file* PIO_open(Database* dbb, const Firebird::PathName& string, const Firebird::PathName& file_name, const bool share_delete) { /************************************** * * P I O _ o p e n * ************************************** * * Functional description * Open a database file. * **************************************/ const TEXT* const ptr = (string.hasData() ? string : file_name).c_str(); bool readOnly = false; adjustFsCacheOnce.init(); HANDLE desc = CreateFile(ptr, GENERIC_READ | GENERIC_WRITE, g_dwShareFlags | (share_delete ? FILE_SHARE_DELETE : 0), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | g_dwExtraFlags, 0); 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 | g_dwExtraFlags, 0); if (desc == INVALID_HANDLE_VALUE) { ERR_post(Arg::Gds(isc_io_error) << Arg::Str("CreateFile (open)") << Arg::Str(file_name) << Arg::Gds(isc_io_open_err) << Arg::Windows(GetLastError())); } else { // If this is the primary file, set Database flag to indicate that it is // 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. readOnly = true; PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); if (!pageSpace->file) dbb->dbb_flags |= DBB_being_opened_read_only; } } return setup_file(dbb, string, desc, readOnly); } bool PIO_read(jrd_file* file, BufferDesc* bdb, Ods::pag* page, ISC_STATUS* status_vector) { /************************************** * * P I O _ r e a d * ************************************** * * Functional description * Read a data page. * **************************************/ Database* const dbb = bdb->bdb_dbb; const DWORD size = dbb->dbb_page_size; Database::Checkout dcoHolder(dbb); FileExtendLockGuard extLock(file->fil_ext_lock, false); OVERLAPPED overlapped, *overlapped_ptr; if (!(file = seek_file(file, bdb, status_vector, &overlapped, &overlapped_ptr))) return false; HANDLE desc = file->fil_desc; if (dbb->dbb_encrypt_key.hasData()) { SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)]; DWORD actual_length; if (!ReadFile(desc, spare_buffer, size, &actual_length, overlapped_ptr) || actual_length != size) { return nt_error("ReadFile", file, isc_io_read_err, status_vector); } (*dbb->dbb_decrypt) (dbb->dbb_encrypt_key.c_str(), spare_buffer, size, page); } else { DWORD actual_length; if (!ReadFile(desc, page, size, &actual_length, overlapped_ptr) || actual_length != size) { #ifdef SUPERSERVER_V2 if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) || actual_length != size) { release_io_event(file, overlapped_ptr); return nt_error("GetOverlappedResult", file, isc_io_read_err, status_vector); } #else return nt_error("ReadFile", file, isc_io_read_err, status_vector); #endif } } #ifdef SUPERSERVER_V2 release_io_event(file, overlapped_ptr); #endif return true; } #ifdef SUPERSERVER_V2 bool PIO_read_ahead(Database* dbb, SLONG start_page, SCHAR* buffer, SLONG pages, phys_io_blk* piob, ISC_STATUS* status_vector) { /************************************** * * 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; Database::Checkout dcoHolder(dbb); // 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; 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, &bdb, status_vector, overlapped_ptr, &overlapped_ptr); if (!file) { return false; } // 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; while (pages && start_page >= file->fil_min_page && start_page <= file->fil_max_page) { segmented_length += dbb->dbb_page_size; ++start_page; --pages; } HANDLE desc = file->fil_desc; DWORD actual_length; if (ReadFile(desc, buffer, segmented_length, &actual_length, overlapped_ptr) && actual_length == segmented_length) { if (piob && !pages) { piob->piob_flags = PIOB_success; } } else if (piob && !pages) { piob->piob_flags = PIOB_pending; piob->piob_desc = reinterpret_cast(desc); piob->piob_file = file; piob->piob_io_length = segmented_length; } else if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) || actual_length != segmented_length) { if (piob) { piob->piob_flags = PIOB_error; } 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; } #endif #ifdef SUPERSERVER_V2 bool PIO_status(Database* dbb, phys_io_blk* piob, ISC_STATUS* status_vector) { /************************************** * * P I O _ s t a t u s * ************************************** * * Functional description * Check the status of an asynchronous I/O. * **************************************/ Database::Checkout dcoHolder(dbb); if (!(piob->piob_flags & PIOB_success)) { if (piob->piob_flags & PIOB_error) { return false; } DWORD actual_length; if (!GetOverlappedResult((HANDLE) piob->piob_desc, (OVERLAPPED*) &piob->piob_io_event, &actual_length, piob->piob_wait) || actual_length != piob->piob_io_length) { release_io_event(piob->piob_file, (OVERLAPPED*) &piob->piob_io_event); return nt_error("GetOverlappedResult", piob->piob_file, isc_io_error, status_vector); // io_error is wrong here as primary & secondary error. } } release_io_event(piob->piob_file, (OVERLAPPED*) &piob->piob_io_event); return true; } #endif bool PIO_write(jrd_file* file, BufferDesc* bdb, Ods::pag* page, ISC_STATUS* status_vector) { /************************************** * * P I O _ w r i t e * ************************************** * * Functional description * Write a data page. * **************************************/ OVERLAPPED overlapped, *overlapped_ptr; Database* const dbb = bdb->bdb_dbb; const DWORD size = dbb->dbb_page_size; Database::Checkout dcoHolder(dbb); FileExtendLockGuard extLock(file->fil_ext_lock, false); file = seek_file(file, bdb, status_vector, &overlapped, &overlapped_ptr); if (!file) { return false; } HANDLE desc = file->fil_desc; if (dbb->dbb_encrypt_key.hasData()) { SLONG spare_buffer[MAX_PAGE_SIZE / sizeof(SLONG)]; (*dbb->dbb_encrypt) (dbb->dbb_encrypt_key.c_str(), page, size, spare_buffer); DWORD actual_length; if (!WriteFile(desc, spare_buffer, size, &actual_length, overlapped_ptr) || actual_length != size) { return nt_error("WriteFile", file, isc_io_write_err, status_vector); } } else { DWORD actual_length; if (!WriteFile(desc, page, size, &actual_length, overlapped_ptr) || actual_length != size ) { #ifdef SUPERSERVER_V2 if (!GetOverlappedResult(desc, overlapped_ptr, &actual_length, TRUE) || actual_length != size) { release_io_event(file, overlapped_ptr); return nt_error("GetOverlappedResult", file, isc_io_write_err, status_vector); } #else return nt_error("WriteFile", file, isc_io_write_err, status_vector); #endif } } #ifdef SUPERSERVER_V2 release_io_event(file, overlapped_ptr); #endif return true; } ULONG PIO_get_number_of_pages(const jrd_file* file, const USHORT pagesize) { /************************************** * * P I O _ g e t _ n u m b e r _ o f _ p a g e s * ************************************** * * Functional description * Compute number of pages in file, based only on file size. * **************************************/ HANDLE hFile = file->fil_desc; DWORD dwFileSizeHigh; const DWORD dwFileSizeLow = GetFileSize(hFile, &dwFileSizeHigh); if (dwFileSizeLow == (DWORD) -1) { nt_error("GetFileSize", file, isc_io_access_err, 0); } const ULONGLONG ullFileSize = (((ULONGLONG) dwFileSizeHigh) << 32) + dwFileSizeLow; return (ULONG) ((ullFileSize + pagesize - 1) / pagesize); } void PIO_get_unique_file_id(const Jrd::jrd_file* file, Firebird::UCharBuffer& id) { /************************************** * * P I O _ g e t _ u n i q u e _ f i l e _ i d * ************************************** * * Functional description * Return a binary string that uniquely identifies the file. * **************************************/ BY_HANDLE_FILE_INFORMATION file_info; GetFileInformationByHandle(file->fil_desc, &file_info); // 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. const size_t len1 = sizeof(file_info.dwVolumeSerialNumber); const size_t len2 = sizeof(file_info.nFileIndexHigh); const size_t len3 = sizeof(file_info.nFileIndexLow); UCHAR* p = id.getBuffer(len1 + len2 + len3); memcpy(p, &file_info.dwVolumeSerialNumber, len1); p += len1; memcpy(p, &file_info.nFileIndexHigh, len2); p += len2; memcpy(p, &file_info.nFileIndexLow, len3); } #ifdef SUPERSERVER_V2 static void release_io_event(jrd_file* file, OVERLAPPED* overlapped) { /************************************** * * 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; Firebird::MutexLockGuard guard(file->fil_mutex); for (int i = 0; i < MAX_FILE_IO; i++) { if (!file->fil_io_events[i]) { file->fil_io_events[i] = overlapped->hEvent; overlapped->hEvent = NULL; break; } } if (overlapped->hEvent) CloseHandle(overlapped->hEvent); } #endif static jrd_file* seek_file(jrd_file* file, BufferDesc* bdb, ISC_STATUS* /*status_vector*/, OVERLAPPED* overlapped, OVERLAPPED** overlapped_ptr) { /************************************** * * s e e k _ f i l e * ************************************** * * Functional description * Given a buffer descriptor block, find the appropriate * file block and seek to the proper page in that file. * **************************************/ Database* const dbb = bdb->bdb_dbb; ULONG page = bdb->bdb_page.getPageNum(); for (;; file = file->fil_next) { if (!file) { CORRUPT(158); // msg 158 database file not available } else if (page >= file->fil_min_page && page <= file->fil_max_page) { break; } } page -= file->fil_min_page - file->fil_fudge; LARGE_INTEGER liOffset; liOffset.QuadPart = UInt32x32To64((DWORD) page, (DWORD) dbb->dbb_page_size); overlapped->Offset = liOffset.LowPart; overlapped->OffsetHigh = liOffset.HighPart; overlapped->Internal = 0; overlapped->InternalHigh = 0; overlapped->hEvent = (HANDLE) 0; *overlapped_ptr = overlapped; fb_assert(dbb == bdb->bdb_dbb); #ifdef SUPERSERVER_V2 Firebird::MutexLockGuard guard(file->fil_mutex); for (USHORT i = 0; i < MAX_FILE_IO; i++) { if (overlapped->hEvent = (HANDLE) file->fil_io_events[i]) { file->fil_io_events[i] = 0; break; } } if (!overlapped->hEvent && !(overlapped->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { nt_error("CreateEvent", file, isc_io_access_err, status_vector); return 0; } ResetEvent(overlapped->hEvent); #endif return file; } static jrd_file* setup_file(Database* dbb, const Firebird::PathName& file_name, HANDLE desc, bool read_only) { /************************************** * * s e t u p _ f i l e * ************************************** * * Functional description * Set up file and lock blocks for a file. * **************************************/ jrd_file* file = NULL; try { file = FB_NEW_RPT(*dbb->dbb_permanent, file_name.length() + 1) jrd_file(); file->fil_desc = desc; file->fil_max_page = MAX_ULONG; strcpy(file->fil_string, file_name.c_str()); #ifdef SUPERSERVER_V2 memset(file->fil_io_events, 0, MAX_FILE_IO * sizeof(void*)); #endif if (read_only) file->fil_flags |= FIL_readonly; // If this isn't the primary file, we're done const PageSpace* const pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE); if (pageSpace && pageSpace->file) return file; file->fil_ext_lock = FB_NEW(*dbb->dbb_permanent) Firebird::RWLock(); } catch (const Firebird::Exception&) { CloseHandle(desc); delete file; throw; } fb_assert(file); return file; } static bool maybeCloseFile(HANDLE& hFile) { /************************************** * * 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) { CloseHandle(hFile); hFile = INVALID_HANDLE_VALUE; return true; } return false; } static bool nt_error(const TEXT* string, const jrd_file* file, ISC_STATUS operation, ISC_STATUS* const status_vector) { /************************************** * * 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) { ERR_post(Arg::Gds(isc_io_error) << Arg::Str(string) << Arg::Str(file->fil_string) << Arg::Gds(operation) << Arg::Windows(GetLastError())); } ERR_build_status(status_vector, Arg::Gds(isc_io_error) << Arg::Str(string) << Arg::Str(file->fil_string) << Arg::Gds(operation) << Arg::Windows(GetLastError())); gds__log_status(0, status_vector); return false; } // These are defined in Windows Server 2008 SDK #ifndef FILE_CACHE_FLAGS_DEFINED #define FILE_CACHE_MAX_HARD_ENABLE 0x00000001 #define FILE_CACHE_MAX_HARD_DISABLE 0x00000002 #define FILE_CACHE_MIN_HARD_ENABLE 0x00000004 #define FILE_CACHE_MIN_HARD_DISABLE 0x00000008 #endif // FILE_CACHE_FLAGS_DEFINED BOOL SetPrivilege( HANDLE hToken, // access token handle LPCTSTR lpszPrivilege, // name of privilege to enable/disable BOOL bEnablePrivilege // to enable or disable privilege ) { TOKEN_PRIVILEGES tp; LUID luid; if ( !LookupPrivilegeValue( NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &luid ) ) // receives LUID of privilege { // gds__log("LookupPrivilegeValue error: %u", GetLastError() ); return FALSE; } tp.PrivilegeCount = 1; tp.Privileges[0].Luid = luid; if (bEnablePrivilege) tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else tp.Privileges[0].Attributes = 0; // Enable or disable the privilege if ( !AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES) NULL, (PDWORD) NULL) ) { //gds__log("AdjustTokenPrivileges error: %u", GetLastError() ); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { //gds__log("The token does not have the specified privilege"); return FALSE; } return TRUE; } static void adjustFileSystemCacheSize() { int percent = Config::getFileSystemCacheSize(); // firebird.conf asks to do nothing if (percent == 0) return; // Ensure that the setting has a sensible value if (percent > 95 || percent < 10) { gds__log("Incorrect FileSystemCacheSize setting %d. Using default (30 percent).", percent); percent = 30; } HMODULE hmodKernel32 = GetModuleHandle("kernel32.dll"); // This one requires 64-bit XP or Windows Server 2003 SP1 typedef BOOL (WINAPI *PFnSetSystemFileCacheSize)(SIZE_T, SIZE_T, DWORD); typedef BOOL (WINAPI *PFnGetSystemFileCacheSize)(PSIZE_T, PSIZE_T, PDWORD); // This one needs any NT, but load it dynamically anyways just in case typedef BOOL (WINAPI *PFnGlobalMemoryStatusEx)(LPMEMORYSTATUSEX); PFnSetSystemFileCacheSize pfnSetSystemFileCacheSize = (PFnSetSystemFileCacheSize) GetProcAddress(hmodKernel32, "SetSystemFileCacheSize"); PFnGetSystemFileCacheSize pfnGetSystemFileCacheSize = (PFnGetSystemFileCacheSize) GetProcAddress(hmodKernel32, "GetSystemFileCacheSize"); PFnGlobalMemoryStatusEx pfnGlobalMemoryStatusEx = (PFnGlobalMemoryStatusEx) GetProcAddress(hmodKernel32, "GlobalMemoryStatusEx"); // If we got too old OS and functions are not there - do not bother if (!pfnGetSystemFileCacheSize || !pfnSetSystemFileCacheSize || !pfnGlobalMemoryStatusEx) return; MEMORYSTATUSEX msex; msex.dwLength = sizeof (msex); // This should work if (!pfnGlobalMemoryStatusEx(&msex)) system_call_failed::raise("GlobalMemoryStatusEx", GetLastError()); SIZE_T origMinimumFileCacheSize, origMaximumFileCacheSize; DWORD origFlags; BOOL result = pfnGetSystemFileCacheSize(&origMinimumFileCacheSize, &origMaximumFileCacheSize, &origFlags); if (!result) { gds__log("GetSystemFileCacheSize error %d", GetLastError()); return; } // Somebody has already configured maximum cache size limit // Hope it is a sensible one - do not bother to adjust it if ((origFlags & FILE_CACHE_MAX_HARD_ENABLE) != 0) return; DWORDLONG maxMem = (msex.ullTotalPhys / 100) * percent; #ifndef _WIN64 // If we are trying to set the limit so high that it doesn't fit // in 32-bit API - leave settings alone and write a message to log file if (maxMem > (SIZE_T)(-2)) { gds__log("Could not use 32-bit SetSystemFileCacheSize API to set cache size limit to %I64d." "Please use 64-bit engine or configure cache size limit externally", maxMem); return; } #endif HANDLE hToken; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { gds__log("OpenProcessToken error %d", GetLastError()); return; } SetPrivilege(hToken, "SeIncreaseQuotaPrivilege", TRUE); result = pfnSetSystemFileCacheSize(0, maxMem, FILE_CACHE_MAX_HARD_ENABLE); DWORD error = GetLastError(); SetPrivilege(hToken, "SeIncreaseQuotaPrivilege", FALSE); CloseHandle(hToken); if (!result) { // If we do not have enough permissions - silently ignore the error gds__log("SetSystemFileCacheSize error %d. " "The engine will continue to operate, but the system " "performance may degrade significantly when working with " "large databases", error); } }