Merge "Merge QP1A.190122.001" into q-keystone-qcom-dev
diff --git a/Checkpoint.cpp b/Checkpoint.cpp
index 28855e6..7586a6c 100644
--- a/Checkpoint.cpp
+++ b/Checkpoint.cpp
@@ -229,6 +229,7 @@
     uint32_t magic;
     uint32_t count;
     uint32_t sequence;
+    uint64_t sector0;
     struct log_entry entries[];
 } __attribute__((packed));
 
@@ -289,62 +290,119 @@
 
 }  // namespace
 
+static void read(std::fstream& device, std::vector<log_entry> const& logs, sector_t sector,
+                 char* buffer) {
+    for (auto l = logs.rbegin(); l != logs.rend(); l++)
+        if (sector >= l->source && (sector - l->source) * kSectorSize < l->size)
+            sector = sector - l->source + l->dest;
+
+    device.seekg(sector * kSectorSize);
+    device.read(buffer, kBlockSize);
+}
+
+static std::vector<char> read(std::fstream& device, std::vector<log_entry> const& logs,
+                              bool validating, sector_t sector, uint32_t size) {
+    if (!validating) {
+        std::vector<char> buffer(size);
+        device.seekg(sector * kSectorSize);
+        device.read(&buffer[0], size);
+        return buffer;
+    }
+
+    // Crude approach at first where we do this sector by sector and just scan
+    // the entire logs for remappings each time
+    std::vector<char> buffer(size);
+
+    for (uint32_t i = 0; i < size; i += kBlockSize, sector += kBlockSize / kSectorSize)
+        read(device, logs, sector, &buffer[i]);
+
+    return buffer;
+}
+
 Status cp_restoreCheckpoint(const std::string& blockDevice) {
-    LOG(INFO) << "Restoring checkpoint on " << blockDevice;
-    std::fstream device(blockDevice, std::ios::binary | std::ios::in | std::ios::out);
-    if (!device) {
-        PLOG(ERROR) << "Cannot open " << blockDevice;
-        return Status::fromExceptionCode(errno, ("Cannot open " + blockDevice).c_str());
-    }
-    alignas(alignof(log_sector)) char ls_buffer[kBlockSize];
-    device.read(ls_buffer, kBlockSize);
-    log_sector& ls = *reinterpret_cast<log_sector*>(ls_buffer);
-    if (ls.magic != kMagic) {
-        LOG(ERROR) << "No magic";
-        return Status::fromExceptionCode(EINVAL, "No magic");
-    }
+    bool validating = true;
+    std::string action = "Validating";
 
-    LOG(INFO) << "Restoring " << ls.sequence << " log sectors";
+    for (;;) {
+        std::vector<log_entry> logs;
+        Status status = Status::ok();
 
-    for (int sequence = ls.sequence; sequence >= 0; sequence--) {
-        device.seekg(0);
-        device.read(ls_buffer, kBlockSize);
-        ls = *reinterpret_cast<log_sector*>(ls_buffer);
+        LOG(INFO) << action << " checkpoint on " << blockDevice;
+        std::fstream device(blockDevice, std::ios::binary | std::ios::in | std::ios::out);
+        if (!device) {
+            PLOG(ERROR) << "Cannot open " << blockDevice;
+            return Status::fromExceptionCode(errno, ("Cannot open " + blockDevice).c_str());
+        }
+        auto buffer = read(device, logs, validating, 0, kBlockSize);
+        log_sector& ls = *reinterpret_cast<log_sector*>(&buffer[0]);
         if (ls.magic != kMagic) {
-            LOG(ERROR) << "No magic!";
+            LOG(ERROR) << "No magic";
             return Status::fromExceptionCode(EINVAL, "No magic");
         }
 
-        if ((int)ls.sequence != sequence) {
-            LOG(ERROR) << "Expecting log sector " << sequence << " but got " << ls.sequence;
-            return Status::fromExceptionCode(
-                EINVAL, ("Expecting log sector " + std::to_string(sequence) + " but got " +
-                         std::to_string(ls.sequence))
-                            .c_str());
-        }
+        LOG(INFO) << action << " " << ls.sequence << " log sectors";
 
-        LOG(INFO) << "Restoring from log sector " << ls.sequence;
-
-        for (log_entry* le = &ls.entries[ls.count - 1]; le >= ls.entries; --le) {
-            LOG(INFO) << "Restoring " << le->size << " bytes from sector " << le->dest << " to "
-                      << le->source << " with checksum " << std::hex << le->checksum;
-            std::vector<char> buffer(le->size);
-            device.seekg(le->dest * kSectorSize);
-            device.read(&buffer[0], le->size);
-
-            uint32_t checksum = le->source / (kBlockSize / kSectorSize);
-            for (size_t i = 0; i < le->size; i += kBlockSize) {
-                crc32(&buffer[i], kBlockSize, &checksum);
+        for (int sequence = ls.sequence; sequence >= 0 && status.isOk(); sequence--) {
+            auto buffer = read(device, logs, validating, 0, kBlockSize);
+            log_sector& ls = *reinterpret_cast<log_sector*>(&buffer[0]);
+            if (ls.magic != kMagic) {
+                LOG(ERROR) << "No magic!";
+                status = Status::fromExceptionCode(EINVAL, "No magic");
+                break;
             }
 
-            if (le->checksum && checksum != le->checksum) {
-                LOG(ERROR) << "Checksums don't match " << std::hex << checksum;
-                return Status::fromExceptionCode(EINVAL, "Checksums don't match");
+            if ((int)ls.sequence != sequence) {
+                LOG(ERROR) << "Expecting log sector " << sequence << " but got " << ls.sequence;
+                status = Status::fromExceptionCode(
+                    EINVAL, ("Expecting log sector " + std::to_string(sequence) + " but got " +
+                             std::to_string(ls.sequence))
+                                .c_str());
+                break;
             }
 
-            device.seekg(le->source * kSectorSize);
-            device.write(&buffer[0], le->size);
+            LOG(INFO) << action << " from log sector " << ls.sequence;
+
+            for (log_entry* le = &ls.entries[ls.count - 1]; le >= ls.entries; --le) {
+                LOG(INFO) << action << " " << le->size << " bytes from sector " << le->dest
+                          << " to " << le->source << " with checksum " << std::hex << le->checksum;
+                auto buffer = read(device, logs, validating, le->dest, le->size);
+                uint32_t checksum = le->source / (kBlockSize / kSectorSize);
+                for (size_t i = 0; i < le->size; i += kBlockSize) {
+                    crc32(&buffer[i], kBlockSize, &checksum);
+                }
+
+                if (le->checksum && checksum != le->checksum) {
+                    LOG(ERROR) << "Checksums don't match " << std::hex << checksum;
+                    status = Status::fromExceptionCode(EINVAL, "Checksums don't match");
+                    break;
+                }
+
+                logs.push_back(*le);
+
+                if (!validating) {
+                    device.seekg(le->source * kSectorSize);
+                    device.write(&buffer[0], le->size);
+                }
+            }
         }
+
+        if (!status.isOk()) {
+            if (!validating) {
+                LOG(ERROR) << "Checkpoint restore failed even though checkpoint validation passed";
+                return status;
+            }
+
+            LOG(WARNING) << "Checkpoint validation failed - attempting to roll forward";
+            auto buffer = read(device, logs, false, ls.sector0, kBlockSize);
+            device.seekg(0);
+            device.write(&buffer[0], kBlockSize);
+            return Status::ok();
+        }
+
+        if (!validating) break;
+
+        validating = false;
+        action = "Restoring";
     }
 
     return Status::ok();
diff --git a/FsCrypt.cpp b/FsCrypt.cpp
index f52e32f..9041838 100644
--- a/FsCrypt.cpp
+++ b/FsCrypt.cpp
@@ -176,27 +176,12 @@
     auto const current_path = get_ce_key_current_path(directory_path);
     if (to_fix != current_path) {
         LOG(DEBUG) << "Renaming " << to_fix << " to " << current_path;
-        android::base::unique_fd fd(TEMP_FAILURE_RETRY(
-            open(to_fix.c_str(), O_RDONLY | O_CLOEXEC)));
-        if (fd == -1) {
-            PLOG(ERROR) << "Failed to open " << to_fix;
-            return;
-        }
-        if (fsync(fd) == -1) {
-            if (errno == EROFS || errno == EINVAL) {
-                PLOG(WARNING) << "Skip fsync " << to_fix
-                              << " on a file system does not support synchronization";
-            } else {
-                PLOG(ERROR) << "Failed to fsync " << to_fix;
-                unlink(to_fix.c_str());
-                return;
-            }
-        }
         if (rename(to_fix.c_str(), current_path.c_str()) != 0) {
             PLOG(WARNING) << "Unable to rename " << to_fix << " to " << current_path;
             return;
         }
     }
+    android::vold::FsyncDirectory(directory_path);
 }
 
 static bool read_and_fixate_user_ce_key(userid_t user_id,
@@ -684,6 +669,7 @@
     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::FsyncDirectory(directory_path)) return false;
     return true;
 }
 
diff --git a/KeyStorage.cpp b/KeyStorage.cpp
index dffece5..36a7777 100644
--- a/KeyStorage.cpp
+++ b/KeyStorage.cpp
@@ -544,6 +544,7 @@
         if (!encryptWithoutKeymaster(appId, key, &encryptedKey)) return false;
     }
     if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;
+    if (!FsyncDirectory(dir)) return false;
     return true;
 }
 
diff --git a/Utils.cpp b/Utils.cpp
index 4ec2679..341c927 100644
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -753,9 +753,52 @@
     return android::base::GetBoolProperty("ro.kernel.qemu", false);
 }
 
