Index: src/platform/update_engine/utils.cc |
diff --git a/src/platform/update_engine/utils.cc b/src/platform/update_engine/utils.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..693062df6a4a77aa3ed2b1b88640a6fbaf89feee |
--- /dev/null |
+++ b/src/platform/update_engine/utils.cc |
@@ -0,0 +1,253 @@ |
+// Copyright (c) 2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "update_engine/utils.h" |
+#include <sys/mount.h> |
+#include <sys/stat.h> |
+#include <sys/types.h> |
+#include <dirent.h> |
+#include <errno.h> |
+#include <stdio.h> |
+#include <stdlib.h> |
+#include <string.h> |
+#include <unistd.h> |
+#include <algorithm> |
+#include "chromeos/obsolete_logging.h" |
+ |
+using std::min; |
+using std::string; |
+using std::vector; |
+ |
+namespace chromeos_update_engine { |
+ |
+namespace utils { |
+ |
+bool ReadFile(const std::string& path, std::vector<char>* out) { |
+ CHECK(out); |
+ FILE* fp = fopen(path.c_str(), "r"); |
+ if (!fp) |
+ return false; |
+ const size_t kChunkSize = 1024; |
+ size_t read_size; |
+ do { |
+ char buf[kChunkSize]; |
+ read_size = fread(buf, 1, kChunkSize, fp); |
+ if (read_size == 0) |
+ break; |
+ out->insert(out->end(), buf, buf + read_size); |
+ } while (read_size == kChunkSize); |
+ bool success = !ferror(fp); |
+ TEST_AND_RETURN_FALSE_ERRNO(fclose(fp) == 0); |
+ return success; |
+} |
+ |
+bool ReadFileToString(const std::string& path, std::string* out) { |
+ vector<char> data; |
+ bool success = ReadFile(path, &data); |
+ if (!success) { |
+ return false; |
+ } |
+ (*out) = string(&data[0], data.size()); |
+ return true; |
+} |
+ |
+void HexDumpArray(const unsigned char* const arr, const size_t length) { |
+ const unsigned char* const char_arr = |
+ reinterpret_cast<const unsigned char* const>(arr); |
+ LOG(INFO) << "Logging array of length: " << length; |
+ const unsigned int bytes_per_line = 16; |
+ for (size_t i = 0; i < length; i += bytes_per_line) { |
+ const unsigned int bytes_remaining = length - i; |
+ const unsigned int bytes_per_this_line = min(bytes_per_line, |
+ bytes_remaining); |
+ char header[100]; |
+ int r = snprintf(header, sizeof(header), "0x%08x : ", i); |
+ TEST_AND_RETURN(r == 13); |
+ string line = header; |
+ for (unsigned int j = 0; j < bytes_per_this_line; j++) { |
+ char buf[20]; |
+ unsigned char c = char_arr[i + j]; |
+ r = snprintf(buf, sizeof(buf), "%02x ", static_cast<unsigned int>(c)); |
+ TEST_AND_RETURN(r == 3); |
+ line += buf; |
+ } |
+ LOG(INFO) << line; |
+ } |
+} |
+ |
+namespace { |
+class ScopedDirCloser { |
+ public: |
+ explicit ScopedDirCloser(DIR** dir) : dir_(dir) {} |
+ ~ScopedDirCloser() { |
+ if (dir_ && *dir_) { |
+ int r = closedir(*dir_); |
+ TEST_AND_RETURN_ERRNO(r == 0); |
+ *dir_ = NULL; |
+ dir_ = NULL; |
+ } |
+ } |
+ private: |
+ DIR** dir_; |
+}; |
+} // namespace {} |
+ |
+bool RecursiveUnlinkDir(const std::string& path) { |
+ struct stat stbuf; |
+ int r = lstat(path.c_str(), &stbuf); |
+ TEST_AND_RETURN_FALSE_ERRNO((r == 0) || (errno == ENOENT)); |
+ if ((r < 0) && (errno == ENOENT)) |
+ // path request is missing. that's fine. |
+ return true; |
+ if (!S_ISDIR(stbuf.st_mode)) { |
+ TEST_AND_RETURN_FALSE_ERRNO((unlink(path.c_str()) == 0) || |
+ (errno == ENOENT)); |
+ // success or path disappeared before we could unlink. |
+ return true; |
+ } |
+ { |
+ // We have a dir, unlink all children, then delete dir |
+ DIR *dir = opendir(path.c_str()); |
+ TEST_AND_RETURN_FALSE_ERRNO(dir); |
+ ScopedDirCloser dir_closer(&dir); |
+ struct dirent dir_entry; |
+ struct dirent *dir_entry_p; |
+ int err = 0; |
+ while ((err = readdir_r(dir, &dir_entry, &dir_entry_p)) == 0) { |
+ if (dir_entry_p == NULL) { |
+ // end of stream reached |
+ break; |
+ } |
+ // Skip . and .. |
+ if (!strcmp(dir_entry_p->d_name, ".") || |
+ !strcmp(dir_entry_p->d_name, "..")) |
+ continue; |
+ TEST_AND_RETURN_FALSE(RecursiveUnlinkDir(path + "/" + |
+ dir_entry_p->d_name)); |
+ } |
+ TEST_AND_RETURN_FALSE(err == 0); |
+ } |
+ // unlink dir |
+ TEST_AND_RETURN_FALSE_ERRNO((rmdir(path.c_str()) == 0) || (errno == ENOENT)); |
+ return true; |
+} |
+ |
+std::string ErrnoNumberAsString(int err) { |
+ char buf[100]; |
+ buf[0] = '\0'; |
+ return strerror_r(err, buf, sizeof(buf)); |
+} |
+ |
+std::string NormalizePath(const std::string& path, bool strip_trailing_slash) { |
+ string ret; |
+ bool last_insert_was_slash = false; |
+ for (string::const_iterator it = path.begin(); it != path.end(); ++it) { |
+ if (*it == '/') { |
+ if (last_insert_was_slash) |
+ continue; |
+ last_insert_was_slash = true; |
+ } else { |
+ last_insert_was_slash = false; |
+ } |
+ ret.push_back(*it); |
+ } |
+ if (strip_trailing_slash && last_insert_was_slash) { |
+ string::size_type last_non_slash = ret.find_last_not_of('/'); |
+ if (last_non_slash != string::npos) { |
+ ret.resize(last_non_slash + 1); |
+ } else { |
+ ret = ""; |
+ } |
+ } |
+ return ret; |
+} |
+ |
+bool FileExists(const char* path) { |
+ struct stat stbuf; |
+ return 0 == lstat(path, &stbuf); |
+} |
+ |
+std::string TempFilename(string path) { |
+ static const string suffix("XXXXXX"); |
+ CHECK(StringHasSuffix(path, suffix)); |
+ do { |
+ string new_suffix; |
+ for (unsigned int i = 0; i < suffix.size(); i++) { |
+ int r = rand() % (26 * 2 + 10); // [a-zA-Z0-9] |
+ if (r < 26) |
+ new_suffix.append(1, 'a' + r); |
+ else if (r < (26 * 2)) |
+ new_suffix.append(1, 'A' + r - 26); |
+ else |
+ new_suffix.append(1, '0' + r - (26 * 2)); |
+ } |
+ CHECK_EQ(new_suffix.size(), suffix.size()); |
+ path.resize(path.size() - new_suffix.size()); |
+ path.append(new_suffix); |
+ } while (FileExists(path.c_str())); |
+ return path; |
+} |
+ |
+bool StringHasSuffix(const std::string& str, const std::string& suffix) { |
+ if (suffix.size() > str.size()) |
+ return false; |
+ return 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); |
+} |
+ |
+bool StringHasPrefix(const std::string& str, const std::string& prefix) { |
+ if (prefix.size() > str.size()) |
+ return false; |
+ return 0 == str.compare(0, prefix.size(), prefix); |
+} |
+ |
+const std::string BootDevice() { |
+ string proc_cmdline; |
+ if (!ReadFileToString("/proc/cmdline", &proc_cmdline)) |
+ return ""; |
+ // look for "root=" in the command line |
+ string::size_type pos = 0; |
+ if (!StringHasPrefix(proc_cmdline, "root=")) { |
+ pos = proc_cmdline.find(" root=") + 1; |
+ } |
+ if (pos == string::npos) { |
+ // can't find root= |
+ return ""; |
+ } |
+ // at this point, pos is the point in the string where "root=" starts |
+ string ret; |
+ pos += strlen("root="); // advance to the device name itself |
+ while (pos < proc_cmdline.size()) { |
+ char c = proc_cmdline[pos]; |
+ if (c == ' ') |
+ break; |
+ ret += c; |
+ pos++; |
+ } |
+ return ret; |
+ // TODO(adlr): use findfs to figure out UUID= or LABEL= filesystems |
+} |
+ |
+bool MountFilesystem(const string& device, |
+ const string& mountpoint) { |
+ int rc = mount(device.c_str(), mountpoint.c_str(), "ext3", 0, NULL); |
+ if (rc < 0) { |
+ string msg = ErrnoNumberAsString(errno); |
+ LOG(ERROR) << "Unable to mount destination device: " << msg << ". " |
+ << device << " on " << mountpoint; |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+bool UnmountFilesystem(const string& mountpoint) { |
+ TEST_AND_RETURN_FALSE_ERRNO(umount(mountpoint.c_str()) == 0); |
+ return true; |
+} |
+ |
+const string kStatefulPartition = "/mnt/stateful_partition"; |
+ |
+} // namespace utils |
+ |
+} // namespace chromeos_update_engine |
+ |