📄 file.cxx
字号:
return false;
}
}
} else { // windows 95
if (fd != INVALID_HANDLE_VALUE) {
CloseHandle(fd);
fd = INVALID_HANDLE_VALUE;
}
BOOL backup = MoveFile(name, sav_name);// may be exists, may be not
if (!backup && GetLastError() == ERROR_ALREADY_EXISTS) {
DeleteFile(sav_name); // remove previous backup file
backup = MoveFile(name, sav_name);
if (!backup) {
error_code = GetLastError();
TRACE_MSG(("file::flush: failed to make backup copy of "
"file '%s'\n", name));
return false;
}
}
if (!MoveFile(tmp_name, name)) {
error_code = GetLastError();
TRACE_MSG(("file::flush: failed to rename file '%s' to '%s'\n",
tmp_name, name));
return false;
}
if (backup) {
DeleteFile(sav_name);
}
}
error_code = ok;
return true;
}
}
bool file::close()
{
if (mode == shadow_pages_transaction) {
file *fp, **fpp = &chain;
while ((fp = *fpp) != this) {
fpp = &fp->next;
}
*fpp = fp->next;
if (n_locked_pages != 0 || platform != VER_PLATFORM_WIN32_NT) {
if (!flush_log_buffer()) {
return false;
}
}
if (platform != VER_PLATFORM_WIN32_NT) {
if (!write_dirty_pages_in_file()) {
return false;
}
delete[] dirty_page_map;
}
}
if (vmem != NULL) {
if (!VirtualFree(vmem, 0, MEM_RELEASE)) {
error_code = GetLastError();
TRACE_MSG(("file::close: failed to free %p: %d\n",
vmem, error_code));
return false;
}
}
if (md != NULL) {
if (!UnmapViewOfFile(base)) {
error_code = GetLastError();
TRACE_MSG(("file::close: failed to unmap %p: %d\n",
base, error_code));
return false;
}
if (!CloseHandle(md)) {
error_code = GetLastError();
TRACE_MSG(("file::close: failed to close file mapping: %d\n",
error_code));
return false;
}
}
if ((mode == shadow_pages_transaction
&& platform == VER_PLATFORM_WIN32_NT)
|| mode == map_file)
{
if (SetFilePointer(fd, size, NULL, FILE_BEGIN) != size ||
!SetEndOfFile(fd))
{
error_code = GetLastError();
TRACE_MSG(("file::close: failed to change size of file to %ld: "
"%d\n", size, error_code));
return false;
}
}
if (fd != INVALID_HANDLE_VALUE) {
if (!CloseHandle(fd)) {
error_code = GetLastError();
TRACE_MSG(("file::close: failed to close file: %d\n", error_code));
return false;
}
}
if (mode == shadow_pages_transaction) {
if (!CloseHandle(log)) {
TRACE_MSG(("file::close: CloseHandle for log failed: %d\n",
GetLastError()));
}
if (!DeleteFile(log_name)) {
TRACE_MSG(("file::close: failed to remove log file: %d\n",
GetLastError()));
}
}
error_code = ok;
return true;
}
char* file::get_error_text(char* buf, size_t buf_size)
{
char* err_txt;
char errbuf[64];
switch (error_code) {
case ok:
err_txt = "no error";
break;
case file_size_not_aligned:
err_txt = "size of file is not aligned on segment boundary";
break;
case file_mapping_size_exceeded:
err_txt = "attempt to exeed the limit on mapped object size";
break;
case not_in_transaction:
err_txt = "operation is possible only inside transaction";
break;
case end_of_file:
err_txt = "unexpected end of file";
break;
default:
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error_code, 0,
buf, buf_size, NULL) > 0)
{
return buf;
} else {
sprintf(errbuf, "unknown error code %u", error_code);
err_txt = errbuf;
}
}
return strncpy(buf, err_txt, buf_size);
}
char* file::get_program_timestamp()
{
static char buf[32];
char program_name[MAX_PATH];
GetModuleFileName(NULL, program_name, sizeof program_name);
FILETIME mftime;
SYSTEMTIME mstime;
HANDLE program = CreateFile(program_name, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
if (program != INVALID_HANDLE_VALUE) {
if (GetFileTime(program, NULL, NULL, &mftime)
&& FileTimeToSystemTime(&mftime, &mstime))
{
sprintf(buf, "%02d.%02d.%04d %02d:%02d.%02d",
mstime.wDay, mstime.wMonth, mstime.wYear,
mstime.wHour, mstime.wMinute, mstime.wSecond);
}
CloseHandle(program);
}
assert(*buf != '\0');
return buf;
}
#else // Unix
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/resource.h>
#ifdef SA_SIGINFO
#if defined(__linux__) && !defined(__alpha__)
#undef SA_SIGINFO // Intel Linux doesn't set sa_addr in siginfo
#define SA_SIGINFO 0
#else
#include <sys/siginfo.h>
#endif
#else
#if defined(__linux__) // Linux 5.0 doesn't define sigcontext
#define sigcontext_struct sigcontext
#include <asm/sigcontext.h>
#endif
#define SA_SIGINFO 0
#endif
#ifndef SA_RESTART
#define SA_RESTART 0
#endif
#ifndef MAP_FAILED
#define MAP_FAILED (-1)
#endif
#ifndef MAP_FILE
#define MAP_FILE 0
#endif
#ifndef MAP_VARIABLE
#define MAP_VARIABLE 0
#endif
#ifndef MAP_NORESERVE
// This attrbute used in Solaris to avoid reservation space in swap file
#define MAP_NORESERVE 0
#endif
#define USE_WRITEV 1 // most modern Unix-s have this function
#ifdef USE_WRITEV
#include <sys/uio.h>
#endif
#define USE_FDATASYNC 0 // more efficient version of fsync()
#ifndef O_SYNC
#define O_SYNC O_FSYNC
#endif
// minimal number of pages appended to the file at each file extensin
#define FILE_EXTENSION_QUANTUM 256
//
// mlock(), munlock() functions can be used in transaction mode to
// provide buffering of shadow pages writes to transaction log.
// In Unix unly superuser can use this functions. Method file::open()
// will check if it is possible to use mlock() function.
//
#if !defined(__osf__) && !defined(_AIX) && !defined(__hpux__)
// These systems doesn't support mlock()
#define USE_MLOCK 1
#endif
#ifndef USE_MLOCK
#undef mlock
#undef munlock
#define mlock(p, s) -1
#define munlock(p, s) -1
#endif
#ifndef MAP_ANONYMOUS
int dev_zero = open("/dev/zero", O_RDONLY, 0);
#endif
#if defined(__sun__)
extern "C" int getpagesize(void);
#endif
bool file::handle_page_modification(void* addr)
{
for (file* fp = chain; fp != NULL; fp = fp->next) {
if (size_t((char*)addr - fp->base) < fp->mapped_size) {
return fp->prot != read_only && fp->create_shadow_page(true, addr);
}
}
return false;
}
//
// It will takes a lot of time and disk space to produce core file
// for program with huge memory mapped segment. This option allows you
// to automatically start debugger when some exception is happened in program.
//
#define CATCH_SIGNALS 1
#ifdef CATCH_SIGNALS
#include <signal.h>
#define WAIT_DEBUGGER_TIMEOUT 10
static void fatal_error_handler(int signo) {
char buf[64];
char* exe_name = getenv("_"); // defined by BASH
if (exe_name == NULL) {
exe_name = "a.out";
}
fprintf(stderr, "\
Program catch signal %d.\n\
Input path to executable file to debug or press Ctrl/C to terminate program:\n\
[%s] ",
signo, exe_name);
int pid = getpid();
if (fgets(buf, sizeof buf, stdin)) {
char pid_str[16];
int len = strlen(buf);
if (len > 1) {
buf[len-1] = '\0'; // truncate '\n'
exe_name = buf;
}
sprintf(pid_str, "%d", pid);
if (fork()) {
sleep(WAIT_DEBUGGER_TIMEOUT);
} else {
execlp("gdb", "gdb", "-q", "-s", exe_name, exe_name, pid_str, 0);
}
} else {
kill(pid, SIGKILL);
}
}
#endif
inline bool file::flush_log_buffer()
{
#if USE_FDATASYNC
if (fdatasync(log) != ok) {
#else
if (fsync(log) != ok) {
#endif
error_code = errno;
TRACE_MSG(("file::flush_log_buffer: fsync failed: %s\n",
strerror(error_code)));
return false;
}
for (int i = n_locked_pages; --i >= 0;) {
if (munlock(locked_page[i], page_size) != ok) {
error_code = errno;
TRACE_MSG(("file::flush_log_buffer: munlock %p failed: %s\n",
locked_page[i], strerror(error_code)));
return false;
}
}
n_locked_pages = 0;
return true;
}
bool file::create_shadow_page(int, void* addr)
{
char* page = (char*)(long(addr) & ~(page_size-1));
if (mprotect(page, page_size, PROT_READ|PROT_WRITE) != ok) {
TRACE_MSG(("file::create_shadow_page: mprotect failed for %p: %s\n",
page, strerror(errno)));
return false;
}
#if USE_WRITEV
iovec iov[2];
long offs = page - base;
iov[0].iov_base = (char*)&offs;
iov[0].iov_len = sizeof offs;
iov[1].iov_base = page;
iov[1].iov_len = page_size;
if ((size_t)writev(log, iov, 2) != sizeof(long) + page_size) {
TRACE_MSG(("file::create_shadow_page: writev failed: %s\n",
strerror(errno)));
return false;
}
#else
*(long*)log_buffer = page - base;
memcpy(log_buffer + sizeof(long), page, page_size);
size_t trans_size = page_size + sizeof(long);
if (write(log, log_buffer, trans_size) != trans_size) {
TRACE_MSG(("file::create_shadow_page: failed to write log file: %s\n",
strerror(errno)));
return false;
}
#endif
if (max_locked_pages != 0) {
if (n_locked_pages >= max_locked_pages) {
return flush_log_buffer();
}
if (mlock(page, page_size) != ok) {
TRACE_MSG(("file::create_shadow_page: mlock %p failed: %s\n",
page, strerror(errno)));
if (n_locked_pages != 0) {
max_locked_pages = n_locked_pages;
}
return flush_log_buffer();
}
locked_page[n_locked_pages++] = page;
}
return true;
}
#if defined(__linux__) && !defined(__alpha__)
static void sigsegv_handler(int, struct sigcontext scp)
#elif defined(_AIX) || defined(__FreeBSD__)
static void sigsegv_handler(int, int, struct sigcontext *scp)
#else
static void sigsegv_handler(int, siginfo_t *info)
#endif
{
char* fault_addr;
#if defined(__linux__) && !defined(__alpha__)
fault_addr = (char*)scp.cr2;
#elif defined(_AIX)
fault_addr = (char*)scp->sc_jmpbuf.jmp_context.o_vaddr;
#elif defined(__FreeBSD__)
fault_addr = (char*)scp->sc_err;
#else
fault_addr = (char*)info->si_addr;
#endif
if (!file::handle_page_modification(fault_addr)) {
kill(getpid(), SIGABRT);
}
}
file::file(const char* name, size_t max_file_size, size_t max_locked_pages)
{
// reserver one char for backup/log file names construction
set_file_name(name);
#ifndef USE_MLOCK
max_locked_pages = 0;
#else
if (max_locked_pages != 0) {
// Check if we can use mlock()
if (mlock((char*)this, page_size) != ok) {
max_locked_pages = 0;
TRACE_MSG(("file::open: mlock test failed: %s\n",
strerror(errno)));
} else {
munlock((char*)this, page_size);
}
}
#endif
allocation_granularity = page_size = getpagesize();
struct rlimit mem_limit;
if (getrlimit(RLIMIT_DATA, &mem_limit) == ok) {
if (max_file_size > size_t(mem_limit.rlim_cur)) {
max_file_size = mem_limit.rlim_cur & ~(allocation_granularity-1);
TRACE_MSG(("file::file: set max_file_size to %ld bytes\n",
max_file_size));
}
} else {
TRACE_MSG(("file::file: getrlimit failed: %s\n", strerror(errno)));
}
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];
file_extension_granularity = FILE_EXTENSION_QUANTUM*page_size;
static struct sigaction sigact;
sigact.sa_handler = (void(*)(int))sigsegv_handler;
sigact.sa_flags = SA_RESTART|SA_SIGINFO;
sigaction(SIGSEGV, &sigact, NULL);
#if defined(__FreeBSD__)
sigaction(SIGBUS, &sigact, NULL);
#endif
#ifdef CATCH_SIGNALS
sigact.sa_flags = 0;
sigact.sa_handler = fatal_error_handler;
#if !defined(__FreeBSD__)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -