Merge "Fix vfat retry fsck logic" into main
diff --git a/AppFuseUtil.cpp b/AppFuseUtil.cpp
index a3beaec..711e70b 100644
--- a/AppFuseUtil.cpp
+++ b/AppFuseUtil.cpp
@@ -50,15 +50,14 @@
 
 static android::status_t Mount(int device_fd, const std::string& path) {
     const auto opts = StringPrintf(
-            "fd=%i,"
-            "rootmode=40000,"
-            "default_permissions,"
-            "allow_other,"
-            "max_read=65536,"
-            "user_id=0,group_id=0,"
-            "context=\"u:object_r:app_fuse_file:s0\","
-            "fscontext=u:object_r:app_fusefs:s0",
-            device_fd);
+        "fd=%i,"
+        "rootmode=40000,"
+        "default_permissions,"
+        "allow_other,"
+        "user_id=0,group_id=0,"
+        "context=\"u:object_r:app_fuse_file:s0\","
+        "fscontext=u:object_r:app_fusefs:s0",
+        device_fd);
 
     const int result =
         TEMP_FAILURE_RETRY(mount("/dev/fuse", path.c_str(), "fuse",
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index eca49ef..598a87b 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -198,7 +198,8 @@
 
     // Walk mounted file systems
     for (const auto& mount_rec : mounts) {
-        const auto fstab_rec = GetEntryForMountPoint(&fstab_default, mount_rec.mount_point);
+        const auto fstab_rec =
+                GetEntryForMountPoint(&fstab_default, mount_rec.mount_point, mount_rec.fs_type);
         if (!fstab_rec) continue;
 
         if (fstab_rec->fs_mgr_flags.checkpoint_fs) {
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 51b35c5..3eb4599 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -675,26 +675,13 @@
     return success;
 }
 
-static bool parse_hex(const std::string& hex, std::string* result) {
-    if (hex == "!") {
-        *result = "";
-        return true;
-    }
-    if (android::vold::HexToStr(hex, *result) != 0) {
-        LOG(ERROR) << "Invalid FBE hex string";  // Don't log the string for security reasons
-        return false;
-    }
-    return true;
-}
-
-static std::optional<android::vold::KeyAuthentication> authentication_from_hex(
-        const std::string& secret_hex) {
-    std::string secret;
-    if (!parse_hex(secret_hex, &secret)) return std::optional<android::vold::KeyAuthentication>();
-    if (secret.empty()) {
+static android::vold::KeyAuthentication authentication_from_secret(
+        const std::vector<uint8_t>& secret) {
+    std::string secret_str(secret.begin(), secret.end());
+    if (secret_str.empty()) {
         return kEmptyAuthentication;
     } else {
-        return android::vold::KeyAuthentication(secret);
+        return android::vold::KeyAuthentication(secret_str);
     }
 }
 
@@ -743,12 +730,11 @@
 // re-encrypting the CE key upon upgrade from an Android version where the CE
 // key was stored with kEmptyAuthentication when the user didn't have an LSKF.
 // See the comments below for the different cases handled.
-bool fscrypt_set_ce_key_protection(userid_t user_id, const std::string& secret_hex) {
+bool fscrypt_set_ce_key_protection(userid_t user_id, const std::vector<uint8_t>& secret) {
     LOG(DEBUG) << "fscrypt_set_ce_key_protection " << user_id;
     if (!IsFbeEnabled()) return true;
-    auto auth = authentication_from_hex(secret_hex);
-    if (!auth) return false;
-    if (auth->secret.empty()) {
+    auto auth = authentication_from_secret(secret);
+    if (auth.secret.empty()) {
         LOG(ERROR) << "fscrypt_set_ce_key_protection: secret must be nonempty";
         return false;
     }
@@ -776,7 +762,7 @@
             // with the given secret.  This isn't expected, but in theory it
             // could happen if an upgrade is requested for a user more than once
             // due to a power-off or other interruption.
-            if (read_and_fixate_user_ce_key(user_id, *auth, &ce_key)) {
+            if (read_and_fixate_user_ce_key(user_id, auth, &ce_key)) {
                 LOG(WARNING) << "CE key is already protected by given secret";
                 return true;
             }
@@ -802,7 +788,7 @@
     auto const paths = get_ce_key_paths(directory_path);
     std::string ce_key_path;
     if (!get_ce_key_new_path(directory_path, paths, &ce_key_path)) return false;
-    if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, *auth, ce_key)) return false;
+    if (!android::vold::storeKeyAtomically(ce_key_path, user_key_temp, auth, ce_key)) return false;
 
     // Fixate the key, i.e. delete all other bindings of it.  (In practice this
     // just means the kEmptyAuthentication binding, if there is one.)  However,
@@ -845,17 +831,16 @@
 // Unlocks internal CE storage for the given user.  This only unlocks internal storage, since
 // fscrypt_prepare_user_storage() has to be called for each adoptable storage volume anyway (since
 // the volume might have been absent when the user was created), and that handles the unlocking.
-bool fscrypt_unlock_ce_storage(userid_t user_id, const std::string& secret_hex) {
+bool fscrypt_unlock_ce_storage(userid_t user_id, const std::vector<uint8_t>& secret) {
     LOG(DEBUG) << "fscrypt_unlock_ce_storage " << user_id;
     if (!IsFbeEnabled()) return true;
     if (s_ce_policies.count(user_id) != 0) {
         LOG(WARNING) << "CE storage for user " << user_id << " is already unlocked";
         return true;
     }
-    auto auth = authentication_from_hex(secret_hex);
-    if (!auth) return false;
+    auto auth = authentication_from_secret(secret);
     KeyBuffer ce_key;
-    if (!read_and_fixate_user_ce_key(user_id, *auth, &ce_key)) return false;
+    if (!read_and_fixate_user_ce_key(user_id, auth, &ce_key)) return false;
     EncryptionPolicy ce_policy;
     if (!install_storage_key(DATA_MNT_POINT, s_data_options, ce_key, &ce_policy)) return false;
     s_ce_policies[user_id].internal = ce_policy;
diff --git a/FsCrypt.h b/FsCrypt.h
index afcedfb..be21fba 100644
--- a/FsCrypt.h
+++ b/FsCrypt.h
@@ -25,11 +25,11 @@
 extern bool fscrypt_init_user0_done;
 bool fscrypt_create_user_keys(userid_t user_id, bool ephemeral);
 bool fscrypt_destroy_user_keys(userid_t user_id);
-bool fscrypt_set_ce_key_protection(userid_t user_id, const std::string& secret);
+bool fscrypt_set_ce_key_protection(userid_t user_id, const std::vector<uint8_t>& secret);
 void fscrypt_deferred_fixate_ce_keys();
 
 std::vector<int> fscrypt_get_unlocked_users();
-bool fscrypt_unlock_ce_storage(userid_t user_id, const std::string& secret);
+bool fscrypt_unlock_ce_storage(userid_t user_id, const std::vector<uint8_t>& secret);
 bool fscrypt_lock_ce_storage(userid_t user_id);
 
 bool fscrypt_prepare_user_storage(const std::string& volume_uuid, userid_t user_id, int flags);
diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp
index 0e2cad1..a1106fd 100644
--- a/MetadataCrypt.cpp
+++ b/MetadataCrypt.cpp
@@ -47,6 +47,7 @@
 namespace android {
 namespace vold {
 
+using android::base::Basename;
 using android::fs_mgr::FstabEntry;
 using android::fs_mgr::GetEntryForMountPoint;
 using android::fscrypt::GetFirstApiLevel;
@@ -63,7 +64,6 @@
 };
 
 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};
@@ -153,7 +153,7 @@
 
 static bool create_crypto_blk_dev(const std::string& dm_name, const std::string& blk_device,
                                   const KeyBuffer& key, const CryptoOptions& options,
-                                  std::string* crypto_blkdev, uint64_t* nr_sec) {
+                                  std::string* crypto_blkdev, uint64_t* nr_sec, bool is_userdata) {
     if (!get_number_of_sectors(blk_device, nr_sec)) return false;
     // TODO(paulcrowley): don't hardcode that DmTargetDefaultKey uses 4096-byte
     // sectors
@@ -204,7 +204,7 @@
     // If there are multiple partitions used for a single mount, F2FS stores
     // their partition paths in superblock. If the paths are dm targets, we
     // cannot guarantee them across device boots. Let's use the logical paths.
-    if (dm_name == kDmNameUserdata || dm_name == kDmNameUserdataZoned) {
+    if (is_userdata) {
         *crypto_blkdev = "/dev/block/mapper/" + dm_name;
     }
     return true;
@@ -246,11 +246,16 @@
 
 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& zoned_device) {
+                                      const std::string& fs_type, bool is_zoned,
+                                      const std::vector<std::string>& user_devices) {
     LOG(DEBUG) << "fscrypt_mount_metadata_encrypted: " << mount_point
                << " encrypt: " << needs_encrypt << " format: " << should_format << " with "
-               << fs_type << " block device: " << blk_device
-               << " and zoned device: " << zoned_device;
+               << fs_type << " block device: " << blk_device << " with zoned " << is_zoned;
+
+    for (auto& device : user_devices) {
+        LOG(DEBUG) << " - user devices: " << 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: "
@@ -290,7 +295,7 @@
     }
 
     auto default_metadata_key_dir = data_rec->metadata_key_dir;
-    if (!zoned_device.empty()) {
+    if (!user_devices.empty()) {
         default_metadata_key_dir = default_metadata_key_dir + "/default";
     }
     auto gen = needs_encrypt ? makeGen(options) : neverGen();
@@ -302,27 +307,28 @@
 
     std::string crypto_blkdev;
     uint64_t nr_sec;
-    if (!create_crypto_blk_dev(kDmNameUserdata, blk_device, key, options, &crypto_blkdev,
-                               &nr_sec)) {
+    if (!create_crypto_blk_dev(kDmNameUserdata, blk_device, key, options, &crypto_blkdev, &nr_sec,
+                               true)) {
         LOG(ERROR) << "create_crypto_blk_dev failed in mountFstab";
         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";
+    // create dm-default-key for user devices
+    std::vector<std::string> crypto_user_blkdev;
+    for (auto& device : user_devices) {
+        std::string name = Basename(device);
+        auto metadata_key_dir = data_rec->metadata_key_dir + "/" + name;
 
-        if (!read_key(zoned_metadata_key_dir, gen, false, &key)) {
-            LOG(ERROR) << "read_key failed with zoned device: " << zoned_device;
+        if (!read_key(metadata_key_dir, gen, false, &key)) {
+            LOG(ERROR) << "read_key failed with zoned device: " << 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;
+        std::string crypto_blkdev_arg;
+        if (!create_crypto_blk_dev(name, device, key, options, &crypto_blkdev_arg, &nr_sec, true)) {
+            LOG(ERROR) << "fscrypt_mount_metadata_encrypted: failed with device: " << device;
             return false;
         }
+        crypto_user_blkdev.push_back(crypto_blkdev_arg.c_str());
     }
 
     if (needs_encrypt) {
@@ -332,7 +338,7 @@
             if (fs_type == "ext4") {
                 error = ext4::Format(crypto_blkdev, 0, mount_point);
             } else if (fs_type == "f2fs") {
-                error = f2fs::Format(crypto_blkdev, crypto_zoned_blkdev);
+                error = f2fs::Format(crypto_blkdev, is_zoned, crypto_user_blkdev);
             } else {
                 LOG(ERROR) << "Unknown filesystem type: " << fs_type;
                 return false;
@@ -344,8 +350,9 @@
             }
             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.";
+            if (!user_devices.empty()) {
+                LOG(ERROR) << "encrypt_inplace cannot support zoned or userdata_exp device; should "
+                              "format it.";
                 return false;
             }
             if (!encrypt_inplace(crypto_blkdev, blk_device, nr_sec)) {
@@ -384,7 +391,8 @@
     CryptoOptions options;
     if (!get_volume_options(&options)) return false;
     uint64_t nr_sec;
-    return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec);
+    return create_crypto_blk_dev(label, blk_device, key, options, out_crypto_blkdev, &nr_sec,
+                                 false);
 }
 
 bool destroy_dsu_metadata_key(const std::string& dsu_slot) {
diff --git a/MetadataCrypt.h b/MetadataCrypt.h
index f6d6b8e..2c07a14 100644
--- a/MetadataCrypt.h
+++ b/MetadataCrypt.h
@@ -28,8 +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,
-                                      const std::string& zoned_device);
+                                      bool should_format, const std::string& fs_type, bool is_zoned,
+                                      const std::vector<std::string>& user_devices);
 
 bool defaultkey_volume_keygen(KeyGeneration* gen);
 
diff --git a/TEST_MAPPING b/TEST_MAPPING
index a535181..93938b6 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,15 @@
       "name": "CtsScopedStorageDeviceOnlyTest"
     },
     {
+      "name": "CtsScopedStorageBypassDatabaseOperationsTest"
+    },
+    {
+      "name": "CtsScopedStorageGeneralTest"
+    },
+    {
+      "name": "CtsScopedStorageRedactUriTest"
+    },
+    {
       "name": "AdoptableHostTest"
     }
   ],
@@ -24,6 +33,15 @@
       "name": "CtsScopedStorageDeviceOnlyTest"
     },
     {
+      "name": "CtsScopedStorageBypassDatabaseOperationsTest"
+    },
+    {
+      "name": "CtsScopedStorageGeneralTest"
+    },
+    {
+      "name": "CtsScopedStorageRedactUriTest"
+    },
+    {
       "name": "AdoptableHostTest"
     }
   ]
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index 96f4eaf..a70639c 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -588,24 +588,24 @@
 }
 
 binder::Status VoldNativeService::mountFstab(const std::string& blkDevice,
-                                             const std::string& mountPoint,
-                                             const std::string& zonedDevice) {
+                                             const std::string& mountPoint, bool isZoned,
+                                             const std::vector<std::string>& userDevices) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
     return translateBool(fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, false, false,
-                                                          "null", zonedDevice));
+                                                          "null", isZoned, userDevices));
 }
 
 binder::Status VoldNativeService::encryptFstab(const std::string& blkDevice,
                                                const std::string& mountPoint, bool shouldFormat,
-                                               const std::string& fsType,
-                                               const std::string& zonedDevice) {
+                                               const std::string& fsType, bool isZoned,
+                                               const std::vector<std::string>& userDevices) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
     return translateBool(fscrypt_mount_metadata_encrypted(blkDevice, mountPoint, true, shouldFormat,
-                                                          fsType, zonedDevice));
+                                                          fsType, isZoned, userDevices));
 }
 
 binder::Status VoldNativeService::setStorageBindingSeed(const std::vector<uint8_t>& seed) {
@@ -630,7 +630,7 @@
 }
 
 binder::Status VoldNativeService::setCeStorageProtection(int32_t userId,
-                                                         const std::string& secret) {
+                                                         const std::vector<uint8_t>& secret) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_CRYPT_LOCK;
 
@@ -645,7 +645,8 @@
     return Ok();
 }
 
-binder::Status VoldNativeService::unlockCeStorage(int32_t userId, const std::string& secret) {
+binder::Status VoldNativeService::unlockCeStorage(int32_t userId,
+                                                  const std::vector<uint8_t>& secret) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_CRYPT_LOCK;
 
diff --git a/VoldNativeService.h b/VoldNativeService.h
index bb00d35..619c720 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -106,20 +106,20 @@
 
     binder::Status initUser0();
     binder::Status mountFstab(const std::string& blkDevice, const std::string& mountPoint,
-                              const std::string& zonedDevice);
+                              bool isZoned, const std::vector<std::string>& userDevices);
     binder::Status encryptFstab(const std::string& blkDevice, const std::string& mountPoint,
-                                bool shouldFormat, const std::string& fsType,
-                                const std::string& zonedDevice);
+                                bool shouldFormat, const std::string& fsType, bool isZoned,
+                                const std::vector<std::string>& userDevices);
 
     binder::Status setStorageBindingSeed(const std::vector<uint8_t>& seed);
 
     binder::Status createUserStorageKeys(int32_t userId, bool ephemeral);
     binder::Status destroyUserStorageKeys(int32_t userId);
 
-    binder::Status setCeStorageProtection(int32_t userId, const std::string& secret);
+    binder::Status setCeStorageProtection(int32_t userId, const std::vector<uint8_t>& secret);
 
     binder::Status getUnlockedUsers(std::vector<int>* _aidl_return);
-    binder::Status unlockCeStorage(int32_t userId, const std::string& secret);
+    binder::Status unlockCeStorage(int32_t userId, const std::vector<uint8_t>& secret);
     binder::Status lockCeStorage(int32_t userId);
 
     binder::Status prepareUserStorage(const std::optional<std::string>& uuid, int32_t userId,
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index c981f2d..a1ac20d 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -921,25 +921,34 @@
 int VolumeManager::reset() {
     // Tear down all existing disks/volumes and start from a blank slate so
     // newly connected framework hears all events.
+
+    // Destroy StubVolume disks. This needs to be done before destroying
+    // EmulatedVolumes because in ARC (Android on ChromeOS), ChromeOS Downloads
+    // directory (which is in a StubVolume) is bind-mounted to
+    // /data/media/0/Download.
+    // We do not recreate StubVolumes here because they are managed from outside
+    // Android (e.g. from ChromeOS) and their disk recreation on reset events
+    // should be handled from outside by calling createStubVolume() again.
+    for (const auto& disk : mDisks) {
+        if (disk->isStub()) {
+            disk->destroy();
+        }
+    }
+    // Remove StubVolume from both mDisks and mPendingDisks.
+    const auto isStub = [](const auto& disk) { return disk->isStub(); };
+    mDisks.remove_if(isStub);
+    mPendingDisks.remove_if(isStub);
+
     for (const auto& vol : mInternalEmulatedVolumes) {
         vol->destroy();
     }
     mInternalEmulatedVolumes.clear();
 
-    // Destroy and recreate all disks except that StubVolume disks are just
-    // destroyed and removed from both mDisks and mPendingDisks.
-    // StubVolumes are managed from outside Android (e.g. from Chrome OS) and
-    // their disk recreation on reset events should be handled from outside by
-    // calling createStubVolume() again.
+    // Destroy and recreate non-StubVolume disks.
     for (const auto& disk : mDisks) {
         disk->destroy();
-        if (!disk->isStub()) {
-            disk->create();
-        }
+        disk->create();
     }
-    const auto isStub = [](const auto& disk) { return disk->isStub(); };
-    mDisks.remove_if(isStub);
-    mPendingDisks.remove_if(isStub);
 
     updateVirtualDisk();
     mAddedUsers.clear();
@@ -958,11 +967,20 @@
         return 0;  // already shutdown
     }
     android::vold::sSleepOnUnmount = false;
+    // Destroy StubVolume disks before destroying EmulatedVolumes (see the
+    // comment in VolumeManager::reset()).
+    for (const auto& disk : mDisks) {
+        if (disk->isStub()) {
+            disk->destroy();
+        }
+    }
     for (const auto& vol : mInternalEmulatedVolumes) {
         vol->destroy();
     }
     for (const auto& disk : mDisks) {
-        disk->destroy();
+        if (!disk->isStub()) {
+            disk->destroy();
+        }
     }
 
     mInternalEmulatedVolumes.clear();
@@ -978,11 +996,20 @@
     ATRACE_NAME("VolumeManager::unmountAll()");
 
     // First, try gracefully unmounting all known devices
+    // Unmount StubVolume disks before unmounting EmulatedVolumes (see the
+    // comment in VolumeManager::reset()).
+    for (const auto& disk : mDisks) {
+        if (disk->isStub()) {
+            disk->unmountAll();
+        }
+    }
     for (const auto& vol : mInternalEmulatedVolumes) {
         vol->unmount();
     }
     for (const auto& disk : mDisks) {
-        disk->unmountAll();
+        if (!disk->isStub()) {
+            disk->unmountAll();
+        }
     }
 
     // Worst case we might have some stale mounts lurking around, so
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index d121dee..919369b 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -22,6 +22,7 @@
 import android.os.IVoldTaskListener;
 
 /** {@hide} */
+@SensitiveData
 interface IVold {
     void setListener(IVoldListener listener);
 
@@ -82,18 +83,18 @@
     void fbeEnable();
 
     void initUser0();
-    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 mountFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, boolean isZoned, in @utf8InCpp String[] userDevices);
+    void encryptFstab(@utf8InCpp String blkDevice, @utf8InCpp String mountPoint, boolean shouldFormat, @utf8InCpp String fsType, boolean isZoned, in @utf8InCpp String[] userDevices);
 
     void setStorageBindingSeed(in byte[] seed);
 
     void createUserStorageKeys(int userId, boolean ephemeral);
     void destroyUserStorageKeys(int userId);
 
-    void setCeStorageProtection(int userId, @utf8InCpp String secret);
+    void setCeStorageProtection(int userId, in byte[] secret);
 
     int[] getUnlockedUsers();
-    void unlockCeStorage(int userId, @utf8InCpp String secret);
+    void unlockCeStorage(int userId, in byte[] secret);
     void lockCeStorage(int userId);
 
     void prepareUserStorage(@nullable @utf8InCpp String uuid, int userId, int storageFlags);
diff --git a/fs/F2fs.cpp b/fs/F2fs.cpp
index 07f8480..99afc32 100644
--- a/fs/F2fs.cpp
+++ b/fs/F2fs.cpp
@@ -71,7 +71,8 @@
     return res;
 }
 
-status_t Format(const std::string& source, const std::string& zoned_device) {
+status_t Format(const std::string& source, bool is_zoned,
+                const std::vector<std::string>& user_devices) {
     std::vector<char const*> cmd;
     cmd.emplace_back(kMkfsPath);
 
@@ -96,12 +97,13 @@
         cmd.emplace_back("-C");
         cmd.emplace_back("utf8");
     }
-    if (!zoned_device.empty()) {
-        cmd.emplace_back("-c");
-        cmd.emplace_back(zoned_device.c_str());
+    if (is_zoned) {
         cmd.emplace_back("-m");
     }
-
+    for (auto& device : user_devices) {
+        cmd.emplace_back("-c");
+        cmd.emplace_back(device.c_str());
+    }
     std::string block_size = std::to_string(getpagesize());
     cmd.emplace_back("-b");
     cmd.emplace_back(block_size.c_str());
diff --git a/fs/F2fs.h b/fs/F2fs.h
index cdad581..7192b54 100644
--- a/fs/F2fs.h
+++ b/fs/F2fs.h
@@ -29,7 +29,8 @@
 
 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, const std::string& zoned_device = "");
+status_t Format(const std::string& source, const bool is_zoned,
+                const std::vector<std::string>& user_devices);
 
 }  // namespace f2fs
 }  // namespace vold
