zone_table_segment_mapped.cc 8.7 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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 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 {

ZoneTableSegmentMapped::ZoneTableSegmentMapped(const RRClass& rrclass) :
    ZoneTableSegment(rrclass),
    rrclass_(rrclass),
    header_(NULL)
{
}

void
ZoneTableSegmentMapped::reset(MemorySegmentOpenMode mode,
                              isc::data::ConstElementPtr params)
{
    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();
43
44
45
46
47
            const MemorySegment::NamedAddressResult result =
                mem_sgmt_->getNamedAddress("zone_table_checksum");
            assert(result.first);
            assert(result.second);
            uint32_t* checksum = static_cast<uint32_t*>(result.second);
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
            // 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();
    }

    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();

    // In case there is a checksum mismatch, we throw. We want the
    // segment to be automatically destroyed then.
    std::auto_ptr<MemorySegmentMapped> segment;

    switch (mode) {
    case CREATE: {
        segment.reset(new MemorySegmentMapped
                      (filename,
                       MemorySegmentMapped::CREATE_ONLY));
        // There must be no previously saved checksum.
89
90
91
        MemorySegment::NamedAddressResult result =
            segment->getNamedAddress("zone_table_checksum");
        if (result.first) {
92
93
94
95
96
97
98
99
100
101
            isc_throw(isc::Unexpected,
                      "There is already a saved checksum in a mapped segment "
                      "opened in create mode.");
        }
        // Allocate space for a checksum (which is saved during close).
        void* checksum = segment->allocate(sizeof(uint32_t));
        *static_cast<uint32_t*>(checksum) = 0;
        segment->setNamedAddress("zone_table_checksum", checksum);

        // There must be no previously saved ZoneTableHeader.
102
103
        result = segment->getNamedAddress("zone_table_header");
        if (result.first) {
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
            isc_throw(isc::Unexpected,
                      "There is already a saved ZoneTableHeader in a "
                      "mapped segment opened in create mode.");
        }
        void* ptr = segment->allocate(sizeof(ZoneTableHeader));
        ZoneTableHeader* new_header = new(ptr)
             ZoneTableHeader(ZoneTable::create(*segment, rrclass_));
        segment->setNamedAddress("zone_table_header", new_header);
        header_ = new_header;

        break;
    }
    case READ_WRITE: {
        segment.reset(new MemorySegmentMapped
                      (filename, MemorySegmentMapped::OPEN_OR_CREATE));
        // If there is a previously saved checksum, verify that it is
        // consistent. Otherwise, allocate space for a checksum (which
        // is saved during close).
122
123
124
        MemorySegment::NamedAddressResult result =
            segment->getNamedAddress("zone_table_checksum");
        if (result.first) {
125
126
            // The segment was already shrunk when it was last
            // closed. Check that its checksum is consistent.
127
128
            assert(result.second);
            uint32_t* checksum = static_cast<uint32_t*>(result.second);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
129
            const uint32_t saved_checksum = *checksum;
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
            // 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) {
                 isc_throw(isc::Unexpected,
                           "Saved checksum doesn't match mapped segment data");
            }
        } else {
            void* checksum = segment->allocate(sizeof(uint32_t));
            *static_cast<uint32_t*>(checksum) = 0;
            segment->setNamedAddress("zone_table_checksum", checksum);
        }

        // If there is a previously saved ZoneTableHeader, use
        // it. Otherwise, allocate a new header.
146
147
148
149
150
        result = segment->getNamedAddress("zone_table_header");
        if (result.first) {
            assert(result.second);
            header_ = static_cast<ZoneTableHeader*>(result.second);
        } else {
151
152
153
154
155
156
157
158
159
160
161
162
            void* ptr = segment->allocate(sizeof(ZoneTableHeader));
            ZoneTableHeader* new_header = new(ptr)
                ZoneTableHeader(ZoneTable::create(*segment, rrclass_));
            segment->setNamedAddress("zone_table_header", new_header);
            header_ = new_header;
        }

        break;
    }
    case READ_ONLY: {
        segment.reset(new MemorySegmentMapped(filename));
        // There must be a previously saved checksum.
163
164
165
        MemorySegment::NamedAddressResult result =
            segment->getNamedAddress("zone_table_checksum");
        if (!result.first) {
166
167
168
169
            isc_throw(isc::Unexpected,
                      "There is no previously saved checksum in a "
                      "mapped segment opened in read-only mode.");
        }
170
171
172
173

        // 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.
174
175

        // There must be a previously saved ZoneTableHeader.
176
177
178
179
180
        result = segment->getNamedAddress("zone_table_header");
        if (result.first) {
            assert(result.second);
            header_ = static_cast<ZoneTableHeader*>(result.second);
        } else {
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
            isc_throw(isc::Unexpected,
                      "There is no previously saved ZoneTableHeader in a "
                      "mapped segment opened in read-only mode.");
        }
    }
    }

    current_mode_ = mode;
    mem_sgmt_.reset(segment.release());
}

// 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_) {
198
        isc_throw(isc::InvalidOperation,
199
200
201
202
203
204
205
206
                  "getHeader() called without calling reset() first");
    }
    return (*header_);
}

const ZoneTableHeader&
ZoneTableSegmentMapped::getHeader() const {
    if (!mem_sgmt_) {
207
        isc_throw(isc::InvalidOperation,
208
209
210
211
212
213
214
215
                  "getHeader() called without calling reset() first");
    }
    return (*header_);
}

MemorySegment&
ZoneTableSegmentMapped::getMemorySegment() {
    if (!mem_sgmt_) {
216
        isc_throw(isc::InvalidOperation,
217
218
219
220
221
222
223
224
                  "getMemorySegment() called without calling reset() first");
    }
    return (*mem_sgmt_);
}

bool
ZoneTableSegmentMapped::isWritable() const {
    if (!mem_sgmt_) {
225
226
227
228
        // If reset() was never performed for this segment, or if the
        // most recent reset() had failed, then the segment is not
        // writable.
        return (false);
229
    }
230

231
232
233
234
235
236
    return ((current_mode_ == CREATE) || (current_mode_ == READ_WRITE));
}

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