zone_table_segment_mapped.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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 <datasrc/memory/zone_table.h>
  16. #include <datasrc/memory/segment_object_holder.h>
  17. #include <memory>
  18. using namespace isc::data;
  19. using namespace isc::dns;
  20. using namespace isc::util;
  21. using isc::datasrc::memory::detail::SegmentObjectHolder;
  22. namespace isc {
  23. namespace datasrc {
  24. namespace memory {
  25. namespace { // unnamed namespace
  26. // The name with which the zone table checksum is associated in the segment.
  27. const char* const ZONE_TABLE_CHECKSUM_NAME = "zone_table_checksum";
  28. // The name with which the zone table header is associated in the segment.
  29. const char* const ZONE_TABLE_HEADER_NAME = "zone_table_header";
  30. } // end of unnamed namespace
  31. ZoneTableSegmentMapped::ZoneTableSegmentMapped(const RRClass& rrclass) :
  32. ZoneTableSegment(rrclass),
  33. impl_type_("mapped"),
  34. rrclass_(rrclass)
  35. {
  36. }
  37. ZoneTableSegmentMapped::~ZoneTableSegmentMapped() {
  38. sync();
  39. }
  40. const std::string&
  41. ZoneTableSegmentMapped::getImplType() const {
  42. return (impl_type_);
  43. }
  44. bool
  45. ZoneTableSegmentMapped::processChecksum(MemorySegmentMapped& segment,
  46. bool create, bool has_allocations,
  47. std::string& error_msg)
  48. {
  49. const MemorySegment::NamedAddressResult result =
  50. segment.getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  51. if (result.first) {
  52. if (create) {
  53. // There must be no previously saved checksum.
  54. error_msg = "There is already a saved checksum in the segment "
  55. "opened in create mode";
  56. return (false);
  57. } else {
  58. // The segment was already shrunk when it was last
  59. // closed. Check that its checksum is consistent.
  60. assert(result.second);
  61. size_t* checksum = static_cast<size_t*>(result.second);
  62. const size_t saved_checksum = *checksum;
  63. // First, clear the checksum so that getCheckSum() returns a
  64. // consistent value.
  65. *checksum = 0;
  66. const size_t new_checksum = segment.getCheckSum();
  67. if (saved_checksum != new_checksum) {
  68. error_msg = "Saved checksum doesn't match segment data";
  69. return (false);
  70. }
  71. }
  72. } else {
  73. if ((!create) && has_allocations) {
  74. // If we are resetting in READ_WRITE mode, and some memory
  75. // was already allocated but there is no checksum name, that
  76. // indicates that the segment is corrupted.
  77. error_msg = "Existing segment is missing a checksum name";
  78. return (false);
  79. }
  80. // Allocate space for a checksum (which is saved during close).
  81. void* checksum = NULL;
  82. while (!checksum) {
  83. try {
  84. checksum = segment.allocate(sizeof(size_t));
  85. } catch (const MemorySegmentGrown&) {
  86. // Do nothing and try again.
  87. }
  88. }
  89. *static_cast<size_t*>(checksum) = 0;
  90. segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME, checksum);
  91. }
  92. return (true);
  93. }
  94. bool
  95. ZoneTableSegmentMapped::processHeader(MemorySegmentMapped& segment,
  96. bool create, bool has_allocations,
  97. std::string& error_msg)
  98. {
  99. const MemorySegment::NamedAddressResult result =
  100. segment.getNamedAddress(ZONE_TABLE_HEADER_NAME);
  101. if (result.first) {
  102. if (create) {
  103. // There must be no previously saved header.
  104. error_msg = "There is already a saved ZoneTableHeader in the "
  105. "segment opened in create mode";
  106. return (false);
  107. } else {
  108. assert(result.second);
  109. }
  110. } else {
  111. if ((!create) && has_allocations) {
  112. // If we are resetting in READ_WRITE mode, and some memory
  113. // was already allocated but there is no header name, that
  114. // indicates that the segment is corrupted.
  115. error_msg = "Existing segment is missing a ZoneTableHeader name";
  116. return (false);
  117. }
  118. while (true) {
  119. try {
  120. SegmentObjectHolder<ZoneTable, int> zt_holder(segment, 0);
  121. zt_holder.set(ZoneTable::create(segment, rrclass_));
  122. void* ptr = segment.allocate(sizeof(ZoneTableHeader));
  123. ZoneTableHeader* new_header = new(ptr)
  124. ZoneTableHeader(zt_holder.release());
  125. segment.setNamedAddress(ZONE_TABLE_HEADER_NAME, new_header);
  126. break;
  127. } catch (const MemorySegmentGrown&) {}
  128. }
  129. }
  130. return (true);
  131. }
  132. MemorySegmentMapped*
  133. ZoneTableSegmentMapped::openReadWrite(const std::string& filename,
  134. bool create)
  135. {
  136. const MemorySegmentMapped::OpenMode mode = create ?
  137. MemorySegmentMapped::CREATE_ONLY :
  138. MemorySegmentMapped::OPEN_OR_CREATE;
  139. // In case there is a problem, we throw. We want the segment to be
  140. // automatically destroyed then.
  141. std::auto_ptr<MemorySegmentMapped> segment
  142. (new MemorySegmentMapped(filename, mode));
  143. // This flag is used inside processCheckSum() and processHeader(),
  144. // and must be initialized before we make any further allocations.
  145. const bool has_allocations = !segment->allMemoryDeallocated();
  146. std::string error_msg;
  147. if ((!processChecksum(*segment, create, has_allocations, error_msg)) ||
  148. (!processHeader(*segment, create, has_allocations, error_msg))) {
  149. if (mem_sgmt_) {
  150. isc_throw(ResetFailed,
  151. "Error in resetting zone table segment to use "
  152. << filename << ": " << error_msg);
  153. } else {
  154. isc_throw(ResetFailedAndSegmentCleared,
  155. "Error in resetting zone table segment to use "
  156. << filename << ": " << error_msg);
  157. }
  158. }
  159. return (segment.release());
  160. }
  161. MemorySegmentMapped*
  162. ZoneTableSegmentMapped::openReadOnly(const std::string& filename) {
  163. // In case the checksum or table header is missing, we throw. We
  164. // want the segment to be automatically destroyed then.
  165. std::auto_ptr<MemorySegmentMapped> segment
  166. (new MemorySegmentMapped(filename));
  167. // There must be a previously saved checksum.
  168. MemorySegment::NamedAddressResult result =
  169. segment->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  170. if (!result.first) {
  171. const std::string error_msg =
  172. "There is no previously saved checksum in a "
  173. "mapped segment opened in read-only mode";
  174. if (mem_sgmt_) {
  175. isc_throw(ResetFailed,
  176. "Error in resetting zone table segment to use "
  177. << filename << ": " << error_msg);
  178. } else {
  179. isc_throw(ResetFailedAndSegmentCleared,
  180. "Error in resetting zone table segment to use "
  181. << filename << ": " << error_msg);
  182. }
  183. }
  184. // We can't verify the checksum here as we can't set the checksum to
  185. // 0 for checksum calculation in a read-only segment. So we continue
  186. // without verifying the checksum.
  187. // There must be a previously saved ZoneTableHeader.
  188. result = segment->getNamedAddress(ZONE_TABLE_HEADER_NAME);
  189. if (result.first) {
  190. assert(result.second);
  191. } else {
  192. const std::string error_msg =
  193. "There is no previously saved ZoneTableHeader in a "
  194. "mapped segment opened in read-only mode.";
  195. if (mem_sgmt_) {
  196. isc_throw(ResetFailed,
  197. "Error in resetting zone table segment to use "
  198. << filename << ": " << error_msg);
  199. } else {
  200. isc_throw(ResetFailedAndSegmentCleared,
  201. "Error in resetting zone table segment to use "
  202. << filename << ": " << error_msg);
  203. }
  204. }
  205. return (segment.release());
  206. }
  207. void
  208. ZoneTableSegmentMapped::reset(MemorySegmentOpenMode mode,
  209. isc::data::ConstElementPtr params)
  210. {
  211. if (!params || params->getType() != Element::map) {
  212. isc_throw(isc::InvalidParameter,
  213. "Configuration does not contain a map");
  214. }
  215. if (!params->contains("mapped-file")) {
  216. isc_throw(isc::InvalidParameter,
  217. "Configuration does not contain a \"mapped-file\" key");
  218. }
  219. ConstElementPtr mapped_file = params->get("mapped-file");
  220. if ((!mapped_file) || (mapped_file->getType() != Element::string)) {
  221. isc_throw(isc::InvalidParameter,
  222. "Value of \"mapped-file\" is not a string");
  223. }
  224. const std::string filename = mapped_file->stringValue();
  225. if (mem_sgmt_ && (filename == current_filename_)) {
  226. // This reset() is an attempt to re-open the currently open
  227. // mapped file. We cannot do this in many mode combinations
  228. // unless we close the existing mapped file. So just close it.
  229. clear();
  230. } else {
  231. sync();
  232. }
  233. // In case current_filename_ below fails, we want the segment to be
  234. // automatically destroyed.
  235. std::auto_ptr<MemorySegmentMapped> segment;
  236. switch (mode) {
  237. case CREATE:
  238. segment.reset(openReadWrite(filename, true));
  239. break;
  240. case READ_WRITE:
  241. segment.reset(openReadWrite(filename, false));
  242. break;
  243. case READ_ONLY:
  244. segment.reset(openReadOnly(filename));
  245. break;
  246. default:
  247. isc_throw(isc::InvalidParameter,
  248. "Invalid MemorySegmentOpenMode passed to reset()");
  249. }
  250. current_filename_ = filename;
  251. current_mode_ = mode;
  252. mem_sgmt_.reset(segment.release());
  253. if (!isWritable()) {
  254. // Given what we setup above, the following must not throw at
  255. // this point. If it does, all bets are off.
  256. cached_ro_header_ = getHeaderHelper<ZoneTableHeader>(true);
  257. }
  258. }
  259. void
  260. ZoneTableSegmentMapped::sync() {
  261. // Synchronize checksum, etc.
  262. if (mem_sgmt_ && isWritable()) {
  263. // If there is a previously opened segment, and it was opened in
  264. // read-write mode, update its checksum.
  265. mem_sgmt_->shrinkToFit();
  266. const MemorySegment::NamedAddressResult result =
  267. mem_sgmt_->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  268. assert(result.first);
  269. assert(result.second);
  270. size_t* checksum = static_cast<size_t*>(result.second);
  271. // First, clear the checksum so that getCheckSum() returns a
  272. // consistent value.
  273. *checksum = 0;
  274. const size_t new_checksum = mem_sgmt_->getCheckSum();
  275. // Now, update it into place.
  276. *checksum = new_checksum;
  277. }
  278. }
  279. void
  280. ZoneTableSegmentMapped::clear() {
  281. if (mem_sgmt_) {
  282. sync();
  283. mem_sgmt_.reset();
  284. }
  285. }
  286. template<typename T>
  287. T*
  288. ZoneTableSegmentMapped::getHeaderHelper(bool initial) const {
  289. if (!isUsable()) {
  290. isc_throw(isc::InvalidOperation,
  291. "getHeader() called without calling reset() first");
  292. }
  293. if (!isWritable() && !initial) {
  294. // The header address would not have changed since reset() for
  295. // READ_ONLY segments.
  296. return (cached_ro_header_);
  297. }
  298. const MemorySegment::NamedAddressResult result =
  299. mem_sgmt_->getNamedAddress(ZONE_TABLE_HEADER_NAME);
  300. if (!result.first) {
  301. isc_throw(isc::Unexpected,
  302. "Unable to look up the address of the table header in "
  303. "getHeader()");
  304. }
  305. T* header = static_cast<ZoneTableHeader*>(result.second);
  306. assert(header);
  307. return (header);
  308. }
  309. ZoneTableHeader&
  310. ZoneTableSegmentMapped::getHeader() {
  311. return (*getHeaderHelper<ZoneTableHeader>(false));
  312. }
  313. const ZoneTableHeader&
  314. ZoneTableSegmentMapped::getHeader() const {
  315. return (*getHeaderHelper<const ZoneTableHeader>(false));
  316. }
  317. MemorySegment&
  318. ZoneTableSegmentMapped::getMemorySegment() {
  319. if (!isUsable()) {
  320. isc_throw(isc::InvalidOperation,
  321. "getMemorySegment() called without calling reset() first");
  322. }
  323. return (*mem_sgmt_);
  324. }
  325. bool
  326. ZoneTableSegmentMapped::isUsable() const {
  327. // If mem_sgmt_ is not empty, then it is usable.
  328. return (mem_sgmt_);
  329. }
  330. bool
  331. ZoneTableSegmentMapped::isWritable() const {
  332. if (!isUsable()) {
  333. // If reset() was never performed for this segment, or if the
  334. // most recent reset() had failed, or if the segment had been
  335. // cleared, then the segment is not writable.
  336. return (false);
  337. }
  338. return ((current_mode_ == CREATE) || (current_mode_ == READ_WRITE));
  339. }
  340. } // namespace memory
  341. } // namespace datasrc
  342. } // namespace isc