diff --git a/model/Disk.cpp b/model/Disk.cpp
index 4df4e9d..01cd0c3 100644
--- a/model/Disk.cpp
+++ b/model/Disk.cpp
@@ -365,7 +365,6 @@
                 continue;
             }
         } else if (*it == "PART") {
-            foundParts = true;
 
             if (++it == split.end()) continue;
             int i = 0;
@@ -390,6 +389,7 @@
                     case 0x0c:  // W95 FAT32 (LBA)
                     case 0x0e:  // W95 FAT16 (LBA)
                         createPublicVolume(partDevice);
+                        foundParts = true;
                         break;
                 }
             } else if (table == Table::kGpt) {
@@ -400,8 +400,10 @@
 
                 if (android::base::EqualsIgnoreCase(typeGuid, kGptBasicData)) {
                     createPublicVolume(partDevice);
+                    foundParts = true;
                 } else if (android::base::EqualsIgnoreCase(typeGuid, kGptAndroidExpand)) {
                     createPrivateVolume(partDevice, partGuid);
+                    foundParts = true;
                 }
             }
         }
diff --git a/model/PrivateVolume.cpp b/model/PrivateVolume.cpp
index 55b8d0b..bb52647 100644
--- a/model/PrivateVolume.cpp
+++ b/model/PrivateVolume.cpp
@@ -234,7 +234,7 @@
             return -EIO;
         }
     } else if (resolvedFsType == "f2fs") {
-        if (f2fs::Format(mDmDevPath)) {
+        if (f2fs::Format(mDmDevPath, false, {})) {
             PLOG(ERROR) << getId() << " failed to format";
             return -EIO;
         }
diff --git a/tests/Android.bp b/tests/Android.bp
index 36142b4..95a9580 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -1,4 +1,5 @@
 package {
+    default_team: "trendy_team_android_kernel",
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
@@ -14,7 +15,7 @@
         "VoldNativeServiceValidation_test.cpp",
     ],
     static_libs: ["libvold"],
-    shared_libs: ["libbinder"]
+    shared_libs: ["libbinder"],
 }
 
 cc_fuzz {
@@ -24,7 +25,7 @@
         "vold_default_libs",
         "keystore2_use_latest_aidl_ndk_shared",
         "service_fuzzer_defaults",
-        "fuzzer_disable_leaks"
+        "fuzzer_disable_leaks",
     ],
     static_libs: [
         "libvold",
@@ -40,5 +41,5 @@
         cc: [
             "maco@google.com",
         ],
-    }
+    },
 }
