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 <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 
33 using std::string;
34 using std::vector;
35 
36 namespace {
37 // The .map file is defined in terms of 4K blocks.
38 size_t kMapfileBlockSize = 4096;
39 }  // namespace
40 
41 namespace chromeos_update_engine {
42 
CreateFromFile(const string & filename,const string & mapfile_filename)43 std::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 
MapfileFilesystem(const string & mapfile_filename,off_t num_blocks)68 MapfileFilesystem::MapfileFilesystem(const string& mapfile_filename,
69                                      off_t num_blocks)
70     : mapfile_filename_(mapfile_filename), num_blocks_(num_blocks) {}
71 
GetBlockSize() const72 size_t MapfileFilesystem::GetBlockSize() const {
73   return kMapfileBlockSize;
74 }
75 
GetBlockCount() const76 size_t MapfileFilesystem::GetBlockCount() const {
77   return num_blocks_;
78 }
79 
GetFiles(vector<File> * files) const80 bool 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 
LoadSettings(brillo::KeyValueStore * store) const145 bool 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
152