📄 file.cxx
字号:
sigaction(SIGBUS, &sigact, NULL);
#endif
sigaction(SIGILL, &sigact, NULL);
sigaction(SIGABRT, &sigact, NULL);
#endif
error_code = ok;
}
file::~file()
{
delete[] name;
delete[] log_name;
delete[] tmp_name;
delete[] sav_name;
delete[] log_buffer;
delete[] locked_page;
}
char* file::get_error_text(char* buf, size_t buf_size)
{
char* err_txt;
switch (error_code) {
case ok:
err_txt = "no error";
break;
case file_size_not_aligned:
err_txt = "size of file is not aligned on page 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:
err_txt = strerror(error_code);
}
return strncpy(buf, err_txt, buf_size);
}
inline bool file::recover_file()
{
ssize_t rc, trans_size = page_size + sizeof(long);
int n_recovered_pages = 0;
TRACE_MSG(("file::recover_file: recover data file from transaction log\n"));
while ((rc = read(log, log_buffer, trans_size)) == trans_size) {
memcpy(base + *(long*)log_buffer, log_buffer+sizeof(long), page_size);
n_recovered_pages += 1;
}
if (rc != 0) {
if (rc < 0) {
error_code = errno;
TRACE_MSG(("file::recover: log read failed: %s\n",
strerror(error_code)));
} else {
error_code = end_of_file;
TRACE_MSG(("file::recover: expected end of log\n"));
}
return false;
}
allocated_size = 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;
}
bool file::open(open_mode mode, access_prot prot)
{
assert(name != NULL);
if (prot == read_only) {
mode = map_file;
}
this->mode = mode;
this->prot = prot;
if (mode == shadow_pages_transaction) {
fd = ::open(name, O_RDWR|O_CREAT|O_SYNC, 0777);
if (fd < 0) {
error_code = errno;
TRACE_MSG(("file::open: failed to open file '%s': %s\n",
name, strerror(error_code)));
return false;
}
file_header hdr;
hdr.base_address = NULL;
hdr.file_size = 0;
read(fd, &hdr, sizeof hdr);
base = (char*)hdr.base_address;
allocated_size = size = ALIGN(hdr.file_size, page_size);
mapped_size = size > max_file_size ? size : max_file_size;
void* p = mmap(base, mapped_size, PROT_READ|PROT_WRITE,
MAP_VARIABLE|MAP_SHARED|MAP_FILE, fd, 0);
if (p == (char*)MAP_FAILED) {
error_code = errno;
TRACE_MSG(("file::open: mmap failed: base=%p, size=%ld: %s\n",
base, mapped_size, strerror(error_code)));
::close(fd);
return false;
}
base = (char*)p;
n_locked_pages = 0;
int log_flags = O_RDWR;
if (max_locked_pages == 0) {
log_flags |= O_SYNC;
}
log = ::open(log_name, log_flags, 0);
if (log < 0) {
if (errno != ENOENT ||
(log = ::open(log_name, log_flags|O_CREAT, 0777)) < 0)
{
error_code = errno;
TRACE_MSG(("file::open: failed to open log file '%s': %s\n",
log_name, strerror(error_code)));
munmap(base, mapped_size);
::close(fd);
return false;
}
if (size != 0 && mprotect(base, size, PROT_READ) != ok) {
error_code = errno;
TRACE_MSG(("file::open: mprotect failed: address=%p, size=%ld:"
" %s\n", base, size, strerror(error_code)));
munmap(base, mapped_size);
::close(fd);
::close(log);
return false;
}
next = chain;
chain = this;
} else {
next = chain;
chain = this;
recover_file();
if (!commit()) {
chain = next;
munmap(base, mapped_size);
::close(fd);
::close(log);
return false;
}
}
} else { // mapping file in non-transaction mode
int open_mode = O_RDONLY;
if (prot != read_only) {
if (mode == map_file) {
open_mode = O_RDWR|O_CREAT;
} else if (mode == copy_on_write_map) {
open_mode = O_RDWR;
}
} else {
open_mode = O_RDWR; // to make it possible to change memory object protection
}
fd = ::open(name, open_mode, 0777);
base = NULL;
int d;
if (fd >= 0) {
size_t rc = read(fd, &base, sizeof base);
if (rc != 0 && rc != sizeof base) {
error_code = errno;
TRACE_MSG(("file::open: failed to read from file '%s': %s\n",
name, strerror(error_code)));
::close(fd);
return false;
}
size = ALIGN(lseek(fd, 0, SEEK_END), allocation_granularity);
TRACE_MSG(("file::open: file '%s' exists: size=%ld, base=%p\n",
name, size, base));
} else {
error_code = errno;
TRACE_MSG(("file::open: can't open file '%s', %s\n",
name, strerror(error_code)));
if (error_code != ENOENT || mode == map_file) {
return false;
}
if (prot == read_only) {
TRACE_MSG(("file::open: failed to open storage in read_only "
"mode because file '%s' doesn't exist\n", name));
return false;
}
size = 0;
}
mapped_size = (prot == read_only || size > max_file_size)
? size : max_file_size;
allocated_size = size;
int mmap_attr = (mode == map_file)
? MAP_VARIABLE|MAP_SHARED : MAP_VARIABLE|MAP_PRIVATE;
if (fd >= 0 && mode != load_in_memory) {
mmap_attr |= MAP_FILE;
d = fd;
} else {
#ifndef MAP_ANONYMOUS
d = dev_zero;
#else
d = -1;
mmap_attr |= MAP_ANONYMOUS;
#endif
}
int mmap_prot =
(prot == read_only) ? PROT_READ : PROT_READ|PROT_WRITE;
void* p = mmap(base, mapped_size, mmap_prot, mmap_attr, d, 0);
if (p == (void*)MAP_FAILED) {
error_code = errno;
TRACE_MSG(("file::open: mmap to %p failed: %s\n",
base, strerror(error_code)));
if (fd >= 0) {
::close(fd);
}
return false;
}
TRACE_MSG(("file::open: map file to address %p\n", p));
base = (char*)p;
if (fd >= 0 && mode == load_in_memory) {
//
// Read file to memory
//
lseek(fd, 0, SEEK_SET);
if ((size_t)read(fd, base, size) != size) {
error_code = errno;
TRACE_MSG(("file::open: failed to read file in memory: %s\n",
strerror(error_code)));
munmap(base, mapped_size);
::close(fd);
return false;
}
::close(fd); // file is nor more needed
fd = -1;
}
}
error_code = ok;
return true;
}
bool file::set_size(size_t new_size)
{
new_size = ALIGN(new_size, allocation_granularity);
if (new_size > mapped_size) {
error_code = file_mapping_size_exceeded;
return false;
}
if (fd >= 0 && new_size > allocated_size) {
allocated_size = ALIGN(new_size, file_extension_granularity);
if (ftruncate(fd, allocated_size) != ok) {
error_code = errno;
return false;
}
}
error_code = ok;
size = new_size;
return true;
}
bool file::set_protection(access_prot prot)
{
if (mprotect(base, mapped_size,
prot == read_only ? PROT_READ : PROT_READ|PROT_WRITE) != ok)
{
error_code = errno;
return false;
} else {
error_code = ok;
this->prot = prot;
return true;
}
}
bool file::commit()
{
if (mode != shadow_pages_transaction) {
error_code = not_in_transaction;
return false;
}
if (n_locked_pages != 0 && !flush_log_buffer()) {
return false;
}
if (size > 0 && msync(base, size, MS_SYNC) != ok) {
error_code = errno;
TRACE_MSG(("file::commit: msync failed for address %p size %ld: %s\n",
base, size, strerror(error_code)));
return false;
}
if (lseek(log, 0, SEEK_SET) != 0 ||
ftruncate(log, 0) != ok)
{
error_code = errno;
TRACE_MSG(("file::commit: failed to truncate log file: %s\n",
strerror(error_code)));
return false;
}
if (size > 0 && mprotect(base, size, PROT_READ) != ok) {
error_code = errno;
TRACE_MSG(("file::commit: mprotect failed for address %p size %ld: "
"%s\n", base, size, strerror(error_code)));
return false;
}
error_code = ok;
return true;
}
bool file::rollback()
{
if (mode != shadow_pages_transaction) {
error_code = not_in_transaction;
return false;
}
if (lseek(log, 0, SEEK_SET) != 0) {
error_code = errno;
TRACE_MSG(("file::rollback; failed to set position in log file: %s\n",
strerror(error_code)));
return false;
}
return recover_file();
}
bool file::flush()
{
if (prot == read_only) {
return true;
}
if (mode == shadow_pages_transaction) {
return commit();
} else if (mode == map_file) {
if (size > 0 && msync(base, size, MS_ASYNC) != ok) {
TRACE_MSG(("file::flush: failed to flush file: %s\n",
strerror(error_code)));
return false;
}
error_code = ok;
return true;
} else {
int new_fd = ::open(tmp_name, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (new_fd < 0) {
error_code = errno;
return false;
}
if ((size_t)write(new_fd, base, size) != size) {
error_code = errno;
::close(new_fd);
return false;
}
::close(new_fd);
if (rename(tmp_name, name) != ok) {
error_code = errno;
return false;
}
error_code = ok;
return true;
}
}
bool file::close()
{
if (base == NULL) {
if (fd >= 0 && ::close(fd) != ok) {
error_code = errno;
TRACE_MSG(("file::close: failed to close file: %s\n",
strerror(error_code)));
return false;
}
return true;
}
if (mode == shadow_pages_transaction) {
file *fp, **fpp = &chain;
while ((fp = *fpp) != this) {
fpp = &fp->next;
}
*fpp = fp->next;
if (n_locked_pages != 0 && !flush_log_buffer()) {
return false;
}
}
if (munmap(base, mapped_size) != ok) {
error_code = errno;
TRACE_MSG(("file::close: failed to unmap memory segment: %s\n",
strerror(error_code)));
return false;
}
if (fd >= 0) {
if (size != allocated_size) {
if (ftruncate(fd, size) != ok) {
error_code = errno;
TRACE_MSG(("file::close: failed to truncate file: %s\n",
strerror(error_code)));
return false;
}
}
if (::close(fd) != ok) {
error_code = errno;
TRACE_MSG(("file::close: failed to close file: %s\n",
strerror(error_code)));
return false;
}
}
if (mode == shadow_pages_transaction) {
if (::close(log) != ok) {
TRACE_MSG(("file::close: failed to close transaction log file: "
"%s\n", strerror(errno)));
}
if (unlink(log_name) != ok) {
TRACE_MSG(("file::close: failed to remove transaction log file: "
"%s\n", strerror(errno)));
}
}
error_code = ok;
return true;
}
char* file::get_program_timestamp()
{
static char buf[32];
if (program_compilation_time == NULL) {
char* image = getenv("_");
assert(image != NULL);
struct stat fs;
int rc = stat(image, &fs);
assert(rc == 0);
struct tm* fmtime = localtime(&fs.st_mtime);
sprintf(buf, "%02d.%02d.%04d %02d:%02d.%02d",
fmtime->tm_mday, fmtime->tm_mon, fmtime->tm_year+1900,
fmtime->tm_hour, fmtime->tm_min, fmtime->tm_sec);
return buf;
}
return program_compilation_time;
}
#endif // Unix
#if 0 && DEBUG_LEVEL >= DEBUG_CHECK
//
// Sometimes it is more convenient to catch SIGSEGV instead of SIGABRT.
// For example in Digital Unix debugger failed to unroll stack after
// assertion failure. And in Windows NT assertion failure will not cause
// invocation of debugger if program is not started from MSDEV. That is
// why this "strange" version of abort() was implemented.
//
#ifndef _DEBUG
extern "C"
void abort() { while(1) *(int*)0 = 0; /* do not return */ }
#endif
#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -