Make ext4 userdata checkpoints work with metadata encryption

When both ext4 user data checkpoints and metadata encryption are
enabled, we are creating two stacked dm devices. This had not been
properly thought through or debugged.

Test: Enable metadata encryption on taimen (add
keydirectory=/metadata/vold/metadata_encryption to flags for userdata in
fstab.hardware)
    Unfortunately metadata is not wiped by fastboot -w, so it is
    necessary to rm metadata/vold -rf whenever you wipe data.
    fastboot flashall -w works
    fastboot reboot -w works
    A normal boot works
    Disable checkpoint commits with
    setprop persist.vold.dont_commit_checkpoint 1
    vdc checkpoint startCheckpoint 10
    adb reboot
    wait for device to fully boot then
    adb reboot
    Wait for device to fully boot then
    adb logcat -d | grep Checkpoint shows the rollback in the logs

    This tests encryption on top of checkpoints with commit, encryption
    without checkpoints, and rollback, which seems to be the key cases.

Bug: 135905679
Change-Id: I24387a2943dce28b918c34894f24911b20429be7
This commit is contained in:
Paul Lawrence 2019-06-25 14:36:52 -07:00
parent c0349199db
commit 323959ea27
2 changed files with 88 additions and 34 deletions

View file

@ -908,7 +908,7 @@ class CheckpointManager {
public:
CheckpointManager(int needs_checkpoint = -1) : needs_checkpoint_(needs_checkpoint) {}
bool Update(FstabEntry* entry) {
bool Update(FstabEntry* entry, const std::string& block_device = std::string()) {
if (!entry->fs_mgr_flags.checkpoint_blk && !entry->fs_mgr_flags.checkpoint_fs) {
return true;
}
@ -927,7 +927,7 @@ class CheckpointManager {
return true;
}
if (!UpdateCheckpointPartition(entry)) {
if (!UpdateCheckpointPartition(entry, block_device)) {
LERROR << "Could not set up checkpoint partition, skipping!";
return false;
}
@ -957,7 +957,7 @@ class CheckpointManager {
}
private:
bool UpdateCheckpointPartition(FstabEntry* entry) {
bool UpdateCheckpointPartition(FstabEntry* entry, const std::string& block_device) {
if (entry->fs_mgr_flags.checkpoint_fs) {
if (is_f2fs(entry->fs_type)) {
entry->fs_options += ",checkpoint=disable";
@ -965,39 +965,43 @@ class CheckpointManager {
LERROR << entry->fs_type << " does not implement checkpoints.";
}
} else if (entry->fs_mgr_flags.checkpoint_blk) {
unique_fd fd(TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
PERROR << "Cannot open device " << entry->blk_device;
return false;
}
auto actual_block_device = block_device.empty() ? entry->blk_device : block_device;
if (fs_mgr_find_bow_device(actual_block_device).empty()) {
unique_fd fd(
TEMP_FAILURE_RETRY(open(entry->blk_device.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd < 0) {
PERROR << "Cannot open device " << entry->blk_device;
return false;
}
uint64_t size = get_block_device_size(fd) / 512;
if (!size) {
PERROR << "Cannot get device size";
return false;
}
uint64_t size = get_block_device_size(fd) / 512;
if (!size) {
PERROR << "Cannot get device size";
return false;
}
android::dm::DmTable table;
if (!table.AddTarget(
std::make_unique<android::dm::DmTargetBow>(0, size, entry->blk_device))) {
LERROR << "Failed to add bow target";
return false;
}
android::dm::DmTable table;
if (!table.AddTarget(std::make_unique<android::dm::DmTargetBow>(
0, size, entry->blk_device))) {
LERROR << "Failed to add bow target";
return false;
}
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.CreateDevice("bow", table)) {
PERROR << "Failed to create bow device";
return false;
}
DeviceMapper& dm = DeviceMapper::Instance();
if (!dm.CreateDevice("bow", table)) {
PERROR << "Failed to create bow device";
return false;
}
std::string name;
if (!dm.GetDmDevicePathByName("bow", &name)) {
PERROR << "Failed to get bow device name";
return false;
}
std::string name;
if (!dm.GetDmDevicePathByName("bow", &name)) {
PERROR << "Failed to get bow device name";
return false;
}
device_map_[name] = entry->blk_device;
entry->blk_device = name;
device_map_[name] = entry->blk_device;
entry->blk_device = name;
}
}
return true;
}
@ -1007,6 +1011,50 @@ class CheckpointManager {
std::map<std::string, std::string> device_map_;
};
std::string fs_mgr_find_bow_device(const std::string& block_device) {
if (block_device.substr(0, 5) != "/dev/") {
LOG(ERROR) << "Expected block device, got " << block_device;
return std::string();
}
std::string sys_dir = std::string("/sys/") + block_device.substr(5);
for (;;) {
std::string name;
if (!android::base::ReadFileToString(sys_dir + "/dm/name", &name)) {
PLOG(ERROR) << block_device << " is not dm device";
return std::string();
}
if (name == "bow\n") return sys_dir;
std::string slaves = sys_dir + "/slaves";
std::unique_ptr<DIR, decltype(&closedir)> directory(opendir(slaves.c_str()), closedir);
if (!directory) {
PLOG(ERROR) << "Can't open slave directory " << slaves;
return std::string();
}
int count = 0;
for (dirent* entry = readdir(directory.get()); entry; entry = readdir(directory.get())) {
if (entry->d_type != DT_LNK) continue;
if (count == 1) {
LOG(ERROR) << "Too many slaves in " << slaves;
return std::string();
}
++count;
sys_dir = std::string("/sys/block/") + entry->d_name;
}
if (count != 1) {
LOG(ERROR) << "No slave in " << slaves;
return std::string();
}
}
}
static bool IsMountPointMounted(const std::string& mount_point) {
// Check if this is already mounted.
Fstab fstab;
@ -1144,7 +1192,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
}
encryptable = status;
if (status == FS_MGR_MNTALL_DEV_NEEDS_METADATA_ENCRYPTION) {
if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.mount_point})) {
if (!call_vdc({"cryptfs", "encryptFstab", attempted_entry.blk_device,
attempted_entry.mount_point})) {
LERROR << "Encryption failed";
return FS_MGR_MNTALL_FAIL;
}
@ -1215,7 +1264,8 @@ int fs_mgr_mount_all(Fstab* fstab, int mount_mode) {
encryptable = FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED;
} else if (mount_errno != EBUSY && mount_errno != EACCES &&
should_use_metadata_encryption(attempted_entry)) {
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.mount_point})) {
if (!call_vdc({"cryptfs", "mountFstab", attempted_entry.blk_device,
attempted_entry.mount_point})) {
++error_count;
}
encryptable = FS_MGR_MNTALL_DEV_IS_METADATA_ENCRYPTED;
@ -1345,7 +1395,7 @@ static int fs_mgr_do_mount_helper(Fstab* fstab, const std::string& n_name,
}
}
if (!checkpoint_manager.Update(&fstab_entry)) {
if (!checkpoint_manager.Update(&fstab_entry, n_blk_device)) {
LERROR << "Could not set up checkpoint partition, skipping!";
continue;
}

View file

@ -104,3 +104,7 @@ enum FsMgrUmountStatus : int {
// fs_mgr_umount_all() is the reverse of fs_mgr_mount_all. In particular,
// it destroys verity devices from device mapper after the device is unmounted.
int fs_mgr_umount_all(android::fs_mgr::Fstab* fstab);
// Finds the dm_bow device on which this block device is stacked, or returns
// empty string
std::string fs_mgr_find_bow_device(const std::string& block_device);