zone_table_segment_mapped.cc 12.9 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

#include <datasrc/memory/zone_table_segment_mapped.h>

#include <memory>

using namespace isc::data;
using namespace isc::dns;
using namespace isc::util;

namespace isc {
namespace datasrc {
namespace memory {

27
28
namespace { // unnamed namespace

29
// The name with which the zone table checksum is associated in the segment.
30
const char* const ZONE_TABLE_CHECKSUM_NAME = "zone_table_checksum";
31
32

// The name with which the zone table header is associated in the segment.
33
34
35
36
const char* const ZONE_TABLE_HEADER_NAME = "zone_table_header";

} // end of unnamed namespace

37
38
39
40
41
42
43
ZoneTableSegmentMapped::ZoneTableSegmentMapped(const RRClass& rrclass) :
    ZoneTableSegment(rrclass),
    rrclass_(rrclass),
    header_(NULL)
{
}

44
bool
45
ZoneTableSegmentMapped::processChecksum(MemorySegmentMapped& segment,
46
47
                                        bool create,
                                        std::string& error_msg)
48
{
49
    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
55
56
            error_msg = "There is already a saved checksum in the segment "
                 "opened in create mode";
            return (false);
57
58
59
60
61
62
63
64
65
66
67
        } else {
            // The segment was already shrunk when it was last
            // closed. Check that its checksum is consistent.
            assert(result.second);
            uint32_t* checksum = static_cast<uint32_t*>(result.second);
            const uint32_t saved_checksum = *checksum;
            // First, clear the checksum so that getCheckSum() returns a
            // consistent value.
            *checksum = 0;
            const uint32_t new_checksum = segment.getCheckSum();
            if (saved_checksum != new_checksum) {
68
69
                error_msg = "Saved checksum doesn't match segment data";
                return (false);
70
71
72
73
            }
        }
    } else {
        // Allocate space for a checksum (which is saved during close).
74
75
76
77
78
79
80
81
82
83
84
85
86

        // First allocate a ZONE_TABLE_CHECKSUM_NAME, so that we can set
        // it without growing the segment (and changing the checksum's
        // address).
        segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME, NULL);
        void* checksum = NULL;
        while (!checksum) {
            try {
                checksum = segment.allocate(sizeof(uint32_t));
            } catch (const MemorySegmentGrown&) {
                // Do nothing and try again.
            }
        }
87
        *static_cast<uint32_t*>(checksum) = 0;
88
89
        const bool grew = segment.setNamedAddress(ZONE_TABLE_CHECKSUM_NAME,
                                                  checksum);
90
91
92
93
94
95
96
97
98
99
        if (grew) {
            // If the segment grew here, we have a problem as the
            // checksum address may no longer be valid. In this case, we
            // cannot recover. This case is extremely unlikely as we
            // reserved memory for the ZONE_TABLE_CHECKSUM_NAME
            // above. It indicates a very restrictive MemorySegment
            // which we should not use.
            error_msg = "Segment grew unexpectedly in setNamedAddress()";
            return (false);
        }
100
    }
101
102

    return (true);
103
}
104

105
bool
106
ZoneTableSegmentMapped::processHeader(MemorySegmentMapped& segment,
107
108
                                      bool create,
                                      std::string& error_msg)
109
110
111
{
    MemorySegment::NamedAddressResult result =
        segment.getNamedAddress(ZONE_TABLE_HEADER_NAME);
112
    if (result.first) {
113
        if (create) {
114
            // There must be no previously saved checksum.
115
116
117
            error_msg = "There is already a saved ZoneTableHeader in the "
                 "segment opened in create mode";
            return (false);
118
119
120
121
122
        } else {
            assert(result.second);
            header_ = static_cast<ZoneTableHeader*>(result.second);
        }
    } else {
Mukund Sivaraman's avatar
Mukund Sivaraman committed
123
124
125
        // First allocate a ZONE_TABLE_HEADER_NAME, so that we can set
        // it without growing the segment (and changing the header's
        // address).
126
127
128
129
130
131
132
133
134
        segment.setNamedAddress(ZONE_TABLE_HEADER_NAME, NULL);
        void* ptr = NULL;
        while (!ptr) {
            try {
                ptr = segment.allocate(sizeof(ZoneTableHeader));
            } catch (const MemorySegmentGrown&) {
                // Do nothing and try again.
            }
        }
135
136
        ZoneTableHeader* new_header = new(ptr)
            ZoneTableHeader(ZoneTable::create(segment, rrclass_));
137
138
139
        const bool grew = segment.setNamedAddress(ZONE_TABLE_HEADER_NAME,
                                                  new_header);
        if (grew) {
140
141
142
143
144
145
146
147
            // If the segment grew here, we have a problem as the table
            // header address may no longer be valid. In this case, we
            // cannot recover. This case is extremely unlikely as we
            // reserved memory for the ZONE_TABLE_HEADER_NAME above. It
            // indicates a very restrictive MemorySegment which we
            // should not use.
            error_msg = "Segment grew unexpectedly in setNamedAddress()";
            return (false);
148
        }
149
        header_ = new_header;
150
    }
151

152
    return (true);
153
154
155
}

