diff --git a/persistent-data/block.h b/persistent-data/block.h index d463083..c472d9d 100644 --- a/persistent-data/block.h +++ b/persistent-data/block.h @@ -85,6 +85,7 @@ namespace persistent_data { enum mode { READ_ONLY, READ_WRITE, + CREATE }; block_io(std::string const &path, block_address nr_blocks, mode m); diff --git a/persistent-data/block.tcc b/persistent-data/block.tcc index a7faae3..1571650 100644 --- a/persistent-data/block.tcc +++ b/persistent-data/block.tcc @@ -23,6 +23,8 @@ #include #include #include +#include +#include #include #include @@ -35,37 +37,102 @@ using namespace std; //---------------------------------------------------------------- +namespace { + using namespace std; + + int const DEFAULT_MODE = 0666; + int const OPEN_FLAGS = O_DIRECT | O_SYNC; + + // FIXME: introduce a new exception for this, or at least lift this + // to exception.h + void syscall_failed(char const *call) { + char buffer[128]; + char *msg = strerror_r(errno, buffer, sizeof(buffer)); + + ostringstream out; + out << "syscall '" << call << "' failed: " << msg; + throw runtime_error(out.str()); + } + + int open_file(string const &path, int flags) { + int fd = ::open(path.c_str(), OPEN_FLAGS | flags, DEFAULT_MODE); + if (fd < 0) + syscall_failed("open"); + + return fd; + } + + bool file_exists(string const &path) { + typename ::stat info; + + int r = ::stat(path.c_str(), &info); + if (r) { + if (errno == ENOENT) + return false; + + syscall_failed("stat"); + return false; // never get here + + } else + return S_ISREG(info.st_mode) || S_ISBLK(info.st_mode); + } + + int create_block_file(string const &path, off_t file_size) { + if (file_exists(path)) { + ostringstream out; + out << __FUNCTION__ << ": file '" << path << "' already exists"; + throw runtime_error(out.str()); + } + + int fd = open_file(path, O_CREAT | O_RDWR); + int r = ::fallocate(fd, 0, 0, file_size); + if (r < 0) + syscall_failed("fallocate"); + + return fd; + } + + int open_block_file(string const &path, off_t min_size, bool writeable) { + if (!file_exists(path)) { + ostringstream out; + out << __FUNCTION__ << ": file '" << path << "' doesn't exist"; + throw runtime_error(out.str()); + } + + return open_file(path, writeable ? O_RDWR : O_RDONLY); + } +}; + template block_io::block_io(std::string const &path, block_address nr_blocks, mode m) : nr_blocks_(nr_blocks), mode_(m) { - int fd_mode = O_DIRECT | O_SYNC; + off_t file_size = nr_blocks * BlockSize; switch (m) { case READ_ONLY: - fd_mode |= O_RDONLY; + fd_ = open_block_file(path, file_size, false); break; case READ_WRITE: - fd_mode |= O_RDWR; + fd_ = open_block_file(path, file_size, true); + break; + + case CREATE: + fd_ = create_block_file(path, file_size); break; default: throw runtime_error("unsupported mode"); - break; } - - // fd_ = ::open(path.c_str(), writeable ? (O_RDWR | O_CREAT) : O_RDONLY, 0666); - fd_ = ::open(path.c_str(), fd_mode, 0666); - if (fd_ < 0) - throw std::runtime_error("couldn't open file"); } template block_io::~block_io() { - ::close(fd_); + if (::close(fd_) < 0) + syscall_failed("close"); } template diff --git a/unit-tests/block_t.cc b/unit-tests/block_t.cc index 7a1d7c1..bce0988 100644 --- a/unit-tests/block_t.cc +++ b/unit-tests/block_t.cc @@ -29,7 +29,8 @@ namespace { unsigned const MAX_HELD_LOCKS = 16; block_manager<4096>::ptr create_bm(block_address nr = 1024) { - return block_manager<4096>::ptr(new block_manager<4096>("./test.data", nr, MAX_HELD_LOCKS, true)); + return block_manager<4096>::ptr( + new block_manager<4096>("./test.data", nr, MAX_HELD_LOCKS, block_io<>::READ_WRITE)); } template @@ -61,7 +62,8 @@ namespace { BOOST_AUTO_TEST_CASE(bad_path) { - BOOST_CHECK_THROW(bm4096("/bogus/bogus/bogus", 1234, 4), runtime_error); + BOOST_CHECK_THROW(bm4096("/bogus/bogus/bogus", 1234, 4, block_io<>::READ_WRITE), + runtime_error); } BOOST_AUTO_TEST_CASE(out_of_range_access) @@ -110,13 +112,13 @@ BOOST_AUTO_TEST_CASE(write_lock_zero_zeroes) BOOST_AUTO_TEST_CASE(different_block_sizes) { { - bm4096 bm("./test.data", 64, 1); + bm4096 bm("./test.data", 64, 1, block_io<>::READ_WRITE); bm4096::read_ref rr = bm.read_lock(0); BOOST_CHECK_EQUAL(sizeof(rr.data()), 4096); } { - block_manager<64 * 1024> bm("./test.data", 64, true); + block_manager<64 * 1024> bm("./test.data", 64, 1, block_io<64 * 1024>::READ_WRITE); block_manager<64 * 1024>::read_ref rr = bm.read_lock(0); BOOST_CHECK_EQUAL(sizeof(rr.data()), 64 * 1024); }