[automerger skipped] KeyStorage: don't request rollback resistance for wrapped storage keys am: e11788d0f0 -s ours

am skip reason: Merged-In I648a1af9e16787dfcfeefa2b2f2e4a72cac2c6a6 with SHA-1 2d30b890d2 is already in history

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

Change-Id: I4f23a56ba26c4a2531524b51f278079dd04215fe
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/Android.bp b/Android.bp
index 8f413c4..6fb9c6c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -19,8 +19,9 @@
         "clang-analyzer-security*",
         "android-*",
     ],
-    tidy_flags: [
-        "-warnings-as-errors=clang-analyzer-security*,cert-*",
+    tidy_checks_as_errors: [
+        "clang-analyzer-security*",
+        "cert-*",
     ],
 }
 
@@ -42,6 +43,8 @@
     ],
     shared_libs: [
         "android.hardware.boot@1.0",
+        "android.hardware.boot-V1-ndk",
+        "libboot_control_client",
         "libbase",
         "libbinder",
         "libcrypto",
@@ -187,7 +190,7 @@
     required: [
         "mke2fs",
         "vold_prepare_subdirs",
-        "fuse_media.o",
+        "fuseMedia.o",
     ],
 
     shared_libs: [
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 755f0e3..948231d 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -26,12 +26,12 @@
 #include <thread>
 #include <vector>
 
+#include <BootControlClient.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/properties.h>
 #include <android-base/unique_fd.h>
-#include <android/hardware/boot/1.0/IBootControl.h>
 #include <cutils/android_reboot.h>
 #include <fcntl.h>
 #include <fs_mgr.h>
@@ -48,11 +48,7 @@
 using android::binder::Status;
 using android::fs_mgr::Fstab;
 using android::fs_mgr::ReadFstabFromFile;
-using android::hardware::hidl_string;
-using android::hardware::boot::V1_0::BoolResult;
-using android::hardware::boot::V1_0::CommandResult;
-using android::hardware::boot::V1_0::IBootControl;
-using android::hardware::boot::V1_0::Slot;
+using android::hal::BootControlClient;
 
 namespace android {
 namespace vold {
@@ -128,11 +124,10 @@
     if (retry < -1) return error(EINVAL, "Retry count must be more than -1");
     std::string content = std::to_string(retry + 1);
     if (retry == -1) {
-        sp<IBootControl> module = IBootControl::getService();
+        auto module = BootControlClient::WaitForService();
         if (module) {
-            std::string suffix;
-            auto cb = [&suffix](hidl_string s) { suffix = s; };
-            if (module->getSuffix(module->getCurrentSlot(), cb).isOk()) content += " " + suffix;
+            std::string suffix = module->GetSuffix(module->GetCurrentSlot());
+            if (!suffix.empty()) content += " " + suffix;
         }
     }
     if (!android::base::WriteStringToFile(content, kMetadataCPFile))
@@ -162,10 +157,9 @@
             << "NOT COMMITTING CHECKPOINT BECAUSE persist.vold.dont_commit_checkpoint IS 1";
         return Status::ok();
     }
-    sp<IBootControl> module = IBootControl::getService();
+    auto module = BootControlClient::WaitForService();
     if (module) {
-        CommandResult cr;
-        module->markBootSuccessful([&cr](CommandResult result) { cr = result; });
+        auto cr = module->MarkBootSuccessful();
         if (!cr.success)
             return error(EINVAL, "Error marking booted successfully: " + std::string(cr.errMsg));
         LOG(INFO) << "Marked slot as booted successfully.";
@@ -173,6 +167,8 @@
         if (!SetProperty("ota.warm_reset", "0")) {
             LOG(WARNING) << "Failed to reset the warm reset flag";
         }
+    } else {
+        LOG(ERROR) << "Failed to get BootControl HAL, not marking slot as successful.";
     }
     // Must take action for list of mounted checkpointed things here
     // To do this, we walk the list of mounted file systems.
@@ -254,12 +250,11 @@
         if (content == "0") return true;
         if (content.substr(0, 3) == "-1 ") {
             std::string oldSuffix = content.substr(3);
-            sp<IBootControl> module = IBootControl::getService();
+            auto module = BootControlClient::WaitForService();
             std::string newSuffix;
 
             if (module) {
-                auto cb = [&newSuffix](hidl_string s) { newSuffix = s; };
-                module->getSuffix(module->getCurrentSlot(), cb);
+                newSuffix = module->GetSuffix(module->GetCurrentSlot());
                 if (oldSuffix == newSuffix) return true;
             }
         }
@@ -276,11 +271,11 @@
 
     bool ret;
     std::string content;
-    sp<IBootControl> module = IBootControl::getService();
+    auto module = BootControlClient::WaitForService();
 
     if (isCheckpointing) return isCheckpointing;
-
-    if (module && module->isSlotMarkedSuccessful(module->getCurrentSlot()) == BoolResult::FALSE) {
+    // In case of INVALID slot or other failures, we do not perform checkpoint.
+    if (module && !module->IsSlotMarkedSuccessful(module->GetCurrentSlot()).value_or(true)) {
         isCheckpointing = true;
         return true;
     }
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 6c08177..5bc55d0 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -33,7 +33,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
-#include <selinux/android.h>
 #include <sys/mount.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -44,7 +43,6 @@
 
 #include "android/os/IVold.h"
 
-#define EMULATED_USES_SELINUX 0
 #define MANAGE_MISC_DIRS 0
 
 #include <cutils/fs.h>
@@ -94,9 +92,16 @@
 const std::string systemwide_volume_key_dir =
     std::string() + DATA_MNT_POINT + "/misc/vold/volume_keys";
 
+const std::string data_data_dir = std::string() + DATA_MNT_POINT + "/data";
+const std::string data_user_0_dir = std::string() + DATA_MNT_POINT + "/user/0";
+const std::string media_obb_dir = std::string() + DATA_MNT_POINT + "/media/obb";
+
 // Some users are ephemeral, don't try to wipe their keys from disk
 std::set<userid_t> s_ephemeral_users;
 
+// The system DE encryption policy
+EncryptionPolicy s_device_policy;
+
 // Map user ids to encryption policies
 std::map<userid_t, EncryptionPolicy> s_de_policies;
 std::map<userid_t, EncryptionPolicy> s_ce_policies;
@@ -108,10 +113,6 @@
     return KeyGeneration{FSCRYPT_MAX_KEY_SIZE, true, options.use_hw_wrapped_key};
 }
 
-static bool fscrypt_is_emulated() {
-    return property_get_bool("persist.sys.emulate_fbe", false);
-}
-
 static const char* escape_empty(const std::string& value) {
     return value.empty() ? "null" : value.c_str();
 }
@@ -307,15 +308,26 @@
     return true;
 }
 
+// Prepare a directory without assigning it an encryption policy.  The directory
+// will inherit the encryption policy of its parent directory, or will be
+// unencrypted if the parent directory is unencrypted.
 static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gid) {
     LOG(DEBUG) << "Preparing: " << dir;
-    if (fs_prepare_dir(dir.c_str(), mode, uid, gid) != 0) {
+    if (android::vold::PrepareDir(dir, mode, uid, gid, 0) != 0) {
         PLOG(ERROR) << "Failed to prepare " << dir;
         return false;
     }
     return true;
 }
 
+// Prepare a directory and assign it the given encryption policy.
+static bool prepare_dir_with_policy(const std::string& dir, mode_t mode, uid_t uid, gid_t gid,
+                                    const EncryptionPolicy& policy) {
+    if (!prepare_dir(dir, mode, uid, gid)) return false;
+    if (IsFbeEnabled() && !EnsurePolicy(policy, dir)) return false;
+    return true;
+}
+
 static bool destroy_dir(const std::string& dir) {
     LOG(DEBUG) << "Destroying: " << dir;
     if (rmdir(dir.c_str()) != 0 && errno != ENOENT) {
@@ -365,7 +377,6 @@
                           EncryptionPolicy* policy) {
     auto refi = key_map.find(user_id);
     if (refi == key_map.end()) {
-        LOG(DEBUG) << "Cannot find key for " << user_id;
         return false;
     }
     *policy = refi->second;
@@ -443,11 +454,12 @@
                                makeGen(options), &device_key))
         return false;
 
-    EncryptionPolicy device_policy;
-    if (!install_storage_key(DATA_MNT_POINT, options, device_key, &device_policy)) return false;
+    // This initializes s_device_policy, which is a global variable so that
+    // fscrypt_init_user0() can access it later.
+    if (!install_storage_key(DATA_MNT_POINT, options, device_key, &s_device_policy)) return false;
 
     std::string options_string;
-    if (!OptionsToString(device_policy.options, &options_string)) {
+    if (!OptionsToString(s_device_policy.options, &options_string)) {
         LOG(ERROR) << "Unable to serialize options";
         return false;
     }
@@ -455,7 +467,7 @@
     if (!android::vold::writeStringToFile(options_string, options_filename)) return false;
 
     std::string ref_filename = std::string(DATA_MNT_POINT) + fscrypt_key_ref;
-    if (!android::vold::writeStringToFile(device_policy.key_raw_ref, ref_filename)) return false;
+    if (!android::vold::writeStringToFile(s_device_policy.key_raw_ref, ref_filename)) return false;
     LOG(INFO) << "Wrote system DE key reference to:" << ref_filename;
 
     KeyBuffer per_boot_key;
@@ -470,11 +482,58 @@
     return true;
 }
 
+static bool prepare_special_dirs() {
+    // Ensure that /data/data and its "alias" /data/user/0 exist, and create the
+    // bind mount of /data/data onto /data/user/0.  This *should* happen in
+    // fscrypt_prepare_user_storage().  However, it actually must be done early,
+    // before the rest of user 0's CE storage is prepared.  This is because
+    // zygote may need to set up app data isolation before then, which requires
+    // mounting a tmpfs over /data/data to ensure it remains hidden.  This issue
+    // arises due to /data/data being in the top-level directory.
+
+    // /data/user/0 used to be a symlink to /data/data, so we must first delete
+    // the old symlink if present.
+    if (android::vold::IsSymlink(data_user_0_dir) && android::vold::Unlink(data_user_0_dir) != 0)
+        return false;
+    // On first boot, we'll be creating /data/data for the first time, and user
+    // 0's CE key will be installed already since it was just created.  Take the
+    // opportunity to also set the encryption policy of /data/data right away.
+    EncryptionPolicy ce_policy;
+    if (lookup_policy(s_ce_policies, 0, &ce_policy)) {
+        if (!prepare_dir_with_policy(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM, ce_policy))
+            return false;
+    } else {
+        if (!prepare_dir(data_data_dir, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
+        // EnsurePolicy() will have to happen later, in fscrypt_prepare_user_storage().
+    }
+    if (!prepare_dir(data_user_0_dir, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
+    if (android::vold::BindMount(data_data_dir, data_user_0_dir) != 0) return false;
+
+    // If /data/media/obb doesn't exist, create it and encrypt it with the
+    // device policy.  Normally, device-policy-encrypted directories are created
+    // and encrypted by init; /data/media/obb is special because it is located
+    // in /data/media.  Since /data/media also contains per-user encrypted
+    // directories, by design only vold can write to it.  As a side effect of
+    // that, vold must create /data/media/obb.
+    //
+    // We must tolerate /data/media/obb being unencrypted if it already exists
+    // on-disk, since it used to be unencrypted (b/64566063).
+    if (android::vold::pathExists(media_obb_dir)) {
+        if (!prepare_dir(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+    } else {
+        if (!prepare_dir_with_policy(media_obb_dir, 0770, AID_MEDIA_RW, AID_MEDIA_RW,
+                                     s_device_policy))
+            return false;
+    }
+    return true;
+}
+
 bool fscrypt_init_user0_done;
 
 bool fscrypt_init_user0() {
     LOG(DEBUG) << "fscrypt_init_user0";
-    if (fscrypt_is_native()) {
+
+    if (IsFbeEnabled()) {
         if (!prepare_dir(user_key_dir, 0700, AID_ROOT, AID_ROOT)) return false;
         if (!prepare_dir(user_key_dir + "/ce", 0700, AID_ROOT, AID_ROOT)) return false;
         if (!prepare_dir(user_key_dir + "/de", 0700, AID_ROOT, AID_ROOT)) return false;
@@ -485,24 +544,23 @@
         // explicit calls to install DE keys for secondary users
         if (!load_all_de_keys()) return false;
     }
-    // We can only safely prepare DE storage here, since CE keys are probably
-    // entangled with user credentials.  The framework will always prepare CE
-    // storage once CE keys are installed.
+
+    // Now that user 0's CE key has been created, we can prepare /data/data.
+    if (!prepare_special_dirs()) return false;
+
+    // With the exception of what is done by prepare_special_dirs() above, we
+    // only prepare DE storage here, since user 0's CE key won't be installed
+    // yet unless it was just created.  The framework will prepare the user's CE
+    // storage later, once their CE key is installed.
     if (!fscrypt_prepare_user_storage("", 0, 0, android::os::IVold::STORAGE_FLAG_DE)) {
         LOG(ERROR) << "Failed to prepare user 0 storage";
         return false;
     }
 
-    // If this is a non-FBE device that recently left an emulated mode,
-    // restore user data directories to known-good state.
-    if (!fscrypt_is_native() && !fscrypt_is_emulated()) {
-        fscrypt_unlock_user_key(0, 0, "!");
-    }
-
     // In some scenarios (e.g. userspace reboot) we might unmount userdata
     // without doing a hard reboot. If CE keys were stored in fs keyring then
     // they will be lost after unmount. Attempt to re-install them.
-    if (fscrypt_is_native() && android::vold::isFsKeyringSupported()) {
+    if (IsFbeEnabled() && android::vold::isFsKeyringSupported()) {
         if (!try_reload_ce_keys()) return false;
     }
 
@@ -512,7 +570,7 @@
 
 bool fscrypt_vold_create_user_key(userid_t user_id, int serial, bool ephemeral) {
     LOG(DEBUG) << "fscrypt_vold_create_user_key for " << user_id << " serial " << serial;
-    if (!fscrypt_is_native()) {
+    if (!IsFbeEnabled()) {
         return true;
     }
     // FIXME test for existence of key that is not loaded yet
@@ -563,7 +621,7 @@
 
 bool fscrypt_destroy_user_key(userid_t user_id) {
     LOG(DEBUG) << "fscrypt_destroy_user_key(" << user_id << ")";
-    if (!fscrypt_is_native()) {
+    if (!IsFbeEnabled()) {
         return true;
     }
     bool success = true;
@@ -592,36 +650,6 @@
     return success;
 }
 
-static bool emulated_lock(const std::string& path) {
-    if (chmod(path.c_str(), 0000) != 0) {
-        PLOG(ERROR) << "Failed to chmod " << path;
-        return false;
-    }
-#if EMULATED_USES_SELINUX
-    if (setfilecon(path.c_str(), "u:object_r:storage_stub_file:s0") != 0) {
-        PLOG(WARNING) << "Failed to setfilecon " << path;
-        return false;
-    }
-#endif
-    return true;
-}
-
-static bool emulated_unlock(const std::string& path, mode_t mode) {
-    if (chmod(path.c_str(), mode) != 0) {
-        PLOG(ERROR) << "Failed to chmod " << path;
-        // FIXME temporary workaround for b/26713622
-        if (fscrypt_is_emulated()) return false;
-    }
-#if EMULATED_USES_SELINUX
-    if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_FORCE) != 0) {
-        PLOG(WARNING) << "Failed to restorecon " << path;
-        // FIXME temporary workaround for b/26713622
-        if (fscrypt_is_emulated()) return false;
-    }
-#endif
-    return true;
-}
-
 static bool parse_hex(const std::string& hex, std::string* result) {
     if (hex == "!") {
         *result = "";
@@ -712,7 +740,7 @@
 
 bool fscrypt_add_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) {
     LOG(DEBUG) << "fscrypt_add_user_key_auth " << user_id << " serial=" << serial;
-    if (!fscrypt_is_native()) return true;
+    if (!IsFbeEnabled()) return true;
     auto auth = authentication_from_hex(secret_hex);
     if (!auth) return false;
     return fscrypt_rewrap_user_key(user_id, serial, kEmptyAuthentication, *auth);
@@ -720,7 +748,7 @@
 
 bool fscrypt_clear_user_key_auth(userid_t user_id, int serial, const std::string& secret_hex) {
     LOG(DEBUG) << "fscrypt_clear_user_key_auth " << user_id << " serial=" << serial;
-    if (!fscrypt_is_native()) return true;
+    if (!IsFbeEnabled()) return true;
     auto auth = authentication_from_hex(secret_hex);
     if (!auth) return false;
     return fscrypt_rewrap_user_key(user_id, serial, *auth, kEmptyAuthentication);
@@ -728,7 +756,7 @@
 
 bool fscrypt_fixate_newest_user_key_auth(userid_t user_id) {
     LOG(DEBUG) << "fscrypt_fixate_newest_user_key_auth " << user_id;
-    if (!fscrypt_is_native()) return true;
+    if (!IsFbeEnabled()) return true;
     if (s_ephemeral_users.count(user_id) != 0) return true;
     auto const directory_path = get_ce_key_directory_path(user_id);
     auto const paths = get_ce_key_paths(directory_path);
@@ -751,7 +779,7 @@
 // TODO: rename to 'install' for consistency, and take flags to know which keys to install
 bool fscrypt_unlock_user_key(userid_t user_id, int serial, const std::string& secret_hex) {
     LOG(DEBUG) << "fscrypt_unlock_user_key " << user_id << " serial=" << serial;
-    if (fscrypt_is_native()) {
+    if (IsFbeEnabled()) {
         if (s_ce_policies.count(user_id) != 0) {
             LOG(WARNING) << "Tried to unlock already-unlocked key for user " << user_id;
             return true;
@@ -762,17 +790,6 @@
             LOG(ERROR) << "Couldn't read key for " << user_id;
             return false;
         }
-    } else {
-        // When in emulation mode, we just use chmod. However, we also
-        // unlock directories when not in emulation mode, to bring devices
-        // back into a known-good state.
-        if (!emulated_unlock(android::vold::BuildDataSystemCePath(user_id), 0771) ||
-            !emulated_unlock(android::vold::BuildDataMiscCePath("", user_id), 01771) ||
-            !emulated_unlock(android::vold::BuildDataMediaCePath("", user_id), 0770) ||
-            !emulated_unlock(android::vold::BuildDataUserCePath("", user_id), 0771)) {
-            LOG(ERROR) << "Failed to unlock user " << user_id;
-            return false;
-        }
     }
     return true;
 }
@@ -780,19 +797,9 @@
 // TODO: rename to 'evict' for consistency
 bool fscrypt_lock_user_key(userid_t user_id) {
     LOG(DEBUG) << "fscrypt_lock_user_key " << user_id;
-    if (fscrypt_is_native()) {
+    if (IsFbeEnabled()) {
         return evict_ce_key(user_id);
-    } else if (fscrypt_is_emulated()) {
-        // When in emulation mode, we just use chmod
-        if (!emulated_lock(android::vold::BuildDataSystemCePath(user_id)) ||
-            !emulated_lock(android::vold::BuildDataMiscCePath("", user_id)) ||
-            !emulated_lock(android::vold::BuildDataMediaCePath("", user_id)) ||
-            !emulated_lock(android::vold::BuildDataUserCePath("", user_id))) {
-            LOG(ERROR) << "Failed to lock user " << user_id;
-            return false;
-        }
     }
-
     return true;
 }
 
@@ -836,11 +843,26 @@
         auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);
 
         // DE_n key
+        EncryptionPolicy de_policy;
         auto system_de_path = android::vold::BuildDataSystemDePath(user_id);
         auto misc_de_path = android::vold::BuildDataMiscDePath(volume_uuid, user_id);
         auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);
         auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);
 
+        if (IsFbeEnabled()) {
+            if (volume_uuid.empty()) {
+                if (!lookup_policy(s_de_policies, user_id, &de_policy)) {
+                    LOG(ERROR) << "Cannot find DE policy for user " << user_id;
+                    return false;
+                }
+            } else {
+                auto misc_de_empty_volume_path = android::vold::BuildDataMiscDePath("", user_id);
+                if (!read_or_create_volkey(misc_de_empty_volume_path, volume_uuid, &de_policy)) {
+                    return false;
+                }
+            }
+        }
+
         if (volume_uuid.empty()) {
             if (!prepare_dir(system_legacy_path, 0700, AID_SYSTEM, AID_SYSTEM)) return false;
 #if MANAGE_MISC_DIRS
@@ -850,43 +872,49 @@
 #endif
             if (!prepare_dir(profiles_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
 
-            if (!prepare_dir(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
-            if (!prepare_dir(vendor_de_path, 0771, AID_ROOT, AID_ROOT)) return false;
+            if (!prepare_dir_with_policy(system_de_path, 0770, AID_SYSTEM, AID_SYSTEM, de_policy))
+                return false;
+            if (!prepare_dir_with_policy(vendor_de_path, 0771, AID_ROOT, AID_ROOT, de_policy))
+                return false;
         }
 
-        if (!prepare_dir(misc_de_path, 01771, AID_SYSTEM, AID_MISC)) return false;
-        if (!prepare_dir(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
-
-        if (fscrypt_is_native()) {
-            EncryptionPolicy de_policy;
-            if (volume_uuid.empty()) {
-                if (!lookup_policy(s_de_policies, user_id, &de_policy)) return false;
-                if (!EnsurePolicy(de_policy, system_de_path)) return false;
-                if (!EnsurePolicy(de_policy, vendor_de_path)) return false;
-            } else {
-                auto misc_de_empty_volume_path = android::vold::BuildDataMiscDePath("", user_id);
-                if (!read_or_create_volkey(misc_de_empty_volume_path, volume_uuid, &de_policy)) {
-                    return false;
-                }
-            }
-            if (!EnsurePolicy(de_policy, misc_de_path)) return false;
-            if (!EnsurePolicy(de_policy, user_de_path)) return false;
-        }
+        if (!prepare_dir_with_policy(misc_de_path, 01771, AID_SYSTEM, AID_MISC, de_policy))
+            return false;
+        if (!prepare_dir_with_policy(user_de_path, 0771, AID_SYSTEM, AID_SYSTEM, de_policy))
+            return false;
     }
 
     if (flags & android::os::IVold::STORAGE_FLAG_CE) {
         // CE_n key
+        EncryptionPolicy ce_policy;
         auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
         auto misc_ce_path = android::vold::BuildDataMiscCePath(volume_uuid, user_id);
         auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id);
         auto media_ce_path = android::vold::BuildDataMediaCePath(volume_uuid, user_id);
         auto user_ce_path = android::vold::BuildDataUserCePath(volume_uuid, user_id);
 
-        if (volume_uuid.empty()) {
-            if (!prepare_dir(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM)) return false;
-            if (!prepare_dir(vendor_ce_path, 0771, AID_ROOT, AID_ROOT)) return false;
+        if (IsFbeEnabled()) {
+            if (volume_uuid.empty()) {
+                if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) {
+                    LOG(ERROR) << "Cannot find CE policy for user " << user_id;
+                    return false;
+                }
+            } else {
+                auto misc_ce_empty_volume_path = android::vold::BuildDataMiscCePath("", user_id);
+                if (!read_or_create_volkey(misc_ce_empty_volume_path, volume_uuid, &ce_policy)) {
+                    return false;
+                }
+            }
         }
-        if (!prepare_dir(media_ce_path, 02770, AID_MEDIA_RW, AID_MEDIA_RW)) return false;
+
+        if (volume_uuid.empty()) {
+            if (!prepare_dir_with_policy(system_ce_path, 0770, AID_SYSTEM, AID_SYSTEM, ce_policy))
+                return false;
+            if (!prepare_dir_with_policy(vendor_ce_path, 0771, AID_ROOT, AID_ROOT, ce_policy))
+                return false;
+        }
+        if (!prepare_dir_with_policy(media_ce_path, 02770, AID_MEDIA_RW, AID_MEDIA_RW, ce_policy))
+            return false;
         // On devices without sdcardfs (kernel 5.4+), the path permissions aren't fixed
         // up automatically; therefore, use a default ACL, to ensure apps with MEDIA_RW
         // can keep reading external storage; in particular, this allows app cloning
@@ -895,26 +923,10 @@
         if (ret != android::OK) {
             return false;
         }
-
-        if (!prepare_dir(misc_ce_path, 01771, AID_SYSTEM, AID_MISC)) return false;
-        if (!prepare_dir(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM)) return false;
-
-        if (fscrypt_is_native()) {
-            EncryptionPolicy ce_policy;
-            if (volume_uuid.empty()) {
-                if (!lookup_policy(s_ce_policies, user_id, &ce_policy)) return false;
-                if (!EnsurePolicy(ce_policy, system_ce_path)) return false;
-                if (!EnsurePolicy(ce_policy, vendor_ce_path)) return false;
-            } else {
-                auto misc_ce_empty_volume_path = android::vold::BuildDataMiscCePath("", user_id);
-                if (!read_or_create_volkey(misc_ce_empty_volume_path, volume_uuid, &ce_policy)) {
-                    return false;
-                }
-            }
-            if (!EnsurePolicy(ce_policy, media_ce_path)) return false;
-            if (!EnsurePolicy(ce_policy, misc_ce_path)) return false;
-            if (!EnsurePolicy(ce_policy, user_ce_path)) return false;
-        }
+        if (!prepare_dir_with_policy(misc_ce_path, 01771, AID_SYSTEM, AID_MISC, ce_policy))
+            return false;
+        if (!prepare_dir_with_policy(user_ce_path, 0771, AID_SYSTEM, AID_SYSTEM, ce_policy))
+            return false;
 
         if (volume_uuid.empty()) {
             // Now that credentials have been installed, we can run restorecon
@@ -952,7 +964,7 @@
             res &= destroy_dir(system_ce_path);
             res &= destroy_dir(vendor_ce_path);
         } else {
-            if (fscrypt_is_native()) {
+            if (IsFbeEnabled()) {
                 auto misc_ce_empty_volume_path = android::vold::BuildDataMiscCePath("", user_id);
                 res &= destroy_volkey(misc_ce_empty_volume_path, volume_uuid);
             }
@@ -982,7 +994,7 @@
             res &= destroy_dir(system_de_path);
             res &= destroy_dir(vendor_de_path);
         } else {
-            if (fscrypt_is_native()) {
+            if (IsFbeEnabled()) {
                 auto misc_de_empty_volume_path = android::vold::BuildDataMiscDePath("", user_id);
                 res &= destroy_volkey(misc_de_empty_volume_path, volume_uuid);
             }
diff --git a/IdleMaint.cpp b/IdleMaint.cpp
index 9d3450a..0c6b115 100644
--- a/IdleMaint.cpp
+++ b/IdleMaint.cpp
@@ -86,7 +86,6 @@
 static const int GC_TIMEOUT_SEC = 420;
 static const int DEVGC_TIMEOUT_SEC = 120;
 static const int KBYTES_IN_SEGMENT = 2048;
-static const int MIN_GC_URGENT_SLEEP_TIME = 500;
 static const int ONE_MINUTE_IN_MS = 60000;
 static const int GC_NORMAL_MODE = 0;
 static const int GC_URGENT_MID_MODE = 3;
@@ -509,9 +508,10 @@
 }
 
 void SetGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, float dirtyReclaimRate,
-                     float reclaimWeight, int32_t gcPeriod) {
+                     float reclaimWeight, int32_t gcPeriod, int32_t minGCSleepTime,
+                     int32_t targetDirtyRatio) {
     std::list<std::string> paths;
-    bool needGC = true;
+    bool needGC = false;
     int32_t sleepTime;
 
     addFromFstab(&paths, PathTypes::kBlkDevice, true);
@@ -554,25 +554,36 @@
     int32_t reservedBlocks = std::stoi(ovpSegmentsStr) + std::stoi(reservedBlocksStr);
 
     freeSegments = freeSegments > reservedBlocks ? freeSegments - reservedBlocks : 0;
-    neededSegments *= reclaimWeight;
-    if (freeSegments >= neededSegments) {
-        LOG(INFO) << "Enough free segments: " << freeSegments
-                   << ", needed segments: " << neededSegments;
-        needGC = false;
-    } else if (freeSegments + dirtySegments < minSegmentThreshold) {
+    int32_t totalSegments = freeSegments + dirtySegments;
+    int32_t finalTargetSegments = 0;
+
+    if (totalSegments < minSegmentThreshold) {
         LOG(INFO) << "The sum of free segments: " << freeSegments
-                   << ", dirty segments: " << dirtySegments << " is under " << minSegmentThreshold;
-        needGC = false;
+                  << ", dirty segments: " << dirtySegments << " is under " << minSegmentThreshold;
     } else {
-        neededSegments -= freeSegments;
-        neededSegments = std::min(neededSegments, (int32_t)(dirtySegments * dirtyReclaimRate));
-        if (neededSegments == 0) {
-            LOG(INFO) << "Low dirty segments: " << dirtySegments;
-            needGC = false;
+        int32_t dirtyRatio = dirtySegments * 100 / totalSegments;
+        int32_t neededForTargetRatio =
+                (dirtyRatio > targetDirtyRatio)
+                        ? totalSegments * (dirtyRatio - targetDirtyRatio) / 100
+                        : 0;
+        neededSegments *= reclaimWeight;
+        neededSegments = (neededSegments > freeSegments) ? neededSegments - freeSegments : 0;
+
+        finalTargetSegments = std::max(neededSegments, neededForTargetRatio);
+        if (finalTargetSegments == 0) {
+            LOG(INFO) << "Enough free segments: " << freeSegments;
         } else {
-            sleepTime = gcPeriod * ONE_MINUTE_IN_MS / neededSegments;
-            if (sleepTime < MIN_GC_URGENT_SLEEP_TIME) {
-                sleepTime = MIN_GC_URGENT_SLEEP_TIME;
+            finalTargetSegments =
+                    std::min(finalTargetSegments, (int32_t)(dirtySegments * dirtyReclaimRate));
+            if (finalTargetSegments == 0) {
+                LOG(INFO) << "Low dirty segments: " << dirtySegments;
+            } else if (neededSegments >= neededForTargetRatio) {
+                LOG(INFO) << "Trigger GC, because of needed segments exceeding free segments";
+                needGC = true;
+            } else {
+                LOG(INFO) << "Trigger GC for target dirty ratio diff of: "
+                          << dirtyRatio - targetDirtyRatio;
+                needGC = true;
             }
         }
     }
@@ -584,6 +595,11 @@
         return;
     }
 
+    sleepTime = gcPeriod * ONE_MINUTE_IN_MS / finalTargetSegments;
+    if (sleepTime < minGCSleepTime) {
+        sleepTime = minGCSleepTime;
+    }
+
     if (!WriteStringToFile(std::to_string(sleepTime), gcSleepTimePath)) {
         PLOG(WARNING) << "Writing failed in " << gcSleepTimePath;
         return;
@@ -595,8 +611,8 @@
     }
 
     LOG(INFO) << "Successfully set gc urgent mode: "
-               << "free segments: " << freeSegments << ", reclaim target: " << neededSegments
-               << ", sleep time: " << sleepTime;
+              << "free segments: " << freeSegments << ", reclaim target: " << finalTargetSegments
+              << ", sleep time: " << sleepTime;
 }
 
 static int32_t getLifeTimeWrite() {
diff --git a/IdleMaint.h b/IdleMaint.h
index 9a2af4a..a28cde2 100644
--- a/IdleMaint.h
+++ b/IdleMaint.h
@@ -27,7 +27,8 @@
 int AbortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
 int32_t GetStorageLifeTime();
 void SetGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold, float dirtyReclaimRate,
-                     float reclaimWeight, int32_t gcPeriod);
+                     float reclaimWeight, int32_t gcPeriod, int32_t minGCSleepTime,
+                     int32_t targetDirtyRatio);
 void RefreshLatestWrite();
 int32_t GetWriteAmount();
 
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 5c9e644..4152e25 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -63,6 +63,7 @@
 };
 
 static const std::string kDmNameUserdata = "userdata";
+static const std::string kDmNameUserdataZoned = "userdata_zoned";
 
 // The first entry in this table is the default crypto type.
 constexpr CryptoType supported_crypto_types[] = {aes_256_xts, adiantum};
@@ -238,10 +239,11 @@
 
 bool fscrypt_mount_metadata_encrypted(const std::string& blk_device, const std::string& mount_point,
                                       bool needs_encrypt, bool should_format,
-                                      const std::string& fs_type) {
+                                      const std::string& fs_type, const std::string& zoned_device) {
     LOG(DEBUG) << "fscrypt_mount_metadata_encrypted: " << mount_point
                << " encrypt: " << needs_encrypt << " format: " << should_format << " with "
-               << fs_type;
+               << fs_type << " block device: " << blk_device
+               << " and zoned device: " << zoned_device;
     auto encrypted_state = android::base::GetProperty("ro.crypto.state", "");
     if (encrypted_state != "" && encrypted_state != "encrypted") {
         LOG(ERROR) << "fscrypt_mount_metadata_encrypted got unexpected starting state: "
@@ -280,9 +282,13 @@
         return false;
     }
 
+    auto default_metadata_key_dir = data_rec->metadata_key_dir;
+    if (!zoned_device.empty()) {
+        default_metadata_key_dir = default_metadata_key_dir + "/default";
+    }
     auto gen = needs_encrypt ? makeGen(options) : neverGen();
     KeyBuffer key;
-    if (!read_key(data_rec->metadata_key_dir, gen, &key)) {
+    if (!read_key(default_metadata_key_dir, gen, &key)) {
         LOG(ERROR) << "read_key failed in mountFstab";
         return false;
     }
@@ -295,6 +301,23 @@
         return false;
     }
 
+    // create dm-default-key for zoned device
+    std::string crypto_zoned_blkdev;
+    if (!zoned_device.empty()) {
+        auto zoned_metadata_key_dir = data_rec->metadata_key_dir + "/zoned";
+
+        if (!read_key(zoned_metadata_key_dir, gen, &key)) {
+            LOG(ERROR) << "read_key failed with zoned device: " << zoned_device;
+            return false;
+        }
+        if (!create_crypto_blk_dev(kDmNameUserdataZoned, zoned_device, key, options,
+                                   &crypto_zoned_blkdev, &nr_sec)) {
+            LOG(ERROR) << "fscrypt_mount_metadata_encrypted: failed with zoned device: "
+                       << zoned_device;
+            return false;
+        }
+    }
+
     if (needs_encrypt) {
         if (should_format) {
             status_t error;
@@ -302,7 +325,7 @@
             if (fs_type == "ext4") {
                 error = ext4::Format(crypto_blkdev, 0, mount_point);
             } else if (fs_type == "f2fs") {
-                error = f2fs::Format(crypto_blkdev);
+                error = f2fs::Format(crypto_blkdev, crypto_zoned_blkdev);
             } else {
                 LOG(ERROR) << "Unknown filesystem type: " << fs_type;
                 return false;
@@ -314,6 +337,10 @@
             }
             LOG(DEBUG) << "Format of " << crypto_blkdev << " for " << mount_point << " succeeded.";
         } else {
+            if (!zoned_device.empty()) {
+                LOG(ERROR) << "encrypt_inplace cannot support zoned device; should format it.";
+                return false;
+            }
             if (!encrypt_inplace(crypto_blkdev, blk_device, nr_sec)) {
                 LOG(ERROR) << "encrypt_inplace failed in mountFstab";
                 return false;
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index 06131ad..f6d6b8e 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -28,7 +28,8 @@
 void defaultkey_precreate_dm_device();
 bool fscrypt_mount_metadata_encrypted(const std::string& block_device,
                                       const std::string& mount_point, bool needs_encrypt,
-                                      bool should_format, const std::string& fs_type);
+                                      bool should_format, const std::string& fs_type,
+                                      const std::string& zoned_device);
 
 bool defaultkey_volume_keygen(KeyGeneration* gen);
 
diff --git a/Utils.cpp b/Utils.cpp
index e8049ed..9e77a9e 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -1160,14 +1160,6 @@
 std::string BuildDataUserCePath(const std::string& volumeUuid, userid_t userId) {
     // TODO: unify with installd path generation logic
     std::string data(BuildDataPath(volumeUuid));
-    if (volumeUuid.empty() && userId == 0) {
-        std::string legacy = StringPrintf("%s/data", data.c_str());
-        struct stat sb;
-        if (lstat(legacy.c_str(), &sb) == 0 && S_ISDIR(sb.st_mode)) {
-            /* /data/data is dir, return /data/data for legacy system */
-            return legacy;
-        }
-    }
     return StringPrintf("%s/user/%u", data.c_str(), userId);
 }
 
@@ -1187,6 +1179,12 @@
     }
 }
 
+// Returns true if |path| exists and is a symlink.
+bool IsSymlink(const std::string& path) {
+    struct stat stbuf;
+    return lstat(path.c_str(), &stbuf) == 0 && S_ISLNK(stbuf.st_mode);
+}
+
 // 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;
diff --git a/Utils.h b/Utils.h
index 429669b..fbd0f30 100644
--- a/Utils.h
+++ b/Utils.h
@@ -162,6 +162,8 @@
 
 dev_t GetDevice(const std::string& path);
 
+bool IsSymlink(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);
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 8ba3aaf..ea2c98c 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -495,11 +495,13 @@
 binder::Status VoldNativeService::setGCUrgentPace(int32_t neededSegments,
                                                   int32_t minSegmentThreshold,
                                                   float dirtyReclaimRate, float reclaimWeight,
-                                                  int32_t gcPeriod) {
+                                                  int32_t gcPeriod, int32_t minGCSleepTime,
+                                                  int32_t targetDirtyRatio) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    SetGCUrgentPace(neededSegments, minSegmentThreshold, dirtyReclaimRate, reclaimWeight, gcPeriod);
+    SetGCUrgentPace(neededSegments, minSegmentThreshold, dirtyReclaimRate, reclaimWeight, gcPeriod,
+                    minGCSleepTime, targetDirtyRatio);
     return Ok();
 }
 
@@ -566,22 +568,24 @@
 }
 
 binder::Status VoldNativeService::mountFstab(const std::string& blkDevice,
-                                             const std::string& mountPoint) {
+                                             const std::string& mountPoint,
+                                             const std::string& zonedDevice) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    return translateBool(
-            fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, false, false, "null"));
+    return translateBool(fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, false, false,
+                                                          "null", zonedDevice));
 }
 
 binder::Status VoldNativeService::encryptFstab(const std::string& blkDevice,
                                                const std::string& mountPoint, bool shouldFormat,
-                                               const std::string& fsType) {
+                                               const std::string& fsType,
+                                               const std::string& zonedDevice) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    return translateBool(
-            fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, true, shouldFormat, fsType));
+    return translateBool(fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, true, shouldFormat,
+                                                          fsType, zonedDevice));
 }
 
 binder::Status VoldNativeService::setStorageBindingSeed(const std::vector<uint8_t>& seed) {
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 423e8f9..37a988b 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -90,7 +90,8 @@
     binder::Status abortIdleMaint(const android::sp<android::os::IVoldTaskListener>& listener);
     binder::Status getStorageLifeTime(int32_t* _aidl_return);
     binder::Status setGCUrgentPace(int32_t neededSegments, int32_t minSegmentThreshold,
-                                   float dirtyReclaimRate, float reclaimWeight, int32_t gcPeriod);
+                                   float dirtyReclaimRate, float reclaimWeight, int32_t gcPeriod,
+                                   int32_t minGCSleepTime, int32_t targetDirtyRatio);
     binder::Status refreshLatestWrite();
     binder::Status getWriteAmount(int32_t* _aidl_return);
 
@@ -103,9 +104,11 @@
     binder::Status fbeEnable();
 
     binder::Status initUser0();
-    binder::Status mountFstab(const std::string& blkDevice, const std::string& mountPoint);
+    binder::Status mountFstab(const std::string& blkDevice, const std::string& mountPoint,
+                              const std::string& zonedDevice);
     binder::Status encryptFstab(const std::string& blkDevice, const std::string& mountPoint,
-                                bool shouldFormat, const std::string& fsType);
+                                bool shouldFormat, const std::string& fsType,
+                                const std::string& zonedDevice);
 
     binder::Status setStorageBindingSeed(const std::vector<uint8_t>& seed);
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index a7d39c1..5cef239 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -358,7 +358,7 @@
         LOG(ERROR) << "Failed to unlink " << keyPath;
         success = false;
     }
-    if (fscrypt_is_native()) {
+    if (IsFbeEnabled()) {
         if (!fscrypt_destroy_volume_keys(fsUuid)) {
             success = false;
         }
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index d77c7da..77478d9 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -69,7 +69,8 @@
     int getStorageLifeTime();
     void setGCUrgentPace(int neededSegments, int minSegmentThreshold,
                          float dirtyReclaimRate, float reclaimWeight,
-                         int gcPeriod);
+                         int gcPeriod, int minGCSleepTime,
+                         int targetDirtyRatio);
     void refreshLatestWrite();
     int getWriteAmount();
 
@@ -79,8 +80,8 @@
     void fbeEnable();
 
     void initUser0();
-    void mountFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint);
-    void encryptFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, boolean shouldFormat, @utf8InCpp String fsType);
+    void mountFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, @utf8InCpp String zonedDevice);
+    void encryptFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, boolean shouldFormat, @utf8InCpp String fsType, @utf8InCpp String zonedDevice);
 
     void setStorageBindingSeed(in byte[] seed);
 
diff --git a/cryptfs.cpp b/cryptfs.cpp
index ab8f3ec..5213c18 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -156,7 +156,7 @@
                                                   key_ascii, 0, real_blkdev, 0);
     target->AllowDiscards();
 
-    if (fscrypt_is_native() &&
+    if (IsFbeEnabled() &&
         android::base::GetBoolProperty("ro.crypto.allow_encrypt_override", false)) {
         target->AllowEncryptOverride();
     }
diff --git a/fs/Ext4.cpp b/fs/Ext4.cpp
index 52f6772..293efc4 100644
--- a/fs/Ext4.cpp
+++ b/fs/Ext4.cpp
@@ -182,7 +182,7 @@
     if (android::base::GetBoolProperty("vold.has_quota", false)) {
         options += ",quota";
     }
-    if (fscrypt_is_native()) {
+    if (IsFbeEnabled()) {
         options += ",encrypt";
     }
     if (needs_casefold) {
diff --git a/fs/F2fs.cpp b/fs/F2fs.cpp
index 55b0823..23363e3 100644
--- a/fs/F2fs.cpp
+++ b/fs/F2fs.cpp
@@ -71,7 +71,7 @@
     return res;
 }
 
-status_t Format(const std::string& source) {
+status_t Format(const std::string& source, const std::string& zoned_device) {
     std::vector<char const*> cmd;
     cmd.emplace_back(kMkfsPath);
 
@@ -96,6 +96,11 @@
         cmd.emplace_back("-C");
         cmd.emplace_back("utf8");
     }
+    if (!zoned_device.empty()) {
+        cmd.emplace_back("-c");
+        cmd.emplace_back(zoned_device.c_str());
+        cmd.emplace_back("-m");
+    }
     cmd.emplace_back(source.c_str());
     return logwrap_fork_execvp(cmd.size(), cmd.data(), nullptr, false, LOG_KLOG,
                              false, nullptr);
diff --git a/fs/F2fs.h b/fs/F2fs.h
index f710212..cdad581 100644
--- a/fs/F2fs.h
+++ b/fs/F2fs.h
@@ -29,7 +29,7 @@
 
 status_t Check(const std::string& source);
 status_t Mount(const std::string& source, const std::string& target);
-status_t Format(const std::string& source);
+status_t Format(const std::string& source, const std::string& zoned_device = "");
 
 }  // namespace f2fs
 }  // namespace vold
diff --git a/vdc.cpp b/vdc.cpp
index 740e246..b63abbb 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -124,14 +124,14 @@
         checkStatus(args, vold->reset());
     } else if (args[0] == "cryptfs" && args[1] == "bindkeys") {
         bindkeys(args, vold);
-    } 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() == 6) {
+    } else if (args[0] == "cryptfs" && args[1] == "mountFstab" && args.size() == 5) {
+        checkStatus(args, vold->mountFstab(args[2], args[3], args[4]));
+    } else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 7) {
         auto shouldFormat = android::base::ParseBool(args[4]);
         if (shouldFormat == android::base::ParseBoolResult::kError) exit(EINVAL);
         checkStatus(args, vold->encryptFstab(args[2], args[3],
                                              shouldFormat == android::base::ParseBoolResult::kTrue,
-                                             args[5]));
+                                             args[5], args[6]));
     } else if (args[0] == "checkpoint" && args[1] == "supportsCheckpoint" && args.size() == 2) {
         bool supported = false;
         checkStatus(args, vold->supportsCheckpoint(&supported));