[automerger skipped] vold: only allow emmc_optimized on eMMC storage am: 006eed8e3a am: 32de8f0962 -s ours

am skip reason: Change-Id Ie27b80658db53b1a4207b3cbb4e309d05130812e with SHA-1 006eed8e3a is in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/system/vold/+/12098822

Change-Id: Ia156e546d9051c994fe8faf52b2c65ee4d00f18a
diff --git a/Android.bp b/Android.bp
index b033647..4852fe4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -89,7 +89,7 @@
         export_aidl_headers: true,
     },
     whole_static_libs: [
-        "libincremental_aidl-cpp",
+        "libincremental_aidl-unstable-cpp",
     ],
 }
 
@@ -110,7 +110,6 @@
     srcs: [
         "AppFuseUtil.cpp",
         "Benchmark.cpp",
-        "CheckEncryption.cpp",
         "Checkpoint.cpp",
         "CryptoType.cpp",
         "Devmapper.cpp",
@@ -149,15 +148,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 +170,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/EncryptInplace.cpp b/EncryptInplace.cpp
index 9d304da..bdb2da7 100644
--- a/EncryptInplace.cpp
+++ b/EncryptInplace.cpp
@@ -49,11 +49,7 @@
 /* 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;
@@ -205,9 +201,16 @@
         data->count = 0;
 
         for (block = 0; block < block_count; block++) {
-            int used = (aux_info.bg_desc[i].bg_flags & EXT4_BG_BLOCK_UNINIT)
-                           ? 0
-                           : bitmap_get_bit(block_bitmap, 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) {
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index e21524a..9bdade5 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -269,10 +269,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)) {
diff --git a/KeyUtil.cpp b/KeyUtil.cpp
index acc42db..f3a2986 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) {
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index ca2813d..c61132c 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -283,10 +283,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) {
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 a9b7440..17921e8 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -416,7 +416,32 @@
     return OK;
 }
 
-status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid) {
+int SetAttrs(const std::string& path, unsigned int attrs) {
+    unsigned long flags;
+    android::base::unique_fd fd(
+            TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
+
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << path;
+        return -1;
+    }
+
+    if (ioctl(fd, FS_IOC_GETFLAGS, (void*)&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)) {
+        PLOG(ERROR) << "Failed to set flags for " << path << "(0x" << std::hex << attrs << ")";
+        return -1;
+    }
+    return 0;
+}
+
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+                    unsigned int attrs) {
     std::lock_guard<std::mutex> lock(kSecurityLock);
     const char* cpath = path.c_str();
 
@@ -434,6 +459,9 @@
         freecon(secontext);
     }
 
+    if (res) return -errno;
+    if (attrs) res = SetAttrs(path, attrs);
+
     if (res == 0) {
         return OK;
     } else {
diff --git a/Utils.h b/Utils.h
index 04cbac4..769161a 100644
--- a/Utils.h
+++ b/Utils.h
@@ -34,7 +34,6 @@
 namespace android {
 namespace vold {
 
-static const char* kPropFuse = "persist.sys.fuse";
 static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled";
 static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled";
 
@@ -67,7 +66,8 @@
                           bool fixupExisting);
 
 /* fs_prepare_dir wrapper that creates with SELinux context */
-status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid);
+status_t PrepareDir(const std::string& path, mode_t mode, uid_t uid, gid_t gid,
+                    unsigned int attrs = 0);
 
 /* Really unmounts the path, killing active processes along the way */
 status_t ForceUnmount(const std::string& path);
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 0cb86ce..81e7c63 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -30,7 +30,6 @@
 #include <thread>
 
 #include "Benchmark.h"
-#include "CheckEncryption.h"
 #include "Checkpoint.h"
 #include "FsCrypt.h"
 #include "IdleMaint.h"
@@ -282,12 +281,6 @@
         return translate(res);
     }
 
-    if ((mountFlags & MOUNT_FLAG_PRIMARY) != 0) {
-        res = VolumeManager::Instance()->setPrimary(vol);
-        if (res != OK) {
-            return translate(res);
-        }
-    }
     return translate(OK);
 }
 
@@ -351,17 +344,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 +733,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 +745,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 = "";
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 013d1c2..f10bf5f 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,
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index a543573..8319b24 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -379,21 +379,6 @@
     return success ? 0 : -1;
 }
 
