1 //
2 // Copyright (C) 2016 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include "update_engine/payload_generator/mapfile_filesystem.h"
18
19 #include <algorithm>
20 #include <map>
21
22 #include <android-base/parseint.h>
23 #include <base/files/file_util.h>
24 #include <base/logging.h>
25 #include <base/memory/ptr_util.h>
26 #include <base/strings/string_split.h>
27
28 #include "update_engine/common/utils.h"
29 #include "update_engine/payload_generator/extent_ranges.h"
30 #include "update_engine/update_metadata.pb.h"
31
32 using std::string;
33 using std::vector;
34
35 namespace {
36 // The .map file is defined in terms of 4K blocks.
37 size_t kMapfileBlockSize = 4096;
38 } // namespace
39
40 namespace chromeos_update_engine {
41
CreateFromFile(const string & filename,const string & mapfile_filename)42 std::unique_ptr<MapfileFilesystem> MapfileFilesystem::CreateFromFile(
43 const string& filename, const string& mapfile_filename) {
44 if (filename.empty() || mapfile_filename.empty())
45 return nullptr;
46
47 off_t file_size = utils::FileSize(filename);
48 if (file_size < 0)
49 return nullptr;
50
51 if (file_size % kMapfileBlockSize) {
52 LOG(ERROR) << "Image file " << filename << " has a size of " << file_size
53 << " which is not multiple of " << kMapfileBlockSize;
54 return nullptr;
55 }
56 off_t num_blocks = file_size / kMapfileBlockSize;
57
58 if (!utils::FileExists(mapfile_filename.c_str())) {
59 LOG(ERROR) << "File " << mapfile_filename << " doesn't exist";
60 return nullptr;
61 }
62
63 return base::WrapUnique(new MapfileFilesystem(mapfile_filename, num_blocks));
64 }
65
MapfileFilesystem(const string & mapfile_filename,off_t num_blocks)66 MapfileFilesystem::MapfileFilesystem(const string& mapfile_filename,
67 off_t num_blocks)
68 : mapfile_filename_(mapfile_filename), num_blocks_(num_blocks) {}
69
GetBlockSize() const70 size_t MapfileFilesystem::GetBlockSize() const {
71 return kMapfileBlockSize;
72 }
73
GetBlockCount() const74 size_t MapfileFilesystem::GetBlockCount() const {
75 return num_blocks_;
76 }
77
GetFiles(vector<File> * files) const78 bool MapfileFilesystem::GetFiles(vector<File>* files) const {
79 files->clear();
80
81 string file_data;
82 if (!base::ReadFileToString(base::FilePath(mapfile_filename_), &file_data)) {
83 LOG(ERROR) << "Unable to read .map file: " << mapfile_filename_;
84 return false;
85 }
86
87 // Iterate over all the lines in the file and generate one File entry per
88 // line.
89 vector<base::StringPiece> lines = base::SplitStringPiece(
90 file_data, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
91 for (const base::StringPiece& line : lines) {
92 File mapped_file;
93
94 mapped_file.extents = {};
95 size_t delim, last_delim = line.size();
96 while ((delim = line.rfind(' ', last_delim - 1)) != string::npos) {
97 string blocks =
98 line.substr(delim + 1, last_delim - (delim + 1)).as_string();
99 size_t dash = blocks.find('-', 0);
100 uint64_t block_start, block_end;
101 if (dash == string::npos &&
102 android::base::ParseUint<uint64_t>(blocks, &block_start)) {
103 mapped_file.extents.push_back(ExtentForRange(block_start, 1));
104 } else if (dash != string::npos &&
105 android::base::ParseUint<uint64_t>(blocks.substr(0, dash),
106 &block_start) &&
107 android::base::ParseUint<uint64_t>(blocks.substr(dash + 1),
108 &block_end)) {
109 if (block_end < block_start) {
110 LOG(ERROR) << "End block " << block_end
111 << " is smaller than start block " << block_start
112 << std::endl
113 << line;
114 return false;
115 }
116 if (block_end > static_cast<uint64_t>(num_blocks_)) {
117 LOG(ERROR) << "The end block " << block_end
118 << " is past the end of the file of " << num_blocks_
119 << " blocks" << std::endl
120 << line;
121 return false;
122 }
123 mapped_file.extents.push_back(
124 ExtentForRange(block_start, block_end - block_start + 1));
125 } else {
126 // If we can't parse N or N-M, we assume the block is actually part of
127 // the name of the file.
128 break;
129 }
130 last_delim = delim;
131 }
132 // We parsed the blocks from the end of the line, so we need to reverse
133 // the Extents in the file.
134 std::reverse(mapped_file.extents.begin(), mapped_file.extents.end());
135
136 if (last_delim == string::npos)
137 continue;
138 mapped_file.name = line.substr(0, last_delim).as_string();
139
140 files->push_back(mapped_file);
141 }
142
143 return true;
144 }
145
146 } // namespace chromeos_update_engine
147