blob: 190bb83e20fb4306db9ebb8d48dc53b0a242c73d [file] [log] [blame]
Paul Crowleyf71ace32016-06-02 11:01:19 -07001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "EncryptInplace.h"
18
Paul Crowleyf71ace32016-06-02 11:01:19 -070019#include <ext4_utils/ext4.h>
20#include <ext4_utils/ext4_utils.h>
21#include <f2fs_sparseblock.h>
Paul Crowley14c8c072018-09-18 13:30:21 -070022#include <fcntl.h>
Paul Crowleyf71ace32016-06-02 11:01:19 -070023
24#include <algorithm>
Eric Biggersf038c5f2020-11-03 14:11:02 -080025#include <vector>
Paul Crowleyf71ace32016-06-02 11:01:19 -070026
Paul Crowley772cc852018-02-01 09:53:27 -080027#include <android-base/logging.h>
Eric Biggersf038c5f2020-11-03 14:11:02 -080028#include <android-base/unique_fd.h>
Paul Crowleyf71ace32016-06-02 11:01:19 -070029
Eric Biggersf038c5f2020-11-03 14:11:02 -080030enum EncryptInPlaceError {
31 kSuccess,
32 kFailed,
33 kFilesystemNotFound,
Paul Crowleyf71ace32016-06-02 11:01:19 -070034};
35
Eric Biggersf038c5f2020-11-03 14:11:02 -080036static uint64_t round_up(uint64_t val, size_t amount) {
37 if (val % amount) val += amount - (val % amount);
38 return val;
39}
Paul Crowleyf71ace32016-06-02 11:01:19 -070040
Eric Biggersf038c5f2020-11-03 14:11:02 -080041class InPlaceEncrypter {
42 public:
43 bool EncryptInPlace(const std::string& crypto_blkdev, const std::string& real_blkdev,
Eric Biggers640a1a92022-03-09 20:39:04 +000044 uint64_t nr_sec);
Eric Biggersf038c5f2020-11-03 14:11:02 -080045 bool ProcessUsedBlock(uint64_t block_num);
46
47 private:
48 // aligned 32K writes tends to make flash happy.
49 // SD card association recommends it.
50 static const size_t kIOBufferSize = 32768;
51
52 // Avoid spamming the logs. Print the "Encrypting blocks" log message once
53 // every 10000 blocks (which is usually every 40 MB or so), and once at the end.
54 static const int kLogInterval = 10000;
55
56 std::string DescribeFilesystem();
57 void InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt, uint64_t total_blocks,
58 unsigned int block_size);
59 void UpdateProgress(size_t blocks, bool done);
60 bool EncryptPendingData();
61 bool DoEncryptInPlace();
62
63 // ext4 methods
64 bool ReadExt4BlockBitmap(uint32_t group, uint8_t* buf);
65 uint64_t FirstBlockInGroup(uint32_t group);
66 uint32_t NumBlocksInGroup(uint32_t group);
67 uint32_t NumBaseMetaBlocksInGroup(uint64_t group);
68 EncryptInPlaceError EncryptInPlaceExt4();
69
70 // f2fs methods
71 EncryptInPlaceError EncryptInPlaceF2fs();
72
73 std::string real_blkdev_;
74 std::string crypto_blkdev_;
75 uint64_t nr_sec_;
Eric Biggersf038c5f2020-11-03 14:11:02 -080076
77 android::base::unique_fd realfd_;
78 android::base::unique_fd cryptofd_;
79
Eric Biggersf038c5f2020-11-03 14:11:02 -080080 std::string fs_type_;
81 uint64_t blocks_done_;
82 uint64_t blocks_to_encrypt_;
83 unsigned int block_size_;
Eric Biggersf038c5f2020-11-03 14:11:02 -080084
85 std::vector<uint8_t> io_buffer_;
86 uint64_t first_pending_block_;
87 size_t blocks_pending_;
88};
89
90std::string InPlaceEncrypter::DescribeFilesystem() {
91 if (fs_type_.empty())
92 return "full block device " + real_blkdev_;
93 else
94 return fs_type_ + " filesystem on " + real_blkdev_;
95}
96
97// Finishes initializing the encrypter, now that the filesystem details are known.
98void InPlaceEncrypter::InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt,
99 uint64_t total_blocks, unsigned int block_size) {
100 fs_type_ = fs_type;
101 blocks_done_ = 0;
102 blocks_to_encrypt_ = blocks_to_encrypt;
103 block_size_ = block_size;
Eric Biggersf038c5f2020-11-03 14:11:02 -0800104
105 // Allocate the I/O buffer. kIOBufferSize should always be a multiple of
106 // the filesystem block size, but round it up just in case.
107 io_buffer_.resize(round_up(kIOBufferSize, block_size));
108 first_pending_block_ = 0;
109 blocks_pending_ = 0;
110
111 LOG(INFO) << "Encrypting " << DescribeFilesystem() << " in-place via " << crypto_blkdev_;
112 LOG(INFO) << blocks_to_encrypt << " blocks (" << (blocks_to_encrypt * block_size) / 1000000
113 << " MB) of " << total_blocks << " blocks are in-use";
114}
115
116void InPlaceEncrypter::UpdateProgress(size_t blocks, bool done) {
117 // A log message already got printed for blocks_done_ if one was due, so the
118 // next message will be due at the *next* block rounded up to kLogInterval.
119 uint64_t blocks_next_msg = round_up(blocks_done_ + 1, kLogInterval);
120
121 blocks_done_ += blocks;
122
123 // Ensure that a log message gets printed at the end, but not if one was
124 // already printed due to the block count being a multiple of kLogInterval.
125 // E.g. we want to show "50000 of 50327" and then "50327 of "50327", but not
126 // "50000 of 50000" and then redundantly "50000 of 50000" again.
127 if (done && blocks_done_ % kLogInterval != 0) blocks_next_msg = blocks_done_;
128
129 if (blocks_done_ >= blocks_next_msg)
130 LOG(DEBUG) << "Encrypted " << blocks_next_msg << " of " << blocks_to_encrypt_ << " blocks";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700131}
132
Eric Biggersf038c5f2020-11-03 14:11:02 -0800133bool InPlaceEncrypter::EncryptPendingData() {
134 if (blocks_pending_ == 0) return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700135
Eric Biggersf038c5f2020-11-03 14:11:02 -0800136 ssize_t bytes = blocks_pending_ * block_size_;
137 uint64_t offset = first_pending_block_ * block_size_;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700138
Eric Biggersf038c5f2020-11-03 14:11:02 -0800139 if (pread64(realfd_, &io_buffer_[0], bytes, offset) != bytes) {
140 PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev_ << " for inplace encrypt";
141 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700142 }
143
Eric Biggersf038c5f2020-11-03 14:11:02 -0800144 if (pwrite64(cryptofd_, &io_buffer_[0], bytes, offset) != bytes) {
145 PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev_ << " for inplace encrypt";
146 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700147 }
148
Eric Biggersf038c5f2020-11-03 14:11:02 -0800149 UpdateProgress(blocks_pending_, false);
150
151 blocks_pending_ = 0;
152 return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700153}
154
Eric Biggersf038c5f2020-11-03 14:11:02 -0800155bool InPlaceEncrypter::ProcessUsedBlock(uint64_t block_num) {
156 // Flush if the amount of pending data has reached the I/O buffer size, if
157 // there's a gap between the pending blocks and the next block (due to
158 // block(s) not being used by the filesystem and thus not needing
159 // encryption), or if the next block will be aligned to the I/O buffer size.
160 if (blocks_pending_ * block_size_ == io_buffer_.size() ||
161 block_num != first_pending_block_ + blocks_pending_ ||
162 (block_num * block_size_) % io_buffer_.size() == 0) {
163 if (!EncryptPendingData()) return false;
164 first_pending_block_ = block_num;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700165 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800166 blocks_pending_++;
167 return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700168}
169
Eric Biggersf038c5f2020-11-03 14:11:02 -0800170// Reads the block bitmap for block group |group| into |buf|.
171bool InPlaceEncrypter::ReadExt4BlockBitmap(uint32_t group, uint8_t* buf) {
172 uint64_t offset = (uint64_t)aux_info.bg_desc[group].bg_block_bitmap * info.block_size;
173 if (pread64(realfd_, buf, info.block_size, offset) != (ssize_t)info.block_size) {
174 PLOG(ERROR) << "Failed to read block bitmap for block group " << group;
175 return false;
176 }
177 return true;
178}
179
180uint64_t InPlaceEncrypter::FirstBlockInGroup(uint32_t group) {
Eric Biggers7e70d692020-11-03 14:11:01 -0800181 return aux_info.first_data_block + (group * (uint64_t)info.blocks_per_group);
182}
183
Eric Biggersf038c5f2020-11-03 14:11:02 -0800184uint32_t InPlaceEncrypter::NumBlocksInGroup(uint32_t group) {
185 uint64_t remaining = aux_info.len_blocks - FirstBlockInGroup(group);
Eric Biggers7e70d692020-11-03 14:11:01 -0800186 return std::min<uint64_t>(info.blocks_per_group, remaining);
187}
188
189// In block groups with an uninitialized block bitmap, we only need to encrypt
190// the backup superblock and the block group descriptors (if they are present).
Eric Biggersf038c5f2020-11-03 14:11:02 -0800191uint32_t InPlaceEncrypter::NumBaseMetaBlocksInGroup(uint64_t group) {
Eric Biggers7e70d692020-11-03 14:11:01 -0800192 if (!ext4_bg_has_super_block(group)) return 0;
193 return 1 + aux_info.bg_desc_blocks;
194}
195
Eric Biggersf038c5f2020-11-03 14:11:02 -0800196EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceExt4() {
197 if (setjmp(setjmp_env)) // NOLINT
198 return kFilesystemNotFound;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700199
Eric Biggersf038c5f2020-11-03 14:11:02 -0800200 if (read_ext(realfd_, 0) != 0) return kFilesystemNotFound;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700201
Eric Biggersf038c5f2020-11-03 14:11:02 -0800202 LOG(DEBUG) << "ext4 filesystem has " << aux_info.groups << " block groups";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700203
Eric Biggersf038c5f2020-11-03 14:11:02 -0800204 uint64_t blocks_to_encrypt = 0;
205 for (uint32_t group = 0; group < aux_info.groups; group++) {
206 if (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT)
207 blocks_to_encrypt += NumBaseMetaBlocksInGroup(group);
Eric Biggers7e70d692020-11-03 14:11:01 -0800208 else
Eric Biggersf038c5f2020-11-03 14:11:02 -0800209 blocks_to_encrypt +=
210 (NumBlocksInGroup(group) - aux_info.bg_desc[group].bg_free_blocks_count);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700211 }
212
Eric Biggersf038c5f2020-11-03 14:11:02 -0800213 InitFs("ext4", blocks_to_encrypt, aux_info.len_blocks, info.block_size);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700214
Eric Biggersf038c5f2020-11-03 14:11:02 -0800215 // Encrypt each block group.
216 std::vector<uint8_t> block_bitmap(info.block_size);
217 for (uint32_t group = 0; group < aux_info.groups; group++) {
218 if (!ReadExt4BlockBitmap(group, &block_bitmap[0])) return kFailed;
219
220 uint64_t first_block_num = FirstBlockInGroup(group);
221 bool uninit = (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT);
222 uint32_t block_count = uninit ? NumBaseMetaBlocksInGroup(group) : NumBlocksInGroup(group);
223
224 // Encrypt each used block in the block group.
225 for (uint32_t i = 0; i < block_count; i++) {
226 if (uninit || bitmap_get_bit(&block_bitmap[0], i))
227 ProcessUsedBlock(first_block_num + i);
228 }
Paul Crowleyf71ace32016-06-02 11:01:19 -0700229 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800230 return kSuccess;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700231}
232
Eric Biggersf038c5f2020-11-03 14:11:02 -0800233static int encrypt_f2fs_block(uint64_t block_num, void* _encrypter) {
234 InPlaceEncrypter* encrypter = reinterpret_cast<InPlaceEncrypter*>(_encrypter);
235 if (!encrypter->ProcessUsedBlock(block_num)) return -1;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700236 return 0;
237}
238
Eric Biggersf038c5f2020-11-03 14:11:02 -0800239EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceF2fs() {
240 std::unique_ptr<struct f2fs_info, void (*)(struct f2fs_info*)> fs_info(
241 generate_f2fs_info(realfd_), free_f2fs_info);
242 if (!fs_info) return kFilesystemNotFound;
243
244 InitFs("f2fs", get_num_blocks_used(fs_info.get()), fs_info->total_blocks, fs_info->block_size);
245 if (run_on_used_blocks(0, fs_info.get(), encrypt_f2fs_block, this) != 0) return kFailed;
246 return kSuccess;
247}
248
249bool InPlaceEncrypter::DoEncryptInPlace() {
250 EncryptInPlaceError rc;
251
252 rc = EncryptInPlaceExt4();
253 if (rc != kFilesystemNotFound) return rc == kSuccess;
254
255 rc = EncryptInPlaceF2fs();
256 if (rc != kFilesystemNotFound) return rc == kSuccess;
257
258 LOG(WARNING) << "No recognized filesystem found on " << real_blkdev_
259 << ". Falling back to encrypting the full block device.";
260 InitFs("", nr_sec_, nr_sec_, 512);
261 for (uint64_t i = 0; i < nr_sec_; i++) {
262 if (!ProcessUsedBlock(i)) return false;
263 }
264 return true;
265}
266
267bool InPlaceEncrypter::EncryptInPlace(const std::string& crypto_blkdev,
Eric Biggers640a1a92022-03-09 20:39:04 +0000268 const std::string& real_blkdev, uint64_t nr_sec) {
Eric Biggersf038c5f2020-11-03 14:11:02 -0800269 real_blkdev_ = real_blkdev;
270 crypto_blkdev_ = crypto_blkdev;
271 nr_sec_ = nr_sec;
Eric Biggersf038c5f2020-11-03 14:11:02 -0800272
273 realfd_.reset(open64(real_blkdev.c_str(), O_RDONLY | O_CLOEXEC));
274 if (realfd_ < 0) {
275 PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
276 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700277 }
278
Eric Biggersf038c5f2020-11-03 14:11:02 -0800279 cryptofd_.reset(open64(crypto_blkdev.c_str(), O_WRONLY | O_CLOEXEC));
280 if (cryptofd_ < 0) {
281 PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
282 return false;
283 }
Paul Crowleyf71ace32016-06-02 11:01:19 -0700284
Eric Biggersf038c5f2020-11-03 14:11:02 -0800285 bool success = DoEncryptInPlace();
Denis Hsu1740eff2019-12-26 16:06:37 +0800286
Eric Biggersf038c5f2020-11-03 14:11:02 -0800287 if (success) success &= EncryptPendingData();
288
Eric Biggers1ba88652020-11-04 18:38:38 -0800289 if (success && fsync(cryptofd_) != 0) {
290 PLOG(ERROR) << "Error syncing " << crypto_blkdev_;
291 success = false;
292 }
293
Eric Biggersf038c5f2020-11-03 14:11:02 -0800294 if (!success) {
295 LOG(ERROR) << "In-place encryption of " << DescribeFilesystem() << " failed";
296 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700297 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800298 if (blocks_done_ != blocks_to_encrypt_) {
299 LOG(WARNING) << "blocks_to_encrypt (" << blocks_to_encrypt_
300 << ") was incorrect; we actually encrypted " << blocks_done_
301 << " blocks. Encryption progress was inaccurate";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700302 }
Eric Biggers640a1a92022-03-09 20:39:04 +0000303 // Ensure that the final progress message is printed, so the series of log
304 // messages ends with e.g. "Encrypted 50327 of 50327 blocks" rather than
305 // "Encrypted 50000 of 50327 blocks".
Eric Biggersf038c5f2020-11-03 14:11:02 -0800306 UpdateProgress(0, true);
Eric Biggers640a1a92022-03-09 20:39:04 +0000307
Eric Biggersf038c5f2020-11-03 14:11:02 -0800308 LOG(INFO) << "Successfully encrypted " << DescribeFilesystem();
309 return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700310}
311
Eric Biggersf038c5f2020-11-03 14:11:02 -0800312// Encrypts |real_blkdev| in-place by reading the data from |real_blkdev| and
313// writing it to |crypto_blkdev|, which should be a dm-crypt or dm-default-key
314// device backed by |real_blkdev|. The size to encrypt is |nr_sec| 512-byte
315// sectors; however, if a filesystem is detected, then its size will be used
316// instead, and only the in-use blocks of the filesystem will be encrypted.
317bool encrypt_inplace(const std::string& crypto_blkdev, const std::string& real_blkdev,
Eric Biggers640a1a92022-03-09 20:39:04 +0000318 uint64_t nr_sec) {
Eric Biggersf038c5f2020-11-03 14:11:02 -0800319 LOG(DEBUG) << "encrypt_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << nr_sec
Eric Biggers640a1a92022-03-09 20:39:04 +0000320 << ")";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700321
Eric Biggersf038c5f2020-11-03 14:11:02 -0800322 InPlaceEncrypter encrypter;
Eric Biggers640a1a92022-03-09 20:39:04 +0000323 return encrypter.EncryptInPlace(crypto_blkdev, real_blkdev, nr_sec);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700324}