aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tests/component/updater_test.cpp217
-rw-r--r--updater/blockimg.cpp181
-rw-r--r--updater/include/updater/blockimg.h3
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
711TEST_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
799TEST_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
855TEST_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";
67static constexpr mode_t STASH_DIRECTORY_MODE = 0700; 69static constexpr mode_t STASH_DIRECTORY_MODE = 0700;
68static constexpr mode_t STASH_FILE_MODE = 0600; 70static constexpr mode_t STASH_FILE_MODE = 0600;
69 71
72std::string last_command_file = "/cache/recovery/last_command";
73
70static CauseCode failure_type = kNoCause; 74static CauseCode failure_type = kNoCause;
71static bool is_retry = false; 75static bool is_retry = false;
72static std::unordered_map<std::string, RangeSet> stash_map; 76static std::unordered_map<std::string, RangeSet> stash_map;
73 77
78static 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.
86static 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.
121static 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
74static int read_all(int fd, uint8_t* data, size_t size) { 162static 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,
439struct CommandParameters { 527struct 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
1184static int PerformCommandFree(CommandParameters& params) { 1288static 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(&params.nti.mu); 1806 pthread_mutex_destroy(&params.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
22extern std::string last_command_file;
20void RegisterBlockImageFunctions(); 23void RegisterBlockImageFunctions();
21 24
22#endif 25#endif