diff options
-rw-r--r-- | postinstall_runner_action.cc | 15 | ||||
-rw-r--r-- | utils.cc | 82 | ||||
-rw-r--r-- | utils.h | 5 | ||||
-rw-r--r-- | utils_unittest.cc | 42 |
4 files changed, 144 insertions, 0 deletions
diff --git a/postinstall_runner_action.cc b/postinstall_runner_action.cc index 6be13e15..f8bcc361 100644 --- a/postinstall_runner_action.cc +++ b/postinstall_runner_action.cc @@ -18,7 +18,13 @@ using std::string; using std::vector; namespace { +// The absolute path to the post install command. const char kPostinstallScript[] = "/postinst"; + +// Path to the binary file used by kPostinstallScript. Used to get and log the +// file format of the binary to debug issues when the ELF format on the update +// doesn't match the one on the current system. This path is not executed. +const char kDebugPostinstallBinaryPath[] = "/usr/bin/cros_installer"; } void PostinstallRunnerAction::PerformAction() { @@ -66,6 +72,15 @@ void PostinstallRunnerAction::PerformAction() { } } + // Logs the file format of the postinstall script we are about to run. This + // will help debug when the postinstall script doesn't match the architecture + // of our build. + LOG(INFO) << "Format file for new " << kPostinstallScript << " is: " + << utils::GetFileFormat(temp_rootfs_dir_ + kPostinstallScript); + LOG(INFO) << "Format file for new " << kDebugPostinstallBinaryPath << " is: " + << utils::GetFileFormat( + temp_rootfs_dir_ + kDebugPostinstallBinaryPath); + // Runs the postinstall script asynchronously to free up the main loop while // it's running. vector<string> command; @@ -56,6 +56,11 @@ namespace { // one second. const int kUnmountMaxNumOfRetries = 5; const int kUnmountRetryIntervalInMicroseconds = 200 * 1000; // 200 ms + +// Number of bytes to read from a file to attempt to detect its contents. Used +// in GetFileFormat. +const int kGetFileFormatMaxHeaderSize = 32; + } // namespace namespace utils { @@ -665,6 +670,83 @@ bool GetFilesystemSizeFromFD(int fd, return true; } +// Tries to parse the header of an ELF file to obtain a human-readable +// description of it on the |output| string. +static bool GetFileFormatELF(const char* buffer, size_t size, string* output) { + // 0x00: EI_MAG - ELF magic header, 4 bytes. + if (size < 4 || memcmp(buffer, "\x7F""ELF", 4) != 0) + return false; + *output = "ELF"; + + // 0x04: EI_CLASS, 1 byte. + if (size < 0x04 + 1) + return true; + switch (buffer[4]) { + case 1: + *output += " 32-bit"; + break; + case 2: + *output += " 64-bit"; + break; + default: + *output += " ?-bit"; + } + + // 0x05: EI_DATA, endianness, 1 byte. + if (size < 0x05 + 1) + return true; + char ei_data = buffer[5]; + switch (ei_data) { + case 1: + *output += " little-endian"; + break; + case 2: + *output += " big-endian"; + break; + default: + *output += " ?-endian"; + // Don't parse anything after the 0x10 offset if endianness is unknown. + return true; + } + + // 0x12: e_machine, 2 byte endianness based on ei_data + if (size < 0x12 + 2) + return true; + uint16 e_machine = *reinterpret_cast<const uint16*>(buffer+0x12); + // Fix endianess regardless of the host endianess. + if (ei_data == 1) + e_machine = le16toh(e_machine); + else + e_machine = be16toh(e_machine); + + switch (e_machine) { + case 0x03: + *output += " x86"; + break; + case 0x28: + *output += " arm"; + break; + case 0x3E: + *output += " x86-64"; + break; + default: + *output += " unknown-arch"; + } + return true; +} + +string GetFileFormat(const string& path) { + vector<char> buffer; + if (!ReadFileChunkAndAppend(path, 0, kGetFileFormatMaxHeaderSize, &buffer)) + return "File not found."; + + string result; + if (GetFileFormatELF(buffer.data(), buffer.size(), &result)) + return result; + + return "data"; +} + bool GetBootloader(BootLoader* out_bootloader) { // For now, hardcode to syslinux. *out_bootloader = BootLoader_SYSLINUX; @@ -193,6 +193,11 @@ bool GetFilesystemSizeFromFD(int fd, int* out_block_count, int* out_block_size); + +// Returns a human-readable string with the file format based on magic constants +// on the header of the file. +std::string GetFileFormat(const std::string& path); + // Returns the string representation of the given UTC time. // such as "11/14/2011 14:05:30 GMT". std::string ToString(const base::Time utc_time); diff --git a/utils_unittest.cc b/utils_unittest.cc index d75a6b04..ef771b9f 100644 --- a/utils_unittest.cc +++ b/utils_unittest.cc @@ -327,6 +327,48 @@ TEST(UtilsTest, GetInstallDevTest) { } namespace { +void GetFileFormatTester(const string& expected, + const vector<uint8>& contents) { + ScopedTempFile file; + ASSERT_TRUE(utils::WriteFile(file.GetPath().c_str(), + reinterpret_cast<const char*>(contents.data()), + contents.size())); + EXPECT_EQ(expected, utils::GetFileFormat(file.GetPath())); +} +} + +TEST(UtilsTest, GetFileFormatTest) { + EXPECT_EQ("File not found.", utils::GetFileFormat("/path/to/nowhere")); + GetFileFormatTester("data", vector<uint8>{1, 2, 3, 4, 5, 6, 7, 8}); + GetFileFormatTester("ELF", vector<uint8>{0x7f, 0x45, 0x4c, 0x46}); + + // Real tests from cros_installer on different boards. + // ELF 32-bit LSB executable, Intel 80386 + GetFileFormatTester( + "ELF 32-bit little-endian x86", + vector<uint8>{0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x90, 0x83, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00}); + + // ELF 32-bit LSB executable, ARM + GetFileFormatTester( + "ELF 32-bit little-endian arm", + vector<uint8>{0x7f, 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x28, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x85, 0x8b, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00}); + + // ELF 64-bit LSB executable, x86-64 + GetFileFormatTester( + "ELF 64-bit little-endian x86-64", + vector<uint8>{0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, + 0xb0, 0x04, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00}); +} + +namespace { gboolean TerminateScheduleCrashReporterUploadTest(void* arg) { GMainLoop* loop = reinterpret_cast<GMainLoop*>(arg); g_main_loop_quit(loop); |