OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "update_engine/set_bootable_flag_action.h" | 5 #include "update_engine/set_bootable_flag_action.h" |
6 #include <sys/stat.h> | 6 #include <sys/stat.h> |
7 #include <sys/types.h> | 7 #include <sys/types.h> |
8 #include <errno.h> | 8 #include <errno.h> |
9 #include <fcntl.h> | 9 #include <fcntl.h> |
10 #include <string> | 10 #include <string> |
| 11 #include <vector> |
| 12 #include "update_engine/subprocess.h" |
11 #include "update_engine/utils.h" | 13 #include "update_engine/utils.h" |
12 | 14 |
13 using std::string; | 15 using std::string; |
| 16 using std::vector; |
14 | 17 |
15 namespace chromeos_update_engine { | 18 namespace chromeos_update_engine { |
16 | 19 |
| 20 namespace { |
| 21 const char* const kGpt = "/usr/bin/gpt"; |
| 22 const ssize_t kPmbrLength = 512; |
| 23 } |
| 24 |
17 void SetBootableFlagAction::PerformAction() { | 25 void SetBootableFlagAction::PerformAction() { |
18 ScopedActionCompleter completer(processor_, this); | 26 ScopedActionCompleter completer(processor_, this); |
19 | 27 |
20 if (!HasInputObject() || GetInputObject().empty()) { | 28 TEST_AND_RETURN(HasInputObject()); |
21 LOG(ERROR) << "SetBootableFlagAction: No input object."; | 29 const InstallPlan install_plan = GetInputObject(); |
22 return; | 30 TEST_AND_RETURN(!install_plan.install_path.empty()); |
| 31 const string partition = install_plan.install_path; |
| 32 string root_device = utils::RootDevice(partition); |
| 33 string partition_number = utils::PartitionNumber(partition); |
| 34 |
| 35 utils::BootLoader boot_loader; |
| 36 TEST_AND_RETURN(utils::GetBootloader(&boot_loader)); |
| 37 |
| 38 // For now, only support Syslinux bootloader |
| 39 TEST_AND_RETURN(boot_loader == utils::BootLoader_SYSLINUX); |
| 40 |
| 41 string temp_file; |
| 42 TEST_AND_RETURN(utils::MakeTempFile("/tmp/pmbr_copy.XXXXXX", |
| 43 &temp_file, |
| 44 NULL)); |
| 45 ScopedPathUnlinker temp_file_unlinker(temp_file); |
| 46 |
| 47 // Copy existing PMBR to temp file |
| 48 vector<char> buf(kPmbrLength); |
| 49 { |
| 50 int fd = open(root_device.c_str(), O_RDONLY); |
| 51 TEST_AND_RETURN(fd >= 0); |
| 52 ScopedFdCloser fd_closer(&fd); |
| 53 ssize_t bytes_read; |
| 54 TEST_AND_RETURN(utils::PReadAll(fd, &buf[0], buf.size(), 0, &bytes_read)); |
23 } | 55 } |
24 string device = GetInputObject(); | 56 TEST_AND_RETURN(utils::WriteFile(temp_file.c_str(), &buf[0], buf.size())); |
25 | 57 |
26 if (device.size() < 2) { | 58 // Call gpt tool to do the work |
27 LOG(ERROR) << "Device name too short: " << device; | 59 vector<string> command; |
28 return; | 60 command.push_back(kGpt); |
29 } | 61 command.push_back("-S"); |
| 62 command.push_back("boot"); |
| 63 command.push_back("-i"); |
| 64 command.push_back(partition_number); |
| 65 command.push_back("-b"); |
| 66 command.push_back(temp_file); |
| 67 command.push_back(root_device); |
| 68 int rc = 0; |
| 69 Subprocess::SynchronousExec(command, &rc); |
| 70 TEST_AND_RETURN(rc == 0); |
30 | 71 |
31 // Make sure device is valid. | 72 completer.set_success(true); |
32 const char last_char = device[device.size() - 1]; | 73 if (HasOutputPipe()) |
33 if ((last_char < '1') || (last_char > '4')) { | 74 SetOutputObject(GetInputObject()); |
34 LOG(ERROR) << "Bad device:" << device; | |
35 return; | |
36 } | |
37 | |
38 // We were passed the partition_number'th partition; indexed from 1, not 0 | |
39 int partition_number = last_char - '0'; | |
40 | |
41 const char second_to_last_char = device[device.size() - 2]; | |
42 if ((second_to_last_char >= '0') && (second_to_last_char <= '9')) { | |
43 LOG(ERROR) << "Bad device:" << device; | |
44 return; | |
45 } | |
46 | |
47 // Strip trailing 1-4 off end of device | |
48 device.resize(device.size() - 1); | |
49 | |
50 char mbr[512]; // MBR is the first 512 bytes of the device | |
51 if (!ReadMbr(mbr, sizeof(mbr), device.c_str())) | |
52 return; | |
53 | |
54 // Check MBR. Verify that last two byes are 0x55aa. This is the MBR signature. | |
55 if ((mbr[510] != static_cast<char>(0x55)) || | |
56 (mbr[511] != static_cast<char>(0xaa))) { | |
57 LOG(ERROR) << "Bad MBR signature"; | |
58 return; | |
59 } | |
60 | |
61 // Mark partition passed in bootable and all other partitions non bootable. | |
62 // There are 4 partition table entries, each 16 bytes, stored consecutively | |
63 // beginning at byte 446. Within each entry, the first byte is the | |
64 // bootable flag. It's set to 0x80 (bootable) or 0x00 (not bootable). | |
65 for (int i = 0; i < 4; i++) { | |
66 int offset = 446 + 16 * i; | |
67 | |
68 // partition_number is indexed from 1, not 0 | |
69 if ((i + 1) == partition_number) | |
70 mbr[offset] = 0x80; | |
71 else | |
72 mbr[offset] = '\0'; | |
73 } | |
74 | |
75 // Write MBR back to disk | |
76 bool success = WriteMbr(mbr, sizeof(mbr), device.c_str()); | |
77 if (success) { | |
78 completer.set_success(true); | |
79 if (HasOutputPipe()) { | |
80 SetOutputObject(GetInputObject()); | |
81 } | |
82 } | |
83 } | 75 } |
84 | 76 |
85 bool SetBootableFlagAction::ReadMbr(char* buffer, | |
86 int buffer_len, | |
87 const char* device) { | |
88 int fd = open(device, O_RDONLY, 0); | |
89 TEST_AND_RETURN_FALSE(fd >= 0); | |
90 | |
91 ssize_t r = read(fd, buffer, buffer_len); | |
92 close(fd); | |
93 TEST_AND_RETURN_FALSE(r == buffer_len); | |
94 | |
95 return true; | |
96 } | |
97 | |
98 bool SetBootableFlagAction::WriteMbr(const char* buffer, | |
99 int buffer_len, | |
100 const char* device) { | |
101 int fd = open(device, O_WRONLY, 0666); | |
102 TEST_AND_RETURN_FALSE(fd >= 0); | |
103 ScopedFdCloser fd_closer(&fd); | |
104 | |
105 ssize_t bytes_written = 0; | |
106 while (bytes_written < buffer_len) { | |
107 ssize_t r = write(fd, buffer + bytes_written, buffer_len - bytes_written); | |
108 TEST_AND_RETURN_FALSE_ERRNO(r >= 0); | |
109 bytes_written += r; | |
110 } | |
111 TEST_AND_RETURN_FALSE(bytes_written == buffer_len); | |
112 | |
113 return true; | |
114 } | |
115 | |
116 | |
117 } // namespace chromeos_update_engine | 77 } // namespace chromeos_update_engine |
OLD | NEW |