blob: 06fff05502e824a11f3799c5cb01df4c38f67f95 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2015 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//
Alex Deymoa26432a2015-03-12 16:08:04 -070016
17#include "update_engine/payload_generator/verity_utils.h"
18
19#include <algorithm>
20#include <utility>
21#include <vector>
22
23#include <base/logging.h>
24#include <base/strings/string_number_conversions.h>
25#include <base/strings/string_util.h>
26#include <chromeos/strings/string_utils.h>
27extern "C" {
28#include <vboot/vboot_host.h>
29}
30
31using std::string;
32using std::vector;
33
34extern "C" {
35
36// vboot_host.h has a default VbExError() that will call exit() when a function
37// fails. We redefine that function here so it doesn't exit.
38void VbExError(const char* format, ...) {
39 va_list ap;
40 va_start(ap, format);
41 fprintf(stderr, "ERROR: ");
42 va_end(ap);
43}
44
45}
46
47namespace {
48
49// Splits a string with zero or more arguments separated by spaces into a list
50// of strings, but respecting the double quotes. For example, the string:
51// a="foo" b=foo c="bar baz" "my dir"/"my file"
52// has only four arguments, since some parts are grouped together due to the
53// double quotes.
54vector<string> SplitQuotedArgs(const string arglist) {
55 vector<string> terms = chromeos::string_utils::Split(
56 arglist, " ", false, false);
57 vector<string> result;
58 string last_term;
59 size_t quotes = 0;
60 for (const string& term : terms) {
61 if (quotes % 2 == 0 && term.empty())
62 continue;
63
64 quotes += std::count(term.begin(), term.end(), '"');
65 if (last_term.empty()) {
66 last_term = term;
67 } else {
68 last_term += " " + term;
69 }
70 if (quotes % 2 == 0) {
71 result.push_back(last_term);
72 last_term.clear();
73 quotes = 0;
74 }
75 }
76 // Unterminated quoted string found.
77 if (!last_term.empty())
78 result.push_back(last_term);
79 return result;
80}
81
82} // namespace
83
84namespace chromeos_update_engine {
85
86bool ParseVerityRootfsSize(const string& kernel_cmdline,
87 uint64_t* rootfs_size) {
88 vector<string> kernel_args = SplitQuotedArgs(kernel_cmdline);
89
90 for (const string& arg : kernel_args) {
91 std::pair<string, string> key_value =
92 chromeos::string_utils::SplitAtFirst(arg, "=", true);
93 if (key_value.first != "dm")
94 continue;
95 string value = key_value.second;
96 if (value.size() > 1 && value.front() == '"' && value.back() == '"')
97 value = value.substr(1, value.size() - 1);
98
99 vector<string> dm_parts = SplitQuotedArgs(value);
100 // Check if this is a dm-verity device.
101 if (std::find(dm_parts.begin(), dm_parts.end(), "verity") == dm_parts.end())
102 continue;
103 for (const string& dm_part : dm_parts) {
104 key_value = chromeos::string_utils::SplitAtFirst(dm_part, "=", true);
105 if (key_value.first != "hashstart")
106 continue;
107 if (!base::StringToUint64(key_value.second, rootfs_size))
108 continue;
109 // The hashstart= value is specified in 512-byte blocks, so we need to
110 // convert that to bytes.
111 *rootfs_size *= 512;
112 return true;
113 }
114 }
115 return false;
116}
117
118bool GetVerityRootfsSize(const string& kernel_dev, uint64_t* rootfs_size) {
119 string kernel_cmdline;
120 char *config = FindKernelConfig(kernel_dev.c_str(), USE_PREAMBLE_LOAD_ADDR);
121 if (!config) {
122 LOG(WARNING) << "Error retrieving kernel command line from '"
123 << kernel_dev << "', ignoring.";
124 return false;
125 }
126 kernel_cmdline = string(config, MAX_KERNEL_CONFIG_SIZE);
127
128 // FindKernelConfig() expects the caller to free the char*.
129 free(config);
130
131 if (!ParseVerityRootfsSize(kernel_cmdline, rootfs_size)) {
132 LOG(INFO) << "Didn't find the rootfs size in the kernel command line: "
133 << kernel_cmdline;
134 return false;
135 }
136 return true;
137}
138
139} // namespace chromeos_update_engine