Merge changes from topic "encryptinplace-cleanup"
* changes: Refactor EncryptInplace.cpp Correctly calculate tot_used_blocks on ext4 with uninit_bg Fix memory leak of f2fs_info Remove special handling for missing crypto_blkdev Check return value of create_crypto_blk_dev() Remove unused support for partial encryption
This commit is contained in:
commit
91e4f1dd76
4 changed files with 332 additions and 718 deletions
|
@ -20,614 +20,356 @@
|
|||
#include <ext4_utils/ext4_utils.h>
|
||||
#include <f2fs_sparseblock.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
// HORRIBLE HACK, FIXME
|
||||
#include "cryptfs.h"
|
||||
|
||||
// FIXME horrible cut-and-paste code
|
||||
static inline int unix_read(int fd, void* buff, int len) {
|
||||
return TEMP_FAILURE_RETRY(read(fd, buff, len));
|
||||
}
|
||||
|
||||
static inline int unix_write(int fd, const void* buff, int len) {
|
||||
return TEMP_FAILURE_RETRY(write(fd, buff, len));
|
||||
}
|
||||
|
||||
#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
|
||||
|
||||
/* aligned 32K writes tends to make flash happy.
|
||||
* SD card association recommends it.
|
||||
*/
|
||||
#define BLOCKS_AT_A_TIME 8
|
||||
|
||||
struct encryptGroupsData {
|
||||
int realfd;
|
||||
int cryptofd;
|
||||
off64_t numblocks;
|
||||
off64_t one_pct, cur_pct, new_pct;
|
||||
off64_t blocks_already_done, tot_numblocks;
|
||||
off64_t used_blocks_already_done, tot_used_blocks;
|
||||
const char* real_blkdev;
|
||||
const char* crypto_blkdev;
|
||||
int count;
|
||||
off64_t offset;
|
||||
char* buffer;
|
||||
off64_t last_written_sector;
|
||||
int completed;
|
||||
time_t time_started;
|
||||
int remaining_time;
|
||||
bool set_progress_properties;
|
||||
enum EncryptInPlaceError {
|
||||
kSuccess,
|
||||
kFailed,
|
||||
kFilesystemNotFound,
|
||||
};
|
||||
|
||||
static void update_progress(struct encryptGroupsData* data, int is_used) {
|
||||
data->blocks_already_done++;
|
||||
static uint64_t round_up(uint64_t val, size_t amount) {
|
||||
if (val % amount) val += amount - (val % amount);
|
||||
return val;
|
||||
}
|
||||
|
||||
if (is_used) {
|
||||
data->used_blocks_already_done++;
|
||||
}
|
||||
if (data->tot_used_blocks) {
|
||||
data->new_pct = data->used_blocks_already_done / data->one_pct;
|
||||
class InPlaceEncrypter {
|
||||
public:
|
||||
bool EncryptInPlace(const std::string& crypto_blkdev, const std::string& real_blkdev,
|
||||
uint64_t nr_sec, bool set_progress_properties);
|
||||
bool ProcessUsedBlock(uint64_t block_num);
|
||||
|
||||
private:
|
||||
// aligned 32K writes tends to make flash happy.
|
||||
// SD card association recommends it.
|
||||
static const size_t kIOBufferSize = 32768;
|
||||
|
||||
// Avoid spamming the logs. Print the "Encrypting blocks" log message once
|
||||
// every 10000 blocks (which is usually every 40 MB or so), and once at the end.
|
||||
static const int kLogInterval = 10000;
|
||||
|
||||
std::string DescribeFilesystem();
|
||||
void InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt, uint64_t total_blocks,
|
||||
unsigned int block_size);
|
||||
void UpdateProgress(size_t blocks, bool done);
|
||||
bool EncryptPendingData();
|
||||
bool DoEncryptInPlace();
|
||||
|
||||
// ext4 methods
|
||||
bool ReadExt4BlockBitmap(uint32_t group, uint8_t* buf);
|
||||
uint64_t FirstBlockInGroup(uint32_t group);
|
||||
uint32_t NumBlocksInGroup(uint32_t group);
|
||||
uint32_t NumBaseMetaBlocksInGroup(uint64_t group);
|
||||
EncryptInPlaceError EncryptInPlaceExt4();
|
||||
|
||||
// f2fs methods
|
||||
EncryptInPlaceError EncryptInPlaceF2fs();
|
||||
|
||||
std::string real_blkdev_;
|
||||
std::string crypto_blkdev_;
|
||||
uint64_t nr_sec_;
|
||||
bool set_progress_properties_;
|
||||
|
||||
android::base::unique_fd realfd_;
|
||||
android::base::unique_fd cryptofd_;
|
||||
|
||||
time_t time_started_;
|
||||
int remaining_time_;
|
||||
|
||||
std::string fs_type_;
|
||||
uint64_t blocks_done_;
|
||||
uint64_t blocks_to_encrypt_;
|
||||
unsigned int block_size_;
|
||||
unsigned int cur_pct_;
|
||||
|
||||
std::vector<uint8_t> io_buffer_;
|
||||
uint64_t first_pending_block_;
|
||||
size_t blocks_pending_;
|
||||
};
|
||||
|
||||
std::string InPlaceEncrypter::DescribeFilesystem() {
|
||||
if (fs_type_.empty())
|
||||
return "full block device " + real_blkdev_;
|
||||
else
|
||||
return fs_type_ + " filesystem on " + real_blkdev_;
|
||||
}
|
||||
|
||||
// Finishes initializing the encrypter, now that the filesystem details are known.
|
||||
void InPlaceEncrypter::InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt,
|
||||
uint64_t total_blocks, unsigned int block_size) {
|
||||
fs_type_ = fs_type;
|
||||
blocks_done_ = 0;
|
||||
blocks_to_encrypt_ = blocks_to_encrypt;
|
||||
block_size_ = block_size;
|
||||
cur_pct_ = 0;
|
||||
|
||||
// Allocate the I/O buffer. kIOBufferSize should always be a multiple of
|
||||
// the filesystem block size, but round it up just in case.
|
||||
io_buffer_.resize(round_up(kIOBufferSize, block_size));
|
||||
first_pending_block_ = 0;
|
||||
blocks_pending_ = 0;
|
||||
|
||||
LOG(INFO) << "Encrypting " << DescribeFilesystem() << " in-place via " << crypto_blkdev_;
|
||||
LOG(INFO) << blocks_to_encrypt << " blocks (" << (blocks_to_encrypt * block_size) / 1000000
|
||||
<< " MB) of " << total_blocks << " blocks are in-use";
|
||||
}
|
||||
|
||||
void InPlaceEncrypter::UpdateProgress(size_t blocks, bool done) {
|
||||
// A log message already got printed for blocks_done_ if one was due, so the
|
||||
// next message will be due at the *next* block rounded up to kLogInterval.
|
||||
uint64_t blocks_next_msg = round_up(blocks_done_ + 1, kLogInterval);
|
||||
|
||||
blocks_done_ += blocks;
|
||||
|
||||
// Ensure that a log message gets printed at the end, but not if one was
|
||||
// already printed due to the block count being a multiple of kLogInterval.
|
||||
// E.g. we want to show "50000 of 50327" and then "50327 of "50327", but not
|
||||
// "50000 of 50000" and then redundantly "50000 of 50000" again.
|
||||
if (done && blocks_done_ % kLogInterval != 0) blocks_next_msg = blocks_done_;
|
||||
|
||||
if (blocks_done_ >= blocks_next_msg)
|
||||
LOG(DEBUG) << "Encrypted " << blocks_next_msg << " of " << blocks_to_encrypt_ << " blocks";
|
||||
|
||||
if (!set_progress_properties_) return;
|
||||
|
||||
uint64_t new_pct;
|
||||
if (done) {
|
||||
new_pct = 100;
|
||||
} else {
|
||||
data->new_pct = data->blocks_already_done / data->one_pct;
|
||||
new_pct = (blocks_done_ * 100) / std::max<uint64_t>(blocks_to_encrypt_, 1);
|
||||
new_pct = std::min<uint64_t>(new_pct, 99);
|
||||
}
|
||||
if (new_pct > cur_pct_) {
|
||||
cur_pct_ = new_pct;
|
||||
android::base::SetProperty("vold.encrypt_progress", std::to_string(new_pct));
|
||||
}
|
||||
|
||||
if (!data->set_progress_properties) return;
|
||||
|
||||
if (data->new_pct > data->cur_pct) {
|
||||
char buf[8];
|
||||
data->cur_pct = data->new_pct;
|
||||
snprintf(buf, sizeof(buf), "%" PRId64, data->cur_pct);
|
||||
android::base::SetProperty("vold.encrypt_progress", buf);
|
||||
}
|
||||
|
||||
if (data->cur_pct >= 5) {
|
||||
if (cur_pct_ >= 5) {
|
||||
struct timespec time_now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &time_now)) {
|
||||
LOG(WARNING) << "Error getting time";
|
||||
PLOG(WARNING) << "Error getting time while updating encryption progress";
|
||||
} else {
|
||||
double elapsed_time = difftime(time_now.tv_sec, data->time_started);
|
||||
off64_t remaining_blocks = data->tot_used_blocks - data->used_blocks_already_done;
|
||||
int remaining_time =
|
||||
(int)(elapsed_time * remaining_blocks / data->used_blocks_already_done);
|
||||
double elapsed_time = difftime(time_now.tv_sec, time_started_);
|
||||
|
||||
uint64_t remaining_blocks = 0;
|
||||
if (blocks_done_ < blocks_to_encrypt_)
|
||||
remaining_blocks = blocks_to_encrypt_ - blocks_done_;
|
||||
|
||||
int remaining_time = 0;
|
||||
if (blocks_done_ != 0)
|
||||
remaining_time = (int)(elapsed_time * remaining_blocks / blocks_done_);
|
||||
|
||||
// Change time only if not yet set, lower, or a lot higher for
|
||||
// best user experience
|
||||
if (data->remaining_time == -1 || remaining_time < data->remaining_time ||
|
||||
remaining_time > data->remaining_time + 60) {
|
||||
char buf[8];
|
||||
snprintf(buf, sizeof(buf), "%d", remaining_time);
|
||||
android::base::SetProperty("vold.encrypt_time_remaining", buf);
|
||||
data->remaining_time = remaining_time;
|
||||
if (remaining_time_ == -1 || remaining_time < remaining_time_ ||
|
||||
remaining_time > remaining_time_ + 60) {
|
||||
remaining_time_ = remaining_time;
|
||||
android::base::SetProperty("vold.encrypt_time_remaining",
|
||||
std::to_string(remaining_time));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void log_progress(struct encryptGroupsData const* data, bool completed) {
|
||||
// Precondition - if completed data = 0 else data != 0
|
||||
bool InPlaceEncrypter::EncryptPendingData() {
|
||||
if (blocks_pending_ == 0) return true;
|
||||
|
||||
// Track progress so we can skip logging blocks
|
||||
static off64_t offset = -1;
|
||||
ssize_t bytes = blocks_pending_ * block_size_;
|
||||
uint64_t offset = first_pending_block_ * block_size_;
|
||||
|
||||
// Need to close existing 'Encrypting from' log?
|
||||
if (completed || (offset != -1 && data->offset != offset)) {
|
||||
LOG(INFO) << "Encrypted to sector " << offset / info.block_size * CRYPT_SECTOR_SIZE;
|
||||
offset = -1;
|
||||
if (pread64(realfd_, &io_buffer_[0], bytes, offset) != bytes) {
|
||||
PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev_ << " for inplace encrypt";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Need to start new 'Encrypting from' log?
|
||||
if (!completed && offset != data->offset) {
|
||||
LOG(INFO) << "Encrypting from sector " << data->offset / info.block_size * CRYPT_SECTOR_SIZE;
|
||||
if (pwrite64(cryptofd_, &io_buffer_[0], bytes, offset) != bytes) {
|
||||
PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev_ << " for inplace encrypt";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update offset
|
||||
if (!completed) {
|
||||
offset = data->offset + (off64_t)data->count * info.block_size;
|
||||
}
|
||||
UpdateProgress(blocks_pending_, false);
|
||||
|
||||
blocks_pending_ = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int flush_outstanding_data(struct encryptGroupsData* data) {
|
||||
if (data->count == 0) {
|
||||
return 0;
|
||||
bool InPlaceEncrypter::ProcessUsedBlock(uint64_t block_num) {
|
||||
// Flush if the amount of pending data has reached the I/O buffer size, if
|
||||
// there's a gap between the pending blocks and the next block (due to
|
||||
// block(s) not being used by the filesystem and thus not needing
|
||||
// encryption), or if the next block will be aligned to the I/O buffer size.
|
||||
if (blocks_pending_ * block_size_ == io_buffer_.size() ||
|
||||
block_num != first_pending_block_ + blocks_pending_ ||
|
||||
(block_num * block_size_) % io_buffer_.size() == 0) {
|
||||
if (!EncryptPendingData()) return false;
|
||||
first_pending_block_ = block_num;
|
||||
}
|
||||
blocks_pending_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reads the block bitmap for block group |group| into |buf|.
|
||||
bool InPlaceEncrypter::ReadExt4BlockBitmap(uint32_t group, uint8_t* buf) {
|
||||
uint64_t offset = (uint64_t)aux_info.bg_desc[group].bg_block_bitmap * info.block_size;
|
||||
if (pread64(realfd_, buf, info.block_size, offset) != (ssize_t)info.block_size) {
|
||||
PLOG(ERROR) << "Failed to read block bitmap for block group " << group;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t InPlaceEncrypter::FirstBlockInGroup(uint32_t group) {
|
||||
return aux_info.first_data_block + (group * (uint64_t)info.blocks_per_group);
|
||||
}
|
||||
|
||||
uint32_t InPlaceEncrypter::NumBlocksInGroup(uint32_t group) {
|
||||
uint64_t remaining = aux_info.len_blocks - FirstBlockInGroup(group);
|
||||
return std::min<uint64_t>(info.blocks_per_group, remaining);
|
||||
}
|
||||
|
||||
// In block groups with an uninitialized block bitmap, we only need to encrypt
|
||||
// the backup superblock and the block group descriptors (if they are present).
|
||||
uint32_t InPlaceEncrypter::NumBaseMetaBlocksInGroup(uint64_t group) {
|
||||
if (!ext4_bg_has_super_block(group)) return 0;
|
||||
return 1 + aux_info.bg_desc_blocks;
|
||||
}
|
||||
|
||||
EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceExt4() {
|
||||
if (setjmp(setjmp_env)) // NOLINT
|
||||
return kFilesystemNotFound;
|
||||
|
||||
if (read_ext(realfd_, 0) != 0) return kFilesystemNotFound;
|
||||
|
||||
LOG(DEBUG) << "ext4 filesystem has " << aux_info.groups << " block groups";
|
||||
|
||||
uint64_t blocks_to_encrypt = 0;
|
||||
for (uint32_t group = 0; group < aux_info.groups; group++) {
|
||||
if (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT)
|
||||
blocks_to_encrypt += NumBaseMetaBlocksInGroup(group);
|
||||
else
|
||||
blocks_to_encrypt +=
|
||||
(NumBlocksInGroup(group) - aux_info.bg_desc[group].bg_free_blocks_count);
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset;
|
||||
InitFs("ext4", blocks_to_encrypt, aux_info.len_blocks, info.block_size);
|
||||
|
||||
if (pread64(data->realfd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
|
||||
LOG(ERROR) << "Error reading real_blkdev " << data->real_blkdev << " for inplace encrypt";
|
||||
return -1;
|
||||
// Encrypt each block group.
|
||||
std::vector<uint8_t> block_bitmap(info.block_size);
|
||||
for (uint32_t group = 0; group < aux_info.groups; group++) {
|
||||
if (!ReadExt4BlockBitmap(group, &block_bitmap[0])) return kFailed;
|
||||
|
||||
uint64_t first_block_num = FirstBlockInGroup(group);
|
||||
bool uninit = (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT);
|
||||
uint32_t block_count = uninit ? NumBaseMetaBlocksInGroup(group) : NumBlocksInGroup(group);
|
||||
|
||||
// Encrypt each used block in the block group.
|
||||
for (uint32_t i = 0; i < block_count; i++) {
|
||||
if (uninit || bitmap_get_bit(&block_bitmap[0], i))
|
||||
ProcessUsedBlock(first_block_num + i);
|
||||
}
|
||||
}
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
if (pwrite64(data->cryptofd, data->buffer, info.block_size * data->count, data->offset) <= 0) {
|
||||
LOG(ERROR) << "Error writing crypto_blkdev " << data->crypto_blkdev
|
||||
<< " for inplace encrypt";
|
||||
return -1;
|
||||
} else {
|
||||
log_progress(data, false);
|
||||
}
|
||||
|
||||
data->count = 0;
|
||||
data->last_written_sector =
|
||||
(data->offset + data->count) / info.block_size * CRYPT_SECTOR_SIZE - 1;
|
||||
static int encrypt_f2fs_block(uint64_t block_num, void* _encrypter) {
|
||||
InPlaceEncrypter* encrypter = reinterpret_cast<InPlaceEncrypter*>(_encrypter);
|
||||
if (!encrypter->ProcessUsedBlock(block_num)) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int encrypt_groups(struct encryptGroupsData* data) {
|
||||
unsigned int i;
|
||||
u8* block_bitmap = 0;
|
||||
unsigned int block;
|
||||
off64_t ret;
|
||||
int rc = -1;
|
||||
EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceF2fs() {
|
||||
std::unique_ptr<struct f2fs_info, void (*)(struct f2fs_info*)> fs_info(
|
||||
generate_f2fs_info(realfd_), free_f2fs_info);
|
||||
if (!fs_info) return kFilesystemNotFound;
|
||||
|
||||
data->buffer = (char*)malloc(info.block_size * BLOCKS_AT_A_TIME);
|
||||
if (!data->buffer) {
|
||||
LOG(ERROR) << "Failed to allocate crypto buffer";
|
||||
goto errout;
|
||||
}
|
||||
|
||||
block_bitmap = (u8*)malloc(info.block_size);
|
||||
if (!block_bitmap) {
|
||||
LOG(ERROR) << "failed to allocate block bitmap";
|
||||
goto errout;
|
||||
}
|
||||
|
||||
for (i = 0; i < aux_info.groups; ++i) {
|
||||
LOG(INFO) << "Encrypting group " << i;
|
||||
|
||||
u32 first_block = aux_info.first_data_block + i * info.blocks_per_group;
|
||||
u32 block_count = std::min(info.blocks_per_group, (u32)(aux_info.len_blocks - first_block));
|
||||
|
||||
off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap;
|
||||
|
||||
ret = pread64(data->realfd, block_bitmap, info.block_size, offset);
|
||||
if (ret != (int)info.block_size) {
|
||||
LOG(ERROR) << "failed to read all of block group bitmap " << i;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
offset = (u64)info.block_size * first_block;
|
||||
|
||||
data->count = 0;
|
||||
|
||||
for (block = 0; block < block_count; block++) {
|
||||
int used;
|
||||
|
||||
if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT) {
|
||||
// In block groups with an uninitialized block bitmap, we only
|
||||
// need to encrypt the backup superblock (if one is present).
|
||||
used = (ext4_bg_has_super_block(i) && block < 1 + aux_info.bg_desc_blocks);
|
||||
} else {
|
||||
used = bitmap_get_bit(block_bitmap, block);
|
||||
}
|
||||
|
||||
update_progress(data, used);
|
||||
if (used) {
|
||||
if (data->count == 0) {
|
||||
data->offset = offset;
|
||||
}
|
||||
data->count++;
|
||||
} else {
|
||||
if (flush_outstanding_data(data)) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
offset += info.block_size;
|
||||
|
||||
/* Write data if we are aligned or buffer size reached */
|
||||
if (offset % (info.block_size * BLOCKS_AT_A_TIME) == 0 ||
|
||||
data->count == BLOCKS_AT_A_TIME) {
|
||||
if (flush_outstanding_data(data)) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (flush_outstanding_data(data)) {
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
data->completed = 1;
|
||||
rc = 0;
|
||||
|
||||
errout:
|
||||
log_progress(0, true);
|
||||
free(data->buffer);
|
||||
free(block_bitmap);
|
||||
return rc;
|
||||
InitFs("f2fs", get_num_blocks_used(fs_info.get()), fs_info->total_blocks, fs_info->block_size);
|
||||
if (run_on_used_blocks(0, fs_info.get(), encrypt_f2fs_block, this) != 0) return kFailed;
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev,
|
||||
off64_t size, off64_t* size_already_done, off64_t tot_size,
|
||||
off64_t previously_encrypted_upto,
|
||||
bool set_progress_properties) {
|
||||
u32 i;
|
||||
struct encryptGroupsData data;
|
||||
int rc; // Can't initialize without causing warning -Wclobbered
|
||||
int retries = RETRY_MOUNT_ATTEMPTS;
|
||||
bool InPlaceEncrypter::DoEncryptInPlace() {
|
||||
EncryptInPlaceError rc;
|
||||
|
||||
rc = EncryptInPlaceExt4();
|
||||
if (rc != kFilesystemNotFound) return rc == kSuccess;
|
||||
|
||||
rc = EncryptInPlaceF2fs();
|
||||
if (rc != kFilesystemNotFound) return rc == kSuccess;
|
||||
|
||||
LOG(WARNING) << "No recognized filesystem found on " << real_blkdev_
|
||||
<< ". Falling back to encrypting the full block device.";
|
||||
InitFs("", nr_sec_, nr_sec_, 512);
|
||||
for (uint64_t i = 0; i < nr_sec_; i++) {
|
||||
if (!ProcessUsedBlock(i)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InPlaceEncrypter::EncryptInPlace(const std::string& crypto_blkdev,
|
||||
const std::string& real_blkdev, uint64_t nr_sec,
|
||||
bool set_progress_properties) {
|
||||
struct timespec time_started = {0};
|
||||
|
||||
if (previously_encrypted_upto > *size_already_done) {
|
||||
LOG(DEBUG) << "Not fast encrypting since resuming part way through";
|
||||
return -1;
|
||||
}
|
||||
real_blkdev_ = real_blkdev;
|
||||
crypto_blkdev_ = crypto_blkdev;
|
||||
nr_sec_ = nr_sec;
|
||||
set_progress_properties_ = set_progress_properties;
|
||||
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.real_blkdev = real_blkdev;
|
||||
data.crypto_blkdev = crypto_blkdev;
|
||||
data.set_progress_properties = set_progress_properties;
|
||||
|
||||
LOG(DEBUG) << "Opening" << real_blkdev;
|
||||
if ((data.realfd = open(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
|
||||
realfd_.reset(open64(real_blkdev.c_str(), O_RDONLY | O_CLOEXEC));
|
||||
if (realfd_ < 0) {
|
||||
PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
|
||||
rc = -1;
|
||||
goto errout;
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Opening" << crypto_blkdev;
|
||||
// Wait until the block device appears. Re-use the mount retry values since it is reasonable.
|
||||
while ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
|
||||
if (--retries) {
|
||||
PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
|
||||
<< " for ext4 inplace encrypt, retrying";
|
||||
sleep(RETRY_MOUNT_DELAY_SECONDS);
|
||||
} else {
|
||||
PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
|
||||
<< " for ext4 inplace encrypt";
|
||||
rc = ENABLE_INPLACE_ERR_DEV;
|
||||
goto errout;
|
||||
}
|
||||
}
|
||||
|
||||
if (setjmp(setjmp_env)) { // NOLINT
|
||||
LOG(ERROR) << "Reading ext4 extent caused an exception";
|
||||
rc = -1;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
if (read_ext(data.realfd, 0) != 0) {
|
||||
LOG(ERROR) << "Failed to read ext4 extent";
|
||||
rc = -1;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
|
||||
LOG(INFO) << "Encrypting ext4 filesystem in place...";
|
||||
|
||||
data.tot_used_blocks = data.numblocks;
|
||||
for (i = 0; i < aux_info.groups; ++i) {
|
||||
data.tot_used_blocks -= aux_info.bg_desc[i].bg_free_blocks_count;
|
||||
}
|
||||
|
||||
data.one_pct = data.tot_used_blocks / 100;
|
||||
data.cur_pct = 0;
|
||||
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
|
||||
LOG(WARNING) << "Error getting time at start";
|
||||
// Note - continue anyway - we'll run with 0
|
||||
}
|
||||
data.time_started = time_started.tv_sec;
|
||||
data.remaining_time = -1;
|
||||
|
||||
rc = encrypt_groups(&data);
|
||||
if (rc) {
|
||||
LOG(ERROR) << "Error encrypting groups";
|
||||
goto errout;
|
||||
}
|
||||
|
||||
*size_already_done += data.completed ? size : data.last_written_sector;
|
||||
rc = 0;
|
||||
|
||||
errout:
|
||||
close(data.realfd);
|
||||
close(data.cryptofd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void log_progress_f2fs(u64 block, bool completed) {
|
||||
// Precondition - if completed data = 0 else data != 0
|
||||
|
||||
// Track progress so we can skip logging blocks
|
||||
static u64 last_block = (u64)-1;
|
||||
|
||||
// Need to close existing 'Encrypting from' log?
|
||||
if (completed || (last_block != (u64)-1 && block != last_block + 1)) {
|
||||
LOG(INFO) << "Encrypted to block " << last_block;
|
||||
last_block = -1;
|
||||
}
|
||||
|
||||
// Need to start new 'Encrypting from' log?
|
||||
if (!completed && (last_block == (u64)-1 || block != last_block + 1)) {
|
||||
LOG(INFO) << "Encrypting from block " << block;
|
||||
}
|
||||
|
||||
// Update offset
|
||||
if (!completed) {
|
||||
last_block = block;
|
||||
}
|
||||
}
|
||||
|
||||
static int encrypt_one_block_f2fs(u64 pos, void* data) {
|
||||
struct encryptGroupsData* priv_dat = (struct encryptGroupsData*)data;
|
||||
|
||||
priv_dat->blocks_already_done = pos - 1;
|
||||
update_progress(priv_dat, 1);
|
||||
|
||||
off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
|
||||
|
||||
if (pread64(priv_dat->realfd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
|
||||
LOG(ERROR) << "Error reading real_blkdev " << priv_dat->crypto_blkdev
|
||||
<< " for f2fs inplace encrypt";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pwrite64(priv_dat->cryptofd, priv_dat->buffer, CRYPT_INPLACE_BUFSIZE, offset) <= 0) {
|
||||
LOG(ERROR) << "Error writing crypto_blkdev " << priv_dat->crypto_blkdev
|
||||
<< " for f2fs inplace encrypt";
|
||||
return -1;
|
||||
} else {
|
||||
log_progress_f2fs(pos, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev,
|
||||
off64_t size, off64_t* size_already_done, off64_t tot_size,
|
||||
off64_t previously_encrypted_upto,
|
||||
bool set_progress_properties) {
|
||||
struct encryptGroupsData data;
|
||||
struct f2fs_info* f2fs_info = NULL;
|
||||
int rc = ENABLE_INPLACE_ERR_OTHER;
|
||||
struct timespec time_started = {0};
|
||||
|
||||
if (previously_encrypted_upto > *size_already_done) {
|
||||
LOG(DEBUG) << "Not fast encrypting since resuming part way through";
|
||||
return ENABLE_INPLACE_ERR_OTHER;
|
||||
}
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.real_blkdev = real_blkdev;
|
||||
data.crypto_blkdev = crypto_blkdev;
|
||||
data.set_progress_properties = set_progress_properties;
|
||||
data.realfd = -1;
|
||||
data.cryptofd = -1;
|
||||
if ((data.realfd = open64(real_blkdev, O_RDWR | O_CLOEXEC)) < 0) {
|
||||
PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for f2fs inplace encrypt";
|
||||
goto errout;
|
||||
}
|
||||
if ((data.cryptofd = open64(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
|
||||
PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev
|
||||
<< " for f2fs inplace encrypt";
|
||||
rc = ENABLE_INPLACE_ERR_DEV;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
f2fs_info = generate_f2fs_info(data.realfd);
|
||||
if (!f2fs_info) goto errout;
|
||||
|
||||
data.numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
data.tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
data.blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
|
||||
data.tot_used_blocks = get_num_blocks_used(f2fs_info);
|
||||
|
||||
data.one_pct = data.tot_used_blocks / 100;
|
||||
data.cur_pct = 0;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
|
||||
LOG(WARNING) << "Error getting time at start";
|
||||
// Note - continue anyway - we'll run with 0
|
||||
}
|
||||
data.time_started = time_started.tv_sec;
|
||||
data.remaining_time = -1;
|
||||
|
||||
|
||||
data.buffer = (char*)malloc(f2fs_info->block_size);
|
||||
if (!data.buffer) {
|
||||
LOG(ERROR) << "Failed to allocate crypto buffer";
|
||||
goto errout;
|
||||
}
|
||||
|
||||
data.count = 0;
|
||||
|
||||
/* Currently, this either runs to completion, or hits a nonrecoverable error */
|
||||
rc = run_on_used_blocks(data.blocks_already_done, f2fs_info, &encrypt_one_block_f2fs, &data);
|
||||
|
||||
if (rc) {
|
||||
LOG(ERROR) << "Error in running over f2fs blocks";
|
||||
rc = ENABLE_INPLACE_ERR_OTHER;
|
||||
goto errout;
|
||||
}
|
||||
|
||||
*size_already_done += size;
|
||||
rc = 0;
|
||||
|
||||
errout:
|
||||
if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev;
|
||||
|
||||
log_progress_f2fs(0, true);
|
||||
free(f2fs_info);
|
||||
free(data.buffer);
|
||||
close(data.realfd);
|
||||
close(data.cryptofd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cryptfs_enable_inplace_full(const char* crypto_blkdev, const char* real_blkdev,
|
||||
off64_t size, off64_t* size_already_done, off64_t tot_size,
|
||||
off64_t previously_encrypted_upto,
|
||||
bool set_progress_properties) {
|
||||
int realfd, cryptofd;
|
||||
char* buf[CRYPT_INPLACE_BUFSIZE];
|
||||
int rc = ENABLE_INPLACE_ERR_OTHER;
|
||||
off64_t numblocks, i, remainder;
|
||||
off64_t one_pct, cur_pct, new_pct;
|
||||
off64_t blocks_already_done, tot_numblocks;
|
||||
|
||||
if ((realfd = open(real_blkdev, O_RDONLY | O_CLOEXEC)) < 0) {
|
||||
PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
|
||||
return ENABLE_INPLACE_ERR_OTHER;
|
||||
}
|
||||
|
||||
if ((cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
|
||||
cryptofd_.reset(open64(crypto_blkdev.c_str(), O_WRONLY | O_CLOEXEC));
|
||||
if (cryptofd_ < 0) {
|
||||
PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
|
||||
close(realfd);
|
||||
return ENABLE_INPLACE_ERR_DEV;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* This is pretty much a simple loop of reading 4K, and writing 4K.
|
||||
* The size passed in is the number of 512 byte sectors in the filesystem.
|
||||
* So compute the number of whole 4K blocks we should read/write,
|
||||
* and the remainder.
|
||||
*/
|
||||
numblocks = size / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
remainder = size % CRYPT_SECTORS_PER_BUFSIZE;
|
||||
tot_numblocks = tot_size / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
blocks_already_done = *size_already_done / CRYPT_SECTORS_PER_BUFSIZE;
|
||||
|
||||
LOG(ERROR) << "Encrypting filesystem in place...";
|
||||
|
||||
i = previously_encrypted_upto + 1 - *size_already_done;
|
||||
|
||||
if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev;
|
||||
goto errout;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
|
||||
PLOG(WARNING) << "Error getting time at start of in-place encryption";
|
||||
// Note - continue anyway - we'll run with 0
|
||||
}
|
||||
time_started_ = time_started.tv_sec;
|
||||
remaining_time_ = -1;
|
||||
|
||||
if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
|
||||
PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev;
|
||||
goto errout;
|
||||
bool success = DoEncryptInPlace();
|
||||
|
||||
if (success) success &= EncryptPendingData();
|
||||
|
||||
if (!success) {
|
||||
LOG(ERROR) << "In-place encryption of " << DescribeFilesystem() << " failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; i < size && i % CRYPT_SECTORS_PER_BUFSIZE != 0; ++i) {
|
||||
if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
|
||||
PLOG(ERROR) << "Error reading initial sectors from real_blkdev " << real_blkdev
|
||||
<< " for inplace encrypt";
|
||||
goto errout;
|
||||
}
|
||||
if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
|
||||
PLOG(ERROR) << "Error writing initial sectors to crypto_blkdev " << crypto_blkdev
|
||||
<< " for inplace encrypt";
|
||||
goto errout;
|
||||
} else {
|
||||
LOG(INFO) << "Encrypted 1 block at " << i;
|
||||
}
|
||||
if (blocks_done_ != blocks_to_encrypt_) {
|
||||
LOG(WARNING) << "blocks_to_encrypt (" << blocks_to_encrypt_
|
||||
<< ") was incorrect; we actually encrypted " << blocks_done_
|
||||
<< " blocks. Encryption progress was inaccurate";
|
||||
}
|
||||
|
||||
one_pct = tot_numblocks / 100;
|
||||
cur_pct = 0;
|
||||
/* process the majority of the filesystem in blocks */
|
||||
for (i /= CRYPT_SECTORS_PER_BUFSIZE; i < numblocks; i++) {
|
||||
new_pct = (i + blocks_already_done) / one_pct;
|
||||
if (set_progress_properties && new_pct > cur_pct) {
|
||||
char property_buf[8];
|
||||
|
||||
cur_pct = new_pct;
|
||||
snprintf(property_buf, sizeof(property_buf), "%" PRId64, cur_pct);
|
||||
android::base::SetProperty("vold.encrypt_progress", property_buf);
|
||||
}
|
||||
if (unix_read(realfd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
|
||||
PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev << " for inplace encrypt";
|
||||
goto errout;
|
||||
}
|
||||
if (unix_write(cryptofd, buf, CRYPT_INPLACE_BUFSIZE) <= 0) {
|
||||
PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
|
||||
goto errout;
|
||||
} else {
|
||||
LOG(DEBUG) << "Encrypted " << CRYPT_SECTORS_PER_BUFSIZE << " block at "
|
||||
<< i * CRYPT_SECTORS_PER_BUFSIZE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Do any remaining sectors */
|
||||
for (i = 0; i < remainder; i++) {
|
||||
if (unix_read(realfd, buf, CRYPT_SECTOR_SIZE) <= 0) {
|
||||
LOG(ERROR) << "Error reading final sectors from real_blkdev " << real_blkdev
|
||||
<< " for inplace encrypt";
|
||||
goto errout;
|
||||
}
|
||||
if (unix_write(cryptofd, buf, CRYPT_SECTOR_SIZE) <= 0) {
|
||||
LOG(ERROR) << "Error writing final sectors to crypto_blkdev " << crypto_blkdev
|
||||
<< " for inplace encrypt";
|
||||
goto errout;
|
||||
} else {
|
||||
LOG(INFO) << "Encrypted 1 block at next location";
|
||||
}
|
||||
}
|
||||
|
||||
*size_already_done += size;
|
||||
rc = 0;
|
||||
|
||||
errout:
|
||||
close(realfd);
|
||||
close(cryptofd);
|
||||
|
||||
return rc;
|
||||
// Make sure vold.encrypt_progress gets set to 100.
|
||||
UpdateProgress(0, true);
|
||||
LOG(INFO) << "Successfully encrypted " << DescribeFilesystem();
|
||||
return true;
|
||||
}
|
||||
|
||||
/* returns on of the ENABLE_INPLACE_* return codes */
|
||||
int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
|
||||
off64_t* size_already_done, off64_t tot_size,
|
||||
off64_t previously_encrypted_upto, bool set_progress_properties) {
|
||||
int rc_ext4, rc_f2fs, rc_full;
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size
|
||||
<< ", " << size_already_done << ", " << tot_size << ", " << previously_encrypted_upto
|
||||
<< ", " << set_progress_properties << ")";
|
||||
if (previously_encrypted_upto) {
|
||||
LOG(DEBUG) << "Continuing encryption from " << previously_encrypted_upto;
|
||||
}
|
||||
// Encrypts |real_blkdev| in-place by reading the data from |real_blkdev| and
|
||||
// writing it to |crypto_blkdev|, which should be a dm-crypt or dm-default-key
|
||||
// device backed by |real_blkdev|. The size to encrypt is |nr_sec| 512-byte
|
||||
// sectors; however, if a filesystem is detected, then its size will be used
|
||||
// instead, and only the in-use blocks of the filesystem will be encrypted.
|
||||
bool encrypt_inplace(const std::string& crypto_blkdev, const std::string& real_blkdev,
|
||||
uint64_t nr_sec, bool set_progress_properties) {
|
||||
LOG(DEBUG) << "encrypt_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << nr_sec
|
||||
<< ", " << (set_progress_properties ? "true" : "false") << ")";
|
||||
|
||||
if (*size_already_done + size < previously_encrypted_upto) {
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace already done";
|
||||
*size_already_done += size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* TODO: identify filesystem type.
|
||||
* As is, cryptfs_enable_inplace_ext4 will fail on an f2fs partition, and
|
||||
* then we will drop down to cryptfs_enable_inplace_f2fs.
|
||||
* */
|
||||
if ((rc_ext4 = cryptfs_enable_inplace_ext4(crypto_blkdev, real_blkdev, size, size_already_done,
|
||||
tot_size, previously_encrypted_upto,
|
||||
set_progress_properties)) == 0) {
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success";
|
||||
return 0;
|
||||
}
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4;
|
||||
|
||||
if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size, size_already_done,
|
||||
tot_size, previously_encrypted_upto,
|
||||
set_progress_properties)) == 0) {
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success";
|
||||
return 0;
|
||||
}
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs;
|
||||
|
||||
rc_full =
|
||||
cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, size_already_done, tot_size,
|
||||
previously_encrypted_upto, set_progress_properties);
|
||||
LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full;
|
||||
|
||||
/* Hack for b/17898962, the following is the symptom... */
|
||||
if (rc_ext4 == ENABLE_INPLACE_ERR_DEV && rc_f2fs == ENABLE_INPLACE_ERR_DEV &&
|
||||
rc_full == ENABLE_INPLACE_ERR_DEV) {
|
||||
LOG(DEBUG) << "ENABLE_INPLACE_ERR_DEV";
|
||||
return ENABLE_INPLACE_ERR_DEV;
|
||||
}
|
||||
return rc_full;
|
||||
InPlaceEncrypter encrypter;
|
||||
return encrypter.EncryptInPlace(crypto_blkdev, real_blkdev, nr_sec, set_progress_properties);
|
||||
}
|
||||
|
|
|
@ -17,20 +17,10 @@
|
|||
#ifndef _ENCRYPT_INPLACE_H
|
||||
#define _ENCRYPT_INPLACE_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
|
||||
#define CRYPT_INPLACE_BUFSIZE 4096
|
||||
#define CRYPT_SECTOR_SIZE 512
|
||||
#define RETRY_MOUNT_ATTEMPTS 10
|
||||
#define RETRY_MOUNT_DELAY_SECONDS 1
|
||||
|
||||
/* Return values for cryptfs_enable_inplace() */
|
||||
#define ENABLE_INPLACE_OK 0
|
||||
#define ENABLE_INPLACE_ERR_OTHER (-1)
|
||||
#define ENABLE_INPLACE_ERR_DEV (-2) /* crypto_blkdev issue */
|
||||
|
||||
int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
|
||||
off64_t* size_already_done, off64_t tot_size,
|
||||
off64_t previously_encrypted_upto, bool set_progress_properties);
|
||||
bool encrypt_inplace(const std::string& crypto_blkdev, const std::string& real_blkdev,
|
||||
uint64_t nr_sec, bool set_progress_properties);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -305,21 +305,7 @@ bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::
|
|||
return false;
|
||||
|
||||
// FIXME handle the corrupt case
|
||||
if (needs_encrypt) {
|
||||
LOG(INFO) << "Beginning inplace encryption, nr_sec: " << nr_sec;
|
||||
off64_t size_already_done = 0;
|
||||
auto rc = cryptfs_enable_inplace(crypto_blkdev.data(), blk_device.data(), nr_sec,
|
||||
&size_already_done, nr_sec, 0, false);
|
||||
if (rc != 0) {
|
||||
LOG(ERROR) << "Inplace crypto failed with code: " << rc;
|
||||
return false;
|
||||
}
|
||||
if (static_cast<uint64_t>(size_already_done) != nr_sec) {
|
||||
LOG(ERROR) << "Inplace crypto only got up to sector: " << size_already_done;
|
||||
return false;
|
||||
}
|
||||
LOG(INFO) << "Inplace encryption complete";
|
||||
}
|
||||
if (needs_encrypt && !encrypt_inplace(crypto_blkdev, blk_device, nr_sec, false)) return false;
|
||||
|
||||
LOG(DEBUG) << "Mounting metadata-encrypted filesystem:" << mount_point;
|
||||
mount_via_fs_mgr(mount_point.c_str(), crypto_blkdev.c_str());
|
||||
|
|
172
cryptfs.cpp
172
cryptfs.cpp
|
@ -90,6 +90,8 @@ using namespace std::chrono_literals;
|
|||
#define CRYPT_FOOTER_TO_PERSIST_OFFSET 0x1000
|
||||
#define CRYPT_PERSIST_DATA_SIZE 0x1000
|
||||
|
||||
#define CRYPT_SECTOR_SIZE 512
|
||||
|
||||
#define MAX_CRYPTO_TYPE_NAME_LEN 64
|
||||
|
||||
#define MAX_KEY_LEN 48
|
||||
|
@ -98,9 +100,7 @@ using namespace std::chrono_literals;
|
|||
|
||||
/* definitions of flags in the structure below */
|
||||
#define CRYPT_MNT_KEY_UNENCRYPTED 0x1 /* The key for the partition is not encrypted. */
|
||||
#define CRYPT_ENCRYPTION_IN_PROGRESS \
|
||||
0x2 /* Encryption partially completed, \
|
||||
encrypted_upto valid*/
|
||||
#define CRYPT_ENCRYPTION_IN_PROGRESS 0x2 /* no longer used */
|
||||
#define CRYPT_INCONSISTENT_STATE \
|
||||
0x4 /* Set when starting encryption, clear when \
|
||||
exit cleanly, either through success or \
|
||||
|
@ -195,12 +195,8 @@ struct crypt_mnt_ftr {
|
|||
__le8 N_factor; /* (1 << N) */
|
||||
__le8 r_factor; /* (1 << r) */
|
||||
__le8 p_factor; /* (1 << p) */
|
||||
__le64 encrypted_upto; /* If we are in state CRYPT_ENCRYPTION_IN_PROGRESS and
|
||||
we have to stop (e.g. power low) this is the last
|
||||
encrypted 512 byte sector.*/
|
||||
__le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* When CRYPT_ENCRYPTION_IN_PROGRESS
|
||||
set, hash of first block, used
|
||||
to validate before continuing*/
|
||||
__le64 encrypted_upto; /* no longer used */
|
||||
__le8 hash_first_block[SHA256_DIGEST_LENGTH]; /* no longer used */
|
||||
|
||||
/* key_master key, used to sign the derived key which is then used to generate
|
||||
* the intermediate key
|
||||
|
@ -2069,61 +2065,6 @@ static int cryptfs_init_crypt_mnt_ftr(struct crypt_mnt_ftr* ftr) {
|
|||
|
||||
#define FRAMEWORK_BOOT_WAIT 60
|
||||
|
||||
static int cryptfs_SHA256_fileblock(const char* filename, __le8* buf) {
|
||||
int fd = open(filename, O_RDONLY | O_CLOEXEC);
|
||||
if (fd == -1) {
|
||||
SLOGE("Error opening file %s", filename);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char block[CRYPT_INPLACE_BUFSIZE];
|
||||
memset(block, 0, sizeof(block));
|
||||
if (unix_read(fd, block, sizeof(block)) < 0) {
|
||||
SLOGE("Error reading file %s", filename);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
SHA256_CTX c;
|
||||
SHA256_Init(&c);
|
||||
SHA256_Update(&c, block, sizeof(block));
|
||||
SHA256_Final(buf, &c);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cryptfs_enable_all_volumes(struct crypt_mnt_ftr* crypt_ftr, const char* crypto_blkdev,
|
||||
const char* real_blkdev, int previously_encrypted_upto) {
|
||||
off64_t cur_encryption_done = 0, tot_encryption_size = 0;
|
||||
int rc = -1;
|
||||
|
||||
/* The size of the userdata partition, and add in the vold volumes below */
|
||||
tot_encryption_size = crypt_ftr->fs_size;
|
||||
|
||||
rc = cryptfs_enable_inplace(crypto_blkdev, real_blkdev, crypt_ftr->fs_size, &cur_encryption_done,
|
||||
tot_encryption_size, previously_encrypted_upto, true);
|
||||
|
||||
if (rc == ENABLE_INPLACE_ERR_DEV) {
|
||||
/* Hack for b/17898962 */
|
||||
SLOGE("cryptfs_enable: crypto block dev failure. Must reboot...\n");
|
||||
cryptfs_reboot(RebootType::reboot);
|
||||
}
|
||||
|
||||
if (!rc) {
|
||||
crypt_ftr->encrypted_upto = cur_encryption_done;
|
||||
}
|
||||
|
||||
if (!rc && crypt_ftr->encrypted_upto == crypt_ftr->fs_size) {
|
||||
/* The inplace routine never actually sets the progress to 100% due
|
||||
* to the round down nature of integer division, so set it here */
|
||||
property_set("vold.encrypt_progress", "100");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int vold_unmountAll(void) {
|
||||
VolumeManager* vm = VolumeManager::Instance();
|
||||
return vm->unmountAll();
|
||||
|
@ -2140,26 +2081,12 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) {
|
|||
char lockid[32] = {0};
|
||||
std::string key_loc;
|
||||
int num_vols;
|
||||
off64_t previously_encrypted_upto = 0;
|
||||
bool rebootEncryption = false;
|
||||
bool onlyCreateHeader = false;
|
||||
std::unique_ptr<android::wakelock::WakeLock> wakeLock = nullptr;
|
||||
|
||||
if (get_crypt_ftr_and_key(&crypt_ftr) == 0) {
|
||||
if (crypt_ftr.flags & CRYPT_ENCRYPTION_IN_PROGRESS) {
|
||||
/* An encryption was underway and was interrupted */
|
||||
previously_encrypted_upto = crypt_ftr.encrypted_upto;
|
||||
crypt_ftr.encrypted_upto = 0;
|
||||
crypt_ftr.flags &= ~CRYPT_ENCRYPTION_IN_PROGRESS;
|
||||
|
||||
/* At this point, we are in an inconsistent state. Until we successfully
|
||||
complete encryption, a reboot will leave us broken. So mark the
|
||||
encryption failed in case that happens.
|
||||
On successfully completing encryption, remove this flag */
|
||||
crypt_ftr.flags |= CRYPT_INCONSISTENT_STATE;
|
||||
|
||||
put_crypt_ftr_and_key(&crypt_ftr);
|
||||
} else if (crypt_ftr.flags & CRYPT_FORCE_ENCRYPTION) {
|
||||
if (crypt_ftr.flags & CRYPT_FORCE_ENCRYPTION) {
|
||||
if (!check_ftr_sha(&crypt_ftr)) {
|
||||
memset(&crypt_ftr, 0, sizeof(crypt_ftr));
|
||||
put_crypt_ftr_and_key(&crypt_ftr);
|
||||
|
@ -2177,7 +2104,7 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) {
|
|||
}
|
||||
|
||||
property_get("ro.crypto.state", encrypted_state, "");
|
||||
if (!strcmp(encrypted_state, "encrypted") && !previously_encrypted_upto) {
|
||||
if (!strcmp(encrypted_state, "encrypted")) {
|
||||
SLOGE("Device is already running encrypted, aborting");
|
||||
goto error_unencrypted;
|
||||
}
|
||||
|
@ -2264,7 +2191,7 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) {
|
|||
|
||||
/* Start the actual work of making an encrypted filesystem */
|
||||
/* Initialize a crypt_mnt_ftr for the partition */
|
||||
if (previously_encrypted_upto == 0 && !rebootEncryption) {
|
||||
if (!rebootEncryption) {
|
||||
if (cryptfs_init_crypt_mnt_ftr(&crypt_ftr)) {
|
||||
goto error_shutting_down;
|
||||
}
|
||||
|
@ -2339,77 +2266,46 @@ int cryptfs_enable_internal(int crypt_type, const char* passwd, int no_ui) {
|
|||
}
|
||||
|
||||
decrypt_master_key(passwd, decrypted_master_key, &crypt_ftr, 0, 0);
|
||||
create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev.c_str(), &crypto_blkdev,
|
||||
CRYPTO_BLOCK_DEVICE, 0);
|
||||
|
||||
/* If we are continuing, check checksums match */
|
||||
rc = 0;
|
||||
if (previously_encrypted_upto) {
|
||||
__le8 hash_first_block[SHA256_DIGEST_LENGTH];
|
||||
rc = cryptfs_SHA256_fileblock(crypto_blkdev.c_str(), hash_first_block);
|
||||
|
||||
if (!rc &&
|
||||
memcmp(hash_first_block, crypt_ftr.hash_first_block, sizeof(hash_first_block)) != 0) {
|
||||
SLOGE("Checksums do not match - trigger wipe");
|
||||
rc = -1;
|
||||
}
|
||||
}
|
||||
|
||||
rc = create_crypto_blk_dev(&crypt_ftr, decrypted_master_key, real_blkdev.c_str(),
|
||||
&crypto_blkdev, CRYPTO_BLOCK_DEVICE, 0);
|
||||
if (!rc) {
|
||||
rc = cryptfs_enable_all_volumes(&crypt_ftr, crypto_blkdev.c_str(), real_blkdev.data(),
|
||||
previously_encrypted_upto);
|
||||
}
|
||||
|
||||
/* Calculate checksum if we are not finished */
|
||||
if (!rc && crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
|
||||
rc = cryptfs_SHA256_fileblock(crypto_blkdev.c_str(), crypt_ftr.hash_first_block);
|
||||
if (rc) {
|
||||
SLOGE("Error calculating checksum for continuing encryption");
|
||||
if (encrypt_inplace(crypto_blkdev, real_blkdev, crypt_ftr.fs_size, true)) {
|
||||
crypt_ftr.encrypted_upto = crypt_ftr.fs_size;
|
||||
rc = 0;
|
||||
} else {
|
||||
rc = -1;
|
||||
}
|
||||
/* Undo the dm-crypt mapping whether we succeed or not */
|
||||
delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
|
||||
}
|
||||
|
||||
/* Undo the dm-crypt mapping whether we succeed or not */
|
||||
delete_crypto_blk_dev(CRYPTO_BLOCK_DEVICE);
|
||||
|
||||
if (!rc) {
|
||||
/* Success */
|
||||
crypt_ftr.flags &= ~CRYPT_INCONSISTENT_STATE;
|
||||
|
||||
if (crypt_ftr.encrypted_upto != crypt_ftr.fs_size) {
|
||||
SLOGD("Encrypted up to sector %lld - will continue after reboot",
|
||||
crypt_ftr.encrypted_upto);
|
||||
crypt_ftr.flags |= CRYPT_ENCRYPTION_IN_PROGRESS;
|
||||
}
|
||||
|
||||
put_crypt_ftr_and_key(&crypt_ftr);
|
||||
|
||||
if (crypt_ftr.encrypted_upto == crypt_ftr.fs_size) {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.crypto.state", value, "");
|
||||
if (!strcmp(value, "")) {
|
||||
/* default encryption - continue first boot sequence */
|
||||
property_set("ro.crypto.state", "encrypted");
|
||||
property_set("ro.crypto.type", "block");
|
||||
wakeLock.reset(nullptr);
|
||||
if (rebootEncryption && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
|
||||
// Bring up cryptkeeper that will check the password and set it
|
||||
property_set("vold.decrypt", "trigger_shutdown_framework");
|
||||
sleep(2);
|
||||
property_set("vold.encrypt_progress", "");
|
||||
cryptfs_trigger_restart_min_framework();
|
||||
} else {
|
||||
cryptfs_check_passwd(DEFAULT_PASSWORD);
|
||||
cryptfs_restart_internal(1);
|
||||
}
|
||||
return 0;
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.crypto.state", value, "");
|
||||
if (!strcmp(value, "")) {
|
||||
/* default encryption - continue first boot sequence */
|
||||
property_set("ro.crypto.state", "encrypted");
|
||||
property_set("ro.crypto.type", "block");
|
||||
wakeLock.reset(nullptr);
|
||||
if (rebootEncryption && crypt_ftr.crypt_type != CRYPT_TYPE_DEFAULT) {
|
||||
// Bring up cryptkeeper that will check the password and set it
|
||||
property_set("vold.decrypt", "trigger_shutdown_framework");
|
||||
sleep(2);
|
||||
property_set("vold.encrypt_progress", "");
|
||||
cryptfs_trigger_restart_min_framework();
|
||||
} else {
|
||||
sleep(2); /* Give the UI a chance to show 100% progress */
|
||||
cryptfs_reboot(RebootType::reboot);
|
||||
cryptfs_check_passwd(DEFAULT_PASSWORD);
|
||||
cryptfs_restart_internal(1);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
sleep(2); /* Partially encrypted, ensure writes flushed to ssd */
|
||||
cryptfs_reboot(RebootType::shutdown);
|
||||
sleep(2); /* Give the UI a chance to show 100% progress */
|
||||
cryptfs_reboot(RebootType::reboot);
|
||||
}
|
||||
} else {
|
||||
char value[PROPERTY_VALUE_MAX];
|
||||
|
|
Loading…
Reference in a new issue