1 //
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 //
16 
17 #include "update_engine/image_properties.h"
18 
19 #include <fcntl.h>
20 
21 #include <string>
22 
23 #include <android-base/properties.h>
24 #include <base/logging.h>
25 #include <base/strings/string_util.h>
26 #include <bootloader_message/bootloader_message.h>
27 #include <brillo/osrelease_reader.h>
28 #include <brillo/strings/string_utils.h>
29 
30 #include "update_engine/common/boot_control_interface.h"
31 #include "update_engine/common/constants.h"
32 #include "update_engine/common/platform_constants.h"
33 #include "update_engine/common/prefs_interface.h"
34 #include "update_engine/common/utils.h"
35 #include "update_engine/system_state.h"
36 
37 using android::base::GetProperty;
38 using std::string;
39 
40 namespace chromeos_update_engine {
41 
42 namespace {
43 
44 // Build time properties name used in Android Things.
45 const char kProductId[] = "product_id";
46 const char kProductVersion[] = "product_version";
47 const char kSystemId[] = "system_id";
48 const char kSystemVersion[] = "system_version";
49 
50 // The path to the product_components file which stores the version of each
51 // components in OEM partition.
52 const char kProductComponentsPath[] = "/oem/os-release.d/product_components";
53 
54 // Prefs used to store the powerwash settings.
55 const char kPrefsImgPropPowerwashAllowed[] = "img-prop-powerwash-allowed";
56 
57 // System properties that identifies the "board".
58 const char kPropProductName[] = "ro.product.name";
59 const char kPropBuildFingerprint[] = "ro.build.fingerprint";
60 const char kPropBuildType[] = "ro.build.type";
61 
62 // Default channel from factory.prop
63 const char kPropDefaultChannel[] = "ro.update.default_channel";
64 
65 // A prefix added to the path, used for testing.
66 const char* root_prefix = nullptr;
67 
68 string GetStringWithDefault(const brillo::OsReleaseReader& osrelease,
69                             const string& key,
70                             const string& default_value) {
71   string result;
72   if (osrelease.GetString(key, &result))
73     return result;
74   LOG(INFO) << "Cannot load ImageProperty " << key << ", using default value "
75             << default_value;
76   return default_value;
77 }
78 
79 // Open misc partition for read or write and output the fd in |out_fd|.
80 bool OpenMisc(bool write, int* out_fd) {
81   string misc_device;
82   int flags = write ? O_WRONLY | O_SYNC : O_RDONLY;
83   if (root_prefix) {
84     // Use a file for unittest and create one if doesn't exist.
85     misc_device = base::FilePath(root_prefix).Append("misc").value();
86     if (write)
87       flags |= O_CREAT;
88   } else {
89     string err;
90     misc_device = get_bootloader_message_blk_device(&err);
91     if (misc_device.empty()) {
92       LOG(ERROR) << "Unable to get misc block device: " << err;
93       return false;
94     }
95   }
96 
97   int fd = HANDLE_EINTR(open(misc_device.c_str(), flags, 0600));
98   if (fd < 0) {
99     PLOG(ERROR) << "Opening misc failed";
100     return false;
101   }
102   *out_fd = fd;
103   return true;
104 }
105 
106 // The offset and size of the channel field in misc partition.
107 constexpr size_t kChannelOffset =
108     BOOTLOADER_MESSAGE_OFFSET_IN_MISC +
109     offsetof(bootloader_message_ab, update_channel);
110 constexpr size_t kChannelSize = sizeof(bootloader_message_ab::update_channel);
111 
112 // Read channel from misc partition to |out_channel|, return false if unable to
113 // read misc or no channel is set in misc.
114 bool ReadChannelFromMisc(string* out_channel) {
115   int fd;
116   TEST_AND_RETURN_FALSE(OpenMisc(false, &fd));
117   ScopedFdCloser fd_closer(&fd);
118   char channel[kChannelSize] = {0};
119   ssize_t bytes_read = 0;
120   if (!utils::PReadAll(
121           fd, channel, kChannelSize - 1, kChannelOffset, &bytes_read) ||
122       bytes_read != kChannelSize - 1) {
123     PLOG(ERROR) << "Reading update channel from misc failed";
124     return false;
125   }
126   if (channel[0] == '\0') {
127     LOG(INFO) << "No channel set in misc.";
128     return false;
129   }
130   if (!base::EndsWith(channel, "-channel", base::CompareCase::SENSITIVE)) {
131     LOG(ERROR) << "Channel " << channel << " doesn't end with -channel.";
132     return false;
133   }
134   out_channel->assign(channel);
135   return true;
136 }
137 
138 // Write |in_channel| to misc partition, return false if failed to write.
139 bool WriteChannelToMisc(const string& in_channel) {
140   int fd;
141   TEST_AND_RETURN_FALSE(OpenMisc(true, &fd));
142   ScopedFdCloser fd_closer(&fd);
143   if (in_channel.size() >= kChannelSize) {
144     LOG(ERROR) << "Channel name is too long: " << in_channel
145                << ", the maximum length is " << kChannelSize - 1;
146     return false;
147   }
148   char channel[kChannelSize] = {0};
149   memcpy(channel, in_channel.data(), in_channel.size());
150   if (!utils::PWriteAll(fd, channel, kChannelSize, kChannelOffset)) {
151     PLOG(ERROR) << "Writing update channel to misc failed";
152     return false;
153   }
154   return true;
155 }
156 
157 string GetTargetChannel() {
158   string channel;
159   if (!ReadChannelFromMisc(&channel))
160     channel = GetProperty(kPropDefaultChannel, "stable-channel");
161   return channel;
162 }
163 }  // namespace
164 
165 namespace test {
166 void SetImagePropertiesRootPrefix(const char* test_root_prefix) {
167   root_prefix = test_root_prefix;
168 }
169 }  // namespace test
170 
171 ImageProperties LoadImageProperties(SystemState* system_state) {
172   ImageProperties result;
173 
174   brillo::OsReleaseReader osrelease;
175   if (root_prefix)
176     osrelease.LoadTestingOnly(base::FilePath(root_prefix));
177   else
178     osrelease.Load();
179   result.product_id =
180       GetStringWithDefault(osrelease, kProductId, "invalid-product");
181   result.system_id = GetStringWithDefault(
182       osrelease, kSystemId, "developer-boards:brillo-starter-board");
183   // Update the system id to match the prefix of product id for testing.
184   string prefix, not_used, system_id;
185   if (brillo::string_utils::SplitAtFirst(
186           result.product_id, ":", &prefix, &not_used, false) &&
187       brillo::string_utils::SplitAtFirst(
188           result.system_id, ":", &not_used, &system_id, false)) {
189     result.system_id = prefix + ":" + system_id;
190   }
191   result.canary_product_id = result.product_id;
192   result.version = GetStringWithDefault(osrelease, kProductVersion, "0.0.0.0");
193   result.system_version =
194       GetStringWithDefault(osrelease, kSystemVersion, "0.0.0.0");
195   // Can't read it with OsReleaseReader because it has multiple lines.
196   utils::ReadFile(kProductComponentsPath, &result.product_components);
197 
198   result.board = GetProperty(kPropProductName, "brillo");
199   result.build_fingerprint = GetProperty(kPropBuildFingerprint, "none");
200   result.build_type = GetProperty(kPropBuildType, "");
201 
202   // Android doesn't have channel information in system image, we try to read
203   // the channel of current slot from prefs and then fallback to use the
204   // persisted target channel as current channel.
205   string current_channel_key =
206       kPrefsChannelOnSlotPrefix +
207       std::to_string(system_state->boot_control()->GetCurrentSlot());
208   string current_channel;
209   if (!system_state->prefs()->Exists(current_channel_key) ||
210       !system_state->prefs()->GetString(current_channel_key, &current_channel))
211     current_channel = GetTargetChannel();
212   result.current_channel = current_channel;
213   result.allow_arbitrary_channels = true;
214 
215   // Brillo only supports the official omaha URL.
216   result.omaha_url = constants::kOmahaDefaultProductionURL;
217 
218   return result;
219 }
220 
221 MutableImageProperties LoadMutableImageProperties(SystemState* system_state) {
222   MutableImageProperties result;
223   result.target_channel = GetTargetChannel();
224   if (!system_state->prefs()->GetBoolean(kPrefsImgPropPowerwashAllowed,
225                                          &result.is_powerwash_allowed)) {
226     result.is_powerwash_allowed = false;
227   }
228   return result;
229 }
230 
231 bool StoreMutableImageProperties(SystemState* system_state,
232                                  const MutableImageProperties& properties) {
233   bool ret = true;
234   if (!WriteChannelToMisc(properties.target_channel))
235     ret = false;
236   if (!system_state->prefs()->SetBoolean(kPrefsImgPropPowerwashAllowed,
237                                          properties.is_powerwash_allowed))
238     ret = false;
239   return ret;
240 }
241 
242 void LogImageProperties() {
243   // TODO(*): Implement this.
244 }
245 
246 }  // namespace chromeos_update_engine
247