1 // Copyright (C) 2018 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <sys/stat.h>
16
17 #include <cstdio>
18 #include <iostream>
19 #include <memory>
20 #include <string_view>
21
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <gtest/gtest.h>
25 #include <libsnapshot/cow_reader.h>
26 #include <libsnapshot/cow_writer.h>
27
28 using testing::AssertionFailure;
29 using testing::AssertionResult;
30 using testing::AssertionSuccess;
31
32 namespace android {
33 namespace snapshot {
34
35 class CowTest : public ::testing::Test {
36 protected:
SetUp()37 virtual void SetUp() override {
38 cow_ = std::make_unique<TemporaryFile>();
39 ASSERT_GE(cow_->fd, 0) << strerror(errno);
40 }
41
TearDown()42 virtual void TearDown() override { cow_ = nullptr; }
43
44 std::unique_ptr<TemporaryFile> cow_;
45 };
46
47 // Sink that always appends to the end of a string.
48 class StringSink : public IByteSink {
49 public:
GetBuffer(size_t requested,size_t * actual)50 void* GetBuffer(size_t requested, size_t* actual) override {
51 size_t old_size = stream_.size();
52 stream_.resize(old_size + requested, '\0');
53 *actual = requested;
54 return stream_.data() + old_size;
55 }
ReturnData(void *,size_t)56 bool ReturnData(void*, size_t) override { return true; }
Reset()57 void Reset() { stream_.clear(); }
58
stream()59 std::string& stream() { return stream_; }
60
61 private:
62 std::string stream_;
63 };
64
TEST_F(CowTest,ReadWrite)65 TEST_F(CowTest, ReadWrite) {
66 CowOptions options;
67 options.cluster_ops = 0;
68 CowWriter writer(options);
69
70 ASSERT_TRUE(writer.Initialize(cow_->fd));
71
72 std::string data = "This is some data, believe it";
73 data.resize(options.block_size, '\0');
74
75 ASSERT_TRUE(writer.AddCopy(10, 20));
76 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
77 ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
78 ASSERT_TRUE(writer.Finalize());
79
80 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
81
82 CowReader reader;
83 CowHeader header;
84 CowFooter footer;
85 ASSERT_TRUE(reader.Parse(cow_->fd));
86 ASSERT_TRUE(reader.GetHeader(&header));
87 ASSERT_TRUE(reader.GetFooter(&footer));
88 ASSERT_EQ(header.magic, kCowMagicNumber);
89 ASSERT_EQ(header.major_version, kCowVersionMajor);
90 ASSERT_EQ(header.minor_version, kCowVersionMinor);
91 ASSERT_EQ(header.block_size, options.block_size);
92 ASSERT_EQ(footer.op.num_ops, 4);
93
94 auto iter = reader.GetOpIter();
95 ASSERT_NE(iter, nullptr);
96 ASSERT_FALSE(iter->Done());
97 auto op = &iter->Get();
98
99 ASSERT_EQ(op->type, kCowCopyOp);
100 ASSERT_EQ(op->compression, kCowCompressNone);
101 ASSERT_EQ(op->data_length, 0);
102 ASSERT_EQ(op->new_block, 10);
103 ASSERT_EQ(op->source, 20);
104
105 StringSink sink;
106
107 iter->Next();
108 ASSERT_FALSE(iter->Done());
109 op = &iter->Get();
110
111 ASSERT_EQ(op->type, kCowReplaceOp);
112 ASSERT_EQ(op->compression, kCowCompressNone);
113 ASSERT_EQ(op->data_length, 4096);
114 ASSERT_EQ(op->new_block, 50);
115 ASSERT_TRUE(reader.ReadData(*op, &sink));
116 ASSERT_EQ(sink.stream(), data);
117
118 iter->Next();
119 ASSERT_FALSE(iter->Done());
120 op = &iter->Get();
121
122 // Note: the zero operation gets split into two blocks.
123 ASSERT_EQ(op->type, kCowZeroOp);
124 ASSERT_EQ(op->compression, kCowCompressNone);
125 ASSERT_EQ(op->data_length, 0);
126 ASSERT_EQ(op->new_block, 51);
127 ASSERT_EQ(op->source, 0);
128
129 iter->Next();
130 ASSERT_FALSE(iter->Done());
131 op = &iter->Get();
132
133 ASSERT_EQ(op->type, kCowZeroOp);
134 ASSERT_EQ(op->compression, kCowCompressNone);
135 ASSERT_EQ(op->data_length, 0);
136 ASSERT_EQ(op->new_block, 52);
137 ASSERT_EQ(op->source, 0);
138
139 iter->Next();
140 ASSERT_TRUE(iter->Done());
141 }
142
TEST_F(CowTest,CompressGz)143 TEST_F(CowTest, CompressGz) {
144 CowOptions options;
145 options.cluster_ops = 0;
146 options.compression = "gz";
147 CowWriter writer(options);
148
149 ASSERT_TRUE(writer.Initialize(cow_->fd));
150
151 std::string data = "This is some data, believe it";
152 data.resize(options.block_size, '\0');
153
154 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
155 ASSERT_TRUE(writer.Finalize());
156
157 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
158
159 CowReader reader;
160 ASSERT_TRUE(reader.Parse(cow_->fd));
161
162 auto iter = reader.GetOpIter();
163 ASSERT_NE(iter, nullptr);
164 ASSERT_FALSE(iter->Done());
165 auto op = &iter->Get();
166
167 StringSink sink;
168
169 ASSERT_EQ(op->type, kCowReplaceOp);
170 ASSERT_EQ(op->compression, kCowCompressGz);
171 ASSERT_EQ(op->data_length, 56); // compressed!
172 ASSERT_EQ(op->new_block, 50);
173 ASSERT_TRUE(reader.ReadData(*op, &sink));
174 ASSERT_EQ(sink.stream(), data);
175
176 iter->Next();
177 ASSERT_TRUE(iter->Done());
178 }
179
TEST_F(CowTest,ClusterCompressGz)180 TEST_F(CowTest, ClusterCompressGz) {
181 CowOptions options;
182 options.compression = "gz";
183 options.cluster_ops = 2;
184 CowWriter writer(options);
185
186 ASSERT_TRUE(writer.Initialize(cow_->fd));
187
188 std::string data = "This is some data, believe it";
189 data.resize(options.block_size, '\0');
190 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
191
192 std::string data2 = "More data!";
193 data2.resize(options.block_size, '\0');
194 ASSERT_TRUE(writer.AddRawBlocks(51, data2.data(), data2.size()));
195
196 ASSERT_TRUE(writer.Finalize());
197
198 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
199
200 CowReader reader;
201 ASSERT_TRUE(reader.Parse(cow_->fd));
202
203 auto iter = reader.GetOpIter();
204 ASSERT_NE(iter, nullptr);
205 ASSERT_FALSE(iter->Done());
206 auto op = &iter->Get();
207
208 StringSink sink;
209
210 ASSERT_EQ(op->type, kCowReplaceOp);
211 ASSERT_EQ(op->compression, kCowCompressGz);
212 ASSERT_EQ(op->data_length, 56); // compressed!
213 ASSERT_EQ(op->new_block, 50);
214 ASSERT_TRUE(reader.ReadData(*op, &sink));
215 ASSERT_EQ(sink.stream(), data);
216
217 iter->Next();
218 ASSERT_FALSE(iter->Done());
219 op = &iter->Get();
220
221 ASSERT_EQ(op->type, kCowClusterOp);
222
223 iter->Next();
224 ASSERT_FALSE(iter->Done());
225 op = &iter->Get();
226
227 sink.Reset();
228 ASSERT_EQ(op->compression, kCowCompressGz);
229 ASSERT_EQ(op->data_length, 41); // compressed!
230 ASSERT_EQ(op->new_block, 51);
231 ASSERT_TRUE(reader.ReadData(*op, &sink));
232 ASSERT_EQ(sink.stream(), data2);
233
234 iter->Next();
235 ASSERT_FALSE(iter->Done());
236 op = &iter->Get();
237
238 ASSERT_EQ(op->type, kCowClusterOp);
239
240 iter->Next();
241 ASSERT_TRUE(iter->Done());
242 }
243
TEST_F(CowTest,CompressTwoBlocks)244 TEST_F(CowTest, CompressTwoBlocks) {
245 CowOptions options;
246 options.compression = "gz";
247 options.cluster_ops = 0;
248 CowWriter writer(options);
249
250 ASSERT_TRUE(writer.Initialize(cow_->fd));
251
252 std::string data = "This is some data, believe it";
253 data.resize(options.block_size * 2, '\0');
254
255 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
256 ASSERT_TRUE(writer.Finalize());
257
258 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
259
260 CowReader reader;
261 ASSERT_TRUE(reader.Parse(cow_->fd));
262
263 auto iter = reader.GetOpIter();
264 ASSERT_NE(iter, nullptr);
265 ASSERT_FALSE(iter->Done());
266 iter->Next();
267 ASSERT_FALSE(iter->Done());
268
269 StringSink sink;
270
271 auto op = &iter->Get();
272 ASSERT_EQ(op->type, kCowReplaceOp);
273 ASSERT_EQ(op->compression, kCowCompressGz);
274 ASSERT_EQ(op->new_block, 51);
275 ASSERT_TRUE(reader.ReadData(*op, &sink));
276 }
277
278 // Only return 1-byte buffers, to stress test the partial read logic in
279 // CowReader.
280 class HorribleStringSink : public StringSink {
281 public:
GetBuffer(size_t,size_t * actual)282 void* GetBuffer(size_t, size_t* actual) override { return StringSink::GetBuffer(1, actual); }
283 };
284
285 class CompressionTest : public CowTest, public testing::WithParamInterface<const char*> {};
286
TEST_P(CompressionTest,HorribleSink)287 TEST_P(CompressionTest, HorribleSink) {
288 CowOptions options;
289 options.compression = GetParam();
290 options.cluster_ops = 0;
291 CowWriter writer(options);
292
293 ASSERT_TRUE(writer.Initialize(cow_->fd));
294
295 std::string data = "This is some data, believe it";
296 data.resize(options.block_size, '\0');
297
298 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
299 ASSERT_TRUE(writer.Finalize());
300
301 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
302
303 CowReader reader;
304 ASSERT_TRUE(reader.Parse(cow_->fd));
305
306 auto iter = reader.GetOpIter();
307 ASSERT_NE(iter, nullptr);
308 ASSERT_FALSE(iter->Done());
309
310 HorribleStringSink sink;
311 auto op = &iter->Get();
312 ASSERT_TRUE(reader.ReadData(*op, &sink));
313 ASSERT_EQ(sink.stream(), data);
314 }
315
316 INSTANTIATE_TEST_SUITE_P(CowApi, CompressionTest, testing::Values("none", "gz", "brotli"));
317
TEST_F(CowTest,GetSize)318 TEST_F(CowTest, GetSize) {
319 CowOptions options;
320 options.cluster_ops = 0;
321 CowWriter writer(options);
322 if (ftruncate(cow_->fd, 0) < 0) {
323 perror("Fails to set temp file size");
324 FAIL();
325 }
326 ASSERT_TRUE(writer.Initialize(cow_->fd));
327
328 std::string data = "This is some data, believe it";
329 data.resize(options.block_size, '\0');
330
331 ASSERT_TRUE(writer.AddCopy(10, 20));
332 ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
333 ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
334 auto size_before = writer.GetCowSize();
335 ASSERT_TRUE(writer.Finalize());
336 auto size_after = writer.GetCowSize();
337 ASSERT_EQ(size_before, size_after);
338 struct stat buf;
339
340 ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
341 ASSERT_EQ(buf.st_size, writer.GetCowSize());
342 }
343
TEST_F(CowTest,AppendLabelSmall)344 TEST_F(CowTest, AppendLabelSmall) {
345 CowOptions options;
346 options.cluster_ops = 0;
347 auto writer = std::make_unique<CowWriter>(options);
348 ASSERT_TRUE(writer->Initialize(cow_->fd));
349
350 std::string data = "This is some data, believe it";
351 data.resize(options.block_size, '\0');
352 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
353 ASSERT_TRUE(writer->AddLabel(3));
354 ASSERT_TRUE(writer->Finalize());
355
356 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
357
358 writer = std::make_unique<CowWriter>(options);
359 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 3));
360
361 std::string data2 = "More data!";
362 data2.resize(options.block_size, '\0');
363 ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
364 ASSERT_TRUE(writer->Finalize());
365
366 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
367
368 struct stat buf;
369 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
370 ASSERT_EQ(buf.st_size, writer->GetCowSize());
371
372 // Read back both operations, and label.
373 CowReader reader;
374 uint64_t label;
375 ASSERT_TRUE(reader.Parse(cow_->fd));
376 ASSERT_TRUE(reader.GetLastLabel(&label));
377 ASSERT_EQ(label, 3);
378
379 StringSink sink;
380
381 auto iter = reader.GetOpIter();
382 ASSERT_NE(iter, nullptr);
383
384 ASSERT_FALSE(iter->Done());
385 auto op = &iter->Get();
386 ASSERT_EQ(op->type, kCowReplaceOp);
387 ASSERT_TRUE(reader.ReadData(*op, &sink));
388 ASSERT_EQ(sink.stream(), data);
389
390 iter->Next();
391 sink.Reset();
392
393 ASSERT_FALSE(iter->Done());
394 op = &iter->Get();
395 ASSERT_EQ(op->type, kCowLabelOp);
396 ASSERT_EQ(op->source, 3);
397
398 iter->Next();
399
400 ASSERT_FALSE(iter->Done());
401 op = &iter->Get();
402 ASSERT_EQ(op->type, kCowReplaceOp);
403 ASSERT_TRUE(reader.ReadData(*op, &sink));
404 ASSERT_EQ(sink.stream(), data2);
405
406 iter->Next();
407 ASSERT_TRUE(iter->Done());
408 }
409
TEST_F(CowTest,AppendLabelMissing)410 TEST_F(CowTest, AppendLabelMissing) {
411 CowOptions options;
412 options.cluster_ops = 0;
413 auto writer = std::make_unique<CowWriter>(options);
414 ASSERT_TRUE(writer->Initialize(cow_->fd));
415
416 ASSERT_TRUE(writer->AddLabel(0));
417 std::string data = "This is some data, believe it";
418 data.resize(options.block_size, '\0');
419 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
420 ASSERT_TRUE(writer->AddLabel(1));
421 // Drop the tail end of the last op header, corrupting it.
422 ftruncate(cow_->fd, writer->GetCowSize() - sizeof(CowFooter) - 3);
423
424 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
425
426 writer = std::make_unique<CowWriter>(options);
427 ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 1));
428 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 0));
429
430 ASSERT_TRUE(writer->AddZeroBlocks(51, 1));
431 ASSERT_TRUE(writer->Finalize());
432
433 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
434
435 struct stat buf;
436 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
437 ASSERT_EQ(buf.st_size, writer->GetCowSize());
438
439 // Read back both operations.
440 CowReader reader;
441 ASSERT_TRUE(reader.Parse(cow_->fd));
442
443 StringSink sink;
444
445 auto iter = reader.GetOpIter();
446 ASSERT_NE(iter, nullptr);
447
448 ASSERT_FALSE(iter->Done());
449 auto op = &iter->Get();
450 ASSERT_EQ(op->type, kCowLabelOp);
451 ASSERT_EQ(op->source, 0);
452
453 iter->Next();
454
455 ASSERT_FALSE(iter->Done());
456 op = &iter->Get();
457 ASSERT_EQ(op->type, kCowZeroOp);
458
459 iter->Next();
460
461 ASSERT_TRUE(iter->Done());
462 }
463
TEST_F(CowTest,AppendExtendedCorrupted)464 TEST_F(CowTest, AppendExtendedCorrupted) {
465 CowOptions options;
466 options.cluster_ops = 0;
467 auto writer = std::make_unique<CowWriter>(options);
468 ASSERT_TRUE(writer->Initialize(cow_->fd));
469
470 ASSERT_TRUE(writer->AddLabel(5));
471
472 std::string data = "This is some data, believe it";
473 data.resize(options.block_size * 2, '\0');
474 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
475 ASSERT_TRUE(writer->AddLabel(6));
476
477 // fail to write the footer. Cow Format does not know if Label 6 is valid
478
479 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
480
481 // Get the last known good label
482 CowReader label_reader;
483 uint64_t label;
484 ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
485 ASSERT_TRUE(label_reader.GetLastLabel(&label));
486 ASSERT_EQ(label, 5);
487
488 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
489
490 writer = std::make_unique<CowWriter>(options);
491 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
492
493 ASSERT_TRUE(writer->Finalize());
494
495 struct stat buf;
496 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
497 ASSERT_EQ(buf.st_size, writer->GetCowSize());
498
499 // Read back all valid operations
500 CowReader reader;
501 ASSERT_TRUE(reader.Parse(cow_->fd));
502
503 StringSink sink;
504
505 auto iter = reader.GetOpIter();
506 ASSERT_NE(iter, nullptr);
507
508 ASSERT_FALSE(iter->Done());
509 auto op = &iter->Get();
510 ASSERT_EQ(op->type, kCowLabelOp);
511 ASSERT_EQ(op->source, 5);
512
513 iter->Next();
514 ASSERT_TRUE(iter->Done());
515 }
516
TEST_F(CowTest,AppendbyLabel)517 TEST_F(CowTest, AppendbyLabel) {
518 CowOptions options;
519 options.cluster_ops = 0;
520 auto writer = std::make_unique<CowWriter>(options);
521 ASSERT_TRUE(writer->Initialize(cow_->fd));
522
523 std::string data = "This is some data, believe it";
524 data.resize(options.block_size * 2, '\0');
525 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
526
527 ASSERT_TRUE(writer->AddLabel(4));
528
529 ASSERT_TRUE(writer->AddZeroBlocks(50, 2));
530
531 ASSERT_TRUE(writer->AddLabel(5));
532
533 ASSERT_TRUE(writer->AddCopy(5, 6));
534
535 ASSERT_TRUE(writer->AddLabel(6));
536
537 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
538
539 writer = std::make_unique<CowWriter>(options);
540 ASSERT_FALSE(writer->InitializeAppend(cow_->fd, 12));
541 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 5));
542
543 // This should drop label 6
544 ASSERT_TRUE(writer->Finalize());
545
546 struct stat buf;
547 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
548 ASSERT_EQ(buf.st_size, writer->GetCowSize());
549
550 // Read back all ops
551 CowReader reader;
552 ASSERT_TRUE(reader.Parse(cow_->fd));
553
554 StringSink sink;
555
556 auto iter = reader.GetOpIter();
557 ASSERT_NE(iter, nullptr);
558
559 ASSERT_FALSE(iter->Done());
560 auto op = &iter->Get();
561 ASSERT_EQ(op->type, kCowReplaceOp);
562 ASSERT_TRUE(reader.ReadData(*op, &sink));
563 ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
564
565 iter->Next();
566 sink.Reset();
567
568 ASSERT_FALSE(iter->Done());
569 op = &iter->Get();
570 ASSERT_EQ(op->type, kCowReplaceOp);
571 ASSERT_TRUE(reader.ReadData(*op, &sink));
572 ASSERT_EQ(sink.stream(), data.substr(options.block_size, 2 * options.block_size));
573
574 iter->Next();
575 sink.Reset();
576
577 ASSERT_FALSE(iter->Done());
578 op = &iter->Get();
579 ASSERT_EQ(op->type, kCowLabelOp);
580 ASSERT_EQ(op->source, 4);
581
582 iter->Next();
583
584 ASSERT_FALSE(iter->Done());
585 op = &iter->Get();
586 ASSERT_EQ(op->type, kCowZeroOp);
587
588 iter->Next();
589
590 ASSERT_FALSE(iter->Done());
591 op = &iter->Get();
592 ASSERT_EQ(op->type, kCowZeroOp);
593
594 iter->Next();
595 ASSERT_FALSE(iter->Done());
596 op = &iter->Get();
597 ASSERT_EQ(op->type, kCowLabelOp);
598 ASSERT_EQ(op->source, 5);
599
600 iter->Next();
601
602 ASSERT_TRUE(iter->Done());
603 }
604
TEST_F(CowTest,ClusterTest)605 TEST_F(CowTest, ClusterTest) {
606 CowOptions options;
607 options.cluster_ops = 4;
608 auto writer = std::make_unique<CowWriter>(options);
609 ASSERT_TRUE(writer->Initialize(cow_->fd));
610
611 std::string data = "This is some data, believe it";
612 data.resize(options.block_size, '\0');
613 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
614
615 ASSERT_TRUE(writer->AddLabel(4));
616
617 ASSERT_TRUE(writer->AddZeroBlocks(50, 2)); // Cluster split in middle
618
619 ASSERT_TRUE(writer->AddLabel(5));
620
621 ASSERT_TRUE(writer->AddCopy(5, 6));
622
623 // Cluster split
624
625 ASSERT_TRUE(writer->AddLabel(6));
626
627 ASSERT_TRUE(writer->Finalize()); // No data for cluster, so no cluster split needed
628
629 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
630
631 // Read back all ops
632 CowReader reader;
633 ASSERT_TRUE(reader.Parse(cow_->fd));
634
635 StringSink sink;
636
637 auto iter = reader.GetOpIter();
638 ASSERT_NE(iter, nullptr);
639
640 ASSERT_FALSE(iter->Done());
641 auto op = &iter->Get();
642 ASSERT_EQ(op->type, kCowReplaceOp);
643 ASSERT_TRUE(reader.ReadData(*op, &sink));
644 ASSERT_EQ(sink.stream(), data.substr(0, options.block_size));
645
646 iter->Next();
647 sink.Reset();
648
649 ASSERT_FALSE(iter->Done());
650 op = &iter->Get();
651 ASSERT_EQ(op->type, kCowLabelOp);
652 ASSERT_EQ(op->source, 4);
653
654 iter->Next();
655
656 ASSERT_FALSE(iter->Done());
657 op = &iter->Get();
658 ASSERT_EQ(op->type, kCowZeroOp);
659
660 iter->Next();
661
662 ASSERT_FALSE(iter->Done());
663 op = &iter->Get();
664 ASSERT_EQ(op->type, kCowClusterOp);
665
666 iter->Next();
667
668 ASSERT_FALSE(iter->Done());
669 op = &iter->Get();
670 ASSERT_EQ(op->type, kCowZeroOp);
671
672 iter->Next();
673
674 ASSERT_FALSE(iter->Done());
675 op = &iter->Get();
676 ASSERT_EQ(op->type, kCowLabelOp);
677 ASSERT_EQ(op->source, 5);
678
679 iter->Next();
680
681 ASSERT_FALSE(iter->Done());
682 op = &iter->Get();
683 ASSERT_EQ(op->type, kCowCopyOp);
684
685 iter->Next();
686
687 ASSERT_FALSE(iter->Done());
688 op = &iter->Get();
689 ASSERT_EQ(op->type, kCowClusterOp);
690
691 iter->Next();
692
693 ASSERT_FALSE(iter->Done());
694 op = &iter->Get();
695 ASSERT_EQ(op->type, kCowLabelOp);
696 ASSERT_EQ(op->source, 6);
697
698 iter->Next();
699
700 ASSERT_TRUE(iter->Done());
701 }
702
TEST_F(CowTest,ClusterAppendTest)703 TEST_F(CowTest, ClusterAppendTest) {
704 CowOptions options;
705 options.cluster_ops = 3;
706 auto writer = std::make_unique<CowWriter>(options);
707 ASSERT_TRUE(writer->Initialize(cow_->fd));
708
709 ASSERT_TRUE(writer->AddLabel(50));
710 ASSERT_TRUE(writer->Finalize()); // Adds a cluster op, should be dropped on append
711
712 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
713
714 writer = std::make_unique<CowWriter>(options);
715 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 50));
716
717 std::string data2 = "More data!";
718 data2.resize(options.block_size, '\0');
719 ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
720 ASSERT_TRUE(writer->Finalize()); // Adds a cluster op
721
722 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
723
724 struct stat buf;
725 ASSERT_EQ(fstat(cow_->fd, &buf), 0);
726 ASSERT_EQ(buf.st_size, writer->GetCowSize());
727
728 // Read back both operations, plus cluster op at end
729 CowReader reader;
730 uint64_t label;
731 ASSERT_TRUE(reader.Parse(cow_->fd));
732 ASSERT_TRUE(reader.GetLastLabel(&label));
733 ASSERT_EQ(label, 50);
734
735 StringSink sink;
736
737 auto iter = reader.GetOpIter();
738 ASSERT_NE(iter, nullptr);
739
740 ASSERT_FALSE(iter->Done());
741 auto op = &iter->Get();
742 ASSERT_EQ(op->type, kCowLabelOp);
743 ASSERT_EQ(op->source, 50);
744
745 iter->Next();
746
747 ASSERT_FALSE(iter->Done());
748 op = &iter->Get();
749 ASSERT_EQ(op->type, kCowReplaceOp);
750 ASSERT_TRUE(reader.ReadData(*op, &sink));
751 ASSERT_EQ(sink.stream(), data2);
752
753 iter->Next();
754
755 ASSERT_FALSE(iter->Done());
756 op = &iter->Get();
757 ASSERT_EQ(op->type, kCowClusterOp);
758
759 iter->Next();
760
761 ASSERT_TRUE(iter->Done());
762 }
763
TEST_F(CowTest,AppendAfterFinalize)764 TEST_F(CowTest, AppendAfterFinalize) {
765 CowOptions options;
766 options.cluster_ops = 0;
767 auto writer = std::make_unique<CowWriter>(options);
768 ASSERT_TRUE(writer->Initialize(cow_->fd));
769
770 std::string data = "This is some data, believe it";
771 data.resize(options.block_size, '\0');
772 ASSERT_TRUE(writer->AddRawBlocks(50, data.data(), data.size()));
773 ASSERT_TRUE(writer->AddLabel(3));
774 ASSERT_TRUE(writer->Finalize());
775
776 std::string data2 = "More data!";
777 data2.resize(options.block_size, '\0');
778 ASSERT_TRUE(writer->AddRawBlocks(51, data2.data(), data2.size()));
779 ASSERT_TRUE(writer->Finalize());
780
781 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
782
783 // COW should be valid.
784 CowReader reader;
785 ASSERT_TRUE(reader.Parse(cow_->fd));
786 }
787
WriteDataBlock(CowWriter * writer,uint64_t new_block,std::string data)788 AssertionResult WriteDataBlock(CowWriter* writer, uint64_t new_block, std::string data) {
789 data.resize(writer->options().block_size, '\0');
790 if (!writer->AddRawBlocks(new_block, data.data(), data.size())) {
791 return AssertionFailure() << "Failed to add raw block";
792 }
793 return AssertionSuccess();
794 }
795
CompareDataBlock(CowReader * reader,const CowOperation & op,const std::string & data)796 AssertionResult CompareDataBlock(CowReader* reader, const CowOperation& op,
797 const std::string& data) {
798 CowHeader header;
799 reader->GetHeader(&header);
800
801 std::string cmp = data;
802 cmp.resize(header.block_size, '\0');
803
804 StringSink sink;
805 if (!reader->ReadData(op, &sink)) {
806 return AssertionFailure() << "Failed to read data block";
807 }
808 if (cmp != sink.stream()) {
809 return AssertionFailure() << "Data blocks did not match, expected " << cmp << ", got "
810 << sink.stream();
811 }
812
813 return AssertionSuccess();
814 }
815
TEST_F(CowTest,ResumeMidCluster)816 TEST_F(CowTest, ResumeMidCluster) {
817 CowOptions options;
818 options.cluster_ops = 7;
819 auto writer = std::make_unique<CowWriter>(options);
820 ASSERT_TRUE(writer->Initialize(cow_->fd));
821
822 ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
823 ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
824 ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
825 ASSERT_TRUE(writer->AddLabel(1));
826 ASSERT_TRUE(writer->Finalize());
827 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
828 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
829
830 writer = std::make_unique<CowWriter>(options);
831 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
832 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
833 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
834 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
835 ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
836 ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
837 ASSERT_TRUE(writer->AddLabel(2));
838 ASSERT_TRUE(writer->Finalize());
839 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
840
841 CowReader reader;
842 ASSERT_TRUE(reader.Parse(cow_->fd));
843
844 auto iter = reader.GetOpIter();
845 size_t num_replace = 0;
846 size_t max_in_cluster = 0;
847 size_t num_in_cluster = 0;
848 size_t num_clusters = 0;
849 while (!iter->Done()) {
850 const auto& op = iter->Get();
851
852 num_in_cluster++;
853 max_in_cluster = std::max(max_in_cluster, num_in_cluster);
854
855 if (op.type == kCowReplaceOp) {
856 num_replace++;
857
858 ASSERT_EQ(op.new_block, num_replace);
859 ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
860 } else if (op.type == kCowClusterOp) {
861 num_in_cluster = 0;
862 num_clusters++;
863 }
864
865 iter->Next();
866 }
867 ASSERT_EQ(num_replace, 8);
868 ASSERT_EQ(max_in_cluster, 7);
869 ASSERT_EQ(num_clusters, 2);
870 }
871
TEST_F(CowTest,ResumeEndCluster)872 TEST_F(CowTest, ResumeEndCluster) {
873 CowOptions options;
874 int cluster_ops = 5;
875 options.cluster_ops = cluster_ops;
876 auto writer = std::make_unique<CowWriter>(options);
877 ASSERT_TRUE(writer->Initialize(cow_->fd));
878
879 ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
880 ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
881 ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
882 ASSERT_TRUE(writer->AddLabel(1));
883 ASSERT_TRUE(writer->Finalize());
884 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
885 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
886 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
887 ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
888 ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
889 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
890
891 writer = std::make_unique<CowWriter>(options);
892 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
893 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
894 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
895 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
896 ASSERT_TRUE(WriteDataBlock(writer.get(), 7, "Block 7"));
897 ASSERT_TRUE(WriteDataBlock(writer.get(), 8, "Block 8"));
898 ASSERT_TRUE(writer->AddLabel(2));
899 ASSERT_TRUE(writer->Finalize());
900 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
901
902 CowReader reader;
903 ASSERT_TRUE(reader.Parse(cow_->fd));
904
905 auto iter = reader.GetOpIter();
906 size_t num_replace = 0;
907 size_t max_in_cluster = 0;
908 size_t num_in_cluster = 0;
909 size_t num_clusters = 0;
910 while (!iter->Done()) {
911 const auto& op = iter->Get();
912
913 num_in_cluster++;
914 max_in_cluster = std::max(max_in_cluster, num_in_cluster);
915
916 if (op.type == kCowReplaceOp) {
917 num_replace++;
918
919 ASSERT_EQ(op.new_block, num_replace);
920 ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
921 } else if (op.type == kCowClusterOp) {
922 num_in_cluster = 0;
923 num_clusters++;
924 }
925
926 iter->Next();
927 }
928 ASSERT_EQ(num_replace, 8);
929 ASSERT_EQ(max_in_cluster, cluster_ops);
930 ASSERT_EQ(num_clusters, 3);
931 }
932
TEST_F(CowTest,DeleteMidCluster)933 TEST_F(CowTest, DeleteMidCluster) {
934 CowOptions options;
935 options.cluster_ops = 7;
936 auto writer = std::make_unique<CowWriter>(options);
937 ASSERT_TRUE(writer->Initialize(cow_->fd));
938
939 ASSERT_TRUE(WriteDataBlock(writer.get(), 1, "Block 1"));
940 ASSERT_TRUE(WriteDataBlock(writer.get(), 2, "Block 2"));
941 ASSERT_TRUE(WriteDataBlock(writer.get(), 3, "Block 3"));
942 ASSERT_TRUE(writer->AddLabel(1));
943 ASSERT_TRUE(writer->Finalize());
944 ASSERT_TRUE(WriteDataBlock(writer.get(), 4, "Block 4"));
945 ASSERT_TRUE(WriteDataBlock(writer.get(), 5, "Block 5"));
946 ASSERT_TRUE(WriteDataBlock(writer.get(), 6, "Block 6"));
947 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
948
949 writer = std::make_unique<CowWriter>(options);
950 ASSERT_TRUE(writer->InitializeAppend(cow_->fd, 1));
951 ASSERT_TRUE(writer->Finalize());
952 ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);
953
954 CowReader reader;
955 ASSERT_TRUE(reader.Parse(cow_->fd));
956
957 auto iter = reader.GetOpIter();
958 size_t num_replace = 0;
959 size_t max_in_cluster = 0;
960 size_t num_in_cluster = 0;
961 size_t num_clusters = 0;
962 while (!iter->Done()) {
963 const auto& op = iter->Get();
964
965 num_in_cluster++;
966 max_in_cluster = std::max(max_in_cluster, num_in_cluster);
967 if (op.type == kCowReplaceOp) {
968 num_replace++;
969
970 ASSERT_EQ(op.new_block, num_replace);
971 ASSERT_TRUE(CompareDataBlock(&reader, op, "Block " + std::to_string(num_replace)));
972 } else if (op.type == kCowClusterOp) {
973 num_in_cluster = 0;
974 num_clusters++;
975 }
976
977 iter->Next();
978 }
979 ASSERT_EQ(num_replace, 3);
980 ASSERT_EQ(max_in_cluster, 5); // 3 data, 1 label, 1 cluster op
981 ASSERT_EQ(num_clusters, 1);
982 }
983
984 } // namespace snapshot
985 } // namespace android
986
main(int argc,char ** argv)987 int main(int argc, char** argv) {
988 ::testing::InitGoogleTest(&argc, argv);
989 return RUN_ALL_TESTS();
990 }
991