-status_t UnmountTree(const std::string& prefix) {
-    if (umount2(prefix.c_str(), MNT_DETACH)) {
-        PLOG(ERROR) << "Failed to unmount " << prefix;
+static status_t findMountPointsWithPrefix(const std::string& prefix,
+                                          std::list<std::string>& mountPoints) {
+    // Add a trailing slash if the client didn't provide one so that we don't match /foo/barbaz
+    // when the prefix is /foo/bar
+    std::string prefixWithSlash(prefix);
+    if (prefix.back() != '/') {
+        android::base::StringAppendF(&prefixWithSlash, "/");
+    }
+
+    std::unique_ptr<FILE, int (*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
+    if (!mnts) {
+        PLOG(ERROR) << "Unable to open /proc/mounts";
+        return -errno;
+    }
+
+    // Some volumes can be stacked on each other, so force unmount in
+    // reverse order to give us the best chance of success.
+    struct mntent* mnt;  // getmntent returns a thread local, so it's safe.
+    while ((mnt = getmntent(mnts.get())) != nullptr) {
+        auto mountPoint = std::string(mnt->mnt_dir) + "/";
+        if (android::base::StartsWith(mountPoint, prefixWithSlash)) {
+            mountPoints.push_front(mountPoint);
+        }
+    }
+    return OK;
+}
+
+// Unmount all mountpoints that start with prefix. prefix itself doesn't need to be a mountpoint.
+status_t UnmountTreeWithPrefix(const std::string& prefix) {
+    std::list<std::string> toUnmount;
+    status_t result = findMountPointsWithPrefix(prefix, toUnmount);
+    if (result < 0) {
+        return result;
+    }
+    for (const auto& path : toUnmount) {
+        if (umount2(path.c_str(), MNT_DETACH)) {
+            PLOG(ERROR) << "Failed to unmount " << path;
+            result = -errno;
+        }
+    }
+    return result;
+}
+
+status_t UnmountTree(const std::string& mountPoint) {
+    if (umount2(mountPoint.c_str(), MNT_DETACH)) {
+        PLOG(ERROR) << "Failed to unmount " << mountPoint;
         return -errno;
     }
     return OK;
@@ -768,7 +811,7 @@
         return -errno;
     }
 
-    status_t result;
+    status_t result = OK;
     struct dirent* de;
     while ((de = readdir(dir))) {
         const char* name = de->d_name;
@@ -846,5 +889,23 @@
     return -1;
 }
 
+bool FsyncDirectory(const std::string& dirname) {
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname.c_str(), O_RDONLY | O_CLOEXEC)));
+    if (fd == -1) {
+        PLOG(ERROR) << "Failed to open " << dirname;
+        return false;
+    }
+    if (fsync(fd) == -1) {
+        if (errno == EROFS || errno == EINVAL) {
+            PLOG(WARNING) << "Skip fsync " << dirname
+                          << " on a file system does not support synchronization";
+        } else {
+            PLOG(ERROR) << "Failed to fsync " << dirname;
+            return false;
+        }
+    }
+    return true;
+}
+
 }  // namespace vold
 }  // namespace android
