Home | History | Annotate | Download | only in payload_generator
      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/memory/ptr_util.h>
     25 #include <base/strings/string_number_conversions.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/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 
     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 base::WrapUnique(new MapfileFilesystem(mapfile_filename, num_blocks));
     65 }
     66 
     67 MapfileFilesystem::MapfileFilesystem(const string& mapfile_filename,
     68                                      off_t num_blocks)
     69     : mapfile_filename_(mapfile_filename), num_blocks_(num_blocks) {}
     70 
     71 size_t MapfileFilesystem::GetBlockSize() const {
     72   return kMapfileBlockSize;
     73 }
     74 
     75 size_t MapfileFilesystem::GetBlockCount() const {
     76   return num_blocks_;
     77 }
     78 
     79 bool MapfileFilesystem::GetFiles(vector<File>* files) const {
     80   files->clear();
     81 
     82   string file_data;
     83   if (!base::ReadFileToString(base::FilePath(mapfile_filename_), &file_data)) {
     84     LOG(ERROR) << "Unable to read .map file: " << mapfile_filename_;
     85     return false;
     86   }
     87 
     88   // Iterate over all the lines in the file and generate one File entry per
     89   // line.
     90   vector<base::StringPiece> lines = base::SplitStringPiece(
     91       file_data, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
     92   for (const base::StringPiece& line : lines) {
     93     File mapped_file;
     94 
     95     mapped_file.extents = {};
     96     size_t delim, last_delim = line.size();
     97     while ((delim = line.rfind(' ', last_delim - 1)) != string::npos) {
     98       string blocks =
     99           line.substr(delim + 1, last_delim - (delim + 1)).as_string();
    100       size_t dash = blocks.find('-', 0);
    101       uint64_t block_start, block_end;
    102       if (dash == string::npos && base::StringToUint64(blocks, &block_start)) {
    103         mapped_file.extents.push_back(ExtentForRange(block_start, 1));
    104       } else if (dash != string::npos &&
    105                  base::StringToUint64(blocks.substr(0, dash), &block_start) &&
    106                  base::StringToUint64(blocks.substr(dash + 1), &block_end)) {
    107         if (block_end < block_start) {
    108           LOG(ERROR) << "End block " << block_end
    109                      << " is smaller than start block " << block_start
    110                      << std::endl
    111                      << line;
    112           return false;
    113         }
    114         if (block_end > static_cast<uint64_t>(num_blocks_)) {
    115           LOG(ERROR) << "The end block " << block_end
    116                      << " is past the end of the file of " << num_blocks_
    117                      << " blocks" << std::endl
    118                      << line;
    119           return false;
    120         }
    121         mapped_file.extents.push_back(
    122             ExtentForRange(block_start, block_end - block_start + 1));
    123       } else {
    124         // If we can't parse N or N-M, we assume the block is actually part of
    125         // the name of the file.
    126         break;
    127       }
    128       last_delim = delim;
    129     }
    130     // We parsed the blocks from the end of the line, so we need to reverse
    131     // the Extents in the file.
    132     std::reverse(mapped_file.extents.begin(), mapped_file.extents.end());
    133 
    134     if (last_delim == string::npos)
    135       continue;
    136     mapped_file.name = line.substr(0, last_delim).as_string();
    137 
    138     files->push_back(mapped_file);
    139   }
    140 
    141   return true;
    142 }
    143 
    144 bool MapfileFilesystem::LoadSettings(brillo::KeyValueStore* store) const {
    145   // Settings not supported in mapfile since the storage format is unknown.
    146   LOG(ERROR) << "mapfile doesn't support LoadSettings().";
    147   return false;
    148 }
    149 
    150 }  // namespace chromeos_update_engine
    151