zone_table_segment_mapped.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. // Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <datasrc/memory/zone_table_segment_mapped.h>
  15. #include <memory>
  16. using namespace isc::data;
  17. using namespace isc::dns;
  18. using namespace isc::util;
  19. namespace isc {
  20. namespace datasrc {
  21. namespace memory {
  22. namespace { // unnamed namespace
  23. // The name with which the zone table checksum is associated in the segment.
  24. const char* const ZONE_TABLE_CHECKSUM_NAME = "zone_table_checksum";
  25. // The name with which the zone table header is associated in the segment.
  26. const char* const ZONE_TABLE_HEADER_NAME = "zone_table_header";
  27. } // end of unnamed namespace
  28. ZoneTableSegmentMapped::ZoneTableSegmentMapped(const RRClass& rrclass) :
  29. ZoneTableSegment(rrclass),
  30. impl_type_("mapped"),
  31. rrclass_(rrclass),
  32. cached_header_(NULL)
  33. {
  34. }
  35. ZoneTableSegmentMapped::~ZoneTableSegmentMapped() {
  36. sync();
  37. }
  38. const std::string&
  39. ZoneTableSegmentMapped::getImplType() const {
  40. return (impl_type_);
  41. }
  42. bool
  43. ZoneTableSegmentMapped::processChecksum(MemorySegmentMapped& segment,
  44. bool create,
  45. std::string& error_msg)
  46. {
  47. const MemorySegment::NamedAddressResult result =
  48. segment.getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  49. if (result.first) {
  50. if (create) {
  51. // There must be no previously saved checksum.
  52. error_msg = "There is already a saved checksum in the segment "
  53. "opened in create mode";
  54. return (false);
  55. } else {
  56. // The segment was already shrunk when it was last
  57. // closed. Check that its checksum is consistent.
  58. assert(result.second);
  59. size_t* checksum = static_cast<size_t*>(result.second);
  60. const size_t saved_checksum = *checksum;
  61. // First, clear the checksum so that getCheckSum() returns a
  62. // consistent value.
  63. *checksum = 0;
  64. const size_t new_checksum = segment.getCheckSum();
  65. if (saved_checksum != new_checksum) {
  66. error_msg = "Saved checksum doesn't match segment data";
  67. return (false);
  68. }
  69. }
  70. } else {
  71. // Allocate space for a checksum (which is saved during close).
  72. void* checksum = NULL;
  73. while (!checksum) {
  74. try {
  75. checksum = segment.allocate(sizeof(size_t));
  76. } catch (const MemorySegmentGrown&) {
  77. // Do nothing and try again.
  78. }
  79. }
  80. *static_cast<size_t*>(checksum) = 0;
  81. const bool grew = segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME,
  82. checksum);
  83. assert(!grew);
  84. }
  85. return (true);
  86. }
  87. bool
  88. ZoneTableSegmentMapped::processHeader(MemorySegmentMapped& segment,
  89. bool create,
  90. std::string& error_msg)
  91. {
  92. const MemorySegment::NamedAddressResult result =
  93. segment.getNamedAddress(ZONE_TABLE_HEADER_NAME);
  94. if (result.first) {
  95. if (create) {
  96. // There must be no previously saved checksum.
  97. error_msg = "There is already a saved ZoneTableHeader in the "
  98. "segment opened in create mode";
  99. return (false);
  100. } else {
  101. assert(result.second);
  102. }
  103. } else {
  104. void* ptr = NULL;
  105. while (!ptr) {
  106. try {
  107. ptr = segment.allocate(sizeof(ZoneTableHeader));
  108. } catch (const MemorySegmentGrown&) {
  109. // Do nothing and try again.
  110. }
  111. }
  112. try {
  113. ZoneTableHeader* new_header = new(ptr)
  114. ZoneTableHeader(ZoneTable::create(segment, rrclass_));
  115. const bool grew = segment.setNamedAddress(ZONE_TABLE_HEADER_NAME,
  116. new_header);
  117. assert(!grew);
  118. } catch (const MemorySegmentGrown&) {
  119. // This is extremely unlikely and we just throw a fatal
  120. // exception here without attempting to recover.
  121. throw std::bad_alloc();
  122. }
  123. }
  124. return (true);
  125. }
  126. MemorySegmentMapped*
  127. ZoneTableSegmentMapped::openReadWrite(const std::string& filename,
  128. bool create)
  129. {
  130. const MemorySegmentMapped::OpenMode mode = create ?
  131. MemorySegmentMapped::CREATE_ONLY :
  132. MemorySegmentMapped::OPEN_OR_CREATE;
  133. // In case there is a problem, we throw. We want the segment to be
  134. // automatically destroyed then.
  135. std::auto_ptr<MemorySegmentMapped> segment
  136. (new MemorySegmentMapped(filename, mode));
  137. std::string error_msg;
  138. if ((!processChecksum(*segment, create, error_msg)) ||
  139. (!processHeader(*segment, create, error_msg))) {
  140. if (mem_sgmt_) {
  141. isc_throw(ResetFailed,
  142. "Error in resetting zone table segment to use "
  143. << filename << ": " << error_msg);
  144. } else {
  145. isc_throw(ResetFailedAndSegmentCleared,
  146. "Error in resetting zone table segment to use "
  147. << filename << ": " << error_msg);
  148. }
  149. }
  150. return (segment.release());
  151. }
  152. MemorySegmentMapped*
  153. ZoneTableSegmentMapped::openReadOnly(const std::string& filename) {
  154. // In case the checksum or table header is missing, we throw. We
  155. // want the segment to be automatically destroyed then.
  156. std::auto_ptr<MemorySegmentMapped> segment
  157. (new MemorySegmentMapped(filename));
  158. // There must be a previously saved checksum.
  159. MemorySegment::NamedAddressResult result =
  160. segment->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  161. if (!result.first) {
  162. const std::string error_msg =
  163. "There is no previously saved checksum in a "
  164. "mapped segment opened in read-only mode";
  165. if (mem_sgmt_) {
  166. isc_throw(ResetFailed,
  167. "Error in resetting zone table segment to use "
  168. << filename << ": " << error_msg);
  169. } else {
  170. isc_throw(ResetFailedAndSegmentCleared,
  171. "Error in resetting zone table segment to use "
  172. << filename << ": " << error_msg);
  173. }
  174. }
  175. // We can't verify the checksum here as we can't set the checksum to
  176. // 0 for checksum calculation in a read-only segment. So we continue
  177. // without verifying the checksum.
  178. // There must be a previously saved ZoneTableHeader.
  179. result = segment->getNamedAddress(ZONE_TABLE_HEADER_NAME);
  180. if (result.first) {
  181. assert(result.second);
  182. } else {
  183. const std::string error_msg =
  184. "There is no previously saved ZoneTableHeader in a "
  185. "mapped segment opened in read-only mode.";
  186. if (mem_sgmt_) {
  187. isc_throw(ResetFailed,
  188. "Error in resetting zone table segment to use "
  189. << filename << ": " << error_msg);
  190. } else {
  191. isc_throw(ResetFailedAndSegmentCleared,
  192. "Error in resetting zone table segment to use "
  193. << filename << ": " << error_msg);
  194. }
  195. }
  196. return (segment.release());
  197. }
  198. void
  199. ZoneTableSegmentMapped::reset(MemorySegmentOpenMode mode,
  200. isc::data::ConstElementPtr params)
  201. {
  202. if (!params || params->getType() != Element::map) {
  203. isc_throw(isc::InvalidParameter,
  204. "Configuration does not contain a map");
  205. }
  206. if (!params->contains("mapped-file")) {
  207. isc_throw(isc::InvalidParameter,
  208. "Configuration does not contain a \"mapped-file\" key");
  209. }
  210. ConstElementPtr mapped_file = params->get("mapped-file");
  211. if ((!mapped_file) || (mapped_file->getType() != Element::string)) {
  212. isc_throw(isc::InvalidParameter,
  213. "Value of \"mapped-file\" is not a string");
  214. }
  215. const std::string filename = mapped_file->stringValue();
  216. if (mem_sgmt_ && (filename == current_filename_)) {
  217. // This reset() is an attempt to re-open the currently open
  218. // mapped file. We cannot do this in many mode combinations
  219. // unless we close the existing mapped file. So just close it.
  220. clear();
  221. } else {
  222. sync();
  223. }
  224. // In case current_filename_ below fails, we want the segment to be
  225. // automatically destroyed.
  226. std::auto_ptr<MemorySegmentMapped> segment;
  227. switch (mode) {
  228. case CREATE:
  229. segment.reset(openReadWrite(filename, true));
  230. break;
  231. case READ_WRITE:
  232. segment.reset(openReadWrite(filename, false));
  233. break;
  234. case READ_ONLY:
  235. segment.reset(openReadOnly(filename));
  236. break;
  237. default:
  238. isc_throw(isc::InvalidOperation,
  239. "Invalid MemorySegmentOpenMode passed to reset()");
  240. }
  241. current_filename_ = filename;
  242. current_mode_ = mode;
  243. mem_sgmt_.reset(segment.release());
  244. // Given what we setup above, resetHeader() must not throw at this
  245. // point. If it does, all bets are off.
  246. resetHeader();
  247. }
  248. void
  249. ZoneTableSegmentMapped::sync() {
  250. // Synchronize checksum, etc.
  251. if (mem_sgmt_ && isWritable()) {
  252. // If there is a previously opened segment, and it was opened in
  253. // read-write mode, update its checksum.
  254. mem_sgmt_->shrinkToFit();
  255. const MemorySegment::NamedAddressResult result =
  256. mem_sgmt_->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  257. assert(result.first);
  258. assert(result.second);
  259. size_t* checksum = static_cast<size_t*>(result.second);
  260. // First, clear the checksum so that getCheckSum() returns a
  261. // consistent value.
  262. *checksum = 0;
  263. const size_t new_checksum = mem_sgmt_->getCheckSum();
  264. // Now, update it into place.
  265. *checksum = new_checksum;
  266. }
  267. }
  268. void
  269. ZoneTableSegmentMapped::clear() {
  270. if (mem_sgmt_) {
  271. sync();
  272. mem_sgmt_.reset();
  273. }
  274. }
  275. void
  276. ZoneTableSegmentMapped::resetHeader() {
  277. // We cannot perform resetHeader() lazily during getHeader() as
  278. // getHeader() has to work on const objects too. So we do it here
  279. // now.
  280. if (!isUsable()) {
  281. isc_throw(isc::InvalidOperation,
  282. "resetHeader() called without calling reset() first");
  283. }
  284. const MemorySegment::NamedAddressResult result =
  285. mem_sgmt_->getNamedAddress(ZONE_TABLE_HEADER_NAME);
  286. if (!result.first) {
  287. isc_throw(isc::Unexpected,
  288. "Unable to look up the address of the table header in "
  289. "getHeader()");
  290. }
  291. cached_header_ = static_cast<ZoneTableHeader*>(result.second);
  292. }
  293. template<typename T>
  294. T*
  295. ZoneTableSegmentMapped::getHeaderHelper() const {
  296. if (!isUsable()) {
  297. isc_throw(isc::InvalidOperation,
  298. "getHeader() called without calling reset() first");
  299. }
  300. assert(cached_header_);
  301. return (cached_header_);
  302. }
  303. ZoneTableHeader&
  304. ZoneTableSegmentMapped::getHeader() {
  305. return (*getHeaderHelper<ZoneTableHeader>());
  306. }
  307. const ZoneTableHeader&
  308. ZoneTableSegmentMapped::getHeader() const {
  309. return (*getHeaderHelper<const ZoneTableHeader>());
  310. }
  311. MemorySegment&
  312. ZoneTableSegmentMapped::getMemorySegment() {
  313. if (!isUsable()) {
  314. isc_throw(isc::InvalidOperation,
  315. "getMemorySegment() called without calling reset() first");
  316. }
  317. return (*mem_sgmt_);
  318. }
  319. bool
  320. ZoneTableSegmentMapped::isUsable() const {
  321. // If mem_sgmt_ is not empty, then it is usable.
  322. return (mem_sgmt_);
  323. }
  324. bool
  325. ZoneTableSegmentMapped::isWritable() const {
  326. if (!isUsable()) {
  327. // If reset() was never performed for this segment, or if the
  328. // most recent reset() had failed, then the segment is not
  329. // writable.
  330. return (false);
  331. }
  332. return ((current_mode_ == CREATE) || (current_mode_ == READ_WRITE));
  333. }
  334. } // namespace memory
  335. } // namespace datasrc
  336. } // namespace isc