#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>
-// 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));
+enum EncryptInPlaceError {
+ kSuccess,
+ kFailed,
+ kFilesystemNotFound,
+};
+
+static uint64_t round_up(uint64_t val, size_t amount) {
+ if (val % amount) val += amount - (val % amount);
+ return val;
}
-static inline int unix_write(int fd, const void* buff, int len) {
- return TEMP_FAILURE_RETRY(write(fd, buff, len));
+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_;
}
-#define CRYPT_SECTORS_PER_BUFSIZE (CRYPT_INPLACE_BUFSIZE / CRYPT_SECTOR_SIZE)
+// 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";
+}
-/* 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 one_pct, cur_pct, new_pct;
- off64_t blocks_already_done;
- off64_t used_blocks_already_done, tot_used_blocks;
- const char* real_blkdev;
- const char* crypto_blkdev;
- int count;
- off64_t offset;
- char* buffer;
- time_t time_started;
- int remaining_time;
- bool set_progress_properties;
-};
+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);
-static void update_progress(struct encryptGroupsData* data, int is_used) {
- data->blocks_already_done++;
+ blocks_done_ += blocks;
- if (is_used) {
- data->used_blocks_already_done++;
- }
- if (data->tot_used_blocks) {
- data->new_pct = data->used_blocks_already_done / data->one_pct;
- } else {
- data->new_pct = data->blocks_already_done / data->one_pct;
- }
+ // 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 (!data->set_progress_properties) return;
+ if (blocks_done_ >= blocks_next_msg)
+ LOG(DEBUG) << "Encrypted " << blocks_next_msg << " of " << blocks_to_encrypt_ << " blocks";
- 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 (!set_progress_properties_) return;
+
+ uint64_t new_pct;
+ if (done) {
+ new_pct = 100;
+ } else {
+ 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->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;
- }
-}
-
-static int flush_outstanding_data(struct encryptGroupsData* data) {
- if (data->count == 0) {
- return 0;
- }
+ UpdateProgress(blocks_pending_, false);
- LOG(DEBUG) << "Copying " << data->count << " blocks at offset " << data->offset;
+ blocks_pending_ = 0;
+ return true;
+}
- 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;
- }
+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;
+}
- 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);
+// 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;
}
-
- data->count = 0;
- return 0;
+ return true;
}
-static uint64_t first_block_in_group(uint32_t group) {
+uint64_t InPlaceEncrypter::FirstBlockInGroup(uint32_t group) {
return aux_info.first_data_block + (group * (uint64_t)info.blocks_per_group);
}
-static uint32_t num_blocks_in_group(uint32_t group) {
- uint64_t remaining = aux_info.len_blocks - first_block_in_group(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).
-static uint32_t num_base_meta_blocks_in_group(uint64_t group) {
+uint32_t InPlaceEncrypter::NumBaseMetaBlocksInGroup(uint64_t group) {
if (!ext4_bg_has_super_block(group)) return 0;
return 1 + aux_info.bg_desc_blocks;
}
-static int encrypt_groups(struct encryptGroupsData* data) {
- unsigned int i;
- u8* block_bitmap = 0;
- unsigned int block;
- off64_t ret;
- int rc = -1;
-
- 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;
+EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceExt4() {
+ if (setjmp(setjmp_env)) // NOLINT
+ return kFilesystemNotFound;
- u32 block_count = num_blocks_in_group(i);
+ if (read_ext(realfd_, 0) != 0) return kFilesystemNotFound;
- off64_t offset = (u64)info.block_size * aux_info.bg_desc[i].bg_block_bitmap;
+ LOG(DEBUG) << "ext4 filesystem has " << aux_info.groups << " block groups";
- 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_in_group(i);
-
- data->count = 0;
-
- for (block = 0; block < block_count; block++) {
- int used;
+ 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);
+ }
- if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT)
- used = (block < num_base_meta_blocks_in_group(i));
- else
- used = bitmap_get_bit(block_bitmap, block);
+ InitFs("ext4", blocks_to_encrypt, aux_info.len_blocks, info.block_size);
- update_progress(data, used);
- if (used) {
- if (data->count == 0) {
- data->offset = offset;
- }
- data->count++;
- } else {
- if (flush_outstanding_data(data)) {
- goto errout;
- }
- }
+ // 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;
- offset += info.block_size;
+ 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);
- /* 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;
+ // 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);
}
}
-
- rc = 0;
-
-errout:
- log_progress(0, true);
- free(data->buffer);
- free(block_bitmap);
- return rc;
+ return kSuccess;
}
-static int cryptfs_enable_inplace_ext4(const char* crypto_blkdev, const char* real_blkdev,
- off64_t size, bool set_progress_properties) {
- u32 i;
- struct encryptGroupsData data;
- int rc; // Can't initialize without causing warning -Wclobbered
- int retries = RETRY_MOUNT_ATTEMPTS;
- struct timespec time_started = {0};
-
- 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) {
- PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
- rc = -1;
- goto errout;
- }
-
- LOG(DEBUG) << "Opening" << crypto_blkdev;
- if ((data.cryptofd = open(crypto_blkdev, O_WRONLY | O_CLOEXEC)) < 0) {
- PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
- rc = -1;
- 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.blocks_already_done = 0;
-
- LOG(INFO) << "Encrypting ext4 filesystem in place...";
-
- data.tot_used_blocks = 0;
- for (i = 0; i < aux_info.groups; ++i) {
- if (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT)
- data.tot_used_blocks += num_base_meta_blocks_in_group(i);
- else
- data.tot_used_blocks +=
- (num_blocks_in_group(i) - 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;
- }
-
- rc = 0;
-
-errout:
- close(data.realfd);
- close(data.cryptofd);
-
- return rc;
+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 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;
- }
+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;
- // Update offset
- if (!completed) {
- last_block = block;
- }
+ 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 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);
+bool InPlaceEncrypter::DoEncryptInPlace() {
+ EncryptInPlaceError rc;
- off64_t offset = pos * CRYPT_INPLACE_BUFSIZE;
+ rc = EncryptInPlaceExt4();
+ if (rc != kFilesystemNotFound) return rc == kSuccess;
- 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;
- }
+ rc = EncryptInPlaceF2fs();
+ if (rc != kFilesystemNotFound) return rc == kSuccess;
- 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);
+ 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 0;
+ return true;
}
-static int cryptfs_enable_inplace_f2fs(const char* crypto_blkdev, const char* real_blkdev,
- off64_t size, bool set_progress_properties) {
- struct encryptGroupsData data;
- struct f2fs_info* f2fs_info = NULL;
- int rc = ENABLE_INPLACE_ERR_OTHER;
+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};
- 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";
- goto errout;
- }
-
- f2fs_info = generate_f2fs_info(data.realfd);
- if (!f2fs_info) goto errout;
-
- data.blocks_already_done = 0;
-
- 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;
- }
+ real_blkdev_ = real_blkdev;
+ crypto_blkdev_ = crypto_blkdev;
+ nr_sec_ = nr_sec;
+ set_progress_properties_ = set_progress_properties;
- rc = 0;
-
-errout:
- if (rc) LOG(ERROR) << "Failed to encrypt f2fs filesystem on " << real_blkdev;
-
- log_progress_f2fs(0, true);
- free_f2fs_info(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, 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;
-
- if ((realfd = open(real_blkdev, O_RDONLY | 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";
- return ENABLE_INPLACE_ERR_OTHER;
+ return false;
}
- 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_OTHER;
+ 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;
-
- LOG(ERROR) << "Encrypting filesystem in place...";
-
- one_pct = numblocks / 100;
- cur_pct = 0;
- /* process the majority of the filesystem in blocks */
- for (i = 0; i < numblocks; i++) {
- new_pct = i / 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";
- }
+ 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;
- rc = 0;
+ bool success = DoEncryptInPlace();
-errout:
- close(realfd);
- close(cryptofd);
+ if (success) success &= EncryptPendingData();
- return rc;
-}
-
-/* returns on of the ENABLE_INPLACE_* return codes */
-int cryptfs_enable_inplace(const char* crypto_blkdev, const char* real_blkdev, off64_t size,
- bool set_progress_properties) {
- int rc_ext4, rc_f2fs, rc_full;
- LOG(DEBUG) << "cryptfs_enable_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << size
- << ", " << set_progress_properties << ")";
-
- /* 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,
- set_progress_properties)) == 0) {
- LOG(DEBUG) << "cryptfs_enable_inplace_ext4 success";
- return 0;
+ if (!success) {
+ LOG(ERROR) << "In-place encryption of " << DescribeFilesystem() << " failed";
+ return false;
}
- LOG(DEBUG) << "cryptfs_enable_inplace_ext4()=" << rc_ext4;
-
- if ((rc_f2fs = cryptfs_enable_inplace_f2fs(crypto_blkdev, real_blkdev, size,
- set_progress_properties)) == 0) {
- LOG(DEBUG) << "cryptfs_enable_inplace_f2fs success";
- return 0;
+ 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";
}
- LOG(DEBUG) << "cryptfs_enable_inplace_f2fs()=" << rc_f2fs;
+ // Make sure vold.encrypt_progress gets set to 100.
+ UpdateProgress(0, true);
+ LOG(INFO) << "Successfully encrypted " << DescribeFilesystem();
+ return true;
+}
- rc_full =
- cryptfs_enable_inplace_full(crypto_blkdev, real_blkdev, size, set_progress_properties);
- LOG(DEBUG) << "cryptfs_enable_inplace_full()=" << rc_full;
- return rc_full;
+// 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") << ")";
+
+ InPlaceEncrypter encrypter;
+ return encrypter.EncryptInPlace(crypto_blkdev, real_blkdev, nr_sec, set_progress_properties);
}