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

paticcaa / pain / 17470612455

04 Sep 2025 04:35PM UTC coverage: 54.596% (-2.0%) from 56.572%
17470612455

push

github

ivanallen
check index

Signed-off-by: allen <1007729991@qq.com>

1509 of 3772 branches covered (40.01%)

Branch coverage included in aggregate %.

80 of 104 new or added lines in 4 files covered. (76.92%)

5 existing lines in 2 files now uncovered.

1758 of 2212 relevant lines covered (79.48%)

424.78 hits per line

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

57.4
/src/deva/namespace.cc
1
#include "deva/namespace.h"
2
#include <pain/base/plog.h>
3
#include <pain/base/scope_exit.h>
4
#include "common/txn_manager.h"
5
#include "common/txn_store.h"
6

7
namespace pain::deva {
8

9
Namespace::Namespace(common::StorePtr store) : _store(store) {
25✔
10
    _root = UUID::from_str_or_die("00000000-0000-0000-0000-000000000000");
75!
11
}
25✔
12

13
Status Namespace::load() {
×
14
    return Status::OK();
×
15
}
16

17
Status Namespace::create(const UUID& parent, const std::string& name, FileType type, const UUID& inode) {
94✔
18
    auto in_txn = common::TxnManager::instance().in_txn();
94!
19
    auto this_txn = _store->begin_txn();
94!
20
    auto txn = in_txn ? common::TxnManager::instance().get_txn_store() : this_txn.get();
94!
21
    if (txn == nullptr) {
94!
UNCOV
22
        return Status(EIO, "Failed to begin transaction");
×
23
    }
24
    auto rollback = make_scope_exit([&txn]() {
188✔
25
        auto status = txn->rollback();
×
26
        if (!status.ok()) {
×
27
            PLOG_ERROR(("desc", "Failed to rollback")("status", status));
×
28
        }
29
    });
94✔
30
    if (in_txn) {
94✔
31
        rollback.release();
76✔
32
    }
33
    // find parent
34
    std::string dentries_str;
94✔
35
    proto::DirEntries dentries;
94!
36
    auto status = txn->hget(_dentry_key, parent.str(), &dentries_str);
94!
37
    if (status.ok()) {
94✔
38
        if (!dentries.ParseFromString(dentries_str)) {
69!
39
            return Status(EBADMSG, "Failed to parse dentries");
×
40
        }
41
    } else if (status.error_code() != ENOENT) {
25!
42
        // something wrong
43
        return status;
×
44
    }
45
    // check if name or inode already exists
46
    for (const auto& dentry : dentries.entries()) {
642!
47
        if (dentry.name() == name) {
549!
48
            return Status(EEXIST, "File name already exists");
×
49
        }
50
        if (dentry.file_id().high() == inode.high() && dentry.file_id().low() == inode.low()) {
549!
51
            return Status(EEXIST, "File inode already exists");
×
52
        }
53
    }
54
    // create dentry
55
    auto dentry = dentries.add_entries();
94!
56
    dentry->set_name(name);
57
    dentry->mutable_file_id()->set_high(inode.high());
94!
58
    dentry->mutable_file_id()->set_low(inode.low());
93!
59
    dentry->mutable_parent_file_id()->set_high(parent.high());
94!
60
    dentry->mutable_parent_file_id()->set_low(parent.low());
94!
61
    dentry->set_type(static_cast<proto::FileType>(type));
94!
62
    dentries_str.clear();
93✔
63
    if (!dentries.SerializeToString(&dentries_str)) {
94!
64
        return Status(EBADMSG, "Failed to serialize dentries");
×
65
    }
66
    // create dentry
67
    txn->hset(_dentry_key, parent.str(), dentries_str);
94!
68

69
    // insert file info
70
    proto::FileInfo file_info;
94!
71
    file_info.mutable_file_id()->set_high(inode.high());
94!
72
    file_info.mutable_file_id()->set_low(inode.low());
94!
73
    file_info.set_type(static_cast<proto::FileType>(type));
94!
74
    file_info.set_size(0);
94!
75
    file_info.set_ctime(0);
94!
76
    file_info.set_mtime(0);
94!
77
    file_info.set_atime(0);
94!
78
    file_info.set_uid(0);
94!
79
    file_info.set_gid(0);
94!
80
    std::string file_info_str;
94✔
81
    if (!file_info.SerializeToString(&file_info_str)) {
94!
82
        return Status(EBADMSG, "Failed to serialize file info");
×
83
    }
84
    status = txn->hset(_inode_key, inode.str(), file_info_str);
94!
85
    if (!status.ok()) {
94!
NEW
86
        return status;
×
87
    }
88

89
    if (in_txn) {
94✔
90
        return Status::OK();
76✔
91
    }
92

93
    status = txn->commit();
18!
94
    if (!status.ok()) {
18!
95
        return status;
×
96
    }
97
    rollback.release();
18✔
98
    return Status::OK();
18✔
99
}
94✔
100

101
Status Namespace::remove(const UUID& parent, const std::string& name) {
3✔
102
    auto in_txn = common::TxnManager::instance().in_txn();
3!
103
    auto this_txn = _store->begin_txn();
3!
104
    auto txn = in_txn ? common::TxnManager::instance().get_txn_store() : this_txn.get();
3!
105
    if (txn == nullptr) {
3!
UNCOV
106
        return Status(EIO, "Failed to begin transaction");
×
107
    }
108
    // find parent
109
    auto rollback = make_scope_exit([&txn]() {
6✔
110
        auto status = txn->rollback();
×
111
        if (!status.ok()) {
×
112
            PLOG_ERROR(("desc", "Failed to rollback")("status", status));
×
113
        }
114
    });
3✔
115
    if (in_txn) {
3!
NEW
116
        rollback.release();
×
117
    }
118
    // get dentries
119
    std::string dentries_str;
3✔
120
    auto status = txn->hget(_dentry_key, parent.str(), &dentries_str);
3!
121
    if (!status.ok()) {
3!
122
        return status;
×
123
    }
124
    proto::DirEntries dentries;
3!
125
    if (!dentries.ParseFromString(dentries_str)) {
3!
126
        return Status(EBADMSG, "Failed to parse dentries");
×
127
    }
128
    // remove dentry
129
    UUID file_id;
3✔
130
    auto entry =
3✔
131
        std::find_if(dentries.entries().begin(), dentries.entries().end(), [&name](const proto::DirEntry& dentry) {
3!
132
            return dentry.name() == name;
3✔
133
        });
134
    if (entry == dentries.entries().end()) {
3!
135
        return Status(ENOENT, "No such file or directory");
×
136
    }
137
    file_id = UUID(entry->file_id().high(), entry->file_id().low());
3!
138
    dentries.mutable_entries()->erase(entry);
3!
139
    // set dentries
140
    if (!dentries.SerializeToString(&dentries_str)) {
3!
141
        return Status(EBADMSG, "Failed to serialize dentries");
×
142
    }
143
    status = txn->hset(_dentry_key, parent.str(), dentries_str);
3!
144
    if (!status.ok()) {
3!
NEW
145
        return status;
×
146
    }
147

148
    // remove file info
149
    status = txn->hdel(_inode_key, file_id.str());
3!
150
    if (!status.ok()) {
3!
NEW
151
        return status;
×
152
    }
153

154
    if (in_txn) {
3!
NEW
155
        return Status::OK();
×
156
    }
157

158
    status = txn->commit();
3!
159
    if (!status.ok()) {
3!
160
        return status;
×
161
    }
162
    rollback.release();
3✔
163
    return Status::OK();
3✔
164
}
3✔
165

166
void Namespace::list(const UUID& parent, std::list<DirEntry>* entries) const {
10✔
167
    entries->clear();
10✔
168
    std::string dentries_str;
10✔
169
    auto in_txn = common::TxnManager::instance().in_txn();
10!
170
    auto this_txn = _store->begin_txn();
10!
171
    auto txn = in_txn ? common::TxnManager::instance().get_txn_store() : this_txn.get();
10!
172
    if (txn == nullptr) {
10!
NEW
173
        return;
×
174
    }
175
    auto status = txn->hget(_dentry_key, parent.str(), &dentries_str);
10!
176
    if (!status.ok()) {
10!
NEW
177
        PLOG_ERROR(("desc", "Failed to get dentries")("status", status));
×
UNCOV
178
        return;
×
179
    }
180
    proto::DirEntries dentries;
10!
181
    if (!dentries.ParseFromString(dentries_str)) {
10!
NEW
182
        PLOG_ERROR(("desc", "Failed to parse dentries")("status", status));
×
UNCOV
183
        return;
×
184
    }
185
    for (const auto& dentry : dentries.entries()) {
49!
186
        entries->emplace_back(
78!
187
            UUID(dentry.file_id().high(), dentry.file_id().low()), dentry.name(), static_cast<FileType>(dentry.type()));
78!
188
    }
189
    if (in_txn) {
10!
NEW
190
        return;
×
191
    }
192

193
    status = txn->commit();
10!
194
    if (!status.ok()) {
10!
NEW
195
        PLOG_ERROR(("desc", "Failed to commit")("status", status));
×
196
    }
197
}
10!
198

199
// parse path such as /a/b/c to ["a", "b", "c"]
200
Status Namespace::parse_path(const char* path, std::list<std::string_view>* components) const {
92✔
201
    const char* p = path;
92✔
202
    if (*p != '/') {
92✔
203
        return Status(EINVAL, fmt::format("Invalid path:{}", path));
3!
204
    }
205

206
    while (*p != '\0') {
311✔
207
        if (*p == '/') {
220✔
208
            p++;
119✔
209
            continue;
119✔
210
        }
211
        const char* q = p;
101✔
212
        while (*q != '\0' && *q != '/') {
401✔
213
            q++;
300✔
214
        }
215
        components->emplace_back(p, q - p);
101!
216
        p = q;
101✔
217
    }
218
    return Status::OK();
91✔
219
}
220

221
Status Namespace::lookup(const char* path, UUID* inode, FileType* file_type) const {
85✔
222
    std::list<std::string_view> components;
85✔
223
    auto status = parse_path(path, &components);
85!
224
    if (!status.ok()) {
85!
225
        return status;
×
226
    }
227

228
    UUID parent = _root;
85✔
229
    *file_type = FileType::kDirectory;
85✔
230

231
    auto in_txn = common::TxnManager::instance().in_txn();
85!
232
    auto this_txn = _store->begin_txn();
85!
233
    auto txn = in_txn ? common::TxnManager::instance().get_txn_store() : this_txn.get();
85!
234
    if (txn == nullptr) {
85!
NEW
235
        return Status(EIO, "Failed to begin transaction");
×
236
    }
237

238
    for (const auto& component : components) {
162✔
239
        std::string dentries_str;
79✔
240
        auto status = txn->hget(_dentry_key, parent.str(), &dentries_str);
79!
241
        if (!status.ok()) {
79!
242
            return status;
×
243
        }
244
        proto::DirEntries dentries;
79!
245
        if (!dentries.ParseFromString(dentries_str)) {
79!
246
            return Status(EBADMSG, fmt::format("Failed to parse dentries: {}", component));
×
247
        }
248
        auto entry = std::find_if(
158!
249
            dentries.entries().begin(), dentries.entries().end(), [&component](const proto::DirEntry& dentry) {
79!
250
                return dentry.name() == component;
89✔
251
            });
252
        if (entry == dentries.entries().end()) {
79!
253
            return Status(ENOENT, fmt::format("No such file or directory: {}", component));
3!
254
        }
255
        if (entry->type() == static_cast<proto::FileType>(FileType::kFile) && component != components.back()) {
78!
256
            return Status(ENOENT, fmt::format("Not a directory: {}", component));
3!
257
        }
258
        parent = UUID(entry->file_id().high(), entry->file_id().low());
77!
259
        *file_type = static_cast<FileType>(entry->type());
77!
260
    }
83✔
261
    *inode = parent;
83✔
262
    if (in_txn) {
83✔
263
        return Status::OK();
73✔
264
    }
265

266
    status = txn->commit();
10!
267
    if (!status.ok()) {
10!
NEW
268
        PLOG_ERROR(("desc", "Failed to commit")("status", status));
×
269
    }
270
    return status;
10!
271
}
85✔
272

273
} // namespace pain::deva
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