From f019ab400a2745b69f9bbd32b0ac438e50addf2f Mon Sep 17 00:00:00 2001 From: Connor O'Brien Date: Fri, 18 Nov 2016 20:16:53 +0000 Subject: Revert "Convert update_verifier to boot HIDL HAL" This reverts commit f50593c447faf8415615b5dea2666d7f0f24a0fb. Bug: 32973182 Change-Id: I5b14a812671ea02575cb452242ff1a6f05edb9c1 (cherry picked from commit 30628db65cc7541c61d9b5e866d661d71cff6f4c) --- update_verifier/Android.mk | 5 +---- update_verifier/update_verifier.cpp | 31 ++++++++++++++----------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk index 5d118f873..c822999f3 100644 --- a/update_verifier/Android.mk +++ b/update_verifier/Android.mk @@ -24,10 +24,7 @@ LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ libhardware \ - liblog \ - libutils \ - libhidl \ - android.hardware.boot@1.0 + liblog LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. LOCAL_CFLAGS := -Werror diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index e97a3adba..93ac605b1 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -45,12 +45,7 @@ #include #include #include -#include - -using android::sp; -using android::hardware::boot::V1_0::IBootControl; -using android::hardware::boot::V1_0::BoolResult; -using android::hardware::boot::V1_0::CommandResult; +#include constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; constexpr int BLOCKSIZE = 4096; @@ -147,18 +142,21 @@ int main(int argc, char** argv) { LOG(INFO) << "Started with arg " << i << ": " << argv[i]; } - sp module = IBootControl::getService("bootctrl"); - if (module == nullptr) { + const hw_module_t* hw_module; + if (hw_get_module("bootctrl", &hw_module) != 0) { LOG(ERROR) << "Error getting bootctrl module."; return -1; } - uint32_t current_slot = module->getCurrentSlot(); - BoolResult is_successful = module->isSlotMarkedSuccessful(current_slot); - LOG(INFO) << "Booting slot " << current_slot << ": isSlotMarkedSuccessful=" - << static_cast(is_successful); + boot_control_module_t* module = reinterpret_cast( + const_cast(hw_module)); + module->init(module); + + unsigned current_slot = module->getCurrentSlot(module); + int is_successful= module->isSlotMarkedSuccessful(module, current_slot); + LOG(INFO) << "Booting slot " << current_slot << ": isSlotMarkedSuccessful=" << is_successful; - if (is_successful == BoolResult::FALSE) { + if (is_successful == 0) { // The current slot has not booted successfully. char verity_mode[PROPERTY_VALUE_MAX]; if (property_get("ro.boot.veritymode", verity_mode, "") == -1) { @@ -177,10 +175,9 @@ int main(int argc, char** argv) { return -1; } - CommandResult cr; - module->markBootSuccessful([&cr](CommandResult result) { cr = result; }); - if (!cr.success) { - LOG(ERROR) << "Error marking booted successfully: " << cr.errMsg; + int ret = module->markBootSuccessful(module); + if (ret != 0) { + LOG(ERROR) << "Error marking booted successfully: " << strerror(-ret); return -1; } LOG(INFO) << "Marked slot " << current_slot << " as booted successfully."; -- cgit v1.2.3 From 37cd3ae5d274d64dd5875a95f34272c619aa316b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 21 Nov 2016 09:42:33 -0800 Subject: applypatch: Release FD when explicitly calling close. We use android::base::unique_fd() to avoid leaking FD. We also want to call close (or ota_close) to explicitly check the close result. When combining the two together, we need to release the unique_fd to avoid closing the same FD twice. Bug: 33034669 Test: Trigger applypatch with install-recovery.sh. Change-Id: I1a4f5d5fba7a23ef98d8bd7b7b07e87ae6f705c5 (cherry picked from commit 48cf770471ef53fbf0a1837196220862a0bdb18d) --- applypatch/applypatch.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 9b84fa104..41a8d582b 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -210,7 +210,7 @@ int SaveFileContents(const char* filename, const FileContents* file) { printf("fsync of \"%s\" failed: %s\n", filename, strerror(errno)); return -1; } - if (ota_close(fd) != 0) { + if (ota_close(fd.release()) != 0) { printf("close of \"%s\" failed: %s\n", filename, strerror(errno)); return -1; } @@ -268,7 +268,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const std::string& t printf("failed to sync to %s: %s\n", partition, strerror(errno)); return -1; } - if (ota_close(fd) != 0) { + if (ota_close(fd.release()) != 0) { printf("failed to close %s: %s\n", partition, strerror(errno)); return -1; } @@ -287,7 +287,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const std::string& t } else { printf(" caches dropped\n"); } - ota_close(dc); + ota_close(dc.release()); sleep(1); // Verify. @@ -339,7 +339,7 @@ int WriteToPartition(const unsigned char* data, size_t len, const std::string& t return -1; } - if (ota_close(fd) == -1) { + if (ota_close(fd.release()) == -1) { printf("error closing %s: %s\n", partition, strerror(errno)); return -1; } @@ -782,7 +782,7 @@ static int GenerateTarget(FileContents* source_file, printf("failed to fsync file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno)); result = 1; } - if (ota_close(output_fd) != 0) { + if (ota_close(output_fd.release()) != 0) { printf("failed to close file \"%s\": %s\n", tmp_target_filename.c_str(), strerror(errno)); result = 1; } -- cgit v1.2.3 From 75672d5ee926eceb54368b1e4349351393645b10 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 13 Dec 2016 01:06:24 +0000 Subject: Revert "write_bootloader_message() should not reset stage field." This reverts commit 7e31f421a514da09b90e46dbd642a5e9b16e0003. Bug: 33534933 Change-Id: Ib173f6b1e36a79deafc3592785195693a6779471 (cherry picked from commit 26d5ae741efd904fdc20187eddad33ca31dd64fa) --- bootloader_message/bootloader_message.cpp | 8 +------- .../include/bootloader_message/bootloader_message.h | 3 +-- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 294b1725d..9a5671843 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -164,13 +164,7 @@ bool clear_bootloader_message(std::string* err) { } bool write_bootloader_message(const std::vector& options, std::string* err) { - bootloader_message boot; - if (!read_bootloader_message(&boot, err)) { - return false; - } - // Zero out the entire fields. - memset(boot.command, 0, sizeof(boot.command)); - memset(boot.recovery, 0, sizeof(boot.recovery)); + bootloader_message boot = {}; strlcpy(boot.command, "boot-recovery", sizeof(boot.command)); strlcpy(boot.recovery, "recovery\n", sizeof(boot.recovery)); for (const auto& s : options) { diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 437189e32..b3d2182df 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -193,8 +193,7 @@ bool write_bootloader_message(const bootloader_message& boot, std::string* err); bool write_bootloader_message_to(const bootloader_message& boot, const std::string& misc_blk_device, std::string* err); -// Write bootloader message (boots into recovery with the options) to BCB. Will -// set command and recovery fields only. +// Write bootloader message (boots into recovery with the options) to BCB. bool write_bootloader_message(const std::vector& options, std::string* err); // Clear BCB. -- cgit v1.2.3 From 1d2d8cae8016343980eabcbdd4bf77636321abce Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Wed, 4 Jan 2017 10:30:32 -0800 Subject: Write aliases before ffs mount in recovery This is necessary to support kernel changes that allow for multiple ffs functions. Some kernels require aliases in order to name function instances before mount time. Test: Reboot into recovery, verify adb works Bug: 34070894 Change-Id: I8376304d92af9b3e8c734fdb8cc77f0dc8bc4850 (cherry picked from commit e66f861a7c71f0d59fabd8ec1d096485faf0901e) --- etc/init.rc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/init.rc b/etc/init.rc index faa7b2a1c..fa3689df9 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -30,6 +30,7 @@ on init write /proc/sys/vm/max_map_count 1000000 on fs + write /sys/class/android_usb/android0/f_ffs/aliases adb mkdir /dev/usb-ffs 0770 shell shell mkdir /dev/usb-ffs/adb 0770 shell shell mount functionfs adb /dev/usb-ffs/adb uid=2000,gid=2000 @@ -37,7 +38,6 @@ on fs write /sys/class/android_usb/android0/enable 0 write /sys/class/android_usb/android0/idVendor 18D1 write /sys/class/android_usb/android0/idProduct D001 - write /sys/class/android_usb/android0/f_ffs/aliases adb write /sys/class/android_usb/android0/functions adb write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer} write /sys/class/android_usb/android0/iProduct ${ro.product.model} -- cgit v1.2.3 From 441975b6b8b40fefa14b26a753e18d27cb14d897 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 15 Mar 2017 09:56:03 -0700 Subject: Revert "Print SHA-1 in hex for corrupted blocks" This reverts commit bb0cd75a0e1f6760bdf96bd141f3a546ffa45fbc. Broke the 'free' command that deletes a stash. Bug: 36242722 Test: The previously failed incremental applies successfully. Change-Id: I1237cb0a33adfbeea57e0465b629704862ba13aa (cherry picked from commit 90eff6a340f9983792d700df3b1ea0203aced207) --- updater/blockimg.cpp | 143 ++------------------------------------------------- 1 file changed, 3 insertions(+), 140 deletions(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index c2897a83e..03ce4136e 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -66,21 +66,6 @@ struct RangeSet { size_t count; // Limit is INT_MAX. size_t size; std::vector pos; // Actual limit is INT_MAX. - - // Get the block number for the ith(starting from 0) block in the range set. - int get_block(size_t idx) const { - if (idx >= size) { - LOG(ERROR) << "index: " << idx << " is greater than range set size: " << size; - return -1; - } - for (size_t i = 0; i < pos.size(); i += 2) { - if (idx < pos[i + 1] - pos[i]) { - return pos[i] + idx; - } - idx -= (pos[i + 1] - pos[i]); - } - return -1; - } }; static CauseCode failure_type = kNoCause; @@ -456,117 +441,6 @@ static int LoadSrcTgtVersion1(CommandParameters& params, RangeSet& tgt, size_t& return rc; } -// Print the hash in hex for corrupted source blocks (excluding the stashed blocks which is -// handled separately). -static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, - const std::vector& buffer) { - LOG(INFO) << "unexpected contents of source blocks in cmd:\n" << params.cmdline; - if (params.version < 3) { - // TODO handle version 1,2 - LOG(WARNING) << "version number " << params.version << " is not supported to print hashes"; - return; - } - - CHECK(params.tokens[0] == "move" || params.tokens[0] == "bsdiff" || - params.tokens[0] == "imgdiff"); - - size_t pos = 0; - // Command example: - // move [ ] - // bsdiff - // [ ] - if (params.tokens[0] == "move") { - // src_range for move starts at the 4th position. - if (params.tokens.size() < 5) { - LOG(ERROR) << "failed to parse source range in cmd:\n" << params.cmdline; - return; - } - pos = 4; - } else { - // src_range for diff starts at the 7th position. - if (params.tokens.size() < 8) { - LOG(ERROR) << "failed to parse source range in cmd:\n" << params.cmdline; - return; - } - pos = 7; - } - - // Source blocks in stash only, no work to do. - if (params.tokens[pos] == "-") { - return; - } - - RangeSet src = parse_range(params.tokens[pos++]); - - RangeSet locs; - // If there's no stashed blocks, content in the buffer is consecutive and has the same - // order as the source blocks. - if (pos == params.tokens.size()) { - locs.count = 1; - locs.size = src.size; - locs.pos = { 0, src.size }; - } else { - // Otherwise, the next token is the offset of the source blocks in the target range. - // Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> ; - // We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38]; - // this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978. - locs = parse_range(params.tokens[pos++]); - CHECK_EQ(src.size, locs.size); - CHECK_EQ(locs.pos.size() % 2, static_cast(0)); - } - - LOG(INFO) << "printing hash in hex for " << src.size << " source blocks"; - for (size_t i = 0; i < src.size; i++) { - int block_num = src.get_block(i); - CHECK_NE(block_num, -1); - int buffer_index = locs.get_block(i); - CHECK_NE(buffer_index, -1); - CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size()); - - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(buffer.data() + buffer_index * BLOCKSIZE, BLOCKSIZE, digest); - std::string hexdigest = print_sha1(digest); - LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest; - } -} - -// If the calculated hash for the whole stash doesn't match the stash id, print the SHA-1 -// in hex for each block. -static void PrintHashForCorruptedStashedBlocks(const std::string& id, - const std::vector& buffer, - const RangeSet& src) { - LOG(INFO) << "printing hash in hex for stash_id: " << id; - CHECK_EQ(src.size * BLOCKSIZE, buffer.size()); - - for (size_t i = 0; i < src.size; i++) { - int block_num = src.get_block(i); - CHECK_NE(block_num, -1); - - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1(buffer.data() + i * BLOCKSIZE, BLOCKSIZE, digest); - std::string hexdigest = print_sha1(digest); - LOG(INFO) << " block number: " << block_num << ", SHA-1: " << hexdigest; - } -} - -// If the stash file doesn't exist, read the source blocks this stash contains and print the -// SHA-1 for these blocks. -static void PrintHashForMissingStashedBlocks(const std::string& id, int fd) { - if (stash_map.find(id) == stash_map.end()) { - LOG(ERROR) << "No stash saved for id: " << id; - return; - } - - LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id; - const RangeSet& src = stash_map[id]; - std::vector buffer(src.size * BLOCKSIZE); - if (ReadBlocks(src, buffer, fd) == -1) { - LOG(ERROR) << "failed to read source blocks for stash: " << id; - return; - } - PrintHashForCorruptedStashedBlocks(id, buffer, src); -} - static int VerifyBlocks(const std::string& expected, const std::vector& buffer, const size_t blocks, bool printerror) { uint8_t digest[SHA_DIGEST_LENGTH]; @@ -699,7 +573,6 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s } if (VerifyBlocks(id, buffer, src.size, true) != 0) { LOG(ERROR) << "failed to verify loaded source blocks in stash map."; - PrintHashForCorruptedStashedBlocks(id, buffer, src); return -1; } return 0; @@ -724,7 +597,6 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s if (res == -1) { if (errno != ENOENT || printnoent) { PLOG(ERROR) << "stat \"" << fn << "\" failed"; - PrintHashForMissingStashedBlocks(id, params.fd); } return -1; } @@ -752,13 +624,6 @@ static int LoadStash(CommandParameters& params, const std::string& base, const s if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) { LOG(ERROR) << "unexpected contents in " << fn; - if (stash_map.find(id) == stash_map.end()) { - LOG(ERROR) << "failed to find source blocks number for stash " << id - << " when executing command: " << params.cmdname; - } else { - const RangeSet& src = stash_map[id]; - PrintHashForCorruptedStashedBlocks(id, buffer, src); - } DeleteFile(fn, nullptr); return -1; } @@ -930,7 +795,6 @@ static int SaveStash(CommandParameters& params, const std::string& base, return -1; } blocks = src.size; - stash_map[id] = src; if (usehash && VerifyBlocks(id, buffer, blocks, true) != 0) { // Source blocks have unexpected contents. If we actually need this @@ -941,8 +805,9 @@ static int SaveStash(CommandParameters& params, const std::string& base, return 0; } - // In verify mode, we don't need to stash any blocks. + // In verify mode, save source range_set instead of stashing blocks. if (!params.canwrite && usehash) { + stash_map[id] = src; return 0; } @@ -1161,8 +1026,6 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& // Valid source data not available, update cannot be resumed LOG(ERROR) << "partition has unexpected contents"; - PrintHashForCorruptedSourceBlocks(params, params.buffer); - params.isunresumable = true; return -1; @@ -1231,7 +1094,7 @@ static int PerformCommandFree(CommandParameters& params) { const std::string& id = params.tokens[params.cpos++]; - if (stash_map.find(id) != stash_map.end()) { + if (!params.canwrite && stash_map.find(id) != stash_map.end()) { stash_map.erase(id); return 0; } -- cgit v1.2.3 From 91bc6ddb4ee4c71276856e7825875a2a906fbd23 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 23 Mar 2017 06:34:20 -0700 Subject: updater: Fix the broken case for apply_patch_check(). It's valid to provide only 1 argument to apply_patch_check(). We shouldn't fail the argument parsing. Bug: 36541737 Test: recovery_component_test passes. Test: recovery_component_test captures the failure without the fix. Test: The previously failed update applies successfully. Change-Id: Iee4c54ed33b877fc4885945b085341ec5c64f663 (cherry picked from commit db56eb073e595a862f620e244e23471665f63527) --- tests/component/updater_test.cpp | 49 ++++++++++++++++++++++++++++++++++++++++ updater/install.cpp | 2 +- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index ef121a973..5652ddf46 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -127,6 +127,55 @@ TEST_F(UpdaterTest, sha1_check) { expect(nullptr, "sha1_check()", kArgsParsingFailure); } +TEST_F(UpdaterTest, apply_patch_check) { + // Zero-argument is not valid. + expect(nullptr, "apply_patch_check()", kArgsParsingFailure); + + // File not found. + expect("", "apply_patch_check(\"/doesntexist\")", kNoCause); + + std::string src_file = from_testdata_base("old.file"); + std::string src_content; + ASSERT_TRUE(android::base::ReadFileToString(src_file, &src_content)); + size_t src_size = src_content.size(); + std::string src_hash = get_sha1(src_content); + + // One-argument with EMMC:file:size:sha1 should pass the check. + std::string filename = android::base::Join( + std::vector{ "EMMC", src_file, std::to_string(src_size), src_hash }, ":"); + std::string cmd = "apply_patch_check(\"" + filename + "\")"; + expect("t", cmd.c_str(), kNoCause); + + // EMMC:file:(size-1):sha1:(size+1):sha1 should fail the check. + std::string filename_bad = android::base::Join( + std::vector{ "EMMC", src_file, std::to_string(src_size - 1), src_hash, + std::to_string(src_size + 1), src_hash }, + ":"); + cmd = "apply_patch_check(\"" + filename_bad + "\")"; + expect("", cmd.c_str(), kNoCause); + + // EMMC:file:(size-1):sha1:size:sha1:(size+1):sha1 should pass the check. + filename_bad = + android::base::Join(std::vector{ "EMMC", src_file, std::to_string(src_size - 1), + src_hash, std::to_string(src_size), src_hash, + std::to_string(src_size + 1), src_hash }, + ":"); + cmd = "apply_patch_check(\"" + filename_bad + "\")"; + expect("t", cmd.c_str(), kNoCause); + + // Multiple arguments. + cmd = "apply_patch_check(\"" + filename + "\", \"wrong_sha1\", \"wrong_sha2\")"; + expect("", cmd.c_str(), kNoCause); + + cmd = "apply_patch_check(\"" + filename + "\", \"wrong_sha1\", \"" + src_hash + + "\", \"wrong_sha2\")"; + expect("t", cmd.c_str(), kNoCause); + + cmd = "apply_patch_check(\"" + filename_bad + "\", \"wrong_sha1\", \"" + src_hash + + "\", \"wrong_sha2\")"; + expect("t", cmd.c_str(), kNoCause); +} + TEST_F(UpdaterTest, file_getprop) { // file_getprop() expects two arguments. expect(nullptr, "file_getprop()", kArgsParsingFailure); diff --git a/updater/install.cpp b/updater/install.cpp index c9a0270ec..f91f3fc9f 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -691,7 +691,7 @@ Value* ApplyPatchCheckFn(const char* name, State* state, const std::vector sha1s; - if (!ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) { + if (argv.size() > 1 && !ReadArgs(state, argv, &sha1s, 1, argv.size() - 1)) { return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); } int result = applypatch_check(filename.c_str(), sha1s); -- cgit v1.2.3 From f18e639ce885e816f7cb2a46b209135ddf717db6 Mon Sep 17 00:00:00 2001 From: Bowgo Tsai Date: Mon, 27 Mar 2017 17:47:21 +0000 Subject: Revert "libbootloader_message: use different fstab paths for normal/recovery boot" This reverts commit 37bd44174bf2511320961f3f8cd1f698a1c72b66. The logic here is better to be moved into fs_mgr, not fs_mgr clients. Bug: 35811655 Bug: 36502022 Change-Id: Iae79bd8f7131516ad223f3323f1bc1d805206d51 Test: normal boot sailfish, go to Settings > System & tap on "Factory Data reset" Test: recovery boot sailfish (cherry picked from commit 4508f2388419dc15c7d9a9c0ebb82d9e5d67c993) --- bootloader_message/bootloader_message.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index d17e055bb..d8086be28 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -31,13 +30,8 @@ #include static std::string get_misc_blk_device(std::string* err) { - std::unique_ptr fstab(nullptr, fs_mgr_free_fstab); - // Use different fstab paths for normal boot and recovery boot, respectively - if (access("/sbin/recovery", F_OK) == 0) { - fstab.reset(fs_mgr_read_fstab_with_dt("/etc/recovery.fstab")); - } else { - fstab.reset(fs_mgr_read_fstab_default()); - } + std::unique_ptr fstab(fs_mgr_read_fstab_default(), + fs_mgr_free_fstab); if (!fstab) { *err = "failed to read default fstab"; return ""; -- cgit v1.2.3