Index: src/platform/update_engine/set_bootable_flag_action.cc |
diff --git a/src/platform/update_engine/set_bootable_flag_action.cc b/src/platform/update_engine/set_bootable_flag_action.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..8075b2cb992ee346def8654171865c4ad85bbdeb |
--- /dev/null |
+++ b/src/platform/update_engine/set_bootable_flag_action.cc |
@@ -0,0 +1,117 @@ |
+// 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/set_bootable_flag_action.h" |
+#include <sys/stat.h> |
+#include <sys/types.h> |
+#include <errno.h> |
+#include <fcntl.h> |
+#include <string> |
+#include "update_engine/utils.h" |
+ |
+using std::string; |
+ |
+namespace chromeos_update_engine { |
+ |
+void SetBootableFlagAction::PerformAction() { |
+ ScopedActionCompleter completer(processor_, this); |
+ |
+ if (!HasInputObject() || GetInputObject().empty()) { |
+ LOG(ERROR) << "SetBootableFlagAction: No input object."; |
+ return; |
+ } |
+ string device = GetInputObject(); |
+ |
+ if (device.size() < 2) { |
+ LOG(ERROR) << "Device name too short: " << device; |
+ return; |
+ } |
+ |
+ // Make sure device is valid. |
+ const char last_char = device[device.size() - 1]; |
+ if ((last_char < '1') || (last_char > '4')) { |
+ LOG(ERROR) << "Bad device:" << device; |
+ return; |
+ } |
+ |
+ // We were passed the partition_number'th partition; indexed from 1, not 0 |
+ int partition_number = last_char - '0'; |
+ |
+ const char second_to_last_char = device[device.size() - 2]; |
+ if ((second_to_last_char >= '0') && (second_to_last_char <= '9')) { |
+ LOG(ERROR) << "Bad device:" << device; |
+ return; |
+ } |
+ |
+ // Strip trailing 1-4 off end of device |
+ device.resize(device.size() - 1); |
+ |
+ char mbr[512]; // MBR is the first 512 bytes of the device |
+ if (!ReadMbr(mbr, sizeof(mbr), device.c_str())) |
+ return; |
+ |
+ // Check MBR. Verify that last two byes are 0x55aa. This is the MBR signature. |
+ if ((mbr[510] != static_cast<char>(0x55)) || |
+ (mbr[511] != static_cast<char>(0xaa))) { |
+ LOG(ERROR) << "Bad MBR signature"; |
+ return; |
+ } |
+ |
+ // Mark partition passed in bootable and all other partitions non bootable. |
+ // There are 4 partition table entries, each 16 bytes, stored consecutively |
+ // beginning at byte 446. Within each entry, the first byte is the |
+ // bootable flag. It's set to 0x80 (bootable) or 0x00 (not bootable). |
+ for (int i = 0; i < 4; i++) { |
+ int offset = 446 + 16 * i; |
+ |
+ // partition_number is indexed from 1, not 0 |
+ if ((i + 1) == partition_number) |
+ mbr[offset] = 0x80; |
+ else |
+ mbr[offset] = '\0'; |
+ } |
+ |
+ // Write MBR back to disk |
+ bool success = WriteMbr(mbr, sizeof(mbr), device.c_str()); |
+ if (success) { |
+ completer.set_success(true); |
+ if (HasOutputPipe()) { |
+ SetOutputObject(GetInputObject()); |
+ } |
+ } |
+} |
+ |
+bool SetBootableFlagAction::ReadMbr(char* buffer, |
+ int buffer_len, |
+ const char* device) { |
+ int fd = open(device, O_RDONLY, 0); |
+ TEST_AND_RETURN_FALSE(fd >= 0); |
+ |
+ ssize_t r = read(fd, buffer, buffer_len); |
+ close(fd); |
+ TEST_AND_RETURN_FALSE(r == buffer_len); |
+ |
+ return true; |
+} |
+ |
+bool SetBootableFlagAction::WriteMbr(const char* buffer, |
+ int buffer_len, |
+ const char* device) { |
+ int fd = open(device, O_WRONLY, 0666); |
+ TEST_AND_RETURN_FALSE(fd >= 0); |
+ ScopedFdCloser fd_closer(&fd); |
+ |
+ ssize_t bytes_written = 0; |
+ while (bytes_written < buffer_len) { |
+ ssize_t r = write(fd, buffer + bytes_written, buffer_len - bytes_written); |
+ TEST_AND_RETURN_FALSE_ERRNO(r >= 0); |
+ bytes_written += r; |
+ } |
+ TEST_AND_RETURN_FALSE(bytes_written == buffer_len); |
+ |
+ return true; |
+} |
+ |
+ |
+} // namespace chromeos_update_engine |