]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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/librbd/test_mock_fixture.h" | |
5 | #include "test/librbd/test_support.h" | |
6 | #include "test/librbd/mock/MockImageCtx.h" | |
7 | #include "test/librbd/mock/MockObjectMap.h" | |
8 | #include "test/librbd/mock/crypto/MockCryptoInterface.h" | |
9 | #include "librbd/crypto/CryptoObjectDispatch.h" | |
10 | #include "librbd/io/ObjectDispatchSpec.h" | |
11 | #include "librbd/io/Utils.h" | |
12 | ||
13 | #include "librbd/io/Utils.cc" | |
14 | template bool librbd::io::util::trigger_copyup( | |
15 | MockImageCtx *image_ctx, uint64_t object_no, IOContext io_context, | |
16 | Context* on_finish); | |
17 | ||
18 | template class librbd::io::ObjectWriteRequest<librbd::MockImageCtx>; | |
19 | template class librbd::io::AbstractObjectWriteRequest<librbd::MockImageCtx>; | |
20 | #include "librbd/io/ObjectRequest.cc" | |
21 | ||
22 | namespace librbd { | |
23 | ||
24 | namespace util { | |
25 | ||
26 | inline ImageCtx *get_image_ctx(MockImageCtx *image_ctx) { | |
27 | return image_ctx->image_ctx; | |
28 | } | |
29 | ||
30 | } // namespace util | |
31 | ||
32 | namespace io { | |
33 | ||
34 | template <> | |
35 | struct CopyupRequest<librbd::MockImageCtx> { | |
36 | MOCK_METHOD0(send, void()); | |
37 | MOCK_METHOD2(append_request, void( | |
38 | AbstractObjectWriteRequest<librbd::MockImageCtx>*, | |
39 | const Extents&)); | |
40 | ||
41 | static CopyupRequest* s_instance; | |
1e59de90 TL |
42 | static CopyupRequest* create(librbd::MockImageCtx* ictx, uint64_t objectno, |
43 | Extents&& image_extents, ImageArea area, | |
44 | const ZTracer::Trace& parent_trace) { | |
f67539c2 TL |
45 | return s_instance; |
46 | } | |
47 | ||
48 | CopyupRequest() { | |
49 | s_instance = this; | |
50 | } | |
51 | }; | |
52 | ||
53 | CopyupRequest<librbd::MockImageCtx>* CopyupRequest< | |
54 | librbd::MockImageCtx>::s_instance = nullptr; | |
55 | ||
56 | namespace util { | |
57 | ||
f67539c2 TL |
58 | namespace { |
59 | ||
60 | struct Mock { | |
61 | static Mock* s_instance; | |
62 | ||
63 | Mock() { | |
64 | s_instance = this; | |
65 | } | |
66 | ||
67 | MOCK_METHOD6(read_parent, | |
68 | void(MockImageCtx*, uint64_t, io::ReadExtents*, | |
69 | librados::snap_t, const ZTracer::Trace &, Context*)); | |
70 | }; | |
71 | ||
72 | Mock *Mock::s_instance = nullptr; | |
73 | ||
74 | } // anonymous namespace | |
75 | ||
76 | template <> void read_parent( | |
77 | MockImageCtx *image_ctx, uint64_t object_no, | |
78 | io::ReadExtents* extents, librados::snap_t snap_id, | |
79 | const ZTracer::Trace &trace, Context* on_finish) { | |
80 | ||
81 | Mock::s_instance->read_parent(image_ctx, object_no, extents, snap_id, trace, | |
82 | on_finish); | |
83 | } | |
84 | ||
85 | } // namespace util | |
86 | } // namespace io | |
87 | ||
88 | } // namespace librbd | |
89 | ||
90 | #include "librbd/crypto/CryptoObjectDispatch.cc" | |
91 | ||
92 | namespace librbd { | |
93 | namespace crypto { | |
94 | ||
1e59de90 TL |
95 | template <> |
96 | uint64_t get_file_offset(MockImageCtx *image_ctx, uint64_t object_no, | |
97 | uint64_t offset) { | |
98 | return Striper::get_file_offset(image_ctx->cct, &image_ctx->layout, | |
99 | object_no, offset); | |
100 | } | |
101 | ||
f67539c2 TL |
102 | using ::testing::_; |
103 | using ::testing::ElementsAre; | |
104 | using ::testing::Invoke; | |
105 | using ::testing::InSequence; | |
106 | using ::testing::Pair; | |
107 | using ::testing::Return; | |
108 | using ::testing::WithArg; | |
109 | ||
110 | struct TestMockCryptoCryptoObjectDispatch : public TestMockFixture { | |
111 | typedef CryptoObjectDispatch<librbd::MockImageCtx> MockCryptoObjectDispatch; | |
112 | typedef io::AbstractObjectWriteRequest<librbd::MockImageCtx> | |
113 | MockAbstractObjectWriteRequest; | |
114 | typedef io::CopyupRequest<librbd::MockImageCtx> MockCopyupRequest; | |
115 | typedef io::util::Mock MockUtils; | |
116 | ||
1e59de90 | 117 | MockCryptoInterface crypto; |
f67539c2 TL |
118 | MockImageCtx* mock_image_ctx; |
119 | MockCryptoObjectDispatch* mock_crypto_object_dispatch; | |
120 | ||
121 | C_SaferCond finished_cond; | |
122 | Context *on_finish = &finished_cond; | |
123 | C_SaferCond dispatched_cond; | |
124 | Context *on_dispatched = &dispatched_cond; | |
125 | Context *dispatcher_ctx; | |
126 | ceph::bufferlist data; | |
127 | io::DispatchResult dispatch_result; | |
128 | io::Extents extent_map; | |
129 | int object_dispatch_flags = 0; | |
130 | MockUtils mock_utils; | |
131 | MockCopyupRequest copyup_request; | |
132 | ||
133 | void SetUp() override { | |
134 | TestMockFixture::SetUp(); | |
135 | ||
136 | librbd::ImageCtx *ictx; | |
137 | ASSERT_EQ(0, open_image(m_image_name, &ictx)); | |
f67539c2 TL |
138 | mock_image_ctx = new MockImageCtx(*ictx); |
139 | mock_crypto_object_dispatch = new MockCryptoObjectDispatch( | |
1e59de90 | 140 | mock_image_ctx, &crypto); |
f67539c2 TL |
141 | data.append(std::string(4096, '1')); |
142 | } | |
143 | ||
144 | void TearDown() override { | |
145 | C_SaferCond cond; | |
146 | Context *on_finish = &cond; | |
147 | mock_crypto_object_dispatch->shut_down(on_finish); | |
148 | ASSERT_EQ(0, cond.wait()); | |
149 | ||
20effc67 | 150 | delete mock_crypto_object_dispatch; |
f67539c2 TL |
151 | delete mock_image_ctx; |
152 | ||
153 | TestMockFixture::TearDown(); | |
154 | } | |
155 | ||
156 | void expect_object_read(io::ReadExtents* extents, uint64_t version = 0) { | |
157 | EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_)) | |
158 | .WillOnce(Invoke([this, extents, | |
159 | version](io::ObjectDispatchSpec* spec) { | |
160 | auto* read = boost::get<io::ObjectDispatchSpec::ReadRequest>( | |
161 | &spec->request); | |
162 | ASSERT_TRUE(read != nullptr); | |
163 | ||
164 | ASSERT_EQ(extents->size(), read->extents->size()); | |
165 | for (uint64_t i = 0; i < extents->size(); ++i) { | |
166 | ASSERT_EQ((*extents)[i].offset, (*read->extents)[i].offset); | |
167 | ASSERT_EQ((*extents)[i].length, (*read->extents)[i].length); | |
168 | (*read->extents)[i].bl = (*extents)[i].bl; | |
169 | (*read->extents)[i].extent_map = (*extents)[i].extent_map; | |
170 | } | |
171 | ||
172 | if (read->version != nullptr) { | |
173 | *(read->version) = version; | |
174 | } | |
175 | ||
176 | spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; | |
177 | dispatcher_ctx = &spec->dispatcher_ctx; | |
178 | })); | |
179 | } | |
180 | ||
181 | void expect_read_parent(MockUtils &mock_utils, uint64_t object_no, | |
182 | io::ReadExtents* extents, librados::snap_t snap_id, | |
183 | int r) { | |
184 | EXPECT_CALL(mock_utils, | |
185 | read_parent(_, object_no, extents, snap_id, _, _)) | |
186 | .WillOnce(WithArg<5>(CompleteContext( | |
187 | r, static_cast<asio::ContextWQ*>(nullptr)))); | |
188 | } | |
189 | ||
190 | void expect_object_write(uint64_t object_off, const std::string& data, | |
191 | int write_flags, | |
192 | std::optional<uint64_t> assert_version) { | |
193 | EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_)) | |
194 | .WillOnce(Invoke([this, object_off, data, write_flags, | |
195 | assert_version](io::ObjectDispatchSpec* spec) { | |
196 | auto* write = boost::get<io::ObjectDispatchSpec::WriteRequest>( | |
197 | &spec->request); | |
198 | ASSERT_TRUE(write != nullptr); | |
199 | ||
200 | ASSERT_EQ(object_off, write->object_off); | |
201 | ASSERT_TRUE(data == write->data.to_str()); | |
202 | ASSERT_EQ(write_flags, write->write_flags); | |
203 | ASSERT_EQ(assert_version, write->assert_version); | |
204 | ||
205 | spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; | |
206 | dispatcher_ctx = &spec->dispatcher_ctx; | |
207 | })); | |
208 | } | |
209 | ||
210 | void expect_object_write_same() { | |
211 | EXPECT_CALL(*mock_image_ctx->io_object_dispatcher, send(_)) | |
212 | .WillOnce(Invoke([this](io::ObjectDispatchSpec* spec) { | |
213 | auto* write_same = boost::get< | |
214 | io::ObjectDispatchSpec::WriteSameRequest>( | |
215 | &spec->request); | |
216 | ASSERT_TRUE(write_same != nullptr); | |
217 | ||
218 | spec->dispatch_result = io::DISPATCH_RESULT_COMPLETE; | |
219 | dispatcher_ctx = &spec->dispatcher_ctx; | |
220 | })); | |
221 | } | |
222 | ||
223 | void expect_get_object_size() { | |
224 | EXPECT_CALL(*mock_image_ctx, get_object_size()).WillOnce(Return( | |
225 | mock_image_ctx->layout.object_size)); | |
226 | } | |
227 | ||
1e59de90 TL |
228 | void expect_remap_to_logical(uint64_t offset, uint64_t length) { |
229 | EXPECT_CALL(*mock_image_ctx->io_image_dispatcher, remap_to_logical( | |
230 | ElementsAre(Pair(offset, length)))); | |
f67539c2 TL |
231 | } |
232 | ||
233 | void expect_get_parent_overlap(uint64_t overlap) { | |
234 | EXPECT_CALL(*mock_image_ctx, get_parent_overlap(_, _)) | |
235 | .WillOnce(WithArg<1>(Invoke([overlap](uint64_t *o) { | |
236 | *o = overlap; | |
237 | return 0; | |
238 | }))); | |
239 | } | |
240 | ||
241 | void expect_prune_parent_extents(uint64_t object_overlap) { | |
1e59de90 | 242 | EXPECT_CALL(*mock_image_ctx, prune_parent_extents(_, _, _, _)) |
f67539c2 TL |
243 | .WillOnce(Return(object_overlap)); |
244 | } | |
245 | ||
246 | void expect_copyup(MockAbstractObjectWriteRequest** write_request, int r) { | |
247 | EXPECT_CALL(copyup_request, append_request(_, _)) | |
248 | .WillOnce(WithArg<0>( | |
249 | Invoke([write_request]( | |
250 | MockAbstractObjectWriteRequest *req) { | |
251 | *write_request = req; | |
252 | }))); | |
253 | EXPECT_CALL(copyup_request, send()) | |
254 | .WillOnce(Invoke([write_request, r]() { | |
255 | (*write_request)->handle_copyup(r); | |
256 | })); | |
257 | } | |
258 | ||
259 | void expect_encrypt(int count = 1) { | |
1e59de90 | 260 | EXPECT_CALL(crypto, encrypt(_, _)).Times(count); |
f67539c2 TL |
261 | } |
262 | ||
263 | void expect_decrypt(int count = 1) { | |
1e59de90 | 264 | EXPECT_CALL(crypto, decrypt(_, _)).Times(count); |
f67539c2 TL |
265 | } |
266 | }; | |
267 | ||
268 | TEST_F(TestMockCryptoCryptoObjectDispatch, Flush) { | |
269 | ASSERT_FALSE(mock_crypto_object_dispatch->flush( | |
270 | io::FLUSH_SOURCE_USER, {}, nullptr, nullptr, &on_finish, nullptr)); | |
271 | ASSERT_EQ(on_finish, &finished_cond); // not modified | |
272 | on_finish->complete(0); | |
273 | ASSERT_EQ(0, finished_cond.wait()); | |
274 | } | |
275 | ||
276 | TEST_F(TestMockCryptoCryptoObjectDispatch, Discard) { | |
277 | expect_object_write_same(); | |
278 | ASSERT_TRUE(mock_crypto_object_dispatch->discard( | |
1e59de90 | 279 | 11, 0, 4096, mock_image_ctx->get_data_io_context(), 0, {}, |
f67539c2 TL |
280 | &object_dispatch_flags, nullptr, &dispatch_result, &on_finish, |
281 | on_dispatched)); | |
282 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
283 | ||
284 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
285 | dispatcher_ctx->complete(0); | |
286 | ASSERT_EQ(0, dispatched_cond.wait()); | |
287 | } | |
288 | ||
289 | TEST_F(TestMockCryptoCryptoObjectDispatch, AlignedReadFail) { | |
290 | io::ReadExtents extents = {{0, 4096}}; | |
291 | expect_object_read(&extents); | |
292 | ASSERT_TRUE(mock_crypto_object_dispatch->read( | |
1e59de90 | 293 | 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {}, |
f67539c2 TL |
294 | nullptr, &object_dispatch_flags, &dispatch_result, |
295 | &on_finish, on_dispatched)); | |
296 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
297 | ASSERT_EQ(on_finish, &finished_cond); | |
298 | ||
299 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
300 | dispatcher_ctx->complete(-EIO); | |
301 | ASSERT_EQ(-EIO, dispatched_cond.wait()); | |
302 | } | |
303 | ||
304 | TEST_F(TestMockCryptoCryptoObjectDispatch, AlignedRead) { | |
305 | io::ReadExtents extents = {{0, 16384}, {32768, 4096}}; | |
306 | extents[0].bl.append(std::string(1024, '1') + std::string(1024, '2') + | |
307 | std::string(1024, '3') + std::string(1024, '4')); | |
308 | extents[0].extent_map = {{1024, 1024}, {3072, 2048}, {16384 - 1024, 1024}}; | |
309 | extents[1].bl.append(std::string(4096, '0')); | |
310 | expect_object_read(&extents); | |
311 | ASSERT_TRUE(mock_crypto_object_dispatch->read( | |
1e59de90 | 312 | 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {}, |
f67539c2 TL |
313 | nullptr, &object_dispatch_flags, &dispatch_result, |
314 | &on_finish, on_dispatched)); | |
315 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
316 | ASSERT_EQ(on_finish, &finished_cond); | |
317 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
318 | ||
319 | expect_decrypt(3); | |
320 | dispatcher_ctx->complete(0); | |
321 | ASSERT_EQ(16384 + 4096, dispatched_cond.wait()); | |
322 | ||
323 | auto expected_bl_data = ( | |
324 | std::string(1024, '\0') + std::string(1024, '1') + | |
325 | std::string(1024, '\0') + std::string(1024, '2') + | |
326 | std::string(1024, '3') + std::string(3072, '\0') + | |
327 | std::string(3072, '\0') + std::string(1024, '4')); | |
328 | ASSERT_TRUE(extents[0].bl.to_str() == expected_bl_data); | |
329 | ASSERT_THAT(extents[0].extent_map, | |
330 | ElementsAre(Pair(0, 8192), Pair(16384 - 4096, 4096))); | |
331 | } | |
332 | ||
333 | TEST_F(TestMockCryptoCryptoObjectDispatch, ReadFromParent) { | |
334 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
335 | expect_object_read(&extents); | |
1e59de90 | 336 | expect_read_parent(mock_utils, 11, &extents, CEPH_NOSNAP, 8192); |
f67539c2 | 337 | ASSERT_TRUE(mock_crypto_object_dispatch->read( |
1e59de90 | 338 | 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {}, |
f67539c2 TL |
339 | nullptr, &object_dispatch_flags, &dispatch_result, |
340 | &on_finish, on_dispatched)); | |
341 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
342 | ASSERT_EQ(on_finish, &finished_cond); | |
343 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
344 | ||
345 | // no decrypt | |
346 | dispatcher_ctx->complete(-ENOENT); | |
347 | ASSERT_EQ(8192, dispatched_cond.wait()); | |
348 | } | |
349 | ||
350 | TEST_F(TestMockCryptoCryptoObjectDispatch, ReadFromParentDisabled) { | |
351 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
352 | expect_object_read(&extents); | |
353 | ASSERT_TRUE(mock_crypto_object_dispatch->read( | |
1e59de90 | 354 | 11, &extents, mock_image_ctx->get_data_io_context(), 0, |
f67539c2 TL |
355 | io::READ_FLAG_DISABLE_READ_FROM_PARENT, {}, |
356 | nullptr, &object_dispatch_flags, &dispatch_result, | |
357 | &on_finish, on_dispatched)); | |
358 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
359 | ASSERT_EQ(on_finish, &finished_cond); | |
360 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
361 | ||
362 | // no decrypt | |
363 | dispatcher_ctx->complete(-ENOENT); | |
364 | ASSERT_EQ(-ENOENT, dispatched_cond.wait()); | |
365 | } | |
366 | ||
367 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedRead) { | |
368 | io::ReadExtents extents = {{0, 1}, {8191, 1}, {8193, 1}, | |
369 | {16384 + 1, 4096 * 5 - 2}}; | |
370 | io::ReadExtents aligned_extents = {{0, 4096}, {4096, 4096}, {8192, 4096}, | |
371 | {16384, 4096 * 5}}; | |
372 | aligned_extents[0].bl.append(std::string("1") + std::string(4096, '0')); | |
373 | aligned_extents[1].bl.append(std::string(4095, '0') + std::string("2")); | |
374 | aligned_extents[2].bl.append(std::string("03") + std::string(4094, '0')); | |
375 | aligned_extents[3].bl.append(std::string("0") + std::string(4095, '4') + | |
376 | std::string(4096, '5') + | |
377 | std::string(4095, '6') + std::string("0")); | |
378 | aligned_extents[3].extent_map = {{16384, 4096}, {16384 + 2 * 4096, 4096}, | |
379 | {16384 + 4 * 4096, 4096}}; | |
380 | ||
381 | expect_object_read(&aligned_extents); | |
382 | ASSERT_TRUE(mock_crypto_object_dispatch->read( | |
1e59de90 | 383 | 11, &extents, mock_image_ctx->get_data_io_context(), 0, 0, {}, |
f67539c2 TL |
384 | nullptr, &object_dispatch_flags, &dispatch_result, |
385 | &on_finish, on_dispatched)); | |
386 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
387 | ASSERT_EQ(on_finish, &finished_cond); | |
388 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
389 | ||
390 | dispatcher_ctx->complete(4096*8); | |
391 | ASSERT_EQ(3 + 4096 * 5 - 2, dispatched_cond.wait()); | |
392 | ASSERT_TRUE(extents[0].bl.to_str() == std::string("1")); | |
393 | ASSERT_TRUE(extents[1].bl.to_str() == std::string("2")); | |
394 | ASSERT_TRUE(extents[2].bl.to_str() == std::string("3")); | |
395 | ||
396 | auto expected_bl_data = (std::string(4095, '4') + std::string(4096, '5') + | |
397 | std::string(4095, '6')); | |
398 | ASSERT_TRUE(extents[3].bl.to_str() == expected_bl_data); | |
399 | ASSERT_THAT(extents[3].extent_map, | |
400 | ElementsAre(Pair(16384 + 1, 4095), Pair(16384 + 2 * 4096, 4096), | |
401 | Pair(16384 + 4 * 4096, 4095))); | |
402 | } | |
403 | ||
404 | TEST_F(TestMockCryptoCryptoObjectDispatch, AlignedWrite) { | |
405 | expect_encrypt(); | |
406 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 407 | 11, 0, std::move(data), mock_image_ctx->get_data_io_context(), 0, 0, |
f67539c2 TL |
408 | std::nullopt, {}, nullptr, nullptr, &dispatch_result, &on_finish, |
409 | on_dispatched)); | |
410 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_CONTINUE); | |
411 | ASSERT_EQ(on_finish, &finished_cond); // not modified | |
412 | on_finish->complete(0); | |
413 | ASSERT_EQ(0, finished_cond.wait()); | |
414 | } | |
415 | ||
416 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWrite) { | |
417 | ceph::bufferlist write_data; | |
418 | uint64_t version = 1234; | |
419 | write_data.append(std::string(8192, '1')); | |
420 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
421 | extents[0].bl.append(std::string(4096, '2')); | |
422 | extents[1].bl.append(std::string(4096, '3')); | |
423 | expect_object_read(&extents, version); | |
424 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 425 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
426 | 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result, |
427 | &on_finish, on_dispatched)); | |
428 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
429 | ASSERT_EQ(on_finish, &finished_cond); | |
430 | ||
431 | auto expected_data = | |
432 | std::string("2") + std::string(8192, '1') + std::string(4095, '3'); | |
433 | expect_object_write(0, expected_data, 0, std::make_optional(version)); | |
434 | dispatcher_ctx->complete(8192); // complete read | |
435 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
436 | dispatcher_ctx->complete(0); // complete write | |
437 | ASSERT_EQ(0, dispatched_cond.wait()); | |
438 | } | |
439 | ||
440 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteWithNoObject) { | |
441 | ceph::bufferlist write_data; | |
442 | write_data.append(std::string(8192, '1')); | |
443 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
444 | expect_object_read(&extents); | |
445 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 446 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
447 | 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result, |
448 | &on_finish, on_dispatched)); | |
449 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
450 | ASSERT_EQ(on_finish, &finished_cond); | |
451 | ||
452 | expect_get_object_size(); | |
453 | expect_get_parent_overlap(0); | |
454 | auto expected_data = (std::string(1, '\0') + std::string(8192, '1') + | |
455 | std::string(4095, '\0')); | |
456 | expect_object_write(0, expected_data, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, | |
457 | std::nullopt); | |
458 | dispatcher_ctx->complete(-ENOENT); // complete read | |
459 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
460 | dispatcher_ctx->complete(0); // complete write | |
461 | ASSERT_EQ(0, dispatched_cond.wait()); | |
462 | } | |
463 | ||
464 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteFailCreate) { | |
465 | ceph::bufferlist write_data; | |
466 | write_data.append(std::string(8192, '1')); | |
467 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
468 | expect_object_read(&extents); | |
469 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 470 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
471 | 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result, |
472 | &on_finish, on_dispatched)); | |
473 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
474 | ASSERT_EQ(on_finish, &finished_cond); | |
475 | ||
476 | expect_get_object_size(); | |
477 | expect_get_parent_overlap(0); | |
478 | auto expected_data = (std::string(1, '\0') + std::string(8192, '1') + | |
479 | std::string(4095, '\0')); | |
480 | expect_object_write(0, expected_data, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, | |
481 | std::nullopt); | |
482 | dispatcher_ctx->complete(-ENOENT); // complete read | |
483 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
484 | ||
485 | extents[0].bl.append(std::string(4096, '2')); | |
486 | extents[1].bl.append(std::string(4096, '3')); | |
487 | uint64_t version = 1234; | |
488 | expect_object_read(&extents, version); | |
489 | dispatcher_ctx->complete(-EEXIST); // complete write, request will restart | |
490 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
491 | ||
492 | auto expected_data2 = | |
493 | std::string("2") + std::string(8192, '1') + std::string(4095, '3'); | |
494 | expect_object_write(0, expected_data2, 0, std::make_optional(version)); | |
495 | dispatcher_ctx->complete(8192); // complete read | |
496 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
497 | dispatcher_ctx->complete(0); // complete write | |
498 | ASSERT_EQ(0, dispatched_cond.wait()); | |
499 | } | |
500 | ||
501 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteCopyup) { | |
502 | MockObjectMap mock_object_map; | |
503 | mock_image_ctx->object_map = &mock_object_map; | |
504 | MockExclusiveLock mock_exclusive_lock; | |
505 | mock_image_ctx->exclusive_lock = &mock_exclusive_lock; | |
506 | ||
507 | ceph::bufferlist write_data; | |
508 | write_data.append(std::string(8192, '1')); | |
509 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
510 | expect_object_read(&extents); | |
511 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 512 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
513 | 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result, |
514 | &on_finish, on_dispatched)); | |
515 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
516 | ASSERT_EQ(on_finish, &finished_cond); | |
517 | ||
518 | expect_get_object_size(); | |
1e59de90 TL |
519 | expect_get_parent_overlap(100 << 20); |
520 | expect_remap_to_logical(11 * mock_image_ctx->layout.object_size, | |
521 | mock_image_ctx->layout.object_size); | |
f67539c2 TL |
522 | expect_prune_parent_extents(mock_image_ctx->layout.object_size); |
523 | EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillRepeatedly( | |
524 | Return(true)); | |
1e59de90 | 525 | EXPECT_CALL(*mock_image_ctx->object_map, object_may_exist(11)).WillOnce( |
f67539c2 TL |
526 | Return(false)); |
527 | MockAbstractObjectWriteRequest *write_request = nullptr; | |
528 | expect_copyup(&write_request, 0); | |
529 | ||
530 | // unaligned write restarted | |
531 | uint64_t version = 1234; | |
532 | extents[0].bl.append(std::string(4096, '2')); | |
533 | extents[1].bl.append(std::string(4096, '3')); | |
534 | expect_object_read(&extents, version); | |
535 | dispatcher_ctx->complete(-ENOENT); // complete first read | |
536 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
537 | ||
538 | auto expected_data = | |
539 | std::string("2") + std::string(8192, '1') + std::string(4095, '3'); | |
540 | expect_object_write(0, expected_data, 0, std::make_optional(version)); | |
541 | dispatcher_ctx->complete(8192); // complete second read | |
542 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
543 | dispatcher_ctx->complete(0); // complete write | |
544 | ASSERT_EQ(0, dispatched_cond.wait()); | |
545 | } | |
546 | ||
547 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteEmptyCopyup) { | |
548 | MockObjectMap mock_object_map; | |
549 | mock_image_ctx->object_map = &mock_object_map; | |
550 | MockExclusiveLock mock_exclusive_lock; | |
551 | mock_image_ctx->exclusive_lock = &mock_exclusive_lock; | |
552 | ||
553 | ceph::bufferlist write_data; | |
554 | write_data.append(std::string(8192, '1')); | |
555 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
556 | expect_object_read(&extents); | |
557 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 558 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
559 | 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result, |
560 | &on_finish, on_dispatched)); | |
561 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
562 | ASSERT_EQ(on_finish, &finished_cond); | |
563 | ||
564 | expect_get_object_size(); | |
1e59de90 TL |
565 | expect_get_parent_overlap(100 << 20); |
566 | expect_remap_to_logical(11 * mock_image_ctx->layout.object_size, | |
567 | mock_image_ctx->layout.object_size); | |
f67539c2 TL |
568 | expect_prune_parent_extents(mock_image_ctx->layout.object_size); |
569 | EXPECT_CALL(mock_exclusive_lock, is_lock_owner()).WillRepeatedly( | |
570 | Return(true)); | |
1e59de90 | 571 | EXPECT_CALL(*mock_image_ctx->object_map, object_may_exist(11)).WillOnce( |
f67539c2 TL |
572 | Return(false)); |
573 | MockAbstractObjectWriteRequest *write_request = nullptr; | |
574 | expect_copyup(&write_request, 0); | |
575 | ||
576 | // unaligned write restarted | |
577 | expect_object_read(&extents); | |
578 | dispatcher_ctx->complete(-ENOENT); // complete first read | |
579 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
580 | ||
581 | auto expected_data = | |
582 | std::string(1, '\0') + std::string(8192, '1') + | |
583 | std::string(4095, '\0'); | |
584 | expect_object_write(0, expected_data, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, | |
585 | std::nullopt); | |
586 | dispatcher_ctx->complete(-ENOENT); // complete second read | |
587 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
588 | dispatcher_ctx->complete(0); // complete write | |
589 | ASSERT_EQ(0, dispatched_cond.wait()); | |
590 | } | |
591 | ||
592 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteFailVersionCheck) { | |
593 | ceph::bufferlist write_data; | |
594 | uint64_t version = 1234; | |
595 | write_data.append(std::string(8192, '1')); | |
596 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
597 | extents[0].bl.append(std::string(4096, '2')); | |
598 | extents[1].bl.append(std::string(4096, '3')); | |
599 | expect_object_read(&extents, version); | |
600 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 601 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
602 | 0, 0, std::nullopt, {}, nullptr, nullptr, &dispatch_result, |
603 | &on_finish, on_dispatched)); | |
604 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
605 | ASSERT_EQ(on_finish, &finished_cond); | |
606 | ||
607 | auto expected_data = | |
608 | std::string("2") + std::string(8192, '1') + std::string(4095, '3'); | |
609 | expect_object_write(0, expected_data, 0, std::make_optional(version)); | |
610 | dispatcher_ctx->complete(8192); // complete read | |
611 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
612 | ||
613 | version = 1235; | |
614 | expect_object_read(&extents, version); | |
615 | extents[0].bl.append(std::string(4096, '2')); | |
616 | extents[1].bl.append(std::string(4096, '3')); | |
617 | dispatcher_ctx->complete(-ERANGE); // complete write, request will restart | |
618 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
619 | ||
620 | expect_object_write(0, expected_data, 0, std::make_optional(version)); | |
621 | dispatcher_ctx->complete(8192); // complete read | |
622 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
623 | dispatcher_ctx->complete(0); // complete write | |
624 | ASSERT_EQ(0, dispatched_cond.wait()); | |
625 | } | |
626 | ||
627 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteWithAssertVersion) { | |
628 | ceph::bufferlist write_data; | |
629 | uint64_t version = 1234; | |
630 | uint64_t assert_version = 1233; | |
631 | write_data.append(std::string(8192, '1')); | |
632 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
633 | extents[0].bl.append(std::string(4096, '2')); | |
634 | extents[1].bl.append(std::string(4096, '3')); | |
635 | expect_object_read(&extents, version); | |
636 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 637 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
638 | 0, 0, std::make_optional(assert_version), {}, nullptr, nullptr, |
639 | &dispatch_result, &on_finish, on_dispatched)); | |
640 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
641 | ASSERT_EQ(on_finish, &finished_cond); | |
642 | ||
643 | dispatcher_ctx->complete(8192); // complete read | |
644 | ASSERT_EQ(-ERANGE, dispatched_cond.wait()); | |
645 | } | |
646 | ||
647 | TEST_F(TestMockCryptoCryptoObjectDispatch, UnalignedWriteWithExclusiveCreate) { | |
648 | ceph::bufferlist write_data; | |
649 | write_data.append(std::string(8192, '1')); | |
650 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}}; | |
651 | extents[0].bl.append(std::string(4096, '2')); | |
652 | extents[1].bl.append(std::string(4096, '3')); | |
653 | expect_object_read(&extents); | |
654 | ASSERT_TRUE(mock_crypto_object_dispatch->write( | |
1e59de90 | 655 | 11, 1, std::move(write_data), mock_image_ctx->get_data_io_context(), |
f67539c2 TL |
656 | 0, io::OBJECT_WRITE_FLAG_CREATE_EXCLUSIVE, std::nullopt, {}, nullptr, |
657 | nullptr, &dispatch_result, &on_finish, on_dispatched)); | |
658 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
659 | ASSERT_EQ(on_finish, &finished_cond); | |
660 | ||
661 | dispatcher_ctx->complete(8192); // complete read | |
662 | ASSERT_EQ(-EEXIST, dispatched_cond.wait()); | |
663 | } | |
664 | ||
665 | TEST_F(TestMockCryptoCryptoObjectDispatch, CompareAndWrite) { | |
666 | ceph::bufferlist write_data; | |
667 | uint64_t version = 1234; | |
668 | write_data.append(std::string(8192, '1')); | |
669 | ceph::bufferlist cmp_data; | |
670 | cmp_data.append(std::string(4096, '2')); | |
671 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}, {0, 8192}}; | |
672 | extents[0].bl.append(std::string(4096, '2')); | |
673 | extents[1].bl.append(std::string(4096, '3')); | |
674 | extents[2].bl.append(std::string(8192, '2')); | |
675 | expect_object_read(&extents, version); | |
676 | ||
677 | ASSERT_TRUE(mock_crypto_object_dispatch->compare_and_write( | |
1e59de90 | 678 | 11, 1, std::move(cmp_data), std::move(write_data), |
f67539c2 TL |
679 | mock_image_ctx->get_data_io_context(), 0, {}, nullptr, nullptr, |
680 | nullptr, &dispatch_result, &on_finish, on_dispatched)); | |
681 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
682 | ASSERT_EQ(on_finish, &finished_cond); | |
683 | ||
684 | auto expected_data = | |
685 | std::string("2") + std::string(8192, '1') + std::string(4095, '3'); | |
686 | expect_object_write(0, expected_data, 0, std::make_optional(version)); | |
687 | dispatcher_ctx->complete(4096*4); // complete read | |
688 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
689 | dispatcher_ctx->complete(0); // complete write | |
690 | ASSERT_EQ(0, dispatched_cond.wait()); | |
691 | } | |
692 | ||
693 | TEST_F(TestMockCryptoCryptoObjectDispatch, CompareAndWriteFail) { | |
694 | ceph::bufferlist write_data; | |
695 | uint64_t version = 1234; | |
696 | write_data.append(std::string(8192, '1')); | |
697 | ceph::bufferlist cmp_data; | |
698 | cmp_data.append(std::string(4094, '2') + std::string(2, '4')); | |
699 | io::ReadExtents extents = {{0, 4096}, {8192, 4096}, {0, 8192}}; | |
700 | extents[0].bl.append(std::string(4096, '2')); | |
701 | extents[1].bl.append(std::string(4096, '3')); | |
702 | extents[2].bl.append(std::string(8192, '2')); | |
703 | expect_object_read(&extents, version); | |
704 | ||
705 | uint64_t mismatch_offset; | |
706 | ASSERT_TRUE(mock_crypto_object_dispatch->compare_and_write( | |
1e59de90 | 707 | 11, 1, std::move(cmp_data), std::move(write_data), |
f67539c2 TL |
708 | mock_image_ctx->get_data_io_context(), 0, {}, &mismatch_offset, |
709 | nullptr, nullptr, &dispatch_result, &on_finish, on_dispatched)); | |
710 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
711 | ASSERT_EQ(on_finish, &finished_cond); | |
712 | ||
713 | dispatcher_ctx->complete(4096*4); // complete read | |
714 | ASSERT_EQ(-EILSEQ, dispatched_cond.wait()); | |
715 | ASSERT_EQ(mismatch_offset, 4094); | |
716 | } | |
717 | ||
718 | TEST_F(TestMockCryptoCryptoObjectDispatch, WriteSame) { | |
719 | io::LightweightBufferExtents buffer_extents; | |
720 | ceph::bufferlist write_data; | |
721 | write_data.append(std::string("12")); | |
722 | expect_object_write(0, std::string("12121") , 0, std::nullopt); | |
723 | ASSERT_TRUE(mock_crypto_object_dispatch->write_same( | |
1e59de90 | 724 | 11, 0, 5, {{0, 5}}, std::move(write_data), |
f67539c2 TL |
725 | mock_image_ctx->get_data_io_context(), 0, {}, nullptr, nullptr, |
726 | &dispatch_result, &on_finish, on_dispatched)); | |
727 | ASSERT_EQ(dispatch_result, io::DISPATCH_RESULT_COMPLETE); | |
728 | ||
729 | ASSERT_EQ(ETIMEDOUT, dispatched_cond.wait_for(0)); | |
730 | dispatcher_ctx->complete(0); | |
731 | ASSERT_EQ(0, dispatched_cond.wait()); | |
732 | } | |
733 | ||
734 | TEST_F(TestMockCryptoCryptoObjectDispatch, PrepareCopyup) { | |
735 | char* data = (char*)"0123456789"; | |
736 | io::SnapshotSparseBufferlist snapshot_sparse_bufferlist; | |
737 | auto& snap1 = snapshot_sparse_bufferlist[0]; | |
738 | auto& snap2 = snapshot_sparse_bufferlist[1]; | |
739 | ||
740 | snap1.insert(0, 1, {io::SPARSE_EXTENT_STATE_DATA, 1, | |
741 | ceph::bufferlist::static_from_mem(data + 1, 1)}); | |
742 | snap1.insert(8191, 1, {io::SPARSE_EXTENT_STATE_DATA, 1, | |
743 | ceph::bufferlist::static_from_mem(data + 2, 1)}); | |
744 | snap1.insert(8193, 3, {io::SPARSE_EXTENT_STATE_DATA, 3, | |
745 | ceph::bufferlist::static_from_mem(data + 3, 3)}); | |
746 | ||
747 | snap2.insert(0, 2, {io::SPARSE_EXTENT_STATE_ZEROED, 2}); | |
748 | snap2.insert(8191, 3, {io::SPARSE_EXTENT_STATE_DATA, 3, | |
749 | ceph::bufferlist::static_from_mem(data + 6, 3)}); | |
750 | snap2.insert(16384, 1, {io::SPARSE_EXTENT_STATE_DATA, 1, | |
751 | ceph::bufferlist::static_from_mem(data + 9, 1)}); | |
752 | ||
753 | expect_get_object_size(); | |
754 | expect_encrypt(6); | |
755 | InSequence seq; | |
1e59de90 TL |
756 | uint64_t base = 11 * mock_image_ctx->layout.object_size; |
757 | expect_remap_to_logical(base, 4096); | |
758 | expect_remap_to_logical(base + 4096, 4096); | |
759 | expect_remap_to_logical(base + 8192, 4096); | |
760 | expect_remap_to_logical(base, 4096); | |
761 | expect_remap_to_logical(base + 4096, 8192); | |
762 | expect_remap_to_logical(base + 16384, 4096); | |
f67539c2 | 763 | ASSERT_EQ(0, mock_crypto_object_dispatch->prepare_copyup( |
1e59de90 | 764 | 11, &snapshot_sparse_bufferlist)); |
f67539c2 TL |
765 | |
766 | ASSERT_EQ(2, snapshot_sparse_bufferlist.size()); | |
767 | ||
768 | auto& snap1_result = snapshot_sparse_bufferlist[0]; | |
769 | auto& snap2_result = snapshot_sparse_bufferlist[1]; | |
770 | ||
771 | auto it = snap1_result.begin(); | |
772 | ASSERT_NE(it, snap1_result.end()); | |
773 | ASSERT_EQ(0, it.get_off()); | |
774 | ASSERT_EQ(4096 * 3, it.get_len()); | |
775 | ||
776 | ASSERT_TRUE(it.get_val().bl.to_str() == | |
777 | std::string("1") + std::string(4095, '\0') + | |
778 | std::string(4095, '\0') + std::string("2") + | |
779 | std::string(1, '\0') + std::string("345") + std::string(4092, '\0')); | |
780 | ASSERT_EQ(++it, snap1_result.end()); | |
781 | ||
782 | it = snap2_result.begin(); | |
783 | ASSERT_NE(it, snap2_result.end()); | |
784 | ASSERT_EQ(0, it.get_off()); | |
785 | ASSERT_EQ(4096 * 3, it.get_len()); | |
786 | ASSERT_TRUE(it.get_val().bl.to_str() == | |
787 | std::string(4096, '\0') + | |
788 | std::string(4095, '\0') + std::string("6") + | |
789 | std::string("7845") + std::string(4092, '\0')); | |
790 | ||
791 | ASSERT_NE(++it, snap2_result.end()); | |
792 | ASSERT_EQ(16384, it.get_off()); | |
793 | ASSERT_EQ(4096, it.get_len()); | |
794 | ASSERT_TRUE(it.get_val().bl.to_str() == | |
795 | std::string("9") + std::string(4095, '\0')); | |
796 | ASSERT_EQ(++it, snap2_result.end()); | |
797 | } | |
798 | ||
1e59de90 | 799 | } // namespace crypto |
f67539c2 | 800 | } // namespace librbd |