zone_table_segment_mapped.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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. rrclass_(rrclass),
  31. header_(NULL)
  32. {
  33. }
  34. bool
  35. ZoneTableSegmentMapped::processChecksum(MemorySegmentMapped& segment,
  36. bool create,
  37. std::string& error_msg)
  38. {
  39. MemorySegment::NamedAddressResult result =
  40. segment.getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  41. if (result.first) {
  42. if (create) {
  43. // There must be no previously saved checksum.
  44. error_msg = "There is already a saved checksum in the segment "
  45. "opened in create mode";
  46. return (false);
  47. } else {
  48. // The segment was already shrunk when it was last
  49. // closed. Check that its checksum is consistent.
  50. assert(result.second);
  51. uint32_t* checksum = static_cast<uint32_t*>(result.second);
  52. const uint32_t saved_checksum = *checksum;
  53. // First, clear the checksum so that getCheckSum() returns a
  54. // consistent value.
  55. *checksum = 0;
  56. const uint32_t new_checksum = segment.getCheckSum();
  57. if (saved_checksum != new_checksum) {
  58. error_msg = "Saved checksum doesn't match segment data";
  59. return (false);
  60. }
  61. }
  62. } else {
  63. // Allocate space for a checksum (which is saved during close).
  64. // First allocate a ZONE_TABLE_CHECKSUM_NAME, so that we can set
  65. // it without growing the segment (and changing the checksum's
  66. // address).
  67. segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME, NULL);
  68. void* checksum = NULL;
  69. while (!checksum) {
  70. try {
  71. checksum = segment.allocate(sizeof(uint32_t));
  72. } catch (const MemorySegmentGrown&) {
  73. // Do nothing and try again.
  74. }
  75. }
  76. *static_cast<uint32_t*>(checksum) = 0;
  77. const bool grew = segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME,
  78. checksum);
  79. if (grew) {
  80. // If the segment grew here, we have a problem as the
  81. // checksum address may no longer be valid. In this case, we
  82. // cannot recover. This case is extremely unlikely as we
  83. // reserved memory for the ZONE_TABLE_CHECKSUM_NAME
  84. // above. It indicates a very restrictive MemorySegment
  85. // which we should not use.
  86. error_msg = "Segment grew unexpectedly in setNamedAddress()";
  87. return (false);
  88. }
  89. }
  90. return (true);
  91. }
  92. bool
  93. ZoneTableSegmentMapped::processHeader(MemorySegmentMapped& segment,
  94. bool create,
  95. std::string& error_msg)
  96. {
  97. MemorySegment::NamedAddressResult result =
  98. segment.getNamedAddress(ZONE_TABLE_HEADER_NAME);
  99. if (result.first) {
  100. if (create) {
  101. // There must be no previously saved checksum.
  102. error_msg = "There is already a saved ZoneTableHeader in the "
  103. "segment opened in create mode";
  104. return (false);
  105. } else {
  106. assert(result.second);
  107. header_ = static_cast<ZoneTableHeader*>(result.second);
  108. }
  109. } else {
  110. // First allocate a ZONE_TABLE_HEADER_NAME, so that we can set
  111. // it without growing the segment (and changing the header's
  112. // address).
  113. segment.setNamedAddress(ZONE_TABLE_HEADER_NAME, NULL);
  114. void* ptr = NULL;
  115. while (!ptr) {
  116. try {
  117. ptr = segment.allocate(sizeof(ZoneTableHeader));
  118. } catch (const MemorySegmentGrown&) {
  119. // Do nothing and try again.
  120. }
  121. }
  122. ZoneTableHeader* new_header = new(ptr)
  123. ZoneTableHeader(ZoneTable::create(segment, rrclass_));
  124. const bool grew = segment.setNamedAddress(ZONE_TABLE_HEADER_NAME,
  125. new_header);
  126. if (grew) {
  127. // If the segment grew here, we have a problem as the table
  128. // header address may no longer be valid. In this case, we
  129. // cannot recover. This case is extremely unlikely as we
  130. // reserved memory for the ZONE_TABLE_HEADER_NAME above. It
  131. // indicates a very restrictive MemorySegment which we
  132. // should not use.
  133. error_msg = "Segment grew unexpectedly in setNamedAddress()";
  134. return (false);
  135. }
  136. header_ = new_header;
  137. }
  138. return (true);
  139. }
  140. void
  141. ZoneTableSegmentMapped::openReadWrite(const std::string& filename,
  142. bool create)
  143. {
  144. const MemorySegmentMapped::OpenMode mode = create ?
  145. MemorySegmentMapped::CREATE_ONLY :
  146. MemorySegmentMapped::OPEN_OR_CREATE;
  147. // In case there is a problem, we throw. We want the segment to be
  148. // automatically destroyed then.
  149. std::auto_ptr<MemorySegmentMapped> segment
  150. (new MemorySegmentMapped(filename, mode));
  151. std::string error_msg;
  152. if (!processChecksum(*segment, create, error_msg)) {
  153. if (mem_sgmt_) {
  154. isc_throw(ResetFailed,
  155. "Error in resetting zone table segment to use "
  156. << filename << ": " << error_msg);
  157. } else {
  158. isc_throw(ResetFailedAndSegmentCleared,
  159. "Error in resetting zone table segment to use "
  160. << filename << ": " << error_msg);
  161. }
  162. }
  163. if (!processHeader(*segment, create, error_msg)) {
  164. if (mem_sgmt_) {
  165. isc_throw(ResetFailed,
  166. "Error in resetting zone table segment to use "
  167. << filename << ": " << error_msg);
  168. } else {
  169. isc_throw(ResetFailedAndSegmentCleared,
  170. "Error in resetting zone table segment to use "
  171. << filename << ": " << error_msg);
  172. }
  173. }
  174. mem_sgmt_.reset(segment.release());
  175. }
  176. void
  177. ZoneTableSegmentMapped::openReadOnly(const std::string& filename) {
  178. // In case the checksum or table header is missing, we throw. We
  179. // want the segment to be automatically destroyed then.
  180. std::auto_ptr<MemorySegmentMapped> segment
  181. (new MemorySegmentMapped(filename));
  182. // There must be a previously saved checksum.
  183. MemorySegment::NamedAddressResult result =
  184. segment->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  185. if (!result.first) {
  186. const std::string error_msg =
  187. "There is no previously saved checksum in a "
  188. "mapped segment opened in read-only mode";
  189. if (mem_sgmt_) {
  190. isc_throw(ResetFailed,
  191. "Error in resetting zone table segment to use "
  192. << filename << ": " << error_msg);
  193. } else {
  194. isc_throw(ResetFailedAndSegmentCleared,
  195. "Error in resetting zone table segment to use "
  196. << filename << ": " << error_msg);
  197. }
  198. }
  199. // We can't verify the checksum here as we can't set the checksum to
  200. // 0 for checksum calculation in a read-only segment. So we continue
  201. // without verifying the checksum.
  202. // There must be a previously saved ZoneTableHeader.
  203. result = segment->getNamedAddress(ZONE_TABLE_HEADER_NAME);
  204. if (result.first) {
  205. assert(result.second);
  206. header_ = static_cast<ZoneTableHeader*>(result.second);
  207. } else {
  208. const std::string error_msg =
  209. "There is no previously saved ZoneTableHeader in a "
  210. "mapped segment opened in read-only mode.";
  211. if (mem_sgmt_) {
  212. isc_throw(ResetFailed,
  213. "Error in resetting zone table segment to use "
  214. << filename << ": " << error_msg);
  215. } else {
  216. isc_throw(ResetFailedAndSegmentCleared,
  217. "Error in resetting zone table segment to use "
  218. << filename << ": " << error_msg);
  219. }
  220. }
  221. mem_sgmt_.reset(segment.release());
  222. }
  223. void
  224. ZoneTableSegmentMapped::reset(MemorySegmentOpenMode mode,
  225. isc::data::ConstElementPtr params)
  226. {
  227. if (!params || params->getType() != Element::map) {
  228. isc_throw(isc::InvalidParameter,
  229. "Configuration does not contain a map");
  230. }
  231. if (!params->contains("mapped-file")) {
  232. isc_throw(isc::InvalidParameter,
  233. "Configuration does not contain a \"mapped-file\" key");
  234. }
  235. ConstElementPtr mapped_file = params->get("mapped-file");
  236. if ((!mapped_file) || (mapped_file->getType() != Element::string)) {
  237. isc_throw(isc::InvalidParameter,
  238. "Value of \"mapped-file\" is not a string");
  239. }
  240. const std::string filename = mapped_file->stringValue();
  241. if (mem_sgmt_ && (filename == current_filename_)) {
  242. // This reset() is an attempt to re-open the currently open
  243. // mapped file. We cannot do this in many mode combinations
  244. // unless we close the existing mapped file. So just close it.
  245. clear();
  246. }
  247. switch (mode) {
  248. case CREATE:
  249. openReadWrite(filename, true);
  250. break;
  251. case READ_WRITE:
  252. openReadWrite(filename, false);
  253. break;
  254. case READ_ONLY:
  255. openReadOnly(filename);
  256. }
  257. current_mode_ = mode;
  258. current_filename_ = filename;
  259. }
  260. void
  261. ZoneTableSegmentMapped::clear()
  262. {
  263. if (mem_sgmt_) {
  264. if (isWritable()) {
  265. // If there is a previously opened segment, and it was
  266. // opened in read-write mode, update its checksum.
  267. mem_sgmt_->shrinkToFit();
  268. const MemorySegment::NamedAddressResult result =
  269. mem_sgmt_->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
  270. assert(result.first);
  271. assert(result.second);
  272. uint32_t* checksum = static_cast<uint32_t*>(result.second);
  273. // First, clear the checksum so that getCheckSum() returns
  274. // a consistent value.
  275. *checksum = 0;
  276. const uint32_t new_checksum = mem_sgmt_->getCheckSum();
  277. // Now, update it into place.
  278. *checksum = new_checksum;
  279. }
  280. // Close the segment here in case the code further below
  281. // doesn't complete successfully.
  282. header_ = NULL;
  283. mem_sgmt_.reset();
  284. }
  285. }
  286. // After more methods' definitions are added here, it would be a good
  287. // idea to move getHeader() and getMemorySegment() definitions to the
  288. // header file.
  289. ZoneTableHeader&
  290. ZoneTableSegmentMapped::getHeader() {
  291. if (!mem_sgmt_) {
  292. isc_throw(isc::InvalidOperation,
  293. "getHeader() called without calling reset() first");
  294. }
  295. return (*header_);
  296. }
  297. const ZoneTableHeader&
  298. ZoneTableSegmentMapped::getHeader() const {
  299. if (!mem_sgmt_) {
  300. isc_throw(isc::InvalidOperation,
  301. "getHeader() called without calling reset() first");
  302. }
  303. return (*header_);
  304. }
  305. MemorySegment&
  306. ZoneTableSegmentMapped::getMemorySegment() {
  307. if (!mem_sgmt_) {
  308. isc_throw(isc::InvalidOperation,
  309. "getMemorySegment() called without calling reset() first");
  310. }
  311. return (*mem_sgmt_);
  312. }
  313. bool
  314. ZoneTableSegmentMapped::isWritable() const {
  315. if (!mem_sgmt_) {
  316. // If reset() was never performed for this segment, or if the
  317. // most recent reset() had failed, then the segment is not
  318. // writable.
  319. return (false);
  320. }
  321. return ((current_mode_ == CREATE) || (current_mode_ == READ_WRITE));
  322. }
  323. } // namespace memory
  324. } // namespace datasrc
  325. } // namespace isc