Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / objclass / class_api.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "common/config.h"
5 #include "common/debug.h"
6
7 #include "objclass/objclass.h"
8 #include "osd/PrimaryLogPG.h"
9 #include "osd/osd_types.h"
10
11 #include "osd/ClassHandler.h"
12
13 #include "auth/Crypto.h"
14 #include "common/armor.h"
15
16 static constexpr int dout_subsys = ceph_subsys_objclass;
17
18 static ClassHandler *ch;
19
20 void cls_initialize(ClassHandler *h)
21 {
22   ch = h;
23 }
24
25 void cls_finalize()
26 {
27   ch = NULL;
28 }
29
30
31 void *cls_alloc(size_t size)
32 {
33   return malloc(size);
34 }
35
36 void cls_free(void *p)
37 {
38   free(p);
39 }
40
41 int cls_register(const char *name, cls_handle_t *handle)
42 {
43   ClassHandler::ClassData *cls = ch->register_class(name);
44   *handle = (cls_handle_t)cls;
45   return (cls != NULL);
46 }
47
48 int cls_unregister(cls_handle_t handle)
49 {
50   ClassHandler::ClassData *cls = (ClassHandler::ClassData *)handle;
51   ch->unregister_class(cls);
52   return 1;
53 }
54
55 int cls_register_method(cls_handle_t hclass, const char *method,
56                         int flags,
57                         cls_method_call_t class_call, cls_method_handle_t *handle)
58 {
59   if (!(flags & (CLS_METHOD_RD | CLS_METHOD_WR)))
60     return -EINVAL;
61   ClassHandler::ClassData *cls = (ClassHandler::ClassData *)hclass;
62   cls_method_handle_t hmethod =(cls_method_handle_t)cls->register_method(method, flags, class_call);
63   if (handle)
64     *handle = hmethod;
65   return (hmethod != NULL);
66 }
67
68 int cls_register_cxx_method(cls_handle_t hclass, const char *method,
69                             int flags,
70                             cls_method_cxx_call_t class_call, cls_method_handle_t *handle)
71 {
72   ClassHandler::ClassData *cls = (ClassHandler::ClassData *)hclass;
73   cls_method_handle_t hmethod = (cls_method_handle_t)cls->register_cxx_method(method, flags, class_call);
74   if (handle)
75     *handle = hmethod;
76   return (hmethod != NULL);
77 }
78
79 int cls_unregister_method(cls_method_handle_t handle)
80 {
81   ClassHandler::ClassMethod *method = (ClassHandler::ClassMethod *)handle;
82   method->unregister();
83   return 1;
84 }
85
86 int cls_register_cxx_filter(cls_handle_t hclass,
87                             const std::string &filter_name,
88                             cls_cxx_filter_factory_t fn,
89                             cls_filter_handle_t *handle)
90 {
91   ClassHandler::ClassData *cls = (ClassHandler::ClassData *)hclass;
92   cls_filter_handle_t hfilter = (cls_filter_handle_t)cls->register_cxx_filter(filter_name, fn);
93   if (handle) {
94     *handle = hfilter;
95   }
96   return (hfilter != NULL);
97 }
98
99 void cls_unregister_filter(cls_filter_handle_t handle)
100 {
101   ClassHandler::ClassFilter *filter = (ClassHandler::ClassFilter *)handle;
102   filter->unregister();
103 }
104
105 int cls_call(cls_method_context_t hctx, const char *cls, const char *method,
106                                  char *indata, int datalen,
107                                  char **outdata, int *outdatalen)
108 {
109   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
110   bufferlist idata;
111   vector<OSDOp> nops(1);
112   OSDOp& op = nops[0];
113   int r;
114
115   op.op.op = CEPH_OSD_OP_CALL;
116   op.op.cls.class_len = strlen(cls);
117   op.op.cls.method_len = strlen(method);
118   op.op.cls.indata_len = datalen;
119   op.indata.append(cls, op.op.cls.class_len);
120   op.indata.append(method, op.op.cls.method_len);
121   op.indata.append(indata, datalen);
122   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
123   if (r < 0)
124     return r;
125
126   *outdata = (char *)malloc(op.outdata.length());
127   if (!*outdata)
128     return -ENOMEM;
129   memcpy(*outdata, op.outdata.c_str(), op.outdata.length());
130   *outdatalen = op.outdata.length();
131
132   return r;
133 }
134
135 int cls_getxattr(cls_method_context_t hctx, const char *name,
136                                  char **outdata, int *outdatalen)
137 {
138   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
139   bufferlist name_data;
140   vector<OSDOp> nops(1);
141   OSDOp& op = nops[0];
142   int r;
143
144   op.op.op = CEPH_OSD_OP_GETXATTR;
145   op.indata.append(name);
146   op.op.xattr.name_len = strlen(name);
147   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
148   if (r < 0)
149     return r;
150
151   *outdata = (char *)malloc(op.outdata.length());
152   if (!*outdata)
153     return -ENOMEM;
154   memcpy(*outdata, op.outdata.c_str(), op.outdata.length());
155   *outdatalen = op.outdata.length();
156
157   return r;
158 }
159
160 int cls_setxattr(cls_method_context_t hctx, const char *name,
161                                  const char *value, int val_len)
162 {
163   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
164   bufferlist name_data;
165   vector<OSDOp> nops(1);
166   OSDOp& op = nops[0];
167   int r;
168
169   op.op.op = CEPH_OSD_OP_SETXATTR;
170   op.indata.append(name);
171   op.indata.append(value);
172   op.op.xattr.name_len = strlen(name);
173   op.op.xattr.value_len = val_len;
174   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
175
176   return r;
177 }
178
179 int cls_read(cls_method_context_t hctx, int ofs, int len,
180                                  char **outdata, int *outdatalen)
181 {
182   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
183   vector<OSDOp> ops(1);
184   ops[0].op.op = CEPH_OSD_OP_SYNC_READ;
185   ops[0].op.extent.offset = ofs;
186   ops[0].op.extent.length = len;
187   int r = (*pctx)->pg->do_osd_ops(*pctx, ops);
188   if (r < 0)
189     return r;
190
191   *outdata = (char *)malloc(ops[0].outdata.length());
192   if (!*outdata)
193     return -ENOMEM;
194   memcpy(*outdata, ops[0].outdata.c_str(), ops[0].outdata.length());
195   *outdatalen = ops[0].outdata.length();
196
197   return *outdatalen;
198 }
199
200 int cls_get_request_origin(cls_method_context_t hctx, entity_inst_t *origin)
201 {
202   PrimaryLogPG::OpContext **pctx = static_cast<PrimaryLogPG::OpContext **>(hctx);
203   *origin = (*pctx)->op->get_req()->get_orig_source_inst();
204   return 0;
205 }
206
207 int cls_cxx_create(cls_method_context_t hctx, bool exclusive)
208 {
209   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
210   vector<OSDOp> ops(1);
211   ops[0].op.op = CEPH_OSD_OP_CREATE;
212   ops[0].op.flags = (exclusive ? CEPH_OSD_OP_FLAG_EXCL : 0);
213   return (*pctx)->pg->do_osd_ops(*pctx, ops);
214 }
215
216 int cls_cxx_remove(cls_method_context_t hctx)
217 {
218   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
219   vector<OSDOp> ops(1);
220   ops[0].op.op = CEPH_OSD_OP_DELETE;
221   return (*pctx)->pg->do_osd_ops(*pctx, ops);
222 }
223
224 int cls_cxx_stat(cls_method_context_t hctx, uint64_t *size, time_t *mtime)
225 {
226   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
227   vector<OSDOp> ops(1);
228   int ret;
229   ops[0].op.op = CEPH_OSD_OP_STAT;
230   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
231   if (ret < 0)
232     return ret;
233   bufferlist::iterator iter = ops[0].outdata.begin();
234   utime_t ut;
235   uint64_t s;
236   try {
237     ::decode(s, iter);
238     ::decode(ut, iter);
239   } catch (buffer::error& err) {
240     return -EIO;
241   }
242   if (size)
243     *size = s;
244   if (mtime)
245     *mtime = ut.sec();
246   return 0;
247 }
248
249 int cls_cxx_stat2(cls_method_context_t hctx, uint64_t *size, ceph::real_time *mtime)
250 {
251   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
252   vector<OSDOp> ops(1);
253   int ret;
254   ops[0].op.op = CEPH_OSD_OP_STAT;
255   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
256   if (ret < 0)
257     return ret;
258   bufferlist::iterator iter = ops[0].outdata.begin();
259   real_time ut;
260   uint64_t s;
261   try {
262     ::decode(s, iter);
263     ::decode(ut, iter);
264   } catch (buffer::error& err) {
265     return -EIO;
266   }
267   if (size)
268     *size = s;
269   if (mtime)
270     *mtime = ut;
271   return 0;
272 }
273
274 int cls_cxx_read(cls_method_context_t hctx, int ofs, int len, bufferlist *outbl)
275 {
276   return cls_cxx_read2(hctx, ofs, len, outbl, 0);
277 }
278
279 int cls_cxx_read2(cls_method_context_t hctx, int ofs, int len,
280                   bufferlist *outbl, uint32_t op_flags)
281 {
282   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
283   vector<OSDOp> ops(1);
284   int ret;
285   ops[0].op.op = CEPH_OSD_OP_SYNC_READ;
286   ops[0].op.extent.offset = ofs;
287   ops[0].op.extent.length = len;
288   ops[0].op.flags = op_flags;
289   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
290   if (ret < 0)
291     return ret;
292   outbl->claim(ops[0].outdata);
293   return outbl->length();
294 }
295
296 int cls_cxx_write(cls_method_context_t hctx, int ofs, int len, bufferlist *inbl)
297 {
298   return cls_cxx_write2(hctx, ofs, len, inbl, 0);
299 }
300
301 int cls_cxx_write2(cls_method_context_t hctx, int ofs, int len,
302                    bufferlist *inbl, uint32_t op_flags)
303 {
304   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
305   vector<OSDOp> ops(1);
306   ops[0].op.op = CEPH_OSD_OP_WRITE;
307   ops[0].op.extent.offset = ofs;
308   ops[0].op.extent.length = len;
309   ops[0].op.flags = op_flags;
310   ops[0].indata = *inbl;
311   return (*pctx)->pg->do_osd_ops(*pctx, ops);
312 }
313
314 int cls_cxx_write_full(cls_method_context_t hctx, bufferlist *inbl)
315 {
316   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
317   vector<OSDOp> ops(1);
318   ops[0].op.op = CEPH_OSD_OP_WRITEFULL;
319   ops[0].op.extent.offset = 0;
320   ops[0].op.extent.length = inbl->length();
321   ops[0].indata = *inbl;
322   return (*pctx)->pg->do_osd_ops(*pctx, ops);
323 }
324
325 int cls_cxx_replace(cls_method_context_t hctx, int ofs, int len, bufferlist *inbl)
326 {
327   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
328   vector<OSDOp> ops(2);
329   ops[0].op.op = CEPH_OSD_OP_TRUNCATE;
330   ops[0].op.extent.offset = 0;
331   ops[0].op.extent.length = 0;
332   ops[1].op.op = CEPH_OSD_OP_WRITE;
333   ops[1].op.extent.offset = ofs;
334   ops[1].op.extent.length = len;
335   ops[1].indata = *inbl;
336   return (*pctx)->pg->do_osd_ops(*pctx, ops);
337 }
338
339 int cls_cxx_getxattr(cls_method_context_t hctx, const char *name,
340                      bufferlist *outbl)
341 {
342   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
343   bufferlist name_data;
344   vector<OSDOp> nops(1);
345   OSDOp& op = nops[0];
346   int r;
347
348   op.op.op = CEPH_OSD_OP_GETXATTR;
349   op.indata.append(name);
350   op.op.xattr.name_len = strlen(name);
351   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
352   if (r < 0)
353     return r;
354
355   outbl->claim(op.outdata);
356   return outbl->length();
357 }
358
359 int cls_cxx_getxattrs(cls_method_context_t hctx, map<string, bufferlist> *attrset)
360 {
361   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
362   vector<OSDOp> nops(1);
363   OSDOp& op = nops[0];
364   int r;
365
366   op.op.op = CEPH_OSD_OP_GETXATTRS;
367   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
368   if (r < 0)
369     return r;
370
371   bufferlist::iterator iter = op.outdata.begin();
372   try {
373     ::decode(*attrset, iter);
374   } catch (buffer::error& err) {
375     return -EIO;
376   }
377   return 0;
378 }
379
380 int cls_cxx_setxattr(cls_method_context_t hctx, const char *name,
381                      bufferlist *inbl)
382 {
383   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
384   bufferlist name_data;
385   vector<OSDOp> nops(1);
386   OSDOp& op = nops[0];
387   int r;
388
389   op.op.op = CEPH_OSD_OP_SETXATTR;
390   op.indata.append(name);
391   op.indata.append(*inbl);
392   op.op.xattr.name_len = strlen(name);
393   op.op.xattr.value_len = inbl->length();
394   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
395
396   return r;
397 }
398
399 int cls_cxx_snap_revert(cls_method_context_t hctx, snapid_t snapid)
400 {
401   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
402   vector<OSDOp> ops(1);
403   ops[0].op.op = CEPH_OSD_OP_ROLLBACK;
404   ops[0].op.snap.snapid = snapid;
405   return (*pctx)->pg->do_osd_ops(*pctx, ops);
406 }
407
408 int cls_cxx_map_get_all_vals(cls_method_context_t hctx, map<string, bufferlist>* vals,
409                              bool *more)
410 {
411   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
412   vector<OSDOp> ops(1);
413   OSDOp& op = ops[0];
414   int ret;
415
416   string start_after;
417   string filter_prefix;
418   uint64_t max = (uint64_t)-1;
419
420   ::encode(start_after, op.indata);
421   ::encode(max, op.indata);
422   ::encode(filter_prefix, op.indata);
423
424   op.op.op = CEPH_OSD_OP_OMAPGETVALS;
425   
426   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
427   if (ret < 0)
428     return ret;
429
430   bufferlist::iterator iter = op.outdata.begin();
431   try {
432     ::decode(*vals, iter);
433     ::decode(*more, iter);
434   } catch (buffer::error& err) {
435     return -EIO;
436   }
437   return vals->size();
438 }
439
440 int cls_cxx_map_get_keys(cls_method_context_t hctx, const string &start_obj,
441                          uint64_t max_to_get, set<string> *keys,
442                          bool *more)
443 {
444   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
445   vector<OSDOp> ops(1);
446   OSDOp& op = ops[0];
447   int ret;
448
449   ::encode(start_obj, op.indata);
450   ::encode(max_to_get, op.indata);
451
452   op.op.op = CEPH_OSD_OP_OMAPGETKEYS;
453
454   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
455   if (ret < 0)
456     return ret;
457
458   bufferlist::iterator iter = op.outdata.begin();
459   try {
460     ::decode(*keys, iter);
461     ::decode(*more, iter);
462   } catch (buffer::error& err) {
463     return -EIO;
464   }
465   return keys->size();
466 }
467
468 int cls_cxx_map_get_vals(cls_method_context_t hctx, const string &start_obj,
469                          const string &filter_prefix, uint64_t max_to_get,
470                          map<string, bufferlist> *vals, bool *more)
471 {
472   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
473   vector<OSDOp> ops(1);
474   OSDOp& op = ops[0];
475   int ret;
476
477   ::encode(start_obj, op.indata);
478   ::encode(max_to_get, op.indata);
479   ::encode(filter_prefix, op.indata);
480
481   op.op.op = CEPH_OSD_OP_OMAPGETVALS;
482   
483   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
484   if (ret < 0)
485     return ret;
486
487   bufferlist::iterator iter = op.outdata.begin();
488   try {
489     ::decode(*vals, iter);
490     ::decode(*more, iter);
491   } catch (buffer::error& err) {
492     return -EIO;
493   }
494   return vals->size();
495 }
496
497 int cls_cxx_map_read_header(cls_method_context_t hctx, bufferlist *outbl)
498 {
499   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
500   vector<OSDOp> ops(1);
501   OSDOp& op = ops[0];
502   int ret;
503   op.op.op = CEPH_OSD_OP_OMAPGETHEADER;
504   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
505   if (ret < 0)
506     return ret;
507
508   outbl->claim(op.outdata);
509
510   return 0;
511 }
512
513 int cls_cxx_map_get_val(cls_method_context_t hctx, const string &key,
514                         bufferlist *outbl)
515 {
516   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
517   vector<OSDOp> ops(1);
518   OSDOp& op = ops[0];
519   int ret;
520
521   set<string> k;
522   k.insert(key);
523   ::encode(k, op.indata);
524
525   op.op.op = CEPH_OSD_OP_OMAPGETVALSBYKEYS;
526   ret = (*pctx)->pg->do_osd_ops(*pctx, ops);
527   if (ret < 0)
528     return ret;
529
530   bufferlist::iterator iter = op.outdata.begin();
531   try {
532     map<string, bufferlist> m;
533
534     ::decode(m, iter);
535     map<string, bufferlist>::iterator iter = m.begin();
536     if (iter == m.end())
537       return -ENOENT;
538
539     *outbl = iter->second;
540   } catch (buffer::error& e) {
541     return -EIO;
542   }
543   return 0;
544 }
545
546 int cls_cxx_map_set_val(cls_method_context_t hctx, const string &key,
547                         bufferlist *inbl)
548 {
549   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
550   vector<OSDOp> ops(1);
551   OSDOp& op = ops[0];
552   bufferlist& update_bl = op.indata;
553   map<string, bufferlist> m;
554   m[key] = *inbl;
555   ::encode(m, update_bl);
556
557   op.op.op = CEPH_OSD_OP_OMAPSETVALS;
558
559   return (*pctx)->pg->do_osd_ops(*pctx, ops);
560 }
561
562 int cls_cxx_map_set_vals(cls_method_context_t hctx,
563                          const std::map<string, bufferlist> *map)
564 {
565   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
566   vector<OSDOp> ops(1);
567   OSDOp& op = ops[0];
568   bufferlist& update_bl = op.indata;
569   ::encode(*map, update_bl);
570
571   op.op.op = CEPH_OSD_OP_OMAPSETVALS;
572
573   return (*pctx)->pg->do_osd_ops(*pctx, ops);
574 }
575
576 int cls_cxx_map_clear(cls_method_context_t hctx)
577 {
578   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
579   vector<OSDOp> ops(1);
580   OSDOp& op = ops[0];
581
582   op.op.op = CEPH_OSD_OP_OMAPCLEAR;
583
584   return (*pctx)->pg->do_osd_ops(*pctx, ops);
585 }
586
587 int cls_cxx_map_write_header(cls_method_context_t hctx, bufferlist *inbl)
588 {
589   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
590   vector<OSDOp> ops(1);
591   OSDOp& op = ops[0];
592   op.indata.claim(*inbl);
593
594   op.op.op = CEPH_OSD_OP_OMAPSETHEADER;
595
596   return (*pctx)->pg->do_osd_ops(*pctx, ops);
597 }
598
599 int cls_cxx_map_remove_key(cls_method_context_t hctx, const string &key)
600 {
601   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
602   vector<OSDOp> ops(1);
603   OSDOp& op = ops[0];
604   bufferlist& update_bl = op.indata;
605   set<string> to_rm;
606   to_rm.insert(key);
607
608   ::encode(to_rm, update_bl);
609
610   op.op.op = CEPH_OSD_OP_OMAPRMKEYS;
611
612   return (*pctx)->pg->do_osd_ops(*pctx, ops);
613 }
614
615 int cls_cxx_list_watchers(cls_method_context_t hctx,
616                           obj_list_watch_response_t *watchers)
617 {
618   PrimaryLogPG::OpContext **pctx = (PrimaryLogPG::OpContext **)hctx;
619   vector<OSDOp> nops(1);
620   OSDOp& op = nops[0];
621   int r;
622
623   op.op.op = CEPH_OSD_OP_LIST_WATCHERS;
624   r = (*pctx)->pg->do_osd_ops(*pctx, nops);
625   if (r < 0)
626     return r;
627
628   bufferlist::iterator iter = op.outdata.begin();
629   try {
630     ::decode(*watchers, iter);
631   } catch (buffer::error& err) {
632     return -EIO;
633   }
634   return 0;
635 }
636
637 int cls_gen_random_bytes(char *buf, int size)
638 {
639   return get_random_bytes(buf, size);
640 }
641
642 int cls_gen_rand_base64(char *dest, int size) /* size should be the required string size + 1 */
643 {
644   char buf[size];
645   char tmp_dest[size + 4]; /* so that there's space for the extra '=' characters, and some */
646   int ret;
647
648   ret = cls_gen_random_bytes(buf, sizeof(buf));
649   if (ret < 0) {
650     lgeneric_derr(ch->cct) << "cannot get random bytes: " << ret << dendl;
651     return -1;
652   }
653
654   ret = ceph_armor(tmp_dest, &tmp_dest[sizeof(tmp_dest)],
655                    (const char *)buf, ((const char *)buf) + ((size - 1) * 3 + 4 - 1) / 4);
656   if (ret < 0) {
657     lgeneric_derr(ch->cct) << "ceph_armor failed" << dendl;
658     return -1;
659   }
660   tmp_dest[ret] = '\0';
661   memcpy(dest, tmp_dest, size);
662   dest[size-1] = '\0';
663
664   return 0;
665 }
666
667 uint64_t cls_current_version(cls_method_context_t hctx)
668 {
669   PrimaryLogPG::OpContext *ctx = *(PrimaryLogPG::OpContext **)hctx;
670
671   return ctx->pg->info.last_user_version;
672 }
673
674
675 int cls_current_subop_num(cls_method_context_t hctx)
676 {
677   PrimaryLogPG::OpContext *ctx = *(PrimaryLogPG::OpContext **)hctx;
678
679   return ctx->current_osd_subop_num;
680 }
681
682 uint64_t cls_get_features(cls_method_context_t hctx)
683 {
684   PrimaryLogPG::OpContext *ctx = *(PrimaryLogPG::OpContext **)hctx;
685   return ctx->pg->get_osdmap()->get_up_osd_features();
686 }
687
688 uint64_t cls_get_client_features(cls_method_context_t hctx)
689 {
690   PrimaryLogPG::OpContext *ctx = *(PrimaryLogPG::OpContext **)hctx;
691   return ctx->op->get_req()->get_connection()->get_features();
692 }
693
694 void cls_cxx_subop_version(cls_method_context_t hctx, string *s)
695 {
696   if (!s)
697     return;
698
699   char buf[32];
700   uint64_t ver = cls_current_version(hctx);
701   int subop_num = cls_current_subop_num(hctx);
702   snprintf(buf, sizeof(buf), "%lld.%d", (long long)ver, subop_num);
703
704   *s = buf;
705 }
706
707 int cls_log(int level, const char *format, ...)
708 {
709    int size = 256;
710    va_list ap;
711    while (1) {
712      char buf[size];
713      va_start(ap, format);
714      int n = vsnprintf(buf, size, format, ap);
715      va_end(ap);
716 #define MAX_SIZE 8196
717      if ((n > -1 && n < size) || size > MAX_SIZE) {
718        ldout(ch->cct, level) << buf << dendl;
719        return n;
720      }
721      size *= 2;
722    }
723 }