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