diff --git a/Utils.h b/Utils.h
index 68c82eb..e51ec1e 100644
--- a/Utils.h
+++ b/Utils.h
@@ -127,12 +127,15 @@
 /* Checks if Android is running in QEMU */
 bool IsRunningInEmulator();
 
-status_t UnmountTree(const std::string& prefix);
+status_t UnmountTreeWithPrefix(const std::string& prefix);
+status_t UnmountTree(const std::string& mountPoint);
 
 status_t DeleteDirContentsAndDir(const std::string& pathname);
 
 status_t WaitForFile(const char* filename, std::chrono::nanoseconds timeout);
 
+bool FsyncDirectory(const std::string& dirname);
+
 }  // namespace vold
 }  // namespace android
 
diff --git a/VolumeManager.cpp b/VolumeManager.cpp
index d113cf9..f600f64 100644
--- a/VolumeManager.cpp
+++ b/VolumeManager.cpp
@@ -79,6 +79,7 @@
 static const char* kPathVirtualDisk = "/data/misc/vold/virtual_disk";
 
 static const char* kIsolatedStorage = "persist.sys.isolated_storage";
+static const char* kIsolatedStorageSnapshot = "sys.isolated_storage_snapshot";
 static const char* kPropVirtualDisk = "persist.sys.virtual_disk";
 
 static const std::string kEmptyString("");
