| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "cryptohome/mount_encrypted/encrypted_fs.h" |
| |
| #include <fcntl.h> |
| #include <grp.h> |
| #include <memory> |
| #include <pwd.h> |
| #include <string.h> |
| #include <sys/mount.h> |
| #include <sys/statvfs.h> |
| #include <sys/types.h> |
| |
| #include <string> |
| |
| #include <base/compiler_specific.h> |
| #include <base/files/file_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <brillo/process/process.h> |
| #include <brillo/secure_blob.h> |
| |
| #include "cryptohome/cryptolib.h" |
| #include "cryptohome/mount_encrypted/mount_encrypted.h" |
| |
| namespace mount_encrypted { |
| |
| namespace { |
| |
| constexpr char kEncryptedFSType[] = "ext4"; |
| constexpr char kCryptDevName[] = "encstateful"; |
| constexpr char kDevMapperPath[] = "/dev/mapper"; |
| constexpr char kDumpe2fsLogPath[] = "/run/mount_encrypted/dumpe2fs.log"; |
| constexpr char kProcDirtyExpirePath[] = "/proc/sys/vm/dirty_expire_centisecs"; |
| constexpr float kSizePercent = 0.3; |
| constexpr uint64_t kSectorSize = 512; |
| constexpr uint64_t kExt4BlockSize = 4096; |
| constexpr uint64_t kExt4MinBytes = 16 * 1024 * 1024; |
| constexpr int kCryptAllowDiscard = 1; |
| constexpr unsigned int kResizeStepSeconds = 2; |
| constexpr uint64_t kExt4ResizeBlocks = 32768 * 10; |
| constexpr uint64_t kExt4BlocksPerGroup = 32768; |
| constexpr uint64_t kExt4InodeRatioDefault = 16384; |
| constexpr uint64_t kExt4InodeRatioMinimum = 2048; |
| // Block size is 4k => Minimum free space available to try resizing is 400MB. |
| constexpr int64_t kMinBlocksAvailForResize = 102400; |
| constexpr char kExt4ExtendedOptions[] = "discard,lazy_itable_init"; |
| constexpr char kDmCryptDefaultCipher[] = "aes-cbc-essiv:sha256"; |
| |
| bool CheckBind(cryptohome::Platform* platform, const BindMount& bind) { |
| uid_t user; |
| gid_t group; |
| |
| if (platform->Access(bind.src, R_OK) && |
| !platform->CreateDirectory(bind.src)) { |
| PLOG(ERROR) << "mkdir " << bind.src; |
| return false; |
| } |
| |
| if (platform->Access(bind.dst, R_OK) && |
| !(platform->CreateDirectory(bind.dst) && |
| platform->SetPermissions(bind.dst, bind.mode))) { |
| PLOG(ERROR) << "mkdir " << bind.dst; |
| return false; |
| } |
| |
| if (!platform->GetUserId(bind.owner, &user, &group)) { |
| PLOG(ERROR) << "getpwnam" << bind.owner; |
| return false; |
| } |
| |
| // Destination may be on read-only filesystem, so skip tweaks. |
| // Must do explicit chmod since mkdir()'s mode respects umask. |
| if (!platform->SetPermissions(bind.src, bind.mode)) { |
| PLOG(ERROR) << "chmod " << bind.src; |
| return false; |
| } |
| if (!platform->SetOwnership(bind.src, user, group, true)) { |
| PLOG(ERROR) << "chown " << bind.src; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // TODO(sarthakkukreti): Evaulate resizing: it is a no-op on new encrypted |
| // stateful setups and would slow down boot once for legacy devices on update, |
| // as long as we do not iteratively resize. |
| // Spawns a filesystem resizing process and waits for it to finish. |
| void SpawnResizer(cryptohome::Platform* platform, |
| const base::FilePath& device, |
| uint64_t blocks, |
| uint64_t blocks_max) { |
| // Ignore resizing if we know the filesystem was built to max size. |
| if (blocks >= blocks_max) { |
| PLOG(ERROR) << " Resizing aborted"; |
| return; |
| } |
| |
| // TODO(keescook): Read superblock to find out the current size of |
| // the filesystem (since statvfs does not report the correct value). |
| // For now, instead of doing multi-step resizing, just resize to the |
| // full size of the block device in one step. |
| blocks = blocks_max; |
| |
| LOG(INFO) << "Resizing started in " << kResizeStepSeconds << " second steps."; |
| |
| do { |
| blocks += kExt4ResizeBlocks; |
| |
| if (blocks > blocks_max) |
| blocks = blocks_max; |
| |
| // Run the resizing function. For a fresh setup, the resize should be |
| // a no-op, the only case where this might be slow is legacy devices which |
| // have a smaller encrypted stateful partition. |
| platform->ResizeFilesystem(device, blocks); |
| } while (blocks < blocks_max); |
| |
| LOG(INFO) << "Resizing done."; |
| return; |
| } |
| |
| std::string GetMountOpts() { |
| // Use vm.dirty_expire_centisecs / 100 as the commit interval. |
| std::string dirty_expire; |
| uint64_t dirty_expire_centisecs; |
| uint64_t commit_interval = 600; |
| |
| if (base::ReadFileToString(base::FilePath(kProcDirtyExpirePath), |
| &dirty_expire) && |
| base::StringToUint64(dirty_expire, &dirty_expire_centisecs)) { |
| LOG(INFO) << "Using vm.dirty_expire_centisecs/100 as the commit interval"; |
| |
| // Keep commit interval as 5 seconds (default for ext4) for smaller |
| // values of dirty_expire_centisecs. |
| if (dirty_expire_centisecs < 600) |
| commit_interval = 5; |
| else |
| commit_interval = dirty_expire_centisecs / 100; |
| } |
| return "discard,commit=" + std::to_string(commit_interval); |
| } |
| |
| // When creating a filesystem that will grow, the inode ratio is calculated |
| // using the starting size not the hinted "resize" size, which means the |
| // number of inodes can be highly constrained on tiny starting filesystems. |
| // Instead, calculate what the correct inode ratio should be for a given |
| // filesystem based on its expected starting and ending sizes. |
| // |
| // inode-ratio_mkfs = |
| // |
| // ceil(blocks_max / group-ratio) * size_mkfs |
| // ------------------------------------------------------------------ |
| // ceil(size_max / inode-ratio_max) * ceil(blocks_mkfs / group-ratio) |
| // |
| static uint64_t Ext4GetInodeRatio(uint64_t block_bytes_in, |
| uint64_t blocks_mkfs_in, |
| uint64_t blocks_max_in) { |
| double block_bytes = static_cast<double>(block_bytes_in); |
| double blocks_mkfs = static_cast<double>(blocks_mkfs_in); |
| double blocks_max = static_cast<double>(blocks_max_in); |
| |
| double size_max, size_mkfs, groups_max; |
| double inode_ratio_mkfs; |
| |
| uint64_t denom, inodes_max, groups_mkfs; |
| |
| size_max = block_bytes * blocks_max; |
| size_mkfs = block_bytes * blocks_mkfs; |
| |
| groups_max = ceil(blocks_max / kExt4BlocksPerGroup); |
| groups_mkfs = static_cast<uint64_t>(ceil(blocks_mkfs / kExt4BlocksPerGroup)); |
| |
| inodes_max = static_cast<uint64_t>(ceil(size_max / kExt4InodeRatioDefault)); |
| |
| denom = inodes_max * groups_mkfs; |
| // Make sure we never trigger divide-by-zero. |
| if (denom == 0) |
| return kExt4InodeRatioDefault; |
| |
| inode_ratio_mkfs = (groups_max * size_mkfs) / denom; |
| |
| // Make sure we never calculate anything totally huge or totally tiny. |
| if (inode_ratio_mkfs > blocks_mkfs || |
| inode_ratio_mkfs < kExt4InodeRatioMinimum) |
| return kExt4InodeRatioDefault; |
| |
| return (uint64_t)inode_ratio_mkfs; |
| } |
| |
| std::vector<std::string> BuildExt4FormatOpts(uint64_t block_bytes, |
| uint64_t blocks_min, |
| uint64_t blocks_max) { |
| return { |
| "-T", |
| "default", |
| "-b", |
| std::to_string(block_bytes), |
| "-m", |
| "0", |
| "-O", |
| "^huge_file,^flex_bg", |
| "-i", |
| std::to_string(Ext4GetInodeRatio(block_bytes, blocks_min, blocks_max)), |
| "-E", |
| kExt4ExtendedOptions + ((blocks_min < blocks_max) |
| ? ",resize=" + std::to_string(blocks_max) |
| : "")}; |
| } |
| |
| bool UdevAdmSettle(const base::FilePath& device_path, bool wait_for_device) { |
| brillo::ProcessImpl udevadm_process; |
| udevadm_process.AddArg("/bin/udevadm"); |
| udevadm_process.AddArg("settle"); |
| |
| if (wait_for_device) { |
| udevadm_process.AddArg("-t"); |
| udevadm_process.AddArg("10"); |
| udevadm_process.AddArg("-E"); |
| udevadm_process.AddArg(device_path.value()); |
| } |
| // Close unused file descriptors in child process. |
| udevadm_process.SetCloseUnusedFileDescriptors(true); |
| |
| // Start the process and return. |
| int rc = udevadm_process.Run(); |
| if (rc != 0) |
| return false; |
| |
| return true; |
| } |
| |
| void CheckSparseFileSize(const base::FilePath& sparse_file, int64_t file_size) { |
| base::File file(sparse_file, base::File::FLAG_OPEN | base::File::FLAG_WRITE); |
| |
| if (file.IsValid() && file.GetLength() < file_size) { |
| LOG(INFO) << "Expanding underlying sparse file to " << file_size; |
| file.SetLength(file_size); |
| } |
| } |
| |
| void Dumpe2fs(const base::FilePath& device_path) { |
| brillo::ProcessImpl dumpe2fs; |
| dumpe2fs.AddArg("/sbin/dumpe2fs"); |
| dumpe2fs.AddArg("-fh"); |
| dumpe2fs.AddArg(device_path.value()); |
| dumpe2fs.RedirectOutput(kDumpe2fsLogPath); |
| |
| dumpe2fs.Run(); |
| } |
| |
| } // namespace |
| |
| EncryptedFs::EncryptedFs(const base::FilePath& rootdir, |
| cryptohome::Platform* platform, |
| brillo::LoopDeviceManager* loop_device_manager, |
| brillo::DeviceMapper* device_mapper) |
| : platform_(platform), |
| loopdev_manager_(loop_device_manager), |
| device_mapper_(device_mapper), |
| rootdir_(rootdir) { |
| dmcrypt_name_ = std::string(kCryptDevName); |
| if (rootdir_ != base::FilePath("/")) { |
| brillo::SecureBlob digest = |
| cryptohome::CryptoLib::Sha256(brillo::SecureBlob(rootdir_.value())); |
| std::string hex = cryptohome::CryptoLib::SecureBlobToHex(digest); |
| dmcrypt_name_ += "_" + hex.substr(0, 16); |
| } |
| // Initialize remaining directories. |
| stateful_mount_ = rootdir_.Append(STATEFUL_MNT); |
| block_path_ = rootdir_.Append(STATEFUL_MNT "/encrypted.block"); |
| encrypted_mount_ = rootdir_.Append(ENCRYPTED_MNT); |
| dmcrypt_dev_ = base::FilePath(kDevMapperPath).Append(dmcrypt_name_.c_str()); |
| |
| // Create bind mounts. |
| bind_mounts_.push_back( |
| {rootdir_.Append(ENCRYPTED_MNT "/var"), rootdir_.Append("var"), "root", |
| "root", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, false}); |
| |
| bind_mounts_.push_back({rootdir_.Append(ENCRYPTED_MNT "/chronos"), |
| rootdir_.Append("home/chronos"), "chronos", "chronos", |
| S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH, |
| true}); |
| } |
| |
| bool EncryptedFs::Purge() { |
| LOG(INFO) << "Purging block file"; |
| return platform_->DeleteFile(block_path_, false); |
| } |
| |
| bool EncryptedFs::CreateSparseBackingFile(int64_t file_size) { |
| return platform_->CreateSparseFile(block_path_, file_size) && |
| platform_->SetPermissions(block_path_, S_IRUSR | S_IWUSR); |
| } |
| |
| // Do all the work needed to actually set up the encrypted partition. |
| result_code EncryptedFs::Setup(const brillo::SecureBlob& encryption_key, |
| bool rebuild) { |
| result_code rc = RESULT_FAIL_FATAL; |
| struct statvfs stateful_statbuf; |
| |
| // Get stateful partition statistics. This acts as an indicator of how large |
| // we want the encrypted stateful partition to be. |
| if (!platform_->StatVFS(stateful_mount_, &stateful_statbuf)) { |
| PLOG(ERROR) << "stat() failed on: " << stateful_mount_; |
| return rc; |
| } |
| |
| // Calculate the maximum size of the encrypted stateful partition. |
| // truncate()/ftruncate() use int64_t for file size. |
| int64_t fs_bytes_max = static_cast<int64_t>(stateful_statbuf.f_blocks); |
| fs_bytes_max *= kSizePercent; |
| fs_bytes_max *= stateful_statbuf.f_frsize; |
| |
| if (rebuild) { |
| // Wipe out the old files, and ignore errors. |
| Purge(); |
| |
| // Create new sparse file. |
| LOG(INFO) << "Creating sparse backing file with size " << fs_bytes_max; |
| |
| if (!CreateSparseBackingFile(fs_bytes_max)) { |
| PLOG(ERROR) << "Failed to create sparse backing file " << block_path_; |
| return rc; |
| } |
| } |
| |
| // b/131123943: Check the size of the sparse file and resize if necessary. |
| // Resizing the sparse file via truncate() should be a no-op but resizing |
| // the filesystem residing on the file is a bit more involved and may need |
| // to write metadata to several blocks. If there aren't enough blocks |
| // available, we might succeed here but eventually fail to resize and corrupt |
| // the encrypted stateful file system. Check if there are at least a few |
| // blocks available on the stateful partition. |
| if (stateful_statbuf.f_bfree > kMinBlocksAvailForResize) |
| CheckSparseFileSize(block_path_, fs_bytes_max); |
| else |
| LOG(WARNING) << "Low space on stateful partition; not attempting to resize " |
| << "the underlying block file."; |
| |
| // Set up loopback device. |
| LOG(INFO) << "Loopback attaching " << block_path_ << " named " |
| << dmcrypt_name_; |
| std::unique_ptr<brillo::LoopDevice> lodev = |
| loopdev_manager_->AttachDeviceToFile(block_path_); |
| if (!lodev->IsValid()) { |
| LOG(ERROR) << "Loop attach failed"; |
| return rc; |
| } |
| |
| // Set loop device name. |
| if (!lodev->SetName(dmcrypt_name_)) { |
| LOG(ERROR) << "Loop set name failed"; |
| return rc; |
| } |
| |
| base::FilePath lodev_path = lodev->GetDevicePath(); |
| |
| // Get size as seen by block device. |
| uint64_t blkdev_size; |
| if (!platform_->GetBlkSize(lodev_path, &blkdev_size) || |
| blkdev_size < kExt4BlockSize) { |
| LOG(ERROR) << "Failed to read device size"; |
| TeardownByStage(TeardownStage::kTeardownLoopDevice, true); |
| return rc; |
| } |
| |
| // Mount loopback device with dm-crypt using the encryption key. |
| LOG(INFO) << "Setting up dm-crypt " << lodev_path << " as " << dmcrypt_dev_; |
| |
| uint64_t sectors = blkdev_size / kSectorSize; |
| brillo::SecureBlob dm_parameters = |
| brillo::DevmapperTable::CryptCreateParameters( |
| kDmCryptDefaultCipher, // cipher. |
| encryption_key, // encryption key. |
| 0, // iv offset. |
| lodev_path, // device_path. |
| 0, // device offset. |
| kCryptAllowDiscard); // allow discards. |
| |
| brillo::DevmapperTable dm_table(0, sectors, "crypt", dm_parameters); |
| if (!device_mapper_->Setup(dmcrypt_name_, dm_table)) { |
| // If dm_setup() fails, it could be due to lacking |
| // "allow_discard" support, so try again with discard |
| // disabled. There doesn't seem to be a way to query |
| // the kernel for this feature short of a fallible |
| // version test or just trying to set up the dm table |
| // again, so do the latter. |
| dm_parameters = brillo::DevmapperTable::CryptCreateParameters( |
| kDmCryptDefaultCipher, // cipher. |
| encryption_key, // encryption key. |
| 0, // iv offset. |
| lodev_path, // device_path. |
| 0, // device offset. |
| !kCryptAllowDiscard); // allow discards. |
| brillo::DevmapperTable dm_table(0, sectors, "crypt", dm_parameters); |
| if (!device_mapper_->Setup(dmcrypt_name_, dm_table)) { |
| LOG(ERROR) << "dm_setup failed"; |
| TeardownByStage(TeardownStage::kTeardownLoopDevice, true); |
| return rc; |
| } |
| LOG(INFO) << dmcrypt_dev_ |
| << ": dm-crypt does not support discard; disabling."; |
| } |
| |
| if (!UdevAdmSettle(dmcrypt_dev_, true)) { |
| LOG(ERROR) << "udevadm settle failed."; |
| TeardownByStage(TeardownStage::kTeardownDevmapper, true); |
| return rc; |
| } |
| |
| // Calculate filesystem min/max size. |
| uint64_t blocks_max = blkdev_size / kExt4BlockSize; |
| uint64_t blocks_min = kExt4MinBytes / kExt4BlockSize; |
| |
| if (rebuild) { |
| LOG(INFO) << "Building filesystem on " << dmcrypt_dev_ |
| << "(blocksize: " << kExt4BlockSize << ", min: " << blocks_min |
| << ", max: " << blocks_max; |
| if (!platform_->FormatExt4( |
| dmcrypt_dev_, |
| BuildExt4FormatOpts(kExt4BlockSize, blocks_min, blocks_max), |
| blocks_min)) { |
| TeardownByStage(TeardownStage::kTeardownDevmapper, true); |
| return rc; |
| } |
| } |
| |
| // Mount the dm-crypt partition finally. |
| LOG(INFO) << "Mounting " << dmcrypt_dev_ << " onto " << encrypted_mount_; |
| if (platform_->Access(encrypted_mount_, R_OK) && |
| !(platform_->CreateDirectory(encrypted_mount_) && |
| platform_->SetPermissions(encrypted_mount_, |
| S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))) { |
| PLOG(ERROR) << dmcrypt_dev_; |
| TeardownByStage(TeardownStage::kTeardownDevmapper, true); |
| return rc; |
| } |
| if (!platform_->Mount(dmcrypt_dev_, encrypted_mount_, kEncryptedFSType, |
| MS_NODEV | MS_NOEXEC | MS_NOSUID | MS_NOATIME, |
| GetMountOpts().c_str())) { |
| PLOG(ERROR) << "mount: " << dmcrypt_dev_ << ", " << encrypted_mount_; |
| // On failure to mount, use dumpe2fs to collect debugging data about |
| // the unencrypted block device that failed to mount. Since mount-encrypted |
| // cleans up afterwards, this is the only point where this data can be |
| // collected. |
| Dumpe2fs(dmcrypt_dev_); |
| TeardownByStage(TeardownStage::kTeardownDevmapper, true); |
| return rc; |
| } |
| |
| // Always spawn filesystem resizer, in case growth was interrupted. |
| // TODO(keescook): if already full size, don't resize. |
| SpawnResizer(platform_, dmcrypt_dev_, blocks_min, blocks_max); |
| |
| // Perform bind mounts. |
| for (auto& bind : bind_mounts_) { |
| LOG(INFO) << "Bind mounting " << bind.src << " onto " << bind.dst; |
| if (!CheckBind(platform_, bind)) { |
| TeardownByStage(TeardownStage::kTeardownUnbind, true); |
| return rc; |
| } |
| if (!platform_->Bind(bind.src, bind.dst)) { |
| PLOG(ERROR) << "mount: " << bind.src << ", " << bind.dst; |
| TeardownByStage(TeardownStage::kTeardownUnbind, true); |
| return rc; |
| } |
| } |
| |
| // Everything completed without error. |
| return RESULT_SUCCESS; |
| } |
| |
| // Clean up all bind mounts, mounts, attaches, etc. Only the final |
| // action informs the return value. This makes it so that failures |
| // can be cleaned up from, and continue the shutdown process on a |
| // second call. If the loopback cannot be found, claim success. |
| result_code EncryptedFs::Teardown() { |
| return TeardownByStage(TeardownStage::kTeardownUnbind, false); |
| } |
| |
| result_code EncryptedFs::TeardownByStage(TeardownStage stage, |
| bool ignore_errors) { |
| switch (stage) { |
| case TeardownStage::kTeardownUnbind: |
| for (auto& bind : bind_mounts_) { |
| LOG(INFO) << "Unmounting " << bind.dst; |
| errno = 0; |
| // Allow either success or a "not mounted" failure. |
| if (!platform_->Unmount(bind.dst, false, nullptr) && !ignore_errors) { |
| if (errno != EINVAL) { |
| PLOG(ERROR) << "umount " << bind.dst; |
| return RESULT_FAIL_FATAL; |
| } |
| } |
| } |
| |
| LOG(INFO) << "Unmounting " << encrypted_mount_; |
| errno = 0; |
| // Allow either success or a "not mounted" failure. |
| if (!platform_->Unmount(encrypted_mount_, false, nullptr) && |
| !ignore_errors) { |
| if (errno != EINVAL) { |
| PLOG(ERROR) << "umount " << encrypted_mount_; |
| return RESULT_FAIL_FATAL; |
| } |
| } |
| |
| // Force syncs to make sure we don't tickle racey/buggy kernel |
| // routines that might be causing crosbug.com/p/17610. |
| platform_->Sync(); |
| |
| // Intentionally fall through here to teardown the lower dmcrypt device. |
| FALLTHROUGH; |
| case TeardownStage::kTeardownDevmapper: |
| LOG(INFO) << "Removing " << dmcrypt_dev_; |
| if (!device_mapper_->Remove(dmcrypt_name_) && !ignore_errors) { |
| LOG(ERROR) << "dm_teardown: " << dmcrypt_dev_; |
| return RESULT_FAIL_FATAL; |
| } |
| if (!UdevAdmSettle(dmcrypt_dev_, false) && !ignore_errors) |
| LOG(WARNING) << "udevadm settle failed."; |
| platform_->Sync(); |
| |
| // Intentionally fall through here to teardown the lower loop device. |
| FALLTHROUGH; |
| case TeardownStage::kTeardownLoopDevice: |
| LOG(INFO) << "Unlooping " << block_path_ << " named " << dmcrypt_name_; |
| std::unique_ptr<brillo::LoopDevice> lodev = |
| loopdev_manager_->GetAttachedDeviceByName(dmcrypt_name_); |
| if (!(lodev->IsValid() && lodev->Detach()) && !ignore_errors) { |
| LOG(ERROR) << "loop_detach_name: " << dmcrypt_name_; |
| return RESULT_FAIL_FATAL; |
| } |
| platform_->Sync(); |
| return RESULT_SUCCESS; |
| } |
| |
| LOG(ERROR) << "Teardown failed."; |
| return RESULT_FAIL_FATAL; |
| } |
| |
| result_code EncryptedFs::CheckStates(void) { |
| // Verify stateful partition exists. |
| if (platform_->Access(stateful_mount_, R_OK)) { |
| LOG(INFO) << stateful_mount_ << "does not exist."; |
| return RESULT_FAIL_FATAL; |
| } |
| // Verify stateful is either a separate mount, or that the |
| // root directory is writable (i.e. a factory install, dev mode |
| // where root remounted rw, etc). |
| if (platform_->SameVFS(stateful_mount_, rootdir_) && |
| platform_->Access(rootdir_, W_OK)) { |
| LOG(INFO) << stateful_mount_ << " is not mounted."; |
| return RESULT_FAIL_FATAL; |
| } |
| |
| // Verify encrypted partition is missing or not already mounted. |
| if (platform_->Access(encrypted_mount_, R_OK) == 0 && |
| !platform_->SameVFS(encrypted_mount_, stateful_mount_)) { |
| LOG(INFO) << encrypted_mount_ << " already appears to be mounted."; |
| return RESULT_SUCCESS; |
| } |
| |
| // Verify that bind mount targets exist. |
| for (auto& bind : bind_mounts_) { |
| if (platform_->Access(bind.dst, R_OK)) { |
| PLOG(ERROR) << bind.dst << " mount point is missing."; |
| return RESULT_FAIL_FATAL; |
| } |
| } |
| |
| // Verify that old bind mounts on stateful haven't happened yet. |
| for (auto& bind : bind_mounts_) { |
| if (bind.submount) |
| continue; |
| |
| if (platform_->SameVFS(bind.dst, stateful_mount_)) { |
| LOG(INFO) << bind.dst << " already bind mounted."; |
| return RESULT_FAIL_FATAL; |
| } |
| } |
| |
| LOG(INFO) << "VFS mount state sanity check ok."; |
| return RESULT_SUCCESS; |
| } |
| |
| result_code EncryptedFs::ReportInfo(void) const { |
| printf("rootdir: %s\n", rootdir_.value().c_str()); |
| printf("stateful_mount: %s\n", stateful_mount_.value().c_str()); |
| printf("block_path: %s\n", block_path_.value().c_str()); |
| printf("encrypted_mount: %s\n", encrypted_mount_.value().c_str()); |
| printf("dmcrypt_name: %s\n", dmcrypt_name_.c_str()); |
| printf("dmcrypt_dev: %s\n", dmcrypt_dev_.value().c_str()); |
| printf("bind mounts:\n"); |
| for (auto& mnt : bind_mounts_) { |
| printf("\tsrc:%s\n", mnt.src.value().c_str()); |
| printf("\tdst:%s\n", mnt.dst.value().c_str()); |
| printf("\towner:%s\n", mnt.owner.c_str()); |
| printf("\tmode:%o\n", mnt.mode); |
| printf("\tsubmount:%d\n", mnt.submount); |
| printf("\n"); |
| } |
| return RESULT_SUCCESS; |
| } |
| |
| brillo::SecureBlob EncryptedFs::GetKey() const { |
| brillo::DevmapperTable dm_table = device_mapper_->GetTable(dmcrypt_name_); |
| return dm_table.CryptGetKey(); |
| } |
| |
| } // namespace mount_encrypted |