1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "test/librados_test_stub/TestMemIoCtxImpl.h"
5 #include "test/librados_test_stub/TestMemRadosClient.h"
6 #include "common/Clock.h"
7 #include "common/RWLock.h"
8 #include "include/err.h"
9 #include <boost/algorithm/string/predicate.hpp>
10 #include <boost/bind.hpp>
11 #include <errno.h>
12 #include <include/compat.h>
13
14 static void to_vector(const interval_set<uint64_t> &set,
15 std::vector<std::pair<uint64_t, uint64_t> > *vec) {
16 vec->clear();
17 for (interval_set<uint64_t>::const_iterator it = set.begin();
18 it != set.end(); ++it) {
19 vec->push_back(*it);
20 }
21 }
22
23 // see PrimaryLogPG::finish_extent_cmp()
24 static int cmpext_compare(const bufferlist &bl, const bufferlist &read_bl) {
25 for (uint64_t idx = 0; idx < bl.length(); ++idx) {
26 char read_byte = (idx < read_bl.length() ? read_bl[idx] : 0);
27 if (bl[idx] != read_byte) {
28 return -MAX_ERRNO - idx;
29 }
30 }
31 return 0;
32 }
33
34 namespace librados {
35
36 TestMemIoCtxImpl::TestMemIoCtxImpl() {
37 }
38
39 TestMemIoCtxImpl::TestMemIoCtxImpl(const TestMemIoCtxImpl& rhs)
40 : TestIoCtxImpl(rhs), m_client(rhs.m_client), m_pool(rhs.m_pool) {
41 m_pool->get();
42 }
43
44 TestMemIoCtxImpl::TestMemIoCtxImpl(TestMemRadosClient *client, int64_t pool_id,
45 const std::string& pool_name,
46 TestMemCluster::Pool *pool)
47 : TestIoCtxImpl(client, pool_id, pool_name), m_client(client),
48 m_pool(pool) {
49 m_pool->get();
50 }
51
(1) Event exn_spec_violation: |
An exception of type "std::length_error" is thrown but the throw list "throw()" doesn't allow it to be thrown. This will cause a call to unexpected() which usually calls terminate(). |
Also see events: |
[fun_call_w_exception] |
52 TestMemIoCtxImpl::~TestMemIoCtxImpl() {
(2) Event fun_call_w_exception: |
Called function throws an exception of type "std::length_error". [details] |
Also see events: |
[exn_spec_violation] |
53 m_pool->put();
54 }
55
56 TestIoCtxImpl *TestMemIoCtxImpl::clone() {
57 return new TestMemIoCtxImpl(*this);
58 }
59
60 int TestMemIoCtxImpl::aio_remove(const std::string& oid, AioCompletionImpl *c, int flags) {
61 m_client->add_aio_operation(oid, true,
62 boost::bind(&TestMemIoCtxImpl::remove, this, oid,
63 get_snap_context()),
64 c);
65 return 0;
66 }
67
68 int TestMemIoCtxImpl::append(const std::string& oid, const bufferlist &bl,
69 const SnapContext &snapc) {
70 if (get_snap_read() != CEPH_NOSNAP) {
71 return -EROFS;
72 } else if (m_client->is_blacklisted()) {
73 return -EBLACKLISTED;
74 }
75
76 TestMemCluster::SharedFile file;
77 {
78 std::unique_lock l{m_pool->file_lock};
79 file = get_file(oid, true, snapc);
80 }
81
82 std::unique_lock l{file->lock};
83 auto off = file->data.length();
84 ensure_minimum_length(off + bl.length(), &file->data);
85 file->data.copy_in(off, bl.length(), bl);
86 return 0;
87 }
88
89 int TestMemIoCtxImpl::assert_exists(const std::string &oid) {
90 if (m_client->is_blacklisted()) {
91 return -EBLACKLISTED;
92 }
93
94 std::shared_lock l{m_pool->file_lock};
95 TestMemCluster::SharedFile file = get_file(oid, false, get_snap_context());
96 if (file == NULL) {
97 return -ENOENT;
98 }
99 return 0;
100 }
101
102 int TestMemIoCtxImpl::create(const std::string& oid, bool exclusive,
103 const SnapContext &snapc) {
104 if (get_snap_read() != CEPH_NOSNAP) {
105 return -EROFS;
106 } else if (m_client->is_blacklisted()) {
107 return -EBLACKLISTED;
108 }
109
110 std::unique_lock l{m_pool->file_lock};
111 get_file(oid, true, snapc);
112 return 0;
113 }
114
115 int TestMemIoCtxImpl::list_snaps(const std::string& oid, snap_set_t *out_snaps) {
116 if (m_client->is_blacklisted()) {
117 return -EBLACKLISTED;
118 }
119
120 out_snaps->seq = 0;
121 out_snaps->clones.clear();
122
123 std::shared_lock l{m_pool->file_lock};
124 TestMemCluster::Files::iterator it = m_pool->files.find(
125 {get_namespace(), oid});
126 if (it == m_pool->files.end()) {
127 return -ENOENT;
128 }
129
130 bool include_head = false;
131 TestMemCluster::FileSnapshots &file_snaps = it->second;
132 for (TestMemCluster::FileSnapshots::iterator s_it = file_snaps.begin();
133 s_it != file_snaps.end(); ++s_it) {
134 TestMemCluster::File &file = *s_it->get();
135
136 if (file_snaps.size() > 1) {
137 out_snaps->seq = file.snap_id;
138 TestMemCluster::FileSnapshots::iterator next_it(s_it);
139 ++next_it;
140 if (next_it == file_snaps.end()) {
141 include_head = true;
142 break;
143 }
144
145 ++out_snaps->seq;
146 if (!file.exists) {
147 continue;
148 }
149
150 // update the overlap with the next version's overlap metadata
151 TestMemCluster::File &next_file = *next_it->get();
152 interval_set<uint64_t> overlap;
153 if (next_file.exists) {
154 overlap = next_file.snap_overlap;
155 }
156
157 clone_info_t clone;
158 clone.cloneid = file.snap_id;
159 clone.snaps = file.snaps;
160 to_vector(overlap, &clone.overlap);
161 clone.size = file.data.length();
162 out_snaps->clones.push_back(clone);
163 }
164 }
165
166 if ((file_snaps.size() == 1 && file_snaps.back()->data.length() > 0) ||
167 include_head)
168 {
169 // Include the SNAP_HEAD
170 TestMemCluster::File &file = *file_snaps.back();
171 if (file.exists) {
172 std::shared_lock l2{file.lock};
173 if (out_snaps->seq == 0 && !include_head) {
174 out_snaps->seq = file.snap_id;
175 }
176 clone_info_t head_clone;
177 head_clone.cloneid = librados::SNAP_HEAD;
178 head_clone.size = file.data.length();
179 out_snaps->clones.push_back(head_clone);
180 }
181 }
182 return 0;
183
184 }
185
186 int TestMemIoCtxImpl::omap_get_vals2(const std::string& oid,
187 const std::string& start_after,
188 const std::string &filter_prefix,
189 uint64_t max_return,
190 std::map<std::string, bufferlist> *out_vals,
191 bool *pmore) {
192 if (out_vals == NULL) {
193 return -EINVAL;
194 } else if (m_client->is_blacklisted()) {
195 return -EBLACKLISTED;
196 }
197
198 TestMemCluster::SharedFile file;
199 {
200 std::shared_lock l{m_pool->file_lock};
201 file = get_file(oid, false, get_snap_context());
202 if (file == NULL) {
203 return -ENOENT;
204 }
205 }
206
207 out_vals->clear();
208
209 std::shared_lock l{file->lock};
210 TestMemCluster::FileOMaps::iterator o_it = m_pool->file_omaps.find(
211 {get_namespace(), oid});
212 if (o_it == m_pool->file_omaps.end()) {
213 if (pmore) {
214 *pmore = false;
215 }
216 return 0;
217 }
218
219 TestMemCluster::OMap &omap = o_it->second;
220 TestMemCluster::OMap::iterator it = omap.begin();
221 if (!start_after.empty()) {
222 it = omap.upper_bound(start_after);
223 }
224
225 while (it != omap.end() && max_return > 0) {
226 if (filter_prefix.empty() ||
227 boost::algorithm::starts_with(it->first, filter_prefix)) {
228 (*out_vals)[it->first] = it->second;
229 --max_return;
230 }
231 ++it;
232 }
233 if (pmore) {
234 *pmore = (it != omap.end());
235 }
236 return 0;
237 }
238
239 int TestMemIoCtxImpl::omap_get_vals(const std::string& oid,
240 const std::string& start_after,
241 const std::string &filter_prefix,
242 uint64_t max_return,
243 std::map<std::string, bufferlist> *out_vals) {
244 return omap_get_vals2(oid, start_after, filter_prefix, max_return, out_vals, nullptr);
245 }
246
247 int TestMemIoCtxImpl::omap_rm_keys(const std::string& oid,
248 const std::set<std::string>& keys) {
249 if (get_snap_read() != CEPH_NOSNAP) {
250 return -EROFS;
251 } else if (m_client->is_blacklisted()) {
252 return -EBLACKLISTED;
253 }
254
255 TestMemCluster::SharedFile file;
256 {
257 std::unique_lock l{m_pool->file_lock};
258 file = get_file(oid, true, get_snap_context());
259 if (file == NULL) {
260 return -ENOENT;
261 }
262 }
263
264 std::unique_lock l{file->lock};
265 for (std::set<std::string>::iterator it = keys.begin();
266 it != keys.end(); ++it) {
267 m_pool->file_omaps[{get_namespace(), oid}].erase(*it);
268 }
269 return 0;
270 }
271
272 int TestMemIoCtxImpl::omap_set(const std::string& oid,
273 const std::map<std::string, bufferlist> &map) {
274 if (get_snap_read() != CEPH_NOSNAP) {
275 return -EROFS;
276 } else if (m_client->is_blacklisted()) {
277 return -EBLACKLISTED;
278 }
279
280 TestMemCluster::SharedFile file;
281 {
282 std::unique_lock l{m_pool->file_lock};
283 file = get_file(oid, true, get_snap_context());
284 if (file == NULL) {
285 return -ENOENT;
286 }
287 }
288
289 std::unique_lock l{file->lock};
290 for (std::map<std::string, bufferlist>::const_iterator it = map.begin();
291 it != map.end(); ++it) {
292 bufferlist bl;
293 bl.append(it->second);
294 m_pool->file_omaps[{get_namespace(), oid}][it->first] = bl;
295 }
296
297 return 0;
298 }
299
300 int TestMemIoCtxImpl::read(const std::string& oid, size_t len, uint64_t off,
301 bufferlist *bl) {
302 if (m_client->is_blacklisted()) {
303 return -EBLACKLISTED;
304 }
305
306 TestMemCluster::SharedFile file;
307 {
308 std::shared_lock l{m_pool->file_lock};
309 file = get_file(oid, false, get_snap_context());
310 if (file == NULL) {
311 return -ENOENT;
312 }
313 }
314
315 std::shared_lock l{file->lock};
316 if (len == 0) {
317 len = file->data.length();
318 }
319 len = clip_io(off, len, file->data.length());
320 if (bl != NULL && len > 0) {
321 bufferlist bit;
322 bit.substr_of(file->data, off, len);
323 append_clone(bit, bl);
324 }
325 return len;
326 }
327
328 int TestMemIoCtxImpl::remove(const std::string& oid, const SnapContext &snapc) {
329 if (get_snap_read() != CEPH_NOSNAP) {
330 return -EROFS;
331 } else if (m_client->is_blacklisted()) {
332 return -EBLACKLISTED;
333 }
334
335 std::unique_lock l{m_pool->file_lock};
336 TestMemCluster::SharedFile file = get_file(oid, false, snapc);
337 if (file == NULL) {
338 return -ENOENT;
339 }
340 file = get_file(oid, true, snapc);
341
342 {
343 std::unique_lock l2{file->lock};
344 file->exists = false;
345 }
346
347 TestCluster::ObjectLocator locator(get_namespace(), oid);
348 TestMemCluster::Files::iterator it = m_pool->files.find(locator);
349 ceph_assert(it != m_pool->files.end());
350
351 if (*it->second.rbegin() == file) {
352 TestMemCluster::ObjectHandlers object_handlers;
353 std::swap(object_handlers, m_pool->file_handlers[locator]);
354 m_pool->file_handlers.erase(locator);
355
356 for (auto object_handler : object_handlers) {
357 object_handler->handle_removed(m_client);
358 }
359 }
360
361 if (it->second.size() == 1) {
362 m_pool->files.erase(it);
363 m_pool->file_omaps.erase(locator);
364 }
365 return 0;
366 }
367
368 int TestMemIoCtxImpl::selfmanaged_snap_create(uint64_t *snapid) {
369 if (m_client->is_blacklisted()) {
370 return -EBLACKLISTED;
371 }
372
373 std::unique_lock l{m_pool->file_lock};
374 *snapid = ++m_pool->snap_id;
375 m_pool->snap_seqs.insert(*snapid);
376 return 0;
377 }
378
379 int TestMemIoCtxImpl::selfmanaged_snap_remove(uint64_t snapid) {
380 if (m_client->is_blacklisted()) {
381 return -EBLACKLISTED;
382 }
383
384 std::unique_lock l{m_pool->file_lock};
385 TestMemCluster::SnapSeqs::iterator it =
386 m_pool->snap_seqs.find(snapid);
387 if (it == m_pool->snap_seqs.end()) {
388 return -ENOENT;
389 }
390
391 // TODO clean up all file snapshots
392 m_pool->snap_seqs.erase(it);
393 return 0;
394 }
395
396 int TestMemIoCtxImpl::selfmanaged_snap_rollback(const std::string& oid,
397 uint64_t snapid) {
398 if (m_client->is_blacklisted()) {
399 return -EBLACKLISTED;
400 }
401
402 std::unique_lock l{m_pool->file_lock};
403
404 TestMemCluster::SharedFile file;
405 TestMemCluster::Files::iterator f_it = m_pool->files.find(
406 {get_namespace(), oid});
407 if (f_it == m_pool->files.end()) {
408 return 0;
409 }
410
411 TestMemCluster::FileSnapshots &snaps = f_it->second;
412 file = snaps.back();
413
414 size_t versions = 0;
415 for (TestMemCluster::FileSnapshots::reverse_iterator it = snaps.rbegin();
416 it != snaps.rend(); ++it) {
417 TestMemCluster::SharedFile file = *it;
418 if (file->snap_id < get_snap_read()) {
419 if (versions == 0) {
420 // already at the snapshot version
421 return 0;
422 } else if (file->snap_id == CEPH_NOSNAP) {
423 if (versions == 1) {
424 // delete it current HEAD, next one is correct version
425 snaps.erase(it.base());
426 } else {
427 // overwrite contents of current HEAD
428 file = TestMemCluster::SharedFile (new TestMemCluster::File(**it));
429 file->snap_id = CEPH_NOSNAP;
430 *it = file;
431 }
432 } else {
433 // create new head version
434 file = TestMemCluster::SharedFile (new TestMemCluster::File(**it));
435 file->snap_id = m_pool->snap_id;
436 snaps.push_back(file);
437 }
438 return 0;
439 }
440 ++versions;
441 }
442 return 0;
443 }
444
445 int TestMemIoCtxImpl::set_alloc_hint(const std::string& oid,
446 uint64_t expected_object_size,
447 uint64_t expected_write_size,
448 const SnapContext &snapc) {
449 if (get_snap_read() != CEPH_NOSNAP) {
450 return -EROFS;
451 } else if (m_client->is_blacklisted()) {
452 return -EBLACKLISTED;
453 }
454
455 {
456 std::unique_lock l{m_pool->file_lock};
457 get_file(oid, true, snapc);
458 }
459
460 return 0;
461 }
462
463 int TestMemIoCtxImpl::sparse_read(const std::string& oid, uint64_t off,
464 uint64_t len,
465 std::map<uint64_t,uint64_t> *m,
466 bufferlist *data_bl) {
467 if (m_client->is_blacklisted()) {
468 return -EBLACKLISTED;
469 }
470
471 // TODO verify correctness
472 TestMemCluster::SharedFile file;
473 {
474 std::shared_lock l{m_pool->file_lock};
475 file = get_file(oid, false, get_snap_context());
476 if (file == NULL) {
477 return -ENOENT;
478 }
479 }
480
481 std::shared_lock l{file->lock};
482 len = clip_io(off, len, file->data.length());
483 // TODO support sparse read
484 if (m != NULL) {
485 m->clear();
486 if (len > 0) {
487 (*m)[off] = len;
488 }
489 }
490 if (data_bl != NULL && len > 0) {
491 bufferlist bit;
492 bit.substr_of(file->data, off, len);
493 append_clone(bit, data_bl);
494 }
495 return len > 0 ? 1 : 0;
496 }
497
498 int TestMemIoCtxImpl::stat(const std::string& oid, uint64_t *psize,
499 time_t *pmtime) {
500 if (m_client->is_blacklisted()) {
501 return -EBLACKLISTED;
502 }
503
504 TestMemCluster::SharedFile file;
505 {
506 std::shared_lock l{m_pool->file_lock};
507 file = get_file(oid, false, get_snap_context());
508 if (file == NULL) {
509 return -ENOENT;
510 }
511 }
512
513 std::shared_lock l{file->lock};
514 if (psize != NULL) {
515 *psize = file->data.length();
516 }
517 if (pmtime != NULL) {
518 *pmtime = file->mtime;
519 }
520 return 0;
521 }
522
523 int TestMemIoCtxImpl::truncate(const std::string& oid, uint64_t size,
524 const SnapContext &snapc) {
525 if (get_snap_read() != CEPH_NOSNAP) {
526 return -EROFS;
527 } else if (m_client->is_blacklisted()) {
528 return -EBLACKLISTED;
529 }
530
531 TestMemCluster::SharedFile file;
532 {
533 std::unique_lock l{m_pool->file_lock};
534 file = get_file(oid, true, snapc);
535 }
536
537 std::unique_lock l{file->lock};
538 bufferlist bl(size);
539
540 interval_set<uint64_t> is;
541 if (file->data.length() > size) {
542 is.insert(size, file->data.length() - size);
543
544 bl.substr_of(file->data, 0, size);
545 file->data.swap(bl);
546 } else if (file->data.length() != size) {
547 if (size == 0) {
548 bl.clear();
549 } else {
550 is.insert(0, size);
551
552 bl.append_zero(size - file->data.length());
553 file->data.append(bl);
554 }
555 }
556 is.intersection_of(file->snap_overlap);
557 file->snap_overlap.subtract(is);
558 return 0;
559 }
560
561 int TestMemIoCtxImpl::write(const std::string& oid, bufferlist& bl, size_t len,
562 uint64_t off, const SnapContext &snapc) {
563 if (get_snap_read() != CEPH_NOSNAP) {
564 return -EROFS;
565 } else if (m_client->is_blacklisted()) {
566 return -EBLACKLISTED;
567 }
568
569 TestMemCluster::SharedFile file;
570 {
571 std::unique_lock l{m_pool->file_lock};
572 file = get_file(oid, true, snapc);
573 }
574
575 std::unique_lock l{file->lock};
576 if (len > 0) {
577 interval_set<uint64_t> is;
578 is.insert(off, len);
579 is.intersection_of(file->snap_overlap);
580 file->snap_overlap.subtract(is);
581 }
582
583 ensure_minimum_length(off + len, &file->data);
584 file->data.copy_in(off, len, bl);
585 return 0;
586 }
587
588 int TestMemIoCtxImpl::write_full(const std::string& oid, bufferlist& bl,
589 const SnapContext &snapc) {
590 if (get_snap_read() != CEPH_NOSNAP) {
591 return -EROFS;
592 } else if (m_client->is_blacklisted()) {
593 return -EBLACKLISTED;
594 }
595
596 TestMemCluster::SharedFile file;
597 {
598 std::unique_lock l{m_pool->file_lock};
599 file = get_file(oid, true, snapc);
600 if (file == NULL) {
601 return -ENOENT;
602 }
603 }
604
605 std::unique_lock l{file->lock};
606 if (bl.length() > 0) {
607 interval_set<uint64_t> is;
608 is.insert(0, bl.length());
609 is.intersection_of(file->snap_overlap);
610 file->snap_overlap.subtract(is);
611 }
612
613 file->data.clear();
614 ensure_minimum_length(bl.length(), &file->data);
615 file->data.copy_in(0, bl.length(), bl);
616 return 0;
617 }
618
619 int TestMemIoCtxImpl::writesame(const std::string& oid, bufferlist& bl, size_t len,
620 uint64_t off, const SnapContext &snapc) {
621 if (get_snap_read() != CEPH_NOSNAP) {
622 return -EROFS;
623 } else if (m_client->is_blacklisted()) {
624 return -EBLACKLISTED;
625 }
626
627 if (len == 0 || (len % bl.length())) {
628 return -EINVAL;
629 }
630
631 TestMemCluster::SharedFile file;
632 {
633 std::unique_lock l{m_pool->file_lock};
634 file = get_file(oid, true, snapc);
635 }
636
637 std::unique_lock l{file->lock};
638 if (len > 0) {
639 interval_set<uint64_t> is;
640 is.insert(off, len);
641 is.intersection_of(file->snap_overlap);
642 file->snap_overlap.subtract(is);
643 }
644
645 ensure_minimum_length(off + len, &file->data);
646 while (len > 0) {
647 file->data.copy_in(off, bl.length(), bl);
648 off += bl.length();
649 len -= bl.length();
650 }
651 return 0;
652 }
653
654 int TestMemIoCtxImpl::cmpext(const std::string& oid, uint64_t off,
655 bufferlist& cmp_bl) {
656 if (m_client->is_blacklisted()) {
657 return -EBLACKLISTED;
658 }
659
660 bufferlist read_bl;
661 uint64_t len = cmp_bl.length();
662
663 TestMemCluster::SharedFile file;
664 {
665 std::shared_lock l{m_pool->file_lock};
666 file = get_file(oid, false, get_snap_context());
667 if (file == NULL) {
668 return cmpext_compare(cmp_bl, read_bl);
669 }
670 }
671
672 std::shared_lock l{file->lock};
673 if (off >= file->data.length()) {
674 len = 0;
675 } else if (off + len > file->data.length()) {
676 len = file->data.length() - off;
677 }
678 read_bl.substr_of(file->data, off, len);
679 return cmpext_compare(cmp_bl, read_bl);
680 }
681
682 int TestMemIoCtxImpl::xattr_get(const std::string& oid,
683 std::map<std::string, bufferlist>* attrset) {
684 if (m_client->is_blacklisted()) {
685 return -EBLACKLISTED;
686 }
687
688 TestMemCluster::SharedFile file;
689 std::shared_lock l{m_pool->file_lock};
690 TestMemCluster::FileXAttrs::iterator it = m_pool->file_xattrs.find(
691 {get_namespace(), oid});
692 if (it == m_pool->file_xattrs.end()) {
693 return -ENODATA;
694 }
695 *attrset = it->second;
696 return 0;
697 }
698
699 int TestMemIoCtxImpl::xattr_set(const std::string& oid, const std::string &name,
700 bufferlist& bl) {
701 if (m_client->is_blacklisted()) {
702 return -EBLACKLISTED;
703 }
704
705 std::unique_lock l{m_pool->file_lock};
706 m_pool->file_xattrs[{get_namespace(), oid}][name] = bl;
707 return 0;
708 }
709
710 int TestMemIoCtxImpl::zero(const std::string& oid, uint64_t off, uint64_t len,
711 const SnapContext &snapc) {
712 if (m_client->is_blacklisted()) {
713 return -EBLACKLISTED;
714 }
715
716 bool truncate_redirect = false;
717 TestMemCluster::SharedFile file;
718 {
719 std::unique_lock l{m_pool->file_lock};
720 file = get_file(oid, false, snapc);
721 if (!file) {
722 return 0;
723 }
724 file = get_file(oid, true, snapc);
725
726 std::shared_lock l2{file->lock};
727 if (len > 0 && off + len >= file->data.length()) {
728 // Zero -> Truncate logic embedded in OSD
729 truncate_redirect = true;
730 }
731 }
732 if (truncate_redirect) {
733 return truncate(oid, off, snapc);
734 }
735
736 bufferlist bl;
737 bl.append_zero(len);
738 return write(oid, bl, len, off, snapc);
739 }
740
741 void TestMemIoCtxImpl::append_clone(bufferlist& src, bufferlist* dest) {
742 // deep-copy the src to ensure our memory-based mock RADOS data cannot
743 // be modified by callers
744 if (src.length() > 0) {
745 bufferlist::iterator iter = src.begin();
746 buffer::ptr ptr;
747 iter.copy_deep(src.length(), ptr);
748 dest->append(ptr);
749 }
750 }
751
752 size_t TestMemIoCtxImpl::clip_io(size_t off, size_t len, size_t bl_len) {
753 if (off >= bl_len) {
754 len = 0;
755 } else if (off + len > bl_len) {
756 len = bl_len - off;
757 }
758 return len;
759 }
760
761 void TestMemIoCtxImpl::ensure_minimum_length(size_t len, bufferlist *bl) {
762 if (len > bl->length()) {
763 bufferptr ptr(buffer::create(len - bl->length()));
764 ptr.zero();
765 bl->append(ptr);
766 }
767 }
768
769 TestMemCluster::SharedFile TestMemIoCtxImpl::get_file(
770 const std::string &oid, bool write, const SnapContext &snapc) {
771 ceph_assert(ceph_mutex_is_locked(m_pool->file_lock) ||
772 ceph_mutex_is_wlocked(m_pool->file_lock));
773 ceph_assert(!write || ceph_mutex_is_wlocked(m_pool->file_lock));
774
775 TestMemCluster::SharedFile file;
776 TestMemCluster::Files::iterator it = m_pool->files.find(
777 {get_namespace(), oid});
778 if (it != m_pool->files.end()) {
779 file = it->second.back();
780 } else if (!write) {
781 return TestMemCluster::SharedFile();
782 }
783
784 if (write) {
785 bool new_version = false;
786 if (!file || !file->exists) {
787 file = TestMemCluster::SharedFile(new TestMemCluster::File());
788 new_version = true;
789 } else {
790 if (!snapc.snaps.empty() && file->snap_id < snapc.seq) {
791 for (std::vector<snapid_t>::const_reverse_iterator seq_it =
792 snapc.snaps.rbegin();
793 seq_it != snapc.snaps.rend(); ++seq_it) {
794 if (*seq_it > file->snap_id && *seq_it <= snapc.seq) {
795 file->snaps.push_back(*seq_it);
796 }
797 }
798
799 bufferlist prev_data = file->data;
800 file = TestMemCluster::SharedFile(
801 new TestMemCluster::File(*file));
802 file->data.clear();
803 append_clone(prev_data, &file->data);
804 if (prev_data.length() > 0) {
805 file->snap_overlap.insert(0, prev_data.length());
806 }
807 new_version = true;
808 }
809 }
810
811 if (new_version) {
812 file->snap_id = snapc.seq;
813 file->mtime = ceph_clock_now().sec();
814 m_pool->files[{get_namespace(), oid}].push_back(file);
815 }
816 return file;
817 }
818
819 if (get_snap_read() == CEPH_NOSNAP) {
820 if (!file->exists) {
821 ceph_assert(it->second.size() > 1);
822 return TestMemCluster::SharedFile();
823 }
824 return file;
825 }
826
827 TestMemCluster::FileSnapshots &snaps = it->second;
828 for (TestMemCluster::FileSnapshots::reverse_iterator it = snaps.rbegin();
829 it != snaps.rend(); ++it) {
830 TestMemCluster::SharedFile file = *it;
831 if (file->snap_id < get_snap_read()) {
832 if (!file->exists) {
833 return TestMemCluster::SharedFile();
834 }
835 return file;
836 }
837 }
838 return TestMemCluster::SharedFile();
839 }
840
841 } // namespace librados
842