1 : // Copyright 2012 Google Inc. All Rights Reserved.
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 "syzygy/msf/msf_writer.h"
16 :
17 : #include <algorithm>
18 : #include <vector>
19 :
20 : #include "gmock/gmock.h"
21 : #include "gtest/gtest.h"
22 : #include "syzygy/core/unittest_util.h"
23 : #include "syzygy/msf/msf_constants.h"
24 : #include "syzygy/msf/msf_data.h"
25 : #include "syzygy/msf/msf_reader.h"
26 : #include "syzygy/msf/unittest_util.h"
27 :
28 : namespace msf {
29 :
30 : namespace {
31 :
32 : uint32 GetNumPages(uint32 num_bytes) {
33 : return (num_bytes + msf::kMsfPageSize - 1) / msf::kMsfPageSize;
34 : }
35 :
36 : class TestMsfWriter : public MsfWriter {
37 : public:
38 E : TestMsfWriter() {
39 E : file_.reset(base::CreateAndOpenTemporaryFile(&path_));
40 E : EXPECT_TRUE(file_.get() != NULL);
41 E : }
42 :
43 E : ~TestMsfWriter() {
44 E : if (file_.get()) {
45 i : fclose(file_.get());
46 i : file_.reset();
47 : }
48 E : base::DeleteFile(path_, false);
49 E : }
50 :
51 E : base::ScopedFILE& file() { return file_; }
52 :
53 : using MsfWriter::AppendStream;
54 : using MsfWriter::WriteHeader;
55 :
56 : base::FilePath path_;
57 : };
58 :
59 : class TestMsfStream : public MsfStream {
60 : public:
61 E : TestMsfStream(uint32 length, uint32 mask) : MsfStream(length), data_(length) {
62 E : uint32* data = reinterpret_cast<uint32*>(data_.data());
63 :
64 : // Just to make sure the data is non-repeating (so we can distinguish if it
65 : // has been correctly written or not) fill it with integers encoding their
66 : // own position in the stream.
67 E : for (size_t i = 0; i < data_.size() / sizeof(data[0]); ++i)
68 E : data[i] = i | mask;
69 E : }
70 :
71 E : bool ReadBytes(void* dest, size_t count, size_t* bytes_read) {
72 E : DCHECK(bytes_read != NULL);
73 :
74 E : if (pos() == length()) {
75 i : *bytes_read = 0;
76 i : return true;
77 : }
78 :
79 E : count = std::min(count, length() - pos());
80 E : ::memcpy(dest, data_.data() + pos(), count);
81 E : Seek(pos() + count);
82 E : *bytes_read = count;
83 :
84 E : return true;
85 E : }
86 :
87 : private:
88 : std::vector<uint8> data_;
89 : };
90 :
91 : void EnsureMsfContentsAreIdentical(const MsfFile& msf_file,
92 : const MsfFile& msf_file_read) {
93 : ASSERT_EQ(msf_file.StreamCount(), msf_file_read.StreamCount());
94 :
95 : for (size_t i = 0; i < msf_file.StreamCount(); ++i) {
96 : MsfStream* stream = msf_file.GetStream(i).get();
97 : MsfStream* stream_read = msf_file_read.GetStream(i).get();
98 :
99 : ASSERT_TRUE(stream != NULL);
100 : ASSERT_TRUE(stream_read != NULL);
101 :
102 : ASSERT_EQ(stream->length(), stream_read->length());
103 :
104 : std::vector<uint8> data;
105 : std::vector<uint8> data_read;
106 : ASSERT_TRUE(stream->Seek(0));
107 : ASSERT_TRUE(stream_read->Seek(0));
108 : ASSERT_TRUE(stream->Read(&data, stream->length()));
109 : ASSERT_TRUE(stream_read->Read(&data_read, stream_read->length()));
110 :
111 : // We don't use ContainerEq because upon failure this generates a
112 : // ridiculously long and useless error message. We don't use memcmp because
113 : // it doesn't given any context as to where the failure occurs.
114 : for (size_t j = 0; j < data.size(); ++j)
115 : ASSERT_EQ(data[j], data_read[j]);
116 : }
117 : }
118 :
119 : } // namespace
120 :
121 : using msf::kMsfHeaderMagicString;
122 : using msf::kMsfPageSize;
123 : using msf::MsfHeader;
124 : using msf::MsfReader;
125 :
126 E : TEST(MsfWriterTest, AppendStream) {
127 E : TestMsfWriter writer;
128 :
129 E : testing::ScopedTempFile temp_file;
130 E : writer.file().reset(base::OpenFile(temp_file.path(), "wb"));
131 E : ASSERT_TRUE(writer.file().get() != NULL);
132 :
133 E : scoped_refptr<MsfStream> stream(new TestMsfStream(4 * kMsfPageSize, 0));
134 :
135 : // Test writing a stream that will force allocation of the free page map
136 : // pages.
137 E : std::vector<uint32> pages_written;
138 E : uint32 page_count = 0;
139 E : EXPECT_TRUE(writer.AppendStream(stream.get(), &pages_written, &page_count));
140 E : writer.file().reset();
141 :
142 : // We expect pages_written to contain 4 pages, like the stream. However, we
143 : // expect page_count to have 2 more pages for the free page map.
144 E : uint32 expected_pages_written[] = {0, 3, 4, 5};
145 : EXPECT_THAT(pages_written,
146 E : ::testing::ElementsAreArray(expected_pages_written));
147 E : EXPECT_EQ(page_count, 6);
148 :
149 : // Build the expected stream contents. Two blank pages should have been
150 : // reserved by the append stream routine.
151 E : stream->Seek(0);
152 E : std::vector<uint8> expected_contents(6 * kMsfPageSize);
153 E : ASSERT_TRUE(stream->Read(expected_contents.data(), kMsfPageSize));
154 : ASSERT_TRUE(stream->Read(expected_contents.data() + 3 * kMsfPageSize,
155 E : 3 * kMsfPageSize));
156 :
157 E : std::vector<uint8> contents(6 * kMsfPageSize);
158 : ASSERT_EQ(
159 : contents.size(),
160 : base::ReadFile(temp_file.path(), reinterpret_cast<char*>(contents.data()),
161 E : contents.size()));
162 :
163 E : EXPECT_THAT(contents, ::testing::ContainerEq(expected_contents));
164 E : }
165 :
166 E : TEST(MsfWriterTest, WriteHeader) {
167 E : TestMsfWriter writer;
168 :
169 E : testing::ScopedTempFile temp_file;
170 E : writer.file().reset(base::OpenFile(temp_file.path(), "wb"));
171 E : ASSERT_TRUE(writer.file().get() != NULL);
172 :
173 E : std::vector<uint32> root_directory_pages(kMsfMaxDirPages + 10, 1);
174 :
175 : // Try to write a root directorty that's too big and expect this to fail.
176 E : EXPECT_FALSE(writer.WriteHeader(root_directory_pages, 67 * 4, 438));
177 :
178 : // Now write a reasonable root directory size.
179 E : root_directory_pages.resize(1);
180 E : EXPECT_TRUE(writer.WriteHeader(root_directory_pages, 67 * 4, 438));
181 E : writer.file().reset();
182 :
183 : // Build the expected stream contents. Two blank pages should have been
184 : // reserved by the append stream routine.
185 E : std::vector<uint8> expected_contents(sizeof(MsfHeader));
186 E : MsfHeader* header = reinterpret_cast<MsfHeader*>(expected_contents.data());
187 : ::memcpy(header->magic_string, kMsfHeaderMagicString,
188 E : kMsfHeaderMagicStringSize);
189 E : header->page_size = kMsfPageSize;
190 E : header->free_page_map = 1;
191 E : header->num_pages = 438;
192 E : header->directory_size = 67 * 4;
193 E : header->root_pages[0] = 1;
194 :
195 E : std::vector<uint8> contents(sizeof(MsfHeader));
196 : ASSERT_EQ(
197 : contents.size(),
198 : base::ReadFile(temp_file.path(), reinterpret_cast<char*>(contents.data()),
199 E : contents.size()));
200 :
201 E : EXPECT_THAT(contents, ::testing::ContainerEq(expected_contents));
202 E : }
203 :
204 E : TEST(MsfWriterTest, WriteMsfFile) {
205 E : MsfFile msf_file;
206 E : for (uint32 i = 0; i < 4; ++i)
207 E : msf_file.AppendStream(new TestMsfStream(1 << (8 + i), (i << 24)));
208 :
209 : // Test that we can create an MSF file and then read it successfully.
210 E : testing::ScopedTempFile file;
211 : {
212 : // Create a scope so that the file gets closed.
213 E : TestMsfWriter writer;
214 E : EXPECT_TRUE(writer.Write(file.path(), msf_file));
215 E : }
216 :
217 E : MsfFile msf_file_read;
218 E : MsfReader reader;
219 E : EXPECT_TRUE(reader.Read(file.path(), &msf_file_read));
220 :
221 : ASSERT_NO_FATAL_FAILURE(
222 E : testing::EnsureMsfContentsAreIdentical(msf_file, msf_file_read));
223 E : }
224 :
225 : } // namespace msf
|