// 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 #include #include #include #include #include #include #include #include #include #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* 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 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(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(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