]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // -*- mode:C; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
7c673cae FG |
4 | #include <errno.h> |
5 | ||
7c673cae | 6 | #include "objclass/objclass.h" |
31f18b77 | 7 | |
7c673cae | 8 | #include "cls/version/cls_version_ops.h" |
7c673cae | 9 | |
7c673cae FG |
10 | #include "include/compat.h" |
11 | ||
12 | CLS_VER(1,0) | |
13 | CLS_NAME(version) | |
14 | ||
15 | ||
16 | #define VERSION_ATTR "ceph.objclass.version" | |
17 | ||
18 | static int set_version(cls_method_context_t hctx, struct obj_version *objv) | |
19 | { | |
20 | bufferlist bl; | |
21 | ||
22 | ::encode(*objv, bl); | |
23 | ||
24 | CLS_LOG(20, "cls_version: set_version %s:%d", objv->tag.c_str(), (int)objv->ver); | |
25 | ||
26 | int ret = cls_cxx_setxattr(hctx, VERSION_ATTR, &bl); | |
27 | if (ret < 0) | |
28 | return ret; | |
29 | ||
30 | return 0; | |
31 | } | |
32 | ||
33 | static int init_version(cls_method_context_t hctx, struct obj_version *objv) | |
34 | { | |
35 | #define TAG_LEN 24 | |
36 | char buf[TAG_LEN + 1]; | |
37 | ||
38 | int ret = cls_gen_rand_base64(buf, sizeof(buf)); | |
39 | if (ret < 0) | |
40 | return ret; | |
41 | ||
42 | objv->ver = 1; | |
43 | objv->tag = buf; | |
44 | ||
45 | CLS_LOG(20, "cls_version: init_version %s:%d", objv->tag.c_str(), (int)objv->ver); | |
46 | ||
47 | return set_version(hctx, objv); | |
48 | } | |
49 | ||
50 | /* implicit create should be true only if called from a write operation (set, inc), never from a read operation (read, check) */ | |
51 | static int read_version(cls_method_context_t hctx, obj_version *objv, bool implicit_create) | |
52 | { | |
53 | bufferlist bl; | |
54 | int ret = cls_cxx_getxattr(hctx, VERSION_ATTR, &bl); | |
55 | if (ret == -ENOENT || ret == -ENODATA) { | |
56 | objv->ver = 0; | |
57 | ||
58 | if (implicit_create) { | |
59 | return init_version(hctx, objv); | |
60 | } | |
61 | return 0; | |
62 | } | |
63 | if (ret < 0) | |
64 | return ret; | |
65 | ||
66 | try { | |
67 | bufferlist::iterator iter = bl.begin(); | |
68 | ::decode(*objv, iter); | |
69 | } catch (buffer::error& err) { | |
70 | CLS_LOG(0, "ERROR: read_version(): failed to decode version entry\n"); | |
71 | return -EIO; | |
72 | } | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | static int cls_version_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
78 | { | |
79 | bufferlist::iterator in_iter = in->begin(); | |
80 | ||
81 | cls_version_set_op op; | |
82 | try { | |
83 | ::decode(op, in_iter); | |
84 | } catch (buffer::error& err) { | |
85 | CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n"); | |
86 | return -EINVAL; | |
87 | } | |
88 | ||
89 | int ret = set_version(hctx, &op.objv); | |
90 | if (ret < 0) | |
91 | return ret; | |
92 | ||
93 | return 0; | |
94 | } | |
95 | ||
96 | static bool check_conds(list<obj_version_cond>& conds, obj_version& objv) | |
97 | { | |
98 | if (conds.empty()) | |
99 | return true; | |
100 | ||
101 | for (list<obj_version_cond>::iterator iter = conds.begin(); iter != conds.end(); ++iter) { | |
102 | obj_version_cond& cond = *iter; | |
103 | obj_version& v = cond.ver; | |
104 | CLS_LOG(20, "cls_version: check_version %s:%d (cond=%d)", v.tag.c_str(), (int)v.ver, (int)cond.cond); | |
105 | ||
106 | switch (cond.cond) { | |
107 | case VER_COND_NONE: | |
108 | break; | |
109 | case VER_COND_EQ: | |
110 | if (!objv.compare(&v)) | |
111 | return false; | |
112 | break; | |
113 | case VER_COND_GT: | |
114 | if (!(objv.ver > v.ver)) | |
115 | return false; | |
116 | break; | |
117 | case VER_COND_GE: | |
118 | if (!(objv.ver >= v.ver)) | |
119 | return false; | |
120 | break; | |
121 | case VER_COND_LT: | |
122 | if (!(objv.ver < v.ver)) | |
123 | return false; | |
124 | break; | |
125 | case VER_COND_LE: | |
126 | if (!(objv.ver <= v.ver)) | |
127 | return false; | |
128 | break; | |
129 | case VER_COND_TAG_EQ: | |
130 | if (objv.tag.compare(v.tag) != 0) | |
131 | return false; | |
132 | break; | |
133 | case VER_COND_TAG_NE: | |
134 | if (objv.tag.compare(v.tag) == 0) | |
135 | return false; | |
136 | break; | |
137 | } | |
138 | } | |
139 | ||
140 | return true; | |
141 | } | |
142 | ||
143 | static int cls_version_inc(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
144 | { | |
145 | bufferlist::iterator in_iter = in->begin(); | |
146 | ||
147 | cls_version_inc_op op; | |
148 | try { | |
149 | ::decode(op, in_iter); | |
150 | } catch (buffer::error& err) { | |
151 | CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n"); | |
152 | return -EINVAL; | |
153 | } | |
154 | ||
155 | obj_version objv; | |
156 | int ret = read_version(hctx, &objv, true); | |
157 | if (ret < 0) | |
158 | return ret; | |
159 | ||
160 | if (!check_conds(op.conds, objv)) { | |
161 | return -ECANCELED; | |
162 | } | |
163 | objv.inc(); | |
164 | ||
165 | ret = set_version(hctx, &objv); | |
166 | if (ret < 0) | |
167 | return ret; | |
168 | ||
169 | return 0; | |
170 | } | |
171 | ||
172 | static int cls_version_check(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
173 | { | |
174 | bufferlist::iterator in_iter = in->begin(); | |
175 | ||
176 | cls_version_check_op op; | |
177 | try { | |
178 | ::decode(op, in_iter); | |
179 | } catch (buffer::error& err) { | |
180 | CLS_LOG(1, "ERROR: cls_version_get(): failed to decode entry\n"); | |
181 | return -EINVAL; | |
182 | } | |
183 | ||
184 | obj_version objv; | |
185 | int ret = read_version(hctx, &objv, false); | |
186 | if (ret < 0) | |
187 | return ret; | |
188 | CLS_LOG(20, "cls_version: read_version %s:%d", objv.tag.c_str(), (int)objv.ver); | |
189 | ||
190 | if (!check_conds(op.conds, objv)) { | |
191 | CLS_LOG(20, "cls_version: failed condition check"); | |
192 | return -ECANCELED; | |
193 | } | |
194 | ||
195 | return 0; | |
196 | } | |
197 | ||
198 | static int cls_version_read(cls_method_context_t hctx, bufferlist *in, bufferlist *out) | |
199 | { | |
200 | obj_version objv; | |
201 | ||
202 | cls_version_read_ret read_ret; | |
203 | int ret = read_version(hctx, &read_ret.objv, false); | |
204 | if (ret < 0) | |
205 | return ret; | |
206 | ||
207 | ::encode(read_ret, *out); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | CLS_INIT(version) | |
213 | { | |
214 | CLS_LOG(1, "Loaded version class!"); | |
215 | ||
216 | cls_handle_t h_class; | |
217 | cls_method_handle_t h_version_set; | |
218 | cls_method_handle_t h_version_inc; | |
219 | cls_method_handle_t h_version_inc_conds; | |
220 | cls_method_handle_t h_version_read; | |
221 | cls_method_handle_t h_version_check_conds; | |
222 | ||
223 | cls_register("version", &h_class); | |
224 | ||
225 | /* version */ | |
226 | cls_register_cxx_method(h_class, "set", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_set, &h_version_set); | |
227 | cls_register_cxx_method(h_class, "inc", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_inc, &h_version_inc); | |
228 | cls_register_cxx_method(h_class, "inc_conds", CLS_METHOD_RD | CLS_METHOD_WR, cls_version_inc, &h_version_inc_conds); | |
229 | cls_register_cxx_method(h_class, "read", CLS_METHOD_RD, cls_version_read, &h_version_read); | |
230 | cls_register_cxx_method(h_class, "check_conds", CLS_METHOD_RD, cls_version_check, &h_version_check_conds); | |
231 | ||
232 | return; | |
233 | } | |
234 |