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