diff --git a/Android.bp b/Android.bp index 8fd29f0..1e45727 100644 --- a/Android.bp +++ b/Android.bp @@ -95,6 +95,7 @@ cc_library_static { srcs: [ "Benchmark.cpp", "CheckEncryption.cpp", + "Checkpoint.cpp", "Devmapper.cpp", "EncryptInplace.cpp", "Ext4Crypt.cpp", diff --git a/Checkpoint.cpp b/Checkpoint.cpp new file mode 100644 index 0000000..476b0ae --- /dev/null +++ b/Checkpoint.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "Checkpoint" +#include "Checkpoint.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace android { +namespace vold { + +static const std::string kMetadataCPFile = "/metadata/vold/checkpoint"; + +bool cp_startCheckpoint(int retry) { + if (retry < -1) return false; + std::string content = std::to_string(retry); + return android::base::WriteStringToFile(content, kMetadataCPFile); +} + +bool cp_commitChanges() { + struct fstab* fstab = fs_mgr_read_fstab_default(); + if (!fstab) return false; + + FILE* fp = setmntent("/proc/mounts", "r"); + mntent* mentry; + + if (fp == NULL) return false; + + while ((mentry = getmntent(fp)) != NULL) { + auto test = std::string(mentry->mnt_dir) + "/"; + for (int i = 0; i < fstab->num_entries; i++) { + if (!fs_mgr_is_checkpoint(&fstab->recs[i])) continue; + + if (!strcmp(fstab->recs[i].mount_point, mentry->mnt_dir) && + !strcmp(fstab->recs[i].fs_type, mentry->mnt_type)) { + if (!strcmp(fstab->recs[i].fs_type, "f2fs")) { + mount(mentry->mnt_fsname, mentry->mnt_dir, "none", + MS_REMOUNT | fstab->recs[i].flags, "checkpoint=enable"); + } + } + } + } + endmntent(fp); + + fs_mgr_free_fstab(fstab); + return android::base::RemoveFileIfExists(kMetadataCPFile); +} + +void cp_abortChanges() { + android_reboot(ANDROID_RB_RESTART2, 0, nullptr); +} + +bool cp_needRollback(const std::string& id) { + std::string content; + bool ret; + + ret = android::base::ReadFileToString(kMetadataCPFile, &content); + if (ret) return content == "0"; + return false; +} + +bool cp_needsCheckpoint(void) { + bool ret; + std::string content; + + ret = android::base::ReadFileToString(kMetadataCPFile, &content); + if (ret) return content != "0"; + return false; +} + +bool cp_prepareDriveForCheckpoint(const std::string& mountPoint) { + return false; +} + +bool cp_markBootAttempt() { + std::string oldContent, newContent; + int retry = 0; + if (!android::base::ReadFileToString(kMetadataCPFile, &oldContent)) return false; + + if (!android::base::ParseInt(oldContent, &retry)) return false; + if (retry > 0) retry--; + + newContent = std::to_string(retry); + return android::base::WriteStringToFile(newContent, kMetadataCPFile); +} + +} // namespace vold +} // namespace android diff --git a/Checkpoint.h b/Checkpoint.h new file mode 100644 index 0000000..8449956 --- /dev/null +++ b/Checkpoint.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _CHECKPOINT_H +#define _CHECKPOINT_H + +#include + +namespace android { +namespace vold { + +bool cp_startCheckpoint(int retry); + +bool cp_commitChanges(); + +void cp_abortChanges(); + +bool cp_needRollback(const std::string& id); + +bool cp_needsCheckpoint(); + +bool cp_prepareDriveForCheckpoint(const std::string& mountPoint); + +bool cp_markBootAttempt(); + +} // namespace vold +} // namespace android + +#endif diff --git a/MetadataCrypt.cpp b/MetadataCrypt.cpp index e5bf819..0934563 100644 --- a/MetadataCrypt.cpp +++ b/MetadataCrypt.cpp @@ -36,6 +36,7 @@ #include #include +#include "Checkpoint.h" #include "EncryptInplace.h" #include "KeyStorage.h" #include "KeyUtil.h" @@ -59,7 +60,8 @@ static bool mount_via_fs_mgr(const char* mount_point, const char* blk_device) { return false; } auto mount_rc = fs_mgr_do_mount(fstab_default, const_cast(mount_point), - const_cast(blk_device), nullptr); + const_cast(blk_device), nullptr, + android::vold::cp_needsCheckpoint()); if (setexeccon(nullptr)) { PLOG(ERROR) << "Failed to clear setexeccon"; return false; diff --git a/VoldNativeService.cpp b/VoldNativeService.cpp index 1a15304..582f84e 100644 --- a/VoldNativeService.cpp +++ b/VoldNativeService.cpp @@ -24,6 +24,7 @@ #include "Process.h" #include "VolumeManager.h" +#include "Checkpoint.h" #include "Ext4Crypt.h" #include "MetadataCrypt.h" #include "cryptfs.h" @@ -764,5 +765,55 @@ binder::Status VoldNativeService::destroyUserStorage(const std::unique_ptr, public os::Bn int32_t userSerial, int32_t flags); binder::Status destroyUserStorage(const std::unique_ptr& uuid, int32_t userId, int32_t flags); + + binder::Status mountExternalStorageForApp(const std::string& packageName, int32_t appId, + const std::string& sandboxId, int32_t userId); + + binder::Status startCheckpoint(int32_t retry, bool* _aidl_return); + binder::Status needsCheckpoint(bool* _aidl_return); + binder::Status commitChanges(bool* _aidl_return); + binder::Status prepareDriveForCheckpoint(const std::string& mountPoint, bool* _aidl_return); + binder::Status markBootAttempt(bool* __aidl_return); + binder::Status abortChanges(); }; } // namespace vold diff --git a/binder/android/os/IVold.aidl b/binder/android/os/IVold.aidl index 567385c..fd97077 100644 --- a/binder/android/os/IVold.aidl +++ b/binder/android/os/IVold.aidl @@ -96,6 +96,13 @@ interface IVold { int storageFlags); void destroyUserStorage(@nullable @utf8InCpp String uuid, int userId, int storageFlags); + boolean startCheckpoint(int retry); + boolean needsCheckpoint(); + void abortChanges(); + boolean commitChanges(); + boolean prepareDriveForCheckpoint(@utf8InCpp String mountPoint); + boolean markBootAttempt(); + const int ENCRYPTION_FLAG_NO_UI = 4; const int ENCRYPTION_STATE_NONE = 1; diff --git a/cryptfs.cpp b/cryptfs.cpp index 0b52650..4a76fb7 100644 --- a/cryptfs.cpp +++ b/cryptfs.cpp @@ -24,6 +24,7 @@ #include "cryptfs.h" +#include "Checkpoint.h" #include "EncryptInplace.h" #include "Ext4Crypt.h" #include "Keymaster.h" @@ -1553,7 +1554,9 @@ static int cryptfs_restart_internal(int restart_main) { SLOGE("Failed to setexeccon"); return -1; } - while ((mount_rc = fs_mgr_do_mount(fstab_default, DATA_MNT_POINT, crypto_blkdev, 0)) != 0) { + bool needs_cp = android::vold::cp_needsCheckpoint(); + while ((mount_rc = fs_mgr_do_mount(fstab_default, DATA_MNT_POINT, crypto_blkdev, 0, + needs_cp)) != 0) { if (mount_rc == FS_MGR_DOMNT_BUSY) { /* TODO: invoke something similar to Process::killProcessWithOpenFiles(DATA_MNT_POINT, diff --git a/vdc.cpp b/vdc.cpp index f49d6b8..ff5073b 100644 --- a/vdc.cpp +++ b/vdc.cpp @@ -31,6 +31,7 @@ #include "android/os/IVold.h" #include +#include #include #include #include @@ -104,6 +105,31 @@ int main(int argc, char** argv) { checkStatus(vold->mountFstab(args[2])); } else if (args[0] == "cryptfs" && args[1] == "encryptFstab" && args.size() == 3) { checkStatus(vold->encryptFstab(args[2])); + } else if (args[0] == "checkpoint" && args[1] == "startCheckpoint" && args.size() == 3) { + int retry; + bool success = false; + if (!android::base::ParseInt(args[2], &retry)) exit(EINVAL); + checkStatus(vold->startCheckpoint(retry, &success)); + return success ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "needsCheckpoint" && args.size() == 2) { + bool enabled = false; + checkStatus(vold->needsCheckpoint(&enabled)); + return enabled ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "commitChanges" && args.size() == 2) { + bool success = false; + checkStatus(vold->commitChanges(&success)); + return success ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "prepareDriveForCheckpoint" && + args.size() == 3) { + bool success = false; + checkStatus(vold->prepareDriveForCheckpoint(args[2], &success)); + return success ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "markBootAttempt" && args.size() == 2) { + bool success = false; + checkStatus(vold->markBootAttempt(&success)); + return success ? 1 : 0; + } else if (args[0] == "checkpoint" && args[1] == "abortChanges" && args.size() == 2) { + checkStatus(vold->abortChanges()); } else { LOG(ERROR) << "Raw commands are no longer supported"; exit(EINVAL);