]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab ft=cpp | |
3 | ||
4 | /* | |
5 | * Ceph - scalable distributed file system | |
6 | * | |
7 | * Copyright (C) 2020 Red Hat, Inc | |
8 | * | |
9 | * This is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU Lesser General Public | |
11 | * License version 2.1, as published by the Free Software | |
12 | * Foundation. See file COPYING. | |
13 | */ | |
14 | ||
15 | #include "objclass/objclass.h" | |
16 | #include "ops.h" | |
17 | ||
18 | CLS_VER(1,0) | |
19 | CLS_NAME(cmpomap) | |
20 | ||
21 | using namespace cls::cmpomap; | |
22 | ||
23 | // returns negative error codes or 0/1 for failed/successful comparisons | |
24 | template <typename T> | |
25 | static int compare_values(Op op, const T& lhs, const T& rhs) | |
26 | { | |
27 | switch (op) { | |
28 | case Op::EQ: return (lhs == rhs); | |
29 | case Op::NE: return (lhs != rhs); | |
30 | case Op::GT: return (lhs > rhs); | |
31 | case Op::GTE: return (lhs >= rhs); | |
32 | case Op::LT: return (lhs < rhs); | |
33 | case Op::LTE: return (lhs <= rhs); | |
34 | default: return -EINVAL; | |
35 | } | |
36 | } | |
37 | ||
38 | static int compare_values_u64(Op op, uint64_t lhs, const bufferlist& value) | |
39 | { | |
40 | try { | |
41 | // decode existing value as rhs | |
42 | uint64_t rhs; | |
43 | auto p = value.cbegin(); | |
44 | using ceph::decode; | |
45 | decode(rhs, p); | |
46 | return compare_values(op, lhs, rhs); | |
47 | } catch (const buffer::error&) { | |
48 | // failures to decode existing values are reported as EIO | |
49 | return -EIO; | |
50 | } | |
51 | } | |
52 | ||
53 | static int compare_value(Mode mode, Op op, const bufferlist& input, | |
54 | const bufferlist& value) | |
55 | { | |
56 | switch (mode) { | |
57 | case Mode::String: | |
58 | return compare_values(op, input, value); | |
59 | case Mode::U64: | |
60 | try { | |
61 | // decode input value as lhs | |
62 | uint64_t lhs; | |
63 | auto p = input.cbegin(); | |
64 | using ceph::decode; | |
65 | decode(lhs, p); | |
66 | return compare_values_u64(op, lhs, value); | |
67 | } catch (const buffer::error&) { | |
68 | // failures to decode input values are reported as EINVAL | |
69 | return -EINVAL; | |
70 | } | |
71 | default: | |
72 | return -EINVAL; | |
73 | } | |
74 | } | |
75 | ||
76 | static int cmp_vals(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
77 | { | |
78 | cmp_vals_op op; | |
79 | try { | |
80 | auto p = in->cbegin(); | |
81 | decode(op, p); | |
82 | } catch (const buffer::error&) { | |
83 | CLS_LOG(1, "ERROR: cmp_vals(): failed to decode input"); | |
84 | return -EINVAL; | |
85 | } | |
86 | ||
87 | // collect the keys we need to read | |
88 | std::set<std::string> keys; | |
89 | for (const auto& kv : op.values) { | |
90 | keys.insert(kv.first); | |
91 | } | |
92 | ||
93 | // read the values for each key to compare | |
94 | std::map<std::string, bufferlist> values; | |
95 | int r = cls_cxx_map_get_vals_by_keys(hctx, keys, &values); | |
96 | if (r < 0) { | |
97 | CLS_LOG(4, "ERROR: cmp_vals() failed to read values r=%d", r); | |
98 | return r; | |
99 | } | |
100 | ||
101 | auto v = values.cbegin(); | |
102 | for (const auto& [key, input] : op.values) { | |
103 | bufferlist value; | |
104 | if (v != values.end() && v->first == key) { | |
105 | value = std::move(v->second); | |
106 | ++v; | |
107 | CLS_LOG(20, "cmp_vals() comparing key=%s mode=%d op=%d", | |
108 | key.c_str(), (int)op.mode, (int)op.comparison); | |
109 | } else if (!op.default_value) { | |
110 | CLS_LOG(20, "cmp_vals() missing key=%s", key.c_str()); | |
111 | return -ECANCELED; | |
112 | } else { | |
113 | // use optional default for missing keys | |
114 | value = *op.default_value; | |
115 | CLS_LOG(20, "cmp_vals() comparing missing key=%s mode=%d op=%d", | |
116 | key.c_str(), (int)op.mode, (int)op.comparison); | |
117 | } | |
118 | ||
119 | r = compare_value(op.mode, op.comparison, input, value); | |
120 | if (r < 0) { | |
121 | CLS_LOG(10, "cmp_vals() failed to compare key=%s r=%d", key.c_str(), r); | |
122 | return r; | |
123 | } | |
124 | if (r == 0) { | |
125 | CLS_LOG(10, "cmp_vals() comparison at key=%s returned false", key.c_str()); | |
126 | return -ECANCELED; | |
127 | } | |
128 | CLS_LOG(20, "cmp_vals() comparison at key=%s returned true", key.c_str()); | |
129 | } | |
130 | ||
131 | return 0; | |
132 | } | |
133 | ||
134 | static int cmp_set_vals(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
135 | { | |
136 | cmp_set_vals_op op; | |
137 | try { | |
138 | auto p = in->cbegin(); | |
139 | decode(op, p); | |
140 | } catch (const buffer::error&) { | |
141 | CLS_LOG(1, "ERROR: cmp_set_vals(): failed to decode input"); | |
142 | return -EINVAL; | |
143 | } | |
144 | ||
145 | // collect the keys we need to read | |
146 | std::set<std::string> keys; | |
147 | for (const auto& kv : op.values) { | |
148 | keys.insert(kv.first); | |
149 | } | |
150 | ||
151 | // read the values for each key to compare | |
152 | std::map<std::string, bufferlist> values; | |
153 | int r = cls_cxx_map_get_vals_by_keys(hctx, keys, &values); | |
154 | if (r < 0) { | |
155 | CLS_LOG(4, "ERROR: cmp_set_vals() failed to read values r=%d", r); | |
156 | return r; | |
157 | } | |
158 | ||
159 | auto v = values.begin(); | |
160 | for (const auto& [key, input] : op.values) { | |
161 | auto k = values.end(); | |
162 | bufferlist value; | |
163 | if (v != values.end() && v->first == key) { | |
164 | value = std::move(v->second); | |
165 | k = v++; | |
166 | CLS_LOG(20, "cmp_set_vals() comparing key=%s mode=%d op=%d", | |
167 | key.c_str(), (int)op.mode, (int)op.comparison); | |
168 | } else if (!op.default_value) { | |
169 | CLS_LOG(20, "cmp_set_vals() missing key=%s", key.c_str()); | |
170 | continue; | |
171 | } else { | |
172 | // use optional default for missing keys | |
173 | value = *op.default_value; | |
174 | CLS_LOG(20, "cmp_set_vals() comparing missing key=%s mode=%d op=%d", | |
175 | key.c_str(), (int)op.mode, (int)op.comparison); | |
176 | } | |
177 | ||
178 | r = compare_value(op.mode, op.comparison, input, value); | |
179 | if (r == -EIO) { | |
180 | r = 0; // treat EIO as a failed comparison | |
181 | } | |
182 | if (r < 0) { | |
183 | CLS_LOG(10, "cmp_set_vals() failed to compare key=%s r=%d", | |
184 | key.c_str(), r); | |
185 | return r; | |
186 | } | |
187 | if (r == 0) { | |
188 | // unsuccessful comparison | |
189 | if (k != values.end()) { | |
190 | values.erase(k); // remove this key from the values to overwrite | |
191 | CLS_LOG(20, "cmp_set_vals() not overwriting key=%s", key.c_str()); | |
192 | } else { | |
193 | CLS_LOG(20, "cmp_set_vals() not writing missing key=%s", key.c_str()); | |
194 | } | |
195 | } else { | |
196 | // successful comparison | |
197 | if (k != values.end()) { | |
198 | // overwrite the value | |
199 | k->second = std::move(input); | |
200 | CLS_LOG(20, "cmp_set_vals() overwriting key=%s", key.c_str()); | |
201 | } else { | |
202 | // insert the value | |
203 | values.emplace(key, std::move(input)); | |
204 | CLS_LOG(20, "cmp_set_vals() overwriting missing key=%s", key.c_str()); | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | if (values.empty()) { | |
210 | CLS_LOG(20, "cmp_set_vals() has no values to overwrite"); | |
211 | return 0; | |
212 | } | |
213 | ||
214 | CLS_LOG(20, "cmp_set_vals() overwriting count=%d", (int)values.size()); | |
215 | return cls_cxx_map_set_vals(hctx, &values); | |
216 | } | |
217 | ||
218 | static int cmp_rm_keys(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
219 | { | |
220 | cmp_rm_keys_op op; | |
221 | try { | |
222 | auto p = in->cbegin(); | |
223 | decode(op, p); | |
224 | } catch (const buffer::error&) { | |
225 | CLS_LOG(1, "ERROR: cmp_rm_keys(): failed to decode input"); | |
226 | return -EINVAL; | |
227 | } | |
228 | ||
229 | // collect the keys we need to read | |
230 | std::set<std::string> keys; | |
231 | for (const auto& kv : op.values) { | |
232 | keys.insert(kv.first); | |
233 | } | |
234 | ||
235 | // read the values for each key to compare | |
236 | std::map<std::string, bufferlist> values; | |
237 | int r = cls_cxx_map_get_vals_by_keys(hctx, keys, &values); | |
238 | if (r < 0) { | |
239 | CLS_LOG(4, "ERROR: cmp_rm_keys() failed to read values r=%d", r); | |
240 | return r; | |
241 | } | |
242 | ||
243 | auto v = values.cbegin(); | |
244 | for (const auto& [key, input] : op.values) { | |
245 | if (v == values.end() || v->first != key) { | |
246 | CLS_LOG(20, "cmp_rm_keys() missing key=%s", key.c_str()); | |
247 | continue; | |
248 | } | |
249 | CLS_LOG(20, "cmp_rm_keys() comparing key=%s mode=%d op=%d", | |
250 | key.c_str(), (int)op.mode, (int)op.comparison); | |
251 | ||
252 | const bufferlist& value = v->second; | |
253 | ++v; | |
254 | ||
255 | r = compare_value(op.mode, op.comparison, input, value); | |
256 | if (r == -EIO) { | |
257 | r = 0; // treat EIO as a failed comparison | |
258 | } | |
259 | if (r < 0) { | |
260 | CLS_LOG(10, "cmp_rm_keys() failed to compare key=%s r=%d", | |
261 | key.c_str(), r); | |
262 | return r; | |
263 | } | |
264 | if (r == 0) { | |
265 | // unsuccessful comparison | |
266 | CLS_LOG(20, "cmp_rm_keys() preserving key=%s", key.c_str()); | |
267 | } else { | |
268 | // successful comparison | |
269 | CLS_LOG(20, "cmp_rm_keys() removing key=%s", key.c_str()); | |
270 | r = cls_cxx_map_remove_key(hctx, key); | |
271 | if (r < 0) { | |
272 | CLS_LOG(1, "ERROR: cmp_rm_keys() failed to remove key=%s r=%d", | |
273 | key.c_str(), r); | |
274 | return r; | |
275 | } | |
276 | } | |
277 | } | |
278 | ||
279 | return 0; | |
280 | } | |
281 | ||
282 | CLS_INIT(cmpomap) | |
283 | { | |
284 | CLS_LOG(1, "Loaded cmpomap class!"); | |
285 | ||
286 | cls_handle_t h_class; | |
287 | cls_method_handle_t h_cmp_vals; | |
288 | cls_method_handle_t h_cmp_set_vals; | |
289 | cls_method_handle_t h_cmp_rm_keys; | |
290 | ||
291 | cls_register("cmpomap", &h_class); | |
292 | ||
293 | cls_register_cxx_method(h_class, "cmp_vals", CLS_METHOD_RD, | |
294 | cmp_vals, &h_cmp_vals); | |
295 | cls_register_cxx_method(h_class, "cmp_set_vals", CLS_METHOD_RD | CLS_METHOD_WR, | |
296 | cmp_set_vals, &h_cmp_set_vals); | |
297 | cls_register_cxx_method(h_class, "cmp_rm_keys", CLS_METHOD_RD | CLS_METHOD_WR, | |
298 | cmp_rm_keys, &h_cmp_rm_keys); | |
299 | } |