diff options
-rw-r--r-- | tests/component/updater_test.cpp | 217 | ||||
-rw-r--r-- | updater/blockimg.cpp | 181 | ||||
-rw-r--r-- | updater/include/updater/blockimg.h | 3 |
3 files changed, 392 insertions, 9 deletions
diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index d9d01d42..448fe493 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp | |||
@@ -707,3 +707,220 @@ TEST_F(UpdaterTest, brotli_new_data) { | |||
707 | ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); | 707 | ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); |
708 | CloseArchive(handle); | 708 | CloseArchive(handle); |
709 | } | 709 | } |
710 | |||
711 | TEST_F(UpdaterTest, last_command_update) { | ||
712 | TemporaryFile temp_file; | ||
713 | last_command_file = temp_file.path; | ||
714 | |||
715 | std::string block1 = std::string(4096, '1'); | ||
716 | std::string block2 = std::string(4096, '2'); | ||
717 | std::string block3 = std::string(4096, '3'); | ||
718 | std::string block1_hash = get_sha1(block1); | ||
719 | std::string block2_hash = get_sha1(block2); | ||
720 | std::string block3_hash = get_sha1(block3); | ||
721 | |||
722 | // Compose the transfer list to fail the first update. | ||
723 | std::vector<std::string> transfer_list_fail = { | ||
724 | "4", | ||
725 | "2", | ||
726 | "0", | ||
727 | "2", | ||
728 | "stash " + block1_hash + " 2,0,1", | ||
729 | "move " + block1_hash + " 2,1,2 1 2,0,1", | ||
730 | "stash " + block3_hash + " 2,2,3", | ||
731 | "fail", | ||
732 | }; | ||
733 | |||
734 | // Mimic a resumed update with the same transfer commands. | ||
735 | std::vector<std::string> transfer_list_continue = { | ||
736 | "4", | ||
737 | "2", | ||
738 | "0", | ||
739 | "2", | ||
740 | "stash " + block1_hash + " 2,0,1", | ||
741 | "move " + block1_hash + " 2,1,2 1 2,0,1", | ||
742 | "stash " + block3_hash + " 2,2,3", | ||
743 | "move " + block1_hash + " 2,2,3 1 2,0,1", | ||
744 | }; | ||
745 | |||
746 | std::unordered_map<std::string, std::string> entries = { | ||
747 | { "new_data", "" }, | ||
748 | { "patch_data", "" }, | ||
749 | { "transfer_list_fail", android::base::Join(transfer_list_fail, '\n') }, | ||
750 | { "transfer_list_continue", android::base::Join(transfer_list_continue, '\n') }, | ||
751 | }; | ||
752 | |||
753 | // Build the update package. | ||
754 | TemporaryFile zip_file; | ||
755 | BuildUpdatePackage(entries, zip_file.release()); | ||
756 | |||
757 | MemMapping map; | ||
758 | ASSERT_TRUE(map.MapFile(zip_file.path)); | ||
759 | ZipArchiveHandle handle; | ||
760 | ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); | ||
761 | |||
762 | // Set up the handler, command_pipe, patch offset & length. | ||
763 | UpdaterInfo updater_info; | ||
764 | updater_info.package_zip = handle; | ||
765 | TemporaryFile temp_pipe; | ||
766 | updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); | ||
767 | updater_info.package_zip_addr = map.addr; | ||
768 | updater_info.package_zip_len = map.length; | ||
769 | |||
770 | std::string src_content = block1 + block2 + block3; | ||
771 | TemporaryFile update_file; | ||
772 | ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); | ||
773 | std::string script = | ||
774 | "block_image_update(\"" + std::string(update_file.path) + | ||
775 | R"(", package_extract_file("transfer_list_fail"), "new_data", "patch_data"))"; | ||
776 | expect("", script.c_str(), kNoCause, &updater_info); | ||
777 | |||
778 | // Expect last_command to contain the last stash command. | ||
779 | std::string last_command_content; | ||
780 | ASSERT_TRUE(android::base::ReadFileToString(last_command_file.c_str(), &last_command_content)); | ||
781 | EXPECT_EQ("2\nstash " + block3_hash + " 2,2,3", last_command_content); | ||
782 | std::string updated_contents; | ||
783 | ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_contents)); | ||
784 | ASSERT_EQ(block1 + block1 + block3, updated_contents); | ||
785 | |||
786 | // Resume the update, expect the first 'move' to be skipped but the second 'move' to be executed. | ||
787 | ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); | ||
788 | std::string script_second_update = | ||
789 | "block_image_update(\"" + std::string(update_file.path) + | ||
790 | R"(", package_extract_file("transfer_list_continue"), "new_data", "patch_data"))"; | ||
791 | expect("t", script_second_update.c_str(), kNoCause, &updater_info); | ||
792 | ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_contents)); | ||
793 | ASSERT_EQ(block1 + block2 + block1, updated_contents); | ||
794 | |||
795 | ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); | ||
796 | CloseArchive(handle); | ||
797 | } | ||
798 | |||
799 | TEST_F(UpdaterTest, last_command_update_unresumable) { | ||
800 | TemporaryFile temp_file; | ||
801 | last_command_file = temp_file.path; | ||
802 | |||
803 | std::string block1 = std::string(4096, '1'); | ||
804 | std::string block2 = std::string(4096, '2'); | ||
805 | std::string block1_hash = get_sha1(block1); | ||
806 | std::string block2_hash = get_sha1(block2); | ||
807 | |||
808 | // Construct an unresumable update with source blocks mismatch. | ||
809 | std::vector<std::string> transfer_list_unresumable = { | ||
810 | "4", "2", "0", "2", "stash " + block1_hash + " 2,0,1", "move " + block2_hash + " 2,1,2 1 2,0,1", | ||
811 | }; | ||
812 | |||
813 | std::unordered_map<std::string, std::string> entries = { | ||
814 | { "new_data", "" }, | ||
815 | { "patch_data", "" }, | ||
816 | { "transfer_list_unresumable", android::base::Join(transfer_list_unresumable, '\n') }, | ||
817 | }; | ||
818 | |||
819 | // Build the update package. | ||
820 | TemporaryFile zip_file; | ||
821 | BuildUpdatePackage(entries, zip_file.release()); | ||
822 | |||
823 | MemMapping map; | ||
824 | ASSERT_TRUE(map.MapFile(zip_file.path)); | ||
825 | ZipArchiveHandle handle; | ||
826 | ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); | ||
827 | |||
828 | // Set up the handler, command_pipe, patch offset & length. | ||
829 | UpdaterInfo updater_info; | ||
830 | updater_info.package_zip = handle; | ||
831 | TemporaryFile temp_pipe; | ||
832 | updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); | ||
833 | updater_info.package_zip_addr = map.addr; | ||
834 | updater_info.package_zip_len = map.length; | ||
835 | |||
836 | // Set up the last_command_file | ||
837 | ASSERT_TRUE( | ||
838 | android::base::WriteStringToFile("0\nstash " + block1_hash + " 2,0,1", last_command_file)); | ||
839 | |||
840 | // The last_command_file will be deleted if the update encounters an unresumable failure | ||
841 | // later. | ||
842 | std::string src_content = block1 + block1; | ||
843 | TemporaryFile update_file; | ||
844 | ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); | ||
845 | std::string script = | ||
846 | "block_image_update(\"" + std::string(update_file.path) + | ||
847 | R"(", package_extract_file("transfer_list_unresumable"), "new_data", "patch_data"))"; | ||
848 | expect("", script.c_str(), kNoCause, &updater_info); | ||
849 | ASSERT_EQ(-1, access(last_command_file.c_str(), R_OK)); | ||
850 | |||
851 | ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); | ||
852 | CloseArchive(handle); | ||
853 | } | ||
854 | |||
855 | TEST_F(UpdaterTest, last_command_verify) { | ||
856 | TemporaryFile temp_file; | ||
857 | last_command_file = temp_file.path; | ||
858 | |||
859 | std::string block1 = std::string(4096, '1'); | ||
860 | std::string block2 = std::string(4096, '2'); | ||
861 | std::string block3 = std::string(4096, '3'); | ||
862 | std::string block1_hash = get_sha1(block1); | ||
863 | std::string block2_hash = get_sha1(block2); | ||
864 | std::string block3_hash = get_sha1(block3); | ||
865 | |||
866 | std::vector<std::string> transfer_list_verify = { | ||
867 | "4", | ||
868 | "2", | ||
869 | "0", | ||
870 | "2", | ||
871 | "stash " + block1_hash + " 2,0,1", | ||
872 | "move " + block1_hash + " 2,0,1 1 2,0,1", | ||
873 | "move " + block1_hash + " 2,1,2 1 2,0,1", | ||
874 | "stash " + block3_hash + " 2,2,3", | ||
875 | }; | ||
876 | |||
877 | std::unordered_map<std::string, std::string> entries = { | ||
878 | { "new_data", "" }, | ||
879 | { "patch_data", "" }, | ||
880 | { "transfer_list_verify", android::base::Join(transfer_list_verify, '\n') }, | ||
881 | }; | ||
882 | |||
883 | // Build the update package. | ||
884 | TemporaryFile zip_file; | ||
885 | BuildUpdatePackage(entries, zip_file.release()); | ||
886 | |||
887 | MemMapping map; | ||
888 | ASSERT_TRUE(map.MapFile(zip_file.path)); | ||
889 | ZipArchiveHandle handle; | ||
890 | ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); | ||
891 | |||
892 | // Set up the handler, command_pipe, patch offset & length. | ||
893 | UpdaterInfo updater_info; | ||
894 | updater_info.package_zip = handle; | ||
895 | TemporaryFile temp_pipe; | ||
896 | updater_info.cmd_pipe = fdopen(temp_pipe.release(), "wbe"); | ||
897 | updater_info.package_zip_addr = map.addr; | ||
898 | updater_info.package_zip_len = map.length; | ||
899 | |||
900 | std::string src_content = block1 + block1 + block3; | ||
901 | TemporaryFile update_file; | ||
902 | ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); | ||
903 | |||
904 | ASSERT_TRUE( | ||
905 | android::base::WriteStringToFile("2\nstash " + block3_hash + " 2,2,3", last_command_file)); | ||
906 | |||
907 | // Expect the verification to succeed and the last_command_file is intact. | ||
908 | std::string script_verify = | ||
909 | "block_image_verify(\"" + std::string(update_file.path) + | ||
910 | R"(", package_extract_file("transfer_list_verify"), "new_data","patch_data"))"; | ||
911 | expect("t", script_verify.c_str(), kNoCause, &updater_info); | ||
912 | |||
913 | std::string last_command_content; | ||
914 | ASSERT_TRUE(android::base::ReadFileToString(last_command_file.c_str(), &last_command_content)); | ||
915 | EXPECT_EQ("2\nstash " + block3_hash + " 2,2,3", last_command_content); | ||
916 | |||
917 | // Expect the verification to succeed but last_command_file to be deleted; because the target | ||
918 | // blocks don't have the expected contents for the second move command. | ||
919 | src_content = block1 + block2 + block3; | ||
920 | ASSERT_TRUE(android::base::WriteStringToFile(src_content, update_file.path)); | ||
921 | expect("t", script_verify.c_str(), kNoCause, &updater_info); | ||
922 | ASSERT_EQ(-1, access(last_command_file.c_str(), R_OK)); | ||
923 | |||
924 | ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); | ||
925 | CloseArchive(handle); | ||
926 | } | ||
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 0e90e94a..feb2aeb2 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp | |||
@@ -34,11 +34,13 @@ | |||
34 | #include <fec/io.h> | 34 | #include <fec/io.h> |
35 | 35 | ||
36 | #include <functional> | 36 | #include <functional> |
37 | #include <limits> | ||
37 | #include <memory> | 38 | #include <memory> |
38 | #include <string> | 39 | #include <string> |
39 | #include <unordered_map> | 40 | #include <unordered_map> |
40 | #include <vector> | 41 | #include <vector> |
41 | 42 | ||
43 | #include <android-base/file.h> | ||
42 | #include <android-base/logging.h> | 44 | #include <android-base/logging.h> |
43 | #include <android-base/parseint.h> | 45 | #include <android-base/parseint.h> |
44 | #include <android-base/strings.h> | 46 | #include <android-base/strings.h> |
@@ -67,10 +69,96 @@ static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery"; | |||
67 | static constexpr mode_t STASH_DIRECTORY_MODE = 0700; | 69 | static constexpr mode_t STASH_DIRECTORY_MODE = 0700; |
68 | static constexpr mode_t STASH_FILE_MODE = 0600; | 70 | static constexpr mode_t STASH_FILE_MODE = 0600; |
69 | 71 | ||
72 | std::string last_command_file = "/cache/recovery/last_command"; | ||
73 | |||
70 | static CauseCode failure_type = kNoCause; | 74 | static CauseCode failure_type = kNoCause; |
71 | static bool is_retry = false; | 75 | static bool is_retry = false; |
72 | static std::unordered_map<std::string, RangeSet> stash_map; | 76 | static std::unordered_map<std::string, RangeSet> stash_map; |
73 | 77 | ||
78 | static void DeleteLastCommandFile() { | ||
79 | if (unlink(last_command_file.c_str()) == -1 && errno != ENOENT) { | ||
80 | PLOG(ERROR) << "Failed to unlink: " << last_command_file; | ||
81 | } | ||
82 | } | ||
83 | |||
84 | // Parse the last command index of the last update and save the result to |last_command_index|. | ||
85 | // Return true if we successfully read the index. | ||
86 | static bool ParseLastCommandFile(int* last_command_index) { | ||
87 | android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(last_command_file.c_str(), O_RDONLY))); | ||
88 | if (fd == -1) { | ||
89 | if (errno != ENOENT) { | ||
90 | PLOG(ERROR) << "Failed to open " << last_command_file; | ||
91 | return false; | ||
92 | } | ||
93 | |||
94 | LOG(INFO) << last_command_file << " doesn't exist."; | ||
95 | return false; | ||
96 | } | ||
97 | |||
98 | // Now that the last_command file exists, parse the last command index of previous update. | ||
99 | std::string content; | ||
100 | if (!android::base::ReadFdToString(fd.get(), &content)) { | ||
101 | LOG(ERROR) << "Failed to read: " << last_command_file; | ||
102 | return false; | ||
103 | } | ||
104 | |||
105 | std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n"); | ||
106 | if (lines.size() != 2) { | ||
107 | LOG(ERROR) << "Unexpected line counts in last command file: " << content; | ||
108 | return false; | ||
109 | } | ||
110 | |||
111 | if (!android::base::ParseInt(lines[0], last_command_index)) { | ||
112 | LOG(ERROR) << "Failed to parse integer in: " << lines[0]; | ||
113 | return false; | ||
114 | } | ||
115 | |||
116 | return true; | ||
117 | } | ||
118 | |||
119 | // Update the last command index in the last_command_file if the current command writes to the | ||
120 | // stash either explicitly or implicitly. | ||
121 | static bool UpdateLastCommandIndex(int command_index, const std::string& command_string) { | ||
122 | std::string last_command_tmp = last_command_file + ".tmp"; | ||
123 | std::string content = std::to_string(command_index) + "\n" + command_string; | ||
124 | android::base::unique_fd wfd( | ||
125 | TEMP_FAILURE_RETRY(open(last_command_tmp.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0660))); | ||
126 | if (wfd == -1 || !android::base::WriteStringToFd(content, wfd)) { | ||
127 | PLOG(ERROR) << "Failed to update last command"; | ||
128 | return false; | ||
129 | } | ||
130 | |||
131 | if (fsync(wfd) == -1) { | ||
132 | PLOG(ERROR) << "Failed to fsync " << last_command_tmp; | ||
133 | return false; | ||
134 | } | ||
135 | |||
136 | if (chown(last_command_tmp.c_str(), AID_SYSTEM, AID_SYSTEM) == -1) { | ||
137 | PLOG(ERROR) << "Failed to change owner for " << last_command_tmp; | ||
138 | return false; | ||
139 | } | ||
140 | |||
141 | if (rename(last_command_tmp.c_str(), last_command_file.c_str()) == -1) { | ||
142 | PLOG(ERROR) << "Failed to rename" << last_command_tmp; | ||
143 | return false; | ||
144 | } | ||
145 | |||
146 | std::string last_command_dir = android::base::Dirname(last_command_file); | ||
147 | android::base::unique_fd dfd( | ||
148 | TEMP_FAILURE_RETRY(ota_open(last_command_dir.c_str(), O_RDONLY | O_DIRECTORY))); | ||
149 | if (dfd == -1) { | ||
150 | PLOG(ERROR) << "Failed to open " << last_command_dir; | ||
151 | return false; | ||
152 | } | ||
153 | |||
154 | if (fsync(dfd) == -1) { | ||
155 | PLOG(ERROR) << "Failed to fsync " << last_command_dir; | ||
156 | return false; | ||
157 | } | ||
158 | |||
159 | return true; | ||
160 | } | ||
161 | |||
74 | static int read_all(int fd, uint8_t* data, size_t size) { | 162 | static int read_all(int fd, uint8_t* data, size_t size) { |
75 | size_t so_far = 0; | 163 | size_t so_far = 0; |
76 | while (so_far < size) { | 164 | while (so_far < size) { |
@@ -439,6 +527,7 @@ static int WriteBlocks(const RangeSet& tgt, const std::vector<uint8_t>& buffer, | |||
439 | struct CommandParameters { | 527 | struct CommandParameters { |
440 | std::vector<std::string> tokens; | 528 | std::vector<std::string> tokens; |
441 | size_t cpos; | 529 | size_t cpos; |
530 | int cmdindex; | ||
442 | const char* cmdname; | 531 | const char* cmdname; |
443 | const char* cmdline; | 532 | const char* cmdline; |
444 | std::string freestash; | 533 | std::string freestash; |
@@ -455,6 +544,7 @@ struct CommandParameters { | |||
455 | pthread_t thread; | 544 | pthread_t thread; |
456 | std::vector<uint8_t> buffer; | 545 | std::vector<uint8_t> buffer; |
457 | uint8_t* patch_start; | 546 | uint8_t* patch_start; |
547 | bool target_verified; // The target blocks have expected contents already. | ||
458 | }; | 548 | }; |
459 | 549 | ||
460 | // Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is | 550 | // Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is |
@@ -1072,6 +1162,10 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* | |||
1072 | return -1; | 1162 | return -1; |
1073 | } | 1163 | } |
1074 | 1164 | ||
1165 | if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) { | ||
1166 | LOG(WARNING) << "Failed to update the last command file."; | ||
1167 | } | ||
1168 | |||
1075 | params.stashed += *src_blocks; | 1169 | params.stashed += *src_blocks; |
1076 | // Can be deleted when the write has completed. | 1170 | // Can be deleted when the write has completed. |
1077 | if (!stash_exists) { | 1171 | if (!stash_exists) { |
@@ -1112,8 +1206,11 @@ static int PerformCommandMove(CommandParameters& params) { | |||
1112 | 1206 | ||
1113 | if (status == 0) { | 1207 | if (status == 0) { |
1114 | params.foundwrites = true; | 1208 | params.foundwrites = true; |
1115 | } else if (params.foundwrites) { | 1209 | } else { |
1116 | LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; | 1210 | params.target_verified = true; |
1211 | if (params.foundwrites) { | ||
1212 | LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; | ||
1213 | } | ||
1117 | } | 1214 | } |
1118 | 1215 | ||
1119 | if (params.canwrite) { | 1216 | if (params.canwrite) { |
@@ -1177,8 +1274,15 @@ static int PerformCommandStash(CommandParameters& params) { | |||
1177 | } | 1274 | } |
1178 | 1275 | ||
1179 | LOG(INFO) << "stashing " << blocks << " blocks to " << id; | 1276 | LOG(INFO) << "stashing " << blocks << " blocks to " << id; |
1180 | params.stashed += blocks; | 1277 | int result = WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr); |
1181 | return WriteStash(params.stashbase, id, blocks, params.buffer, false, nullptr); | 1278 | if (result == 0) { |
1279 | if (!UpdateLastCommandIndex(params.cmdindex, params.cmdline)) { | ||
1280 | LOG(WARNING) << "Failed to update the last command file."; | ||
1281 | } | ||
1282 | |||
1283 | params.stashed += blocks; | ||
1284 | } | ||
1285 | return result; | ||
1182 | } | 1286 | } |
1183 | 1287 | ||
1184 | static int PerformCommandFree(CommandParameters& params) { | 1288 | static int PerformCommandFree(CommandParameters& params) { |
@@ -1306,8 +1410,11 @@ static int PerformCommandDiff(CommandParameters& params) { | |||
1306 | 1410 | ||
1307 | if (status == 0) { | 1411 | if (status == 0) { |
1308 | params.foundwrites = true; | 1412 | params.foundwrites = true; |
1309 | } else if (params.foundwrites) { | 1413 | } else { |
1310 | LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; | 1414 | params.target_verified = true; |
1415 | if (params.foundwrites) { | ||
1416 | LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; | ||
1417 | } | ||
1311 | } | 1418 | } |
1312 | 1419 | ||
1313 | if (params.canwrite) { | 1420 | if (params.canwrite) { |
@@ -1566,6 +1673,23 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, | |||
1566 | 1673 | ||
1567 | params.createdstash = res; | 1674 | params.createdstash = res; |
1568 | 1675 | ||
1676 | // When performing an update, save the index and cmdline of the current command into | ||
1677 | // the last_command_file if this command writes to the stash either explicitly of implicitly. | ||
1678 | // Upon resuming an update, read the saved index first; then | ||
1679 | // 1. In verification mode, check if the 'move' or 'diff' commands before the saved index has | ||
1680 | // the expected target blocks already. If not, these commands cannot be skipped and we need | ||
1681 | // to attempt to execute them again. Therefore, we will delete the last_command_file so that | ||
1682 | // the update will resume from the start of the transfer list. | ||
1683 | // 2. In update mode, skip all commands before the saved index. Therefore, we can avoid deleting | ||
1684 | // stashes with duplicate id unintentionally (b/69858743); and also speed up the update. | ||
1685 | // If an update succeeds or is unresumable, delete the last_command_file. | ||
1686 | int saved_last_command_index; | ||
1687 | if (!ParseLastCommandFile(&saved_last_command_index)) { | ||
1688 | DeleteLastCommandFile(); | ||
1689 | // We failed to parse the last command, set it explicitly to -1. | ||
1690 | saved_last_command_index = -1; | ||
1691 | } | ||
1692 | |||
1569 | start += 2; | 1693 | start += 2; |
1570 | 1694 | ||
1571 | // Build a map of the available commands | 1695 | // Build a map of the available commands |
@@ -1581,14 +1705,20 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, | |||
1581 | int rc = -1; | 1705 | int rc = -1; |
1582 | 1706 | ||
1583 | // Subsequent lines are all individual transfer commands | 1707 | // Subsequent lines are all individual transfer commands |
1584 | for (auto it = lines.cbegin() + start; it != lines.cend(); it++) { | 1708 | for (size_t i = start; i < lines.size(); i++) { |
1585 | const std::string& line(*it); | 1709 | const std::string& line = lines[i]; |
1586 | if (line.empty()) continue; | 1710 | if (line.empty()) continue; |
1587 | 1711 | ||
1588 | params.tokens = android::base::Split(line, " "); | 1712 | params.tokens = android::base::Split(line, " "); |
1589 | params.cpos = 0; | 1713 | params.cpos = 0; |
1714 | if (i - start > std::numeric_limits<int>::max()) { | ||
1715 | params.cmdindex = -1; | ||
1716 | } else { | ||
1717 | params.cmdindex = i - start; | ||
1718 | } | ||
1590 | params.cmdname = params.tokens[params.cpos++].c_str(); | 1719 | params.cmdname = params.tokens[params.cpos++].c_str(); |
1591 | params.cmdline = line.c_str(); | 1720 | params.cmdline = line.c_str(); |
1721 | params.target_verified = false; | ||
1592 | 1722 | ||
1593 | if (cmd_map.find(params.cmdname) == cmd_map.end()) { | 1723 | if (cmd_map.find(params.cmdname) == cmd_map.end()) { |
1594 | LOG(ERROR) << "unexpected command [" << params.cmdname << "]"; | 1724 | LOG(ERROR) << "unexpected command [" << params.cmdname << "]"; |
@@ -1597,11 +1727,38 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, | |||
1597 | 1727 | ||
1598 | const Command* cmd = cmd_map[params.cmdname]; | 1728 | const Command* cmd = cmd_map[params.cmdname]; |
1599 | 1729 | ||
1600 | if (cmd->f != nullptr && cmd->f(params) == -1) { | 1730 | if (cmd->f == nullptr) { |
1731 | LOG(ERROR) << "failed to find the function for command [" << line << "]"; | ||
1732 | goto pbiudone; | ||
1733 | } | ||
1734 | |||
1735 | // Skip all commands before the saved last command index when resuming an update. | ||
1736 | if (params.canwrite && params.cmdindex != -1 && params.cmdindex <= saved_last_command_index) { | ||
1737 | LOG(INFO) << "Skipping already executed command: " << params.cmdindex | ||
1738 | << ", last executed command for previous update: " << saved_last_command_index; | ||
1739 | continue; | ||
1740 | } | ||
1741 | |||
1742 | if (cmd->f(params) == -1) { | ||
1601 | LOG(ERROR) << "failed to execute command [" << line << "]"; | 1743 | LOG(ERROR) << "failed to execute command [" << line << "]"; |
1602 | goto pbiudone; | 1744 | goto pbiudone; |
1603 | } | 1745 | } |
1604 | 1746 | ||
1747 | // In verify mode, check if the commands before the saved last_command_index have been | ||
1748 | // executed correctly. If some target blocks have unexpected contents, delete the last command | ||
1749 | // file so that we will resume the update from the first command in the transfer list. | ||
1750 | if (!params.canwrite && saved_last_command_index != -1 && params.cmdindex != -1 && | ||
1751 | params.cmdindex <= saved_last_command_index) { | ||
1752 | // TODO(xunchang) check that the cmdline of the saved index is correct. | ||
1753 | std::string cmdname = std::string(params.cmdname); | ||
1754 | if ((cmdname == "move" || cmdname == "bsdiff" || cmdname == "imgdiff") && | ||
1755 | !params.target_verified) { | ||
1756 | LOG(WARNING) << "Previously executed command " << saved_last_command_index << ": " | ||
1757 | << params.cmdline << " doesn't produce expected target blocks."; | ||
1758 | saved_last_command_index = -1; | ||
1759 | DeleteLastCommandFile(); | ||
1760 | } | ||
1761 | } | ||
1605 | if (params.canwrite) { | 1762 | if (params.canwrite) { |
1606 | if (ota_fsync(params.fd) == -1) { | 1763 | if (ota_fsync(params.fd) == -1) { |
1607 | failure_type = kFsyncFailure; | 1764 | failure_type = kFsyncFailure; |
@@ -1643,6 +1800,7 @@ pbiudone: | |||
1643 | // Delete stash only after successfully completing the update, as it may contain blocks needed | 1800 | // Delete stash only after successfully completing the update, as it may contain blocks needed |
1644 | // to complete the update later. | 1801 | // to complete the update later. |
1645 | DeleteStash(params.stashbase); | 1802 | DeleteStash(params.stashbase); |
1803 | DeleteLastCommandFile(); | ||
1646 | } | 1804 | } |
1647 | 1805 | ||
1648 | pthread_mutex_destroy(¶ms.nti.mu); | 1806 | pthread_mutex_destroy(¶ms.nti.mu); |
@@ -1661,6 +1819,11 @@ pbiudone: | |||
1661 | BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state); | 1819 | BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state); |
1662 | } | 1820 | } |
1663 | 1821 | ||
1822 | // Delete the last command file if the update cannot be resumed. | ||
1823 | if (params.isunresumable) { | ||
1824 | DeleteLastCommandFile(); | ||
1825 | } | ||
1826 | |||
1664 | // Only delete the stash if the update cannot be resumed, or it's a verification run and we | 1827 | // Only delete the stash if the update cannot be resumed, or it's a verification run and we |
1665 | // created the stash. | 1828 | // created the stash. |
1666 | if (params.isunresumable || (!params.canwrite && params.createdstash)) { | 1829 | if (params.isunresumable || (!params.canwrite && params.createdstash)) { |
diff --git a/updater/include/updater/blockimg.h b/updater/include/updater/blockimg.h index 2f4ad3c0..2cc68ce9 100644 --- a/updater/include/updater/blockimg.h +++ b/updater/include/updater/blockimg.h | |||
@@ -17,6 +17,9 @@ | |||
17 | #ifndef _UPDATER_BLOCKIMG_H_ | 17 | #ifndef _UPDATER_BLOCKIMG_H_ |
18 | #define _UPDATER_BLOCKIMG_H_ | 18 | #define _UPDATER_BLOCKIMG_H_ |
19 | 19 | ||
20 | #include <string> | ||
21 | |||
22 | extern std::string last_command_file; | ||
20 | void RegisterBlockImageFunctions(); | 23 | void RegisterBlockImageFunctions(); |
21 | 24 | ||
22 | #endif | 25 | #endif |