[automerger skipped] Convert to lower fs path for createObb(). am: 772008dbca -s ours
am skip reason: Change-Id I98222bf844a6b7d8ec0d9873eddc71f61aa68c90 with SHA-1 d6a612ac20 is in history
Original change: https://googleplex-android-review.googlesource.com/c/platform/system/vold/+/13092479
Change-Id: I21651924e7b7f946aefb5e30a6d1fe17024b8e96
diff --git a/Android.bp b/Android.bp
index b033647..1c0ed17 100644
--- a/Android.bp
+++ b/Android.bp
@@ -54,6 +54,7 @@
"libdiskconfig",
"libext4_utils",
"libf2fs_sparseblock",
+ "libgsi",
"libhardware",
"libhardware_legacy",
"libincfs",
@@ -89,7 +90,7 @@
export_aidl_headers: true,
},
whole_static_libs: [
- "libincremental_aidl-cpp",
+ "libincremental_aidl-unstable-cpp",
],
}
@@ -110,7 +111,6 @@
srcs: [
"AppFuseUtil.cpp",
"Benchmark.cpp",
- "CheckEncryption.cpp",
"Checkpoint.cpp",
"CryptoType.cpp",
"Devmapper.cpp",
@@ -149,15 +149,6 @@
"model/VolumeEncryption.cpp",
],
product_variables: {
- arc: {
- exclude_srcs: [
- "model/ObbVolume.cpp",
- ],
- static_libs: [
- "arc_services_aidl",
- "libarcobbvolume",
- ],
- },
debuggable: {
cppflags: ["-D__ANDROID_DEBUGGABLE__"],
},
@@ -180,14 +171,6 @@
srcs: ["main.cpp"],
static_libs: ["libvold"],
- product_variables: {
- arc: {
- static_libs: [
- "arc_services_aidl",
- "libarcobbvolume",
- ],
- },
- },
init_rc: [
"vold.rc",
"wait_for_keymaster.rc",
diff --git a/CheckEncryption.cpp b/CheckEncryption.cpp
deleted file mode 100644
index ffa3698..0000000
--- a/CheckEncryption.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "CheckEncryption.h"
-#include "FileDeviceUtils.h"
-#include "Utils.h"
-#include "VolumeManager.h"
-
-#include <android-base/file.h>
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include <cutils/iosched_policy.h>
-#include <private/android_filesystem_config.h>
-
-#include <sstream>
-
-#include <sys/resource.h>
-#include <sys/time.h>
-#include <unistd.h>
-
-#include <assert.h>
-#include <fcntl.h>
-#include <linux/fs.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-using android::base::unique_fd;
-
-using android::base::ReadFileToString;
-using android::base::WriteStringToFile;
-
-namespace android {
-namespace vold {
-
-constexpr uint32_t max_extents = 32;
-constexpr size_t bytecount = 8;
-constexpr int repeats = 256;
-
-bool check_file(const std::string& needle) {
- LOG(DEBUG) << "checkEncryption check_file: " << needle;
- auto haystack = android::vold::BlockDeviceForPath(needle);
- if (haystack.empty()) {
- LOG(ERROR) << "Failed to find device for path: " << needle;
- return false;
- }
-
- std::string randombytes;
- if (ReadRandomBytes(bytecount, randombytes) != 0) {
- LOG(ERROR) << "Failed to read random bytes";
- return false;
- }
- std::string randomhex;
- StrToHex(randombytes, randomhex);
- std::ostringstream os;
- for (int i = 0; i < repeats; i++) os << randomhex;
- auto towrite = os.str();
-
- if (access(needle.c_str(), F_OK) == 0) {
- if (unlink(needle.c_str()) != 0) {
- PLOG(ERROR) << "Failed to unlink " << needle;
- return false;
- }
- }
- LOG(DEBUG) << "Writing to " << needle;
- if (!WriteStringToFile(towrite, needle)) {
- PLOG(ERROR) << "Failed to write " << needle;
- return false;
- }
- sync();
-
- unique_fd haystack_fd(open(haystack.c_str(), O_RDONLY | O_CLOEXEC));
- if (haystack_fd.get() == -1) {
- PLOG(ERROR) << "Failed to open " << haystack;
- return false;
- }
-
- auto fiemap = PathFiemap(needle, max_extents);
-
- std::string area;
- for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++) {
- auto xt = &(fiemap->fm_extents[i]);
- LOG(DEBUG) << "Extent " << i << " at " << xt->fe_physical << " length " << xt->fe_length;
- if (lseek64(haystack_fd.get(), xt->fe_physical, SEEK_SET) == -1) {
- PLOG(ERROR) << "Failed lseek";
- return false;
- }
- auto toread = xt->fe_length;
- while (toread > 0) {
- char buf[BUFSIZ];
- size_t wlen =
- static_cast<size_t>(std::min(static_cast<typeof(toread)>(sizeof(buf)), toread));
- auto l = read(haystack_fd.get(), buf, wlen);
- if (l < 1) {
- PLOG(ERROR) << "Failed read";
- if (errno != EINTR) {
- return false;
- }
- }
- area.append(buf, l);
- toread -= l;
- }
- }
-
- LOG(DEBUG) << "Searching " << area.size() << " bytes of " << needle;
- LOG(DEBUG) << "First position of blob: " << area.find(randomhex);
- return true;
-}
-
-int CheckEncryption(const std::string& path) {
- auto deNeedle(path);
- deNeedle += "/misc";
- if (android::vold::PrepareDir(deNeedle, 01771, AID_SYSTEM, AID_MISC)) {
- return -1;
- }
- deNeedle += "/vold";
- if (android::vold::PrepareDir(deNeedle, 0700, AID_ROOT, AID_ROOT)) {
- return -1;
- }
- deNeedle += "/checkEncryption";
-
- auto neNeedle(path);
- neNeedle += "/unencrypted/checkEncryption";
-
- check_file(deNeedle);
- check_file(neNeedle);
-
- return 0;
-}
-
-} // namespace vold
-} // namespace android
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 61035e5..755f0e3 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -47,7 +47,6 @@
using android::base::SetProperty;
using android::binder::Status;
using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
using android::fs_mgr::ReadFstabFromFile;
using android::hardware::hidl_string;
using android::hardware::boot::V1_0::BoolResult;
diff --git a/EncryptInplace.cpp b/EncryptInplace.cpp
index b1bd11d..057b3ef 100644
--- a/EncryptInplace.cpp
+++ b/EncryptInplace.cpp
@@ -20,618 +20,361 @@
#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.
- */
-#ifndef CONFIG_HW_DISK_ENCRYPTION
-#define BLOCKS_AT_A_TIME 8
-#else
-#define BLOCKS_AT_A_TIME 1024
-#endif
-
-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;
+ 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;
- LOG(ERROR) << "Encrypting filesystem in place...";
+ bool success = DoEncryptInPlace();
- i = previously_encrypted_upto + 1 - *size_already_done;
+ if (success) success &= EncryptPendingData();
- if (lseek64(realfd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
- PLOG(ERROR) << "Cannot seek to previously encrypted point on " << real_blkdev;
- goto errout;
+ if (success && fsync(cryptofd_) != 0) {
+ PLOG(ERROR) << "Error syncing " << crypto_blkdev_;
+ success = false;
}
- if (lseek64(cryptofd, i * CRYPT_SECTOR_SIZE, SEEK_SET) < 0) {
- PLOG(ERROR) << "Cannot seek to previously encrypted point on " << crypto_blkdev;
- goto errout;
+ 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);
}
diff --git a/EncryptInplace.h b/EncryptInplace.h
index a2b46cf..480a47c 100644
--- a/EncryptInplace.h
+++ b/EncryptInplace.h
@@ -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
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 061ee7c..ff8c1f4 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -67,6 +67,7 @@
using android::base::StringPrintf;
using android::fs_mgr::GetEntryForMountPoint;
using android::vold::BuildDataPath;
+using android::vold::IsDotOrDotDot;
using android::vold::IsFilesystemSupported;
using android::vold::kEmptyAuthentication;
using android::vold::KeyBuffer;
@@ -140,6 +141,7 @@
}
break;
}
+ if (IsDotOrDotDot(*entry)) continue;
if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') {
LOG(DEBUG) << "Skipping non-key " << entry->d_name;
continue;
@@ -198,7 +200,7 @@
auto const paths = get_ce_key_paths(directory_path);
for (auto const ce_key_path : paths) {
LOG(DEBUG) << "Trying user CE key " << ce_key_path;
- if (retrieveKey(ce_key_path, auth, ce_key, false)) {
+ if (retrieveKey(ce_key_path, auth, ce_key)) {
LOG(DEBUG) << "Successfully retrieved key";
fixate_user_ce_key(directory_path, ce_key_path, paths);
return true;
@@ -269,10 +271,9 @@
// HEH as default was always a mistake. Use the libfscrypt default (CTS)
// for devices launching on versions above Android 10.
auto first_api_level = GetFirstApiLevel();
- constexpr uint64_t pre_gki_level = 29;
auto filenames_mode =
android::base::GetProperty("ro.crypto.volume.filenames_mode",
- first_api_level > pre_gki_level ? "" : "aes-256-heh");
+ first_api_level > __ANDROID_API_Q__ ? "" : "aes-256-heh");
auto options_string = android::base::GetProperty("ro.crypto.volume.options",
contents_mode + ":" + filenames_mode);
if (!ParseOptionsForApiLevel(first_api_level, options_string, options)) {
@@ -392,6 +393,7 @@
}
break;
}
+ if (IsDotOrDotDot(*entry)) continue;
if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) {
LOG(DEBUG) << "Skipping non-de-key " << entry->d_name;
continue;
@@ -399,7 +401,7 @@
userid_t user_id = std::stoi(entry->d_name);
auto key_path = de_dir + "/" + entry->d_name;
KeyBuffer de_key;
- if (!retrieveKey(key_path, kEmptyAuthentication, &de_key, false)) return false;
+ if (!retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false;
EncryptionPolicy de_policy;
if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
auto ret = s_de_policies.insert({user_id, de_policy});
@@ -433,7 +435,7 @@
KeyBuffer device_key;
if (!retrieveOrGenerateKey(device_key_path, device_key_temp, kEmptyAuthentication,
- makeGen(options), &device_key, false))
+ makeGen(options), &device_key))
return false;
EncryptionPolicy device_policy;
@@ -667,7 +669,7 @@
EncryptionOptions options;
if (!get_volume_file_encryption_options(&options)) return false;
KeyBuffer key;
- if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key, false))
+ if (!retrieveOrGenerateKey(key_path, key_path + "_tmp", auth, makeGen(options), &key))
return false;
if (!install_storage_key(BuildDataPath(volume_uuid), options, key, policy)) return false;
return true;
@@ -686,12 +688,12 @@
auto const directory_path = get_ce_key_directory_path(user_id);
KeyBuffer ce_key;
std::string ce_key_current_path = get_ce_key_current_path(directory_path);
- if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key, false)) {
+ if (retrieveKey(ce_key_current_path, retrieve_auth, &ce_key)) {
LOG(DEBUG) << "Successfully retrieved key";
// TODO(147732812): Remove this once Locksettingservice is fixed.
// Currently it calls fscrypt_clear_user_key_auth with a secret when lockscreen is
// changed from swipe to none or vice-versa
- } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key, false)) {
+ } else if (retrieveKey(ce_key_current_path, kEmptyAuthentication, &ce_key)) {
LOG(DEBUG) << "Successfully retrieved key with empty auth";
} else {
LOG(ERROR) << "Failed to retrieve key for user " << user_id;
@@ -792,6 +794,11 @@
static bool prepare_subdirs(const std::string& action, const std::string& volume_uuid,
userid_t user_id, int flags) {
+ // TODO(b/141677108): Remove this & make it the default behavior
+ if (android::base::GetProperty("ro.vold.level_from_user", "0") == "1") {
+ flags |= android::os::IVold::STORAGE_FLAG_LEVEL_FROM_USER;
+ }
+
if (0 != android::vold::ForkExecvp(
std::vector<std::string>{prepare_subdirs_path, action, volume_uuid,
std::to_string(user_id), std::to_string(flags)})) {
@@ -969,6 +976,7 @@
}
break;
}
+ if (IsDotOrDotDot(*entry)) continue;
if (entry->d_type != DT_DIR || entry->d_name[0] == '.') {
LOG(DEBUG) << "Skipping non-user " << entry->d_name;
continue;
diff --git a/IdleMaint.cpp b/IdleMaint.cpp
index 2b5a8f1..e4a1806 100644
--- a/IdleMaint.cpp
+++ b/IdleMaint.cpp
@@ -17,6 +17,7 @@
#include "IdleMaint.h"
#include "FileDeviceUtils.h"
#include "Utils.h"
+#include "VoldUtil.h"
#include "VolumeManager.h"
#include "model/PrivateVolume.h"
@@ -45,8 +46,6 @@
using android::base::StringPrintf;
using android::base::Timer;
using android::base::WriteStringToFile;
-using android::fs_mgr::Fstab;
-using android::fs_mgr::ReadDefaultFstab;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::health::storage::V1_0::IStorage;
@@ -104,17 +103,18 @@
}
static void addFromFstab(std::list<std::string>* paths, PathTypes path_type) {
- Fstab fstab;
- ReadDefaultFstab(&fstab);
-
std::string previous_mount_point;
- for (const auto& entry : fstab) {
- // Skip raw partitions.
- if (entry.fs_type == "emmc" || entry.fs_type == "mtd") {
+ for (const auto& entry : fstab_default) {
+ // Skip raw partitions and swap space.
+ if (entry.fs_type == "emmc" || entry.fs_type == "mtd" || entry.fs_type == "swap") {
continue;
}
- // Skip read-only filesystems
- if (entry.flags & MS_RDONLY) {
+ // Skip read-only filesystems and bind mounts.
+ if (entry.flags & (MS_RDONLY | MS_BIND)) {
+ continue;
+ }
+ // Skip anything without an underlying block device, e.g. virtiofs.
+ if (entry.blk_device[0] != '/') {
continue;
}
if (entry.fs_mgr_flags.vold_managed) {
@@ -253,11 +253,8 @@
}
static void runDevGcFstab(void) {
- Fstab fstab;
- ReadDefaultFstab(&fstab);
-
std::string path;
- for (const auto& entry : fstab) {
+ for (const auto& entry : fstab_default) {
if (!entry.sysfs_path.empty()) {
path = entry.sysfs_path;
break;
diff --git a/KeyStorage.cpp b/KeyStorage.cpp
index 951536b..8147827 100644
--- a/KeyStorage.cpp
+++ b/KeyStorage.cpp
@@ -21,6 +21,7 @@
#include "ScryptParameters.h"
#include "Utils.h"
+#include <algorithm>
#include <thread>
#include <vector>
@@ -141,9 +142,12 @@
if (!keymaster) return false;
std::string key_temp;
auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8);
- paramBuilder.Authorization(km::TAG_ROLLBACK_RESISTANCE);
paramBuilder.Authorization(km::TAG_STORAGE_KEY);
- if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
+ auto paramsWithRollback = paramBuilder;
+ paramsWithRollback.Authorization(km::TAG_ROLLBACK_RESISTANCE);
+ if (!keymaster.generateKey(paramsWithRollback, &key_temp)) {
+ if (!keymaster.generateKey(paramBuilder, &key_temp)) return false;
+ }
*key = KeyBuffer(key_temp.size());
memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
return true;
@@ -205,75 +209,161 @@
return true;
}
-static void deferedKmDeleteKey(const std::string& kmkey) {
- while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
- LOG(ERROR) << "Wait for boot timed out";
+static std::mutex key_upgrade_lock;
+
+// List of key directories that have had their Keymaster key upgraded during
+// this boot and written to "keymaster_key_blob_upgraded", but replacing the old
+// key was delayed due to an active checkpoint. Protected by key_upgrade_lock.
+static std::vector<std::string> key_dirs_to_commit;
+
+// Replaces |dir|/keymaster_key_blob with |dir|/keymaster_key_blob_upgraded and
+// deletes the old key from Keymaster.
+static bool CommitUpgradedKey(Keymaster& keymaster, const std::string& dir) {
+ auto blob_file = dir + "/" + kFn_keymaster_key_blob;
+ auto upgraded_blob_file = dir + "/" + kFn_keymaster_key_blob_upgraded;
+
+ std::string blob;
+ if (!readFileToString(blob_file, &blob)) return false;
+
+ if (rename(upgraded_blob_file.c_str(), blob_file.c_str()) != 0) {
+ PLOG(ERROR) << "Failed to rename " << upgraded_blob_file << " to " << blob_file;
+ return false;
}
+ // Ensure that the rename is persisted before deleting the Keymaster key.
+ if (!FsyncDirectory(dir)) return false;
+
+ if (!keymaster || !keymaster.deleteKey(blob)) {
+ LOG(WARNING) << "Failed to delete old key " << blob_file
+ << " from Keymaster; continuing anyway";
+ // Continue on, but the space in Keymaster used by the old key won't be freed.
+ }
+ return true;
+}
+
+static void DeferredCommitKeys() {
+ android::base::WaitForProperty("vold.checkpoint_committed", "1");
+ LOG(INFO) << "Committing upgraded keys";
Keymaster keymaster;
- if (!keymaster || !keymaster.deleteKey(kmkey)) {
- LOG(ERROR) << "Defered Key deletion failed during upgrade";
+ if (!keymaster) {
+ LOG(ERROR) << "Failed to open Keymaster; old keys won't be deleted from Keymaster";
+ // Continue on, but the space in Keymaster used by the old keys won't be freed.
+ }
+ std::lock_guard<std::mutex> lock(key_upgrade_lock);
+ for (auto& dir : key_dirs_to_commit) {
+ LOG(INFO) << "Committing upgraded key " << dir;
+ CommitUpgradedKey(keymaster, dir);
+ }
+ key_dirs_to_commit.clear();
+}
+
+// Returns true if the Keymaster key in |dir| has already been upgraded and is
+// pending being committed. Assumes that key_upgrade_lock is held.
+static bool IsKeyCommitPending(const std::string& dir) {
+ for (const auto& dir_to_commit : key_dirs_to_commit) {
+ if (IsSameFile(dir, dir_to_commit)) return true;
+ }
+ return false;
+}
+
+// Schedules the upgraded Keymaster key in |dir| to be committed later.
+// Assumes that key_upgrade_lock is held.
+static void ScheduleKeyCommit(const std::string& dir) {
+ if (key_dirs_to_commit.empty()) std::thread(DeferredCommitKeys).detach();
+ key_dirs_to_commit.push_back(dir);
+}
+
+static void CancelPendingKeyCommit(const std::string& dir) {
+ std::lock_guard<std::mutex> lock(key_upgrade_lock);
+ for (auto it = key_dirs_to_commit.begin(); it != key_dirs_to_commit.end(); it++) {
+ if (IsSameFile(*it, dir)) {
+ LOG(DEBUG) << "Cancelling pending commit of upgraded key " << dir
+ << " because it is being destroyed";
+ key_dirs_to_commit.erase(it);
+ break;
+ }
}
}
-bool kmDeleteKey(Keymaster& keymaster, const std::string& kmKey) {
- bool needs_cp = cp_needsCheckpoint();
-
- if (needs_cp) {
- std::thread(deferedKmDeleteKey, kmKey).detach();
- LOG(INFO) << "Deferring Key deletion during upgrade";
- return true;
- } else {
- return keymaster.deleteKey(kmKey);
+// Deletes a leftover upgraded key, if present. An upgraded key can be left
+// over if an update failed, or if we rebooted before committing the key in a
+// freak accident. Either way, we can re-upgrade the key if we need to.
+static void DeleteUpgradedKey(Keymaster& keymaster, const std::string& path) {
+ if (pathExists(path)) {
+ LOG(DEBUG) << "Deleting leftover upgraded key " << path;
+ std::string blob;
+ if (!android::base::ReadFileToString(path, &blob)) {
+ LOG(WARNING) << "Failed to read leftover upgraded key " << path
+ << "; continuing anyway";
+ } else if (!keymaster.deleteKey(blob)) {
+ LOG(WARNING) << "Failed to delete leftover upgraded key " << path
+ << " from Keymaster; continuing anyway";
+ }
+ if (unlink(path.c_str()) != 0) {
+ LOG(WARNING) << "Failed to unlink leftover upgraded key " << path
+ << "; continuing anyway";
+ }
}
}
-static KeymasterOperation begin(Keymaster& keymaster, const std::string& dir,
- km::KeyPurpose purpose, const km::AuthorizationSet& keyParams,
- const km::AuthorizationSet& opParams,
- const km::HardwareAuthToken& authToken,
- km::AuthorizationSet* outParams, bool keepOld) {
- auto kmKeyPath = dir + "/" + kFn_keymaster_key_blob;
- std::string kmKey;
- if (!readFileToString(kmKeyPath, &kmKey)) return KeymasterOperation();
+// Begins a Keymaster operation using the key stored in |dir|.
+static KeymasterOperation BeginKeymasterOp(Keymaster& keymaster, const std::string& dir,
+ km::KeyPurpose purpose,
+ const km::AuthorizationSet& keyParams,
+ const km::AuthorizationSet& opParams,
+ const km::HardwareAuthToken& authToken,
+ km::AuthorizationSet* outParams) {
km::AuthorizationSet inParams(keyParams);
inParams.append(opParams.begin(), opParams.end());
- for (;;) {
- auto opHandle = keymaster.begin(purpose, kmKey, inParams, authToken, outParams);
- if (opHandle) {
- return opHandle;
- }
- if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
- LOG(DEBUG) << "Upgrading key: " << dir;
- std::string newKey;
- if (!keymaster.upgradeKey(kmKey, keyParams, &newKey)) return KeymasterOperation();
- auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
- if (!writeStringToFile(newKey, newKeyPath)) return KeymasterOperation();
- if (!keepOld) {
- if (rename(newKeyPath.c_str(), kmKeyPath.c_str()) != 0) {
- PLOG(ERROR) << "Unable to move upgraded key to location: " << kmKeyPath;
- return KeymasterOperation();
- }
- if (!android::vold::FsyncDirectory(dir)) {
- LOG(ERROR) << "Key dir sync failed: " << dir;
- return KeymasterOperation();
- }
- if (!kmDeleteKey(keymaster, kmKey)) {
- LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
- }
- }
- kmKey = newKey;
- LOG(INFO) << "Key upgraded: " << dir;
+
+ auto blob_file = dir + "/" + kFn_keymaster_key_blob;
+ auto upgraded_blob_file = dir + "/" + kFn_keymaster_key_blob_upgraded;
+
+ std::lock_guard<std::mutex> lock(key_upgrade_lock);
+
+ std::string blob;
+ bool already_upgraded = IsKeyCommitPending(dir);
+ if (already_upgraded) {
+ LOG(DEBUG)
+ << blob_file
+ << " was already upgraded and is waiting to be committed; using the upgraded blob";
+ if (!readFileToString(upgraded_blob_file, &blob)) return KeymasterOperation();
+ } else {
+ DeleteUpgradedKey(keymaster, upgraded_blob_file);
+ if (!readFileToString(blob_file, &blob)) return KeymasterOperation();
}
+
+ auto opHandle = keymaster.begin(purpose, blob, inParams, authToken, outParams);
+ if (opHandle) return opHandle;
+ if (opHandle.errorCode() != km::ErrorCode::KEY_REQUIRES_UPGRADE) return opHandle;
+
+ if (already_upgraded) {
+ LOG(ERROR) << "Unexpected case; already-upgraded key " << upgraded_blob_file
+ << " still requires upgrade";
+ return KeymasterOperation();
+ }
+ LOG(INFO) << "Upgrading key: " << blob_file;
+ if (!keymaster.upgradeKey(blob, keyParams, &blob)) return KeymasterOperation();
+ if (!writeStringToFile(blob, upgraded_blob_file)) return KeymasterOperation();
+ if (cp_needsCheckpoint()) {
+ LOG(INFO) << "Wrote upgraded key to " << upgraded_blob_file
+ << "; delaying commit due to checkpoint";
+ ScheduleKeyCommit(dir);
+ } else {
+ if (!CommitUpgradedKey(keymaster, dir)) return KeymasterOperation();
+ LOG(INFO) << "Key upgraded: " << blob_file;
+ }
+
+ return keymaster.begin(purpose, blob, inParams, authToken, outParams);
}
static bool encryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const km::AuthorizationSet& keyParams,
- const km::HardwareAuthToken& authToken, const KeyBuffer& message,
- std::string* ciphertext, bool keepOld) {
+ const km::HardwareAuthToken& authToken,
+ const KeyBuffer& message, std::string* ciphertext) {
km::AuthorizationSet opParams;
km::AuthorizationSet outParams;
- auto opHandle = begin(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams, authToken,
- &outParams, keepOld);
+ auto opHandle = BeginKeymasterOp(keymaster, dir, km::KeyPurpose::ENCRYPT, keyParams, opParams,
+ authToken, &outParams);
if (!opHandle) return false;
auto nonceBlob = outParams.GetTagValue(km::TAG_NONCE);
if (!nonceBlob.isOk()) {
@@ -297,14 +387,13 @@
static bool decryptWithKeymasterKey(Keymaster& keymaster, const std::string& dir,
const km::AuthorizationSet& keyParams,
const km::HardwareAuthToken& authToken,
- const std::string& ciphertext, KeyBuffer* message,
- bool keepOld) {
+ const std::string& ciphertext, KeyBuffer* message) {
auto nonce = ciphertext.substr(0, GCM_NONCE_BYTES);
auto bodyAndMac = ciphertext.substr(GCM_NONCE_BYTES);
auto opParams = km::AuthorizationSetBuilder().Authorization(km::TAG_NONCE,
km::support::blob2hidlVec(nonce));
- auto opHandle = begin(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams, authToken,
- nullptr, keepOld);
+ auto opHandle = BeginKeymasterOp(keymaster, dir, km::KeyPurpose::DECRYPT, keyParams, opParams,
+ authToken, nullptr);
if (!opHandle) return false;
if (!opHandle.updateCompletely(bodyAndMac, message)) return false;
if (!opHandle.finish(nullptr)) return false;
@@ -510,8 +599,7 @@
km::AuthorizationSet keyParams;
km::HardwareAuthToken authToken;
std::tie(keyParams, authToken) = beginParams(auth, appId);
- if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey,
- false))
+ if (!encryptWithKeymasterKey(keymaster, dir, keyParams, authToken, key, &encryptedKey))
return false;
} else {
if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
@@ -540,8 +628,7 @@
return true;
}
-bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
- bool keepOld) {
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key) {
std::string version;
if (!readFileToString(dir + "/" + kFn_version, &version)) return false;
if (version != kCurrentVersion) {
@@ -566,8 +653,7 @@
km::AuthorizationSet keyParams;
km::HardwareAuthToken authToken;
std::tie(keyParams, authToken) = beginParams(auth, appId);
- if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key,
- keepOld))
+ if (!decryptWithKeymasterKey(keymaster, dir, keyParams, authToken, encryptedMessage, key))
return false;
} else {
if (!decryptWithoutKeymaster(appId, encryptedMessage, key)) return false;
@@ -575,12 +661,13 @@
return true;
}
-static bool deleteKey(const std::string& dir) {
- std::string kmKey;
- if (!readFileToString(dir + "/" + kFn_keymaster_key_blob, &kmKey)) return false;
+static bool DeleteKeymasterKey(const std::string& blob_file) {
+ std::string blob;
+ if (!readFileToString(blob_file, &blob)) return false;
Keymaster keymaster;
if (!keymaster) return false;
- if (!keymaster.deleteKey(kmKey)) return false;
+ LOG(DEBUG) << "Deleting key " << blob_file << " from Keymaster";
+ if (!keymaster.deleteKey(blob)) return false;
return true;
}
@@ -602,19 +689,23 @@
bool destroyKey(const std::string& dir) {
bool success = true;
- // Try each thing, even if previous things failed.
- bool uses_km = pathExists(dir + "/" + kFn_keymaster_key_blob);
- if (uses_km) {
- success &= deleteKey(dir);
- }
+
+ CancelPendingKeyCommit(dir);
+
auto secdiscard_cmd = std::vector<std::string>{
kSecdiscardPath,
"--",
dir + "/" + kFn_encrypted_key,
dir + "/" + kFn_secdiscardable,
};
- if (uses_km) {
- secdiscard_cmd.emplace_back(dir + "/" + kFn_keymaster_key_blob);
+ // Try each thing, even if previous things failed.
+
+ for (auto& fn : {kFn_keymaster_key_blob, kFn_keymaster_key_blob_upgraded}) {
+ auto blob_file = dir + "/" + fn;
+ if (pathExists(blob_file)) {
+ success &= DeleteKeymasterKey(blob_file);
+ secdiscard_cmd.push_back(blob_file);
+ }
}
if (ForkExecvp(secdiscard_cmd) != 0) {
LOG(ERROR) << "secdiscard failed";
diff --git a/KeyStorage.h b/KeyStorage.h
index 5228f08..1eb26ae 100644
--- a/KeyStorage.h
+++ b/KeyStorage.h
@@ -61,20 +61,7 @@
const KeyAuthentication& auth, const KeyBuffer& key);
// Retrieve the key from the named directory.
-//
-// If the key is wrapped by a Keymaster key that requires an upgrade, then that
-// Keymaster key is upgraded. If |keepOld| is false, then the upgraded
-// Keymaster key replaces the original one. As part of this, the original is
-// deleted from Keymaster; however, if a user data checkpoint is active, this
-// part is delayed until the checkpoint is committed.
-//
-// If instead |keepOld| is true, then the upgraded key doesn't actually replace
-// the original one. This is needed *only* if |dir| isn't located in /data and
-// a user data checkpoint is active. In this case the caller must handle
-// replacing the original key if the checkpoint is committed, and deleting the
-// upgraded key if the checkpoint is rolled back.
-bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key,
- bool keepOld);
+bool retrieveKey(const std::string& dir, const KeyAuthentication& auth, KeyBuffer* key);
// Securely destroy the key stored in the named directory and delete the directory.
bool destroyKey(const std::string& dir);
diff --git a/KeyUtil.cpp b/KeyUtil.cpp
index acc42db..9d01e1e 100644
--- a/KeyUtil.cpp
+++ b/KeyUtil.cpp
@@ -30,7 +30,6 @@
#include <android-base/properties.h>
#include <keyutils.h>
-#include <fscrypt_uapi.h>
#include "KeyStorage.h"
#include "Utils.h"
@@ -64,40 +63,37 @@
}
}
+static bool isFsKeyringSupportedImpl() {
+ android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
+
+ // FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY if
+ // the ioctl isn't supported. Otherwise it will fail with another error
+ // code such as EFAULT.
+ //
+ // Note that there's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY,
+ // since it's guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is.
+ // There's also no need to check for support on external volumes separately
+ // from /data, since either the kernel supports the ioctls on all
+ // fscrypt-capable filesystems or it doesn't.
+ errno = 0;
+ (void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
+ if (errno == ENOTTY) {
+ LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY. Falling back to "
+ "session keyring";
+ return false;
+ }
+ if (errno != EFAULT) {
+ PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
+ }
+ LOG(DEBUG) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
+ android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
+ return true;
+}
+
// Return true if the kernel supports the ioctls to add/remove fscrypt keys
// directly to/from the filesystem.
bool isFsKeyringSupported(void) {
- static bool initialized = false;
- static bool supported;
-
- if (!initialized) {
- android::base::unique_fd fd(open("/data", O_RDONLY | O_DIRECTORY | O_CLOEXEC));
-
- // FS_IOC_ADD_ENCRYPTION_KEY with a NULL argument will fail with ENOTTY
- // if the ioctl isn't supported. Otherwise it will fail with another
- // error code such as EFAULT.
- errno = 0;
- (void)ioctl(fd, FS_IOC_ADD_ENCRYPTION_KEY, NULL);
- if (errno == ENOTTY) {
- LOG(INFO) << "Kernel doesn't support FS_IOC_ADD_ENCRYPTION_KEY. Falling back to "
- "session keyring";
- supported = false;
- } else {
- if (errno != EFAULT) {
- PLOG(WARNING) << "Unexpected error from FS_IOC_ADD_ENCRYPTION_KEY";
- }
- LOG(DEBUG) << "Detected support for FS_IOC_ADD_ENCRYPTION_KEY";
- supported = true;
- android::base::SetProperty("ro.crypto.uses_fs_ioc_add_encryption_key", "true");
- }
- // There's no need to check for FS_IOC_REMOVE_ENCRYPTION_KEY, since it's
- // guaranteed to be available if FS_IOC_ADD_ENCRYPTION_KEY is. There's
- // also no need to check for support on external volumes separately from
- // /data, since either the kernel supports the ioctls on all
- // fscrypt-capable filesystems or it doesn't.
-
- initialized = true;
- }
+ static bool supported = isFsKeyringSupportedImpl();
return supported;
}
@@ -245,7 +241,7 @@
// https://www.kernel.org/doc/html/latest/filesystems/fscrypt.html#fs-ioc-add-encryption-key
static bool installFsKeyringKey(const std::string& mountpoint, const EncryptionOptions& options,
fscrypt_add_key_arg* arg) {
- if (options.use_hw_wrapped_key) arg->flags |= FSCRYPT_ADD_KEY_FLAG_WRAPPED;
+ if (options.use_hw_wrapped_key) arg->__flags |= __FSCRYPT_ADD_KEY_FLAG_HW_WRAPPED;
android::base::unique_fd fd(open(mountpoint.c_str(), O_RDONLY | O_DIRECTORY | O_CLOEXEC));
if (fd == -1) {
@@ -396,10 +392,10 @@
bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& key_authentication, const KeyGeneration& gen,
- KeyBuffer* key, bool keepOld) {
+ KeyBuffer* key) {
if (pathExists(key_path)) {
LOG(DEBUG) << "Key exists, using: " << key_path;
- if (!retrieveKey(key_path, key_authentication, key, keepOld)) return false;
+ if (!retrieveKey(key_path, key_authentication, key)) return false;
} else {
if (!gen.allow_gen) {
LOG(ERROR) << "No key found in " << key_path;
diff --git a/KeyUtil.h b/KeyUtil.h
index 0f5bc93..73255a3 100644
--- a/KeyUtil.h
+++ b/KeyUtil.h
@@ -75,10 +75,10 @@
bool evictKey(const std::string& mountpoint, const EncryptionPolicy& policy);
// Retrieves the key from the named directory, or generates it if it doesn't
-// exist. In most cases |keepOld| must be false; see retrieveKey() for details.
+// exist.
bool retrieveOrGenerateKey(const std::string& key_path, const std::string& tmp_path,
const KeyAuthentication& key_authentication, const KeyGeneration& gen,
- KeyBuffer* key, bool keepOld);
+ KeyBuffer* key);
// Re-installs a file-based encryption key of fscrypt-provisioning type from the
// global session keyring back into fs keyring of the mountpoint.
diff --git a/Loop.cpp b/Loop.cpp
index 9fa876c..87f105d 100644
--- a/Loop.cpp
+++ b/Loop.cpp
@@ -150,7 +150,9 @@
struct loop_info64 li;
if (ioctl(fd.get(), LOOP_GET_STATUS64, &li) < 0) {
- PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
+ if (errno != ENXIO) {
+ PLOG(WARNING) << "Failed to LOOP_GET_STATUS64 " << path;
+ }
continue;
}
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index ca2813d..cf7c5f7 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -17,17 +17,13 @@
#include "MetadataCrypt.h"
#include "KeyBuffer.h"
-#include <algorithm>
#include <string>
-#include <thread>
-#include <vector>
#include <fcntl.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
@@ -35,6 +31,7 @@
#include <cutils/fs.h>
#include <fs_mgr.h>
#include <libdm/dm.h>
+#include <libgsi/libgsi.h>
#include "Checkpoint.h"
#include "CryptoType.h"
@@ -45,8 +42,6 @@
#include "Utils.h"
#include "VoldUtil.h"
-#define TABLE_LOAD_RETRIES 10
-
namespace android {
namespace vold {
@@ -65,9 +60,6 @@
static const std::string kDmNameUserdata = "userdata";
-static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
-static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
-
// The first entry in this table is the default crypto type.
constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
@@ -87,10 +79,6 @@
}
static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) {
- // We're about to mount data not verified by verified boot. Tell Keymaster instances that early
- // boot has ended.
- ::android::vold::Keymaster::earlyBootEnded();
-
// fs_mgr_do_mount runs fsck. Use setexeccon to run trusted
// partitions in the fsck domain.
if (setexeccon(android::vold::sFsckContext)) {
@@ -112,31 +100,6 @@
return true;
}
-// Note: It is possible to orphan a key if it is removed before deleting
-// Update this once keymaster APIs change, and we have a proper commit.
-static void commit_key(const std::string& dir) {
- while (!android::base::WaitForProperty("vold.checkpoint_committed", "1")) {
- LOG(ERROR) << "Wait for boot timed out";
- }
- Keymaster keymaster;
- auto keyPath = dir + "/" + kFn_keymaster_key_blob;
- auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
- std::string key;
-
- if (!android::base::ReadFileToString(keyPath, &key)) {
- LOG(ERROR) << "Failed to read old key: " << dir;
- return;
- }
- if (rename(newKeyPath.c_str(), keyPath.c_str()) != 0) {
- PLOG(ERROR) << "Unable to move upgraded key to location: " << keyPath;
- return;
- }
- if (!keymaster.deleteKey(key)) {
- LOG(ERROR) << "Key deletion failed during upgrade, continuing anyway: " << dir;
- }
- LOG(INFO) << "Old Key deleted: " << dir;
-}
-
static bool read_key(const std::string& metadata_key_dir, const KeyGeneration& gen,
KeyBuffer* key) {
if (metadata_key_dir.empty()) {
@@ -151,25 +114,7 @@
return false;
}
auto temp = metadata_key_dir + "/tmp";
- auto newKeyPath = dir + "/" + kFn_keymaster_key_blob_upgraded;
- /* If we have a leftover upgraded key, delete it.
- * We either failed an update and must return to the old key,
- * or we rebooted before commiting the keys in a freak accident.
- * Either way, we can re-upgrade the key if we need to.
- */
- Keymaster keymaster;
- if (pathExists(newKeyPath)) {
- if (!android::base::ReadFileToString(newKeyPath, &sKey))
- LOG(ERROR) << "Failed to read incomplete key: " << dir;
- else if (!keymaster.deleteKey(sKey))
- LOG(ERROR) << "Incomplete key deletion failed, continuing anyway: " << dir;
- else
- unlink(newKeyPath.c_str());
- }
- bool needs_cp = cp_needsCheckpoint();
- if (!retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key, needs_cp)) return false;
- if (needs_cp && pathExists(newKeyPath)) std::thread(commit_key, dir).detach();
- return true;
+ return retrieveOrGenerateKey(dir, temp, kEmptyAuthentication, gen, key);
}
static bool get_number_of_sectors(const std::string& real_blkdev, uint64_t* nr_sec) {
@@ -215,20 +160,8 @@
table.AddTarget(std::move(target));
auto& dm = DeviceMapper::Instance();
- for (int i = 0;; i++) {
- if (dm.CreateDevice(dm_name, table)) {
- break;
- }
- if (i + 1 >= TABLE_LOAD_RETRIES) {
- PLOG(ERROR) << "Could not create default-key device " << dm_name;
- return false;
- }
- PLOG(INFO) << "Could not create default-key device, retrying";
- usleep(500000);
- }
-
- if (!dm.GetDmDevicePathByName(dm_name, crypto_blkdev)) {
- LOG(ERROR) << "Cannot retrieve default-key device status " << dm_name;
+ if (!dm.CreateDevice(dm_name, table, crypto_blkdev, std::chrono::seconds(5))) {
+ PLOG(ERROR) << "Could not create default-key device " << dm_name;
return false;
}
return true;
@@ -283,10 +216,9 @@
return false;
}
- constexpr unsigned int pre_gki_level = 29;
unsigned int options_format_version = android::base::GetUintProperty<unsigned int>(
"ro.crypto.dm_default_key.options_format.version",
- (GetFirstApiLevel() <= pre_gki_level ? 1 : 2));
+ (GetFirstApiLevel() <= __ANDROID_API_Q__ ? 1 : 2));
CryptoOptions options;
if (options_format_version == 1) {
@@ -319,21 +251,7 @@
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());
@@ -367,5 +285,44 @@
return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
}
+bool destroy_dsu_metadata_key(const std::string& dsu_slot) {
+ LOG(DEBUG) << "destroy_dsu_metadata_key: " << dsu_slot;
+
+ const auto dsu_metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
+ if (!pathExists(dsu_metadata_key_dir)) {
+ LOG(DEBUG) << "DSU metadata_key_dir doesn't exist, nothing to remove: "
+ << dsu_metadata_key_dir;
+ return true;
+ }
+
+ // Ensure that the DSU key directory is different from the host OS'.
+ // Under normal circumstances, this should never happen, but handle it just in case.
+ if (auto data_rec = GetEntryForMountPoint(&fstab_default, "/data")) {
+ if (dsu_metadata_key_dir == data_rec->metadata_key_dir) {
+ LOG(ERROR) << "DSU metadata_key_dir is same as host OS: " << dsu_metadata_key_dir;
+ return false;
+ }
+ }
+
+ bool ok = true;
+ for (auto suffix : {"/key", "/tmp"}) {
+ const auto key_path = dsu_metadata_key_dir + suffix;
+ if (pathExists(key_path)) {
+ LOG(DEBUG) << "Destroy key: " << key_path;
+ if (!android::vold::destroyKey(key_path)) {
+ LOG(ERROR) << "Failed to destroyKey(): " << key_path;
+ ok = false;
+ }
+ }
+ }
+ if (!ok) {
+ return false;
+ }
+
+ LOG(DEBUG) << "Remove DSU metadata_key_dir: " << dsu_metadata_key_dir;
+ // DeleteDirContentsAndDir() already logged any error, so don't log repeatedly.
+ return android::vold::DeleteDirContentsAndDir(dsu_metadata_key_dir) == android::OK;
+}
+
} // namespace vold
} // namespace android
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index dc68e7c..7341a08 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -34,6 +34,8 @@
const android::vold::KeyBuffer& key,
std::string* out_crypto_blkdev);
+bool destroy_dsu_metadata_key(const std::string& dsu_slot);
+
} // namespace vold
} // namespace android
#endif
diff --git a/MoveStorage.cpp b/MoveStorage.cpp
index 2447cce..3f636a2 100644
--- a/MoveStorage.cpp
+++ b/MoveStorage.cpp
@@ -70,9 +70,7 @@
bool found = false;
struct dirent* ent;
while ((ent = readdir(dirp.get())) != NULL) {
- if ((!strcmp(ent->d_name, ".")) || (!strcmp(ent->d_name, ".."))) {
- continue;
- }
+ if (IsDotOrDotDot(*ent)) continue;
auto subdir = path + "/" + ent->d_name;
found |= pushBackContents(subdir, cmd, searchLevels - 1);
}
diff --git a/NetlinkHandler.cpp b/NetlinkHandler.cpp
index d180a95..2231cd1 100644
--- a/NetlinkHandler.cpp
+++ b/NetlinkHandler.cpp
@@ -33,10 +33,6 @@
return this->startListener();
}
-int NetlinkHandler::stop() {
- return this->stopListener();
-}
-
void NetlinkHandler::onEvent(NetlinkEvent* evt) {
VolumeManager* vm = VolumeManager::Instance();
const char* subsys = evt->getSubsystem();
diff --git a/NetlinkHandler.h b/NetlinkHandler.h
index 8af7575..d779eae 100644
--- a/NetlinkHandler.h
+++ b/NetlinkHandler.h
@@ -25,7 +25,6 @@
virtual ~NetlinkHandler();
int start(void);
- int stop(void);
protected:
virtual void onEvent(NetlinkEvent* evt);
diff --git a/NetlinkManager.cpp b/NetlinkManager.cpp
index aacf4b9..56d9df6 100644
--- a/NetlinkManager.cpp
+++ b/NetlinkManager.cpp
@@ -90,19 +90,3 @@
close(mSock);
return -1;
}
-
-int NetlinkManager::stop() {
- int status = 0;
-
- if (mHandler->stop()) {
- PLOG(ERROR) << "Unable to stop NetlinkHandler";
- status = -1;
- }
- delete mHandler;
- mHandler = NULL;
-
- close(mSock);
- mSock = -1;
-
- return status;
-}
diff --git a/NetlinkManager.h b/NetlinkManager.h
index e31fc2e..c4f3ab5 100644
--- a/NetlinkManager.h
+++ b/NetlinkManager.h
@@ -35,7 +35,6 @@
virtual ~NetlinkManager();
int start();
- int stop();
void setBroadcaster(SocketListener* sl) { mBroadcaster = sl; }
SocketListener* getBroadcaster() { return mBroadcaster; }
diff --git a/OWNERS b/OWNERS
index bab0ef6..deeceb7 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,3 +4,4 @@
ebiggers@google.com
drosen@google.com
zezeozue@google.com
+maco@google.com
diff --git a/Utils.cpp b/Utils.cpp
index 17921e8..d5648f7 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -199,7 +199,7 @@
}
int SetQuotaInherit(const std::string& path) {
- unsigned long flags;
+ unsigned int flags;
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
if (fd == -1) {
@@ -417,7 +417,7 @@
}
int SetAttrs(const std::string& path, unsigned int attrs) {
- unsigned long flags;
+ unsigned int flags;
android::base::unique_fd fd(
TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
@@ -426,14 +426,14 @@
return -1;
}
- if (ioctl(fd, FS_IOC_GETFLAGS, (void*)&flags)) {
+ if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) {
PLOG(ERROR) << "Failed to get flags for " << path;
return -1;
}
if ((flags & attrs) == attrs) return 0;
flags |= attrs;
- if (ioctl(fd, FS_IOC_SETFLAGS, (void*)&flags)) {
+ if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) {
PLOG(ERROR) << "Failed to set flags for " << path << "(0x" << std::hex << attrs << ")";
return -1;
}
@@ -956,10 +956,7 @@
int subfd;
/* always skip "." and ".." */
- if (name[0] == '.') {
- if (name[1] == 0) continue;
- if ((name[1] == '.') && (name[2] == 0)) continue;
- }
+ if (IsDotOrDotDot(*de)) continue;
subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
if (subfd >= 0) {
@@ -1132,6 +1129,13 @@
}
}
+// Returns true if |path1| names the same existing file or directory as |path2|.
+bool IsSameFile(const std::string& path1, const std::string& path2) {
+ struct stat stbuf1, stbuf2;
+ if (stat(path1.c_str(), &stbuf1) != 0 || stat(path2.c_str(), &stbuf2) != 0) return false;
+ return stbuf1.st_ino == stbuf2.st_ino && stbuf1.st_dev == stbuf2.st_dev;
+}
+
status_t RestoreconRecursive(const std::string& path) {
LOG(DEBUG) << "Starting restorecon of " << path;
@@ -1256,6 +1260,10 @@
return OK;
}
+bool IsDotOrDotDot(const struct dirent& ent) {
+ return strcmp(ent.d_name, ".") == 0 || strcmp(ent.d_name, "..") == 0;
+}
+
static status_t delete_dir_contents(DIR* dir) {
// Shamelessly borrowed from android::installd
int dfd = dirfd(dir);
@@ -1269,10 +1277,7 @@
const char* name = de->d_name;
if (de->d_type == DT_DIR) {
/* always skip "." and ".." */
- if (name[0] == '.') {
- if (name[1] == 0) continue;
- if ((name[1] == '.') && (name[2] == 0)) continue;
- }
+ if (IsDotOrDotDot(*de)) continue;
android::base::unique_fd subfd(
openat(dfd, name, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC));
@@ -1583,18 +1588,8 @@
std::string pass_through_path(
StringPrintf("/mnt/pass_through/%d/%s", user_id, relative_upper_path.c_str()));
- // Best effort unmount pass_through path
- sSleepOnUnmount = false;
- LOG(INFO) << "Unmounting pass_through_path " << pass_through_path;
- auto status = ForceUnmount(pass_through_path);
- if (status != android::OK) {
- LOG(ERROR) << "Failed to unmount " << pass_through_path;
- }
- rmdir(pass_through_path.c_str());
-
LOG(INFO) << "Unmounting fuse path " << fuse_path;
android::status_t result = ForceUnmount(fuse_path);
- sSleepOnUnmount = true;
if (result != android::OK) {
// TODO(b/135341433): MNT_DETACH is needed for fuse because umount2 can fail with EBUSY.
// Figure out why we get EBUSY and remove this special casing if possible.
@@ -1608,6 +1603,13 @@
}
rmdir(fuse_path.c_str());
+ LOG(INFO) << "Unmounting pass_through_path " << pass_through_path;
+ auto status = ForceUnmount(pass_through_path);
+ if (status != android::OK) {
+ LOG(ERROR) << "Failed to unmount " << pass_through_path;
+ }
+ rmdir(pass_through_path.c_str());
+
return result;
}
diff --git a/Utils.h b/Utils.h
index 5351450..cf3fd9b 100644
--- a/Utils.h
+++ b/Utils.h
@@ -155,6 +155,8 @@
dev_t GetDevice(const std::string& path);
+bool IsSameFile(const std::string& path1, const std::string& path2);
+
status_t EnsureDirExists(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
status_t RestoreconRecursive(const std::string& path);
@@ -168,6 +170,8 @@
status_t UnmountTreeWithPrefix(const std::string& prefix);
status_t UnmountTree(const std::string& mountPoint);
+bool IsDotOrDotDot(const struct dirent& ent);
+
status_t DeleteDirContentsAndDir(const std::string& pathname);
status_t DeleteDirContents(const std::string& pathname);
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 0cb86ce..674a721 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -30,10 +30,10 @@
#include <thread>
#include "Benchmark.h"
-#include "CheckEncryption.h"
#include "Checkpoint.h"
#include "FsCrypt.h"
#include "IdleMaint.h"
+#include "Keymaster.h"
#include "MetadataCrypt.h"
#include "MoveStorage.h"
#include "Process.h"
@@ -351,17 +351,6 @@
return Ok();
}
-binder::Status VoldNativeService::checkEncryption(const std::string& volId) {
- ENFORCE_SYSTEM_OR_ROOT;
- CHECK_ARGUMENT_ID(volId);
- ACQUIRE_LOCK;
-
- std::string path;
- auto status = pathForVolId(volId, &path);
- if (!status.isOk()) return status;
- return translate(android::vold::CheckEncryption(path));
-}
-
binder::Status VoldNativeService::moveStorage(
const std::string& fromVolId, const std::string& toVolId,
const android::sp<android::os::IVoldTaskListener>& listener) {
@@ -751,7 +740,7 @@
return translateBool(fscrypt_lock_user_key(userId));
}
-binder::Status VoldNativeService::prepareUserStorage(const std::unique_ptr<std::string>& uuid,
+binder::Status VoldNativeService::prepareUserStorage(const std::optional<std::string>& uuid,
int32_t userId, int32_t userSerial,
int32_t flags) {
ENFORCE_SYSTEM_OR_ROOT;
@@ -763,7 +752,7 @@
return translateBool(fscrypt_prepare_user_storage(uuid_, userId, userSerial, flags));
}
-binder::Status VoldNativeService::destroyUserStorage(const std::unique_ptr<std::string>& uuid,
+binder::Status VoldNativeService::destroyUserStorage(const std::optional<std::string>& uuid,
int32_t userId, int32_t flags) {
ENFORCE_SYSTEM_OR_ROOT;
std::string empty_string = "";
@@ -891,6 +880,14 @@
return Ok();
}
+binder::Status VoldNativeService::earlyBootEnded() {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ Keymaster::earlyBootEnded();
+ return Ok();
+}
+
binder::Status VoldNativeService::incFsEnabled(bool* _aidl_return) {
ENFORCE_SYSTEM_OR_ROOT;
@@ -962,5 +959,12 @@
return translate(incfs::bindMount(sourceDir, targetDir));
}
+binder::Status VoldNativeService::destroyDsuMetadataKey(const std::string& dsuSlot) {
+ ENFORCE_SYSTEM_OR_ROOT;
+ ACQUIRE_LOCK;
+
+ return translateBool(destroy_dsu_metadata_key(dsuSlot));
+}
+
} // namespace vold
} // namespace android
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 013d1c2..390e9fc 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -59,7 +59,6 @@
binder::Status format(const std::string& volId, const std::string& fsType);
binder::Status benchmark(const std::string& volId,
const android::sp<android::os::IVoldTaskListener>& listener);
- binder::Status checkEncryption(const std::string& volId);
binder::Status moveStorage(const std::string& fromVolId, const std::string& toVolId,
const android::sp<android::os::IVoldTaskListener>& listener);
@@ -126,9 +125,9 @@
const std::string& secret);
binder::Status lockUserKey(int32_t userId);
- binder::Status prepareUserStorage(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ binder::Status prepareUserStorage(const std::optional<std::string>& uuid, int32_t userId,
int32_t userSerial, int32_t flags);
- binder::Status destroyUserStorage(const std::unique_ptr<std::string>& uuid, int32_t userId,
+ binder::Status destroyUserStorage(const std::optional<std::string>& uuid, int32_t userId,
int32_t flags);
binder::Status prepareSandboxForApp(const std::string& packageName, int32_t appId,
@@ -151,6 +150,8 @@
binder::Status supportsFileCheckpoint(bool* _aidl_return);
binder::Status resetCheckpoint();
+ binder::Status earlyBootEnded();
+
binder::Status incFsEnabled(bool* _aidl_return) override;
binder::Status mountIncFs(
const std::string& backingPath, const std::string& targetDir, int32_t flags,
@@ -160,6 +161,8 @@
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
bool enableReadLogs) override;
binder::Status bindMount(const std::string& sourceDir, const std::string& targetDir) override;
+
+ binder::Status destroyDsuMetadataKey(const std::string& dsuSlot) override;
};
} // namespace vold
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 585d2d5..f0fc388 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -198,16 +198,6 @@
return 0;
}
-int VolumeManager::stop() {
- CHECK(!mInternalEmulatedVolumes.empty());
- for (const auto& vol : mInternalEmulatedVolumes) {
- vol->destroy();
- }
- mInternalEmulatedVolumes.clear();
-
- return 0;
-}
-
void VolumeManager::handleBlockEvent(NetlinkEvent* evt) {
std::lock_guard<std::mutex> lock(mLock);
diff --git a/VolumeManager.h b/VolumeManager.h
index 3277f75..54a2443 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -56,7 +56,6 @@
android::sp<android::os::IVoldListener> getListener() const { return mListener; }
int start();
- int stop();
void handleBlockEvent(NetlinkEvent* evt);
diff --git a/CheckEncryption.h b/bench/inodeop_bench/Android.bp
similarity index 60%
rename from CheckEncryption.h
rename to bench/inodeop_bench/Android.bp
index 158d886..a01ddd1 100644
--- a/CheckEncryption.h
+++ b/bench/inodeop_bench/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,19 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
-#ifndef ANDROID_VOLD_CHECK_ENCRYPTION_H
-#define ANDROID_VOLD_CHECK_ENCRYPTION_H
-
-#include <string>
-
-namespace android {
-namespace vold {
-
-/* Check encryption of private volume mounted at the given path */
-int CheckEncryption(const std::string& path);
-
-} // namespace vold
-} // namespace android
-
-#endif
+cc_binary {
+ name: "inodeop_bench",
+ srcs: ["inodeop_bench.cpp"],
+}
diff --git a/bench/inodeop_bench/OWNERS b/bench/inodeop_bench/OWNERS
new file mode 100644
index 0000000..3ced4a1
--- /dev/null
+++ b/bench/inodeop_bench/OWNERS
@@ -0,0 +1,3 @@
+balsini@google.com
+stefanoduo@google.com
+zezeozue@google.com
diff --git a/bench/inodeop_bench/inodeop_bench.cpp b/bench/inodeop_bench/inodeop_bench.cpp
new file mode 100644
index 0000000..cf38e4c
--- /dev/null
+++ b/bench/inodeop_bench/inodeop_bench.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <chrono>
+#include <functional>
+#include <iostream>
+#include <ratio>
+#include <sstream>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static constexpr char VERSION[] = "0";
+
+// Self-contained class for collecting and reporting benchmark metrics
+// (currently only execution time).
+class Collector {
+ using time_point = std::chrono::time_point<std::chrono::steady_clock>;
+ using time_unit = std::chrono::duration<double, std::milli>;
+
+ struct Metric {
+ std::string workload;
+ time_unit exec_time;
+ Metric(const std::string& workload, const time_unit& exec_time)
+ : workload(workload), exec_time(exec_time) {}
+ };
+
+ static constexpr char TIME_UNIT[] = "ms";
+ static constexpr char VERSION[] = "0";
+ std::vector<Metric> metrics;
+ time_point reset_time;
+
+ public:
+ Collector() { reset(); }
+
+ void reset() { reset_time = std::chrono::steady_clock::now(); }
+
+ void collect_metric(const std::string& workload) {
+ auto elapsed = std::chrono::steady_clock::now() - reset_time;
+ metrics.emplace_back(workload, std::chrono::duration_cast<time_unit>(elapsed));
+ }
+
+ void report_metrics() {
+ for (const Metric& metric : metrics)
+ std::cout << VERSION << ";" << metric.workload << ";" << metric.exec_time.count() << ";"
+ << TIME_UNIT << std::endl;
+ }
+};
+
+struct Command {
+ static constexpr char CREATE[] = "create";
+ static constexpr char DELETE[] = "delete";
+ static constexpr char MOVE[] = "move";
+ static constexpr char HARDLINK[] = "hardlink";
+ static constexpr char SYMLINK[] = "symlink";
+ static constexpr char READDIR[] = "readdir";
+ std::string workload;
+ std::string from_dir;
+ std::string from_basename;
+ std::string to_dir;
+ std::string to_basename;
+ bool drop_state;
+ int n_file;
+
+ Command() { reset(); }
+
+ std::string to_string() const {
+ std::stringstream string_repr;
+ string_repr << "Command {\n";
+ string_repr << "\t.workload = " << workload << ",\n";
+ string_repr << "\t.from_dir = " << from_dir << ",\n";
+ string_repr << "\t.from_basename = " << from_basename << ",\n";
+ string_repr << "\t.to_dir = " << to_dir << ",\n";
+ string_repr << "\t.to_basename = " << to_basename << ",\n";
+ string_repr << "\t.drop_state = " << drop_state << ",\n";
+ string_repr << "\t.n_file = " << n_file << "\n";
+ string_repr << "}\n";
+ return string_repr.str();
+ }
+
+ void reset() {
+ workload = "";
+ from_dir = to_dir = "./";
+ from_basename = "from_file";
+ to_basename = "to_file";
+ drop_state = true;
+ n_file = 0;
+ }
+};
+
+void print_version() {
+ std::cout << VERSION << std::endl;
+}
+
+void print_commands(const std::vector<Command>& commands) {
+ for (const Command& command : commands) std::cout << command.to_string();
+}
+
+void usage(std::ostream& ostr, const std::string& program_name) {
+ Command command;
+
+ ostr << "Usage: " << program_name << " [global_options] {[workload_options] -w WORKLOAD_T}\n";
+ ostr << "WORKLOAD_T = {" << Command::CREATE << ", " << Command::DELETE << ", " << Command::MOVE
+ << ", " << Command::HARDLINK << ", " << Command::SYMLINK << "}\n";
+ ostr << "Global options\n";
+ ostr << "\t-v: Print version.\n";
+ ostr << "\t-p: Print parsed workloads and exit.\n";
+ ostr << "Workload options\n";
+ ostr << "\t-d DIR\t\t: Work directory for " << Command::CREATE << "/" << Command::DELETE
+ << " (default '" << command.from_dir << "').\n";
+ ostr << "\t-f FROM-DIR\t: Source directory for " << Command::MOVE << "/" << Command::SYMLINK
+ << "/" << Command::HARDLINK << " (default '" << command.from_dir << "').\n";
+ ostr << "\t-t TO-DIR\t: Destination directory for " << Command::MOVE << "/" << Command::SYMLINK
+ << "/" << Command::HARDLINK << " (default '" << command.to_dir << "').\n";
+ ostr << "\t-n N_FILES\t: Number of files to create/delete etc. (default " << command.n_file
+ << ").\n";
+ ostr << "\t-s\t\t: Do not drop state (caches) before running the workload (default "
+ << !command.drop_state << ").\n";
+ ostr << "NOTE: -w WORKLOAD_T defines a new command and must come after its workload_options."
+ << std::endl;
+}
+
+void drop_state() {
+ // Drop inode/dentry/page caches.
+ std::system("sync; echo 3 > /proc/sys/vm/drop_caches");
+}
+
+static constexpr int OPEN_DIR_FLAGS = O_RDONLY | O_DIRECTORY | O_PATH | O_CLOEXEC;
+
+bool delete_files(const std::string& dir, int n_file, const std::string& basename) {
+ int dir_fd = open(dir.c_str(), OPEN_DIR_FLAGS);
+ if (dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open work directory '" << dir << "', error '" << strerror(error)
+ << "'." << std::endl;
+ return false;
+ }
+
+ bool ret = true;
+ for (int i = 0; i < n_file; i++) {
+ std::string filename = basename + std::to_string(i);
+ ret = ret && (unlinkat(dir_fd, filename.c_str(), 0) == 0);
+ }
+
+ if (!ret) std::cerr << "Failed to delete at least one of the files" << std::endl;
+ close(dir_fd);
+ return ret;
+}
+
+bool create_files(const std::string& dir, int n_file, const std::string& basename) {
+ int dir_fd = open(dir.c_str(), OPEN_DIR_FLAGS);
+ if (dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open work directory '" << dir << "', error '" << strerror(error)
+ << "'." << std::endl;
+ return false;
+ }
+
+ bool ret = true;
+ for (int i = 0; i < n_file; i++) {
+ std::string filename = basename + std::to_string(i);
+ int fd = openat(dir_fd, filename.c_str(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0777);
+ ret = ret && fd != -1;
+ close(fd);
+ }
+
+ close(dir_fd);
+ if (!ret) {
+ std::cerr << "Failed to open at least one of the files" << std::endl;
+ delete_files(dir, n_file, basename);
+ }
+ return ret;
+}
+
+bool move_files(const std::string& from_dir, const std::string& to_dir, int n_file,
+ const std::string& from_basename, const std::string& to_basename) {
+ int from_dir_fd = open(from_dir.c_str(), OPEN_DIR_FLAGS);
+ if (from_dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open source directory '" << from_dir << "', error '"
+ << strerror(error) << "'." << std::endl;
+ return false;
+ }
+ int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
+ if (to_dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
+ << strerror(error) << "'." << std::endl;
+ close(from_dir_fd);
+ return false;
+ }
+
+ bool ret = true;
+ for (int i = 0; i < n_file; i++) {
+ std::string from_filename = from_basename + std::to_string(i);
+ std::string to_filename = to_basename + std::to_string(i);
+ ret = ret &&
+ (renameat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str()) == 0);
+ }
+
+ if (!ret) std::cerr << "Failed to move at least one of the files" << std::endl;
+ close(from_dir_fd);
+ close(from_dir_fd);
+ return ret;
+}
+
+bool hardlink_files(const std::string& from_dir, const std::string& to_dir, int n_file,
+ const std::string& from_basename, const std::string& to_basename) {
+ int from_dir_fd = open(from_dir.c_str(), OPEN_DIR_FLAGS);
+ if (from_dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open source directory '" << from_dir << "', error '"
+ << strerror(error) << "'." << std::endl;
+ return false;
+ }
+ int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
+ if (to_dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
+ << strerror(error) << "'." << std::endl;
+ close(from_dir_fd);
+ return false;
+ }
+
+ bool ret = true;
+ for (int i = 0; i < n_file; i++) {
+ std::string from_filename = from_basename + std::to_string(i);
+ std::string to_filename = to_basename + std::to_string(i);
+ ret = ret &&
+ (linkat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str(), 0) == 0);
+ }
+
+ if (!ret) std::cerr << "Failed to hardlink at least one of the files" << std::endl;
+ close(from_dir_fd);
+ close(to_dir_fd);
+ return ret;
+}
+
+bool symlink_files(std::string from_dir, const std::string& to_dir, int n_file,
+ const std::string& from_basename, const std::string& to_basename) {
+ if (from_dir.back() != '/') from_dir.push_back('/');
+ int to_dir_fd = open(to_dir.c_str(), OPEN_DIR_FLAGS);
+ if (to_dir_fd == -1) {
+ int error = errno;
+ std::cerr << "Failed to open destination directory '" << to_dir << "', error '"
+ << strerror(error) << "'." << std::endl;
+ return false;
+ }
+
+ bool ret = true;
+ for (int i = 0; i < n_file; i++) {
+ std::string from_filepath = from_dir + from_basename + std::to_string(i);
+ std::string to_filename = to_basename + std::to_string(i);
+ ret = ret && (symlinkat(from_filepath.c_str(), to_dir_fd, to_filename.c_str()) == 0);
+ }
+
+ if (!ret) std::cerr << "Failed to symlink at least one of the files" << std::endl;
+ close(to_dir_fd);
+ return ret;
+}
+
+bool exhaustive_readdir(const std::string& from_dir) {
+ DIR* dir = opendir(from_dir.c_str());
+ if (dir == nullptr) {
+ int error = errno;
+ std::cerr << "Failed to open working directory '" << from_dir << "', error '"
+ << strerror(error) << "'." << std::endl;
+ return false;
+ }
+
+ errno = 0;
+ while (readdir(dir) != nullptr)
+ ;
+ // In case of failure readdir returns nullptr and sets errno accordingly (to
+ // something != 0).
+ // In case of success readdir != nullptr and errno is not changed.
+ // Source: man 3 readdir.
+ bool ret = errno == 0;
+ closedir(dir);
+ return ret;
+}
+
+void create_workload(Collector* collector, const Command& command) {
+ if (command.drop_state) drop_state();
+ collector->reset();
+ if (create_files(command.from_dir, command.n_file, command.from_basename))
+ collector->collect_metric(command.workload);
+
+ delete_files(command.from_dir, command.n_file, command.from_basename);
+}
+
+void delete_workload(Collector* collector, const Command& command) {
+ if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+ if (command.drop_state) drop_state();
+ collector->reset();
+ if (delete_files(command.from_dir, command.n_file, command.from_basename))
+ collector->collect_metric(command.workload);
+}
+
+void move_workload(Collector* collector, const Command& command) {
+ if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+ if (command.drop_state) drop_state();
+ collector->reset();
+ if (move_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
+ command.to_basename))
+ collector->collect_metric(command.workload);
+
+ delete_files(command.to_dir, command.n_file, command.to_basename);
+}
+
+void hardlink_workload(Collector* collector, const Command& command) {
+ if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+ if (command.drop_state) drop_state();
+ collector->reset();
+ if (hardlink_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
+ command.to_basename))
+ collector->collect_metric(command.workload);
+
+ delete_files(command.from_dir, command.n_file, command.from_basename);
+ delete_files(command.to_dir, command.n_file, command.to_basename);
+}
+
+void symlink_workload(Collector* collector, const Command& command) {
+ if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+ if (command.drop_state) drop_state();
+ collector->reset();
+ if (symlink_files(command.from_dir, command.to_dir, command.n_file, command.from_basename,
+ command.to_basename))
+ collector->collect_metric(command.workload);
+
+ delete_files(command.to_dir, command.n_file, command.to_basename);
+ delete_files(command.from_dir, command.n_file, command.from_basename);
+}
+
+void readdir_workload(Collector* collector, const Command& command) {
+ if (!create_files(command.from_dir, command.n_file, command.from_basename)) return;
+
+ if (command.drop_state) drop_state();
+ collector->reset();
+ if (exhaustive_readdir(command.from_dir)) collector->collect_metric(command.workload);
+
+ delete_files(command.from_dir, command.n_file, command.from_basename);
+}
+
+using workload_executor_t = std::function<void(Collector*, const Command&)>;
+
+std::unordered_map<std::string, workload_executor_t> executors = {
+ {Command::CREATE, create_workload}, {Command::DELETE, delete_workload},
+ {Command::MOVE, move_workload}, {Command::HARDLINK, hardlink_workload},
+ {Command::SYMLINK, symlink_workload}, {Command::READDIR, readdir_workload}};
+
+int main(int argc, char** argv) {
+ std::vector<Command> commands;
+ Command command;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "hvpsw:d:f:t:n:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(std::cout, argv[0]);
+ return EXIT_SUCCESS;
+ case 'v':
+ print_version();
+ return EXIT_SUCCESS;
+ case 'p':
+ print_commands(commands);
+ return EXIT_SUCCESS;
+ case 's':
+ command.drop_state = false;
+ break;
+ case 'w':
+ command.workload = optarg;
+ commands.push_back(command);
+ command.reset();
+ break;
+ case 'd':
+ case 'f':
+ command.from_dir = optarg;
+ break;
+ case 't':
+ command.to_dir = optarg;
+ break;
+ case 'n':
+ command.n_file = std::stoi(optarg);
+ break;
+ default:
+ usage(std::cerr, argv[0]);
+ return EXIT_FAILURE;
+ }
+ }
+
+ Collector collector;
+ for (const Command& command : commands) {
+ auto executor = executors.find(command.workload);
+ if (executor == executors.end()) continue;
+ executor->second(&collector, command);
+ }
+ collector.report_metrics();
+
+ return EXIT_SUCCESS;
+}
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 54b86d0..bb284b8 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -48,7 +48,6 @@
void unmount(@utf8InCpp String volId);
void format(@utf8InCpp String volId, @utf8InCpp String fsType);
void benchmark(@utf8InCpp String volId, IVoldTaskListener listener);
- void checkEncryption(@utf8InCpp String volId);
void moveStorage(@utf8InCpp String fromVolId, @utf8InCpp String toVolId,
IVoldTaskListener listener);
@@ -127,6 +126,7 @@
boolean supportsFileCheckpoint();
void resetCheckpoint();
+ void earlyBootEnded();
@utf8InCpp String createStubVolume(@utf8InCpp String sourcePath,
@utf8InCpp String mountPath, @utf8InCpp String fsType,
@utf8InCpp String fsUuid, @utf8InCpp String fsLabel, int flags);
@@ -140,6 +140,8 @@
void setIncFsMountOptions(in IncrementalFileSystemControlParcel control, boolean enableReadLogs);
void bindMount(@utf8InCpp String sourceDir, @utf8InCpp String targetDir);
+ void destroyDsuMetadataKey(@utf8InCpp String dsuSlot);
+
const int ENCRYPTION_FLAG_NO_UI = 4;
const int ENCRYPTION_STATE_NONE = 1;
@@ -165,6 +167,7 @@
const int STORAGE_FLAG_DE = 1;
const int STORAGE_FLAG_CE = 2;
+ const int STORAGE_FLAG_LEVEL_FROM_USER = 4;
const int REMOUNT_MODE_NONE = 0;
const int REMOUNT_MODE_DEFAULT = 1;
diff --git a/cryptfs.cpp b/cryptfs.cpp
index 8b7ac0a..faed65b 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -90,6 +90,8 @@
#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 @@
/* 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 @@
__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 @@
#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 @@
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 @@
}
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 @@
/* 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 @@
}
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];
diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp
index 34f1024..7782dd3 100644
--- a/fs/Exfat.cpp
+++ b/fs/Exfat.cpp
@@ -41,7 +41,7 @@
status_t Check(const std::string& source) {
std::vector<std::string> cmd;
cmd.push_back(kFsckPath);
- cmd.push_back("-a");
+ cmd.push_back("-y");
cmd.push_back(source);
int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
diff --git a/fs/F2fs.cpp b/fs/F2fs.cpp
index 9b8d2c4..d6f3dab 100644
--- a/fs/F2fs.cpp
+++ b/fs/F2fs.cpp
@@ -85,7 +85,12 @@
cmd.push_back("-O");
cmd.push_back("encrypt");
}
-
+ if (android::base::GetBoolProperty("vold.has_compress", false)) {
+ cmd.push_back("-O");
+ cmd.push_back("compression");
+ cmd.push_back("-O");
+ cmd.push_back("extra_attr");
+ }
cmd.push_back("-O");
cmd.push_back("verity");
diff --git a/fscrypt_uapi.h b/fscrypt_uapi.h
deleted file mode 100644
index 3cda96e..0000000
--- a/fscrypt_uapi.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#ifndef _UAPI_LINUX_FSCRYPT_VOLD_H
-#define _UAPI_LINUX_FSCRYPT_VOLD_H
-
-#include <linux/fscrypt.h>
-#include <linux/types.h>
-
-#define FSCRYPT_ADD_KEY_FLAG_WRAPPED 0x01
-
-struct sys_fscrypt_add_key_arg {
- struct fscrypt_key_specifier key_spec;
- __u32 raw_size;
- __u32 key_id;
- __u32 __reserved[7];
- __u32 flags;
- __u8 raw[];
-};
-
-struct sys_fscrypt_provisioning_key_payload {
- __u32 type;
- __u32 __reserved;
- __u8 raw[];
-};
-
-#define fscrypt_add_key_arg sys_fscrypt_add_key_arg
-#define fscrypt_provisioning_key_payload sys_fscrypt_provisioning_key_payload
-
-#endif //_UAPI_LINUX_FSCRYPT_VOLD_H
diff --git a/main.cpp b/main.cpp
index ebe5510..1f85fb5 100644
--- a/main.cpp
+++ b/main.cpp
@@ -41,8 +41,14 @@
#include <sys/stat.h>
#include <sys/types.h>
-static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,
- bool* has_reserved);
+typedef struct vold_configs {
+ bool has_adoptable : 1;
+ bool has_quota : 1;
+ bool has_reserved : 1;
+ bool has_compress : 1;
+} VoldConfigs;
+
+static int process_config(VolumeManager* vm, VoldConfigs* configs);
static void coldboot(const char* path);
static void parse_args(int argc, char** argv);
@@ -100,11 +106,8 @@
exit(1);
}
- bool has_adoptable;
- bool has_quota;
- bool has_reserved;
-
- if (process_config(vm, &has_adoptable, &has_quota, &has_reserved)) {
+ VoldConfigs configs = {};
+ if (process_config(vm, &configs)) {
PLOG(ERROR) << "Error reading configuration... continuing anyways";
}
@@ -128,9 +131,10 @@
// This call should go after listeners are started to avoid
// a deadlock between vold and init (see b/34278978 for details)
- android::base::SetProperty("vold.has_adoptable", has_adoptable ? "1" : "0");
- android::base::SetProperty("vold.has_quota", has_quota ? "1" : "0");
- android::base::SetProperty("vold.has_reserved", has_reserved ? "1" : "0");
+ android::base::SetProperty("vold.has_adoptable", configs.has_adoptable ? "1" : "0");
+ android::base::SetProperty("vold.has_quota", configs.has_quota ? "1" : "0");
+ android::base::SetProperty("vold.has_reserved", configs.has_reserved ? "1" : "0");
+ android::base::SetProperty("vold.has_compress", configs.has_compress ? "1" : "0");
// Do coldboot here so it won't block booting,
// also the cold boot is needed in case we have flash drive
@@ -213,8 +217,7 @@
}
}
-static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quota,
- bool* has_reserved) {
+static int process_config(VolumeManager* vm, VoldConfigs* configs) {
ATRACE_NAME("process_config");
if (!ReadDefaultFstab(&fstab_default)) {
@@ -223,19 +226,24 @@
}
/* Loop through entries looking for ones that vold manages */
- *has_adoptable = false;
- *has_quota = false;
- *has_reserved = false;
+ configs->has_adoptable = false;
+ configs->has_quota = false;
+ configs->has_reserved = false;
+ configs->has_compress = false;
for (auto& entry : fstab_default) {
if (entry.fs_mgr_flags.quota) {
- *has_quota = true;
+ configs->has_quota = true;
}
if (entry.reserved_size > 0) {
- *has_reserved = true;
+ configs->has_reserved = true;
+ }
+ if (entry.fs_mgr_flags.fs_compress) {
+ configs->has_compress = true;
}
/* Make sure logical partitions have an updated blk_device. */
- if (entry.fs_mgr_flags.logical && !fs_mgr_update_logical_partition(&entry)) {
+ if (entry.fs_mgr_flags.logical && !fs_mgr_update_logical_partition(&entry) &&
+ !entry.fs_mgr_flags.no_fail) {
PLOG(FATAL) << "could not find logical partition " << entry.blk_device;
}
@@ -251,7 +259,7 @@
if (entry.is_encryptable()) {
flags |= android::vold::Disk::Flags::kAdoptable;
- *has_adoptable = true;
+ configs->has_adoptable = true;
}
if (entry.fs_mgr_flags.no_emulated_sd ||
android::base::GetBoolProperty("vold.debug.default_primary", false)) {
diff --git a/model/VolumeEncryption.cpp b/model/VolumeEncryption.cpp
index 5b0e73d..e6a55a9 100644
--- a/model/VolumeEncryption.cpp
+++ b/model/VolumeEncryption.cpp
@@ -32,16 +32,16 @@
enum class VolumeMethod { kFailed, kCrypt, kDefaultKey };
static VolumeMethod lookup_volume_method() {
- constexpr uint64_t pre_gki_level = 29;
auto first_api_level =
android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
auto method = android::base::GetProperty("ro.crypto.volume.metadata.method", "default");
if (method == "default") {
- return first_api_level > pre_gki_level ? VolumeMethod::kDefaultKey : VolumeMethod::kCrypt;
+ return first_api_level > __ANDROID_API_Q__ ? VolumeMethod::kDefaultKey
+ : VolumeMethod::kCrypt;
} else if (method == "dm-default-key") {
return VolumeMethod::kDefaultKey;
} else if (method == "dm-crypt") {
- if (first_api_level > pre_gki_level) {
+ if (first_api_level > __ANDROID_API_Q__) {
LOG(ERROR) << "volume encryption method dm-crypt cannot be used, "
"ro.product.first_api_level = "
<< first_api_level;
diff --git a/secdiscard.cpp b/secdiscard.cpp
index 4659eed..b91f321 100644
--- a/secdiscard.cpp
+++ b/secdiscard.cpp
@@ -50,6 +50,27 @@
} // namespace
+#ifndef F2FS_IOCTL_MAGIC
+#define F2FS_IOCTL_MAGIC 0xf5
+#endif
+
+// F2FS-specific ioctl
+// It requires the below kernel commit merged in v5.9-rc1.
+// 9af846486d78 ("f2fs: add F2FS_IOC_SEC_TRIM_FILE ioctl")
+// In android12-5.4,
+// 7fc27297c44d ("Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-5.4.y'
+// into android12-5.4")
+#ifndef F2FS_IOC_SEC_TRIM_FILE
+struct f2fs_sectrim_range {
+ __u64 start;
+ __u64 len;
+ __u64 flags;
+};
+#define F2FS_IOC_SEC_TRIM_FILE _IOW(F2FS_IOCTL_MAGIC, 20, struct f2fs_sectrim_range)
+#define F2FS_TRIM_FILE_DISCARD 0x1
+#define F2FS_TRIM_FILE_ZEROOUT 0x2
+#endif
+
int main(int argc, const char* const argv[]) {
android::base::InitLogging(const_cast<char**>(argv));
Options options;
@@ -69,9 +90,6 @@
// In android-4.14,
// ce767d9a55bc ("f2fs: updates on v4.16-rc1")
#ifndef F2FS_IOC_SET_PIN_FILE
-#ifndef F2FS_IOCTL_MAGIC
-#define F2FS_IOCTL_MAGIC 0xf5
-#endif
#define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
#define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
#endif
@@ -85,8 +103,31 @@
ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);
LOG(DEBUG) << "Securely discarding '" << target << "' unlink=" << options.unlink;
- if (!secdiscard_path(target)) {
- LOG(ERROR) << "Secure discard failed for: " << target;
+ struct f2fs_sectrim_range secRange;
+ secRange.start = 0;
+ secRange.len = -1; // until end of file
+ secRange.flags = F2FS_TRIM_FILE_DISCARD | F2FS_TRIM_FILE_ZEROOUT;
+ /*
+ * F2FS_IOC_SEC_TRIM_FILE is only supported by F2FS.
+ * 1. If device supports secure discard, it sends secure discard command on the file.
+ * 2. Otherwise, it sends discard command on the file.
+ * 3. Lastly, it overwrites zero data on it.
+ */
+ int ret = ioctl(fd, F2FS_IOC_SEC_TRIM_FILE, &secRange);
+ if (ret != 0) {
+ if (errno == EOPNOTSUPP) {
+ // If device doesn't support any type of discard, just overwrite zero data.
+ secRange.flags = F2FS_TRIM_FILE_ZEROOUT;
+ ret = ioctl(fd, F2FS_IOC_SEC_TRIM_FILE, &secRange);
+ }
+ if (ret != 0 && errno != ENOTTY) {
+ PLOG(WARNING) << "F2FS_IOC_SEC_TRIM_FILE failed on " << target;
+ }
+ }
+ if (ret != 0) {
+ if (!secdiscard_path(target)) {
+ LOG(ERROR) << "Secure discard failed for: " << target;
+ }
}
if (options.unlink) {
if (unlink(target.c_str()) != 0 && errno != ENOENT) {
diff --git a/vdc.cpp b/vdc.cpp
index c0b798d..b6bb5ed 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -105,8 +105,6 @@
checkStatus(args, vold->shutdown());
} else if (args[0] == "volume" && args[1] == "reset") {
checkStatus(args, vold->reset());
- } else if (args[0] == "cryptfs" && args[1] == "checkEncryption" && args.size() == 3) {
- checkStatus(args, vold->checkEncryption(args[2]));
} else if (args[0] == "cryptfs" && args[1] == "mountFstab" && args.size() == 4) {
checkStatus(args, vold->mountFstab(args[2], args[3]));
} else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 4) {
@@ -154,6 +152,8 @@
checkStatus(args, vold->abortChanges(args[2], retry != 0));
} else if (args[0] == "checkpoint" && args[1] == "resetCheckpoint") {
checkStatus(args, vold->resetCheckpoint());
+ } else if (args[0] == "keymaster" && args[1] == "early-boot-ended") {
+ checkStatus(args, vold->earlyBootEnded());
} else {
LOG(ERROR) << "Raw commands are no longer supported";
exit(EINVAL);
diff --git a/vold_prepare_subdirs.cpp b/vold_prepare_subdirs.cpp
index d624d73..0283614 100644
--- a/vold_prepare_subdirs.cpp
+++ b/vold_prepare_subdirs.cpp
@@ -54,20 +54,33 @@
return s.size() < 40 && s.find_first_not_of("0123456789abcdefABCDEF-_") == std::string::npos;
}
-static bool prepare_dir(struct selabel_handle* sehandle, mode_t mode, uid_t uid, gid_t gid,
- const std::string& path) {
+static bool prepare_dir_for_user(struct selabel_handle* sehandle, mode_t mode, uid_t uid, gid_t gid,
+ const std::string& path, uid_t user_id) {
auto clearfscreatecon = android::base::make_scope_guard([] { setfscreatecon(nullptr); });
auto secontext = std::unique_ptr<char, void (*)(char*)>(nullptr, freecon);
- char* tmp_secontext;
- if (sehandle && selabel_lookup(sehandle, &tmp_secontext, path.c_str(), S_IFDIR) == 0) {
- secontext.reset(tmp_secontext);
+ if (sehandle) {
+ char* tmp_secontext;
+
+ if (selabel_lookup(sehandle, &tmp_secontext, path.c_str(), S_IFDIR) == 0) {
+ secontext.reset(tmp_secontext);
+
+ if (user_id != (uid_t)-1) {
+ if (selinux_android_context_with_level(secontext.get(), &tmp_secontext, user_id,
+ (uid_t)-1) != 0) {
+ PLOG(ERROR) << "Unable to create context with level for: " << path;
+ return false;
+ }
+ secontext.reset(tmp_secontext); // Free the context
+ }
+ }
}
+
LOG(DEBUG) << "Setting up mode " << std::oct << mode << std::dec << " uid " << uid << " gid "
<< gid << " context " << (secontext ? secontext.get() : "null")
<< " on path: " << path;
if (secontext) {
if (setfscreatecon(secontext.get()) != 0) {
- PLOG(ERROR) << "Unable to read setfscreatecon for: " << path;
+ PLOG(ERROR) << "Unable to setfscreatecon for: " << path;
return false;
}
}
@@ -93,6 +106,11 @@
return true;
}
+static bool prepare_dir(struct selabel_handle* sehandle, mode_t mode, uid_t uid, gid_t gid,
+ const std::string& path) {
+ return prepare_dir_for_user(sehandle, mode, uid, gid, path, (uid_t)-1);
+}
+
static bool rmrf_contents(const std::string& path) {
auto dirp = std::unique_ptr<DIR, int (*)(DIR*)>(opendir(path.c_str()), closedir);
if (!dirp) {
@@ -148,8 +166,17 @@
static bool prepare_subdirs(const std::string& volume_uuid, int user_id, int flags) {
struct selabel_handle* sehandle = selinux_android_file_context_handle();
- if (volume_uuid.empty()) {
- if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+ const uid_t user_for_level =
+ (flags & android::os::IVold::STORAGE_FLAG_LEVEL_FROM_USER) ? user_id : -1;
+
+ if (flags & android::os::IVold::STORAGE_FLAG_DE) {
+ auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
+ if (!prepare_dir_for_user(sehandle, 0771, AID_SYSTEM, AID_SYSTEM, user_de_path,
+ user_for_level)) {
+ return false;
+ }
+
+ if (volume_uuid.empty()) {
auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/vold")) return false;
if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/storaged")) return false;
@@ -158,6 +185,12 @@
prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/apexrollback");
prepare_apex_subdirs(sehandle, misc_de_path);
+ auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
+ if (!prepare_dir_for_user(sehandle, 0771, AID_SYSTEM, AID_SYSTEM, profiles_de_path,
+ user_for_level)) {
+ return false;
+ }
+
auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);
if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM, vendor_de_path + "/fpdata")) {
return false;
@@ -167,11 +200,20 @@
return false;
}
}
- if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+ }
+ if (flags & android::os::IVold::STORAGE_FLAG_CE) {
+ auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
+ if (!prepare_dir_for_user(sehandle, 0771, AID_SYSTEM, AID_SYSTEM, user_ce_path,
+ user_for_level)) {
+ return false;
+ }
+
+ if (volume_uuid.empty()) {
auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/vold")) return false;
if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/storaged")) return false;
if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/rollback")) return false;
+
// TODO: Return false if this returns false once sure this should succeed.
prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/apexrollback");
prepare_apex_subdirs(sehandle, misc_ce_path);