OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include <sys/mount.h> |
| 6 #include <inttypes.h> |
| 7 |
| 8 #include <algorithm> |
| 9 #include <string> |
| 10 #include <vector> |
| 11 |
| 12 #include "base/scoped_ptr.h" |
| 13 #include "base/string_util.h" |
| 14 #include <google/protobuf/repeated_field.h> |
| 15 #include <gtest/gtest.h> |
| 16 |
| 17 #include "update_engine/delta_diff_generator.h" |
| 18 #include "update_engine/delta_performer.h" |
| 19 #include "update_engine/graph_types.h" |
| 20 #include "update_engine/test_utils.h" |
| 21 #include "update_engine/update_metadata.pb.h" |
| 22 #include "update_engine/utils.h" |
| 23 |
| 24 namespace chromeos_update_engine { |
| 25 |
| 26 using std::min; |
| 27 using std::string; |
| 28 using std::vector; |
| 29 |
| 30 class DeltaPerformerTest : public ::testing::Test { }; |
| 31 |
| 32 TEST(DeltaPerformerTest, ExtentsToByteStringTest) { |
| 33 uint64_t test[] = {1, 1, 4, 2, kSparseHole, 1, 0, 1}; |
| 34 COMPILE_ASSERT(arraysize(test) % 2 == 0, array_size_uneven); |
| 35 const uint64_t block_size = 4096; |
| 36 const uint64_t file_length = 5 * block_size - 13; |
| 37 |
| 38 google::protobuf::RepeatedPtrField<Extent> extents; |
| 39 for (size_t i = 0; i < arraysize(test); i += 2) { |
| 40 Extent* extent = extents.Add(); |
| 41 extent->set_start_block(test[i]); |
| 42 extent->set_num_blocks(test[i + 1]); |
| 43 } |
| 44 |
| 45 string expected_output = "4096:4096,16384:8192,-1:4096,0:4096"; |
| 46 string actual_output; |
| 47 EXPECT_TRUE(DeltaPerformer::ExtentsToBsdiffPositionsString(extents, |
| 48 block_size, |
| 49 file_length, |
| 50 &actual_output)); |
| 51 EXPECT_EQ(expected_output, actual_output); |
| 52 } |
| 53 |
| 54 class ScopedLoopMounter { |
| 55 public: |
| 56 explicit ScopedLoopMounter(const string& file_path, string* mnt_path, |
| 57 unsigned long flags) { |
| 58 EXPECT_TRUE(utils::MakeTempDirectory("/tmp/mnt.XXXXXX", mnt_path)); |
| 59 dir_remover_.reset(new ScopedDirRemover(*mnt_path)); |
| 60 |
| 61 string loop_dev = GetUnusedLoopDevice(); |
| 62 EXPECT_EQ(0, system(StringPrintf("losetup %s %s", loop_dev.c_str(), |
| 63 file_path.c_str()).c_str())); |
| 64 loop_releaser_.reset(new ScopedLoopbackDeviceReleaser(loop_dev)); |
| 65 |
| 66 EXPECT_TRUE(utils::MountFilesystem(loop_dev, *mnt_path, flags)); |
| 67 unmounter_.reset(new ScopedFilesystemUnmounter(*mnt_path)); |
| 68 } |
| 69 private: |
| 70 scoped_ptr<ScopedDirRemover> dir_remover_; |
| 71 scoped_ptr<ScopedLoopbackDeviceReleaser> loop_releaser_; |
| 72 scoped_ptr<ScopedFilesystemUnmounter> unmounter_; |
| 73 }; |
| 74 |
| 75 void CompareFilesByBlock(const string& a_file, const string& b_file) { |
| 76 vector<char> a_data, b_data; |
| 77 EXPECT_TRUE(utils::ReadFile(a_file, &a_data)); |
| 78 EXPECT_TRUE(utils::ReadFile(b_file, &b_data)); |
| 79 |
| 80 EXPECT_EQ(a_data.size(), b_data.size()); |
| 81 size_t kBlockSize = 4096; |
| 82 EXPECT_EQ(0, a_data.size() % kBlockSize); |
| 83 for (size_t i = 0; i < a_data.size(); i += kBlockSize) { |
| 84 EXPECT_EQ(0, i % kBlockSize); |
| 85 vector<char> a_sub(&a_data[i], &a_data[i + kBlockSize]); |
| 86 vector<char> b_sub(&b_data[i], &b_data[i + kBlockSize]); |
| 87 EXPECT_TRUE(a_sub == b_sub) << "Block " << (i/kBlockSize) << " differs"; |
| 88 } |
| 89 } |
| 90 |
| 91 namespace { |
| 92 bool WriteSparseFile(const string& path, off_t size) { |
| 93 int fd = open(path.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0644); |
| 94 TEST_AND_RETURN_FALSE_ERRNO(fd >= 0); |
| 95 ScopedFdCloser fd_closer(&fd); |
| 96 off_t rc = lseek(fd, size + 1, SEEK_SET); |
| 97 TEST_AND_RETURN_FALSE_ERRNO(rc != static_cast<off_t>(-1)); |
| 98 int return_code = ftruncate(fd, size); |
| 99 TEST_AND_RETURN_FALSE_ERRNO(return_code == 0); |
| 100 return true; |
| 101 } |
| 102 } |
| 103 |
| 104 TEST(DeltaPerformerTest, RunAsRootSmallImageTest) { |
| 105 string a_img, b_img; |
| 106 EXPECT_TRUE(utils::MakeTempFile("/tmp/a_img.XXXXXX", &a_img, NULL)); |
| 107 ScopedPathUnlinker a_img_unlinker(a_img); |
| 108 EXPECT_TRUE(utils::MakeTempFile("/tmp/b_img.XXXXXX", &b_img, NULL)); |
| 109 ScopedPathUnlinker b_img_unlinker(b_img); |
| 110 |
| 111 CreateExtImageAtPath(a_img, NULL); |
| 112 CreateExtImageAtPath(b_img, NULL); |
| 113 |
| 114 // Make some changes to the A image. |
| 115 { |
| 116 string a_mnt; |
| 117 ScopedLoopMounter b_mounter(a_img, &a_mnt, 0); |
| 118 |
| 119 EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress", |
| 120 a_mnt.c_str()).c_str(), |
| 121 reinterpret_cast<const char*>(kRandomString), |
| 122 sizeof(kRandomString) - 1)); |
| 123 // Write 1 MiB of 0xff to try to catch the case where writing a bsdiff |
| 124 // patch fails to zero out the final block. |
| 125 vector<char> ones(1024 * 1024, 0xff); |
| 126 EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/ones", |
| 127 a_mnt.c_str()).c_str(), |
| 128 &ones[0], |
| 129 ones.size())); |
| 130 } |
| 131 |
| 132 // Make some changes to the B image. |
| 133 { |
| 134 string b_mnt; |
| 135 ScopedLoopMounter b_mounter(b_img, &b_mnt, 0); |
| 136 |
| 137 EXPECT_EQ(0, system(StringPrintf("cp %s/hello %s/hello2", b_mnt.c_str(), |
| 138 b_mnt.c_str()).c_str())); |
| 139 EXPECT_EQ(0, system(StringPrintf("rm %s/hello", b_mnt.c_str()).c_str())); |
| 140 EXPECT_EQ(0, system(StringPrintf("mv %s/hello2 %s/hello", b_mnt.c_str(), |
| 141 b_mnt.c_str()).c_str())); |
| 142 EXPECT_EQ(0, system(StringPrintf("echo foo > %s/foo", |
| 143 b_mnt.c_str()).c_str())); |
| 144 EXPECT_EQ(0, system(StringPrintf("touch %s/emptyfile", |
| 145 b_mnt.c_str()).c_str())); |
| 146 EXPECT_TRUE(WriteSparseFile(StringPrintf("%s/fullsparse", b_mnt.c_str()), |
| 147 1024 * 1024)); |
| 148 EXPECT_EQ(0, system(StringPrintf("dd if=/dev/zero of=%s/partsparese bs=1 " |
| 149 "seek=4096 count=1", |
| 150 b_mnt.c_str()).c_str())); |
| 151 EXPECT_TRUE(utils::WriteFile(StringPrintf("%s/hardtocompress", |
| 152 b_mnt.c_str()).c_str(), |
| 153 reinterpret_cast<const char*>(kRandomString), |
| 154 sizeof(kRandomString))); |
| 155 } |
| 156 |
| 157 string delta_path; |
| 158 EXPECT_TRUE(utils::MakeTempFile("/tmp/delta.XXXXXX", &delta_path, NULL)); |
| 159 ScopedPathUnlinker delta_path_unlinker(delta_path); |
| 160 { |
| 161 string a_mnt, b_mnt; |
| 162 ScopedLoopMounter a_mounter(a_img, &a_mnt, MS_RDONLY); |
| 163 ScopedLoopMounter b_mounter(b_img, &b_mnt, MS_RDONLY); |
| 164 |
| 165 EXPECT_TRUE(DeltaDiffGenerator::GenerateDeltaUpdateFile(a_mnt, |
| 166 a_img, |
| 167 b_mnt, |
| 168 b_img, |
| 169 delta_path)); |
| 170 } |
| 171 |
| 172 // Read delta into memory. |
| 173 vector<char> delta; |
| 174 EXPECT_TRUE(utils::ReadFile(delta_path, &delta)); |
| 175 |
| 176 // Update the A image in place. |
| 177 DeltaPerformer performer; |
| 178 |
| 179 EXPECT_EQ(0, performer.Open(a_img.c_str(), 0, 0)); |
| 180 |
| 181 // Write at some number of bytes per operation. Arbitrarily chose 5. |
| 182 const size_t kBytesPerWrite = 5; |
| 183 for (size_t i = 0; i < delta.size(); i += kBytesPerWrite) { |
| 184 size_t count = min(delta.size() - i, kBytesPerWrite); |
| 185 EXPECT_EQ(count, performer.Write(&delta[i], count)); |
| 186 } |
| 187 |
| 188 // Wrapper around close. Returns 0 on success or -errno on error. |
| 189 EXPECT_EQ(0, performer.Close()); |
| 190 |
| 191 CompareFilesByBlock("/tmp/a_ref", "/tmp/b_ref"); |
| 192 } |
| 193 |
| 194 } // namespace chromeos_update_engine |
OLD | NEW |