-int VolumeManager::linkPrimary(userid_t userId) {
-    if (!GetBoolProperty(android::vold::kPropFuse, false)) {
-        std::string source(mPrimary->getPath());
-        if (mPrimary->isEmulated()) {
-            source = StringPrintf("%s/%d", source.c_str(), userId);
-            fs_prepare_dir(source.c_str(), 0755, AID_ROOT, AID_ROOT);
-        }
-
-        std::string target(StringPrintf("/mnt/user/%d/primary", userId));
-        LOG(DEBUG) << "Linking " << source << " to " << target;
-        Symlink(source, target);
-    }
-    return 0;
-}
-
 void VolumeManager::destroyEmulatedVolumesForUser(userid_t userId) {
     // Destroy and remove all unstacked EmulatedVolumes for the user
     auto i = mInternalEmulatedVolumes.begin();
@@ -474,18 +459,6 @@
         createEmulatedVolumesForUser(userId);
     }
 
-    if (!GetBoolProperty(android::vold::kPropFuse, false)) {
-        // Note that sometimes the system will spin up processes from Zygote
-        // before actually starting the user, so we're okay if Zygote
-        // already created this directory.
-        std::string path(StringPrintf("%s/%d", kPathUserMount, userId));
-        fs_prepare_dir(path.c_str(), 0755, AID_ROOT, AID_ROOT);
-
-        if (mPrimary) {
-            linkPrimary(userId);
-        }
-    }
-
     mStartedUsers.insert(userId);
 
     createPendingDisksIfNeeded();
@@ -522,14 +495,6 @@
     return 0;
 }
 
-int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
-    mPrimary = vol;
-    for (userid_t userId : mStartedUsers) {
-        linkPrimary(userId);
-    }
-    return 0;
-}
-
 // This code is executed after a fork so it's very important that the set of
 // methods we call here is strictly limited.
 //
@@ -728,16 +693,6 @@
     return true;
 }
 
-int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
-    if (GetBoolProperty(android::vold::kPropFuse, false)) {
-        // TODO(135341433): Implement fuse specific logic.
-        return 0;
-    }
-    return scanProcProcesses(uid, static_cast<userid_t>(-1),
-            forkAndRemountChild, &mountMode) ? 0 : -1;
-}
-
-
 // In each app's namespace, mount tmpfs on obb and data dir, and bind mount obb and data
 // package dirs.
 static bool remountStorageDirs(int nsFd, const char* android_data_dir, const char* android_obb_dir,
@@ -884,9 +839,6 @@
 
 int VolumeManager::remountAppStorageDirs(int uid, int pid,
         const std::vector<std::string>& packageNames) {
-    if (!GetBoolProperty(android::vold::kPropFuse, false)) {
-        return 0;
-    }
     // Only run the remount if fuse is mounted for that user.
     userid_t userId = multiuser_get_user_id(uid);
     bool fuseMounted = false;
diff --git a/VolumeManager.h b/VolumeManager.h
index 3277f75..23a7fe0 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -115,9 +115,7 @@
     void createPendingDisksIfNeeded();
     int onSecureKeyguardStateChanged(bool isShowing);
 
-    int setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol);
-
-    int remountUid(uid_t uid, int32_t remountMode);
+    int remountUid(uid_t uid, int32_t remountMode) { return 0; }
     int remountAppStorageDirs(int uid, int pid, const std::vector<std::string>& packageNames);
 
     /* Aborts all FUSE filesystems, in case the FUSE daemon is no longer up. */
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..8ff544f
--- /dev/null
+++ b/bench/inodeop_bench/inodeop_bench.cpp
@@ -0,0 +1,403 @@
+/*
+ * 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 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;
+    }
+
+    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);
+        close(fd);
+    }
+
+    close(dir_fd);
+    return true;
+}
+
+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;
+    }
+
+    for (int i = 0; i < n_file; i++) {
+        std::string filename = basename + std::to_string(i);
+        unlinkat(dir_fd, filename.c_str(), 0);
+    }
+
+    close(dir_fd);
+    return true;
+}
+
+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;
+    }
+
+    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);
+        renameat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str());
+    }
+
+    close(from_dir_fd);
+    close(from_dir_fd);
+    return true;
+}
+
+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;
+    }
+
+    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);
+        linkat(from_dir_fd, from_filename.c_str(), to_dir_fd, to_filename.c_str(), 0);
+    }
+
+    close(from_dir_fd);
+    close(to_dir_fd);
+    return true;
+}
+
+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;
+    }
+
+    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);
+        symlinkat(from_filepath.c_str(), to_dir_fd, to_filename.c_str());
+    }
+
+    close(to_dir_fd);
+    return true;
+}
+
+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;
+    }
+
+    while (readdir(dir) != nullptr)
+        ;
+
+    closedir(dir);
+    return true;
+}
+
+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..d0e753e 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);
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/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index db93bc2..4a77846 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -301,8 +301,6 @@
 
     dev_t before = GetDevice(mSdcardFsFull);
 
-    bool isFuse = base::GetBoolProperty(kPropFuse, false);
-
     // Mount sdcardfs regardless of FUSE, since we need it to bind-mount on top of the
     // FUSE volume for various reasons.
     if (mUseSdcardFs && getMountUserId() == 0) {
@@ -350,7 +348,7 @@
         sdcardFsPid = 0;
     }
 
-    if (isFuse && isVisible) {
+    if (isVisible) {
         // Make sure we unmount sdcardfs if we bail out with an error below
         auto sdcardfs_unmounter = [&]() {
             LOG(INFO) << "sdcardfs_unmounter scope_guard running";
diff --git a/model/PrivateVolume.cpp b/model/PrivateVolume.cpp
index 39a946c..1875b7b 100644
--- a/model/PrivateVolume.cpp
+++ b/model/PrivateVolume.cpp
@@ -166,11 +166,14 @@
 
     RestoreconRecursive(mPath);
 
+    int attrs = 0;
+    if (!IsSdcardfsUsed()) attrs = FS_CASEFOLD_FL;
+
     // Verify that common directories are ready to roll
     if (PrepareDir(mPath + "/app", 0771, AID_SYSTEM, AID_SYSTEM) ||
         PrepareDir(mPath + "/user", 0711, AID_SYSTEM, AID_SYSTEM) ||
         PrepareDir(mPath + "/user_de", 0711, AID_SYSTEM, AID_SYSTEM) ||
-        PrepareDir(mPath + "/media", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||
+        PrepareDir(mPath + "/media", 0770, AID_MEDIA_RW, AID_MEDIA_RW, attrs) ||
         PrepareDir(mPath + "/media/0", 0770, AID_MEDIA_RW, AID_MEDIA_RW) ||
         PrepareDir(mPath + "/local", 0751, AID_ROOT, AID_ROOT) ||
         PrepareDir(mPath + "/local/tmp", 0771, AID_SHELL, AID_SHELL)) {
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index d40e3e3..12e31ff 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -227,39 +227,36 @@
         TEMP_FAILURE_RETRY(waitpid(sdcardFsPid, nullptr, 0));
     }
 
-    bool isFuse = base::GetBoolProperty(kPropFuse, false);
-    if (isFuse) {
-        // We need to mount FUSE *after* sdcardfs, since the FUSE daemon may depend
-        // on sdcardfs being up.
-        LOG(INFO) << "Mounting public fuse volume";
-        android::base::unique_fd fd;
-        int user_id = getMountUserId();
-        int result = MountUserFuse(user_id, getInternalPath(), stableName, &fd);
+    // We need to mount FUSE *after* sdcardfs, since the FUSE daemon may depend
+    // on sdcardfs being up.
+    LOG(INFO) << "Mounting public fuse volume";
+    android::base::unique_fd fd;
+    int user_id = getMountUserId();
+    int result = MountUserFuse(user_id, getInternalPath(), stableName, &fd);
 
-        if (result != 0) {
-            LOG(ERROR) << "Failed to mount public fuse volume";
-            doUnmount();
-            return -result;
-        }
-
-        mFuseMounted = true;
-        auto callback = getMountCallback();
-        if (callback) {
-            bool is_ready = false;
-            callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
-            if (!is_ready) {
-                LOG(ERROR) << "Failed to complete public volume mount";
-                doUnmount();
-                return -EIO;
-            }
-        }
-
-        ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, stableName), 256u);
-
-        // See comment in model/EmulatedVolume.cpp
-        ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, stableName), 40u);
+    if (result != 0) {
+        LOG(ERROR) << "Failed to mount public fuse volume";
+        doUnmount();
+        return -result;
     }
 
+    mFuseMounted = true;
+    auto callback = getMountCallback();
+    if (callback) {
+        bool is_ready = false;
+        callback->onVolumeChecking(std::move(fd), getPath(), getInternalPath(), &is_ready);
+        if (!is_ready) {
+            LOG(ERROR) << "Failed to complete public volume mount";
+            doUnmount();
+            return -EIO;
+        }
+    }
+
+    ConfigureReadAheadForFuse(GetFuseMountPathForUser(user_id, stableName), 256u);
+
+    // See comment in model/EmulatedVolume.cpp
+    ConfigureMaxDirtyRatioForFuse(GetFuseMountPathForUser(user_id, stableName), 40u);
+
     return OK;
 }
 
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..11562e7 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) {