• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 2455469166

15 Apr 2026 03:19PM UTC coverage: 81.435% (-0.03%) from 81.466%
2455469166

push

gitlab-ci

vpodzime
ci: Run key-value DB tests on a non-LMDB build in CI

And combine coverage data with the basic (default) build.

Ticket: MEN-9129
Changelog: none
Signed-off-by: Vratislav Podzimek <vratislav.podzimek+auto-signed@northern.tech>

9181 of 11274 relevant lines covered (81.44%)

20196.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.36
/src/common/key_value_database/platform/blobdb/file_blob.cpp
1
// Copyright 2026 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
#include <common/key_value_database_blobdb.hpp>
16
#include <common/key_value_database/platform/blobdb/file_blob.hpp>
17

18
// We need these two C headers for open() and close(), respectively, because we
19
// need to do flock() and there's no C++ API for that.
20
#include <fcntl.h>
21
#include <unistd.h>
22
#include <sys/file.h> // flock()
23

24
#include <cerrno>
25
#include <cstdio>
26

27
#include <common/path.hpp>
28

29
namespace mender {
30
namespace common {
31
namespace key_value_database {
32

33
namespace path = common::path;
34

35
error::Error FileBlobdbTransaction::SerializeDB(const DB &db) {
13✔
36
        // There can be no collision because no two transactions can be serializing
37
        // the DB at the same time so no random/special name needed here.
38
        string new_file_path = path_or_name_ + ".new";
13✔
39
        auto ex_ofs = io::OpenOfstream(new_file_path);
13✔
40
        if (!ex_ofs) {
13✔
41
                return ex_ofs.error().WithContext("Cannot save DB contents");
×
42
        }
43
        auto &ofs = ex_ofs.value();
13✔
44

45
        auto err = error::NoError;
13✔
46
        for (const auto &kv : db) {
31✔
47
                if (kv.first.size() > std::numeric_limits<uint32_t>::max()) {
18✔
48
                        err = error::Error(
×
49
                                generic_category().default_error_condition(EOVERFLOW),
×
50
                                "Key data too long, cannot be serialized");
×
51
                        break;
×
52
                }
53
                if (kv.second.size() > std::numeric_limits<uint32_t>::max()) {
18✔
54
                        err = error::Error(
×
55
                                generic_category().default_error_condition(EOVERFLOW),
×
56
                                "Value data too long, cannot be serialized");
×
57
                        break;
×
58
                }
59
                uint32_t data_size = static_cast<uint32_t>(kv.first.size());
18✔
60
                ofs.write(reinterpret_cast<const char *>(&data_size), sizeof(data_size));
18✔
61
                if (!ofs) {
18✔
62
                        err = error::Error(
×
63
                                generic_category().default_error_condition(errno), "Failed to write key size");
×
64
                        break;
×
65
                }
66
                ofs.write(kv.first.data(), kv.first.size());
18✔
67
                if (!ofs) {
18✔
68
                        err = error::Error(
×
69
                                generic_category().default_error_condition(errno), "Failed to write key data");
×
70
                        break;
×
71
                }
72
                data_size = static_cast<uint32_t>(kv.second.size());
18✔
73
                ofs.write(reinterpret_cast<const char *>(&data_size), sizeof(data_size));
18✔
74
                if (!ofs) {
18✔
75
                        err = error::Error(
×
76
                                generic_category().default_error_condition(errno), "Failed to write value size");
×
77
                        break;
×
78
                }
79
                ofs.write(reinterpret_cast<const char *>(kv.second.data()), kv.second.size());
18✔
80
                if (!ofs) {
18✔
81
                        err = error::Error(
×
82
                                generic_category().default_error_condition(errno), "Failed to write value data");
×
83
                        break;
×
84
                }
85
        }
86
        ofs.close();
13✔
87

88
        if (err == error::NoError) {
13✔
89
                if (std::rename(new_file_path.c_str(), path_or_name_.c_str()) != 0) {
13✔
90
                        err = error::Error(
×
91
                                generic_category().default_error_condition(errno), "Failed to replace DB contents");
×
92
                }
93
        }
94
        if (err != error::NoError) {
13✔
95
                std::remove(new_file_path.c_str());
×
96
        }
97
        return err;
13✔
98
}
99

100
ExpectedDB FileBlobdbTransaction::DeserializeDB() {
18✔
101
        if (!path::FileExists(path_or_name_)) {
18✔
102
                return DB();
×
103
        }
104
        auto ex_ifs = io::OpenIfstream(path_or_name_);
18✔
105
        if (!ex_ifs) {
18✔
106
                return expected::unexpected(ex_ifs.error().WithContext("Cannot load DB contents"));
×
107
        }
108
        auto &ifs = ex_ifs.value();
18✔
109

110
        auto err = error::NoError;
18✔
111
        DB loaded;
112
        while (ifs) {
35✔
113
                uint32_t data_size = 0;
35✔
114
                ifs.read(reinterpret_cast<char *>(&data_size), sizeof(data_size));
35✔
115
                if (data_size == 0) {
35✔
116
                        if (!ifs.eof()) {
18✔
117
                                err = error::Error(
×
118
                                        generic_category().default_error_condition(errno), "Failed to read key size");
×
119
                        }
120
                        break;
18✔
121
                }
122
                auto key = string(data_size, '\0');
17✔
123
                ifs.read(key.data(), data_size);
17✔
124
                if ((!ifs && !ifs.eof()) || (ifs.gcount() != data_size)) {
17✔
125
                        err = error::Error(
×
126
                                generic_category().default_error_condition(errno), "Failed to read key data");
×
127
                        break;
×
128
                }
129
                data_size = 0;
17✔
130
                ifs.read(reinterpret_cast<char *>(&data_size), sizeof(data_size));
17✔
131
                if (data_size == 0) {
17✔
132
                        err = error::Error(
×
133
                                generic_category().default_error_condition(errno), "Failed to read value size");
×
134
                        break;
×
135
                }
136
                auto value = vector<uint8_t>(data_size);
17✔
137
                ifs.read(reinterpret_cast<char *>(value.data()), data_size);
17✔
138
                if ((!ifs && !ifs.eof()) || (ifs.gcount() != data_size)) {
17✔
139
                        err = error::Error(
×
140
                                generic_category().default_error_condition(errno), "Failed to read value data");
×
141
                        break;
142
                }
143
                loaded[key] = value;
17✔
144
        }
145
        if (err == error::NoError && !ifs.eof()) {
18✔
146
                err = error::Error(
×
147
                        generic_category().default_error_condition(errno), "Error while reading DB data");
×
148
        }
149

150
        if (err != error::NoError) {
18✔
151
                return expected::unexpected(err);
×
152
        }
153
        return loaded;
18✔
154
}
155

156
error::Error FileBlobdbTransaction::LockDB() {
18✔
157
        fd_ = open(path_or_name_.c_str(), O_RDONLY | O_CREAT, S_IRUSR | S_IWUSR);
18✔
158
        if (fd_ == -1) {
18✔
159
                return error::Error(
160
                        generic_category().default_error_condition(errno),
×
161
                        "Failed to open DB at '" + path_or_name_ + "'");
×
162
        }
163

164
        int ret;
165
        if (write_) {
18✔
166
                ret = flock(fd_, LOCK_EX);
9✔
167
        } else {
168
                ret = flock(fd_, LOCK_SH);
9✔
169
        }
170
        if (ret != 0) {
18✔
171
                auto flock_errno = errno;
×
172
                close(fd_);
×
173
                fd_ = -1;
×
174
                return error::Error(
175
                        generic_category().default_error_condition(flock_errno),
×
176
                        "Failed to lock DB '" + path_or_name_ + "'");
×
177
        }
178

179
        return error::NoError;
18✔
180
}
181

182
error::Error FileBlobdbTransaction::UnlockDB() {
18✔
183
        errno = 0;
18✔
184
        close(fd_);
18✔
185
        if (errno != 0) {
18✔
186
                return error::Error(
187
                        generic_category().default_error_condition(errno),
×
188
                        "Failed to release lock on DB '" + path_or_name_ + "'");
×
189
        }
190
        return error::NoError;
18✔
191
}
192

193
} // namespace key_value_database
194
} // namespace common
195
} // namespace mender
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc