Merge "Populate the dm table of the early userdata device."
diff --git a/Android.bp b/Android.bp
index 69907b6..1ccfc09 100644
--- a/Android.bp
+++ b/Android.bp
@@ -168,7 +168,7 @@
         "libkeymint_support",
     ],
     whole_static_libs: [
-        "com.android.sysprop.apex",
+        "libcom.android.sysprop.apex",
         "libc++fs",
     ],
 }
@@ -190,7 +190,7 @@
     required: [
         "mke2fs",
         "vold_prepare_subdirs",
-        "fuse_media.o",
+        "fuseMedia.o",
     ],
 
     shared_libs: [
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index 5bc55d0..f871a32 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -417,7 +417,11 @@
         userid_t user_id = std::stoi(entry->d_name);
         auto key_path = de_dir + "/" + entry->d_name;
         KeyBuffer de_key;
-        if (!retrieveKey(key_path, kEmptyAuthentication, &de_key)) return false;
+        if (!retrieveKey(key_path, kEmptyAuthentication, &de_key)) {
+            // This is probably a partially removed user, so ignore
+            if (user_id != 0) continue;
+            return false;
+        }
         EncryptionPolicy de_policy;
         if (!install_storage_key(DATA_MNT_POINT, options, de_key, &de_policy)) return false;
         auto ret = s_de_policies.insert({user_id, de_policy});
diff --git a/IdleMaint.cpp b/IdleMaint.cpp
index d0a3a4c..0c6b115 100644
--- a/IdleMaint.cpp
+++ b/IdleMaint.cpp
@@ -390,28 +390,6 @@
 }
 
 static void runDevGc(void) {
-    auto aidl_service_name = AStorage::descriptor + "/default"s;
-    if (AServiceManager_isDeclared(aidl_service_name.c_str())) {
-        ndk::SpAIBinder binder(AServiceManager_waitForService(aidl_service_name.c_str()));
-        if (binder.get() != nullptr) {
-            std::shared_ptr<AStorage> aidl_service = AStorage::fromBinder(binder);
-            if (aidl_service != nullptr) {
-                runDevGcOnHal<IDL::AIDL>(aidl_service, ndk::SharedRefBase::make<AGcCallbackImpl>(),
-                                         &ndk::ScopedAStatus::getDescription);
-                return;
-            }
-        }
-        LOG(WARNING) << "Device declares " << aidl_service_name
-                     << " but it is not running, skip dev GC on AIDL HAL";
-        return;
-    }
-    auto hidl_service = HStorage::getService();
-    if (hidl_service != nullptr) {
-        runDevGcOnHal<IDL::HIDL>(hidl_service, sp<HGcCallbackImpl>(new HGcCallbackImpl()),
-                                 &Return<void>::description);
-        return;
-    }
-    // fallback to legacy code path
     runDevGcFstab();
 }
 
diff --git a/KeyBuffer.h b/KeyBuffer.h
index a68311f..4468220 100644
--- a/KeyBuffer.h
+++ b/KeyBuffer.h
@@ -17,32 +17,18 @@
 #ifndef ANDROID_VOLD_KEYBUFFER_H
 #define ANDROID_VOLD_KEYBUFFER_H
 