diff --git a/vdc.cpp b/vdc.cpp
index 7d72535..ee8cf9e 100644
--- a/vdc.cpp
+++ b/vdc.cpp
@@ -86,6 +86,38 @@
     checkStatus(args, vold->setStorageBindingSeed(seed));
 }
 
+static void mountFstab(std::vector<std::string>& args,
+                       const android::sp<android::os::IVold>& vold) {
+    auto isZoned = android::base::ParseBool(args[4]);
+    if (isZoned == android::base::ParseBoolResult::kError) exit(EINVAL);
+
+    std::vector<std::string> userDevices = {};
+    if (args[5] != "") {
+        userDevices = android::base::Split(args[5], " ");
+    }
+    checkStatus(args,
+                vold->mountFstab(args[2], args[3], isZoned == android::base::ParseBoolResult::kTrue,
+                                 userDevices));
+}
+
+static void encryptFstab(std::vector<std::string>& args,
+                         const android::sp<android::os::IVold>& vold) {
+    auto shouldFormat = android::base::ParseBool(args[4]);
+    if (shouldFormat == android::base::ParseBoolResult::kError) exit(EINVAL);
+
+    auto isZoned = android::base::ParseBool(args[6]);
+    if (isZoned == android::base::ParseBoolResult::kError) exit(EINVAL);
+
+    std::vector<std::string> userDevices = {};
+    if (args[7] != "") {
+        userDevices = android::base::Split(args[7], " ");
+    }
+    checkStatus(args,
+                vold->encryptFstab(args[2], args[3],
+                                   shouldFormat == android::base::ParseBoolResult::kTrue, args[5],
+                                   isZoned == android::base::ParseBoolResult::kTrue, userDevices));
+}
+
 int main(int argc, char** argv) {
     setenv("ANDROID_LOG_TAGS", "*:v", 1);
     if (getppid() == 1) {
@@ -128,14 +160,10 @@
         LOG(INFO) << size;
     } else if (args[0] == "cryptfs" && args[1] == "bindkeys") {
         bindkeys(args, vold);
-    } 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[6]));
+    } else if (args[0] == "cryptfs" && args[1] == "mountFstab" && args.size() == 6) {
+        mountFstab(args, vold);
+    } else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 8) {
+        encryptFstab(args, vold);
     } else if (args[0] == "checkpoint" && args[1] == "supportsCheckpoint" && args.size() == 2) {
         bool supported = false;
         checkStatus(args, vold->supportsCheckpoint(&supported));