void
156
157
158
159
160
161
162
163
ZoneTableSegmentMapped::openReadWrite(const std::string& filename,
                                      bool create)
{
    const MemorySegmentMapped::OpenMode mode = create ?
         MemorySegmentMapped::CREATE_ONLY :
         MemorySegmentMapped::OPEN_OR_CREATE;
    // In case there is a problem, we throw. We want the segment to be
    // automatically destroyed then.
164
    std::auto_ptr<MemorySegmentMapped> segment
165
166
167
168
169
170
171
172
173
174
175
176
177
178
        (new MemorySegmentMapped(filename, mode));

    std::string error_msg;
    if (!processChecksum(*segment, create, error_msg)) {
         if (mem_sgmt_) {
              isc_throw(ResetFailed,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         } else {
              isc_throw(ResetFailedAndSegmentCleared,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         }
    }
179

180
181
182
183
184
185
186
187
188
189
190
    if (!processHeader(*segment, create, error_msg)) {
         if (mem_sgmt_) {
              isc_throw(ResetFailed,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         } else {
              isc_throw(ResetFailedAndSegmentCleared,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         }
    }
191
192
193
194
195
196

    mem_sgmt_.reset(segment.release());
}

void
ZoneTableSegmentMapped::openReadOnly(const std::string& filename) {
197
198
    // In case the checksum or table header is missing, we throw. We
    // want the segment to be automatically destroyed then.
199
200
201
202
203
204
    std::auto_ptr<MemorySegmentMapped> segment
        (new MemorySegmentMapped(filename));
    // There must be a previously saved checksum.
    MemorySegment::NamedAddressResult result =
        segment->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
    if (!result.first) {
205
206
207
208
209
210
211
212
213
214
215
216
         const std::string error_msg =
             "There is no previously saved checksum in a "
             "mapped segment opened in read-only mode";
         if (mem_sgmt_) {
              isc_throw(ResetFailed,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         } else {
              isc_throw(ResetFailedAndSegmentCleared,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         }
217
218
219
220
221
222
223
224
225
226
227
228
    }

    // We can't verify the checksum here as we can't set the checksum to
    // 0 for checksum calculation in a read-only segment. So we continue
    // without verifying the checksum.

    // There must be a previously saved ZoneTableHeader.
    result = segment->getNamedAddress(ZONE_TABLE_HEADER_NAME);
    if (result.first) {
        assert(result.second);
        header_ = static_cast<ZoneTableHeader*>(result.second);
    } else {
229
230
231
232
233
234
235
236
237
238
239
240
         const std::string error_msg =
             "There is no previously saved ZoneTableHeader in a "
             "mapped segment opened in read-only mode.";
         if (mem_sgmt_) {
              isc_throw(ResetFailed,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         } else {
              isc_throw(ResetFailedAndSegmentCleared,
                        "Error in resetting zone table segment to use "
                        << filename << ": " << error_msg);
         }
241
242
243
244
245
    }

    mem_sgmt_.reset(segment.release());
}

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
void
ZoneTableSegmentMapped::reset(MemorySegmentOpenMode mode,
                              isc::data::ConstElementPtr params)
{
    if (!params || params->getType() != Element::map) {
        isc_throw(isc::InvalidParameter,
                  "Configuration does not contain a map");
    }

    if (!params->contains("mapped-file")) {
        isc_throw(isc::InvalidParameter,
                  "Configuration does not contain a \"mapped-file\" key");
    }

    ConstElementPtr mapped_file = params->get("mapped-file");
    if ((!mapped_file) || (mapped_file->getType() != Element::string)) {
        isc_throw(isc::InvalidParameter,
                  "Value of \"mapped-file\" is not a string");
    }

    const std::string filename = mapped_file->stringValue();

268
269
270
271
272
273
274
    if (mem_sgmt_ && (filename == current_filename_)) {
        // This reset() is an attempt to re-open the currently open
        // mapped file. We cannot do this in many mode combinations
        // unless we close the existing mapped file. So just close it.
        clear();
    }

275
    switch (mode) {
276
    case CREATE:
277
        openReadWrite(filename, true);
278
279
        break;

280
    case READ_WRITE:
281
        openReadWrite(filename, false);
282
        break;
283

284
    case READ_ONLY:
Mukund Sivaraman's avatar
Mukund Sivaraman committed
285
        openReadOnly(filename);
286
287
288
    }

    current_mode_ = mode;
289
    current_filename_ = filename;
290
291
}

292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
void
ZoneTableSegmentMapped::clear()
{
    if (mem_sgmt_) {
        if (isWritable()) {
            // If there is a previously opened segment, and it was
            // opened in read-write mode, update its checksum.
            mem_sgmt_->shrinkToFit();
            const MemorySegment::NamedAddressResult result =
                mem_sgmt_->getNamedAddress(ZONE_TABLE_CHECKSUM_NAME);
            assert(result.first);
            assert(result.second);
            uint32_t* checksum = static_cast<uint32_t*>(result.second);
            // First, clear the checksum so that getCheckSum() returns
            // a consistent value.
            *checksum = 0;
            const uint32_t new_checksum = mem_sgmt_->getCheckSum();
            // Now, update it into place.
            *checksum = new_checksum;
        }
        // Close the segment here in case the code further below
        // doesn't complete successfully.
        header_ = NULL;
        mem_sgmt_.reset();
    }
}

319
320
321
322
323
324
// After more methods' definitions are added here, it would be a good
// idea to move getHeader() and getMemorySegment() definitions to the
// header file.
ZoneTableHeader&
ZoneTableSegmentMapped::getHeader() {
    if (!mem_sgmt_) {
325
        isc_throw(isc::InvalidOperation,
326
327
328
329
330
331
332
333
                  "getHeader() called without calling reset() first");
    }
    return (*header_);
}

const ZoneTableHeader&
ZoneTableSegmentMapped::getHeader() const {
    if (!mem_sgmt_) {
334
        isc_throw(isc::InvalidOperation,
335
336
337
338
339
340
341
342
                  "getHeader() called without calling reset() first");
    }
    return (*header_);
}

MemorySegment&
ZoneTableSegmentMapped::getMemorySegment() {
    if (!mem_sgmt_) {
343
        isc_throw(isc::InvalidOperation,
344
345
346
347
348
349
350
351
                  "getMemorySegment() called without calling reset() first");
    }
    return (*mem_sgmt_);
}

bool
ZoneTableSegmentMapped::isWritable() const {
    if (!mem_sgmt_) {
352
353
354
355
        // If reset() was never performed for this segment, or if the
        // most recent reset() had failed, then the segment is not
        // writable.
        return (false);
356
    }
357

358
359
360
361
362
363
    return ((current_mode_ == CREATE) || (current_mode_ == READ_WRITE));
}

} // namespace memory
} // namespace datasrc
} // namespace isc