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 : // Internal implementation details for msf_reader.h. Not meant to be included
16 : // directly.
17 :
18 : #ifndef SYZYGY_MSF_MSF_READER_IMPL_H_
19 : #define SYZYGY_MSF_MSF_READER_IMPL_H_
20 :
21 : #include <cstdio>
22 : #include <cstring>
23 : #include <memory>
24 : #include <vector>
25 :
26 : #include "base/logging.h"
27 : #include "base/memory/ref_counted.h"
28 : #include "base/strings/string_util.h"
29 : #include "syzygy/msf/msf_data.h"
30 : #include "syzygy/msf/msf_file_stream.h"
31 :
32 : namespace msf {
33 : namespace detail {
34 :
35 : namespace {
36 :
37 E : bool GetFileSize(FILE* file, uint32_t* size) {
38 E : DCHECK(file != NULL);
39 E : DCHECK(size != NULL);
40 :
41 E : if (fseek(file, 0, SEEK_END) != 0) {
42 i : LOG(ERROR) << "Failed seeking to end of file.";
43 i : return false;
44 : }
45 :
46 E : long temp = ftell(file);
47 E : if (temp == -1L) {
48 i : LOG(ERROR) << "Failed to read stream position.";
49 i : return false;
50 : }
51 E : DCHECK_GT(temp, 0);
52 :
53 E : (*size) = static_cast<uint32_t>(temp);
54 E : return true;
55 E : }
56 :
57 E : uint32_t GetNumPages(const MsfHeader& header, uint32_t num_bytes) {
58 E : return (num_bytes + header.page_size - 1) / header.page_size;
59 E : }
60 :
61 : } // namespace
62 :
63 : template <MsfFileType T>
64 : bool MsfReaderImpl<T>::Read(const base::FilePath& msf_path,
65 E : MsfFileImpl<T>* msf_file) {
66 E : DCHECK(msf_file != NULL);
67 :
68 E : msf_file->Clear();
69 :
70 E : scoped_refptr<RefCountedFILE> file(
71 : new RefCountedFILE(base::OpenFile(msf_path, "rb")));
72 E : if (!file->file()) {
73 E : LOG(ERROR) << "Unable to open '" << msf_path.value() << "'.";
74 E : return false;
75 : }
76 :
77 : // Get the file size.
78 E : uint32_t file_size = 0;
79 E : if (!GetFileSize(file->file(), &file_size)) {
80 i : LOG(ERROR) << "Unable to determine size of '" << msf_path.value() << "'.";
81 i : return false;
82 : }
83 :
84 E : MsfHeader header = {0};
85 :
86 : // Read the header from the first page in the file. The page size we use here
87 : // is irrelevant as after reading the header we get the actual page size in
88 : // use by the MSF and from then on use that.
89 E : uint32_t header_page = 0;
90 E : scoped_refptr<MsfFileStreamImpl<T>> header_stream(new MsfFileStreamImpl<T>(
91 : file.get(), sizeof(header), &header_page, kMsfPageSize));
92 E : if (!header_stream->ReadBytesAt(0, sizeof(header), &header)) {
93 i : LOG(ERROR) << "Failed to read MSF file header.";
94 i : return false;
95 : }
96 :
97 : // Sanity checks.
98 E : if (header.num_pages * header.page_size != file_size) {
99 E : LOG(ERROR) << "Invalid MSF file size.";
100 E : return false;
101 : }
102 :
103 : if (memcmp(header.magic_string, kMsfHeaderMagicString,
104 E : sizeof(kMsfHeaderMagicString)) != 0) {
105 i : LOG(ERROR) << "Invalid MSF magic string.";
106 i : return false;
107 : }
108 :
109 : // Load the directory page list (a sequence of uint32_t page numbers that is
110 : // itself written across multiple root pages). To do this we need to know how
111 : // many pages are required to represent the directory, then we load a stream
112 : // containing that many page pointers from the root pages array.
113 : int num_dir_pages =
114 E : static_cast<int>(GetNumPages(header, header.directory_size));
115 E : scoped_refptr<MsfFileStreamImpl<T>> dir_page_stream(
116 : new MsfFileStreamImpl<T>(file.get(), num_dir_pages * sizeof(uint32_t),
117 : header.root_pages, header.page_size));
118 E : std::unique_ptr<uint32_t[]> dir_pages(new uint32_t[num_dir_pages]);
119 E : if (dir_pages.get() == NULL) {
120 i : LOG(ERROR) << "Failed to allocate directory pages.";
121 i : return false;
122 : }
123 E : size_t page_dir_size = num_dir_pages * sizeof(uint32_t);
124 E : if (!dir_page_stream->ReadBytesAt(0, page_dir_size, dir_pages.get())) {
125 i : LOG(ERROR) << "Failed to read directory page stream.";
126 i : return false;
127 : }
128 :
129 : // Load the actual directory.
130 : size_t dir_size =
131 E : static_cast<size_t>(header.directory_size / sizeof(uint32_t));
132 E : scoped_refptr<MsfFileStreamImpl<T>> dir_stream(new MsfFileStreamImpl<T>(
133 : file.get(), header.directory_size, dir_pages.get(), header.page_size));
134 E : std::vector<uint32_t> directory(dir_size);
135 E : if (!dir_stream->ReadBytesAt(0, dir_size * sizeof(uint32_t), &directory[0])) {
136 i : LOG(ERROR) << "Failed to read directory stream.";
137 i : return false;
138 : }
139 :
140 : // Iterate through the streams and construct MsfStreams.
141 E : const uint32_t& num_streams = directory[0];
142 E : const uint32_t* stream_lengths = &(directory[1]);
143 E : const uint32_t* stream_pages = &(directory[1 + num_streams]);
144 :
145 E : uint32_t page_index = 0;
146 E : for (uint32_t stream_index = 0; stream_index < num_streams; ++stream_index) {
147 E : msf_file->AppendStream(
148 : new MsfFileStreamImpl<T>(file.get(), stream_lengths[stream_index],
149 : stream_pages + page_index, header.page_size));
150 E : page_index += GetNumPages(header, stream_lengths[stream_index]);
151 E : }
152 :
153 E : return true;
154 E : }
155 :
156 : } // namespace detail
157 : } // namespace msf
158 :
159 : #endif // SYZYGY_MSF_MSF_READER_IMPL_H_
|