]> git.proxmox.com Git - ceph.git/blame - ceph/src/cls/cmpomap/server.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / cls / cmpomap / server.cc
CommitLineData
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
18CLS_VER(1,0)
19CLS_NAME(cmpomap)
20
21using namespace cls::cmpomap;
22
23// returns negative error codes or 0/1 for failed/successful comparisons
24template <typename T>
25static 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
38static 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
53static 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
76static 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
134static 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
218static 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
282CLS_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}