blob: 8baed784d7e1bdf3e224ee84fed80ce9d8240dcf [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 Crowley14c8c072018-09-18 13:30:21 -070023#include <time.h>
Paul Crowleyf71ace32016-06-02 11:01:19 -070024
25#include <algorithm>
Eric Biggersf038c5f2020-11-03 14:11:02 -080026#include <vector>
Paul Crowleyf71ace32016-06-02 11:01:19 -070027
Paul Crowley772cc852018-02-01 09:53:27 -080028#include <android-base/logging.h>
29#include <android-base/properties.h>
Eric Biggersf038c5f2020-11-03 14:11:02 -080030#include <android-base/unique_fd.h>
Paul Crowleyf71ace32016-06-02 11:01:19 -070031
Eric Biggersf038c5f2020-11-03 14:11:02 -080032enum EncryptInPlaceError {
33 kSuccess,
34 kFailed,
35 kFilesystemNotFound,
Paul Crowleyf71ace32016-06-02 11:01:19 -070036};
37
Eric Biggersf038c5f2020-11-03 14:11:02 -080038static uint64_t round_up(uint64_t val, size_t amount) {
39 if (val % amount) val += amount - (val % amount);
40 return val;
41}
Paul Crowleyf71ace32016-06-02 11:01:19 -070042
Eric Biggersf038c5f2020-11-03 14:11:02 -080043class InPlaceEncrypter {
44 public:
45 bool EncryptInPlace(const std::string& crypto_blkdev, const std::string& real_blkdev,
46 uint64_t nr_sec, bool set_progress_properties);
47 bool ProcessUsedBlock(uint64_t block_num);
48
49 private:
50 // aligned 32K writes tends to make flash happy.
51 // SD card association recommends it.
52 static const size_t kIOBufferSize = 32768;
53
54 // Avoid spamming the logs. Print the "Encrypting blocks" log message once
55 // every 10000 blocks (which is usually every 40 MB or so), and once at the end.
56 static const int kLogInterval = 10000;
57
58 std::string DescribeFilesystem();
59 void InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt, uint64_t total_blocks,
60 unsigned int block_size);
61 void UpdateProgress(size_t blocks, bool done);
62 bool EncryptPendingData();
63 bool DoEncryptInPlace();
64
65 // ext4 methods
66 bool ReadExt4BlockBitmap(uint32_t group, uint8_t* buf);
67 uint64_t FirstBlockInGroup(uint32_t group);
68 uint32_t NumBlocksInGroup(uint32_t group);
69 uint32_t NumBaseMetaBlocksInGroup(uint64_t group);
70 EncryptInPlaceError EncryptInPlaceExt4();
71
72 // f2fs methods
73 EncryptInPlaceError EncryptInPlaceF2fs();
74
75 std::string real_blkdev_;
76 std::string crypto_blkdev_;
77 uint64_t nr_sec_;
78 bool set_progress_properties_;
79
80 android::base::unique_fd realfd_;
81 android::base::unique_fd cryptofd_;
82
83 time_t time_started_;
84 int remaining_time_;
85
86 std::string fs_type_;
87 uint64_t blocks_done_;
88 uint64_t blocks_to_encrypt_;
89 unsigned int block_size_;
90 unsigned int cur_pct_;
91
92 std::vector<uint8_t> io_buffer_;
93 uint64_t first_pending_block_;
94 size_t blocks_pending_;
95};
96
97std::string InPlaceEncrypter::DescribeFilesystem() {
98 if (fs_type_.empty())
99 return "full block device " + real_blkdev_;
100 else
101 return fs_type_ + " filesystem on " + real_blkdev_;
102}
103
104// Finishes initializing the encrypter, now that the filesystem details are known.
105void InPlaceEncrypter::InitFs(const std::string& fs_type, uint64_t blocks_to_encrypt,
106 uint64_t total_blocks, unsigned int block_size) {
107 fs_type_ = fs_type;
108 blocks_done_ = 0;
109 blocks_to_encrypt_ = blocks_to_encrypt;
110 block_size_ = block_size;
111 cur_pct_ = 0;
112
113 // Allocate the I/O buffer. kIOBufferSize should always be a multiple of
114 // the filesystem block size, but round it up just in case.
115 io_buffer_.resize(round_up(kIOBufferSize, block_size));
116 first_pending_block_ = 0;
117 blocks_pending_ = 0;
118
119 LOG(INFO) << "Encrypting " << DescribeFilesystem() << " in-place via " << crypto_blkdev_;
120 LOG(INFO) << blocks_to_encrypt << " blocks (" << (blocks_to_encrypt * block_size) / 1000000
121 << " MB) of " << total_blocks << " blocks are in-use";
122}
123
124void InPlaceEncrypter::UpdateProgress(size_t blocks, bool done) {
125 // A log message already got printed for blocks_done_ if one was due, so the
126 // next message will be due at the *next* block rounded up to kLogInterval.
127 uint64_t blocks_next_msg = round_up(blocks_done_ + 1, kLogInterval);
128
129 blocks_done_ += blocks;
130
131 // Ensure that a log message gets printed at the end, but not if one was
132 // already printed due to the block count being a multiple of kLogInterval.
133 // E.g. we want to show "50000 of 50327" and then "50327 of "50327", but not
134 // "50000 of 50000" and then redundantly "50000 of 50000" again.
135 if (done && blocks_done_ % kLogInterval != 0) blocks_next_msg = blocks_done_;
136
137 if (blocks_done_ >= blocks_next_msg)
138 LOG(DEBUG) << "Encrypted " << blocks_next_msg << " of " << blocks_to_encrypt_ << " blocks";
139
140 if (!set_progress_properties_) return;
141
142 uint64_t new_pct;
143 if (done) {
144 new_pct = 100;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700145 } else {
Eric Biggersf038c5f2020-11-03 14:11:02 -0800146 new_pct = (blocks_done_ * 100) / std::max<uint64_t>(blocks_to_encrypt_, 1);
147 new_pct = std::min<uint64_t>(new_pct, 99);
148 }
149 if (new_pct > cur_pct_) {
150 cur_pct_ = new_pct;
151 android::base::SetProperty("vold.encrypt_progress", std::to_string(new_pct));
Paul Crowleyf71ace32016-06-02 11:01:19 -0700152 }
153
Eric Biggersf038c5f2020-11-03 14:11:02 -0800154 if (cur_pct_ >= 5) {
Paul Crowleyf71ace32016-06-02 11:01:19 -0700155 struct timespec time_now;
156 if (clock_gettime(CLOCK_MONOTONIC, &time_now)) {
Eric Biggersf038c5f2020-11-03 14:11:02 -0800157 PLOG(WARNING) << "Error getting time while updating encryption progress";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700158 } else {
Eric Biggersf038c5f2020-11-03 14:11:02 -0800159 double elapsed_time = difftime(time_now.tv_sec, time_started_);
160
161 uint64_t remaining_blocks = 0;
162 if (blocks_done_ < blocks_to_encrypt_)
163 remaining_blocks = blocks_to_encrypt_ - blocks_done_;
164
165 int remaining_time = 0;
166 if (blocks_done_ != 0)
167 remaining_time = (int)(elapsed_time * remaining_blocks / blocks_done_);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700168
169 // Change time only if not yet set, lower, or a lot higher for
170 // best user experience
Eric Biggersf038c5f2020-11-03 14:11:02 -0800171 if (remaining_time_ == -1 || remaining_time < remaining_time_ ||
172 remaining_time > remaining_time_ + 60) {
173 remaining_time_ = remaining_time;
174 android::base::SetProperty("vold.encrypt_time_remaining",
175 std::to_string(remaining_time));
Paul Crowleyf71ace32016-06-02 11:01:19 -0700176 }
177 }
178 }
179}
180
Eric Biggersf038c5f2020-11-03 14:11:02 -0800181bool InPlaceEncrypter::EncryptPendingData() {
182 if (blocks_pending_ == 0) return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700183
Eric Biggersf038c5f2020-11-03 14:11:02 -0800184 ssize_t bytes = blocks_pending_ * block_size_;
185 uint64_t offset = first_pending_block_ * block_size_;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700186
Eric Biggersf038c5f2020-11-03 14:11:02 -0800187 if (pread64(realfd_, &io_buffer_[0], bytes, offset) != bytes) {
188 PLOG(ERROR) << "Error reading real_blkdev " << real_blkdev_ << " for inplace encrypt";
189 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700190 }
191
Eric Biggersf038c5f2020-11-03 14:11:02 -0800192 if (pwrite64(cryptofd_, &io_buffer_[0], bytes, offset) != bytes) {
193 PLOG(ERROR) << "Error writing crypto_blkdev " << crypto_blkdev_ << " for inplace encrypt";
194 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700195 }
196
Eric Biggersf038c5f2020-11-03 14:11:02 -0800197 UpdateProgress(blocks_pending_, false);
198
199 blocks_pending_ = 0;
200 return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700201}
202
Eric Biggersf038c5f2020-11-03 14:11:02 -0800203bool InPlaceEncrypter::ProcessUsedBlock(uint64_t block_num) {
204 // Flush if the amount of pending data has reached the I/O buffer size, if
205 // there's a gap between the pending blocks and the next block (due to
206 // block(s) not being used by the filesystem and thus not needing
207 // encryption), or if the next block will be aligned to the I/O buffer size.
208 if (blocks_pending_ * block_size_ == io_buffer_.size() ||
209 block_num != first_pending_block_ + blocks_pending_ ||
210 (block_num * block_size_) % io_buffer_.size() == 0) {
211 if (!EncryptPendingData()) return false;
212 first_pending_block_ = block_num;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700213 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800214 blocks_pending_++;
215 return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700216}
217
Eric Biggersf038c5f2020-11-03 14:11:02 -0800218// Reads the block bitmap for block group |group| into |buf|.
219bool InPlaceEncrypter::ReadExt4BlockBitmap(uint32_t group, uint8_t* buf) {
220 uint64_t offset = (uint64_t)aux_info.bg_desc[group].bg_block_bitmap * info.block_size;
221 if (pread64(realfd_, buf, info.block_size, offset) != (ssize_t)info.block_size) {
222 PLOG(ERROR) << "Failed to read block bitmap for block group " << group;
223 return false;
224 }
225 return true;
226}
227
228uint64_t InPlaceEncrypter::FirstBlockInGroup(uint32_t group) {
Eric Biggers7e70d692020-11-03 14:11:01 -0800229 return aux_info.first_data_block + (group * (uint64_t)info.blocks_per_group);
230}
231
Eric Biggersf038c5f2020-11-03 14:11:02 -0800232uint32_t InPlaceEncrypter::NumBlocksInGroup(uint32_t group) {
233 uint64_t remaining = aux_info.len_blocks - FirstBlockInGroup(group);
Eric Biggers7e70d692020-11-03 14:11:01 -0800234 return std::min<uint64_t>(info.blocks_per_group, remaining);
235}
236
237// In block groups with an uninitialized block bitmap, we only need to encrypt
238// the backup superblock and the block group descriptors (if they are present).
Eric Biggersf038c5f2020-11-03 14:11:02 -0800239uint32_t InPlaceEncrypter::NumBaseMetaBlocksInGroup(uint64_t group) {
Eric Biggers7e70d692020-11-03 14:11:01 -0800240 if (!ext4_bg_has_super_block(group)) return 0;
241 return 1 + aux_info.bg_desc_blocks;
242}
243
Eric Biggersf038c5f2020-11-03 14:11:02 -0800244EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceExt4() {
245 if (setjmp(setjmp_env)) // NOLINT
246 return kFilesystemNotFound;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700247
Eric Biggersf038c5f2020-11-03 14:11:02 -0800248 if (read_ext(realfd_, 0) != 0) return kFilesystemNotFound;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700249
Eric Biggersf038c5f2020-11-03 14:11:02 -0800250 LOG(DEBUG) << "ext4 filesystem has " << aux_info.groups << " block groups";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700251
Eric Biggersf038c5f2020-11-03 14:11:02 -0800252 uint64_t blocks_to_encrypt = 0;
253 for (uint32_t group = 0; group < aux_info.groups; group++) {
254 if (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT)
255 blocks_to_encrypt += NumBaseMetaBlocksInGroup(group);
Eric Biggers7e70d692020-11-03 14:11:01 -0800256 else
Eric Biggersf038c5f2020-11-03 14:11:02 -0800257 blocks_to_encrypt +=
258 (NumBlocksInGroup(group) - aux_info.bg_desc[group].bg_free_blocks_count);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700259 }
260
Eric Biggersf038c5f2020-11-03 14:11:02 -0800261 InitFs("ext4", blocks_to_encrypt, aux_info.len_blocks, info.block_size);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700262
Eric Biggersf038c5f2020-11-03 14:11:02 -0800263 // Encrypt each block group.
264 std::vector<uint8_t> block_bitmap(info.block_size);
265 for (uint32_t group = 0; group < aux_info.groups; group++) {
266 if (!ReadExt4BlockBitmap(group, &block_bitmap[0])) return kFailed;
267
268 uint64_t first_block_num = FirstBlockInGroup(group);
269 bool uninit = (aux_info.bg_desc[group].bg_flags & EXT4_BG_BLOCK_UNINIT);
270 uint32_t block_count = uninit ? NumBaseMetaBlocksInGroup(group) : NumBlocksInGroup(group);
271
272 // Encrypt each used block in the block group.
273 for (uint32_t i = 0; i < block_count; i++) {
274 if (uninit || bitmap_get_bit(&block_bitmap[0], i))
275 ProcessUsedBlock(first_block_num + i);
276 }
Paul Crowleyf71ace32016-06-02 11:01:19 -0700277 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800278 return kSuccess;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700279}
280
Eric Biggersf038c5f2020-11-03 14:11:02 -0800281static int encrypt_f2fs_block(uint64_t block_num, void* _encrypter) {
282 InPlaceEncrypter* encrypter = reinterpret_cast<InPlaceEncrypter*>(_encrypter);
283 if (!encrypter->ProcessUsedBlock(block_num)) return -1;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700284 return 0;
285}
286
Eric Biggersf038c5f2020-11-03 14:11:02 -0800287EncryptInPlaceError InPlaceEncrypter::EncryptInPlaceF2fs() {
288 std::unique_ptr<struct f2fs_info, void (*)(struct f2fs_info*)> fs_info(
289 generate_f2fs_info(realfd_), free_f2fs_info);
290 if (!fs_info) return kFilesystemNotFound;
291
292 InitFs("f2fs", get_num_blocks_used(fs_info.get()), fs_info->total_blocks, fs_info->block_size);
293 if (run_on_used_blocks(0, fs_info.get(), encrypt_f2fs_block, this) != 0) return kFailed;
294 return kSuccess;
295}
296
297bool InPlaceEncrypter::DoEncryptInPlace() {
298 EncryptInPlaceError rc;
299
300 rc = EncryptInPlaceExt4();
301 if (rc != kFilesystemNotFound) return rc == kSuccess;
302
303 rc = EncryptInPlaceF2fs();
304 if (rc != kFilesystemNotFound) return rc == kSuccess;
305
306 LOG(WARNING) << "No recognized filesystem found on " << real_blkdev_
307 << ". Falling back to encrypting the full block device.";
308 InitFs("", nr_sec_, nr_sec_, 512);
309 for (uint64_t i = 0; i < nr_sec_; i++) {
310 if (!ProcessUsedBlock(i)) return false;
311 }
312 return true;
313}
314
315bool InPlaceEncrypter::EncryptInPlace(const std::string& crypto_blkdev,
316 const std::string& real_blkdev, uint64_t nr_sec,
317 bool set_progress_properties) {
Denis Hsu1740eff2019-12-26 16:06:37 +0800318 struct timespec time_started = {0};
319
Eric Biggersf038c5f2020-11-03 14:11:02 -0800320 real_blkdev_ = real_blkdev;
321 crypto_blkdev_ = crypto_blkdev;
322 nr_sec_ = nr_sec;
323 set_progress_properties_ = set_progress_properties;
324
325 realfd_.reset(open64(real_blkdev.c_str(), O_RDONLY | O_CLOEXEC));
326 if (realfd_ < 0) {
327 PLOG(ERROR) << "Error opening real_blkdev " << real_blkdev << " for inplace encrypt";
328 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700329 }
330
Eric Biggersf038c5f2020-11-03 14:11:02 -0800331 cryptofd_.reset(open64(crypto_blkdev.c_str(), O_WRONLY | O_CLOEXEC));
332 if (cryptofd_ < 0) {
333 PLOG(ERROR) << "Error opening crypto_blkdev " << crypto_blkdev << " for inplace encrypt";
334 return false;
335 }
Paul Crowleyf71ace32016-06-02 11:01:19 -0700336
Denis Hsu1740eff2019-12-26 16:06:37 +0800337 if (clock_gettime(CLOCK_MONOTONIC, &time_started)) {
Eric Biggersf038c5f2020-11-03 14:11:02 -0800338 PLOG(WARNING) << "Error getting time at start of in-place encryption";
Denis Hsu1740eff2019-12-26 16:06:37 +0800339 // Note - continue anyway - we'll run with 0
340 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800341 time_started_ = time_started.tv_sec;
342 remaining_time_ = -1;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700343
Eric Biggersf038c5f2020-11-03 14:11:02 -0800344 bool success = DoEncryptInPlace();
Denis Hsu1740eff2019-12-26 16:06:37 +0800345
Eric Biggersf038c5f2020-11-03 14:11:02 -0800346 if (success) success &= EncryptPendingData();
347
348 if (!success) {
349 LOG(ERROR) << "In-place encryption of " << DescribeFilesystem() << " failed";
350 return false;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700351 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800352 if (blocks_done_ != blocks_to_encrypt_) {
353 LOG(WARNING) << "blocks_to_encrypt (" << blocks_to_encrypt_
354 << ") was incorrect; we actually encrypted " << blocks_done_
355 << " blocks. Encryption progress was inaccurate";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700356 }
Eric Biggersf038c5f2020-11-03 14:11:02 -0800357 // Make sure vold.encrypt_progress gets set to 100.
358 UpdateProgress(0, true);
359 LOG(INFO) << "Successfully encrypted " << DescribeFilesystem();
360 return true;
Paul Crowleyf71ace32016-06-02 11:01:19 -0700361}
362
Eric Biggersf038c5f2020-11-03 14:11:02 -0800363// Encrypts |real_blkdev| in-place by reading the data from |real_blkdev| and
364// writing it to |crypto_blkdev|, which should be a dm-crypt or dm-default-key
365// device backed by |real_blkdev|. The size to encrypt is |nr_sec| 512-byte
366// sectors; however, if a filesystem is detected, then its size will be used
367// instead, and only the in-use blocks of the filesystem will be encrypted.
368bool encrypt_inplace(const std::string& crypto_blkdev, const std::string& real_blkdev,
369 uint64_t nr_sec, bool set_progress_properties) {
370 LOG(DEBUG) << "encrypt_inplace(" << crypto_blkdev << ", " << real_blkdev << ", " << nr_sec
371 << ", " << (set_progress_properties ? "true" : "false") << ")";
Paul Crowleyf71ace32016-06-02 11:01:19 -0700372
Eric Biggersf038c5f2020-11-03 14:11:02 -0800373 InPlaceEncrypter encrypter;
374 return encrypter.EncryptInPlace(crypto_blkdev, real_blkdev, nr_sec, set_progress_properties);
Paul Crowleyf71ace32016-06-02 11:01:19 -0700375}