Merge "Support starting fuse from a block map"

am: 090b62832f

Change-Id: I7f90b15d7ab0cf00b66ac242954490112cbd661d
This commit is contained in:
Tianjie Xu 2019-06-21 10:41:10 -07:00 committed by android-build-merger
commit b96f2bf618
9 changed files with 88 additions and 33 deletions

View file

@ -49,6 +49,11 @@ FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t blo
fuse_block_size_ = block_size;
}
std::unique_ptr<FuseDataProvider> FuseFileDataProvider::CreateFromFile(const std::string& path,
uint32_t block_size) {
return std::make_unique<FuseFileDataProvider>(path, block_size);
}
bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const {
uint64_t offset = static_cast<uint64_t>(start_block) * fuse_block_size_;
@ -127,7 +132,7 @@ bool FuseBlockDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch
return true;
}
std::unique_ptr<FuseBlockDataProvider> FuseBlockDataProvider::CreateFromBlockMap(
std::unique_ptr<FuseDataProvider> FuseBlockDataProvider::CreateFromBlockMap(
const std::string& block_map_path, uint32_t fuse_block_size) {
auto block_map = BlockMapData::ParseBlockMapFile(block_map_path);
if (!block_map) {

View file

@ -44,6 +44,8 @@ class FuseDataProvider {
virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const = 0;
virtual bool Valid() const = 0;
virtual void Close() {}
protected:
@ -60,10 +62,13 @@ class FuseFileDataProvider : public FuseDataProvider {
public:
FuseFileDataProvider(const std::string& path, uint32_t block_size);
static std::unique_ptr<FuseDataProvider> CreateFromFile(const std::string& path,
uint32_t block_size);
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const override;
bool Valid() const {
bool Valid() const override {
return fd_ != -1;
}
@ -78,14 +83,20 @@ class FuseFileDataProvider : public FuseDataProvider {
class FuseBlockDataProvider : public FuseDataProvider {
public:
// Constructs the fuse provider from the block map.
static std::unique_ptr<FuseBlockDataProvider> CreateFromBlockMap(
const std::string& block_map_path, uint32_t fuse_block_size);
static std::unique_ptr<FuseDataProvider> CreateFromBlockMap(const std::string& block_map_path,
uint32_t fuse_block_size);
RangeSet ranges() const {
return ranges_;
}
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const override;
bool Valid() const override {
return fd_ != -1;
}
void Close() override;
private:

View file

@ -61,7 +61,7 @@ cc_library_static {
srcs: [
"adb_install.cpp",
"asn1_decoder.cpp",
"fuse_sdcard_install.cpp",
"fuse_install.cpp",
"install.cpp",
"package.cpp",
"verifier.cpp",

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
#include "install/fuse_sdcard_install.h"
#include "install/fuse_install.h"
#include <dirent.h>
#include <signal.h>
@ -27,6 +27,7 @@
#include <algorithm>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#include <android-base/logging.h>
@ -74,7 +75,8 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco
// Skip "." and ".." entries.
if (name == "." || name == "..") continue;
dirs.push_back(name + "/");
} else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
} else if (de->d_type == DT_REG && (android::base::EndsWithIgnoreCase(name, ".zip") ||
android::base::EndsWithIgnoreCase(name, ".map"))) {
entries.push_back(name);
}
}
@ -119,42 +121,37 @@ static std::string BrowseDirectory(const std::string& path, Device* device, Reco
// Unreachable.
}
static bool StartSdcardFuse(const std::string& path) {
auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536);
static bool StartInstallPackageFuse(std::string_view path) {
if (path.empty()) {
return false;
}
constexpr auto FUSE_BLOCK_SIZE = 65536;
bool is_block_map = android::base::ConsumePrefix(&path, "@");
auto file_data_reader =
is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE)
: FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE);
if (!file_data_reader->Valid()) {
return false;
}
if (android::base::StartsWith(path, SDCARD_ROOT)) {
// The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
// that our open file continues to work but new references see it as unmounted.
umount2("/sdcard", MNT_DETACH);
umount2(SDCARD_ROOT, MNT_DETACH);
}
return run_fuse_sideload(std::move(file_data_reader)) == 0;
}
InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) {
if (ensure_path_mounted(SDCARD_ROOT) != 0) {
LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
return INSTALL_ERROR;
}
std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
if (path.empty()) {
LOG(ERROR) << "\n-- No package file selected.\n";
ensure_path_unmounted(SDCARD_ROOT);
return INSTALL_ERROR;
}
ui->Print("\n-- Install %s ...\n", path.c_str());
SetSdcardUpdateBootloaderMessage();
InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) {
// We used to use fuse in a thread as opposed to a process. Since accessing
// through fuse involves going from kernel to userspace to kernel, it leads
// to deadlock when a page fault occurs. (Bug: 26313124)
pid_t child;
if ((child = fork()) == 0) {
bool status = StartSdcardFuse(path);
bool status = StartInstallPackageFuse(path);
_exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
}
@ -203,6 +200,32 @@ InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui) {
LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
}
return result;
}
InstallResult ApplyFromSdcard(Device* device) {
auto ui = device->GetUI();
if (ensure_path_mounted(SDCARD_ROOT) != 0) {
LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
return INSTALL_ERROR;
}
std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
if (path.empty()) {
LOG(ERROR) << "\n-- No package file selected.\n";
ensure_path_unmounted(SDCARD_ROOT);
return INSTALL_ERROR;
}
// Hint the install function to read from a block map file.
if (android::base::EndsWithIgnoreCase(path, ".map")) {
path = "@" + path;
}
ui->Print("\n-- Install %s ...\n", path.c_str());
SetSdcardUpdateBootloaderMessage();
auto result = InstallWithFuseFromPath(path, ui);
ensure_path_unmounted(SDCARD_ROOT);
return result;
}

View file

@ -16,8 +16,15 @@
#pragma once
#include <string_view>
#include "install/install.h"
#include "recovery_ui/device.h"
#include "recovery_ui/ui.h"
InstallResult ApplyFromSdcard(Device* device, RecoveryUI* ui);
// Starts FUSE with the package from |path| as the data source. And installs the package from
// |FUSE_SIDELOAD_HOST_PATHNAME|. The |path| can point to the location of a package zip file or a
// block map file with the prefix '@'; e.g. /sdcard/package.zip, @/cache/recovery/block.map.
InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui);
InstallResult ApplyFromSdcard(Device* device);

View file

@ -29,6 +29,10 @@ class FuseAdbDataProvider : public FuseDataProvider {
bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
uint32_t start_block) const override;
bool Valid() const override {
return fd_ != -1;
}
private:
// The underlying source to read data from (i.e. the one that talks to the host).
int fd_;

View file

@ -49,7 +49,7 @@
#include "common.h"
#include "fsck_unshare_blocks.h"
#include "install/adb_install.h"
#include "install/fuse_sdcard_install.h"
#include "install/fuse_install.h"
#include "install/install.h"
#include "install/package.h"
#include "install/wipe_data.h"
@ -408,7 +408,7 @@ static Device::BuiltinAction PromptAndWait(Device* device, InstallResult status)
status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action);
} else {
adb = false;
status = ApplyFromSdcard(device, ui);
status = ApplyFromSdcard(device);
}
ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status);

View file

@ -44,7 +44,8 @@ TEST(FuseBlockMapTest, CreateFromBlockMap_smoke) {
ASSERT_TRUE(block_map_data);
ASSERT_EQ(10000, block_map_data->file_size());
ASSERT_EQ(4096, block_map_data->fuse_block_size());
ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }), block_map_data->ranges());
ASSERT_EQ(RangeSet({ { 10, 11 }, { 20, 21 }, { 22, 23 } }),
static_cast<FuseBlockDataProvider*>(block_map_data.get())->ranges());
}
TEST(FuseBlockMapTest, ReadBlockAlignedData_smoke) {

View file

@ -40,6 +40,10 @@ class FuseTestDataProvider : public FuseDataProvider {
bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override {
return true;
}
bool Valid() const override {
return true;
}
};
TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {