// Copyright (C) 2019 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. #include "utility.h" #include #include #include #include #include #include #include #include #include using android::dm::kSectorSize; using android::fiemap::FiemapStatus; using android::fs_mgr::EnsurePathMounted; using android::fs_mgr::EnsurePathUnmounted; using android::fs_mgr::Fstab; using android::fs_mgr::GetEntryForPath; using android::fs_mgr::MetadataBuilder; using android::fs_mgr::Partition; using android::fs_mgr::ReadDefaultFstab; using google::protobuf::RepeatedPtrField; namespace android { namespace snapshot { void AutoDevice::Release() { name_.clear(); } AutoDeviceList::~AutoDeviceList() { // Destroy devices in the reverse order because newer devices may have dependencies // on older devices. for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) { it->reset(); } } void AutoDeviceList::Release() { for (auto&& p : devices_) { p->Release(); } } AutoUnmapDevice::~AutoUnmapDevice() { if (name_.empty()) return; if (!dm_->DeleteDeviceIfExists(name_)) { LOG(ERROR) << "Failed to auto unmap device " << name_; } } AutoUnmapImage::~AutoUnmapImage() { if (name_.empty()) return; if (!images_->UnmapImageIfExists(name_)) { LOG(ERROR) << "Failed to auto unmap cow image " << name_; } } std::vector ListPartitionsWithSuffix(MetadataBuilder* builder, const std::string& suffix) { std::vector ret; for (const auto& group : builder->ListGroups()) { for (auto* partition : builder->ListPartitionsInGroup(group)) { if (!base::EndsWith(partition->name(), suffix)) { continue; } ret.push_back(partition); } } return ret; } AutoDeleteSnapshot::~AutoDeleteSnapshot() { if (!name_.empty() && !manager_->DeleteSnapshot(lock_, name_)) { LOG(ERROR) << "Failed to auto delete snapshot " << name_; } } Return InitializeKernelCow(const std::string& device) { // When the kernel creates a persistent dm-snapshot, it requires a CoW file // to store the modifications. The kernel interface does not specify how // the CoW is used, and there is no standard associated. // By looking at the current implementation, the CoW file is treated as: // - a _NEW_ snapshot if its first 32 bits are zero, so the newly created // dm-snapshot device will look like a perfect copy of the origin device; // - an _EXISTING_ snapshot if the first 32 bits are equal to a // kernel-specified magic number and the CoW file metadata is set as valid, // so it can be used to resume the last state of a snapshot device; // - an _INVALID_ snapshot otherwise. // To avoid zero-filling the whole CoW file when a new dm-snapshot is // created, here we zero-fill only the first chunk to be compliant with // lvm. constexpr ssize_t kDmSnapZeroFillSize = kSectorSize * kSnapshotChunkSize; std::vector zeros(kDmSnapZeroFillSize, 0); android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY)); if (fd < 0) { PLOG(ERROR) << "Can't open COW device: " << device; return Return(FiemapStatus::FromErrno(errno)); } LOG(INFO) << "Zero-filling COW device: " << device; if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) { PLOG(ERROR) << "Can't zero-fill COW device for " << device; return Return(FiemapStatus::FromErrno(errno)); } return Return::Ok(); } std::unique_ptr AutoUnmountDevice::New(const std::string& path) { Fstab fstab; if (!ReadDefaultFstab(&fstab)) { LOG(ERROR) << "Cannot read default fstab"; return nullptr; } if (GetEntryForPath(&fstab, path) == nullptr) { LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping"; return std::unique_ptr(new AutoUnmountDevice("", {})); } if (!EnsurePathMounted(&fstab, path)) { LOG(ERROR) << "Cannot mount " << path; return nullptr; } return std::unique_ptr(new AutoUnmountDevice(path, std::move(fstab))); } AutoUnmountDevice::~AutoUnmountDevice() { if (name_.empty()) return; if (!EnsurePathUnmounted(&fstab_, name_)) { LOG(ERROR) << "Cannot unmount " << name_; } } bool WriteStringToFileAtomic(const std::string& content, const std::string& path) { std::string tmp_path = path + ".tmp"; if (!android::base::WriteStringToFile(content, tmp_path)) { return false; } if (rename(tmp_path.c_str(), path.c_str()) == -1) { PLOG(ERROR) << "rename failed from " << tmp_path << " to " << path; return false; } return true; } std::ostream& operator<<(std::ostream& os, const Now&) { struct tm now; time_t t = time(nullptr); localtime_r(&t, &now); return os << std::put_time(&now, "%Y%m%d-%H%M%S"); } void AppendExtent(RepeatedPtrField* extents, uint64_t start_block, uint64_t num_blocks) { if (extents->size() > 0) { auto last_extent = extents->rbegin(); auto next_block = last_extent->start_block() + last_extent->num_blocks(); if (start_block == next_block) { last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks); return; } } auto* new_extent = extents->Add(); new_extent->set_start_block(start_block); new_extent->set_num_blocks(num_blocks); } bool IsCompressionEnabled() { return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false); } std::string GetOtherPartitionName(const std::string& name) { auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name); CHECK(suffix == "_a" || suffix == "_b"); auto other_suffix = (suffix == "_a") ? "_b" : "_a"; return name.substr(0, name.size() - suffix.size()) + other_suffix; } } // namespace snapshot } // namespace android