]> git.proxmox.com Git - ceph.git/blob - ceph/src/cls/cmpomap/server.cc
import ceph 16.2.6
[ceph.git] / ceph / src / cls / cmpomap / server.cc
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 // empty values compare as 0 for backward compat
41 uint64_t rhs = 0;
42 if (value.length()) {
43 try {
44 // decode existing value as rhs
45 auto p = value.cbegin();
46 using ceph::decode;
47 decode(rhs, p);
48 } catch (const buffer::error&) {
49 // failures to decode existing values are reported as EIO
50 return -EIO;
51 }
52 }
53 return compare_values(op, lhs, rhs);
54 }
55
56 static int compare_value(Mode mode, Op op, const bufferlist& input,
57 const bufferlist& value)
58 {
59 switch (mode) {
60 case Mode::String:
61 return compare_values(op, input, value);
62 case Mode::U64:
63 try {
64 // decode input value as lhs
65 uint64_t lhs;
66 auto p = input.cbegin();
67 using ceph::decode;
68 decode(lhs, p);
69 return compare_values_u64(op, lhs, value);
70 } catch (const buffer::error&) {
71 // failures to decode input values are reported as EINVAL
72 return -EINVAL;
73 }
74 default:
75 return -EINVAL;
76 }
77 }
78
79 static int cmp_vals(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
80 {
81 cmp_vals_op op;
82 try {
83 auto p = in->cbegin();
84 decode(op, p);
85 } catch (const buffer::error&) {
86 CLS_LOG(1, "ERROR: cmp_vals(): failed to decode input");
87 return -EINVAL;
88 }
89
90 // collect the keys we need to read
91 std::set<std::string> keys;
92 for (const auto& kv : op.values) {
93 keys.insert(kv.first);
94 }
95
96 // read the values for each key to compare
97 std::map<std::string, bufferlist> values;
98 int r = cls_cxx_map_get_vals_by_keys(hctx, keys, &values);
99 if (r < 0) {
100 CLS_LOG(4, "ERROR: cmp_vals() failed to read values r=%d", r);
101 return r;
102 }
103
104 auto v = values.cbegin();
105 for (const auto& [key, input] : op.values) {
106 bufferlist value;
107 if (v != values.end() && v->first == key) {
108 value = std::move(v->second);
109 ++v;
110 CLS_LOG(20, "cmp_vals() comparing key=%s mode=%d op=%d",
111 key.c_str(), (int)op.mode, (int)op.comparison);
112 } else if (!op.default_value) {
113 CLS_LOG(20, "cmp_vals() missing key=%s", key.c_str());
114 return -ECANCELED;
115 } else {
116 // use optional default for missing keys
117 value = *op.default_value;
118 CLS_LOG(20, "cmp_vals() comparing missing key=%s mode=%d op=%d",
119 key.c_str(), (int)op.mode, (int)op.comparison);
120 }
121
122 r = compare_value(op.mode, op.comparison, input, value);
123 if (r < 0) {
124 CLS_LOG(10, "cmp_vals() failed to compare key=%s r=%d", key.c_str(), r);
125 return r;
126 }
127 if (r == 0) {
128 CLS_LOG(10, "cmp_vals() comparison at key=%s returned false", key.c_str());
129 return -ECANCELED;
130 }
131 CLS_LOG(20, "cmp_vals() comparison at key=%s returned true", key.c_str());
132 }
133
134 return 0;
135 }
136
137 static int cmp_set_vals(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
138 {
139 cmp_set_vals_op op;
140 try {
141 auto p = in->cbegin();
142 decode(op, p);
143 } catch (const buffer::error&) {
144 CLS_LOG(1, "ERROR: cmp_set_vals(): failed to decode input");
145 return -EINVAL;
146 }
147
148 // collect the keys we need to read
149 std::set<std::string> keys;
150 for (const auto& kv : op.values) {
151 keys.insert(kv.first);
152 }
153
154 // read the values for each key to compare
155 std::map<std::string, bufferlist> values;
156 int r = cls_cxx_map_get_vals_by_keys(hctx, keys, &values);
157 if (r < 0) {
158 CLS_LOG(4, "ERROR: cmp_set_vals() failed to read values r=%d", r);
159 return r;
160 }
161
162 auto v = values.begin();
163 for (const auto& [key, input] : op.values) {
164 auto k = values.end();
165 bufferlist value;
166 if (v != values.end() && v->first == key) {
167 value = std::move(v->second);
168 k = v++;
169 CLS_LOG(20, "cmp_set_vals() comparing key=%s mode=%d op=%d",
170 key.c_str(), (int)op.mode, (int)op.comparison);
171 } else if (!op.default_value) {
172 CLS_LOG(20, "cmp_set_vals() missing key=%s", key.c_str());
173 continue;
174 } else {
175 // use optional default for missing keys
176 value = *op.default_value;
177 CLS_LOG(20, "cmp_set_vals() comparing missing key=%s mode=%d op=%d",
178 key.c_str(), (int)op.mode, (int)op.comparison);
179 }
180
181 r = compare_value(op.mode, op.comparison, input, value);
182 if (r == -EIO) {
183 r = 0; // treat EIO as a failed comparison
184 }
185 if (r < 0) {
186 CLS_LOG(10, "cmp_set_vals() failed to compare key=%s r=%d",
187 key.c_str(), r);
188 return r;
189 }
190 if (r == 0) {
191 // unsuccessful comparison
192 if (k != values.end()) {
193 values.erase(k); // remove this key from the values to overwrite
194 CLS_LOG(20, "cmp_set_vals() not overwriting key=%s", key.c_str());
195 } else {
196 CLS_LOG(20, "cmp_set_vals() not writing missing key=%s", key.c_str());
197 }
198 } else {
199 // successful comparison
200 if (k != values.end()) {
201 // overwrite the value
202 k->second = std::move(input);
203 CLS_LOG(20, "cmp_set_vals() overwriting key=%s", key.c_str());
204 } else {
205 // insert the value
206 values.emplace(key, std::move(input));
207 CLS_LOG(20, "cmp_set_vals() overwriting missing key=%s", key.c_str());
208 }
209 }
210 }
211
212 if (values.empty()) {
213 CLS_LOG(20, "cmp_set_vals() has no values to overwrite");
214 return 0;
215 }
216
217 CLS_LOG(20, "cmp_set_vals() overwriting count=%d", (int)values.size());
218 return cls_cxx_map_set_vals(hctx, &values);
219 }
220
221 static int cmp_rm_keys(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
222 {
223 cmp_rm_keys_op op;
224 try {
225 auto p = in->cbegin();
226 decode(op, p);
227 } catch (const buffer::error&) {
228 CLS_LOG(1, "ERROR: cmp_rm_keys(): failed to decode input");
229 return -EINVAL;
230 }
231
232 // collect the keys we need to read
233 std::set<std::string> keys;
234 for (const auto& kv : op.values) {
235 keys.insert(kv.first);
236 }
237
238 // read the values for each key to compare
239 std::map<std::string, bufferlist> values;
240 int r = cls_cxx_map_get_vals_by_keys(hctx, keys, &values);
241 if (r < 0) {
242 CLS_LOG(4, "ERROR: cmp_rm_keys() failed to read values r=%d", r);
243 return r;
244 }
245
246 auto v = values.cbegin();
247 for (const auto& [key, input] : op.values) {
248 if (v == values.end() || v->first != key) {
249 CLS_LOG(20, "cmp_rm_keys() missing key=%s", key.c_str());
250 continue;
251 }
252 CLS_LOG(20, "cmp_rm_keys() comparing key=%s mode=%d op=%d",
253 key.c_str(), (int)op.mode, (int)op.comparison);
254
255 const bufferlist& value = v->second;
256 ++v;
257
258 r = compare_value(op.mode, op.comparison, input, value);
259 if (r == -EIO) {
260 r = 0; // treat EIO as a failed comparison
261 }
262 if (r < 0) {
263 CLS_LOG(10, "cmp_rm_keys() failed to compare key=%s r=%d",
264 key.c_str(), r);
265 return r;
266 }
267 if (r == 0) {
268 // unsuccessful comparison
269 CLS_LOG(20, "cmp_rm_keys() preserving key=%s", key.c_str());
270 } else {
271 // successful comparison
272 CLS_LOG(20, "cmp_rm_keys() removing key=%s", key.c_str());
273 r = cls_cxx_map_remove_key(hctx, key);
274 if (r < 0) {
275 CLS_LOG(1, "ERROR: cmp_rm_keys() failed to remove key=%s r=%d",
276 key.c_str(), r);
277 return r;
278 }
279 }
280 }
281
282 return 0;
283 }
284
285 CLS_INIT(cmpomap)
286 {
287 CLS_LOG(1, "Loaded cmpomap class!");
288
289 cls_handle_t h_class;
290 cls_method_handle_t h_cmp_vals;
291 cls_method_handle_t h_cmp_set_vals;
292 cls_method_handle_t h_cmp_rm_keys;
293
294 cls_register("cmpomap", &h_class);
295
296 cls_register_cxx_method(h_class, "cmp_vals", CLS_METHOD_RD,
297 cmp_vals, &h_cmp_vals);
298 cls_register_cxx_method(h_class, "cmp_set_vals", CLS_METHOD_RD | CLS_METHOD_WR,
299 cmp_set_vals, &h_cmp_set_vals);
300 cls_register_cxx_method(h_class, "cmp_rm_keys", CLS_METHOD_RD | CLS_METHOD_WR,
301 cmp_rm_keys, &h_cmp_rm_keys);
302 }