📄 file.cxx
字号:
//-< FILE.CXX >------------------------------------------------------*--------*
// POST++ Version 1.0 (c) 1998 GARRET * ? *
// (Persistent Object Storage) * /\| *
// * / \ *
// Created: 2-Feb-98 K.A. Knizhnik * / [] \ *
// Last update: 21-Jan-99 K.A. Knizhnik * GARRET *
// * / \ *
// Port to AIX done by XaoYao 13-Apr-97 * *
// Also thanks to Wolfgang Hendriks and Antonio Corrao for reporting
// bugs in this module.
//-------------------------------------------------------------------*--------*
// Mapped on memory file class implementation
//-------------------------------------------------------------------*--------*
#include <time.h>
#include "storage.h"
file* file::chain;
char* program_compilation_time;
void file::set_file_name(const char* file_name)
{
int len = strlen(file_name);
name = new char[len+5];
log_name = new char[len+5];
sav_name = new char[len+5];
tmp_name = new char[len+5];
strcpy(name, file_name);
if (len < 4 || file_name[len-4] != '.') {
strcat(name, ".odb");
}
strcat(strcpy(log_name, file_name), ".log");
strcat(strcpy(tmp_name, file_name), ".tmp");
strcat(strcpy(sav_name, file_name), ".sav");
}
#ifdef _WIN32
bool file::handle_page_access(DWORD* params)
{
for (file* fp = chain; fp != NULL; fp = fp->next) {
if (params[1] - (DWORD)fp->base < fp->mapped_size) {
return fp->prot != read_only
&& fp->create_shadow_page(params[0], (void*)params[1]);
}
}
return false;
}
static LONG WINAPI AccessViolationHandler(LPEXCEPTION_POINTERS ep)
{
return ep->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION
&& file::handle_page_access(ep->ExceptionRecord->ExceptionInformation)
? EXCEPTION_CONTINUE_EXECUTION
: EXCEPTION_CONTINUE_SEARCH;
}
file::file(const char* name, size_t max_file_size, size_t max_locked_pages)
{
SYSTEM_INFO sysinfo;
MEMORYSTATUS memstat;
OSVERSIONINFO osinfo;
osinfo.dwOSVersionInfoSize = sizeof osinfo;
GetVersionEx(&osinfo);
platform = osinfo.dwPlatformId;
GetSystemInfo(&sysinfo);
allocation_granularity = sysinfo.dwAllocationGranularity;
page_size = sysinfo.dwPageSize;
if (platform == VER_PLATFORM_WIN32_NT) {
DWORD MinimumWorkingSetSize, MaximumWorkingSetSize;
GetProcessWorkingSetSize(GetCurrentProcess(),
&MinimumWorkingSetSize,
&MaximumWorkingSetSize);
if (max_locked_pages > MinimumWorkingSetSize/page_size) {
MinimumWorkingSetSize = max_locked_pages*2*page_size;
if (MaximumWorkingSetSize < MinimumWorkingSetSize) {
MaximumWorkingSetSize = MinimumWorkingSetSize;
}
if (!SetProcessWorkingSetSize(GetCurrentProcess(),
MinimumWorkingSetSize,
MaximumWorkingSetSize))
{
const size_t max_nt_locked_pages = 30;
TRACE_MSG(("file::file: failed to extend process working set "
"size to %ld bytes, set max_locked_pages to %ld\n",
MinimumWorkingSetSize, max_nt_locked_pages));
max_locked_pages = max_nt_locked_pages;
} else {
TRACE_MSG(("file::file: extend process working set size "
"to %ld bytes\n", MinimumWorkingSetSize));
}
}
} else {
max_locked_pages = 0;
}
memstat.dwLength = sizeof(memstat);
GlobalMemoryStatus(&memstat);
if (memstat.dwAvailVirtual < max_file_size) {
max_file_size = memstat.dwAvailVirtual & ~(allocation_granularity-1);
TRACE_MSG(("file::file: set max_file_size to %ld bytes\n",
max_file_size));
}
set_file_name(name);
this->max_file_size = ALIGN(max_file_size, allocation_granularity);
this->max_locked_pages = max_locked_pages;
log_buffer = new char[page_size+sizeof(long)];
locked_page = new char*[max_locked_pages];
SetUnhandledExceptionFilter(AccessViolationHandler);
error_code = ok;
}
file::~file()
{
delete[] name;
delete[] log_name;
delete[] tmp_name;
delete[] sav_name;
delete[] log_buffer;
delete[] locked_page;
}
inline bool file::recover_file()
{
DWORD read_bytes;
size_t trans_size = page_size + sizeof(long);
int n_recovered_pages = 0;
TRACE_MSG(("file::recover_file: recover data file from transaction log\n"));
recovery = true;
while (ReadFile(log, log_buffer, trans_size, &read_bytes, NULL)) {
if (read_bytes != trans_size) {
recovery = false;
if (read_bytes == 0) {
size = ALIGN(((file_header*)base)->file_size, page_size);
TRACE_MSG(("recover::file: recover %d pages, set file size "
"to %ld\n", n_recovered_pages, size));
error_code = ok;
return true;
} else {
error_code = end_of_file;
TRACE_MSG(("file::recover_file: read %ld bytes from log "
"instead of %ld\n",
read_bytes, trans_size));
return false;
}
}
size_t offs = *(long*)log_buffer;
assert(offs < size);
memcpy(base + offs, log_buffer+sizeof(long), page_size);
n_recovered_pages += 1;
}
error_code = GetLastError();
TRACE_MSG(("file::recover_file: failed to read log file: %d\n",
error_code));
recovery = false;
return false;
}
inline bool file::flush_log_buffer()
{
if (!FlushFileBuffers(log)) {
error_code = GetLastError();
TRACE_MSG(("file::flush_log_buffer: FlushFileBuffers failed: %d\n",
error_code));
return false;
}
for (int i = n_locked_pages; --i >= 0;) {
if (!VirtualUnlock(locked_page[i], page_size)) {
error_code = GetLastError();
TRACE_MSG(("file::flush_log_buffer: VirtualUnlock %p failed: %d\n",
locked_page[i], error_code));
return false;
}
}
n_locked_pages = 0;
return true;
}
bool file::create_shadow_page(int modify, void* addr)
{
DWORD old_prot;
DWORD written_bytes;
char* page = (char*)(long(addr) & ~(page_size-1));
DWORD offs = page - base;
if (!VirtualProtect(page, page_size,
modify ? PAGE_READWRITE : PAGE_READONLY, &old_prot))
{
TRACE_MSG(("file::create_shadow_page: VirtualProtect %p failed: %ld\n",
page, GetLastError()));
return false;
}
if (platform != VER_PLATFORM_WIN32_NT) {
if (modify) {
unsigned page_no = offs / page_size;
dirty_page_map[page_no >> 5] |= 1 << (page_no & 0x1F);
if (recovery) {
return true;
}
}
if (old_prot == PAGE_NOACCESS) {
if (!modify) {
if (!VirtualProtect(page,page_size,PAGE_READWRITE,&old_prot)){
TRACE_MSG(("file::create_shadow_page: "
"VirtualProtect2 %p failed: %ld\n",
page, GetLastError()));
return false;
}
}
DWORD read_bytes;
if (SetFilePointer(fd, offs, NULL, FILE_BEGIN) != offs
|| !ReadFile(fd, page, page_size, &read_bytes, NULL))
{
return false;
}
if (!modify) {
if (!VirtualProtect(page,page_size,PAGE_READONLY,&old_prot)) {
TRACE_MSG(("file::create_shadow_page: "
"VirtualProtect3 %p failed: %ld\n",
page, GetLastError()));
}
return true;
}
}
}
*(long*)log_buffer = offs;
memcpy(log_buffer+sizeof(long), page, page_size);
size_t trans_size = page_size + sizeof(long);
if (!WriteFile(log, log_buffer, trans_size, &written_bytes, NULL)
|| written_bytes != trans_size)
{
TRACE_MSG(("file::create_shadow_page: WriteFile failed: %d\n",
GetLastError()));
return false;
}
if (max_locked_pages != 0) {
if (n_locked_pages >= max_locked_pages) {
return flush_log_buffer();
}
if (!VirtualLock(page, page_size)) {
TRACE_MSG(("file::create_shadow_page: VirtualLock %p failed,"
"number of locked pages %ld: %ld\n",
page, n_locked_pages, GetLastError()));
if (n_locked_pages != 0) {
max_locked_pages = n_locked_pages;
}
return flush_log_buffer();
}
locked_page[n_locked_pages++] = page;
}
return true;
}
bool file::read_file_in_memory()
{
#if DEBUG_LEVEL >= DEBUG_TRACE
msg_buf buf;
#endif
DWORD read_bytes;
vmem = (char*)VirtualAlloc(base, mapped_size,
MEM_RESERVE, PAGE_READWRITE);
if (vmem == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::read_file_in_memory: failed to virtual alloc at "
"address %p: %s\n", base, get_error_text(buf, sizeof buf)));
vmem = (char*)VirtualAlloc(NULL, mapped_size,
MEM_RESERVE, PAGE_READWRITE);
}
if (vmem == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::read_file_in_memory: failed to virtual alloc: %s\n",
get_error_text(buf, sizeof buf)));
CloseHandle(fd);
return false;
}
base = vmem;
if (size != 0) {
TRACE_MSG(("file::read_file_in_memory: virtual alloc: address=%p, "
"mapped_size=%ld, size=%ld\n", vmem, mapped_size, size));
if (base != (char*)VirtualAlloc(base, size, MEM_COMMIT,
PAGE_READWRITE)
|| SetFilePointer(fd, 0, NULL, FILE_BEGIN) != 0
|| ReadFile(fd, base, size, &read_bytes, NULL) == false)
{
error_code = GetLastError();
TRACE_MSG(("file::read_file_in_memory: failed to read file in "
"memory: %s\n", get_error_text(buf, sizeof buf)));
CloseHandle(fd);
VirtualFree(vmem, 0, MEM_RELEASE);
return false;
} else if (read_bytes != size) {
error_code = file_size_not_aligned;
TRACE_MSG(("file::read_file_in_memory:read %ld bytes instead "
"of %ld\n", read_bytes, size));
CloseHandle(fd);
VirtualFree(vmem, 0, MEM_RELEASE);
return false;
}
}
return true;
}
bool file::write_dirty_pages_in_file()
{
size_t offs = 0;
unsigned page_no;
for (page_no = 0; offs < size; page_no += 1) {
int mask = 1 << (page_no & 0x1F);
if (dirty_page_map[page_no >> 5] & mask) {
dirty_page_map[page_no >> 5] &= ~mask;
DWORD written_bytes;
if (SetFilePointer(fd, offs, NULL, FILE_BEGIN) != offs
|| !WriteFile(fd, base+offs, page_size, &written_bytes, 0)
|| written_bytes != page_size)
{
error_code = GetLastError();
TRACE_MSG(("file::write_dirty_pages_in_file: failed to write"
" page in file: %d\n", error_code));
return false;
}
DWORD old_prot;
if (!VirtualProtect(base+offs, page_size, PAGE_READONLY, &old_prot)) {
error_code = GetLastError();
TRACE_MSG(("file::write_dirty_pages_in_file: failed to change protection"
" to read-only: %d\n", error_code));
return false;
}
}
offs += page_size;
}
if (!FlushFileBuffers(fd)) {
error_code = GetLastError();
TRACE_MSG(("file::write_dirty_pages_in_file: FlushFileBuffers "
"failed: %d\n", error_code));
return false;
}
return true;
}
bool file::open(open_mode mode, access_prot prot)
{
#if DEBUG_LEVEL >= DEBUG_TRACE
msg_buf buf;
#endif
DWORD old_prot;
assert(name != NULL);
if (prot == read_only) {
mode = map_file;
} else if (mode == copy_on_write_map && platform != VER_PLATFORM_WIN32_NT){
mode = load_in_memory; // copy on write not working in Windows 95
}
this->mode = mode;
this->prot = prot;
vmem = NULL;
md = NULL;
recovery = false;
if (mode == shadow_pages_transaction) {
int flags = (platform == VER_PLATFORM_WIN32_NT)
? FILE_FLAG_WRITE_THROUGH|FILE_FLAG_RANDOM_ACCESS
: FILE_FLAG_RANDOM_ACCESS;
fd = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0,
NULL, OPEN_ALWAYS, flags, NULL);
if (fd == INVALID_HANDLE_VALUE) {
error_code = GetLastError();
TRACE_MSG(("file::open: failed to open file '%s': %s\n",
name, get_error_text(buf, sizeof buf)));
return false;
}
DWORD read_bytes;
file_header hdr;
hdr.base_address = NULL;
hdr.file_size = 0;
if (!ReadFile(fd, &hdr, sizeof hdr, &read_bytes, NULL)
|| (read_bytes != 0 && read_bytes != sizeof hdr))
{
error_code = GetLastError();
TRACE_MSG(("file::open: failed to read file: %s\n",
get_error_text(buf, sizeof buf)));
CloseHandle(fd);
return false;
}
base = (char*)hdr.base_address;
allocated_size = size = ALIGN(hdr.file_size, page_size);
mapped_size = size > max_file_size ? size : max_file_size;
if (platform == VER_PLATFORM_WIN32_NT) {
md = CreateFileMapping(fd,NULL,PAGE_READWRITE,0,mapped_size,NULL);
if (md == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::open: failed to create file mapping: "
"base=%p, size=%ld: %s\n",
base, mapped_size,
get_error_text(buf, sizeof buf)));
CloseHandle(fd);
return false;
}
void* p = MapViewOfFileEx(md, FILE_MAP_ALL_ACCESS, 0, 0, 0, base);
if (p == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::open: failed to map view of file on address "
"%p: %s\n", base, get_error_text(buf, sizeof buf)));
p = MapViewOfFileEx(md, FILE_MAP_ALL_ACCESS, 0, 0, 0, NULL);
if (p == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::open: failed to map view of file: %s\n",
get_error_text(buf, sizeof buf)));
CloseHandle(fd);
CloseHandle(md);
return false;
}
}
base = (char*)p;
dirty_page_map = NULL;
} else {
//
// VirtualProtect doesn't work in Winfows 95 with
// mapped on file memory. We have to use VirtualAlloc instead
// to handle page faults and read pages from the file ourself.
//
vmem = (char*)VirtualAlloc(base, mapped_size,
MEM_RESERVE, PAGE_READWRITE);
if (vmem == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::open: failed to virtual alloc at address"
" %p: %s\n", base, get_error_text(buf,sizeof buf)));
vmem = (char*)VirtualAlloc(NULL, mapped_size,
MEM_RESERVE, PAGE_READWRITE);
}
if (vmem == NULL) {
error_code = GetLastError();
TRACE_MSG(("file::open: failed to virtual alloc: %s\n",
get_error_text(buf, sizeof buf)));
CloseHandle(fd);
return false;
}
base = vmem;
if (size != 0) {
TRACE_MSG(("file::open: virtual alloc: address=%p, "
"mapped_size=%ld, size=%ld\n",
vmem, mapped_size, size));
if (base != (char*)VirtualAlloc(base, size, MEM_COMMIT,
PAGE_NOACCESS))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -