blob: 1d1cb7d51a5a63cf32bd1b1e4db2b02fa83c7bfe [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
Will Drewry8f71da82010-08-30 14:07:11 -050021#include <base/file_path.h>
22#include <base/file_util.h>
23#include <base/rand_util.h>
24#include <base/string_util.h>
25#include <base/logging.h>
26#include <rootdev/rootdev.h>
27
Andrew de los Reyes970bb282009-12-09 16:34:04 -080028#include "update_engine/file_writer.h"
Darin Petkov33d30642010-08-04 10:18:57 -070029#include "update_engine/omaha_request_params.h"
Darin Petkov296889c2010-07-23 16:20:54 -070030#include "update_engine/subprocess.h"
adlr@google.com3defe6a2009-12-04 20:57:17 +000031
32using std::min;
33using std::string;
34using std::vector;
35
36namespace chromeos_update_engine {
37
38namespace utils {
39
Darin Petkov33d30642010-08-04 10:18:57 -070040bool IsOfficialBuild() {
41 OmahaRequestDeviceParams params;
42 if (!params.Init("", "")) {
43 return true;
44 }
45 return params.app_track != "buildbot-build" &&
46 params.app_track != "developer-build";
47}
48
Andrew de los Reyes970bb282009-12-09 16:34:04 -080049bool WriteFile(const char* path, const char* data, int data_len) {
50 DirectFileWriter writer;
51 TEST_AND_RETURN_FALSE_ERRNO(0 == writer.Open(path,
52 O_WRONLY | O_CREAT | O_TRUNC,
53 0666));
54 ScopedFileWriterCloser closer(&writer);
55 TEST_AND_RETURN_FALSE_ERRNO(data_len == writer.Write(data, data_len));
56 return true;
57}
58
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070059bool WriteAll(int fd, const void* buf, size_t count) {
Andrew de los Reyesb10320d2010-03-31 16:44:44 -070060 const char* c_buf = static_cast<const char*>(buf);
61 ssize_t bytes_written = 0;
62 while (bytes_written < static_cast<ssize_t>(count)) {
63 ssize_t rc = write(fd, c_buf + bytes_written, count - bytes_written);
64 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
65 bytes_written += rc;
66 }
67 return true;
68}
69
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070070bool PWriteAll(int fd, const void* buf, size_t count, off_t offset) {
71 const char* c_buf = static_cast<const char*>(buf);
72 ssize_t bytes_written = 0;
73 while (bytes_written < static_cast<ssize_t>(count)) {
74 ssize_t rc = pwrite(fd, c_buf + bytes_written, count - bytes_written,
75 offset + bytes_written);
76 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
77 bytes_written += rc;
78 }
79 return true;
80}
81
82bool PReadAll(int fd, void* buf, size_t count, off_t offset,
83 ssize_t* out_bytes_read) {
84 char* c_buf = static_cast<char*>(buf);
85 ssize_t bytes_read = 0;
86 while (bytes_read < static_cast<ssize_t>(count)) {
87 ssize_t rc = pread(fd, c_buf + bytes_read, count - bytes_read,
88 offset + bytes_read);
89 TEST_AND_RETURN_FALSE_ERRNO(rc >= 0);
90 if (rc == 0) {
91 break;
92 }
93 bytes_read += rc;
94 }
95 *out_bytes_read = bytes_read;
96 return true;
Darin Petkov296889c2010-07-23 16:20:54 -070097
Andrew de los Reyes09e56d62010-04-23 13:45:53 -070098}
99
adlr@google.com3defe6a2009-12-04 20:57:17 +0000100bool ReadFile(const std::string& path, std::vector<char>* out) {
101 CHECK(out);
102 FILE* fp = fopen(path.c_str(), "r");
103 if (!fp)
104 return false;
105 const size_t kChunkSize = 1024;
106 size_t read_size;
107 do {
108 char buf[kChunkSize];
109 read_size = fread(buf, 1, kChunkSize, fp);
110 if (read_size == 0)
111 break;
112 out->insert(out->end(), buf, buf + read_size);
113 } while (read_size == kChunkSize);
114 bool success = !ferror(fp);
115 TEST_AND_RETURN_FALSE_ERRNO(fclose(fp) == 0);
116 return success;
117}
118
119bool ReadFileToString(const std::string& path, std::string* out) {
120 vector<char> data;
121 bool success = ReadFile(path, &data);
122 if (!success) {
123 return false;
124 }
125 (*out) = string(&data[0], data.size());
126 return true;
127}
128
Andrew de los Reyesf4c7ef12010-04-30 10:37:00 -0700129off_t FileSize(const string& path) {
130 struct stat stbuf;
131 int rc = stat(path.c_str(), &stbuf);
132 CHECK_EQ(rc, 0);
133 if (rc < 0)
134 return rc;
135 return stbuf.st_size;
136}
137
adlr@google.com3defe6a2009-12-04 20:57:17 +0000138void HexDumpArray(const unsigned char* const arr, const size_t length) {
139 const unsigned char* const char_arr =
140 reinterpret_cast<const unsigned char* const>(arr);
141 LOG(INFO) << "Logging array of length: " << length;
142 const unsigned int bytes_per_line = 16;
Andrew de los Reyes08c4e272010-04-15 14:02:17 -0700143 for (uint32_t i = 0; i < length; i += bytes_per_line) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000144 const unsigned int bytes_remaining = length - i;
145 const unsigned int bytes_per_this_line = min(bytes_per_line,
146 bytes_remaining);
147 char header[100];
148 int r = snprintf(header, sizeof(header), "0x%08x : ", i);
149 TEST_AND_RETURN(r == 13);
150 string line = header;
151 for (unsigned int j = 0; j < bytes_per_this_line; j++) {
152 char buf[20];
153 unsigned char c = char_arr[i + j];
154 r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c));
155 TEST_AND_RETURN(r == 3);
156 line += buf;
157 }
158 LOG(INFO) << line;
159 }
160}
161
162namespace {
163class ScopedDirCloser {
164 public:
165 explicit ScopedDirCloser(DIR** dir) : dir_(dir) {}
166 ~ScopedDirCloser() {
167 if (dir_ && *dir_) {
168 int r = closedir(*dir_);
169 TEST_AND_RETURN_ERRNO(r == 0);
170 *dir_ = NULL;
171 dir_ = NULL;
172 }
173 }
174 private:
175 DIR** dir_;
176};
177} // namespace {}
178
179bool RecursiveUnlinkDir(const std::string& path) {
180 struct stat stbuf;
181 int r = lstat(path.c_str(), &stbuf);
182 TEST_AND_RETURN_FALSE_ERRNO((r == 0) || (errno == ENOENT));
183 if ((r < 0) && (errno == ENOENT))
184 // path request is missing. that's fine.
185 return true;
186 if (!S_ISDIR(stbuf.st_mode)) {
187 TEST_AND_RETURN_FALSE_ERRNO((unlink(path.c_str()) == 0) ||
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700188 (errno == ENOENT));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000189 // success or path disappeared before we could unlink.
190 return true;
191 }
192 {
193 // We have a dir, unlink all children, then delete dir
194 DIR *dir = opendir(path.c_str());
195 TEST_AND_RETURN_FALSE_ERRNO(dir);
196 ScopedDirCloser dir_closer(&dir);
197 struct dirent dir_entry;
198 struct dirent *dir_entry_p;
199 int err = 0;
200 while ((err = readdir_r(dir, &dir_entry, &dir_entry_p)) == 0) {
201 if (dir_entry_p == NULL) {
202 // end of stream reached
203 break;
204 }
205 // Skip . and ..
206 if (!strcmp(dir_entry_p->d_name, ".") ||
207 !strcmp(dir_entry_p->d_name, ".."))
208 continue;
209 TEST_AND_RETURN_FALSE(RecursiveUnlinkDir(path + "/" +
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700210 dir_entry_p->d_name));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000211 }
212 TEST_AND_RETURN_FALSE(err == 0);
213 }
214 // unlink dir
215 TEST_AND_RETURN_FALSE_ERRNO((rmdir(path.c_str()) == 0) || (errno == ENOENT));
216 return true;
217}
218
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700219string RootDevice(const string& partition_device) {
Darin Petkovf74eb652010-08-04 12:08:38 -0700220 FilePath device_path(partition_device);
221 if (device_path.DirName().value() != "/dev") {
222 return "";
223 }
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700224 string::const_iterator it = --partition_device.end();
225 for (; it >= partition_device.begin(); --it) {
226 if (!isdigit(*it))
227 break;
228 }
229 // Some devices contain a p before the partitions. For example:
230 // /dev/mmc0p4 should be shortened to /dev/mmc0.
231 if (*it == 'p')
232 --it;
233 return string(partition_device.begin(), it + 1);
234}
235
236string PartitionNumber(const string& partition_device) {
237 CHECK(!partition_device.empty());
238 string::const_iterator it = --partition_device.end();
239 for (; it >= partition_device.begin(); --it) {
240 if (!isdigit(*it))
241 break;
242 }
243 return string(it + 1, partition_device.end());
244}
245
Darin Petkovf74eb652010-08-04 12:08:38 -0700246string SysfsBlockDevice(const string& device) {
247 FilePath device_path(device);
248 if (device_path.DirName().value() != "/dev") {
249 return "";
250 }
251 return FilePath("/sys/block").Append(device_path.BaseName()).value();
252}
253
254bool IsRemovableDevice(const std::string& device) {
255 string sysfs_block = SysfsBlockDevice(device);
256 string removable;
257 if (sysfs_block.empty() ||
258 !file_util::ReadFileToString(FilePath(sysfs_block).Append("removable"),
259 &removable)) {
260 return false;
261 }
262 TrimWhitespaceASCII(removable, TRIM_ALL, &removable);
263 return removable == "1";
264}
265
adlr@google.com3defe6a2009-12-04 20:57:17 +0000266std::string ErrnoNumberAsString(int err) {
267 char buf[100];
268 buf[0] = '\0';
269 return strerror_r(err, buf, sizeof(buf));
270}
271
272std::string NormalizePath(const std::string& path, bool strip_trailing_slash) {
273 string ret;
274 bool last_insert_was_slash = false;
275 for (string::const_iterator it = path.begin(); it != path.end(); ++it) {
276 if (*it == '/') {
277 if (last_insert_was_slash)
278 continue;
279 last_insert_was_slash = true;
280 } else {
281 last_insert_was_slash = false;
282 }
283 ret.push_back(*it);
284 }
285 if (strip_trailing_slash && last_insert_was_slash) {
286 string::size_type last_non_slash = ret.find_last_not_of('/');
287 if (last_non_slash != string::npos) {
288 ret.resize(last_non_slash + 1);
289 } else {
290 ret = "";
291 }
292 }
293 return ret;
294}
295
296bool FileExists(const char* path) {
297 struct stat stbuf;
298 return 0 == lstat(path, &stbuf);
299}
300
301std::string TempFilename(string path) {
302 static const string suffix("XXXXXX");
303 CHECK(StringHasSuffix(path, suffix));
304 do {
305 string new_suffix;
306 for (unsigned int i = 0; i < suffix.size(); i++) {
307 int r = rand() % (26 * 2 + 10); // [a-zA-Z0-9]
308 if (r < 26)
309 new_suffix.append(1, 'a' + r);
310 else if (r < (26 * 2))
311 new_suffix.append(1, 'A' + r - 26);
312 else
313 new_suffix.append(1, '0' + r - (26 * 2));
314 }
315 CHECK_EQ(new_suffix.size(), suffix.size());
316 path.resize(path.size() - new_suffix.size());
317 path.append(new_suffix);
318 } while (FileExists(path.c_str()));
319 return path;
320}
321
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700322bool MakeTempFile(const std::string& filename_template,
323 std::string* filename,
324 int* fd) {
325 DCHECK(filename || fd);
326 vector<char> buf(filename_template.size() + 1);
327 memcpy(&buf[0], filename_template.data(), filename_template.size());
328 buf[filename_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700329
Andrew de los Reyesb10320d2010-03-31 16:44:44 -0700330 int mkstemp_fd = mkstemp(&buf[0]);
331 TEST_AND_RETURN_FALSE_ERRNO(mkstemp_fd >= 0);
332 if (filename) {
333 *filename = &buf[0];
334 }
335 if (fd) {
336 *fd = mkstemp_fd;
337 } else {
338 close(mkstemp_fd);
339 }
340 return true;
341}
342
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700343bool MakeTempDirectory(const std::string& dirname_template,
344 std::string* dirname) {
345 DCHECK(dirname);
346 vector<char> buf(dirname_template.size() + 1);
347 memcpy(&buf[0], dirname_template.data(), dirname_template.size());
348 buf[dirname_template.size()] = '\0';
Darin Petkov296889c2010-07-23 16:20:54 -0700349
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700350 char* return_code = mkdtemp(&buf[0]);
351 TEST_AND_RETURN_FALSE_ERRNO(return_code != NULL);
352 *dirname = &buf[0];
353 return true;
354}
355
adlr@google.com3defe6a2009-12-04 20:57:17 +0000356bool StringHasSuffix(const std::string& str, const std::string& suffix) {
357 if (suffix.size() > str.size())
358 return false;
359 return 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix);
360}
361
362bool StringHasPrefix(const std::string& str, const std::string& prefix) {
363 if (prefix.size() > str.size())
364 return false;
365 return 0 == str.compare(0, prefix.size(), prefix);
366}
367
Will Drewry8f71da82010-08-30 14:07:11 -0500368const std::string BootDevice() {
369 char boot_path[PATH_MAX];
370 // Resolve the boot device path fully, including dereferencing
371 // through dm-verity.
372 int ret = rootdev(boot_path, sizeof(boot_path), true, false);
373
374 if (ret < 0) {
375 LOG(ERROR) << "rootdev failed to find the root device";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000376 return "";
377 }
Will Drewry8f71da82010-08-30 14:07:11 -0500378 LOG_IF(WARNING, ret > 0) << "rootdev found a device name with no device node";
379
380 // This local variable is used to construct the return string and is not
381 // passed around after use.
382 return boot_path;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000383}
384
Andrew de los Reyesf9185172010-05-03 11:07:05 -0700385const string BootKernelDevice(const std::string& boot_device) {
386 // Currntly this assumes the last digit of the boot device is
387 // 3, 5, or 7, and changes it to 2, 4, or 6, respectively, to
388 // get the kernel device.
389 string ret = boot_device;
390 if (ret.empty())
391 return ret;
392 char last_char = ret[ret.size() - 1];
393 if (last_char == '3' || last_char == '5' || last_char == '7') {
394 ret[ret.size() - 1] = last_char - 1;
395 return ret;
396 }
397 return "";
398}
399
adlr@google.com3defe6a2009-12-04 20:57:17 +0000400bool MountFilesystem(const string& device,
Andrew de los Reyes09e56d62010-04-23 13:45:53 -0700401 const string& mountpoint,
402 unsigned long mountflags) {
403 int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", mountflags, NULL);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000404 if (rc < 0) {
405 string msg = ErrnoNumberAsString(errno);
406 LOG(ERROR) << "Unable to mount destination device: " << msg << ". "
407 << device << " on " << mountpoint;
408 return false;
409 }
410 return true;
411}
412
413bool UnmountFilesystem(const string& mountpoint) {
414 TEST_AND_RETURN_FALSE_ERRNO(umount(mountpoint.c_str()) == 0);
415 return true;
416}
417
Andrew de los Reyesf9714432010-05-04 10:21:23 -0700418bool GetBootloader(BootLoader* out_bootloader) {
419 // For now, hardcode to syslinux.
420 *out_bootloader = BootLoader_SYSLINUX;
421 return true;
422}
423
Andrew de los Reyesc7020782010-04-28 10:46:04 -0700424const char* GetGErrorMessage(const GError* error) {
425 if (!error)
426 return "Unknown error.";
427 return error->message;
428}
429
Darin Petkov296889c2010-07-23 16:20:54 -0700430bool Reboot() {
431 vector<string> command;
432 command.push_back("/sbin/shutdown");
433 command.push_back("-r");
434 command.push_back("now");
435 int rc = 0;
436 Subprocess::SynchronousExec(command, &rc);
437 TEST_AND_RETURN_FALSE(rc == 0);
438 return true;
439}
440
Darin Petkovc6c135c2010-08-11 13:36:18 -0700441bool SetProcessPriority(ProcessPriority priority) {
442 int prio = static_cast<int>(priority);
443 LOG(INFO) << "Setting process priority to " << prio;
444 TEST_AND_RETURN_FALSE(setpriority(PRIO_PROCESS, 0, prio) == 0);
445 return true;
446}
447
448int ComparePriorities(ProcessPriority priority_lhs,
449 ProcessPriority priority_rhs) {
450 return static_cast<int>(priority_rhs) - static_cast<int>(priority_lhs);
451}
452
Darin Petkov5c0a8af2010-08-24 13:39:13 -0700453int FuzzInt(int value, unsigned int range) {
454 int min = value - range / 2;
455 int max = value + range - range / 2;
456 return base::RandInt(min, max);
457}
458
Andrew de los Reyes4fe15d02009-12-10 19:01:36 -0800459const char* const kStatefulPartition = "/mnt/stateful_partition";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000460
461} // namespace utils
462
463} // namespace chromeos_update_engine