-#include <cstring>
+#include <string.h>
 #include <memory>
 #include <vector>
 
 namespace android {
 namespace vold {
 
-/**
- * Variant of memset() that should never be optimized away. Borrowed from keymaster code.
- */
-#ifdef __clang__
-#define OPTNONE __attribute__((optnone))
-#else  // not __clang__
-#define OPTNONE __attribute__((optimize("O0")))
-#endif  // not __clang__
-inline OPTNONE void* memset_s(void* s, int c, size_t n) {
-    if (!s) return s;
-    return memset(s, c, n);
-}
-#undef OPTNONE
-
 // Allocator that delegates useful work to standard one but zeroes data before deallocating.
 class ZeroingAllocator : public std::allocator<char> {
   public:
     void deallocate(pointer p, size_type n) {
-        memset_s(p, 0, n);
+        memset_explicit(p, 0, n);
         std::allocator<char>::deallocate(p, n);
     }
 };
diff --git a/KeyStorage.cpp b/KeyStorage.cpp
index 3ede67e..55c1709 100644
--- a/KeyStorage.cpp
+++ b/KeyStorage.cpp
@@ -57,16 +57,14 @@
 static const char* kCurrentVersion = "1";
 static const char* kRmPath = "/system/bin/rm";
 static const char* kSecdiscardPath = "/system/bin/secdiscard";
-static const char* kStretch_none = "none";
-static const char* kStretch_nopassword = "nopassword";
 static const char* kHashPrefix_secdiscardable = "Android secdiscardable SHA512";
 static const char* kHashPrefix_keygen = "Android key wrapping key generation SHA512";
 static const char* kFn_encrypted_key = "encrypted_key";
 static const char* kFn_keymaster_key_blob = "keymaster_key_blob";
 static const char* kFn_keymaster_key_blob_upgraded = "keymaster_key_blob_upgraded";
 static const char* kFn_secdiscardable = "secdiscardable";
-static const char* kFn_stretching = "stretching";
 static const char* kFn_version = "version";
+// Note: old key directories may contain a file named "stretching".
 
 namespace {
 
@@ -117,9 +115,13 @@
     SHA512_Final(reinterpret_cast<uint8_t*>(&(*res)[0]), &c);
 }
 
-// Generates a keystore key, using rollback resistance if supported.
-static bool generateKeystoreKey(Keystore& keystore, const km::AuthorizationSetBuilder& paramBuilder,
-                                std::string* key) {
+static bool generateKeyStorageKey(Keystore& keystore, const std::string& appId, std::string* key) {
+    auto paramBuilder = km::AuthorizationSetBuilder()
+                                .AesEncryptionKey(AES_KEY_BYTES * 8)
+                                .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
+                                .Authorization(km::TAG_APPLICATION_ID, appId)
+                                .Authorization(km::TAG_NO_AUTH_REQUIRED);
+    LOG(DEBUG) << "Generating \"key storage\" key";
     auto paramsWithRollback = paramBuilder;
     paramsWithRollback.Authorization(km::TAG_ROLLBACK_RESISTANCE);
 
@@ -132,23 +134,13 @@
     return true;
 }
 
-static bool generateKeyStorageKey(Keystore& keystore, const std::string& appId, std::string* key) {
-    auto paramBuilder = km::AuthorizationSetBuilder()
-                                .AesEncryptionKey(AES_KEY_BYTES * 8)
-                                .GcmModeMinMacLen(GCM_MAC_BYTES * 8)
-                                .Authorization(km::TAG_APPLICATION_ID, appId)
-                                .Authorization(km::TAG_NO_AUTH_REQUIRED);
-    LOG(DEBUG) << "Generating \"key storage\" key";
-    return generateKeystoreKey(keystore, paramBuilder, key);
-}
-
 bool generateWrappedStorageKey(KeyBuffer* key) {
     Keystore keystore;
     if (!keystore) return false;
     std::string key_temp;
     auto paramBuilder = km::AuthorizationSetBuilder().AesEncryptionKey(AES_KEY_BYTES * 8);
     paramBuilder.Authorization(km::TAG_STORAGE_KEY);
-    if (!generateKeystoreKey(keystore, paramBuilder, &key_temp)) return false;
+    if (!keystore.generateKey(paramBuilder, &key_temp)) return false;
     *key = KeyBuffer(key_temp.size());
     memcpy(reinterpret_cast<void*>(key->data()), key_temp.c_str(), key->size());
     return true;
@@ -197,9 +189,13 @@
 }
 
 bool readSecdiscardable(const std::string& filename, std::string* hash) {
-    std::string secdiscardable;
-    if (!readFileToString(filename, &secdiscardable)) return false;
-    hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+    if (pathExists(filename)) {
+        std::string secdiscardable;
+        if (!readFileToString(filename, &secdiscardable)) return false;
+        hashWithPrefix(kHashPrefix_secdiscardable, secdiscardable, hash);
+    } else {
+        *hash = "";
+    }
     return true;
 }
 
@@ -414,36 +410,9 @@
     return true;
 }
 
-static std::string getStretching(const KeyAuthentication& auth) {
-    if (auth.usesKeystore()) {
-        return kStretch_nopassword;
-    } else {
-        return kStretch_none;
-    }
-}
-
-static bool stretchSecret(const std::string& stretching, const std::string& secret,
-                          std::string* stretched) {
-    if (stretching == kStretch_nopassword) {
-        if (!secret.empty()) {
-            LOG(WARNING) << "Password present but stretching is nopassword";
-            // Continue anyway
-        }
-        stretched->clear();
-    } else if (stretching == kStretch_none) {
-        *stretched = secret;
-    } else {
-        LOG(ERROR) << "Unknown stretching type: " << stretching;
-        return false;
-    }
-    return true;
-}
-
-static bool generateAppId(const KeyAuthentication& auth, const std::string& stretching,
-                          const std::string& secdiscardable_hash, std::string* appId) {
-    std::string stretched;
-    if (!stretchSecret(stretching, auth.secret, &stretched)) return false;
-    *appId = secdiscardable_hash + stretched;
+static std::string generateAppId(const KeyAuthentication& auth,
+                                 const std::string& secdiscardable_hash) {
+    std::string appId = secdiscardable_hash + auth.secret;
 
     const std::lock_guard<std::mutex> scope_lock(storage_binding_info.guard);
     switch (storage_binding_info.state) {
@@ -451,14 +420,13 @@
             storage_binding_info.state = StorageBindingInfo::State::NOT_USED;
             break;
         case StorageBindingInfo::State::IN_USE:
-            appId->append(storage_binding_info.seed.begin(), storage_binding_info.seed.end());
+            appId.append(storage_binding_info.seed.begin(), storage_binding_info.seed.end());
             break;
         case StorageBindingInfo::State::NOT_USED:
             // noop
             break;
     }
-
-    return true;
+    return appId;
 }
 
 static void logOpensslError() {
@@ -569,9 +537,12 @@
 
 // Creates a directory at the given path |dir| and stores |key| in it, in such a
 // way that it can only be retrieved via Keystore (if no secret is given in
-// |auth|) or with the given secret (if a secret is given in |auth|), and can be
-// securely deleted.  If a storage binding seed has been set, then the storage
-// binding seed will be required to retrieve the key as well.
+// |auth|) or with the given secret (if a secret is given in |auth|).  In the
+// former case, an attempt is made to make the key securely deletable.  In the
+// latter case, secure deletion is expected to be handled at a higher level.
+//
+// If a storage binding seed has been set, then the storage binding seed will be
+// required to retrieve the key as well.
 static bool storeKey(const std::string& dir, const KeyAuthentication& auth, const KeyBuffer& key) {
     if (TEMP_FAILURE_RETRY(mkdir(dir.c_str(), 0700)) == -1) {
         PLOG(ERROR) << "key mkdir " << dir;
@@ -579,11 +550,10 @@
     }
     if (!writeStringToFile(kCurrentVersion, dir + "/" + kFn_version)) return false;
     std::string secdiscardable_hash;
-    if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
-    std::string stretching = getStretching(auth);
-    if (!writeStringToFile(stretching, dir + "/" + kFn_stretching)) return false;
-    std::string appId;
-    if (!generateAppId(auth, stretching, secdiscardable_hash, &appId)) return false;
+    if (auth.usesKeystore() &&
+        !createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash))
+        return false;
+    std::string appId = generateAppId(auth, secdiscardable_hash);
     std::string encryptedKey;
     if (auth.usesKeystore()) {
         Keystore keystore;
@@ -635,10 +605,7 @@
     }
     std::string secdiscardable_hash;
     if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;
-    std::string stretching;
-    if (!readFileToString(dir + "/" + kFn_stretching, &stretching)) return false;
-    std::string appId;
-    if (!generateAppId(auth, stretching, secdiscardable_hash, &appId)) return false;
+    std::string appId = generateAppId(auth, secdiscardable_hash);
     std::string encryptedMessage;
     if (!readFileToString(dir + "/" + kFn_encrypted_key, &encryptedMessage)) return false;
     if (auth.usesKeystore()) {
diff --git a/Keystore.cpp b/Keystore.cpp
index d993b0d..6040f2d 100644
--- a/Keystore.cpp
+++ b/Keystore.cpp
@@ -48,7 +48,7 @@
 }
 
 static void zeroize_vector(std::vector<uint8_t>& vec) {
-    memset_s(vec.data(), 0, vec.size());
+    memset_explicit(vec.data(), 0, vec.size());
 }
 
 static bool logKeystore2ExceptionIfPresent(::ndk::ScopedAStatus& rc, const std::string& func_name) {
diff --git a/Utils.cpp b/Utils.cpp
index 709853c..23e8867 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -1448,7 +1448,10 @@
 status_t AbortFuseConnections() {
     namespace fs = std::filesystem;
 
-    for (const auto& itEntry : fs::directory_iterator("/sys/fs/fuse/connections")) {
+    static constexpr const char* kFuseConnections = "/sys/fs/fuse/connections";
+
+    std::error_code ec;
+    for (const auto& itEntry : fs::directory_iterator(kFuseConnections, ec)) {
         std::string fsPath = itEntry.path().string() + "/filesystem";
         std::string fs;
 
@@ -1468,6 +1471,11 @@
         }
     }
 
+    if (ec) {
+        LOG(WARNING) << "Failed to iterate through " << kFuseConnections << ": "  << ec.message();
+        return -ec.value();
+    }
+
     return OK;
 }
 
@@ -1763,5 +1771,15 @@
     return {std::move(fd), std::move(linkPath)};
 }
 
+bool IsFuseBpfEnabled() {
+    std::string bpf_override = android::base::GetProperty("persist.sys.fuse.bpf.override", "");
+    if (bpf_override == "true") {
+        return true;
+    } else if (bpf_override == "false") {
+        return false;
+    }
+    return base::GetBoolProperty("ro.fuse.bpf.enabled", false);
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/Utils.h b/Utils.h
index 8f60ff6..fbd0f30 100644
--- a/Utils.h
+++ b/Utils.h
@@ -37,7 +37,6 @@
 
 static const char* kVoldAppDataIsolationEnabled = "persist.sys.vold_app_data_isolation_enabled";
 static const char* kExternalStorageSdcardfs = "external_storage.sdcardfs.enabled";
-static const char* kFuseBpfEnabled = "persist.sys.fuse.bpf.override";
 
 static constexpr std::chrono::seconds kUntrustedFsckSleepTime(45);
 
@@ -208,6 +207,8 @@
 
 status_t PrepareAndroidDirs(const std::string& volumeRoot);
 
+bool IsFuseBpfEnabled();
+
 // Open a given directory as an FD, and return that and the corresponding procfs virtual
 // symlink path that can be used in any API that accepts a path string. Path stays valid until
 // the directory FD is closed.
diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp
index ea2c98c..601323f 100644
--- a/VoldNativeService.cpp
+++ b/VoldNativeService.cpp
@@ -182,11 +182,13 @@
     return translate(VolumeManager::Instance()->abortFuse());
 }
 
-binder::Status VoldNativeService::onUserAdded(int32_t userId, int32_t userSerial) {
+binder::Status VoldNativeService::onUserAdded(int32_t userId, int32_t userSerial,
+                                              int32_t sharesStorageWithUserId) {
     ENFORCE_SYSTEM_OR_ROOT;
     ACQUIRE_LOCK;
 
-    return translate(VolumeManager::Instance()->onUserAdded(userId, userSerial));
+    return translate(
+            VolumeManager::Instance()->onUserAdded(userId, userSerial, sharesStorageWithUserId));
 }
 
 binder::Status VoldNativeService::onUserRemoved(int32_t userId) {
diff --git a/VoldNativeService.h b/VoldNativeService.h
index 37a988b..12a93f5 100644
--- a/VoldNativeService.h
+++ b/VoldNativeService.h
@@ -38,7 +38,7 @@
     binder::Status shutdown();
     binder::Status abortFuse();
 
-    binder::Status onUserAdded(int32_t userId, int32_t userSerial);
+    binder::Status onUserAdded(int32_t userId, int32_t userSerial, int32_t sharesStorageWithUserId);
     binder::Status onUserRemoved(int32_t userId);
     binder::Status onUserStarted(int32_t userId);
     binder::Status onUserStopped(int32_t userId);
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index 5cef239..e29b920 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -81,6 +81,7 @@
 using android::vold::DeleteDirContents;
 using android::vold::DeleteDirContentsAndDir;
 using android::vold::EnsureDirExists;
+using android::vold::GetFuseMountPathForUser;
 using android::vold::IsFilesystemSupported;
 using android::vold::IsSdcardfsUsed;
 using android::vold::IsVirtioBlkDevice;
@@ -424,10 +425,21 @@
     }
 }
 
-int VolumeManager::onUserAdded(userid_t userId, int userSerialNumber) {
+userid_t VolumeManager::getSharedStorageUser(userid_t userId) {
+    if (mSharedStorageUser.find(userId) == mSharedStorageUser.end()) {
+        return USER_UNKNOWN;
+    }
+    return mSharedStorageUser.at(userId);
+}
+
+int VolumeManager::onUserAdded(userid_t userId, int userSerialNumber,
+                               userid_t sharesStorageWithUserId) {
     LOG(INFO) << "onUserAdded: " << userId;
 
     mAddedUsers[userId] = userSerialNumber;
+    if (sharesStorageWithUserId != USER_UNKNOWN) {
+        mSharedStorageUser[userId] = sharesStorageWithUserId;
+    }
     return 0;
 }
 
@@ -436,6 +448,7 @@
 
     onUserStopped(userId);
     mAddedUsers.erase(userId);
+    mSharedStorageUser.erase(userId);
     return 0;
 }
 
@@ -914,6 +927,7 @@
     updateVirtualDisk();
     mAddedUsers.clear();
     mStartedUsers.clear();
+    mSharedStorageUser.clear();
 
     // Abort all FUSE connections to avoid deadlocks if the FUSE daemon was killed
     // with FUSE fds open.
diff --git a/VolumeManager.h b/VolumeManager.h
index a8117c9..943a144 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -104,9 +104,11 @@
 
     const std::set<userid_t>& getStartedUsers() const { return mStartedUsers; }
 
+    userid_t getSharedStorageUser(userid_t userId);
+
     int forgetPartition(const std::string& partGuid, const std::string& fsUuid);
 
-    int onUserAdded(userid_t userId, int userSerialNumber);
+    int onUserAdded(userid_t userId, int userSerialNumber, userid_t cloneParentUserId);
     int onUserRemoved(userid_t userId);
     int onUserStarted(userid_t userId);
     int onUserStopped(userid_t userId);
@@ -225,6 +227,8 @@
     std::list<std::shared_ptr<android::vold::VolumeBase>> mInternalEmulatedVolumes;
 
     std::unordered_map<userid_t, int> mAddedUsers;
+    // Map of users to a user with which they can share storage (eg clone profiles)
+    std::unordered_map<userid_t, userid_t> mSharedStorageUser;
     // This needs to be a regular set because we care about the ordering here;
     // user 0 should always go first, because it is responsible for sdcardfs.
     std::set<userid_t> mStartedUsers;
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 77478d9..c798959 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -30,7 +30,7 @@
     void reset();
     void shutdown();
 
-    void onUserAdded(int userId, int userSerial);
+    void onUserAdded(int userId, int userSerial, int sharesStorageWithUserId);
     void onUserRemoved(int userId);
     void onUserStarted(int userId);
     void onUserStopped(int userId);
diff --git a/cryptfs.cpp b/cryptfs.cpp
index 5213c18..be8ba3c 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -95,37 +95,6 @@
 }
 
 /*
- * If the ro.crypto.fde_sector_size system property is set, append the
- * parameters to make dm-crypt use the specified crypto sector size and round
- * the crypto device size down to a crypto sector boundary.
- */
-static int add_sector_size_param(DmTargetCrypt* target, uint64_t* nr_sec) {
-    constexpr char DM_CRYPT_SECTOR_SIZE[] = "ro.crypto.fde_sector_size";
-    char value[PROPERTY_VALUE_MAX];
-
-    if (property_get(DM_CRYPT_SECTOR_SIZE, value, "") > 0) {
-        unsigned int sector_size;
-
-        if (!ParseUint(value, &sector_size) || sector_size < 512 || sector_size > 4096 ||
-            (sector_size & (sector_size - 1)) != 0) {
-            SLOGE("Invalid value for %s: %s.  Must be >= 512, <= 4096, and a power of 2\n",
-                  DM_CRYPT_SECTOR_SIZE, value);
-            return -1;
-        }
-
-        target->SetSectorSize(sector_size);
-
-        // With this option, IVs will match the sector numbering, instead
-        // of being hard-coded to being based on 512-byte sectors.
-        target->SetIvLargeSectors();
-
-        // Round the crypto device size down to a crypto sector boundary.
-        *nr_sec &= ~((sector_size / 512) - 1);
-    }
-    return 0;
-}
-
-/*
  * Called by vold when it's asked to mount an encrypted external
  * storage volume. The incoming partition has no crypto header/footer,
  * as any metadata is been stored in a separate, small partition.  We
@@ -145,8 +114,25 @@
         return -1;
     }
 
-    auto& dm = DeviceMapper::Instance();
+    constexpr char DM_CRYPT_SECTOR_SIZE[] = "ro.crypto.fde_sector_size";
+    char value[PROPERTY_VALUE_MAX];
+    unsigned int sector_size = 0;
 
+    if (property_get(DM_CRYPT_SECTOR_SIZE, value, "") > 0) {
+        if (!ParseUint(value, &sector_size) || sector_size < 512 || sector_size > 4096 ||
+            (sector_size & (sector_size - 1)) != 0) {
+            SLOGE("Invalid value for %s: %s.  Must be >= 512, <= 4096, and a power of 2\n",
+                  DM_CRYPT_SECTOR_SIZE, value);
+            return -1;
+        }
+    }
+
+    // Round the crypto device size down to a crypto sector boundary.
+    if (sector_size > 0) {
+        nr_sec &= ~((sector_size / 512) - 1);
+    }
+
+    auto& dm = DeviceMapper::Instance();
     // We need two ASCII characters to represent each byte, and need space for
     // the '\0' terminator.
     char key_ascii[MAX_KEY_LEN * 2 + 1];
@@ -160,9 +146,13 @@
         android::base::GetBoolProperty("ro.crypto.allow_encrypt_override", false)) {
         target->AllowEncryptOverride();
     }
-    if (add_sector_size_param(target.get(), &nr_sec)) {
-        SLOGE("Error processing dm-crypt sector size param\n");
-        return -1;
+
+    // Append the parameters to make dm-crypt use the specified crypto sector size.
+    if (sector_size > 0) {
+        target->SetSectorSize(sector_size);
+        // With this option, IVs will match the sector numbering, instead
+        // of being hard-coded to being based on 512-byte sectors.
+        target->SetIvLargeSectors();
     }
 
     DmTable table;
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 7c8a4e0..f99a369 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -18,6 +18,7 @@
 
 #include "AppFuseUtil.h"
 #include "Utils.h"
+#include "VolumeBase.h"
 #include "VolumeManager.h"
 
 #include <android-base/logging.h>
@@ -49,7 +50,7 @@
     mRawPath = rawPath;
     mLabel = "emulated";
     mFuseMounted = false;
-    mFuseBpfEnabled = base::GetBoolProperty(kFuseBpfEnabled, false);
+    mFuseBpfEnabled = IsFuseBpfEnabled();
     mUseSdcardFs = IsSdcardfsUsed();
     mAppDataIsolationEnabled = base::GetBoolProperty(kVoldAppDataIsolationEnabled, false);
 }
@@ -61,14 +62,14 @@
     mRawPath = rawPath;
     mLabel = fsUuid;
     mFuseMounted = false;
-    mFuseBpfEnabled = base::GetBoolProperty(kFuseBpfEnabled, false);
+    mFuseBpfEnabled = IsFuseBpfEnabled();
     mUseSdcardFs = IsSdcardfsUsed();
     mAppDataIsolationEnabled = base::GetBoolProperty(kVoldAppDataIsolationEnabled, false);
 }
 
 EmulatedVolume::~EmulatedVolume() {}
 
-std::string EmulatedVolume::getLabel() {
+std::string EmulatedVolume::getLabel() const {
     // We could have migrated storage to an adopted private volume, so always
     // call primary storage "emulated" to avoid media rescans.
     if (getMountFlags() & MountFlags::kPrimary) {
@@ -91,6 +92,29 @@
     return OK;
 }
 
+// Bind mounts the volume 'volume' onto this volume.
+status_t EmulatedVolume::bindMountVolume(const EmulatedVolume& volume,
+                                         std::list<std::string>& pathsToUnmount) {
+    int myUserId = getMountUserId();
+    int volumeUserId = volume.getMountUserId();
+    std::string label = volume.getLabel();
+
+    // eg /mnt/user/10/emulated/10
+    std::string srcUserPath = GetFuseMountPathForUser(volumeUserId, label);
+    std::string srcPath = StringPrintf("%s/%d", srcUserPath.c_str(), volumeUserId);
+    // eg /mnt/user/0/emulated/10
+    std::string dstUserPath = GetFuseMountPathForUser(myUserId, label);
+    std::string dstPath = StringPrintf("%s/%d", dstUserPath.c_str(), volumeUserId);
+
+    auto status = doFuseBindMount(srcPath, dstPath, pathsToUnmount);
+    if (status == OK) {
+        // Store the mount path, so we can unmount it when this volume goes away
+        mSharedStorageMountPath = dstPath;
+    }
+
+    return status;
+}
+
 status_t EmulatedVolume::mountFuseBindMounts() {
     std::string androidSource;
     std::string label = getLabel();
@@ -152,6 +176,59 @@
         }
     }
 
+    // For users that share their volume with another user (eg a clone
+    // profile), the current mount setup can cause page cache inconsistency
+    // issues.  Let's say this is user 10, and the user it shares storage with
+    // is user 0.
+    // Then:
+    // * The FUSE daemon for user 0 serves /mnt/user/0
+    // * The FUSE daemon for user 10 serves /mnt/user/10
+    // The emulated volume for user 10 would be located at two paths:
+    // /mnt/user/0/emulated/10
+    // /mnt/user/10/emulated/10
+    // Since these paths refer to the same files but are served by different FUSE
+    // daemons, this can result in page cache inconsistency issues. To prevent this,
+    // bind mount the relevant paths for the involved users:
+    // 1. /mnt/user/10/emulated/10 =B=> /mnt/user/0/emulated/10
+    // 2. /mnt/user/0/emulated/0 =B=> /mnt/user/10/emulated/0
+    //
+    // This will ensure that any access to the volume for a specific user always
+    // goes through a single FUSE daemon.
+    userid_t sharedStorageUserId = VolumeManager::Instance()->getSharedStorageUser(userId);
+    if (sharedStorageUserId != USER_UNKNOWN) {
+        auto filter_fn = [&](const VolumeBase& vol) {
+            if (vol.getState() != VolumeBase::State::kMounted) {
+                // The volume must be mounted
+                return false;
+            }
+            if (vol.getType() != VolumeBase::Type::kEmulated) {
+                return false;
+            }
+            if (vol.getMountUserId() != sharedStorageUserId) {
+                return false;
+            }
+            if ((vol.getMountFlags() & MountFlags::kPrimary) == 0) {
+                // We only care about the primary emulated volume, so not a private
+                // volume with an emulated volume stacked on top.
+                return false;
+            }
+            return true;
+        };
+        auto vol = VolumeManager::Instance()->findVolumeWithFilter(filter_fn);
+        if (vol != nullptr) {
+            auto sharedVol = static_cast<EmulatedVolume*>(vol.get());
+            // Bind mount this volume in the other user's primary volume
+            status = sharedVol->bindMountVolume(*this, pathsToUnmount);
+            if (status != OK) {
+                return status;
+            }
+            // And vice-versa
+            status = bindMountVolume(*sharedVol, pathsToUnmount);
+            if (status != OK) {
+                return status;
+            }
+        }
+    }
     unmount_guard.Disable();
     return OK;
 }
@@ -160,6 +237,14 @@
     std::string label = getLabel();
     int userId = getMountUserId();
 
+    if (!mSharedStorageMountPath.empty()) {
+        LOG(INFO) << "Unmounting " << mSharedStorageMountPath;
+        auto status = UnmountTree(mSharedStorageMountPath);
+        if (status != OK) {
+            LOG(ERROR) << "Failed to unmount " << mSharedStorageMountPath;
+        }
+        mSharedStorageMountPath = "";
+    }
     if (mUseSdcardFs || mAppDataIsolationEnabled) {
         std::string installerTarget(
                 StringPrintf("/mnt/installer/%d/%s/%d/Android/obb", userId, label.c_str(), userId));
diff --git a/model/EmulatedVolume.h b/model/EmulatedVolume.h
index 0f39fbd..50fda14 100644
--- a/model/EmulatedVolume.h
+++ b/model/EmulatedVolume.h
@@ -52,7 +52,9 @@
     status_t mountFuseBindMounts();
     status_t unmountFuseBindMounts();
 
-    std::string getLabel();
+    status_t bindMountVolume(const EmulatedVolume& vol, std::list<std::string>& pathsToUnmount);
+
+    std::string getLabel() const;
     std::string mRawPath;
     std::string mLabel;
 
@@ -73,6 +75,9 @@
     /* Whether to use app data isolation is enabled tor this volume */
     bool mAppDataIsolationEnabled;
 
+    /* Location of bind mount for another profile that shares storage with us */
+    std::string mSharedStorageMountPath = "";
+
     DISALLOW_COPY_AND_ASSIGN(EmulatedVolume);
 };
 
diff --git a/secdiscard.cpp b/secdiscard.cpp
index b91f321..490e5a1 100644
--- a/secdiscard.cpp
+++ b/secdiscard.cpp
@@ -97,7 +97,7 @@
             TEMP_FAILURE_RETRY(open(target.c_str(), O_WRONLY | O_CLOEXEC, 0)));
         if (fd == -1) {
             LOG(ERROR) << "Secure discard open failed for: " << target;
-            return 0;
+            continue;
         }
         __u32 set = 1;
         ioctl(fd, F2FS_IOC_SET_PIN_FILE, &set);