blob: c8a4da64e29cf1d23037a4b9c7c329ccc593ef72 [file] [log] [blame]
Darin Petkov296889c2010-07-23 16:20:54 -07001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/utils.h"
Darin Petkovf74eb652010-08-04 12:08:38 -07006
adlr@google.com3defe6a2009-12-04 20:57:17 +00007#include <sys/mount.h>
Darin Petkovc6c135c2010-08-11 13:36:18 -07008#include <sys/resource.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +00009#include <sys/stat.h>
10#include <sys/types.h>
11#include <dirent.h>
12#include <errno.h>
Andrew de los Reyes970bb282009-12-09 16:34:04 -080013#include <fcntl.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <unistd.h>
Darin Petkovf74eb652010-08-04 12:08:38 -070018
adlr@google.com3defe6a2009-12-04 20:57:17 +000019#include <algorithm>
Darin Petkovf74eb652010-08-04 12:08:38 -070020
Darin Petkovd3f8c892010-10-12 21:38:45 -070021#include <base/eintr_wrapper.h>
Will Drewry8f71da82010-08-30 14:07:11 -050022#include <base/file_path.h>
23#include <base/file_util.h>
24#include <base/rand_util.h>
25#include <base/string_util.h>
26#include <base/logging.h>
27#include <rootdev/rootdev.h>
28
Andrew de los Reyes970bb282009-12-09 16:34:04 -080029#include "update_engine/file_writer.h"
Darin Petkov33d30642010-08-04 10:18:57 -070030#include "update_engine/omaha_request_params.h"
Darin Petkov296889c2010-07-23 16:20:54 -070031#include "update_engine/subprocess.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000032
33using std::min;
34using std::string;
35using std::vector;
36
37namespace chromeos_update_engine {
38
39namespace utils {
40
Darin Petkov2a0e6332010-09-24 14:43:41 -070041static const char kOOBECompletedMarker[] = "/home/chronos/.oobe_completed";
42
Darin Petkov33d30642010-08-04 10:18:57 -070043bool IsOfficialBuild() {
44 OmahaRequestDeviceParams params;
45 if (!params.Init("", "")) {
46 return true;
47 }
48 return params.app_track != "buildbot-build" &&
49 params.app_track != "developer-build";
50}
51
Darin Petkov2a0e6332010-09-24 14:43:41 -070052bool IsOOBEComplete() {
53 return file_util::PathExists(FilePath(kOOBECompletedMarker));
54}
55
Andrew de los Reyes970bb282009-12-09 16:34:04 -080056bool WriteFile(const char* path, const char* data, int data_len) {
57 DirectFileWriter writer;
58 TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
59 O_WRONLY | O_CREAT | O_TRUNC,
Chris Masone4dc2ada2010-09-23 12:43:03 -070060 0600));
Andrew de los Reyes970bb282009-12-09 16:34:04 -080061 ScopedFileWriterCloser closer(&writer);
62 TEST_AND_RETURN_FALSE_ERRNO(data_len == writer.Write(data, data_len));
63 return true;
64}
65
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070066bool WriteAll(int fd, const void* buf, size_t count) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070067 const char* c_buf = static_cast<const char*>(buf);
68 ssize_t bytes_written = 0;
69 while (bytes_written < static_cast<ssize_t>(count)) {
70 ssize_t rc = write(fd, c_buf + bytes_written, count - bytes_written);
71 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
72 bytes_written += rc;
73 }
74 return true;
75}
76
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070077bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
78 const char* c_buf = static_cast<const char*>(buf);
79 ssize_t bytes_written = 0;
80 while (bytes_written < static_cast<ssize_t>(count)) {
81 ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
82 offset + bytes_written);
83 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
84 bytes_written += rc;
85 }
86 return true;
87}
88
89bool PReadAll(int fd, void* buf, size_t count, off_t offset,
90 ssize_t* out_bytes_read) {
91 char* c_buf = static_cast<char*>(buf);
92 ssize_t bytes_read = 0;
93 while (bytes_read < static_cast<ssize_t>(count)) {
94 ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
95 offset + bytes_read);
96 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
97 if (rc == 0) {
98 break;
99 }
100 bytes_read += rc;
101 }
102 *out_bytes_read = bytes_read;
103 return true;
Darin Petkov296889c2010-07-23 16:20:54 -0700104
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700105}
106
adlr@google.com3defe6a2009-12-04 20:57:17 +0000107bool ReadFile(const std::string& path, std::vector<char>* out) {
108 CHECK(out);
109 FILE* fp = fopen(path.c_str(), "r");
110 if (!fp)
111 return false;
112 const size_t kChunkSize = 1024;
113 size_t read_size;
114 do {
115 char buf[kChunkSize];
116 read_size = fread(buf, 1, kChunkSize, fp);
117 if (read_size == 0)
118 break;
119 out->insert(out->end(), buf, buf + read_size);
120 } while (read_size == kChunkSize);
121 bool success = !ferror(fp);
122 TEST_AND_RETURN_FALSE_ERRNO(fclose(fp) == 0);
123 return success;
124}
125
126bool ReadFileToString(const std::string& path, std::string* out) {
127 vector<char> data;
128 bool success = ReadFile(path, &data);
129 if (!success) {
130 return false;
131 }
132 (*out) = string(&data[0], data.size());
133 return true;
134}
135
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700136off_t FileSize(const string& path) {
137 struct stat stbuf;
138 int rc = stat(path.c_str(), &stbuf);
139 CHECK_EQ(rc, 0);
140 if (rc < 0)
141 return rc;
142 return stbuf.st_size;
143}
144
adlr@google.com3defe6a2009-12-04 20:57:17 +0000145void HexDumpArray(const unsigned char* const arr, const size_t length) {
146 const unsigned char* const char_arr =
147 reinterpret_cast<const unsigned char* const>(arr);
148 LOG(INFO) << "Logging array of length: " << length;
149 const unsigned int bytes_per_line = 16;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700150 for (uint32_t i = 0; i < length; i += bytes_per_line) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000151 const unsigned int bytes_remaining = length - i;
152 const unsigned int bytes_per_this_line = min(bytes_per_line,
153 bytes_remaining);
154 char header[100];
155 int r = snprintf(header, sizeof(header), "0x%08x : ", i);
156 TEST_AND_RETURN(r == 13);
157 string line = header;
158 for (unsigned int j = 0; j < bytes_per_this_line; j++) {
159 char buf[20];
160 unsigned char c = char_arr[i + j];
161 r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c));
162 TEST_AND_RETURN(r == 3);
163 line += buf;
164 }
165 LOG(INFO) << line;
166 }
167}
168
169namespace {
170class ScopedDirCloser {
171 public:
172 explicit ScopedDirCloser(DIR** dir) : dir_(dir) {}
173 ~ScopedDirCloser() {
174 if (dir_ && *dir_) {
175 int r = closedir(*dir_);
176 TEST_AND_RETURN_ERRNO(r == 0);
177 *dir_ = NULL;
178 dir_ = NULL;
179 }
180 }
181 private:
182 DIR** dir_;
183};
184} // namespace {}
185
186bool RecursiveUnlinkDir(const std::string& path) {
187 struct stat stbuf;
188 int r = lstat(path.c_str(), &stbuf);
189 TEST_AND_RETURN_FALSE_ERRNO((r == 0) || (errno == ENOENT));
190 if ((r < 0) && (errno == ENOENT))
191 // path request is missing. that's fine.
192 return true;
193 if (!S_ISDIR(stbuf.st_mode)) {
194 TEST_AND_RETURN_FALSE_ERRNO((unlink(path.c_str()) == 0) ||
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700195 (errno == ENOENT));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000196 // success or path disappeared before we could unlink.
197 return true;
198 }
199 {
200 // We have a dir, unlink all children, then delete dir
201 DIR *dir = opendir(path.c_str());
202 TEST_AND_RETURN_FALSE_ERRNO(dir);
203 ScopedDirCloser dir_closer(&dir);
204 struct dirent dir_entry;
205 struct dirent *dir_entry_p;
206 int err = 0;
207 while ((err = readdir_r(dir, &dir_entry, &dir_entry_p)) == 0) {
208 if (dir_entry_p == NULL) {
209 // end of stream reached
210 break;
211 }
212 // Skip . and ..
213 if (!strcmp(dir_entry_p->d_name, ".") ||
214 !strcmp(dir_entry_p->d_name, ".."))
215 continue;
216 TEST_AND_RETURN_FALSE(RecursiveUnlinkDir(path + "/" +
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700217 dir_entry_p->d_name));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000218 }
219 TEST_AND_RETURN_FALSE(err == 0);
220 }
221 // unlink dir
222 TEST_AND_RETURN_FALSE_ERRNO((rmdir(path.c_str()) == 0) || (errno == ENOENT));
223 return true;
224}
225
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700226string RootDevice(const string& partition_device) {
Darin Petkovf74eb652010-08-04 12:08:38 -0700227 FilePath device_path(partition_device);
228 if (device_path.DirName().value() != "/dev") {
229 return "";
230 }
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700231 string::const_iterator it = --partition_device.end();
232 for (; it >= partition_device.begin(); --it) {
233 if (!isdigit(*it))
234 break;
235 }
236 // Some devices contain a p before the partitions. For example:
237 // /dev/mmc0p4 should be shortened to /dev/mmc0.
238 if (*it == 'p')
239 --it;
240 return string(partition_device.begin(), it + 1);
241}
242
243string PartitionNumber(const string& partition_device) {
244 CHECK(!partition_device.empty());
245 string::const_iterator it = --partition_device.end();
246 for (; it >= partition_device.begin(); --it) {
247 if (!isdigit(*it))
248 break;
249 }
250 return string(it + 1, partition_device.end());
251}
252
Darin Petkovf74eb652010-08-04 12:08:38 -0700253string SysfsBlockDevice(const string& device) {
254 FilePath device_path(device);
255 if (device_path.DirName().value() != "/dev") {
256 return "";
257 }
258 return FilePath("/sys/block").Append(device_path.BaseName()).value();
259}
260
261bool IsRemovableDevice(const std::string& device) {
262 string sysfs_block = SysfsBlockDevice(device);
263 string removable;
264 if (sysfs_block.empty() ||
265 !file_util::ReadFileToString(FilePath(sysfs_block).Append("removable"),
266 &removable)) {
267 return false;
268 }
269 TrimWhitespaceASCII(removable, TRIM_ALL, &removable);
270 return removable == "1";
271}
272
adlr@google.com3defe6a2009-12-04 20:57:17 +0000273std::string ErrnoNumberAsString(int err) {
274 char buf[100];
275 buf[0] = '\0';
276 return strerror_r(err, buf, sizeof(buf));
277}
278
279std::string NormalizePath(const std::string& path, bool strip_trailing_slash) {
280 string ret;
281 bool last_insert_was_slash = false;
282 for (string::const_iterator it = path.begin(); it != path.end(); ++it) {
283 if (*it == '/') {
284 if (last_insert_was_slash)
285 continue;
286 last_insert_was_slash = true;
287 } else {
288 last_insert_was_slash = false;
289 }
290 ret.push_back(*it);
291 }
292 if (strip_trailing_slash && last_insert_was_slash) {
293 string::size_type last_non_slash = ret.find_last_not_of('/');
294 if (last_non_slash != string::npos) {
295 ret.resize(last_non_slash + 1);
296 } else {
297 ret = "";
298 }
299 }
300 return ret;
301}
302
303bool FileExists(const char* path) {
304 struct stat stbuf;
305 return 0 == lstat(path, &stbuf);
306}
307
308std::string TempFilename(string path) {
309 static const string suffix("XXXXXX");
310 CHECK(StringHasSuffix(path, suffix));
311 do {
312 string new_suffix;
313 for (unsigned int i = 0; i < suffix.size(); i++) {
314 int r = rand() % (26 * 2 + 10); // [a-zA-Z0-9]
315 if (r < 26)
316 new_suffix.append(1, 'a' + r);
317 else if (r < (26 * 2))
318 new_suffix.append(1, 'A' + r - 26);
319 else
320 new_suffix.append(1, '0' + r - (26 * 2));
321 }
322 CHECK_EQ(new_suffix.size(), suffix.size());
323 path.resize(path.size() - new_suffix.size());
324 path.append(new_suffix);
325 } while (FileExists(path.c_str()));
326 return path;
327}
328
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700329bool MakeTempFile(const std::string& filename_template,
330 std::string* filename,
331 int* fd) {
332 DCHECK(filename || fd);
333 vector<char> buf(filename_template.size() + 1);
334 memcpy(&buf[0], filename_template.data(), filename_template.size());
335 buf[filename_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700336
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700337 int mkstemp_fd = mkstemp(&buf[0]);
338 TEST_AND_RETURN_FALSE_ERRNO(mkstemp_fd >= 0);
339 if (filename) {
340 *filename = &buf[0];
341 }
342 if (fd) {
343 *fd = mkstemp_fd;
344 } else {
345 close(mkstemp_fd);
346 }
347 return true;
348}
349
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700350bool MakeTempDirectory(const std::string& dirname_template,
351 std::string* dirname) {
352 DCHECK(dirname);
353 vector<char> buf(dirname_template.size() + 1);
354 memcpy(&buf[0], dirname_template.data(), dirname_template.size());
355 buf[dirname_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700356
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700357 char* return_code = mkdtemp(&buf[0]);
358 TEST_AND_RETURN_FALSE_ERRNO(return_code != NULL);
359 *dirname = &buf[0];
360 return true;
361}
362
adlr@google.com3defe6a2009-12-04 20:57:17 +0000363bool StringHasSuffix(const std::string& str, const std::string& suffix) {
364 if (suffix.size() > str.size())
365 return false;
366 return 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
367}
368
369bool StringHasPrefix(const std::string& str, const std::string& prefix) {
370 if (prefix.size() > str.size())
371 return false;
372 return 0 == str.compare(0, prefix.size(), prefix);
373}
374
Will Drewry8f71da82010-08-30 14:07:11 -0500375const std::string BootDevice() {
376 char boot_path[PATH_MAX];
377 // Resolve the boot device path fully, including dereferencing
378 // through dm-verity.
379 int ret = rootdev(boot_path, sizeof(boot_path), true, false);
380
381 if (ret < 0) {
382 LOG(ERROR) << "rootdev failed to find the root device";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000383 return "";
384 }
Will Drewry8f71da82010-08-30 14:07:11 -0500385 LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node";
386
387 // This local variable is used to construct the return string and is not
388 // passed around after use.
389 return boot_path;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000390}
391
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700392const string BootKernelDevice(const std::string& boot_device) {
393 // Currntly this assumes the last digit of the boot device is
394 // 3, 5, or 7, and changes it to 2, 4, or 6, respectively, to
395 // get the kernel device.
396 string ret = boot_device;
397 if (ret.empty())
398 return ret;
399 char last_char = ret[ret.size() - 1];
400 if (last_char == '3' || last_char == '5' || last_char == '7') {
401 ret[ret.size() - 1] = last_char - 1;
402 return ret;
403 }
404 return "";
405}
406
adlr@google.com3defe6a2009-12-04 20:57:17 +0000407bool MountFilesystem(const string& device,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700408 const string& mountpoint,
409 unsigned long mountflags) {
410 int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", mountflags, NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000411 if (rc < 0) {
412 string msg = ErrnoNumberAsString(errno);
413 LOG(ERROR) << "Unable to mount destination device: " << msg << ". "
414 << device << " on " << mountpoint;
415 return false;
416 }
417 return true;
418}
419
420bool UnmountFilesystem(const string& mountpoint) {
421 TEST_AND_RETURN_FALSE_ERRNO(umount(mountpoint.c_str()) == 0);
422 return true;
423}
424
Darin Petkovd3f8c892010-10-12 21:38:45 -0700425bool GetFilesystemSize(const std::string& device,
426 int* out_block_count,
427 int* out_block_size) {
428 int fd = HANDLE_EINTR(open(device.c_str(), O_RDONLY));
429 TEST_AND_RETURN_FALSE(fd >= 0);
430 ScopedFdCloser fd_closer(&fd);
431 return GetFilesystemSizeFromFD(fd, out_block_count, out_block_size);
432}
433
434bool GetFilesystemSizeFromFD(int fd,
435 int* out_block_count,
436 int* out_block_size) {
437 TEST_AND_RETURN_FALSE(fd >= 0);
438
439 // Determine the ext3 filesystem size by directly reading the block count and
440 // block size information from the superblock. See include/linux/ext3_fs.h for
441 // more details on the structure.
442 ssize_t kBufferSize = 16 * sizeof(uint32_t);
443 char buffer[kBufferSize];
444 const int kSuperblockOffset = 1024;
445 if (HANDLE_EINTR(pread(fd, buffer, kBufferSize, kSuperblockOffset)) !=
446 kBufferSize) {
447 PLOG(ERROR) << "Unable to determine file system size:";
448 return false;
449 }
450 uint32_t block_count; // ext3_fs.h: ext3_super_block.s_blocks_count
451 uint32_t log_block_size; // ext3_fs.h: ext3_super_block.s_log_block_size
452 uint16_t magic; // ext3_fs.h: ext3_super_block.s_magic
453 memcpy(&block_count, &buffer[1 * sizeof(int32_t)], sizeof(block_count));
454 memcpy(&log_block_size, &buffer[6 * sizeof(int32_t)], sizeof(log_block_size));
455 memcpy(&magic, &buffer[14 * sizeof(int32_t)], sizeof(magic));
456 block_count = le32toh(block_count);
457 const int kExt3MinBlockLogSize = 10; // ext3_fs.h: EXT3_MIN_BLOCK_LOG_SIZE
458 log_block_size = le32toh(log_block_size) + kExt3MinBlockLogSize;
459 magic = le16toh(magic);
460
461 // Sanity check the parameters.
462 const uint16_t kExt3SuperMagic = 0xef53; // ext3_fs.h: EXT3_SUPER_MAGIC
463 TEST_AND_RETURN_FALSE(magic == kExt3SuperMagic);
464 const int kExt3MinBlockSize = 1024; // ext3_fs.h: EXT3_MIN_BLOCK_SIZE
465 const int kExt3MaxBlockSize = 4096; // ext3_fs.h: EXT3_MAX_BLOCK_SIZE
466 int block_size = 1 << log_block_size;
467 TEST_AND_RETURN_FALSE(block_size >= kExt3MinBlockSize &&
468 block_size <= kExt3MaxBlockSize);
469 TEST_AND_RETURN_FALSE(block_count > 0);
470
471 if (out_block_count) {
472 *out_block_count = block_count;
473 }
474 if (out_block_size) {
475 *out_block_size = block_size;
476 }
477 return true;
478}
479
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700480bool GetBootloader(BootLoader* out_bootloader) {
481 // For now, hardcode to syslinux.
482 *out_bootloader = BootLoader_SYSLINUX;
483 return true;
484}
485
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700486const char* GetGErrorMessage(const GError* error) {
487 if (!error)
488 return "Unknown error.";
489 return error->message;
490}
491
Darin Petkov296889c2010-07-23 16:20:54 -0700492bool Reboot() {
493 vector<string> command;
494 command.push_back("/sbin/shutdown");
495 command.push_back("-r");
496 command.push_back("now");
497 int rc = 0;
498 Subprocess::SynchronousExec(command, &rc);
499 TEST_AND_RETURN_FALSE(rc == 0);
500 return true;
501}
502
Darin Petkovc6c135c2010-08-11 13:36:18 -0700503bool SetProcessPriority(ProcessPriority priority) {
504 int prio = static_cast<int>(priority);
505 LOG(INFO) << "Setting process priority to " << prio;
506 TEST_AND_RETURN_FALSE(setpriority(PRIO_PROCESS, 0, prio) == 0);
507 return true;
508}
509
510int ComparePriorities(ProcessPriority priority_lhs,
511 ProcessPriority priority_rhs) {
512 return static_cast<int>(priority_rhs) - static_cast<int>(priority_lhs);
513}
514
Darin Petkov5c0a8af2010-08-24 13:39:13 -0700515int FuzzInt(int value, unsigned int range) {
516 int min = value - range / 2;
517 int max = value + range - range / 2;
518 return base::RandInt(min, max);
519}
520
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800521const char* const kStatefulPartition = "/mnt/stateful_partition";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000522
523} // namespace utils
524
525} // namespace chromeos_update_engine