1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab ft=cpp
5 * Ceph - scalable distributed file system
7 * Copyright (C) 2020 Red Hat, Inc
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.
15 #include "objclass/objclass.h"
21 using namespace cls::cmpomap
;
23 // returns negative error codes or 0/1 for failed/successful comparisons
25 static int compare_values(Op op
, const T
& lhs
, const T
& rhs
)
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
;
38 static int compare_values_u64(Op op
, uint64_t lhs
, const bufferlist
& value
)
40 // empty values compare as 0 for backward compat
44 // decode existing value as rhs
45 auto p
= value
.cbegin();
48 } catch (const buffer::error
&) {
49 // failures to decode existing values are reported as EIO
53 return compare_values(op
, lhs
, rhs
);
56 static int compare_value(Mode mode
, Op op
, const bufferlist
& input
,
57 const bufferlist
& value
)
61 return compare_values(op
, input
, value
);
64 // decode input value as lhs
66 auto p
= input
.cbegin();
69 return compare_values_u64(op
, lhs
, value
);
70 } catch (const buffer::error
&) {
71 // failures to decode input values are reported as EINVAL
79 static int cmp_vals(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
83 auto p
= in
->cbegin();
85 } catch (const buffer::error
&) {
86 CLS_LOG(1, "ERROR: cmp_vals(): failed to decode input");
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
);
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
);
100 CLS_LOG(4, "ERROR: cmp_vals() failed to read values r=%d", r
);
104 auto v
= values
.cbegin();
105 for (const auto& [key
, input
] : op
.values
) {
107 if (v
!= values
.end() && v
->first
== key
) {
108 value
= std::move(v
->second
);
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());
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
);
122 r
= compare_value(op
.mode
, op
.comparison
, input
, value
);
124 CLS_LOG(10, "cmp_vals() failed to compare key=%s r=%d", key
.c_str(), r
);
128 CLS_LOG(10, "cmp_vals() comparison at key=%s returned false", key
.c_str());
131 CLS_LOG(20, "cmp_vals() comparison at key=%s returned true", key
.c_str());
137 static int cmp_set_vals(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
141 auto p
= in
->cbegin();
143 } catch (const buffer::error
&) {
144 CLS_LOG(1, "ERROR: cmp_set_vals(): failed to decode input");
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
);
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
);
158 CLS_LOG(4, "ERROR: cmp_set_vals() failed to read values r=%d", r
);
162 auto v
= values
.begin();
163 for (const auto& [key
, input
] : op
.values
) {
164 auto k
= values
.end();
166 if (v
!= values
.end() && v
->first
== key
) {
167 value
= std::move(v
->second
);
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());
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
);
181 r
= compare_value(op
.mode
, op
.comparison
, input
, value
);
183 r
= 0; // treat EIO as a failed comparison
186 CLS_LOG(10, "cmp_set_vals() failed to compare key=%s r=%d",
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());
196 CLS_LOG(20, "cmp_set_vals() not writing missing key=%s", key
.c_str());
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());
206 values
.emplace(key
, std::move(input
));
207 CLS_LOG(20, "cmp_set_vals() overwriting missing key=%s", key
.c_str());
212 if (values
.empty()) {
213 CLS_LOG(20, "cmp_set_vals() has no values to overwrite");
217 CLS_LOG(20, "cmp_set_vals() overwriting count=%d", (int)values
.size());
218 return cls_cxx_map_set_vals(hctx
, &values
);
221 static int cmp_rm_keys(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
225 auto p
= in
->cbegin();
227 } catch (const buffer::error
&) {
228 CLS_LOG(1, "ERROR: cmp_rm_keys(): failed to decode input");
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
);
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
);
242 CLS_LOG(4, "ERROR: cmp_rm_keys() failed to read values r=%d", r
);
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());
252 CLS_LOG(20, "cmp_rm_keys() comparing key=%s mode=%d op=%d",
253 key
.c_str(), (int)op
.mode
, (int)op
.comparison
);
255 const bufferlist
& value
= v
->second
;
258 r
= compare_value(op
.mode
, op
.comparison
, input
, value
);
260 r
= 0; // treat EIO as a failed comparison
263 CLS_LOG(10, "cmp_rm_keys() failed to compare key=%s r=%d",
268 // unsuccessful comparison
269 CLS_LOG(20, "cmp_rm_keys() preserving key=%s", key
.c_str());
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
);
275 CLS_LOG(1, "ERROR: cmp_rm_keys() failed to remove key=%s r=%d",
287 CLS_LOG(1, "Loaded cmpomap class!");
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
;
294 cls_register("cmpomap", &h_class
);
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
);