@@ -108,6 +109,10 @@
 
 VolumeManager::~VolumeManager() {}
 
+static bool hasIsolatedStorage() {
+    return GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false));
+}
+
 int VolumeManager::updateVirtualDisk() {
     ATRACE_NAME("VolumeManager::updateVirtualDisk");
     if (GetBoolProperty(kPropVirtualDisk, false)) {
@@ -420,8 +425,13 @@
         return -1;
     }
 
-    struct stat fullWriteSb;
-    if (TEMP_FAILURE_RETRY(stat("/mnt/runtime/write", &fullWriteSb)) == -1) {
+    struct stat mntFullSb;
+    struct stat mntWriteSb;
+    if (TEMP_FAILURE_RETRY(stat("/mnt/runtime/full", &mntFullSb)) == -1) {
+        PLOG(ERROR) << "Failed to stat /mnt/runtime/full";
+        return -1;
+    }
+    if (TEMP_FAILURE_RETRY(stat("/mnt/runtime/write", &mntWriteSb)) == -1) {
         PLOG(ERROR) << "Failed to stat /mnt/runtime/write";
         return -1;
     }
@@ -500,7 +510,8 @@
 
             int mountMode;
             if (remountMode == -1) {
-                mountMode = getMountModeForRunningProc(packagesForUid, userId, fullWriteSb);
+                mountMode =
+                    getMountModeForRunningProc(packagesForUid, userId, mntWriteSb, mntFullSb);
                 if (mountMode == -1) {
                     _exit(1);
                 }
@@ -520,6 +531,7 @@
                 }
             }
             if (mountMode == VoldNativeService::REMOUNT_MODE_FULL ||
+                mountMode == VoldNativeService::REMOUNT_MODE_LEGACY ||
                 mountMode == VoldNativeService::REMOUNT_MODE_NONE) {
                 // These mount modes are not going to change dynamically, so don't bother
                 // unmounting/remounting dirs.
@@ -573,7 +585,8 @@
 }
 
 int VolumeManager::getMountModeForRunningProc(const std::vector<std::string>& packagesForUid,
-                                              userid_t userId, struct stat& mntWriteStat) {
+                                              userid_t userId, struct stat& mntWriteStat,
+                                              struct stat& mntFullStat) {
     struct stat storageSb;
     if (TEMP_FAILURE_RETRY(stat("/storage", &storageSb)) == -1) {
         PLOG(ERROR) << "Failed to stat /storage";
@@ -581,9 +594,11 @@
     }
 
     // Some packages have access to full external storage, identify processes belonging
-    // to those packages by comparing inode no.s of /mnt/runtime/write and /storage
-    if (storageSb.st_dev == mntWriteStat.st_dev && storageSb.st_ino == mntWriteStat.st_ino) {
+    // to those packages by comparing inode no.s of /mnt/runtime/full and /storage
+    if (storageSb.st_dev == mntFullStat.st_dev && storageSb.st_ino == mntFullStat.st_ino) {
         return VoldNativeService::REMOUNT_MODE_FULL;
+    } else if (storageSb.st_dev == mntWriteStat.st_dev && storageSb.st_ino == mntWriteStat.st_ino) {
+        return VoldNativeService::REMOUNT_MODE_LEGACY;
     }
 
     std::string obbMountFile =
@@ -843,7 +858,7 @@
     if (mPrimary) {
         linkPrimary(userId);
     }
-    if (GetBoolProperty(kIsolatedStorage, false)) {
+    if (hasIsolatedStorage()) {
         std::vector<std::string> visibleVolLabels;
         for (auto& volId : mVisibleVolumeIds) {
             auto vol = findVolume(volId);
@@ -863,11 +878,11 @@
     LOG(VERBOSE) << "onUserStopped: " << userId;
     mStartedUsers.erase(userId);
 
-    if (GetBoolProperty(kIsolatedStorage, false)) {
+    if (hasIsolatedStorage()) {
         mUserPackages.erase(userId);
         std::string mntTargetDir = StringPrintf("/mnt/user/%d", userId);
-        if (android::vold::UnmountTree(mntTargetDir) != 0) {
-            PLOG(ERROR) << "unmountTree on " << mntTargetDir << " failed";
+        if (android::vold::UnmountTreeWithPrefix(mntTargetDir) < 0) {
+            PLOG(ERROR) << "UnmountTreeWithPrefix on " << mntTargetDir << " failed";
             return -errno;
         }
         if (android::vold::DeleteDirContentsAndDir(mntTargetDir) < 0) {
@@ -897,7 +912,7 @@
 
 int VolumeManager::prepareSandboxForApp(const std::string& packageName, appid_t appId,
                                         const std::string& sandboxId, userid_t userId) {
-    if (!GetBoolProperty(kIsolatedStorage, false)) {
+    if (!hasIsolatedStorage()) {
         return 0;
     } else if (mStartedUsers.find(userId) == mStartedUsers.end()) {
         // User not started, no need to do anything now. Required bind mounts for the package will
@@ -923,7 +938,7 @@
 
 int VolumeManager::destroySandboxForApp(const std::string& packageName,
                                         const std::string& sandboxId, userid_t userId) {
-    if (!GetBoolProperty(kIsolatedStorage, false)) {
+    if (!hasIsolatedStorage()) {
         return 0;
     }
     LOG(VERBOSE) << "destroySandboxForApp: " << packageName << ", sandboxId=" << sandboxId
@@ -969,8 +984,8 @@
                  << ", volLabel=" << volLabel;
     std::string pkgSandboxTarget =
         StringPrintf("/mnt/user/%d/package/%s", userId, packageName.c_str());
-    if (android::vold::UnmountTree(pkgSandboxTarget)) {
-        PLOG(ERROR) << "UnmountTree failed on " << pkgSandboxTarget;
+    if (android::vold::UnmountTreeWithPrefix(pkgSandboxTarget) < 0) {
+        PLOG(ERROR) << "UnmountTreeWithPrefix failed on " << pkgSandboxTarget;
     }
 
     std::string sandboxDir = StringPrintf("/mnt/runtime/write/%s", volLabel.c_str());
@@ -1001,7 +1016,7 @@
 }
 
 int VolumeManager::onVolumeMounted(android::vold::VolumeBase* vol) {
-    if (!GetBoolProperty(kIsolatedStorage, false)) {
+    if (!hasIsolatedStorage()) {
         return 0;
     }
 
@@ -1036,7 +1051,7 @@
 }
 
 int VolumeManager::onVolumeUnmounted(android::vold::VolumeBase* vol) {
-    if (!GetBoolProperty(kIsolatedStorage, false)) {
+    if (!hasIsolatedStorage()) {
         return 0;
     }
 
@@ -1070,8 +1085,8 @@
     for (auto& packageName : packageNames) {
         std::string volSandboxRoot = StringPrintf("/mnt/user/%d/package/%s/%s", userId,
                                                   packageName.c_str(), vol->getLabel().c_str());
-        if (android::vold::UnmountTree(volSandboxRoot) != 0) {
-            PLOG(ERROR) << "unmountTree on " << volSandboxRoot << " failed";
+        if (android::vold::UnmountTreeWithPrefix(volSandboxRoot) < 0) {
+            PLOG(ERROR) << "UnmountTreeWithPrefix on " << volSandboxRoot << " failed";
             continue;
         }
         if (android::vold::DeleteDirContentsAndDir(volSandboxRoot) < 0) {
@@ -1084,7 +1099,7 @@
 }
 
 int VolumeManager::setPrimary(const std::shared_ptr<android::vold::VolumeBase>& vol) {
-    if (GetBoolProperty(kIsolatedStorage, false)) {
+    if (hasIsolatedStorage()) {
         return 0;
     }
     mPrimary = vol;
@@ -1095,7 +1110,7 @@
 }
 
 int VolumeManager::remountUid(uid_t uid, int32_t mountMode) {
-    if (!GetBoolProperty(kIsolatedStorage, false)) {
+    if (!hasIsolatedStorage()) {
         return remountUidLegacy(uid, mountMode);
     }
 
@@ -1283,7 +1298,7 @@
     mVisibleVolumeIds.clear();
 
     // For unmounting dirs under /mnt/user/<user-id>/package/<package-name>
-    android::vold::UnmountTree("/mnt/user/");
+    android::vold::UnmountTreeWithPrefix("/mnt/user/");
     return 0;
 }
 
diff --git a/VolumeManager.h b/VolumeManager.h
index 3a7bae4..7d299a1 100644
--- a/VolumeManager.h
+++ b/VolumeManager.h
@@ -174,7 +174,7 @@
     int destroySandboxForAppOnVol(const std::string& packageName, const std::string& sandboxId,
                                   userid_t userId, const std::string& volLabel);
     int getMountModeForRunningProc(const std::vector<std::string>& packagesForUid, userid_t userId,
-                                   struct stat& mntWriteStat);
+                                   struct stat& mntWriteStat, struct stat& mntFullStat);
 
     void handleDiskAdded(const std::shared_ptr<android::vold::Disk>& disk);
     void handleDiskChanged(dev_t device);
diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl
index 59e8795..b9181bd 100644
--- a/binder/android/os/IVold.aidl
+++ b/binder/android/os/IVold.aidl
@@ -152,8 +152,9 @@
     const int REMOUNT_MODE_DEFAULT = 1;
     const int REMOUNT_MODE_READ = 2;
     const int REMOUNT_MODE_WRITE = 3;
-    const int REMOUNT_MODE_INSTALLER = 4;
-    const int REMOUNT_MODE_FULL = 5;
+    const int REMOUNT_MODE_LEGACY = 4;
+    const int REMOUNT_MODE_INSTALLER = 5;
+    const int REMOUNT_MODE_FULL = 6;
 
     const int VOLUME_STATE_UNMOUNTED = 0;
     const int VOLUME_STATE_CHECKING = 1;
diff --git a/cryptfs.cpp b/cryptfs.cpp
index 43eca62..08bcaba 100644
--- a/cryptfs.cpp
+++ b/cryptfs.cpp
@@ -1366,6 +1366,7 @@
     char buffer[DM_CRYPT_BUF_SIZE];
     struct dm_ioctl* io;
     int retval = -1;
+    int err;
 
     if ((fd = open("/dev/device-mapper", O_RDWR | O_CLOEXEC)) < 0) {
         SLOGE("Cannot open device-mapper\n");
@@ -1375,8 +1376,9 @@
     io = (struct dm_ioctl*)buffer;
 
     ioctl_init(io, DM_CRYPT_BUF_SIZE, name, 0);
-    if (ioctl(fd, DM_DEV_REMOVE, io)) {
-        SLOGE("Cannot remove dm-crypt device\n");
+    err = ioctl(fd, DM_DEV_REMOVE, io);
+    if (err) {
+        SLOGE("Cannot remove dm-crypt device %s: %s\n", name, strerror(errno));
         goto errout;
     }
 
diff --git a/model/EmulatedVolume.cpp b/model/EmulatedVolume.cpp
index 8f46191..1d5546c 100644
--- a/model/EmulatedVolume.cpp
+++ b/model/EmulatedVolume.cpp
@@ -66,6 +66,7 @@
     mFuseDefault = StringPrintf("/mnt/runtime/default/%s", label.c_str());
     mFuseRead = StringPrintf("/mnt/runtime/read/%s", label.c_str());
     mFuseWrite = StringPrintf("/mnt/runtime/write/%s", label.c_str());
+    mFuseFull = StringPrintf("/mnt/runtime/full/%s", label.c_str());
 
     setInternalPath(mRawPath);
     setPath(StringPrintf("/storage/%s", label.c_str()));
@@ -73,12 +74,13 @@
 
     if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
         fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
-        fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+        fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
+        fs_prepare_dir(mFuseFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
         PLOG(ERROR) << getId() << " failed to create mount points";
         return -errno;
     }
 
-    dev_t before = GetDevice(mFuseWrite);
+    dev_t before = GetDevice(mFuseFull);
 
     if (!(mFusePid = fork())) {
         // clang-format off
@@ -106,7 +108,7 @@
     }
 
     nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
-    while (before == GetDevice(mFuseWrite)) {
+    while (before == GetDevice(mFuseFull)) {
         LOG(DEBUG) << "Waiting for FUSE to spin up...";
         usleep(50000);  // 50ms
 
@@ -132,14 +134,17 @@
     ForceUnmount(mFuseDefault);
     ForceUnmount(mFuseRead);
     ForceUnmount(mFuseWrite);
+    ForceUnmount(mFuseFull);
 
     rmdir(mFuseDefault.c_str());
     rmdir(mFuseRead.c_str());
     rmdir(mFuseWrite.c_str());
+    rmdir(mFuseFull.c_str());
 
     mFuseDefault.clear();
     mFuseRead.clear();
     mFuseWrite.clear();
+    mFuseFull.clear();
 
     return OK;
 }
diff --git a/model/EmulatedVolume.h b/model/EmulatedVolume.h
index f618c55..fddfe4e 100644
--- a/model/EmulatedVolume.h
+++ b/model/EmulatedVolume.h
@@ -52,6 +52,7 @@
     std::string mFuseDefault;
     std::string mFuseRead;
     std::string mFuseWrite;
+    std::string mFuseFull;
 
     /* PID of FUSE wrapper */
     pid_t mFusePid;
diff --git a/model/PublicVolume.cpp b/model/PublicVolume.cpp
index 77a156c..e751eca 100644
--- a/model/PublicVolume.cpp
+++ b/model/PublicVolume.cpp
@@ -123,6 +123,7 @@
     mFuseDefault = StringPrintf("/mnt/runtime/default/%s", stableName.c_str());
     mFuseRead = StringPrintf("/mnt/runtime/read/%s", stableName.c_str());
     mFuseWrite = StringPrintf("/mnt/runtime/write/%s", stableName.c_str());
+    mFuseFull = StringPrintf("/mnt/runtime/full/%s", stableName.c_str());
 
     setInternalPath(mRawPath);
     if (getMountFlags() & MountFlags::kVisible) {
@@ -161,12 +162,13 @@
 
     if (fs_prepare_dir(mFuseDefault.c_str(), 0700, AID_ROOT, AID_ROOT) ||
         fs_prepare_dir(mFuseRead.c_str(), 0700, AID_ROOT, AID_ROOT) ||
-        fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT)) {
+        fs_prepare_dir(mFuseWrite.c_str(), 0700, AID_ROOT, AID_ROOT) ||
+        fs_prepare_dir(mFuseFull.c_str(), 0700, AID_ROOT, AID_ROOT)) {
         PLOG(ERROR) << getId() << " failed to create FUSE mount points";
         return -errno;
     }
 
-    dev_t before = GetDevice(mFuseWrite);
+    dev_t before = GetDevice(mFuseFull);
 
     if (!(mFusePid = fork())) {
         if (getMountFlags() & MountFlags::kPrimary) {
@@ -183,36 +185,16 @@
                 PLOG(ERROR) << "Failed to exec";
             }
         } else {
-            // In Pre-Q, apps have full read access to secondary storage devices but only
-            // write access for their package specific directories. In Q, they only have access
-            // to their own sandboxes and they can write anywhere inside the sandbox. Instead of
-            // updating sdcardfs to allow packages writing into their own sandboxes, we could
-            // just allow them to write anywhere by passing "-w".
             // clang-format off
-            if (GetBoolProperty(kIsolatedStorage, false)) {
-                if (execl(kFusePath, kFusePath,
-                        "-u", "1023", // AID_MEDIA_RW
-                        "-g", "1023", // AID_MEDIA_RW
-                        "-U", std::to_string(getMountUserId()).c_str(),
-                        "-w",
-                        mRawPath.c_str(),
-                        stableName.c_str(),
-                        NULL)) {
-                    // clang-format on
-                    PLOG(ERROR) << "Failed to exec";
-                }
-            } else {
-                // clang-format off
-                if (execl(kFusePath, kFusePath,
-                        "-u", "1023", // AID_MEDIA_RW
-                        "-g", "1023", // AID_MEDIA_RW
-                        "-U", std::to_string(getMountUserId()).c_str(),
-                        mRawPath.c_str(),
-                        stableName.c_str(),
-                        NULL)) {
-                    // clang-format on
-                    PLOG(ERROR) << "Failed to exec";
-                }
+            if (execl(kFusePath, kFusePath,
+                    "-u", "1023", // AID_MEDIA_RW
+                    "-g", "1023", // AID_MEDIA_RW
+                    "-U", std::to_string(getMountUserId()).c_str(),
+                    mRawPath.c_str(),
+                    stableName.c_str(),
+                    NULL)) {
+                // clang-format on
+                PLOG(ERROR) << "Failed to exec";
             }
         }
 
@@ -226,7 +208,7 @@
     }
 
     nsecs_t start = systemTime(SYSTEM_TIME_BOOTTIME);
-    while (before == GetDevice(mFuseWrite)) {
+    while (before == GetDevice(mFuseFull)) {
         LOG(DEBUG) << "Waiting for FUSE to spin up...";
         usleep(50000);  // 50ms
 
@@ -255,16 +237,19 @@
     ForceUnmount(mFuseDefault);
     ForceUnmount(mFuseRead);
     ForceUnmount(mFuseWrite);
+    ForceUnmount(mFuseFull);
     ForceUnmount(mRawPath);
 
     rmdir(mFuseDefault.c_str());
     rmdir(mFuseRead.c_str());
     rmdir(mFuseWrite.c_str());
+    rmdir(mFuseFull.c_str());
     rmdir(mRawPath.c_str());
 
     mFuseDefault.clear();
     mFuseRead.clear();
     mFuseWrite.clear();
+    mFuseFull.clear();
     mRawPath.clear();
 
     return OK;
diff --git a/model/PublicVolume.h b/model/PublicVolume.h
index c918f52..2feccca 100644
--- a/model/PublicVolume.h
+++ b/model/PublicVolume.h
@@ -63,6 +63,7 @@
     std::string mFuseDefault;
     std::string mFuseRead;
     std::string mFuseWrite;
+    std::string mFuseFull;
 
     /* PID of FUSE wrapper */
     pid_t mFusePid;
diff --git a/vold_prepare_subdirs.cpp b/vold_prepare_subdirs.cpp
index b8e5cb6..1dd5e85 100644
--- a/vold_prepare_subdirs.cpp
+++ b/vold_prepare_subdirs.cpp
@@ -128,6 +128,7 @@
             auto misc_de_path = android::vold::BuildDataMiscDePath(user_id);
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/vold")) return false;
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/storaged")) return false;
+            if (!prepare_dir(sehandle, 0700, 0, 0, misc_de_path + "/rollback")) return false;
 
             auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id);
             if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM, vendor_de_path + "/fpdata")) {
@@ -142,6 +143,16 @@
             auto misc_ce_path = android::vold::BuildDataMiscCePath(user_id);
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/vold")) return false;
             if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/storaged")) return false;
+            if (!prepare_dir(sehandle, 0700, 0, 0, misc_ce_path + "/rollback")) return false;
+
+            auto system_ce_path = android::vold::BuildDataSystemCePath(user_id);
+            if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM, system_ce_path + "/backup")) {
+                return false;
+            }
+            if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM,
+                             system_ce_path + "/backup_stage")) {
+                return false;
+            }
         }
     }
     return true;