]> git.proxmox.com Git - ceph.git/blame - ceph/src/common/hobject.cc
update sources to v12.1.0
[ceph.git] / ceph / src / common / hobject.cc
CommitLineData
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 "hobject.h"
5#include "common/Formatter.h"
6
7static void append_escaped(const string &in, string *out)
8{
9 for (string::const_iterator i = in.begin(); i != in.end(); ++i) {
10 if (*i == '%') {
11 out->push_back('%');
12 out->push_back('p');
13 } else if (*i == '.') {
14 out->push_back('%');
15 out->push_back('e');
16 } else if (*i == '_') {
17 out->push_back('%');
18 out->push_back('u');
19 } else {
20 out->push_back(*i);
21 }
22 }
23}
24
25set<string> hobject_t::get_prefixes(
26 uint32_t bits,
27 uint32_t mask,
28 int64_t pool)
29{
30 uint32_t len = bits;
31 while (len % 4 /* nibbles */) len++;
32
33 set<uint32_t> from;
34 if (bits < 32)
35 from.insert(mask & ~((uint32_t)(~0) << bits));
36 else if (bits == 32)
37 from.insert(mask);
38 else
39 ceph_abort();
40
41
42 set<uint32_t> to;
43 for (uint32_t i = bits; i < len; ++i) {
44 for (set<uint32_t>::iterator j = from.begin();
45 j != from.end();
46 ++j) {
47 to.insert(*j | (1U << i));
48 to.insert(*j);
49 }
50 to.swap(from);
51 to.clear();
52 }
53
54 char buf[20];
55 char *t = buf;
56 uint64_t poolid(pool);
57 t += snprintf(t, sizeof(buf), "%.*llX", 16, (long long unsigned)poolid);
58 *(t++) = '.';
59 string poolstr(buf, t - buf);
60 set<string> ret;
61 for (set<uint32_t>::iterator i = from.begin();
62 i != from.end();
63 ++i) {
64 uint32_t revhash(hobject_t::_reverse_nibbles(*i));
65 snprintf(buf, sizeof(buf), "%.*X", (int)(sizeof(revhash))*2, revhash);
66 ret.insert(poolstr + string(buf, len/4));
67 }
68 return ret;
69}
70
71string hobject_t::to_str() const
72{
73 string out;
74
75 char snap_with_hash[1000];
76 char *t = snap_with_hash;
77 const char *end = t + sizeof(snap_with_hash);
78
79 uint64_t poolid(pool);
80 t += snprintf(t, end - t, "%.*llX", 16, (long long unsigned)poolid);
81
82 uint32_t revhash(get_nibblewise_key_u32());
83 t += snprintf(t, end - t, ".%.*X", 8, revhash);
84
85 if (snap == CEPH_NOSNAP)
86 t += snprintf(t, end - t, ".head");
87 else if (snap == CEPH_SNAPDIR)
88 t += snprintf(t, end - t, ".snapdir");
89 else
90 t += snprintf(t, end - t, ".%llx", (long long unsigned)snap);
91
92 out.append(snap_with_hash, t);
93
94 out.push_back('.');
95 append_escaped(oid.name, &out);
96 out.push_back('.');
97 append_escaped(get_key(), &out);
98 out.push_back('.');
99 append_escaped(nspace, &out);
100
101 return out;
102}
103
104void hobject_t::encode(bufferlist& bl) const
105{
106 ENCODE_START(4, 3, bl);
107 ::encode(key, bl);
108 ::encode(oid, bl);
109 ::encode(snap, bl);
110 ::encode(hash, bl);
111 ::encode(max, bl);
112 ::encode(nspace, bl);
113 ::encode(pool, bl);
114 assert(!max || (*this == hobject_t(hobject_t::get_max())));
115 ENCODE_FINISH(bl);
116}
117
118void hobject_t::decode(bufferlist::iterator& bl)
119{
120 DECODE_START_LEGACY_COMPAT_LEN(4, 3, 3, bl);
121 if (struct_v >= 1)
122 ::decode(key, bl);
123 ::decode(oid, bl);
124 ::decode(snap, bl);
125 ::decode(hash, bl);
126 if (struct_v >= 2)
127 ::decode(max, bl);
128 else
129 max = false;
130 if (struct_v >= 4) {
131 ::decode(nspace, bl);
132 ::decode(pool, bl);
133 // for compat with hammer, which did not handle the transition
134 // from pool -1 -> pool INT64_MIN for MIN properly. this object
135 // name looks a bit like a pgmeta object for the meta collection,
136 // but those do not ever exist (and is_pgmeta() pool >= 0).
137 if (pool == -1 &&
138 snap == 0 &&
139 hash == 0 &&
140 !max &&
141 oid.name.empty()) {
142 pool = INT64_MIN;
143 assert(is_min());
144 }
145
146 // for compatibility with some earlier verisons which might encoded
147 // a non-canonical max object
148 if (max) {
149 *this = hobject_t::get_max();
150 }
151 }
152 DECODE_FINISH(bl);
153 build_hash_cache();
154}
155
156void hobject_t::decode(json_spirit::Value& v)
157{
158 using namespace json_spirit;
159 Object& o = v.get_obj();
160 for (Object::size_type i=0; i<o.size(); i++) {
161 Pair& p = o[i];
162 if (p.name_ == "oid")
163 oid.name = p.value_.get_str();
164 else if (p.name_ == "key")
165 key = p.value_.get_str();
166 else if (p.name_ == "snapid")
167 snap = p.value_.get_uint64();
168 else if (p.name_ == "hash")
169 hash = p.value_.get_int();
170 else if (p.name_ == "max")
171 max = p.value_.get_int();
172 else if (p.name_ == "pool")
173 pool = p.value_.get_int();
174 else if (p.name_ == "namespace")
175 nspace = p.value_.get_str();
176 }
177 build_hash_cache();
178}
179
180void hobject_t::dump(Formatter *f) const
181{
182 f->dump_string("oid", oid.name);
183 f->dump_string("key", key);
184 f->dump_int("snapid", snap);
185 f->dump_int("hash", hash);
186 f->dump_int("max", (int)max);
187 f->dump_int("pool", pool);
188 f->dump_string("namespace", nspace);
189}
190
191void hobject_t::generate_test_instances(list<hobject_t*>& o)
192{
193 o.push_back(new hobject_t);
194 o.push_back(new hobject_t);
195 o.back()->max = true;
196 o.push_back(new hobject_t(object_t("oname"), string(), 1, 234, -1, ""));
197 o.push_back(new hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
198 67, 0, "n1"));
199 o.push_back(new hobject_t(object_t("oname3"), string("oname3"),
200 CEPH_SNAPDIR, 910, 1, "n2"));
201}
202
203static void append_out_escaped(const string &in, string *out)
204{
205 for (string::const_iterator i = in.begin(); i != in.end(); ++i) {
206 if (*i == '%' || *i == ':' || *i == '/' || *i < 32 || *i >= 127) {
207 out->push_back('%');
208 char buf[3];
209 snprintf(buf, sizeof(buf), "%02x", (int)(unsigned char)*i);
210 out->append(buf);
211 } else {
212 out->push_back(*i);
213 }
214 }
215}
216
217static const char *decode_out_escaped(const char *in, string *out)
218{
219 while (*in && *in != ':') {
220 if (*in == '%') {
221 ++in;
222 char buf[3];
223 buf[0] = *in;
224 ++in;
225 buf[1] = *in;
226 buf[2] = 0;
227 int v = strtol(buf, NULL, 16);
228 out->push_back(v);
229 } else {
230 out->push_back(*in);
231 }
232 ++in;
233 }
234 return in;
235}
236
237ostream& operator<<(ostream& out, const hobject_t& o)
238{
239 if (o == hobject_t())
240 return out << "MIN";
241 if (o.is_max())
242 return out << "MAX";
243 out << o.pool << ':';
244 out << std::hex;
245 out.width(8);
246 out.fill('0');
247 out << o.get_bitwise_key_u32(); // << '~' << o.get_hash();
248 out.width(0);
249 out.fill(' ');
250 out << std::dec;
251 out << ':';
252 string v;
253 append_out_escaped(o.nspace, &v);
254 v.push_back(':');
255 append_out_escaped(o.get_key(), &v);
256 v.push_back(':');
257 append_out_escaped(o.oid.name, &v);
258 out << v << ':' << o.snap;
259 return out;
260}
261
262bool hobject_t::parse(const string &s)
263{
264 if (s == "MIN") {
265 *this = hobject_t();
266 return true;
267 }
268 if (s == "MAX") {
269 *this = hobject_t::get_max();
270 return true;
271 }
272
273 const char *start = s.c_str();
274 long long po;
275 unsigned h;
276 int r = sscanf(start, "%lld:%x:", &po, &h);
277 if (r != 2)
278 return false;
279 for (; *start && *start != ':'; ++start) ;
280 for (++start; *start && isxdigit(*start); ++start) ;
281 if (*start != ':')
282 return false;
283
284 string ns, k, name;
285 const char *p = decode_out_escaped(start + 1, &ns);
286 if (*p != ':')
287 return false;
288 p = decode_out_escaped(p + 1, &k);
289 if (*p != ':')
290 return false;
291 p = decode_out_escaped(p + 1, &name);
292 if (*p != ':')
293 return false;
294 start = p + 1;
295
296 unsigned long long sn;
297 if (strncmp(start, "head", 4) == 0) {
298 sn = CEPH_NOSNAP;
299 start += 4;
300 if (*start != 0)
301 return false;
302 } else {
303 r = sscanf(start, "%llx", &sn);
304 if (r != 1)
305 return false;
306 for (++start; *start && isxdigit(*start); ++start) ;
307 if (*start)
308 return false;
309 }
310
311 max = false;
312 pool = po;
313 set_hash(_reverse_bits(h));
314 nspace = ns;
315 oid.name = name;
316 set_key(k);
317 snap = sn;
318 return true;
319}
320
321int cmp(const hobject_t& l, const hobject_t& r)
322{
323 if (l.max < r.max)
324 return -1;
325 if (l.max > r.max)
326 return 1;
327 if (l.pool < r.pool)
328 return -1;
329 if (l.pool > r.pool)
330 return 1;
331 if (l.get_bitwise_key() < r.get_bitwise_key())
332 return -1;
333 if (l.get_bitwise_key() > r.get_bitwise_key())
334 return 1;
335 if (l.nspace < r.nspace)
336 return -1;
337 if (l.nspace > r.nspace)
338 return 1;
339 if (l.get_effective_key() < r.get_effective_key())
340 return -1;
341 if (l.get_effective_key() > r.get_effective_key())
342 return 1;
343 if (l.oid < r.oid)
344 return -1;
345 if (l.oid > r.oid)
346 return 1;
347 if (l.snap < r.snap)
348 return -1;
349 if (l.snap > r.snap)
350 return 1;
351 return 0;
352}
353
354
355
356// This is compatible with decode for hobject_t prior to
357// version 5.
358void ghobject_t::encode(bufferlist& bl) const
359{
360 // when changing this, remember to update encoded_size() too.
361 ENCODE_START(6, 3, bl);
362 ::encode(hobj.key, bl);
363 ::encode(hobj.oid, bl);
364 ::encode(hobj.snap, bl);
365 ::encode(hobj.hash, bl);
366 ::encode(hobj.max, bl);
367 ::encode(hobj.nspace, bl);
368 ::encode(hobj.pool, bl);
369 ::encode(generation, bl);
370 ::encode(shard_id, bl);
371 ::encode(max, bl);
372 ENCODE_FINISH(bl);
373}
374
375size_t ghobject_t::encoded_size() const
376{
377 // this is not in order of encoding or appearance, but rather
378 // in order of known constants first, so it can be (mostly) computed
379 // at compile time.
380 // - encoding header + 3 string lengths
381 size_t r = sizeof(ceph_le32) + 2 * sizeof(__u8) + 3 * sizeof(__u32);
382
383 // hobj.snap
384 r += sizeof(uint64_t);
385
386 // hobj.hash
387 r += sizeof(uint32_t);
388
389 // hobj.max
390 r += sizeof(bool);
391
392 // hobj.pool
393 r += sizeof(uint64_t);
394
395 // hobj.generation
396 r += sizeof(uint64_t);
397
398 // hobj.shard_id
399 r += sizeof(int8_t);
400
401 // max
402 r += sizeof(bool);
403
404 // hobj.key
405 r += hobj.key.size();
406
407 // hobj.oid
408 r += hobj.oid.name.size();
409
410 // hobj.nspace
411 r += hobj.nspace.size();
412
413 return r;
414}
415
416void ghobject_t::decode(bufferlist::iterator& bl)
417{
418 DECODE_START_LEGACY_COMPAT_LEN(6, 3, 3, bl);
419 if (struct_v >= 1)
420 ::decode(hobj.key, bl);
421 ::decode(hobj.oid, bl);
422 ::decode(hobj.snap, bl);
423 ::decode(hobj.hash, bl);
424 if (struct_v >= 2)
425 ::decode(hobj.max, bl);
426 else
427 hobj.max = false;
428 if (struct_v >= 4) {
429 ::decode(hobj.nspace, bl);
430 ::decode(hobj.pool, bl);
431 // for compat with hammer, which did not handle the transition from
432 // pool -1 -> pool INT64_MIN for MIN properly (see hobject_t::decode()).
433 if (hobj.pool == -1 &&
434 hobj.snap == 0 &&
435 hobj.hash == 0 &&
436 !hobj.max &&
437 hobj.oid.name.empty()) {
438 hobj.pool = INT64_MIN;
439 assert(hobj.is_min());
440 }
441 }
442 if (struct_v >= 5) {
443 ::decode(generation, bl);
444 ::decode(shard_id, bl);
445 } else {
446 generation = ghobject_t::NO_GEN;
447 shard_id = shard_id_t::NO_SHARD;
448 }
449 if (struct_v >= 6) {
450 ::decode(max, bl);
451 } else {
452 max = false;
453 }
454 DECODE_FINISH(bl);
455 hobj.build_hash_cache();
456}
457
458void ghobject_t::decode(json_spirit::Value& v)
459{
460 hobj.decode(v);
461 using namespace json_spirit;
462 Object& o = v.get_obj();
463 for (Object::size_type i=0; i<o.size(); i++) {
464 Pair& p = o[i];
465 if (p.name_ == "generation")
466 generation = p.value_.get_uint64();
467 else if (p.name_ == "shard_id")
468 shard_id.id = p.value_.get_int();
469 else if (p.name_ == "max")
470 max = p.value_.get_int();
471 }
472}
473
474void ghobject_t::dump(Formatter *f) const
475{
476 hobj.dump(f);
477 if (generation != NO_GEN)
478 f->dump_int("generation", generation);
479 if (shard_id != shard_id_t::NO_SHARD)
480 f->dump_int("shard_id", shard_id);
481 f->dump_int("max", (int)max);
482}
483
484void ghobject_t::generate_test_instances(list<ghobject_t*>& o)
485{
486 o.push_back(new ghobject_t);
487 o.push_back(new ghobject_t);
488 o.back()->hobj.max = true;
489 o.push_back(new ghobject_t(hobject_t(object_t("oname"), string(), 1, 234, -1, "")));
490
491 o.push_back(new ghobject_t(hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
492 67, 0, "n1"), 1, shard_id_t(0)));
493 o.push_back(new ghobject_t(hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
494 67, 0, "n1"), 1, shard_id_t(1)));
495 o.push_back(new ghobject_t(hobject_t(object_t("oname2"), string("okey"), CEPH_NOSNAP,
496 67, 0, "n1"), 1, shard_id_t(2)));
497 o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
498 CEPH_SNAPDIR, 910, 1, "n2"), 1, shard_id_t(0)));
499 o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
500 CEPH_SNAPDIR, 910, 1, "n2"), 2, shard_id_t(0)));
501 o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
502 CEPH_SNAPDIR, 910, 1, "n2"), 3, shard_id_t(0)));
503 o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
504 CEPH_SNAPDIR, 910, 1, "n2"), 3, shard_id_t(1)));
505 o.push_back(new ghobject_t(hobject_t(object_t("oname3"), string("oname3"),
506 CEPH_SNAPDIR, 910, 1, "n2"), 3, shard_id_t(2)));
507}
508
509ostream& operator<<(ostream& out, const ghobject_t& o)
510{
511 if (o == ghobject_t())
512 return out << "GHMIN";
513 if (o.is_max())
514 return out << "GHMAX";
515 if (o.shard_id != shard_id_t::NO_SHARD)
516 out << std::hex << o.shard_id << std::dec;
517 out << '#' << o.hobj << '#';
518 if (o.generation != ghobject_t::NO_GEN)
519 out << std::hex << (unsigned long long)(o.generation) << std::dec;
520 return out;
521}
522
523bool ghobject_t::parse(const string& s)
524{
525 if (s == "GHMIN") {
526 *this = ghobject_t();
527 return true;
528 }
529 if (s == "GHMAX") {
530 *this = ghobject_t::get_max();
531 return true;
532 }
533
534 // look for shard# prefix
535 const char *start = s.c_str();
536 const char *p;
537 int sh = shard_id_t::NO_SHARD;
538 for (p = start; *p && isxdigit(*p); ++p) ;
539 if (!*p && *p != '#')
540 return false;
541 if (p > start) {
542 int r = sscanf(s.c_str(), "%x", &sh);
543 if (r < 1)
544 return false;
545 start = p + 1;
546 } else {
547 ++start;
548 }
549
550 // look for #generation suffix
551 long long unsigned g = NO_GEN;
552 const char *last = start + strlen(start) - 1;
553 p = last;
554 while (isxdigit(*p))
555 p--;
556 if (*p != '#')
557 return false;
558 if (p < last) {
559 sscanf(p + 1, "%llx", &g);
560 }
561
562 string inner(start, p - start);
563 hobject_t h;
564 if (!h.parse(inner)) {
565 return false;
566 }
567
568 shard_id = shard_id_t(sh);
569 hobj = h;
570 generation = g;
571 max = false;
572 return true;
573}
574
575int cmp(const ghobject_t& l, const ghobject_t& r)
576{
577 if (l.max < r.max)
578 return -1;
579 if (l.max > r.max)
580 return 1;
581 if (l.shard_id < r.shard_id)
582 return -1;
583 if (l.shard_id > r.shard_id)
584 return 1;
585 int ret = cmp(l.hobj, r.hobj);
586 if (ret != 0)
587 return ret;
588 if (l.generation < r.generation)
589 return -1;
590 if (l.generation > r.generation)
591 return 1;
592 return 0;
593}