1 // Copyright (C) 2019 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "utility.h"
16 
17 #include <errno.h>
18 #include <time.h>
19 
20 #include <filesystem>
21 #include <iomanip>
22 #include <sstream>
23 
24 #include <android-base/file.h>
25 #include <android-base/logging.h>
26 #include <android-base/parseint.h>
27 #include <android-base/properties.h>
28 #include <android-base/strings.h>
29 #include <fs_mgr/roots.h>
30 #include <liblp/property_fetcher.h>
31 
32 using android::dm::DeviceMapper;
33 using android::dm::kSectorSize;
34 using android::fiemap::FiemapStatus;
35 using android::fs_mgr::EnsurePathMounted;
36 using android::fs_mgr::EnsurePathUnmounted;
37 using android::fs_mgr::Fstab;
38 using android::fs_mgr::GetEntryForPath;
39 using android::fs_mgr::IPropertyFetcher;
40 using android::fs_mgr::MetadataBuilder;
41 using android::fs_mgr::Partition;
42 using android::fs_mgr::ReadDefaultFstab;
43 using google::protobuf::RepeatedPtrField;
44 
45 namespace android {
46 namespace snapshot {
47 
Release()48 void AutoDevice::Release() {
49     name_.clear();
50 }
51 
~AutoDeviceList()52 AutoDeviceList::~AutoDeviceList() {
53     // Destroy devices in the reverse order because newer devices may have dependencies
54     // on older devices.
55     for (auto it = devices_.rbegin(); it != devices_.rend(); ++it) {
56         it->reset();
57     }
58 }
59 
Release()60 void AutoDeviceList::Release() {
61     for (auto&& p : devices_) {
62         p->Release();
63     }
64 }
65 
~AutoUnmapDevice()66 AutoUnmapDevice::~AutoUnmapDevice() {
67     if (name_.empty()) return;
68     if (!dm_->DeleteDeviceIfExists(name_)) {
69         LOG(ERROR) << "Failed to auto unmap device " << name_;
70     }
71 }
72 
~AutoUnmapImage()73 AutoUnmapImage::~AutoUnmapImage() {
74     if (name_.empty()) return;
75     if (!images_->UnmapImageIfExists(name_)) {
76         LOG(ERROR) << "Failed to auto unmap cow image " << name_;
77     }
78 }
79 
ListPartitionsWithSuffix(MetadataBuilder * builder,const std::string & suffix)80 std::vector<Partition*> ListPartitionsWithSuffix(MetadataBuilder* builder,
81                                                  const std::string& suffix) {
82     std::vector<Partition*> ret;
83     for (const auto& group : builder->ListGroups()) {
84         for (auto* partition : builder->ListPartitionsInGroup(group)) {
85             if (!base::EndsWith(partition->name(), suffix)) {
86                 continue;
87             }
88             ret.push_back(partition);
89         }
90     }
91     return ret;
92 }
93 
~AutoDeleteSnapshot()94 AutoDeleteSnapshot::~AutoDeleteSnapshot() {
95     if (!name_.empty() && !manager_->DeleteSnapshot(lock_, name_)) {
96         LOG(ERROR) << "Failed to auto delete snapshot " << name_;
97     }
98 }
99 
InitializeKernelCow(const std::string & device)100 Return InitializeKernelCow(const std::string& device) {
101     // When the kernel creates a persistent dm-snapshot, it requires a CoW file
102     // to store the modifications. The kernel interface does not specify how
103     // the CoW is used, and there is no standard associated.
104     // By looking at the current implementation, the CoW file is treated as:
105     // - a _NEW_ snapshot if its first 32 bits are zero, so the newly created
106     // dm-snapshot device will look like a perfect copy of the origin device;
107     // - an _EXISTING_ snapshot if the first 32 bits are equal to a
108     // kernel-specified magic number and the CoW file metadata is set as valid,
109     // so it can be used to resume the last state of a snapshot device;
110     // - an _INVALID_ snapshot otherwise.
111     // To avoid zero-filling the whole CoW file when a new dm-snapshot is
112     // created, here we zero-fill only the first chunk to be compliant with
113     // lvm.
114     constexpr ssize_t kDmSnapZeroFillSize = kSectorSize * kSnapshotChunkSize;
115 
116     std::vector<uint8_t> zeros(kDmSnapZeroFillSize, 0);
117     android::base::unique_fd fd(open(device.c_str(), O_WRONLY | O_BINARY));
118     if (fd < 0) {
119         PLOG(ERROR) << "Can't open COW device: " << device;
120         return Return(FiemapStatus::FromErrno(errno));
121     }
122 
123     LOG(INFO) << "Zero-filling COW device: " << device;
124     if (!android::base::WriteFully(fd, zeros.data(), kDmSnapZeroFillSize)) {
125         PLOG(ERROR) << "Can't zero-fill COW device for " << device;
126         return Return(FiemapStatus::FromErrno(errno));
127     }
128     return Return::Ok();
129 }
130 
New(const std::string & path)131 std::unique_ptr<AutoUnmountDevice> AutoUnmountDevice::New(const std::string& path) {
132     Fstab fstab;
133     if (!ReadDefaultFstab(&fstab)) {
134         LOG(ERROR) << "Cannot read default fstab";
135         return nullptr;
136     }
137 
138     if (GetEntryForPath(&fstab, path) == nullptr) {
139         LOG(INFO) << "EnsureMetadataMounted can't find entry for " << path << ", skipping";
140         return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice("", {}));
141     }
142 
143     if (!EnsurePathMounted(&fstab, path)) {
144         LOG(ERROR) << "Cannot mount " << path;
145         return nullptr;
146     }
147     return std::unique_ptr<AutoUnmountDevice>(new AutoUnmountDevice(path, std::move(fstab)));
148 }
149 
~AutoUnmountDevice()150 AutoUnmountDevice::~AutoUnmountDevice() {
151     if (name_.empty()) return;
152     if (!EnsurePathUnmounted(&fstab_, name_)) {
153         LOG(ERROR) << "Cannot unmount " << name_;
154     }
155 }
156 
FsyncDirectory(const char * dirname)157 bool FsyncDirectory(const char* dirname) {
158     android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dirname, O_RDONLY | O_CLOEXEC)));
159     if (fd == -1) {
160         PLOG(ERROR) << "Failed to open " << dirname;
161         return false;
162     }
163     if (fsync(fd) == -1) {
164         if (errno == EROFS || errno == EINVAL) {
165             PLOG(WARNING) << "Skip fsync " << dirname
166                           << " on a file system does not support synchronization";
167         } else {
168             PLOG(ERROR) << "Failed to fsync " << dirname;
169             return false;
170         }
171     }
172     return true;
173 }
174 
WriteStringToFileAtomic(const std::string & content,const std::string & path)175 bool WriteStringToFileAtomic(const std::string& content, const std::string& path) {
176     const std::string tmp_path = path + ".tmp";
177     {
178         const int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY;
179         android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(tmp_path.c_str(), flags, 0666)));
180         if (fd == -1) {
181             PLOG(ERROR) << "Failed to open " << path;
182             return false;
183         }
184         if (!android::base::WriteStringToFd(content, fd)) {
185             PLOG(ERROR) << "Failed to write to fd " << fd;
186             return false;
187         }
188         // rename() without fsync() is not safe. Data could still be living on page cache. To ensure
189         // atomiticity, call fsync()
190         if (fsync(fd) != 0) {
191             PLOG(ERROR) << "Failed to fsync " << tmp_path;
192         }
193     }
194     if (rename(tmp_path.c_str(), path.c_str()) == -1) {
195         PLOG(ERROR) << "rename failed from " << tmp_path << " to " << path;
196         return false;
197     }
198     return FsyncDirectory(std::filesystem::path(path).parent_path().c_str());
199 }
200 
operator <<(std::ostream & os,const Now &)201 std::ostream& operator<<(std::ostream& os, const Now&) {
202     struct tm now{};
203     time_t t = time(nullptr);
204     localtime_r(&t, &now);
205     return os << std::put_time(&now, "%Y%m%d-%H%M%S");
206 }
207 
AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent> * extents,uint64_t start_block,uint64_t num_blocks)208 void AppendExtent(RepeatedPtrField<chromeos_update_engine::Extent>* extents, uint64_t start_block,
209                   uint64_t num_blocks) {
210     if (extents->size() > 0) {
211         auto last_extent = extents->rbegin();
212         auto next_block = last_extent->start_block() + last_extent->num_blocks();
213         if (start_block == next_block) {
214             last_extent->set_num_blocks(last_extent->num_blocks() + num_blocks);
215             return;
216         }
217     }
218     auto* new_extent = extents->Add();
219     new_extent->set_start_block(start_block);
220     new_extent->set_num_blocks(num_blocks);
221 }
222 
GetLegacyCompressionEnabledProperty()223 bool GetLegacyCompressionEnabledProperty() {
224     auto fetcher = IPropertyFetcher::GetInstance();
225     return fetcher->GetBoolProperty("ro.virtual_ab.compression.enabled", false);
226 }
227 
GetUserspaceSnapshotsEnabledProperty()228 bool GetUserspaceSnapshotsEnabledProperty() {
229     auto fetcher = IPropertyFetcher::GetInstance();
230     return fetcher->GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false);
231 }
232 
IsVendorFromAndroid12()233 bool IsVendorFromAndroid12() {
234     auto fetcher = IPropertyFetcher::GetInstance();
235 
236     const std::string UNKNOWN = "unknown";
237     const std::string vendor_release =
238             fetcher->GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN);
239 
240     // No user-space snapshots if vendor partition is on Android 12
241     if (vendor_release.find("12") != std::string::npos) {
242         return true;
243     }
244 
245     return false;
246 }
247 
CanUseUserspaceSnapshots()248 bool CanUseUserspaceSnapshots() {
249     if (!GetUserspaceSnapshotsEnabledProperty()) {
250         LOG(INFO) << "Virtual A/B - Userspace snapshots disabled";
251         return false;
252     }
253 
254     if (IsDmSnapshotTestingEnabled()) {
255         LOG(INFO) << "Userspace snapshots disabled for testing";
256         return false;
257     }
258     if (!KernelSupportsCompressedSnapshots()) {
259         LOG(ERROR) << "Userspace snapshots requested, but no kernel support is available.";
260         return false;
261     }
262     return true;
263 }
264 
GetIouringEnabledProperty()265 bool GetIouringEnabledProperty() {
266     auto fetcher = IPropertyFetcher::GetInstance();
267     return fetcher->GetBoolProperty("ro.virtual_ab.io_uring.enabled", false);
268 }
269 
GetXorCompressionEnabledProperty()270 bool GetXorCompressionEnabledProperty() {
271     auto fetcher = IPropertyFetcher::GetInstance();
272     return fetcher->GetBoolProperty("ro.virtual_ab.compression.xor.enabled", false);
273 }
274 
GetODirectEnabledProperty()275 bool GetODirectEnabledProperty() {
276     auto fetcher = IPropertyFetcher::GetInstance();
277     return fetcher->GetBoolProperty("ro.virtual_ab.o_direct.enabled", false);
278 }
279 
GetOtherPartitionName(const std::string & name)280 std::string GetOtherPartitionName(const std::string& name) {
281     auto suffix = android::fs_mgr::GetPartitionSlotSuffix(name);
282     CHECK(suffix == "_a" || suffix == "_b");
283 
284     auto other_suffix = (suffix == "_a") ? "_b" : "_a";
285     return name.substr(0, name.size() - suffix.size()) + other_suffix;
286 }
287 
IsDmSnapshotTestingEnabled()288 bool IsDmSnapshotTestingEnabled() {
289     auto fetcher = IPropertyFetcher::GetInstance();
290     return fetcher->GetBoolProperty("snapuserd.test.dm.snapshots", false);
291 }
292 
KernelSupportsCompressedSnapshots()293 bool KernelSupportsCompressedSnapshots() {
294     auto& dm = DeviceMapper::Instance();
295     return dm.GetTargetByName("user", nullptr);
296 }
297 
298 }  // namespace snapshot
299 }  // namespace android
300