1 //
2 // Copyright (C) 2012 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/common/utils.h"
18
19 #include <stdint.h>
20
21 #include <dirent.h>
22 #include <elf.h>
23 #include <endian.h>
24 #include <errno.h>
25 #include <ext2fs/ext2fs.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/mount.h>
31 #include <sys/resource.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/wait.h>
35 #include <unistd.h>
36
37 #include <algorithm>
38 #include <utility>
39 #include <vector>
40
41 #include <base/callback.h>
42 #include <base/files/file_path.h>
43 #include <base/files/file_util.h>
44 #include <base/files/scoped_file.h>
45 #include <base/format_macros.h>
46 #include <base/location.h>
47 #include <base/logging.h>
48 #include <base/posix/eintr_wrapper.h>
49 #include <base/rand_util.h>
50 #include <base/strings/string_number_conversions.h>
51 #include <base/strings/string_split.h>
52 #include <base/strings/string_util.h>
53 #include <base/strings/stringprintf.h>
54 #include <brillo/data_encoding.h>
55 #include <brillo/message_loops/message_loop.h>
56
57 #include "update_engine/common/clock_interface.h"
58 #include "update_engine/common/constants.h"
59 #include "update_engine/common/platform_constants.h"
60 #include "update_engine/common/prefs_interface.h"
61 #include "update_engine/common/subprocess.h"
62 #include "update_engine/payload_consumer/file_descriptor.h"
63 #include "update_engine/payload_consumer/file_writer.h"
64 #include "update_engine/payload_consumer/payload_constants.h"
65
66 using base::Time;
67 using base::TimeDelta;
68 using std::min;
69 using std::pair;
70 using std::string;
71 using std::vector;
72
73 namespace chromeos_update_engine {
74
75 namespace {
76
77 // The following constants control how UnmountFilesystem should retry if
78 // umount() fails with an errno EBUSY, i.e. retry 5 times over the course of
79 // one second.
80 const int kUnmountMaxNumOfRetries = 5;
81 const int kUnmountRetryIntervalInMicroseconds = 200 * 1000; // 200 ms
82
83 // Number of bytes to read from a file to attempt to detect its contents. Used
84 // in GetFileFormat.
85 const int kGetFileFormatMaxHeaderSize = 32;
86
87 // The path to the kernel's boot_id.
88 const char kBootIdPath[] = "/proc/sys/kernel/random/boot_id";
89
90 // Return true if |disk_name| is an MTD or a UBI device. Note that this test is
91 // simply based on the name of the device.
IsMtdDeviceName(const string & disk_name)92 bool IsMtdDeviceName(const string& disk_name) {
93 return base::StartsWith(disk_name, "/dev/ubi",
94 base::CompareCase::SENSITIVE) ||
95 base::StartsWith(disk_name, "/dev/mtd", base::CompareCase::SENSITIVE);
96 }
97
98 // Return the device name for the corresponding partition on a NAND device.
99 // WARNING: This function returns device names that are not mountable.
MakeNandPartitionName(int partition_num)100 string MakeNandPartitionName(int partition_num) {
101 switch (partition_num) {
102 case 2:
103 case 4:
104 case 6: {
105 return base::StringPrintf("/dev/mtd%d", partition_num);
106 }
107 default: {
108 return base::StringPrintf("/dev/ubi%d_0", partition_num);
109 }
110 }
111 }
112
113 // Return the device name for the corresponding partition on a NAND device that
114 // may be mountable (but may not be writable).
MakeNandPartitionNameForMount(int partition_num)115 string MakeNandPartitionNameForMount(int partition_num) {
116 switch (partition_num) {
117 case 2:
118 case 4:
119 case 6: {
120 return base::StringPrintf("/dev/mtd%d", partition_num);
121 }
122 case 3:
123 case 5:
124 case 7: {
125 return base::StringPrintf("/dev/ubiblock%d_0", partition_num);
126 }
127 default: {
128 return base::StringPrintf("/dev/ubi%d_0", partition_num);
129 }
130 }
131 }
132
133 // If |path| is absolute, or explicit relative to the current working directory,
134 // leaves it as is. Otherwise, uses the system's temp directory, as defined by
135 // base::GetTempDir() and prepends it to |path|. On success stores the full
136 // temporary path in |template_path| and returns true.
GetTempName(const string & path,base::FilePath * template_path)137 bool GetTempName(const string& path, base::FilePath* template_path) {
138 if (path[0] == '/' ||
139 base::StartsWith(path, "./", base::CompareCase::SENSITIVE) ||
140 base::StartsWith(path, "../", base::CompareCase::SENSITIVE)) {
141 *template_path = base::FilePath(path);
142 return true;
143 }
144
145 base::FilePath temp_dir;
146 #ifdef __ANDROID__
147 temp_dir = base::FilePath(constants::kNonVolatileDirectory).Append("tmp");
148 if (!base::PathExists(temp_dir))
149 TEST_AND_RETURN_FALSE(base::CreateDirectory(temp_dir));
150 #else
151 TEST_AND_RETURN_FALSE(base::GetTempDir(&temp_dir));
152 #endif // __ANDROID__
153 *template_path = temp_dir.Append(path);
154 return true;
155 }
156
157 } // namespace
158
159 namespace utils {
160
ParseECVersion(string input_line)161 string ParseECVersion(string input_line) {
162 base::TrimWhitespaceASCII(input_line, base::TRIM_ALL, &input_line);
163
164 // At this point we want to convert the format key=value pair from mosys to
165 // a vector of key value pairs.
166 vector<pair<string, string>> kv_pairs;
167 if (base::SplitStringIntoKeyValuePairs(input_line, '=', ' ', &kv_pairs)) {
168 for (const pair<string, string>& kv_pair : kv_pairs) {
169 // Finally match against the fw_verion which may have quotes.
170 if (kv_pair.first == "fw_version") {
171 string output;
172 // Trim any quotes.
173 base::TrimString(kv_pair.second, "\"", &output);
174 return output;
175 }
176 }
177 }
178 LOG(ERROR) << "Unable to parse fwid from ec info.";
179 return "";
180 }
181
WriteFile(const char * path,const void * data,int data_len)182 bool WriteFile(const char* path, const void* data, int data_len) {
183 DirectFileWriter writer;
184 TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
185 O_WRONLY | O_CREAT | O_TRUNC,
186 0600));
187 ScopedFileWriterCloser closer(&writer);
188 TEST_AND_RETURN_FALSE_ERRNO(writer.Write(data, data_len));
189 return true;
190 }
191
ReadAll(int fd,void * buf,size_t count,size_t * out_bytes_read,bool * eof)192 bool ReadAll(
193 int fd, void* buf, size_t count, size_t* out_bytes_read, bool* eof) {
194 char* c_buf = static_cast<char*>(buf);
195 size_t bytes_read = 0;
196 *eof = false;
197 while (bytes_read < count) {
198 ssize_t rc = HANDLE_EINTR(read(fd, c_buf + bytes_read, count - bytes_read));
199 if (rc < 0) {
200 // EAGAIN and EWOULDBLOCK are normal return values when there's no more
201 // input and we are in non-blocking mode.
202 if (errno != EWOULDBLOCK && errno != EAGAIN) {
203 PLOG(ERROR) << "Error reading fd " << fd;
204 *out_bytes_read = bytes_read;
205 return false;
206 }
207 break;
208 } else if (rc == 0) {
209 // A value of 0 means that we reached EOF and there is nothing else to
210 // read from this fd.
211 *eof = true;
212 break;
213 } else {
214 bytes_read += rc;
215 }
216 }
217 *out_bytes_read = bytes_read;
218 return true;
219 }
220
WriteAll(int fd,const void * buf,size_t count)221 bool WriteAll(int fd, const void* buf, size_t count) {
222 const char* c_buf = static_cast<const char*>(buf);
223 ssize_t bytes_written = 0;
224 while (bytes_written < static_cast<ssize_t>(count)) {
225 ssize_t rc = write(fd, c_buf + bytes_written, count - bytes_written);
226 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
227 bytes_written += rc;
228 }
229 return true;
230 }
231
PWriteAll(int fd,const void * buf,size_t count,off_t offset)232 bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
233 const char* c_buf = static_cast<const char*>(buf);
234 size_t bytes_written = 0;
235 int num_attempts = 0;
236 while (bytes_written < count) {
237 num_attempts++;
238 ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
239 offset + bytes_written);
240 // TODO(garnold) for debugging failure in chromium-os:31077; to be removed.
241 if (rc < 0) {
242 PLOG(ERROR) << "pwrite error; num_attempts=" << num_attempts
243 << " bytes_written=" << bytes_written
244 << " count=" << count << " offset=" << offset;
245 }
246 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
247 bytes_written += rc;
248 }
249 return true;
250 }
251
WriteAll(FileDescriptorPtr fd,const void * buf,size_t count)252 bool WriteAll(FileDescriptorPtr fd, const void* buf, size_t count) {
253 const char* c_buf = static_cast<const char*>(buf);
254 ssize_t bytes_written = 0;
255 while (bytes_written < static_cast<ssize_t>(count)) {
256 ssize_t rc = fd->Write(c_buf + bytes_written, count - bytes_written);
257 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
258 bytes_written += rc;
259 }
260 return true;
261 }
262
PWriteAll(FileDescriptorPtr fd,const void * buf,size_t count,off_t offset)263 bool PWriteAll(FileDescriptorPtr fd,
264 const void* buf,
265 size_t count,
266 off_t offset) {
267 TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(offset, SEEK_SET) !=
268 static_cast<off_t>(-1));
269 return WriteAll(fd, buf, count);
270 }
271
PReadAll(int fd,void * buf,size_t count,off_t offset,ssize_t * out_bytes_read)272 bool PReadAll(int fd, void* buf, size_t count, off_t offset,
273 ssize_t* out_bytes_read) {
274 char* c_buf = static_cast<char*>(buf);
275 ssize_t bytes_read = 0;
276 while (bytes_read < static_cast<ssize_t>(count)) {
277 ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
278 offset + bytes_read);
279 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
280 if (rc == 0) {
281 break;
282 }
283 bytes_read += rc;
284 }
285 *out_bytes_read = bytes_read;
286 return true;
287 }
288
PReadAll(FileDescriptorPtr fd,void * buf,size_t count,off_t offset,ssize_t * out_bytes_read)289 bool PReadAll(FileDescriptorPtr fd, void* buf, size_t count, off_t offset,
290 ssize_t* out_bytes_read) {
291 TEST_AND_RETURN_FALSE_ERRNO(fd->Seek(offset, SEEK_SET) !=
292 static_cast<off_t>(-1));
293 char* c_buf = static_cast<char*>(buf);
294 ssize_t bytes_read = 0;
295 while (bytes_read < static_cast<ssize_t>(count)) {
296 ssize_t rc = fd->Read(c_buf + bytes_read, count - bytes_read);
297 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
298 if (rc == 0) {
299 break;
300 }
301 bytes_read += rc;
302 }
303 *out_bytes_read = bytes_read;
304 return true;
305 }
306
307 // Append |nbytes| of content from |buf| to the vector pointed to by either
308 // |vec_p| or |str_p|.
AppendBytes(const uint8_t * buf,size_t nbytes,brillo::Blob * vec_p)309 static void AppendBytes(const uint8_t* buf, size_t nbytes,
310 brillo::Blob* vec_p) {
311 CHECK(buf);
312 CHECK(vec_p);
313 vec_p->insert(vec_p->end(), buf, buf + nbytes);
314 }
AppendBytes(const uint8_t * buf,size_t nbytes,string * str_p)315 static void AppendBytes(const uint8_t* buf, size_t nbytes,
316 string* str_p) {
317 CHECK(buf);
318 CHECK(str_p);
319 str_p->append(buf, buf + nbytes);
320 }
321
322 // Reads from an open file |fp|, appending the read content to the container
323 // pointer to by |out_p|. Returns true upon successful reading all of the
324 // file's content, false otherwise. If |size| is not -1, reads up to |size|
325 // bytes.
326 template <class T>
Read(FILE * fp,off_t size,T * out_p)327 static bool Read(FILE* fp, off_t size, T* out_p) {
328 CHECK(fp);
329 CHECK(size == -1 || size >= 0);
330 uint8_t buf[1024];
331 while (size == -1 || size > 0) {
332 off_t bytes_to_read = sizeof(buf);
333 if (size > 0 && bytes_to_read > size) {
334 bytes_to_read = size;
335 }
336 size_t nbytes = fread(buf, 1, bytes_to_read, fp);
337 if (!nbytes) {
338 break;
339 }
340 AppendBytes(buf, nbytes, out_p);
341 if (size != -1) {
342 CHECK(size >= static_cast<off_t>(nbytes));
343 size -= nbytes;
344 }
345 }
346 if (ferror(fp)) {
347 return false;
348 }
349 return size == 0 || feof(fp);
350 }
351
352 // Opens a file |path| for reading and appends its the contents to a container
353 // |out_p|. Starts reading the file from |offset|. If |offset| is beyond the end
354 // of the file, returns success. If |size| is not -1, reads up to |size| bytes.
355 template <class T>
ReadFileChunkAndAppend(const string & path,off_t offset,off_t size,T * out_p)356 static bool ReadFileChunkAndAppend(const string& path,
357 off_t offset,
358 off_t size,
359 T* out_p) {
360 CHECK_GE(offset, 0);
361 CHECK(size == -1 || size >= 0);
362 base::ScopedFILE fp(fopen(path.c_str(), "r"));
363 if (!fp.get())
364 return false;
365 if (offset) {
366 // Return success without appending any data if a chunk beyond the end of
367 // the file is requested.
368 if (offset >= FileSize(path)) {
369 return true;
370 }
371 TEST_AND_RETURN_FALSE_ERRNO(fseek(fp.get(), offset, SEEK_SET) == 0);
372 }
373 return Read(fp.get(), size, out_p);
374 }
375
376 // TODO(deymo): This is only used in unittest, but requires the private
377 // Read<string>() defined here. Expose Read<string>() or move to base/ version.
ReadPipe(const string & cmd,string * out_p)378 bool ReadPipe(const string& cmd, string* out_p) {
379 FILE* fp = popen(cmd.c_str(), "r");
380 if (!fp)
381 return false;
382 bool success = Read(fp, -1, out_p);
383 return (success && pclose(fp) >= 0);
384 }
385
ReadFile(const string & path,brillo::Blob * out_p)386 bool ReadFile(const string& path, brillo::Blob* out_p) {
387 return ReadFileChunkAndAppend(path, 0, -1, out_p);
388 }
389
ReadFile(const string & path,string * out_p)390 bool ReadFile(const string& path, string* out_p) {
391 return ReadFileChunkAndAppend(path, 0, -1, out_p);
392 }
393
ReadFileChunk(const string & path,off_t offset,off_t size,brillo::Blob * out_p)394 bool ReadFileChunk(const string& path, off_t offset, off_t size,
395 brillo::Blob* out_p) {
396 return ReadFileChunkAndAppend(path, offset, size, out_p);
397 }
398
BlockDevSize(int fd)399 off_t BlockDevSize(int fd) {
400 uint64_t dev_size;
401 int rc = ioctl(fd, BLKGETSIZE64, &dev_size);
402 if (rc == -1) {
403 dev_size = -1;
404 PLOG(ERROR) << "Error running ioctl(BLKGETSIZE64) on " << fd;
405 }
406 return dev_size;
407 }
408
FileSize(int fd)409 off_t FileSize(int fd) {
410 struct stat stbuf;
411 int rc = fstat(fd, &stbuf);
412 CHECK_EQ(rc, 0);
413 if (rc < 0) {
414 PLOG(ERROR) << "Error stat-ing " << fd;
415 return rc;
416 }
417 if (S_ISREG(stbuf.st_mode))
418 return stbuf.st_size;
419 if (S_ISBLK(stbuf.st_mode))
420 return BlockDevSize(fd);
421 LOG(ERROR) << "Couldn't determine the type of " << fd;
422 return -1;
423 }
424
FileSize(const string & path)425 off_t FileSize(const string& path) {
426 int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
427 if (fd == -1) {
428 PLOG(ERROR) << "Error opening " << path;
429 return fd;
430 }
431 off_t size = FileSize(fd);
432 if (size == -1)
433 PLOG(ERROR) << "Error getting file size of " << path;
434 close(fd);
435 return size;
436 }
437
HexDumpArray(const uint8_t * const arr,const size_t length)438 void HexDumpArray(const uint8_t* const arr, const size_t length) {
439 LOG(INFO) << "Logging array of length: " << length;
440 const unsigned int bytes_per_line = 16;
441 for (uint32_t i = 0; i < length; i += bytes_per_line) {
442 const unsigned int bytes_remaining = length - i;
443 const unsigned int bytes_per_this_line = min(bytes_per_line,
444 bytes_remaining);
445 char header[100];
446 int r = snprintf(header, sizeof(header), "0x%08x : ", i);
447 TEST_AND_RETURN(r == 13);
448 string line = header;
449 for (unsigned int j = 0; j < bytes_per_this_line; j++) {
450 char buf[20];
451 uint8_t c = arr[i + j];
452 r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c));
453 TEST_AND_RETURN(r == 3);
454 line += buf;
455 }
456 LOG(INFO) << line;
457 }
458 }
459
SplitPartitionName(const string & partition_name,string * out_disk_name,int * out_partition_num)460 bool SplitPartitionName(const string& partition_name,
461 string* out_disk_name,
462 int* out_partition_num) {
463 if (!base::StartsWith(partition_name, "/dev/",
464 base::CompareCase::SENSITIVE)) {
465 LOG(ERROR) << "Invalid partition device name: " << partition_name;
466 return false;
467 }
468
469 size_t last_nondigit_pos = partition_name.find_last_not_of("0123456789");
470 if (last_nondigit_pos == string::npos ||
471 (last_nondigit_pos + 1) == partition_name.size()) {
472 LOG(ERROR) << "Unable to parse partition device name: " << partition_name;
473 return false;
474 }
475
476 size_t partition_name_len = string::npos;
477 if (partition_name[last_nondigit_pos] == '_') {
478 // NAND block devices have weird naming which could be something
479 // like "/dev/ubiblock2_0". We discard "_0" in such a case.
480 size_t prev_nondigit_pos =
481 partition_name.find_last_not_of("0123456789", last_nondigit_pos - 1);
482 if (prev_nondigit_pos == string::npos ||
483 (prev_nondigit_pos + 1) == last_nondigit_pos) {
484 LOG(ERROR) << "Unable to parse partition device name: " << partition_name;
485 return false;
486 }
487
488 partition_name_len = last_nondigit_pos - prev_nondigit_pos;
489 last_nondigit_pos = prev_nondigit_pos;
490 }
491
492 if (out_disk_name) {
493 // Special case for MMC devices which have the following naming scheme:
494 // mmcblk0p2
495 size_t disk_name_len = last_nondigit_pos;
496 if (partition_name[last_nondigit_pos] != 'p' ||
497 last_nondigit_pos == 0 ||
498 !isdigit(partition_name[last_nondigit_pos - 1])) {
499 disk_name_len++;
500 }
501 *out_disk_name = partition_name.substr(0, disk_name_len);
502 }
503
504 if (out_partition_num) {
505 string partition_str = partition_name.substr(last_nondigit_pos + 1,
506 partition_name_len);
507 *out_partition_num = atoi(partition_str.c_str());
508 }
509 return true;
510 }
511
MakePartitionName(const string & disk_name,int partition_num)512 string MakePartitionName(const string& disk_name, int partition_num) {
513 if (partition_num < 1) {
514 LOG(ERROR) << "Invalid partition number: " << partition_num;
515 return string();
516 }
517
518 if (!base::StartsWith(disk_name, "/dev/", base::CompareCase::SENSITIVE)) {
519 LOG(ERROR) << "Invalid disk name: " << disk_name;
520 return string();
521 }
522
523 if (IsMtdDeviceName(disk_name)) {
524 // Special case for UBI block devices.
525 // 1. ubiblock is not writable, we need to use plain "ubi".
526 // 2. There is a "_0" suffix.
527 return MakeNandPartitionName(partition_num);
528 }
529
530 string partition_name = disk_name;
531 if (isdigit(partition_name.back())) {
532 // Special case for devices with names ending with a digit.
533 // Add "p" to separate the disk name from partition number,
534 // e.g. "/dev/loop0p2"
535 partition_name += 'p';
536 }
537
538 partition_name += std::to_string(partition_num);
539
540 return partition_name;
541 }
542
MakePartitionNameForMount(const string & part_name)543 string MakePartitionNameForMount(const string& part_name) {
544 if (IsMtdDeviceName(part_name)) {
545 int partition_num;
546 if (!SplitPartitionName(part_name, nullptr, &partition_num)) {
547 return "";
548 }
549 return MakeNandPartitionNameForMount(partition_num);
550 }
551 return part_name;
552 }
553
ErrnoNumberAsString(int err)554 string ErrnoNumberAsString(int err) {
555 char buf[100];
556 buf[0] = '\0';
557 return strerror_r(err, buf, sizeof(buf));
558 }
559
FileExists(const char * path)560 bool FileExists(const char* path) {
561 struct stat stbuf;
562 return 0 == lstat(path, &stbuf);
563 }
564
IsSymlink(const char * path)565 bool IsSymlink(const char* path) {
566 struct stat stbuf;
567 return lstat(path, &stbuf) == 0 && S_ISLNK(stbuf.st_mode) != 0;
568 }
569
TryAttachingUbiVolume(int volume_num,int timeout)570 bool TryAttachingUbiVolume(int volume_num, int timeout) {
571 const string volume_path = base::StringPrintf("/dev/ubi%d_0", volume_num);
572 if (FileExists(volume_path.c_str())) {
573 return true;
574 }
575
576 int exit_code;
577 vector<string> cmd = {
578 "ubiattach",
579 "-m",
580 base::StringPrintf("%d", volume_num),
581 "-d",
582 base::StringPrintf("%d", volume_num)
583 };
584 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &exit_code, nullptr));
585 TEST_AND_RETURN_FALSE(exit_code == 0);
586
587 cmd = {
588 "ubiblock",
589 "--create",
590 volume_path
591 };
592 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &exit_code, nullptr));
593 TEST_AND_RETURN_FALSE(exit_code == 0);
594
595 while (timeout > 0 && !FileExists(volume_path.c_str())) {
596 sleep(1);
597 timeout--;
598 }
599
600 return FileExists(volume_path.c_str());
601 }
602
MakeTempFile(const string & base_filename_template,string * filename,int * fd)603 bool MakeTempFile(const string& base_filename_template,
604 string* filename,
605 int* fd) {
606 base::FilePath filename_template;
607 TEST_AND_RETURN_FALSE(
608 GetTempName(base_filename_template, &filename_template));
609 DCHECK(filename || fd);
610 vector<char> buf(filename_template.value().size() + 1);
611 memcpy(buf.data(), filename_template.value().data(),
612 filename_template.value().size());
613 buf[filename_template.value().size()] = '\0';
614
615 int mkstemp_fd = mkstemp(buf.data());
616 TEST_AND_RETURN_FALSE_ERRNO(mkstemp_fd >= 0);
617 if (filename) {
618 *filename = buf.data();
619 }
620 if (fd) {
621 *fd = mkstemp_fd;
622 } else {
623 close(mkstemp_fd);
624 }
625 return true;
626 }
627
MakeTempDirectory(const string & base_dirname_template,string * dirname)628 bool MakeTempDirectory(const string& base_dirname_template,
629 string* dirname) {
630 base::FilePath dirname_template;
631 TEST_AND_RETURN_FALSE(GetTempName(base_dirname_template, &dirname_template));
632 DCHECK(dirname);
633 vector<char> buf(dirname_template.value().size() + 1);
634 memcpy(buf.data(), dirname_template.value().data(),
635 dirname_template.value().size());
636 buf[dirname_template.value().size()] = '\0';
637
638 char* return_code = mkdtemp(buf.data());
639 TEST_AND_RETURN_FALSE_ERRNO(return_code != nullptr);
640 *dirname = buf.data();
641 return true;
642 }
643
SetBlockDeviceReadOnly(const string & device,bool read_only)644 bool SetBlockDeviceReadOnly(const string& device, bool read_only) {
645 int fd = HANDLE_EINTR(open(device.c_str(), O_RDONLY | O_CLOEXEC));
646 if (fd < 0) {
647 PLOG(ERROR) << "Opening block device " << device;
648 return false;
649 }
650 ScopedFdCloser fd_closer(&fd);
651 // We take no action if not needed.
652 int read_only_flag;
653 int expected_flag = read_only ? 1 : 0;
654 int rc = ioctl(fd, BLKROGET, &read_only_flag);
655 // In case of failure reading the setting we will try to set it anyway.
656 if (rc == 0 && read_only_flag == expected_flag)
657 return true;
658
659 rc = ioctl(fd, BLKROSET, &expected_flag);
660 if (rc != 0) {
661 PLOG(ERROR) << "Marking block device " << device << " as read_only="
662 << expected_flag;
663 return false;
664 }
665 return true;
666 }
667
MountFilesystem(const string & device,const string & mountpoint,unsigned long mountflags,const string & type,const string & fs_mount_options)668 bool MountFilesystem(const string& device,
669 const string& mountpoint,
670 unsigned long mountflags, // NOLINT(runtime/int)
671 const string& type,
672 const string& fs_mount_options) {
673 vector<const char*> fstypes;
674 if (type.empty()) {
675 fstypes = {"ext2", "ext3", "ext4", "squashfs"};
676 } else {
677 fstypes = {type.c_str()};
678 }
679 for (const char* fstype : fstypes) {
680 int rc = mount(device.c_str(), mountpoint.c_str(), fstype, mountflags,
681 fs_mount_options.c_str());
682 if (rc == 0)
683 return true;
684
685 PLOG(WARNING) << "Unable to mount destination device " << device
686 << " on " << mountpoint << " as " << fstype;
687 }
688 if (!type.empty()) {
689 LOG(ERROR) << "Unable to mount " << device << " with any supported type";
690 }
691 return false;
692 }
693
UnmountFilesystem(const string & mountpoint)694 bool UnmountFilesystem(const string& mountpoint) {
695 for (int num_retries = 0; ; ++num_retries) {
696 if (umount(mountpoint.c_str()) == 0)
697 break;
698
699 TEST_AND_RETURN_FALSE_ERRNO(errno == EBUSY &&
700 num_retries < kUnmountMaxNumOfRetries);
701 usleep(kUnmountRetryIntervalInMicroseconds);
702 }
703 return true;
704 }
705
GetFilesystemSize(const string & device,int * out_block_count,int * out_block_size)706 bool GetFilesystemSize(const string& device,
707 int* out_block_count,
708 int* out_block_size) {
709 int fd = HANDLE_EINTR(open(device.c_str(), O_RDONLY));
710 TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
711 ScopedFdCloser fd_closer(&fd);
712 return GetFilesystemSizeFromFD(fd, out_block_count, out_block_size);
713 }
714
GetFilesystemSizeFromFD(int fd,int * out_block_count,int * out_block_size)715 bool GetFilesystemSizeFromFD(int fd,
716 int* out_block_count,
717 int* out_block_size) {
718 TEST_AND_RETURN_FALSE(fd >= 0);
719
720 // Determine the filesystem size by directly reading the block count and
721 // block size information from the superblock. Supported FS are ext3 and
722 // squashfs.
723
724 // Read from the fd only once and detect in memory. The first 2 KiB is enough
725 // to read the ext2 superblock (located at offset 1024) and the squashfs
726 // superblock (located at offset 0).
727 const ssize_t kBufferSize = 2048;
728
729 uint8_t buffer[kBufferSize];
730 if (HANDLE_EINTR(pread(fd, buffer, kBufferSize, 0)) != kBufferSize) {
731 PLOG(ERROR) << "Unable to read the file system header:";
732 return false;
733 }
734
735 if (GetSquashfs4Size(buffer, kBufferSize, out_block_count, out_block_size))
736 return true;
737 if (GetExt3Size(buffer, kBufferSize, out_block_count, out_block_size))
738 return true;
739
740 LOG(ERROR) << "Unable to determine file system type.";
741 return false;
742 }
743
GetExt3Size(const uint8_t * buffer,size_t buffer_size,int * out_block_count,int * out_block_size)744 bool GetExt3Size(const uint8_t* buffer, size_t buffer_size,
745 int* out_block_count,
746 int* out_block_size) {
747 // See include/linux/ext2_fs.h for more details on the structure. We obtain
748 // ext2 constants from ext2fs/ext2fs.h header but we don't link with the
749 // library.
750 if (buffer_size < SUPERBLOCK_OFFSET + SUPERBLOCK_SIZE)
751 return false;
752
753 const uint8_t* superblock = buffer + SUPERBLOCK_OFFSET;
754
755 // ext3_fs.h: ext3_super_block.s_blocks_count
756 uint32_t block_count =
757 *reinterpret_cast<const uint32_t*>(superblock + 1 * sizeof(int32_t));
758
759 // ext3_fs.h: ext3_super_block.s_log_block_size
760 uint32_t log_block_size =
761 *reinterpret_cast<const uint32_t*>(superblock + 6 * sizeof(int32_t));
762
763 // ext3_fs.h: ext3_super_block.s_magic
764 uint16_t magic =
765 *reinterpret_cast<const uint16_t*>(superblock + 14 * sizeof(int32_t));
766
767 block_count = le32toh(block_count);
768 log_block_size = le32toh(log_block_size) + EXT2_MIN_BLOCK_LOG_SIZE;
769 magic = le16toh(magic);
770
771 // Sanity check the parameters.
772 TEST_AND_RETURN_FALSE(magic == EXT2_SUPER_MAGIC);
773 TEST_AND_RETURN_FALSE(log_block_size >= EXT2_MIN_BLOCK_LOG_SIZE &&
774 log_block_size <= EXT2_MAX_BLOCK_LOG_SIZE);
775 TEST_AND_RETURN_FALSE(block_count > 0);
776
777 if (out_block_count)
778 *out_block_count = block_count;
779 if (out_block_size)
780 *out_block_size = 1 << log_block_size;
781 return true;
782 }
783
GetSquashfs4Size(const uint8_t * buffer,size_t buffer_size,int * out_block_count,int * out_block_size)784 bool GetSquashfs4Size(const uint8_t* buffer, size_t buffer_size,
785 int* out_block_count,
786 int* out_block_size) {
787 // See fs/squashfs/squashfs_fs.h for format details. We only support
788 // Squashfs 4.x little endian.
789
790 // sizeof(struct squashfs_super_block)
791 const size_t kSquashfsSuperBlockSize = 96;
792 if (buffer_size < kSquashfsSuperBlockSize)
793 return false;
794
795 // Check magic, squashfs_fs.h: SQUASHFS_MAGIC
796 if (memcmp(buffer, "hsqs", 4) != 0)
797 return false; // Only little endian is supported.
798
799 // squashfs_fs.h: struct squashfs_super_block.s_major
800 uint16_t s_major = *reinterpret_cast<const uint16_t*>(
801 buffer + 5 * sizeof(uint32_t) + 4 * sizeof(uint16_t));
802
803 if (s_major != 4) {
804 LOG(ERROR) << "Found unsupported squashfs major version " << s_major;
805 return false;
806 }
807
808 // squashfs_fs.h: struct squashfs_super_block.bytes_used
809 uint64_t bytes_used = *reinterpret_cast<const int64_t*>(
810 buffer + 5 * sizeof(uint32_t) + 6 * sizeof(uint16_t) + sizeof(uint64_t));
811
812 const int block_size = 4096;
813
814 // The squashfs' bytes_used doesn't need to be aligned with the block boundary
815 // so we round up to the nearest blocksize.
816 if (out_block_count)
817 *out_block_count = (bytes_used + block_size - 1) / block_size;
818 if (out_block_size)
819 *out_block_size = block_size;
820 return true;
821 }
822
IsExtFilesystem(const string & device)823 bool IsExtFilesystem(const string& device) {
824 brillo::Blob header;
825 // The first 2 KiB is enough to read the ext2 superblock (located at offset
826 // 1024).
827 if (!ReadFileChunk(device, 0, 2048, &header))
828 return false;
829 return GetExt3Size(header.data(), header.size(), nullptr, nullptr);
830 }
831
IsSquashfsFilesystem(const string & device)832 bool IsSquashfsFilesystem(const string& device) {
833 brillo::Blob header;
834 // The first 96 is enough to read the squashfs superblock.
835 const ssize_t kSquashfsSuperBlockSize = 96;
836 if (!ReadFileChunk(device, 0, kSquashfsSuperBlockSize, &header))
837 return false;
838 return GetSquashfs4Size(header.data(), header.size(), nullptr, nullptr);
839 }
840
841 // Tries to parse the header of an ELF file to obtain a human-readable
842 // description of it on the |output| string.
GetFileFormatELF(const uint8_t * buffer,size_t size,string * output)843 static bool GetFileFormatELF(const uint8_t* buffer, size_t size,
844 string* output) {
845 // 0x00: EI_MAG - ELF magic header, 4 bytes.
846 if (size < SELFMAG || memcmp(buffer, ELFMAG, SELFMAG) != 0)
847 return false;
848 *output = "ELF";
849
850 // 0x04: EI_CLASS, 1 byte.
851 if (size < EI_CLASS + 1)
852 return true;
853 switch (buffer[EI_CLASS]) {
854 case ELFCLASS32:
855 *output += " 32-bit";
856 break;
857 case ELFCLASS64:
858 *output += " 64-bit";
859 break;
860 default:
861 *output += " ?-bit";
862 }
863
864 // 0x05: EI_DATA, endianness, 1 byte.
865 if (size < EI_DATA + 1)
866 return true;
867 uint8_t ei_data = buffer[EI_DATA];
868 switch (ei_data) {
869 case ELFDATA2LSB:
870 *output += " little-endian";
871 break;
872 case ELFDATA2MSB:
873 *output += " big-endian";
874 break;
875 default:
876 *output += " ?-endian";
877 // Don't parse anything after the 0x10 offset if endianness is unknown.
878 return true;
879 }
880
881 const Elf32_Ehdr* hdr = reinterpret_cast<const Elf32_Ehdr*>(buffer);
882 // 0x12: e_machine, 2 byte endianness based on ei_data. The position (0x12)
883 // and size is the same for both 32 and 64 bits.
884 if (size < offsetof(Elf32_Ehdr, e_machine) + sizeof(hdr->e_machine))
885 return true;
886 uint16_t e_machine;
887 // Fix endianess regardless of the host endianess.
888 if (ei_data == ELFDATA2LSB)
889 e_machine = le16toh(hdr->e_machine);
890 else
891 e_machine = be16toh(hdr->e_machine);
892
893 switch (e_machine) {
894 case EM_386:
895 *output += " x86";
896 break;
897 case EM_MIPS:
898 *output += " mips";
899 break;
900 case EM_ARM:
901 *output += " arm";
902 break;
903 case EM_X86_64:
904 *output += " x86-64";
905 break;
906 default:
907 *output += " unknown-arch";
908 }
909 return true;
910 }
911
GetFileFormat(const string & path)912 string GetFileFormat(const string& path) {
913 brillo::Blob buffer;
914 if (!ReadFileChunkAndAppend(path, 0, kGetFileFormatMaxHeaderSize, &buffer))
915 return "File not found.";
916
917 string result;
918 if (GetFileFormatELF(buffer.data(), buffer.size(), &result))
919 return result;
920
921 return "data";
922 }
923
924 namespace {
925 // Do the actual trigger. We do it as a main-loop callback to (try to) get a
926 // consistent stack trace.
TriggerCrashReporterUpload()927 void TriggerCrashReporterUpload() {
928 pid_t pid = fork();
929 CHECK_GE(pid, 0) << "fork failed"; // fork() failed. Something is very wrong.
930 if (pid == 0) {
931 // We are the child. Crash.
932 abort(); // never returns
933 }
934 // We are the parent. Wait for child to terminate.
935 pid_t result = waitpid(pid, nullptr, 0);
936 LOG_IF(ERROR, result < 0) << "waitpid() failed";
937 }
938 } // namespace
939
ScheduleCrashReporterUpload()940 void ScheduleCrashReporterUpload() {
941 brillo::MessageLoop::current()->PostTask(
942 FROM_HERE,
943 base::Bind(&TriggerCrashReporterUpload));
944 }
945
FuzzInt(int value,unsigned int range)946 int FuzzInt(int value, unsigned int range) {
947 int min = value - range / 2;
948 int max = value + range - range / 2;
949 return base::RandInt(min, max);
950 }
951
FormatSecs(unsigned secs)952 string FormatSecs(unsigned secs) {
953 return FormatTimeDelta(TimeDelta::FromSeconds(secs));
954 }
955
FormatTimeDelta(TimeDelta delta)956 string FormatTimeDelta(TimeDelta delta) {
957 string str;
958
959 // Handle negative durations by prefixing with a minus.
960 if (delta.ToInternalValue() < 0) {
961 delta *= -1;
962 str = "-";
963 }
964
965 // Canonicalize into days, hours, minutes, seconds and microseconds.
966 unsigned days = delta.InDays();
967 delta -= TimeDelta::FromDays(days);
968 unsigned hours = delta.InHours();
969 delta -= TimeDelta::FromHours(hours);
970 unsigned mins = delta.InMinutes();
971 delta -= TimeDelta::FromMinutes(mins);
972 unsigned secs = delta.InSeconds();
973 delta -= TimeDelta::FromSeconds(secs);
974 unsigned usecs = delta.InMicroseconds();
975
976 if (days)
977 base::StringAppendF(&str, "%ud", days);
978 if (days || hours)
979 base::StringAppendF(&str, "%uh", hours);
980 if (days || hours || mins)
981 base::StringAppendF(&str, "%um", mins);
982 base::StringAppendF(&str, "%u", secs);
983 if (usecs) {
984 int width = 6;
985 while ((usecs / 10) * 10 == usecs) {
986 usecs /= 10;
987 width--;
988 }
989 base::StringAppendF(&str, ".%0*u", width, usecs);
990 }
991 base::StringAppendF(&str, "s");
992 return str;
993 }
994
ToString(const Time utc_time)995 string ToString(const Time utc_time) {
996 Time::Exploded exp_time;
997 utc_time.UTCExplode(&exp_time);
998 return base::StringPrintf("%d/%d/%d %d:%02d:%02d GMT",
999 exp_time.month,
1000 exp_time.day_of_month,
1001 exp_time.year,
1002 exp_time.hour,
1003 exp_time.minute,
1004 exp_time.second);
1005 }
1006
ToString(bool b)1007 string ToString(bool b) {
1008 return (b ? "true" : "false");
1009 }
1010
ToString(DownloadSource source)1011 string ToString(DownloadSource source) {
1012 switch (source) {
1013 case kDownloadSourceHttpsServer: return "HttpsServer";
1014 case kDownloadSourceHttpServer: return "HttpServer";
1015 case kDownloadSourceHttpPeer: return "HttpPeer";
1016 case kNumDownloadSources: return "Unknown";
1017 // Don't add a default case to let the compiler warn about newly added
1018 // download sources which should be added here.
1019 }
1020
1021 return "Unknown";
1022 }
1023
ToString(PayloadType payload_type)1024 string ToString(PayloadType payload_type) {
1025 switch (payload_type) {
1026 case kPayloadTypeDelta: return "Delta";
1027 case kPayloadTypeFull: return "Full";
1028 case kPayloadTypeForcedFull: return "ForcedFull";
1029 case kNumPayloadTypes: return "Unknown";
1030 // Don't add a default case to let the compiler warn about newly added
1031 // payload types which should be added here.
1032 }
1033
1034 return "Unknown";
1035 }
1036
GetBaseErrorCode(ErrorCode code)1037 ErrorCode GetBaseErrorCode(ErrorCode code) {
1038 // Ignore the higher order bits in the code by applying the mask as
1039 // we want the enumerations to be in the small contiguous range
1040 // with values less than ErrorCode::kUmaReportedMax.
1041 ErrorCode base_code = static_cast<ErrorCode>(
1042 static_cast<int>(code) & ~static_cast<int>(ErrorCode::kSpecialFlags));
1043
1044 // Make additional adjustments required for UMA and error classification.
1045 // TODO(jaysri): Move this logic to UeErrorCode.cc when we fix
1046 // chromium-os:34369.
1047 if (base_code >= ErrorCode::kOmahaRequestHTTPResponseBase) {
1048 // Since we want to keep the enums to a small value, aggregate all HTTP
1049 // errors into this one bucket for UMA and error classification purposes.
1050 LOG(INFO) << "Converting error code " << base_code
1051 << " to ErrorCode::kOmahaErrorInHTTPResponse";
1052 base_code = ErrorCode::kOmahaErrorInHTTPResponse;
1053 }
1054
1055 return base_code;
1056 }
1057
CreatePowerwashMarkerFile(const char * file_path)1058 bool CreatePowerwashMarkerFile(const char* file_path) {
1059 const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
1060 bool result = utils::WriteFile(marker_file,
1061 kPowerwashCommand,
1062 strlen(kPowerwashCommand));
1063 if (result) {
1064 LOG(INFO) << "Created " << marker_file << " to powerwash on next reboot";
1065 } else {
1066 PLOG(ERROR) << "Error in creating powerwash marker file: " << marker_file;
1067 }
1068
1069 return result;
1070 }
1071
DeletePowerwashMarkerFile(const char * file_path)1072 bool DeletePowerwashMarkerFile(const char* file_path) {
1073 const char* marker_file = file_path ? file_path : kPowerwashMarkerFile;
1074 const base::FilePath kPowerwashMarkerPath(marker_file);
1075 bool result = base::DeleteFile(kPowerwashMarkerPath, false);
1076
1077 if (result)
1078 LOG(INFO) << "Successfully deleted the powerwash marker file : "
1079 << marker_file;
1080 else
1081 PLOG(ERROR) << "Could not delete the powerwash marker file : "
1082 << marker_file;
1083
1084 return result;
1085 }
1086
TimeFromStructTimespec(struct timespec * ts)1087 Time TimeFromStructTimespec(struct timespec *ts) {
1088 int64_t us = static_cast<int64_t>(ts->tv_sec) * Time::kMicrosecondsPerSecond +
1089 static_cast<int64_t>(ts->tv_nsec) / Time::kNanosecondsPerMicrosecond;
1090 return Time::UnixEpoch() + TimeDelta::FromMicroseconds(us);
1091 }
1092
StringVectorToString(const vector<string> & vec_str)1093 string StringVectorToString(const vector<string> &vec_str) {
1094 string str = "[";
1095 for (vector<string>::const_iterator i = vec_str.begin();
1096 i != vec_str.end(); ++i) {
1097 if (i != vec_str.begin())
1098 str += ", ";
1099 str += '"';
1100 str += *i;
1101 str += '"';
1102 }
1103 str += "]";
1104 return str;
1105 }
1106
CalculateP2PFileId(const string & payload_hash,size_t payload_size)1107 string CalculateP2PFileId(const string& payload_hash, size_t payload_size) {
1108 string encoded_hash = brillo::data_encoding::Base64Encode(payload_hash);
1109 return base::StringPrintf("cros_update_size_%" PRIuS "_hash_%s",
1110 payload_size,
1111 encoded_hash.c_str());
1112 }
1113
DecodeAndStoreBase64String(const string & base64_encoded,base::FilePath * out_path)1114 bool DecodeAndStoreBase64String(const string& base64_encoded,
1115 base::FilePath *out_path) {
1116 brillo::Blob contents;
1117
1118 out_path->clear();
1119
1120 if (base64_encoded.size() == 0) {
1121 LOG(ERROR) << "Can't decode empty string.";
1122 return false;
1123 }
1124
1125 if (!brillo::data_encoding::Base64Decode(base64_encoded, &contents) ||
1126 contents.size() == 0) {
1127 LOG(ERROR) << "Error decoding base64.";
1128 return false;
1129 }
1130
1131 FILE *file = base::CreateAndOpenTemporaryFile(out_path);
1132 if (file == nullptr) {
1133 LOG(ERROR) << "Error creating temporary file.";
1134 return false;
1135 }
1136
1137 if (fwrite(contents.data(), 1, contents.size(), file) != contents.size()) {
1138 PLOG(ERROR) << "Error writing to temporary file.";
1139 if (fclose(file) != 0)
1140 PLOG(ERROR) << "Error closing temporary file.";
1141 if (unlink(out_path->value().c_str()) != 0)
1142 PLOG(ERROR) << "Error unlinking temporary file.";
1143 out_path->clear();
1144 return false;
1145 }
1146
1147 if (fclose(file) != 0) {
1148 PLOG(ERROR) << "Error closing temporary file.";
1149 out_path->clear();
1150 return false;
1151 }
1152
1153 return true;
1154 }
1155
ConvertToOmahaInstallDate(Time time,int * out_num_days)1156 bool ConvertToOmahaInstallDate(Time time, int *out_num_days) {
1157 time_t unix_time = time.ToTimeT();
1158 // Output of: date +"%s" --date="Jan 1, 2007 0:00 PST".
1159 const time_t kOmahaEpoch = 1167638400;
1160 const int64_t kNumSecondsPerWeek = 7*24*3600;
1161 const int64_t kNumDaysPerWeek = 7;
1162
1163 time_t omaha_time = unix_time - kOmahaEpoch;
1164
1165 if (omaha_time < 0)
1166 return false;
1167
1168 // Note, as per the comment in utils.h we are deliberately not
1169 // handling DST correctly.
1170
1171 int64_t num_weeks_since_omaha_epoch = omaha_time / kNumSecondsPerWeek;
1172 *out_num_days = num_weeks_since_omaha_epoch * kNumDaysPerWeek;
1173
1174 return true;
1175 }
1176
GetMinorVersion(const brillo::KeyValueStore & store,uint32_t * minor_version)1177 bool GetMinorVersion(const brillo::KeyValueStore& store,
1178 uint32_t* minor_version) {
1179 string result;
1180 if (store.GetString("PAYLOAD_MINOR_VERSION", &result)) {
1181 if (!base::StringToUint(result, minor_version)) {
1182 LOG(ERROR) << "StringToUint failed when parsing delta minor version.";
1183 return false;
1184 }
1185 return true;
1186 }
1187 return false;
1188 }
1189
IsZlibCompatible(const string & fingerprint)1190 bool IsZlibCompatible(const string& fingerprint) {
1191 if (fingerprint.size() != sizeof(kCompatibleZlibFingerprint[0]) - 1) {
1192 LOG(ERROR) << "Invalid fingerprint: " << fingerprint;
1193 return false;
1194 }
1195 for (auto& f : kCompatibleZlibFingerprint) {
1196 if (base::CompareCaseInsensitiveASCII(fingerprint, f) == 0) {
1197 return true;
1198 }
1199 }
1200 return false;
1201 }
1202
ReadExtents(const string & path,const vector<Extent> & extents,brillo::Blob * out_data,ssize_t out_data_size,size_t block_size)1203 bool ReadExtents(const string& path, const vector<Extent>& extents,
1204 brillo::Blob* out_data, ssize_t out_data_size,
1205 size_t block_size) {
1206 brillo::Blob data(out_data_size);
1207 ssize_t bytes_read = 0;
1208 int fd = open(path.c_str(), O_RDONLY);
1209 TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
1210 ScopedFdCloser fd_closer(&fd);
1211
1212 for (const Extent& extent : extents) {
1213 ssize_t bytes_read_this_iteration = 0;
1214 ssize_t bytes = extent.num_blocks() * block_size;
1215 TEST_AND_RETURN_FALSE(bytes_read + bytes <= out_data_size);
1216 TEST_AND_RETURN_FALSE(utils::PReadAll(fd,
1217 &data[bytes_read],
1218 bytes,
1219 extent.start_block() * block_size,
1220 &bytes_read_this_iteration));
1221 TEST_AND_RETURN_FALSE(bytes_read_this_iteration == bytes);
1222 bytes_read += bytes_read_this_iteration;
1223 }
1224 TEST_AND_RETURN_FALSE(out_data_size == bytes_read);
1225 *out_data = data;
1226 return true;
1227 }
1228
GetBootId(string * boot_id)1229 bool GetBootId(string* boot_id) {
1230 TEST_AND_RETURN_FALSE(
1231 base::ReadFileToString(base::FilePath(kBootIdPath), boot_id));
1232 base::TrimWhitespaceASCII(*boot_id, base::TRIM_TRAILING, boot_id);
1233 return true;
1234 }
1235
1236 } // namespace utils
1237
1238 } // namespace chromeos_update_engine
1239