blob: f4f0804c1fb7798a66dadf31c168c1c7d6c63813 [file] [log] [blame]
Alex Deymo20bdc702016-12-07 21:07:11 -08001//
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 <base/files/file_util.h>
23#include <base/logging.h>
24#include <base/strings/string_number_conversions.h>
25#include <base/strings/string_split.h>
26#include <brillo/make_unique_ptr.h>
27
28#include "update_engine/common/utils.h"
29#include "update_engine/payload_generator/extent_ranges.h"
30#include "update_engine/payload_generator/extent_utils.h"
31#include "update_engine/update_metadata.pb.h"
32
33using std::string;
34using std::vector;
35
36namespace {
37// The .map file is defined in terms of 4K blocks.
38size_t kMapfileBlockSize = 4096;
39} // namespace
40
41namespace chromeos_update_engine {
42
43std::unique_ptr<MapfileFilesystem> MapfileFilesystem::CreateFromFile(
44 const string& filename, const string& mapfile_filename) {
45 if (filename.empty() || mapfile_filename.empty())
46 return nullptr;
47
48 off_t file_size = utils::FileSize(filename);
49 if (file_size < 0)
50 return nullptr;
51
52 if (file_size % kMapfileBlockSize) {
53 LOG(ERROR) << "Image file " << filename << " has a size of " << file_size
54 << " which is not multiple of " << kMapfileBlockSize;
55 return nullptr;
56 }
57 off_t num_blocks = file_size / kMapfileBlockSize;
58
59 if (!utils::FileExists(mapfile_filename.c_str())) {
60 LOG(ERROR) << "File " << mapfile_filename << " doesn't exist";
61 return nullptr;
62 }
63
64 return brillo::make_unique_ptr(
65 new MapfileFilesystem(mapfile_filename, num_blocks));
66}
67
68MapfileFilesystem::MapfileFilesystem(const string& mapfile_filename,
69 off_t num_blocks)
70 : mapfile_filename_(mapfile_filename), num_blocks_(num_blocks) {}
71
72size_t MapfileFilesystem::GetBlockSize() const {
73 return kMapfileBlockSize;
74}
75
76size_t MapfileFilesystem::GetBlockCount() const {
77 return num_blocks_;
78}
79
80bool MapfileFilesystem::GetFiles(vector<File>* files) const {
81 files->clear();
82
83 string file_data;
84 if (!base::ReadFileToString(base::FilePath(mapfile_filename_), &file_data)) {
85 LOG(ERROR) << "Unable to read .map file: " << mapfile_filename_;
86 return false;
87 }
88
89 // Iterate over all the lines in the file and generate one File entry per
90 // line.
91 vector<base::StringPiece> lines = base::SplitStringPiece(
92 file_data, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
93 for (const base::StringPiece& line : lines) {
94 File mapped_file;
95
96 mapped_file.extents = {};
97 size_t delim, last_delim = line.size();
98 while ((delim = line.rfind(' ', last_delim - 1)) != string::npos) {
99 string blocks =
100 line.substr(delim + 1, last_delim - (delim + 1)).as_string();
101 size_t dash = blocks.find('-', 0);
102 uint64_t block_start, block_end;
103 if (dash == string::npos && base::StringToUint64(blocks, &block_start)) {
104 mapped_file.extents.push_back(ExtentForRange(block_start, 1));
105 } else if (dash != string::npos &&
106 base::StringToUint64(blocks.substr(0, dash), &block_start) &&
107 base::StringToUint64(blocks.substr(dash + 1), &block_end)) {
108 if (block_end < block_start) {
109 LOG(ERROR) << "End block " << block_end
110 << " is smaller than start block " << block_start
111 << std::endl
112 << line;
113 return false;
114 }
115 if (block_end > static_cast<uint64_t>(num_blocks_)) {
116 LOG(ERROR) << "The end block " << block_end
117 << " is past the end of the file of " << num_blocks_
118 << " blocks" << std::endl
119 << line;
120 return false;
121 }
122 mapped_file.extents.push_back(
123 ExtentForRange(block_start, block_end - block_start + 1));
124 } else {
125 // If we can't parse N or N-M, we assume the block is actually part of
126 // the name of the file.
127 break;
128 }
129 last_delim = delim;
130 }
131 // We parsed the blocks from the end of the line, so we need to reverse
132 // the Extents in the file.
133 std::reverse(mapped_file.extents.begin(), mapped_file.extents.end());
134
135 if (last_delim == string::npos)
136 continue;
137 mapped_file.name = line.substr(0, last_delim).as_string();
138
139 files->push_back(mapped_file);
140 }
141
142 return true;
143}
144
145bool MapfileFilesystem::LoadSettings(brillo::KeyValueStore* store) const {
146 // Settings not supported in mapfile since the storage format is unknown.
147 LOG(ERROR) << "mapfile doesn't support LoadSettings().";
148 return false;
149}
150
151} // namespace chromeos_update_engine