1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "IncrementalService"
18 
19 #include "IncrementalService.h"
20 
21 #include <android-base/logging.h>
22 #include <android-base/no_destructor.h>
23 #include <android-base/properties.h>
24 #include <android-base/stringprintf.h>
25 #include <binder/AppOpsManager.h>
26 #include <binder/Status.h>
27 #include <sys/stat.h>
28 #include <uuid/uuid.h>
29 
30 #include <charconv>
31 #include <ctime>
32 #include <iterator>
33 #include <span>
34 #include <type_traits>
35 
36 #include "IncrementalServiceValidation.h"
37 #include "Metadata.pb.h"
38 
39 using namespace std::literals;
40 
41 constexpr const char* kLoaderUsageStats = "android.permission.LOADER_USAGE_STATS";
42 constexpr const char* kOpUsage = "android:loader_usage_stats";
43 
44 constexpr const char* kInteractAcrossUsers = "android.permission.INTERACT_ACROSS_USERS";
45 
46 namespace android::incremental {
47 
48 using content::pm::DataLoaderParamsParcel;
49 using content::pm::FileSystemControlParcel;
50 using content::pm::IDataLoader;
51 
52 namespace {
53 
54 using IncrementalFileSystemControlParcel = os::incremental::IncrementalFileSystemControlParcel;
55 
56 struct Constants {
57     static constexpr auto backing = "backing_store"sv;
58     static constexpr auto mount = "mount"sv;
59     static constexpr auto mountKeyPrefix = "MT_"sv;
60     static constexpr auto storagePrefix = "st"sv;
61     static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
62     static constexpr auto infoMdName = ".info"sv;
63     static constexpr auto readLogsDisabledMarkerName = ".readlogs_disabled"sv;
64     static constexpr auto libDir = "lib"sv;
65     static constexpr auto libSuffix = ".so"sv;
66     static constexpr auto blockSize = 4096;
67     static constexpr auto systemPackage = "android"sv;
68 
69     static constexpr auto userStatusDelay = 100ms;
70 
71     static constexpr auto progressUpdateInterval = 1000ms;
72     static constexpr auto perUidTimeoutOffset = progressUpdateInterval * 2;
73     static constexpr auto minPerUidTimeout = progressUpdateInterval * 3;
74 
75     // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays.
76     static constexpr auto healthyDataLoaderUptime = 10min;
77 
78     // For healthy DLs, we'll retry every ~5secs for ~10min
79     static constexpr auto bindRetryInterval = 5s;
80     static constexpr auto bindGracePeriod = 10min;
81 
82     static constexpr auto bindingTimeout = 1min;
83 
84     // 1s, 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs)
85     static constexpr auto minBindDelay = 1s;
86     static constexpr auto maxBindDelay = 10000s;
87     static constexpr auto bindDelayMultiplier = 10;
88     static constexpr auto bindDelayJitterDivider = 10;
89 
90     // Max interval after system invoked the DL when readlog collection can be enabled.
91     static constexpr auto readLogsMaxInterval = 2h;
92 
93     // How long should we wait till dataLoader reports destroyed.
94     static constexpr auto destroyTimeout = 10s;
95 
96     static constexpr auto anyStatus = INT_MIN;
97 };
98 
constants()99 static const Constants& constants() {
100     static constexpr Constants c;
101     return c;
102 }
103 
isPageAligned(IncFsSize s)104 static bool isPageAligned(IncFsSize s) {
105     return (s & (Constants::blockSize - 1)) == 0;
106 }
107 
getAlwaysEnableReadTimeoutsForSystemDataLoaders()108 static bool getAlwaysEnableReadTimeoutsForSystemDataLoaders() {
109     return android::base::
110             GetBoolProperty("debug.incremental.always_enable_read_timeouts_for_system_dataloaders",
111                             true);
112 }
113 
getEnableReadTimeoutsAfterInstall()114 static bool getEnableReadTimeoutsAfterInstall() {
115     return android::base::GetBoolProperty("debug.incremental.enable_read_timeouts_after_install",
116                                           true);
117 }
118 
getEnforceReadLogsMaxIntervalForSystemDataLoaders()119 static bool getEnforceReadLogsMaxIntervalForSystemDataLoaders() {
120     return android::base::GetBoolProperty("debug.incremental.enforce_readlogs_max_interval_for_"
121                                           "system_dataloaders",
122                                           false);
123 }
124 
getReadLogsMaxInterval()125 static Seconds getReadLogsMaxInterval() {
126     constexpr int limit = duration_cast<Seconds>(Constants::readLogsMaxInterval).count();
127     int readlogs_max_interval_secs =
128             std::min(limit,
129                      android::base::GetIntProperty<
130                              int>("debug.incremental.readlogs_max_interval_sec", limit));
131     return Seconds{readlogs_max_interval_secs};
132 }
133 
134 template <base::LogSeverity level = base::ERROR>
mkdirOrLog(std::string_view name,int mode=0770,bool allowExisting=true)135 bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
136     auto cstr = path::c_str(name);
137     if (::mkdir(cstr, mode)) {
138         if (!allowExisting || errno != EEXIST) {
139             PLOG(level) << "Can't create directory '" << name << '\'';
140             return false;
141         }
142         struct stat st;
143         if (::stat(cstr, &st) || !S_ISDIR(st.st_mode)) {
144             PLOG(level) << "Path exists but is not a directory: '" << name << '\'';
145             return false;
146         }
147     }
148     if (::chmod(cstr, mode)) {
149         PLOG(level) << "Changing permission failed for '" << name << '\'';
150         return false;
151     }
152 
153     return true;
154 }
155 
toMountKey(std::string_view path)156 static std::string toMountKey(std::string_view path) {
157     if (path.empty()) {
158         return "@none";
159     }
160     if (path == "/"sv) {
161         return "@root";
162     }
163     if (path::isAbsolute(path)) {
164         path.remove_prefix(1);
165     }
166     if (path.size() > 16) {
167         path = path.substr(0, 16);
168     }
169     std::string res(path);
170     std::replace_if(
171             res.begin(), res.end(), [](char c) { return c == '/' || c == '@'; }, '_');
172     return std::string(constants().mountKeyPrefix) += res;
173 }
174 
makeMountDir(std::string_view incrementalDir,std::string_view path)175 static std::pair<std::string, std::string> makeMountDir(std::string_view incrementalDir,
176                                                         std::string_view path) {
177     auto mountKey = toMountKey(path);
178     const auto prefixSize = mountKey.size();
179     for (int counter = 0; counter < 1000;
180          mountKey.resize(prefixSize), base::StringAppendF(&mountKey, "%d", counter++)) {
181         auto mountRoot = path::join(incrementalDir, mountKey);
182         if (mkdirOrLog(mountRoot, 0777, false)) {
183             return {mountKey, mountRoot};
184         }
185     }
186     return {};
187 }
188 
189 template <class Map>
findParentPath(const Map & map,std::string_view path)190 typename Map::const_iterator findParentPath(const Map& map, std::string_view path) {
191     const auto nextIt = map.upper_bound(path);
192     if (nextIt == map.begin()) {
193         return map.end();
194     }
195     const auto suspectIt = std::prev(nextIt);
196     if (!path::startsWith(path, suspectIt->first)) {
197         return map.end();
198     }
199     return suspectIt;
200 }
201 
dup(base::borrowed_fd fd)202 static base::unique_fd dup(base::borrowed_fd fd) {
203     const auto res = fcntl(fd.get(), F_DUPFD_CLOEXEC, 0);
204     return base::unique_fd(res);
205 }
206 
207 template <class ProtoMessage, class Control>
parseFromIncfs(const IncFsWrapper * incfs,const Control & control,std::string_view path)208 static ProtoMessage parseFromIncfs(const IncFsWrapper* incfs, const Control& control,
209                                    std::string_view path) {
210     auto md = incfs->getMetadata(control, path);
211     ProtoMessage message;
212     return message.ParseFromArray(md.data(), md.size()) ? message : ProtoMessage{};
213 }
214 
isValidMountTarget(std::string_view path)215 static bool isValidMountTarget(std::string_view path) {
216     return path::isAbsolute(path) && path::isEmptyDir(path).value_or(true);
217 }
218 
makeUniqueName(std::string_view prefix)219 std::string makeUniqueName(std::string_view prefix) {
220     static constexpr auto uuidStringSize = 36;
221 
222     uuid_t guid;
223     uuid_generate(guid);
224 
225     std::string name;
226     const auto prefixSize = prefix.size();
227     name.reserve(prefixSize + uuidStringSize);
228 
229     name = prefix;
230     name.resize(prefixSize + uuidStringSize);
231     uuid_unparse(guid, name.data() + prefixSize);
232 
233     return name;
234 }
235 
makeBindMdName()236 std::string makeBindMdName() {
237     return makeUniqueName(constants().mountpointMdPrefix);
238 }
239 
checkReadLogsDisabledMarker(std::string_view root)240 static bool checkReadLogsDisabledMarker(std::string_view root) {
241     const auto markerPath = path::c_str(path::join(root, constants().readLogsDisabledMarkerName));
242     struct stat st;
243     return (::stat(markerPath, &st) == 0);
244 }
245 
246 } // namespace
247 
~IncFsMount()248 IncrementalService::IncFsMount::~IncFsMount() {
249     if (dataLoaderStub) {
250         dataLoaderStub->cleanupResources();
251         dataLoaderStub = {};
252     }
253     control.close();
254     LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
255     for (auto&& [target, _] : bindPoints) {
256         LOG(INFO) << "  bind: " << target;
257         incrementalService.mVold->unmountIncFs(target);
258     }
259     LOG(INFO) << "  root: " << root;
260     incrementalService.mVold->unmountIncFs(path::join(root, constants().mount));
261     cleanupFilesystem(root);
262 }
263 
makeStorage(StorageId id)264 auto IncrementalService::IncFsMount::makeStorage(StorageId id) -> StorageMap::iterator {
265     std::string name;
266     for (int no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), i = 0;
267          i < 1024 && no >= 0; no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), ++i) {
268         name.clear();
269         base::StringAppendF(&name, "%.*s_%d_%d", int(constants().storagePrefix.size()),
270                             constants().storagePrefix.data(), id, no);
271         auto fullName = path::join(root, constants().mount, name);
272         if (auto err = incrementalService.mIncFs->makeDir(control, fullName, 0755); !err) {
273             std::lock_guard l(lock);
274             return storages.insert_or_assign(id, Storage{std::move(fullName)}).first;
275         } else if (err != EEXIST) {
276             LOG(ERROR) << __func__ << "(): failed to create dir |" << fullName << "| " << err;
277             break;
278         }
279     }
280     nextStorageDirNo = 0;
281     return storages.end();
282 }
283 
284 template <class Func>
makeCleanup(Func && f)285 static auto makeCleanup(Func&& f) requires(!std::is_lvalue_reference_v<Func>) {
286     // ok to move a 'forwarding' reference here as lvalues are disabled anyway
287     auto deleter = [f = std::move(f)](auto) { // NOLINT
288         f();
289     };
290     // &f is a dangling pointer here, but we actually never use it as deleter moves it in.
291     return std::unique_ptr<Func, decltype(deleter)>(&f, std::move(deleter));
292 }
293 
openDir(const char * dir)294 static auto openDir(const char* dir) {
295     struct DirCloser {
296         void operator()(DIR* d) const noexcept { ::closedir(d); }
297     };
298     return std::unique_ptr<DIR, DirCloser>(::opendir(dir));
299 }
300 
openDir(std::string_view dir)301 static auto openDir(std::string_view dir) {
302     return openDir(path::c_str(dir));
303 }
304 
rmDirContent(const char * path)305 static int rmDirContent(const char* path) {
306     auto dir = openDir(path);
307     if (!dir) {
308         return -EINVAL;
309     }
310     while (auto entry = ::readdir(dir.get())) {
311         if (entry->d_name == "."sv || entry->d_name == ".."sv) {
312             continue;
313         }
314         auto fullPath = base::StringPrintf("%s/%s", path, entry->d_name);
315         if (entry->d_type == DT_DIR) {
316             if (const auto err = rmDirContent(fullPath.c_str()); err != 0) {
317                 PLOG(WARNING) << "Failed to delete " << fullPath << " content";
318                 return err;
319             }
320             if (const auto err = ::rmdir(fullPath.c_str()); err != 0) {
321                 PLOG(WARNING) << "Failed to rmdir " << fullPath;
322                 return err;
323             }
324         } else {
325             if (const auto err = ::unlink(fullPath.c_str()); err != 0) {
326                 PLOG(WARNING) << "Failed to delete " << fullPath;
327                 return err;
328             }
329         }
330     }
331     return 0;
332 }
333 
cleanupFilesystem(std::string_view root)334 void IncrementalService::IncFsMount::cleanupFilesystem(std::string_view root) {
335     rmDirContent(path::join(root, constants().backing).c_str());
336     ::rmdir(path::join(root, constants().backing).c_str());
337     ::rmdir(path::join(root, constants().mount).c_str());
338     ::rmdir(path::c_str(root));
339 }
340 
setFlag(StorageFlags flag,bool value)341 void IncrementalService::IncFsMount::setFlag(StorageFlags flag, bool value) {
342     if (value) {
343         flags |= flag;
344     } else {
345         flags &= ~flag;
346     }
347 }
348 
IncrementalService(ServiceManagerWrapper && sm,std::string_view rootDir)349 IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir)
350       : mVold(sm.getVoldService()),
351         mDataLoaderManager(sm.getDataLoaderManager()),
352         mIncFs(sm.getIncFs()),
353         mAppOpsManager(sm.getAppOpsManager()),
354         mJni(sm.getJni()),
355         mLooper(sm.getLooper()),
356         mTimedQueue(sm.getTimedQueue()),
357         mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()),
358         mFs(sm.getFs()),
359         mClock(sm.getClock()),
360         mIncrementalDir(rootDir) {
361     CHECK(mVold) << "Vold service is unavailable";
362     CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable";
363     CHECK(mAppOpsManager) << "AppOpsManager is unavailable";
364     CHECK(mJni) << "JNI is unavailable";
365     CHECK(mLooper) << "Looper is unavailable";
366     CHECK(mTimedQueue) << "TimedQueue is unavailable";
367     CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable";
368     CHECK(mFs) << "Fs is unavailable";
369     CHECK(mClock) << "Clock is unavailable";
370 
371     mJobQueue.reserve(16);
372     mJobProcessor = std::thread([this]() {
373         mJni->initializeForCurrentThread();
374         runJobProcessing();
375     });
376     mCmdLooperThread = std::thread([this]() {
377         mJni->initializeForCurrentThread();
378         runCmdLooper();
379     });
380 
381     const auto mountedRootNames = adoptMountedInstances();
382     mountExistingImages(mountedRootNames);
383 }
384 
~IncrementalService()385 IncrementalService::~IncrementalService() {
386     {
387         std::lock_guard lock(mJobMutex);
388         mRunning = false;
389     }
390     mJobCondition.notify_all();
391     mJobProcessor.join();
392     mLooper->wake();
393     mCmdLooperThread.join();
394     mTimedQueue->stop();
395     mProgressUpdateJobQueue->stop();
396     // Ensure that mounts are destroyed while the service is still valid.
397     mBindsByPath.clear();
398     mMounts.clear();
399 }
400 
toString(IncrementalService::BindKind kind)401 static const char* toString(IncrementalService::BindKind kind) {
402     switch (kind) {
403         case IncrementalService::BindKind::Temporary:
404             return "Temporary";
405         case IncrementalService::BindKind::Permanent:
406             return "Permanent";
407     }
408 }
409 
410 template <class Duration>
elapsedMcs(Duration start,Duration end)411 static int64_t elapsedMcs(Duration start, Duration end) {
412     return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
413 }
414 
elapsedUsSinceMonoTs(uint64_t monoTsUs)415 int64_t IncrementalService::elapsedUsSinceMonoTs(uint64_t monoTsUs) {
416     const auto now = mClock->now();
417     const auto nowUs = static_cast<uint64_t>(
418             duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count());
419     return nowUs - monoTsUs;
420 }
421 
loadingStateToString(incfs::LoadingState state)422 static const char* loadingStateToString(incfs::LoadingState state) {
423     switch (state) {
424         case (incfs::LoadingState::Full):
425             return "Full";
426         case (incfs::LoadingState::MissingBlocks):
427             return "MissingBlocks";
428         default:
429             return "error obtaining loading state";
430     }
431 }
432 
onDump(int fd)433 void IncrementalService::onDump(int fd) {
434     dprintf(fd, "Incremental is %s\n", incfs::enabled() ? "ENABLED" : "DISABLED");
435     dprintf(fd, "IncFs features: 0x%x\n", int(mIncFs->features()));
436     dprintf(fd, "Incremental dir: %s\n", mIncrementalDir.c_str());
437 
438     std::unique_lock l(mLock);
439 
440     dprintf(fd, "Mounts (%d): {\n", int(mMounts.size()));
441     for (auto&& [id, ifs] : mMounts) {
442         std::unique_lock ll(ifs->lock);
443         const IncFsMount& mnt = *ifs;
444         dprintf(fd, "  [%d]: {\n", id);
445         if (id != mnt.mountId) {
446             dprintf(fd, "    reference to mountId: %d\n", mnt.mountId);
447         } else {
448             dprintf(fd, "    mountId: %d\n", mnt.mountId);
449             dprintf(fd, "    root: %s\n", mnt.root.c_str());
450             const auto& metricsInstanceName = ifs->metricsKey;
451             dprintf(fd, "    metrics instance name: %s\n", path::c_str(metricsInstanceName).get());
452             dprintf(fd, "    nextStorageDirNo: %d\n", mnt.nextStorageDirNo.load());
453             dprintf(fd, "    flags: %d\n", int(mnt.flags));
454             if (mnt.startLoadingTs.time_since_epoch() == Clock::duration::zero()) {
455                 dprintf(fd, "    not loading\n");
456             } else {
457                 dprintf(fd, "    startLoading: %llds\n",
458                         (long long)(elapsedMcs(mnt.startLoadingTs, Clock::now()) / 1000000));
459             }
460             if (mnt.dataLoaderStub) {
461                 mnt.dataLoaderStub->onDump(fd);
462             } else {
463                 dprintf(fd, "    dataLoader: null\n");
464             }
465             dprintf(fd, "    storages (%d): {\n", int(mnt.storages.size()));
466             for (auto&& [storageId, storage] : mnt.storages) {
467                 auto&& ifs = getIfsLocked(storageId);
468                 dprintf(fd, "      [%d] -> [%s] (%d %% loaded)(%s) \n", storageId,
469                         storage.name.c_str(),
470                         (int)(getLoadingProgressFromPath(mnt, storage.name.c_str()).getProgress() *
471                               100),
472                         ifs ? loadingStateToString(mIncFs->isEverythingFullyLoaded(ifs->control))
473                             : "error obtaining ifs");
474             }
475             dprintf(fd, "    }\n");
476 
477             dprintf(fd, "    bindPoints (%d): {\n", int(mnt.bindPoints.size()));
478             for (auto&& [target, bind] : mnt.bindPoints) {
479                 dprintf(fd, "      [%s]->[%d]:\n", target.c_str(), bind.storage);
480                 dprintf(fd, "        savedFilename: %s\n", bind.savedFilename.c_str());
481                 dprintf(fd, "        sourceDir: %s\n", bind.sourceDir.c_str());
482                 dprintf(fd, "        kind: %s\n", toString(bind.kind));
483             }
484             dprintf(fd, "    }\n");
485 
486             dprintf(fd, "    incfsMetrics: {\n");
487             const auto incfsMetrics = mIncFs->getMetrics(metricsInstanceName);
488             if (incfsMetrics) {
489                 dprintf(fd, "      readsDelayedMin: %d\n", incfsMetrics.value().readsDelayedMin);
490                 dprintf(fd, "      readsDelayedMinUs: %lld\n",
491                         (long long)incfsMetrics.value().readsDelayedMinUs);
492                 dprintf(fd, "      readsDelayedPending: %d\n",
493                         incfsMetrics.value().readsDelayedPending);
494                 dprintf(fd, "      readsDelayedPendingUs: %lld\n",
495                         (long long)incfsMetrics.value().readsDelayedPendingUs);
496                 dprintf(fd, "      readsFailedHashVerification: %d\n",
497                         incfsMetrics.value().readsFailedHashVerification);
498                 dprintf(fd, "      readsFailedOther: %d\n", incfsMetrics.value().readsFailedOther);
499                 dprintf(fd, "      readsFailedTimedOut: %d\n",
500                         incfsMetrics.value().readsFailedTimedOut);
501             } else {
502                 dprintf(fd, "      Metrics not available. Errno: %d\n", errno);
503             }
504             dprintf(fd, "    }\n");
505 
506             const auto lastReadError = mIncFs->getLastReadError(ifs->control);
507             const auto errorNo = errno;
508             dprintf(fd, "    lastReadError: {\n");
509             if (lastReadError) {
510                 if (lastReadError->timestampUs == 0) {
511                     dprintf(fd, "      No read errors.\n");
512                 } else {
513                     dprintf(fd, "      fileId: %s\n",
514                             IncFsWrapper::toString(lastReadError->id).c_str());
515                     dprintf(fd, "      time: %llu microseconds ago\n",
516                             (unsigned long long)elapsedUsSinceMonoTs(lastReadError->timestampUs));
517                     dprintf(fd, "      blockIndex: %d\n", lastReadError->block);
518                     dprintf(fd, "      errno: %d\n", lastReadError->errorNo);
519                 }
520             } else {
521                 dprintf(fd, "      Info not available. Errno: %d\n", errorNo);
522             }
523             dprintf(fd, "    }\n");
524         }
525         dprintf(fd, "  }\n");
526     }
527     dprintf(fd, "}\n");
528     dprintf(fd, "Sorted binds (%d): {\n", int(mBindsByPath.size()));
529     for (auto&& [target, mountPairIt] : mBindsByPath) {
530         const auto& bind = mountPairIt->second;
531         dprintf(fd, "    [%s]->[%d]:\n", target.c_str(), bind.storage);
532         dprintf(fd, "      savedFilename: %s\n", bind.savedFilename.c_str());
533         dprintf(fd, "      sourceDir: %s\n", bind.sourceDir.c_str());
534         dprintf(fd, "      kind: %s\n", toString(bind.kind));
535     }
536     dprintf(fd, "}\n");
537 }
538 
needStartDataLoaderLocked(IncFsMount & ifs)539 bool IncrementalService::needStartDataLoaderLocked(IncFsMount& ifs) {
540     if (!ifs.dataLoaderStub) {
541         return false;
542     }
543     if (ifs.dataLoaderStub->isSystemDataLoader()) {
544         return true;
545     }
546 
547     return mIncFs->isEverythingFullyLoaded(ifs.control) == incfs::LoadingState::MissingBlocks;
548 }
549 
onSystemReady()550 void IncrementalService::onSystemReady() {
551     if (mSystemReady.exchange(true)) {
552         return;
553     }
554 
555     std::vector<IfsMountPtr> mounts;
556     {
557         std::lock_guard l(mLock);
558         mounts.reserve(mMounts.size());
559         for (auto&& [id, ifs] : mMounts) {
560             std::unique_lock ll(ifs->lock);
561 
562             if (ifs->mountId != id) {
563                 continue;
564             }
565 
566             if (needStartDataLoaderLocked(*ifs)) {
567                 mounts.push_back(ifs);
568             }
569         }
570     }
571 
572     if (mounts.empty()) {
573         return;
574     }
575 
576     std::thread([this, mounts = std::move(mounts)]() {
577         mJni->initializeForCurrentThread();
578         for (auto&& ifs : mounts) {
579             std::unique_lock l(ifs->lock);
580             if (ifs->dataLoaderStub) {
581                 ifs->dataLoaderStub->requestStart();
582             }
583         }
584     }).detach();
585 }
586 
getStorageSlotLocked()587 auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator {
588     for (;;) {
589         if (mNextId == kMaxStorageId) {
590             mNextId = 0;
591         }
592         auto id = ++mNextId;
593         auto [it, inserted] = mMounts.try_emplace(id, nullptr);
594         if (inserted) {
595             return it;
596         }
597     }
598 }
599 
createStorage(std::string_view mountPoint,content::pm::DataLoaderParamsParcel dataLoaderParams,CreateOptions options)600 StorageId IncrementalService::createStorage(std::string_view mountPoint,
601                                             content::pm::DataLoaderParamsParcel dataLoaderParams,
602                                             CreateOptions options) {
603     LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
604     if (!path::isAbsolute(mountPoint)) {
605         LOG(ERROR) << "path is not absolute: " << mountPoint;
606         return kInvalidStorageId;
607     }
608 
609     auto mountNorm = path::normalize(mountPoint);
610     {
611         const auto id = findStorageId(mountNorm);
612         if (id != kInvalidStorageId) {
613             if (options & CreateOptions::OpenExisting) {
614                 LOG(INFO) << "Opened existing storage " << id;
615                 return id;
616             }
617             LOG(ERROR) << "Directory " << mountPoint << " is already mounted at storage " << id;
618             return kInvalidStorageId;
619         }
620     }
621 
622     if (!(options & CreateOptions::CreateNew)) {
623         LOG(ERROR) << "not requirested create new storage, and it doesn't exist: " << mountPoint;
624         return kInvalidStorageId;
625     }
626 
627     if (!path::isEmptyDir(mountNorm)) {
628         LOG(ERROR) << "Mounting over existing non-empty directory is not supported: " << mountNorm;
629         return kInvalidStorageId;
630     }
631     auto [mountKey, mountRoot] = makeMountDir(mIncrementalDir, mountNorm);
632     if (mountRoot.empty()) {
633         LOG(ERROR) << "Bad mount point";
634         return kInvalidStorageId;
635     }
636     // Make sure the code removes all crap it may create while still failing.
637     auto firstCleanup = [](const std::string* ptr) { IncFsMount::cleanupFilesystem(*ptr); };
638     auto firstCleanupOnFailure =
639             std::unique_ptr<std::string, decltype(firstCleanup)>(&mountRoot, firstCleanup);
640 
641     auto mountTarget = path::join(mountRoot, constants().mount);
642     const auto backing = path::join(mountRoot, constants().backing);
643     if (!mkdirOrLog(backing, 0777) || !mkdirOrLog(mountTarget)) {
644         return kInvalidStorageId;
645     }
646 
647     std::string metricsKey;
648     IncFsMount::Control control;
649     {
650         std::lock_guard l(mMountOperationLock);
651         IncrementalFileSystemControlParcel controlParcel;
652 
653         if (auto err = rmDirContent(backing.c_str())) {
654             LOG(ERROR) << "Coudn't clean the backing directory " << backing << ": " << err;
655             return kInvalidStorageId;
656         }
657         if (!mkdirOrLog(path::join(backing, ".index"), 0777)) {
658             return kInvalidStorageId;
659         }
660         if (!mkdirOrLog(path::join(backing, ".incomplete"), 0777)) {
661             return kInvalidStorageId;
662         }
663         metricsKey = makeUniqueName(mountKey);
664         auto status = mVold->mountIncFs(backing, mountTarget, 0, metricsKey, &controlParcel);
665         if (!status.isOk()) {
666             LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
667             return kInvalidStorageId;
668         }
669         if (controlParcel.cmd.get() < 0 || controlParcel.pendingReads.get() < 0 ||
670             controlParcel.log.get() < 0) {
671             LOG(ERROR) << "Vold::mountIncFs() returned invalid control parcel.";
672             return kInvalidStorageId;
673         }
674         int cmd = controlParcel.cmd.release().release();
675         int pendingReads = controlParcel.pendingReads.release().release();
676         int logs = controlParcel.log.release().release();
677         int blocksWritten =
678                 controlParcel.blocksWritten ? controlParcel.blocksWritten->release().release() : -1;
679         control = mIncFs->createControl(cmd, pendingReads, logs, blocksWritten);
680     }
681 
682     std::unique_lock l(mLock);
683     const auto mountIt = getStorageSlotLocked();
684     const auto mountId = mountIt->first;
685     l.unlock();
686 
687     auto ifs = std::make_shared<IncFsMount>(std::move(mountRoot), std::move(metricsKey), mountId,
688                                             std::move(control), *this);
689     // Now it's the |ifs|'s responsibility to clean up after itself, and the only cleanup we need
690     // is the removal of the |ifs|.
691     (void)firstCleanupOnFailure.release();
692 
693     auto secondCleanup = [this, &l](auto itPtr) {
694         if (!l.owns_lock()) {
695             l.lock();
696         }
697         mMounts.erase(*itPtr);
698     };
699     auto secondCleanupOnFailure =
700             std::unique_ptr<decltype(mountIt), decltype(secondCleanup)>(&mountIt, secondCleanup);
701 
702     const auto storageIt = ifs->makeStorage(ifs->mountId);
703     if (storageIt == ifs->storages.end()) {
704         LOG(ERROR) << "Can't create a default storage directory";
705         return kInvalidStorageId;
706     }
707 
708     {
709         metadata::Mount m;
710         m.mutable_storage()->set_id(ifs->mountId);
711         m.mutable_loader()->set_type((int)dataLoaderParams.type);
712         m.mutable_loader()->set_package_name(std::move(dataLoaderParams.packageName));
713         m.mutable_loader()->set_class_name(std::move(dataLoaderParams.className));
714         m.mutable_loader()->set_arguments(std::move(dataLoaderParams.arguments));
715         const auto metadata = m.SerializeAsString();
716         if (auto err =
717                     mIncFs->makeFile(ifs->control,
718                                      path::join(ifs->root, constants().mount,
719                                                 constants().infoMdName),
720                                      0777, idFromMetadata(metadata),
721                                      {.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
722             LOG(ERROR) << "Saving mount metadata failed: " << -err;
723             return kInvalidStorageId;
724         }
725     }
726 
727     const auto bk =
728             (options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
729     if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
730                                 std::string(storageIt->second.name), std::move(mountNorm), bk, l);
731         err < 0) {
732         LOG(ERROR) << "Adding bind mount failed: " << -err;
733         return kInvalidStorageId;
734     }
735 
736     // Done here as well, all data structures are in good state.
737     (void)secondCleanupOnFailure.release();
738 
739     mountIt->second = std::move(ifs);
740     l.unlock();
741 
742     LOG(INFO) << "created storage " << mountId;
743     return mountId;
744 }
745 
createLinkedStorage(std::string_view mountPoint,StorageId linkedStorage,IncrementalService::CreateOptions options)746 StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint,
747                                                   StorageId linkedStorage,
748                                                   IncrementalService::CreateOptions options) {
749     if (!isValidMountTarget(mountPoint)) {
750         LOG(ERROR) << "Mount point is invalid or missing";
751         return kInvalidStorageId;
752     }
753 
754     std::unique_lock l(mLock);
755     auto ifs = getIfsLocked(linkedStorage);
756     if (!ifs) {
757         LOG(ERROR) << "Ifs unavailable";
758         return kInvalidStorageId;
759     }
760 
761     const auto mountIt = getStorageSlotLocked();
762     const auto storageId = mountIt->first;
763     const auto storageIt = ifs->makeStorage(storageId);
764     if (storageIt == ifs->storages.end()) {
765         LOG(ERROR) << "Can't create a new storage";
766         mMounts.erase(mountIt);
767         return kInvalidStorageId;
768     }
769 
770     l.unlock();
771 
772     const auto bk =
773             (options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
774     if (auto err = addBindMount(*ifs, storageIt->first, storageIt->second.name,
775                                 std::string(storageIt->second.name), path::normalize(mountPoint),
776                                 bk, l);
777         err < 0) {
778         LOG(ERROR) << "bindMount failed with error: " << err;
779         (void)mIncFs->unlink(ifs->control, storageIt->second.name);
780         ifs->storages.erase(storageIt);
781         return kInvalidStorageId;
782     }
783 
784     mountIt->second = ifs;
785     return storageId;
786 }
787 
startLoading(StorageId storageId,content::pm::DataLoaderParamsParcel dataLoaderParams,DataLoaderStatusListener statusListener,const StorageHealthCheckParams & healthCheckParams,StorageHealthListener healthListener,std::vector<PerUidReadTimeouts> perUidReadTimeouts)788 bool IncrementalService::startLoading(StorageId storageId,
789                                       content::pm::DataLoaderParamsParcel dataLoaderParams,
790                                       DataLoaderStatusListener statusListener,
791                                       const StorageHealthCheckParams& healthCheckParams,
792                                       StorageHealthListener healthListener,
793                                       std::vector<PerUidReadTimeouts> perUidReadTimeouts) {
794     // Per Uid timeouts.
795     if (!perUidReadTimeouts.empty()) {
796         setUidReadTimeouts(storageId, std::move(perUidReadTimeouts));
797     }
798 
799     IfsMountPtr ifs;
800     DataLoaderStubPtr dataLoaderStub;
801 
802     // Re-initialize DataLoader.
803     {
804         ifs = getIfs(storageId);
805         if (!ifs) {
806             return false;
807         }
808 
809         std::unique_lock l(ifs->lock);
810         dataLoaderStub = std::exchange(ifs->dataLoaderStub, nullptr);
811     }
812 
813     if (dataLoaderStub) {
814         dataLoaderStub->cleanupResources();
815         dataLoaderStub = {};
816     }
817 
818     {
819         std::unique_lock l(ifs->lock);
820         if (ifs->dataLoaderStub) {
821             LOG(INFO) << "Skipped data loader stub creation because it already exists";
822             return false;
823         }
824 
825         prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams), std::move(statusListener),
826                                 healthCheckParams, std::move(healthListener));
827         CHECK(ifs->dataLoaderStub);
828         dataLoaderStub = ifs->dataLoaderStub;
829 
830         // Disable long read timeouts for non-system dataloaders.
831         // To be re-enabled after installation is complete.
832         ifs->setReadTimeoutsRequested(dataLoaderStub->isSystemDataLoader() &&
833                                       getAlwaysEnableReadTimeoutsForSystemDataLoaders());
834         applyStorageParamsLocked(*ifs);
835     }
836 
837     if (dataLoaderStub->isSystemDataLoader() &&
838         !getEnforceReadLogsMaxIntervalForSystemDataLoaders()) {
839         // Readlogs from system dataloader (adb) can always be collected.
840         ifs->startLoadingTs = TimePoint::max();
841     } else {
842         // Assign time when installation wants the DL to start streaming.
843         const auto startLoadingTs = mClock->now();
844         ifs->startLoadingTs = startLoadingTs;
845         // Setup a callback to disable the readlogs after max interval.
846         addTimedJob(*mTimedQueue, storageId, getReadLogsMaxInterval(),
847                     [this, storageId, startLoadingTs]() {
848                         const auto ifs = getIfs(storageId);
849                         if (!ifs) {
850                             LOG(WARNING) << "Can't disable the readlogs, invalid storageId: "
851                                          << storageId;
852                             return;
853                         }
854                         std::unique_lock l(ifs->lock);
855                         if (ifs->startLoadingTs != startLoadingTs) {
856                             LOG(INFO) << "Can't disable the readlogs, timestamp mismatch (new "
857                                          "installation?): "
858                                       << storageId;
859                             return;
860                         }
861                         disableReadLogsLocked(*ifs);
862                     });
863     }
864 
865     return dataLoaderStub->requestStart();
866 }
867 
onInstallationComplete(StorageId storage)868 void IncrementalService::onInstallationComplete(StorageId storage) {
869     IfsMountPtr ifs = getIfs(storage);
870     if (!ifs) {
871         return;
872     }
873 
874     // Always enable long read timeouts after installation is complete.
875     std::unique_lock l(ifs->lock);
876     ifs->setReadTimeoutsRequested(getEnableReadTimeoutsAfterInstall());
877     applyStorageParamsLocked(*ifs);
878 }
879 
findStorageLocked(std::string_view path) const880 IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
881         std::string_view path) const {
882     return findParentPath(mBindsByPath, path);
883 }
884 
findStorageId(std::string_view path) const885 StorageId IncrementalService::findStorageId(std::string_view path) const {
886     std::lock_guard l(mLock);
887     auto it = findStorageLocked(path);
888     if (it == mBindsByPath.end()) {
889         return kInvalidStorageId;
890     }
891     return it->second->second.storage;
892 }
893 
disallowReadLogs(StorageId storageId)894 void IncrementalService::disallowReadLogs(StorageId storageId) {
895     const auto ifs = getIfs(storageId);
896     if (!ifs) {
897         LOG(ERROR) << "disallowReadLogs failed, invalid storageId: " << storageId;
898         return;
899     }
900 
901     std::unique_lock l(ifs->lock);
902     if (!ifs->readLogsAllowed()) {
903         return;
904     }
905     ifs->disallowReadLogs();
906 
907     const auto metadata = constants().readLogsDisabledMarkerName;
908     if (auto err = mIncFs->makeFile(ifs->control,
909                                     path::join(ifs->root, constants().mount,
910                                                constants().readLogsDisabledMarkerName),
911                                     0777, idFromMetadata(metadata), {})) {
912         //{.metadata = {metadata.data(), (IncFsSize)metadata.size()}})) {
913         LOG(ERROR) << "Failed to make marker file for storageId: " << storageId << " err: " << -err;
914         return;
915     }
916 
917     disableReadLogsLocked(*ifs);
918 }
919 
setStorageParams(StorageId storageId,bool enableReadLogs)920 int IncrementalService::setStorageParams(StorageId storageId, bool enableReadLogs) {
921     const auto ifs = getIfs(storageId);
922     if (!ifs) {
923         LOG(ERROR) << "setStorageParams failed, invalid storageId: " << storageId;
924         return -EINVAL;
925     }
926 
927     std::string packageName;
928 
929     {
930         std::unique_lock l(ifs->lock);
931         if (!enableReadLogs) {
932             return disableReadLogsLocked(*ifs);
933         }
934 
935         if (!ifs->readLogsAllowed()) {
936             LOG(ERROR) << "enableReadLogs failed, readlogs disallowed for storageId: " << storageId;
937             return -EPERM;
938         }
939 
940         if (!ifs->dataLoaderStub) {
941             // This should never happen - only DL can call enableReadLogs.
942             LOG(ERROR) << "enableReadLogs failed: invalid state";
943             return -EPERM;
944         }
945 
946         // Check installation time.
947         const auto now = mClock->now();
948         const auto startLoadingTs = ifs->startLoadingTs;
949         if (startLoadingTs <= now && now - startLoadingTs > getReadLogsMaxInterval()) {
950             LOG(ERROR)
951                     << "enableReadLogs failed, readlogs can't be enabled at this time, storageId: "
952                     << storageId;
953             return -EPERM;
954         }
955 
956         packageName = ifs->dataLoaderStub->params().packageName;
957         ifs->setReadLogsRequested(true);
958     }
959 
960     // Check loader usage stats permission and apop.
961     if (auto status =
962                 mAppOpsManager->checkPermission(kLoaderUsageStats, kOpUsage, packageName.c_str());
963         !status.isOk()) {
964         LOG(ERROR) << " Permission: " << kLoaderUsageStats
965                    << " check failed: " << status.toString8();
966         return fromBinderStatus(status);
967     }
968 
969     // Check multiuser permission.
970     if (auto status =
971                 mAppOpsManager->checkPermission(kInteractAcrossUsers, nullptr, packageName.c_str());
972         !status.isOk()) {
973         LOG(ERROR) << " Permission: " << kInteractAcrossUsers
974                    << " check failed: " << status.toString8();
975         return fromBinderStatus(status);
976     }
977 
978     {
979         std::unique_lock l(ifs->lock);
980         if (!ifs->readLogsRequested()) {
981             return 0;
982         }
983         if (auto status = applyStorageParamsLocked(*ifs); status != 0) {
984             return status;
985         }
986     }
987 
988     registerAppOpsCallback(packageName);
989 
990     return 0;
991 }
992 
disableReadLogsLocked(IncFsMount & ifs)993 int IncrementalService::disableReadLogsLocked(IncFsMount& ifs) {
994     ifs.setReadLogsRequested(false);
995     return applyStorageParamsLocked(ifs);
996 }
997 
applyStorageParamsLocked(IncFsMount & ifs)998 int IncrementalService::applyStorageParamsLocked(IncFsMount& ifs) {
999     os::incremental::IncrementalFileSystemControlParcel control;
1000     control.cmd.reset(dup(ifs.control.cmd()));
1001     control.pendingReads.reset(dup(ifs.control.pendingReads()));
1002     auto logsFd = ifs.control.logs();
1003     if (logsFd >= 0) {
1004         control.log.reset(dup(logsFd));
1005     }
1006 
1007     bool enableReadLogs = ifs.readLogsRequested();
1008     bool enableReadTimeouts = ifs.readTimeoutsRequested();
1009 
1010     std::lock_guard l(mMountOperationLock);
1011     auto status = mVold->setIncFsMountOptions(control, enableReadLogs, enableReadTimeouts,
1012                                               ifs.metricsKey);
1013     if (status.isOk()) {
1014         // Store states.
1015         ifs.setReadLogsEnabled(enableReadLogs);
1016         ifs.setReadTimeoutsEnabled(enableReadTimeouts);
1017     } else {
1018         LOG(ERROR) << "applyStorageParams failed: " << status.toString8();
1019     }
1020     return status.isOk() ? 0 : fromBinderStatus(status);
1021 }
1022 
deleteStorage(StorageId storageId)1023 void IncrementalService::deleteStorage(StorageId storageId) {
1024     const auto ifs = getIfs(storageId);
1025     if (!ifs) {
1026         return;
1027     }
1028     deleteStorage(*ifs);
1029 }
1030 
deleteStorage(IncrementalService::IncFsMount & ifs)1031 void IncrementalService::deleteStorage(IncrementalService::IncFsMount& ifs) {
1032     std::unique_lock l(ifs.lock);
1033     deleteStorageLocked(ifs, std::move(l));
1034 }
1035 
deleteStorageLocked(IncrementalService::IncFsMount & ifs,std::unique_lock<std::mutex> && ifsLock)1036 void IncrementalService::deleteStorageLocked(IncrementalService::IncFsMount& ifs,
1037                                              std::unique_lock<std::mutex>&& ifsLock) {
1038     const auto storages = std::move(ifs.storages);
1039     // Don't move the bind points out: Ifs's dtor will use them to unmount everything.
1040     const auto bindPoints = ifs.bindPoints;
1041     ifsLock.unlock();
1042 
1043     std::lock_guard l(mLock);
1044     for (auto&& [id, _] : storages) {
1045         if (id != ifs.mountId) {
1046             mMounts.erase(id);
1047         }
1048     }
1049     for (auto&& [path, _] : bindPoints) {
1050         mBindsByPath.erase(path);
1051     }
1052     mMounts.erase(ifs.mountId);
1053 }
1054 
openStorage(std::string_view pathInMount)1055 StorageId IncrementalService::openStorage(std::string_view pathInMount) {
1056     if (!path::isAbsolute(pathInMount)) {
1057         return kInvalidStorageId;
1058     }
1059 
1060     return findStorageId(path::normalize(pathInMount));
1061 }
1062 
getIfs(StorageId storage) const1063 IncrementalService::IfsMountPtr IncrementalService::getIfs(StorageId storage) const {
1064     std::lock_guard l(mLock);
1065     return getIfsLocked(storage);
1066 }
1067 
getIfsLocked(StorageId storage) const1068 const IncrementalService::IfsMountPtr& IncrementalService::getIfsLocked(StorageId storage) const {
1069     auto it = mMounts.find(storage);
1070     if (it == mMounts.end()) {
1071         static const base::NoDestructor<IfsMountPtr> kEmpty{};
1072         return *kEmpty;
1073     }
1074     return it->second;
1075 }
1076 
bind(StorageId storage,std::string_view source,std::string_view target,BindKind kind)1077 int IncrementalService::bind(StorageId storage, std::string_view source, std::string_view target,
1078                              BindKind kind) {
1079     if (!isValidMountTarget(target)) {
1080         LOG(ERROR) << __func__ << ": not a valid bind target " << target;
1081         return -EINVAL;
1082     }
1083 
1084     const auto ifs = getIfs(storage);
1085     if (!ifs) {
1086         LOG(ERROR) << __func__ << ": no ifs object for storage " << storage;
1087         return -EINVAL;
1088     }
1089 
1090     std::unique_lock l(ifs->lock);
1091     const auto storageInfo = ifs->storages.find(storage);
1092     if (storageInfo == ifs->storages.end()) {
1093         LOG(ERROR) << "no storage";
1094         return -EINVAL;
1095     }
1096     std::string normSource = normalizePathToStorageLocked(*ifs, storageInfo, source);
1097     if (normSource.empty()) {
1098         LOG(ERROR) << "invalid source path";
1099         return -EINVAL;
1100     }
1101     l.unlock();
1102     std::unique_lock l2(mLock, std::defer_lock);
1103     return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
1104                         path::normalize(target), kind, l2);
1105 }
1106 
unbind(StorageId storage,std::string_view target)1107 int IncrementalService::unbind(StorageId storage, std::string_view target) {
1108     if (!path::isAbsolute(target)) {
1109         return -EINVAL;
1110     }
1111 
1112     LOG(INFO) << "Removing bind point " << target << " for storage " << storage;
1113 
1114     // Here we should only look up by the exact target, not by a subdirectory of any existing mount,
1115     // otherwise there's a chance to unmount something completely unrelated
1116     const auto norm = path::normalize(target);
1117     std::unique_lock l(mLock);
1118     const auto storageIt = mBindsByPath.find(norm);
1119     if (storageIt == mBindsByPath.end() || storageIt->second->second.storage != storage) {
1120         return -EINVAL;
1121     }
1122     const auto bindIt = storageIt->second;
1123     const auto storageId = bindIt->second.storage;
1124     const auto ifs = getIfsLocked(storageId);
1125     if (!ifs) {
1126         LOG(ERROR) << "Internal error: storageId " << storageId << " for bound path " << target
1127                    << " is missing";
1128         return -EFAULT;
1129     }
1130     mBindsByPath.erase(storageIt);
1131     l.unlock();
1132 
1133     mVold->unmountIncFs(bindIt->first);
1134     std::unique_lock l2(ifs->lock);
1135     if (ifs->bindPoints.size() <= 1) {
1136         ifs->bindPoints.clear();
1137         deleteStorageLocked(*ifs, std::move(l2));
1138     } else {
1139         const std::string savedFile = std::move(bindIt->second.savedFilename);
1140         ifs->bindPoints.erase(bindIt);
1141         l2.unlock();
1142         if (!savedFile.empty()) {
1143             mIncFs->unlink(ifs->control, path::join(ifs->root, constants().mount, savedFile));
1144         }
1145     }
1146 
1147     return 0;
1148 }
1149 
normalizePathToStorageLocked(const IncFsMount & incfs,IncFsMount::StorageMap::const_iterator storageIt,std::string_view path) const1150 std::string IncrementalService::normalizePathToStorageLocked(
1151         const IncFsMount& incfs, IncFsMount::StorageMap::const_iterator storageIt,
1152         std::string_view path) const {
1153     if (!path::isAbsolute(path)) {
1154         return path::normalize(path::join(storageIt->second.name, path));
1155     }
1156     auto normPath = path::normalize(path);
1157     if (path::startsWith(normPath, storageIt->second.name)) {
1158         return normPath;
1159     }
1160     // not that easy: need to find if any of the bind points match
1161     const auto bindIt = findParentPath(incfs.bindPoints, normPath);
1162     if (bindIt == incfs.bindPoints.end()) {
1163         return {};
1164     }
1165     return path::join(bindIt->second.sourceDir, path::relativize(bindIt->first, normPath));
1166 }
1167 
normalizePathToStorage(const IncFsMount & ifs,StorageId storage,std::string_view path) const1168 std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, StorageId storage,
1169                                                        std::string_view path) const {
1170     std::unique_lock l(ifs.lock);
1171     const auto storageInfo = ifs.storages.find(storage);
1172     if (storageInfo == ifs.storages.end()) {
1173         return {};
1174     }
1175     return normalizePathToStorageLocked(ifs, storageInfo, path);
1176 }
1177 
makeFile(StorageId storage,std::string_view path,int mode,FileId id,incfs::NewFileParams params,std::span<const uint8_t> data)1178 int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
1179                                  incfs::NewFileParams params, std::span<const uint8_t> data) {
1180     const auto ifs = getIfs(storage);
1181     if (!ifs) {
1182         return -EINVAL;
1183     }
1184     if ((IncFsSize)data.size() > params.size) {
1185         LOG(ERROR) << "Bad data size - bigger than file size";
1186         return -EINVAL;
1187     }
1188     if (!data.empty() && (IncFsSize)data.size() != params.size) {
1189         // Writing a page is an irreversible operation, and it can't be updated with additional
1190         // data later. Check that the last written page is complete, or we may break the file.
1191         if (!isPageAligned(data.size())) {
1192             LOG(ERROR) << "Bad data size - tried to write half a page?";
1193             return -EINVAL;
1194         }
1195     }
1196     const std::string normPath = normalizePathToStorage(*ifs, storage, path);
1197     if (normPath.empty()) {
1198         LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
1199         return -EINVAL;
1200     }
1201     if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
1202         LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile [" << normPath
1203                    << "]: " << err;
1204         return err;
1205     }
1206     if (params.size > 0) {
1207         if (auto err = mIncFs->reserveSpace(ifs->control, id, params.size)) {
1208             if (err != -EOPNOTSUPP) {
1209                 LOG(ERROR) << "Failed to reserve space for a new file: " << err;
1210                 (void)mIncFs->unlink(ifs->control, normPath);
1211                 return err;
1212             } else {
1213                 LOG(WARNING) << "Reserving space for backing file isn't supported, "
1214                                 "may run out of disk later";
1215             }
1216         }
1217         if (!data.empty()) {
1218             if (auto err = setFileContent(ifs, id, path, data); err) {
1219                 (void)mIncFs->unlink(ifs->control, normPath);
1220                 return err;
1221             }
1222         }
1223     }
1224     return 0;
1225 }
1226 
makeDir(StorageId storageId,std::string_view path,int mode)1227 int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
1228     if (auto ifs = getIfs(storageId)) {
1229         std::string normPath = normalizePathToStorage(*ifs, storageId, path);
1230         if (normPath.empty()) {
1231             return -EINVAL;
1232         }
1233         return mIncFs->makeDir(ifs->control, normPath, mode);
1234     }
1235     return -EINVAL;
1236 }
1237 
makeDirs(StorageId storageId,std::string_view path,int mode)1238 int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int mode) {
1239     const auto ifs = getIfs(storageId);
1240     if (!ifs) {
1241         return -EINVAL;
1242     }
1243     return makeDirs(*ifs, storageId, path, mode);
1244 }
1245 
makeDirs(const IncFsMount & ifs,StorageId storageId,std::string_view path,int mode)1246 int IncrementalService::makeDirs(const IncFsMount& ifs, StorageId storageId, std::string_view path,
1247                                  int mode) {
1248     std::string normPath = normalizePathToStorage(ifs, storageId, path);
1249     if (normPath.empty()) {
1250         return -EINVAL;
1251     }
1252     return mIncFs->makeDirs(ifs.control, normPath, mode);
1253 }
1254 
link(StorageId sourceStorageId,std::string_view oldPath,StorageId destStorageId,std::string_view newPath)1255 int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
1256                              StorageId destStorageId, std::string_view newPath) {
1257     std::unique_lock l(mLock);
1258     auto ifsSrc = getIfsLocked(sourceStorageId);
1259     if (!ifsSrc) {
1260         return -EINVAL;
1261     }
1262     if (sourceStorageId != destStorageId && getIfsLocked(destStorageId) != ifsSrc) {
1263         return -EINVAL;
1264     }
1265     l.unlock();
1266     std::string normOldPath = normalizePathToStorage(*ifsSrc, sourceStorageId, oldPath);
1267     std::string normNewPath = normalizePathToStorage(*ifsSrc, destStorageId, newPath);
1268     if (normOldPath.empty() || normNewPath.empty()) {
1269         LOG(ERROR) << "Invalid paths in link(): " << normOldPath << " | " << normNewPath;
1270         return -EINVAL;
1271     }
1272     if (auto err = mIncFs->link(ifsSrc->control, normOldPath, normNewPath); err < 0) {
1273         PLOG(ERROR) << "Failed to link " << oldPath << "[" << normOldPath << "]"
1274                     << " to " << newPath << "[" << normNewPath << "]";
1275         return err;
1276     }
1277     return 0;
1278 }
1279 
unlink(StorageId storage,std::string_view path)1280 int IncrementalService::unlink(StorageId storage, std::string_view path) {
1281     if (auto ifs = getIfs(storage)) {
1282         std::string normOldPath = normalizePathToStorage(*ifs, storage, path);
1283         return mIncFs->unlink(ifs->control, normOldPath);
1284     }
1285     return -EINVAL;
1286 }
1287 
addBindMount(IncFsMount & ifs,StorageId storage,std::string_view storageRoot,std::string && source,std::string && target,BindKind kind,std::unique_lock<std::mutex> & mainLock)1288 int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage,
1289                                      std::string_view storageRoot, std::string&& source,
1290                                      std::string&& target, BindKind kind,
1291                                      std::unique_lock<std::mutex>& mainLock) {
1292     if (!isValidMountTarget(target)) {
1293         LOG(ERROR) << __func__ << ": invalid mount target " << target;
1294         return -EINVAL;
1295     }
1296 
1297     std::string mdFileName;
1298     std::string metadataFullPath;
1299     if (kind != BindKind::Temporary) {
1300         metadata::BindPoint bp;
1301         bp.set_storage_id(storage);
1302         bp.set_allocated_dest_path(&target);
1303         bp.set_allocated_source_subdir(&source);
1304         const auto metadata = bp.SerializeAsString();
1305         static_cast<void>(bp.release_dest_path());
1306         static_cast<void>(bp.release_source_subdir());
1307         mdFileName = makeBindMdName();
1308         metadataFullPath = path::join(ifs.root, constants().mount, mdFileName);
1309         auto node = mIncFs->makeFile(ifs.control, metadataFullPath, 0444, idFromMetadata(metadata),
1310                                      {.metadata = {metadata.data(), (IncFsSize)metadata.size()}});
1311         if (node) {
1312             LOG(ERROR) << __func__ << ": couldn't create a mount node " << mdFileName;
1313             return int(node);
1314         }
1315     }
1316 
1317     const auto res = addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(source),
1318                                         std::move(target), kind, mainLock);
1319     if (res) {
1320         mIncFs->unlink(ifs.control, metadataFullPath);
1321     }
1322     return res;
1323 }
1324 
addBindMountWithMd(IncrementalService::IncFsMount & ifs,StorageId storage,std::string && metadataName,std::string && source,std::string && target,BindKind kind,std::unique_lock<std::mutex> & mainLock)1325 int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs, StorageId storage,
1326                                            std::string&& metadataName, std::string&& source,
1327                                            std::string&& target, BindKind kind,
1328                                            std::unique_lock<std::mutex>& mainLock) {
1329     {
1330         std::lock_guard l(mMountOperationLock);
1331         const auto status = mVold->bindMount(source, target);
1332         if (!status.isOk()) {
1333             LOG(ERROR) << "Calling Vold::bindMount() failed: " << status.toString8();
1334             return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
1335                     ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode()
1336                                                             : status.serviceSpecificErrorCode() == 0
1337                                     ? -EFAULT
1338                                     : status.serviceSpecificErrorCode()
1339                     : -EIO;
1340         }
1341     }
1342 
1343     if (!mainLock.owns_lock()) {
1344         mainLock.lock();
1345     }
1346     std::lock_guard l(ifs.lock);
1347     addBindMountRecordLocked(ifs, storage, std::move(metadataName), std::move(source),
1348                              std::move(target), kind);
1349     return 0;
1350 }
1351 
addBindMountRecordLocked(IncFsMount & ifs,StorageId storage,std::string && metadataName,std::string && source,std::string && target,BindKind kind)1352 void IncrementalService::addBindMountRecordLocked(IncFsMount& ifs, StorageId storage,
1353                                                   std::string&& metadataName, std::string&& source,
1354                                                   std::string&& target, BindKind kind) {
1355     const auto [it, _] =
1356             ifs.bindPoints.insert_or_assign(target,
1357                                             IncFsMount::Bind{storage, std::move(metadataName),
1358                                                              std::move(source), kind});
1359     mBindsByPath[std::move(target)] = it;
1360 }
1361 
getMetadata(StorageId storage,std::string_view path) const1362 RawMetadata IncrementalService::getMetadata(StorageId storage, std::string_view path) const {
1363     const auto ifs = getIfs(storage);
1364     if (!ifs) {
1365         return {};
1366     }
1367     const auto normPath = normalizePathToStorage(*ifs, storage, path);
1368     if (normPath.empty()) {
1369         return {};
1370     }
1371     return mIncFs->getMetadata(ifs->control, normPath);
1372 }
1373 
getMetadata(StorageId storage,FileId node) const1374 RawMetadata IncrementalService::getMetadata(StorageId storage, FileId node) const {
1375     const auto ifs = getIfs(storage);
1376     if (!ifs) {
1377         return {};
1378     }
1379     return mIncFs->getMetadata(ifs->control, node);
1380 }
1381 
setUidReadTimeouts(StorageId storage,std::vector<PerUidReadTimeouts> && perUidReadTimeouts)1382 void IncrementalService::setUidReadTimeouts(StorageId storage,
1383                                             std::vector<PerUidReadTimeouts>&& perUidReadTimeouts) {
1384     using microseconds = std::chrono::microseconds;
1385     using milliseconds = std::chrono::milliseconds;
1386 
1387     auto maxPendingTimeUs = microseconds(0);
1388     for (const auto& timeouts : perUidReadTimeouts) {
1389         maxPendingTimeUs = std::max(maxPendingTimeUs, microseconds(timeouts.maxPendingTimeUs));
1390     }
1391     if (maxPendingTimeUs < Constants::minPerUidTimeout) {
1392         LOG(ERROR) << "Skip setting read timeouts (maxPendingTime < Constants::minPerUidTimeout): "
1393                    << duration_cast<milliseconds>(maxPendingTimeUs).count() << "ms < "
1394                    << Constants::minPerUidTimeout.count() << "ms";
1395         return;
1396     }
1397 
1398     const auto ifs = getIfs(storage);
1399     if (!ifs) {
1400         LOG(ERROR) << "Setting read timeouts failed: invalid storage id: " << storage;
1401         return;
1402     }
1403 
1404     if (auto err = mIncFs->setUidReadTimeouts(ifs->control, perUidReadTimeouts); err < 0) {
1405         LOG(ERROR) << "Setting read timeouts failed: " << -err;
1406         return;
1407     }
1408 
1409     const auto timeout = Clock::now() + maxPendingTimeUs - Constants::perUidTimeoutOffset;
1410     addIfsStateCallback(storage, [this, timeout](StorageId storageId, IfsState state) -> bool {
1411         if (checkUidReadTimeouts(storageId, state, timeout)) {
1412             return true;
1413         }
1414         clearUidReadTimeouts(storageId);
1415         return false;
1416     });
1417 }
1418 
clearUidReadTimeouts(StorageId storage)1419 void IncrementalService::clearUidReadTimeouts(StorageId storage) {
1420     const auto ifs = getIfs(storage);
1421     if (!ifs) {
1422         return;
1423     }
1424     mIncFs->setUidReadTimeouts(ifs->control, {});
1425 }
1426 
checkUidReadTimeouts(StorageId storage,IfsState state,Clock::time_point timeLimit)1427 bool IncrementalService::checkUidReadTimeouts(StorageId storage, IfsState state,
1428                                               Clock::time_point timeLimit) {
1429     if (Clock::now() >= timeLimit) {
1430         // Reached maximum timeout.
1431         return false;
1432     }
1433     if (state.error) {
1434         // Something is wrong, abort.
1435         return false;
1436     }
1437 
1438     // Still loading?
1439     if (state.fullyLoaded && !state.readLogsEnabled) {
1440         return false;
1441     }
1442 
1443     const auto timeLeft = timeLimit - Clock::now();
1444     if (timeLeft < Constants::progressUpdateInterval) {
1445         // Don't bother.
1446         return false;
1447     }
1448 
1449     return true;
1450 }
1451 
adoptMountedInstances()1452 std::unordered_set<std::string_view> IncrementalService::adoptMountedInstances() {
1453     std::unordered_set<std::string_view> mountedRootNames;
1454     mIncFs->listExistingMounts([this, &mountedRootNames](auto root, auto backingDir, auto binds) {
1455         LOG(INFO) << "Existing mount: " << backingDir << "->" << root;
1456         for (auto [source, target] : binds) {
1457             LOG(INFO) << "  bind: '" << source << "'->'" << target << "'";
1458             LOG(INFO) << "         " << path::join(root, source);
1459         }
1460 
1461         // Ensure it's a kind of a mount that's managed by IncrementalService
1462         if (path::basename(root) != constants().mount ||
1463             path::basename(backingDir) != constants().backing) {
1464             return;
1465         }
1466         const auto expectedRoot = path::dirname(root);
1467         if (path::dirname(backingDir) != expectedRoot) {
1468             return;
1469         }
1470         if (path::dirname(expectedRoot) != mIncrementalDir) {
1471             return;
1472         }
1473         if (!path::basename(expectedRoot).starts_with(constants().mountKeyPrefix)) {
1474             return;
1475         }
1476 
1477         LOG(INFO) << "Looks like an IncrementalService-owned: " << expectedRoot;
1478 
1479         // make sure we clean up the mount if it happens to be a bad one.
1480         // Note: unmounting needs to run first, so the cleanup object is created _last_.
1481         auto cleanupFiles = makeCleanup([&]() {
1482             LOG(INFO) << "Failed to adopt existing mount, deleting files: " << expectedRoot;
1483             IncFsMount::cleanupFilesystem(expectedRoot);
1484         });
1485         auto cleanupMounts = makeCleanup([&]() {
1486             LOG(INFO) << "Failed to adopt existing mount, cleaning up: " << expectedRoot;
1487             for (auto&& [_, target] : binds) {
1488                 mVold->unmountIncFs(std::string(target));
1489             }
1490             mVold->unmountIncFs(std::string(root));
1491         });
1492 
1493         auto control = mIncFs->openMount(root);
1494         if (!control) {
1495             LOG(INFO) << "failed to open mount " << root;
1496             return;
1497         }
1498 
1499         auto mountRecord =
1500                 parseFromIncfs<metadata::Mount>(mIncFs.get(), control,
1501                                                 path::join(root, constants().infoMdName));
1502         if (!mountRecord.has_loader() || !mountRecord.has_storage()) {
1503             LOG(ERROR) << "Bad mount metadata in mount at " << expectedRoot;
1504             return;
1505         }
1506 
1507         auto mountId = mountRecord.storage().id();
1508         mNextId = std::max(mNextId, mountId + 1);
1509 
1510         DataLoaderParamsParcel dataLoaderParams;
1511         {
1512             const auto& loader = mountRecord.loader();
1513             dataLoaderParams.type = (content::pm::DataLoaderType)loader.type();
1514             dataLoaderParams.packageName = loader.package_name();
1515             dataLoaderParams.className = loader.class_name();
1516             dataLoaderParams.arguments = loader.arguments();
1517         }
1518 
1519         // Not way to obtain a real sysfs key at this point - metrics will stop working after "soft"
1520         // reboot.
1521         std::string metricsKey{};
1522         auto ifs = std::make_shared<IncFsMount>(std::string(expectedRoot), std::move(metricsKey),
1523                                                 mountId, std::move(control), *this);
1524         (void)cleanupFiles.release(); // ifs will take care of that now
1525 
1526         // Check if marker file present.
1527         if (checkReadLogsDisabledMarker(root)) {
1528             ifs->disallowReadLogs();
1529         }
1530 
1531         std::vector<std::pair<std::string, metadata::BindPoint>> permanentBindPoints;
1532         auto d = openDir(root);
1533         while (auto e = ::readdir(d.get())) {
1534             if (e->d_type == DT_REG) {
1535                 auto name = std::string_view(e->d_name);
1536                 if (name.starts_with(constants().mountpointMdPrefix)) {
1537                     permanentBindPoints
1538                             .emplace_back(name,
1539                                           parseFromIncfs<metadata::BindPoint>(mIncFs.get(),
1540                                                                               ifs->control,
1541                                                                               path::join(root,
1542                                                                                          name)));
1543                     if (permanentBindPoints.back().second.dest_path().empty() ||
1544                         permanentBindPoints.back().second.source_subdir().empty()) {
1545                         permanentBindPoints.pop_back();
1546                         mIncFs->unlink(ifs->control, path::join(root, name));
1547                     } else {
1548                         LOG(INFO) << "Permanent bind record: '"
1549                                   << permanentBindPoints.back().second.source_subdir() << "'->'"
1550                                   << permanentBindPoints.back().second.dest_path() << "'";
1551                     }
1552                 }
1553             } else if (e->d_type == DT_DIR) {
1554                 if (e->d_name == "."sv || e->d_name == ".."sv) {
1555                     continue;
1556                 }
1557                 auto name = std::string_view(e->d_name);
1558                 if (name.starts_with(constants().storagePrefix)) {
1559                     int storageId;
1560                     const auto res =
1561                             std::from_chars(name.data() + constants().storagePrefix.size() + 1,
1562                                             name.data() + name.size(), storageId);
1563                     if (res.ec != std::errc{} || *res.ptr != '_') {
1564                         LOG(WARNING) << "Ignoring storage with invalid name '" << name
1565                                      << "' for mount " << expectedRoot;
1566                         continue;
1567                     }
1568                     auto [_, inserted] = mMounts.try_emplace(storageId, ifs);
1569                     if (!inserted) {
1570                         LOG(WARNING) << "Ignoring storage with duplicate id " << storageId
1571                                      << " for mount " << expectedRoot;
1572                         continue;
1573                     }
1574                     ifs->storages.insert_or_assign(storageId,
1575                                                    IncFsMount::Storage{path::join(root, name)});
1576                     mNextId = std::max(mNextId, storageId + 1);
1577                 }
1578             }
1579         }
1580 
1581         if (ifs->storages.empty()) {
1582             LOG(WARNING) << "No valid storages in mount " << root;
1583             return;
1584         }
1585 
1586         // now match the mounted directories with what we expect to have in the metadata
1587         {
1588             std::unique_lock l(mLock, std::defer_lock);
1589             for (auto&& [metadataFile, bindRecord] : permanentBindPoints) {
1590                 auto mountedIt = std::find_if(binds.begin(), binds.end(),
1591                                               [&, bindRecord = bindRecord](auto&& bind) {
1592                                                   return bind.second == bindRecord.dest_path() &&
1593                                                           path::join(root, bind.first) ==
1594                                                           bindRecord.source_subdir();
1595                                               });
1596                 if (mountedIt != binds.end()) {
1597                     LOG(INFO) << "Matched permanent bound " << bindRecord.source_subdir()
1598                               << " to mount " << mountedIt->first;
1599                     addBindMountRecordLocked(*ifs, bindRecord.storage_id(), std::move(metadataFile),
1600                                              std::move(*bindRecord.mutable_source_subdir()),
1601                                              std::move(*bindRecord.mutable_dest_path()),
1602                                              BindKind::Permanent);
1603                     if (mountedIt != binds.end() - 1) {
1604                         std::iter_swap(mountedIt, binds.end() - 1);
1605                     }
1606                     binds = binds.first(binds.size() - 1);
1607                 } else {
1608                     LOG(INFO) << "Didn't match permanent bound " << bindRecord.source_subdir()
1609                               << ", mounting";
1610                     // doesn't exist - try mounting back
1611                     if (addBindMountWithMd(*ifs, bindRecord.storage_id(), std::move(metadataFile),
1612                                            std::move(*bindRecord.mutable_source_subdir()),
1613                                            std::move(*bindRecord.mutable_dest_path()),
1614                                            BindKind::Permanent, l)) {
1615                         mIncFs->unlink(ifs->control, metadataFile);
1616                     }
1617                 }
1618             }
1619         }
1620 
1621         // if anything stays in |binds| those are probably temporary binds; system restarted since
1622         // they were mounted - so let's unmount them all.
1623         for (auto&& [source, target] : binds) {
1624             if (source.empty()) {
1625                 continue;
1626             }
1627             mVold->unmountIncFs(std::string(target));
1628         }
1629         (void)cleanupMounts.release(); // ifs now manages everything
1630 
1631         if (ifs->bindPoints.empty()) {
1632             LOG(WARNING) << "No valid bind points for mount " << expectedRoot;
1633             deleteStorage(*ifs);
1634             return;
1635         }
1636 
1637         prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams));
1638         CHECK(ifs->dataLoaderStub);
1639 
1640         mountedRootNames.insert(path::basename(ifs->root));
1641 
1642         // not locking here at all: we're still in the constructor, no other calls can happen
1643         mMounts[ifs->mountId] = std::move(ifs);
1644     });
1645 
1646     return mountedRootNames;
1647 }
1648 
mountExistingImages(const std::unordered_set<std::string_view> & mountedRootNames)1649 void IncrementalService::mountExistingImages(
1650         const std::unordered_set<std::string_view>& mountedRootNames) {
1651     auto dir = openDir(mIncrementalDir);
1652     if (!dir) {
1653         PLOG(WARNING) << "Couldn't open the root incremental dir " << mIncrementalDir;
1654         return;
1655     }
1656     while (auto entry = ::readdir(dir.get())) {
1657         if (entry->d_type != DT_DIR) {
1658             continue;
1659         }
1660         std::string_view name = entry->d_name;
1661         if (!name.starts_with(constants().mountKeyPrefix)) {
1662             continue;
1663         }
1664         if (mountedRootNames.find(name) != mountedRootNames.end()) {
1665             continue;
1666         }
1667         const auto root = path::join(mIncrementalDir, name);
1668         if (!mountExistingImage(root)) {
1669             IncFsMount::cleanupFilesystem(root);
1670         }
1671     }
1672 }
1673 
mountExistingImage(std::string_view root)1674 bool IncrementalService::mountExistingImage(std::string_view root) {
1675     auto mountTarget = path::join(root, constants().mount);
1676     const auto backing = path::join(root, constants().backing);
1677     std::string mountKey(path::basename(path::dirname(mountTarget)));
1678 
1679     IncrementalFileSystemControlParcel controlParcel;
1680     auto metricsKey = makeUniqueName(mountKey);
1681     auto status = mVold->mountIncFs(backing, mountTarget, 0, metricsKey, &controlParcel);
1682     if (!status.isOk()) {
1683         LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
1684         return false;
1685     }
1686 
1687     int cmd = controlParcel.cmd.release().release();
1688     int pendingReads = controlParcel.pendingReads.release().release();
1689     int logs = controlParcel.log.release().release();
1690     int blocksWritten =
1691             controlParcel.blocksWritten ? controlParcel.blocksWritten->release().release() : -1;
1692     IncFsMount::Control control = mIncFs->createControl(cmd, pendingReads, logs, blocksWritten);
1693 
1694     auto ifs = std::make_shared<IncFsMount>(std::string(root), std::move(metricsKey), -1,
1695                                             std::move(control), *this);
1696 
1697     auto mount = parseFromIncfs<metadata::Mount>(mIncFs.get(), ifs->control,
1698                                                  path::join(mountTarget, constants().infoMdName));
1699     if (!mount.has_loader() || !mount.has_storage()) {
1700         LOG(ERROR) << "Bad mount metadata in mount at " << root;
1701         return false;
1702     }
1703 
1704     ifs->mountId = mount.storage().id();
1705     mNextId = std::max(mNextId, ifs->mountId + 1);
1706 
1707     // Check if marker file present.
1708     if (checkReadLogsDisabledMarker(mountTarget)) {
1709         ifs->disallowReadLogs();
1710     }
1711 
1712     // DataLoader params
1713     DataLoaderParamsParcel dataLoaderParams;
1714     {
1715         const auto& loader = mount.loader();
1716         dataLoaderParams.type = (content::pm::DataLoaderType)loader.type();
1717         dataLoaderParams.packageName = loader.package_name();
1718         dataLoaderParams.className = loader.class_name();
1719         dataLoaderParams.arguments = loader.arguments();
1720     }
1721 
1722     prepareDataLoaderLocked(*ifs, std::move(dataLoaderParams));
1723     CHECK(ifs->dataLoaderStub);
1724 
1725     std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
1726     auto d = openDir(mountTarget);
1727     while (auto e = ::readdir(d.get())) {
1728         if (e->d_type == DT_REG) {
1729             auto name = std::string_view(e->d_name);
1730             if (name.starts_with(constants().mountpointMdPrefix)) {
1731                 bindPoints.emplace_back(name,
1732                                         parseFromIncfs<metadata::BindPoint>(mIncFs.get(),
1733                                                                             ifs->control,
1734                                                                             path::join(mountTarget,
1735                                                                                        name)));
1736                 if (bindPoints.back().second.dest_path().empty() ||
1737                     bindPoints.back().second.source_subdir().empty()) {
1738                     bindPoints.pop_back();
1739                     mIncFs->unlink(ifs->control, path::join(ifs->root, constants().mount, name));
1740                 }
1741             }
1742         } else if (e->d_type == DT_DIR) {
1743             if (e->d_name == "."sv || e->d_name == ".."sv) {
1744                 continue;
1745             }
1746             auto name = std::string_view(e->d_name);
1747             if (name.starts_with(constants().storagePrefix)) {
1748                 int storageId;
1749                 const auto res = std::from_chars(name.data() + constants().storagePrefix.size() + 1,
1750                                                  name.data() + name.size(), storageId);
1751                 if (res.ec != std::errc{} || *res.ptr != '_') {
1752                     LOG(WARNING) << "Ignoring storage with invalid name '" << name << "' for mount "
1753                                  << root;
1754                     continue;
1755                 }
1756                 auto [_, inserted] = mMounts.try_emplace(storageId, ifs);
1757                 if (!inserted) {
1758                     LOG(WARNING) << "Ignoring storage with duplicate id " << storageId
1759                                  << " for mount " << root;
1760                     continue;
1761                 }
1762                 ifs->storages.insert_or_assign(storageId,
1763                                                IncFsMount::Storage{
1764                                                        path::join(root, constants().mount, name)});
1765                 mNextId = std::max(mNextId, storageId + 1);
1766             }
1767         }
1768     }
1769 
1770     if (ifs->storages.empty()) {
1771         LOG(WARNING) << "No valid storages in mount " << root;
1772         return false;
1773     }
1774 
1775     int bindCount = 0;
1776     {
1777         std::unique_lock l(mLock, std::defer_lock);
1778         for (auto&& bp : bindPoints) {
1779             bindCount += !addBindMountWithMd(*ifs, bp.second.storage_id(), std::move(bp.first),
1780                                              std::move(*bp.second.mutable_source_subdir()),
1781                                              std::move(*bp.second.mutable_dest_path()),
1782                                              BindKind::Permanent, l);
1783         }
1784     }
1785 
1786     if (bindCount == 0) {
1787         LOG(WARNING) << "No valid bind points for mount " << root;
1788         deleteStorage(*ifs);
1789         return false;
1790     }
1791 
1792     // not locking here at all: we're still in the constructor, no other calls can happen
1793     mMounts[ifs->mountId] = std::move(ifs);
1794     return true;
1795 }
1796 
runCmdLooper()1797 void IncrementalService::runCmdLooper() {
1798     constexpr auto kTimeoutMsecs = -1;
1799     while (mRunning.load(std::memory_order_relaxed)) {
1800         mLooper->pollAll(kTimeoutMsecs);
1801     }
1802 }
1803 
trimReservedSpaceV1(const IncFsMount & ifs)1804 void IncrementalService::trimReservedSpaceV1(const IncFsMount& ifs) {
1805     mIncFs->forEachFile(ifs.control, [this](auto&& control, auto&& fileId) {
1806         if (mIncFs->isFileFullyLoaded(control, fileId) == incfs::LoadingState::Full) {
1807             mIncFs->reserveSpace(control, fileId, -1);
1808         }
1809         return true;
1810     });
1811 }
1812 
prepareDataLoaderLocked(IncFsMount & ifs,DataLoaderParamsParcel && params,DataLoaderStatusListener && statusListener,const StorageHealthCheckParams & healthCheckParams,StorageHealthListener && healthListener)1813 void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderParamsParcel&& params,
1814                                                  DataLoaderStatusListener&& statusListener,
1815                                                  const StorageHealthCheckParams& healthCheckParams,
1816                                                  StorageHealthListener&& healthListener) {
1817     FileSystemControlParcel fsControlParcel;
1818     fsControlParcel.incremental = std::make_optional<IncrementalFileSystemControlParcel>();
1819     fsControlParcel.incremental->cmd.reset(dup(ifs.control.cmd()));
1820     fsControlParcel.incremental->pendingReads.reset(dup(ifs.control.pendingReads()));
1821     fsControlParcel.incremental->log.reset(dup(ifs.control.logs()));
1822     if (ifs.control.blocksWritten() >= 0) {
1823         fsControlParcel.incremental->blocksWritten.emplace(dup(ifs.control.blocksWritten()));
1824     }
1825     fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId);
1826 
1827     ifs.dataLoaderStub =
1828             new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel),
1829                                std::move(statusListener), healthCheckParams,
1830                                std::move(healthListener), path::join(ifs.root, constants().mount));
1831 
1832     // pre-v2 IncFS doesn't do automatic reserved space trimming - need to run it manually
1833     if (!(mIncFs->features() & incfs::Features::v2)) {
1834         addIfsStateCallback(ifs.mountId, [this](StorageId storageId, IfsState state) -> bool {
1835             if (!state.fullyLoaded) {
1836                 return true;
1837             }
1838 
1839             const auto ifs = getIfs(storageId);
1840             if (!ifs) {
1841                 return false;
1842             }
1843             trimReservedSpaceV1(*ifs);
1844             return false;
1845         });
1846     }
1847 
1848     addIfsStateCallback(ifs.mountId, [this](StorageId storageId, IfsState state) -> bool {
1849         if (!state.fullyLoaded || state.readLogsEnabled) {
1850             return true;
1851         }
1852 
1853         DataLoaderStubPtr dataLoaderStub;
1854         {
1855             const auto ifs = getIfs(storageId);
1856             if (!ifs) {
1857                 return false;
1858             }
1859 
1860             std::unique_lock l(ifs->lock);
1861             dataLoaderStub = std::exchange(ifs->dataLoaderStub, nullptr);
1862         }
1863 
1864         if (dataLoaderStub) {
1865             dataLoaderStub->cleanupResources();
1866         }
1867 
1868         return false;
1869     });
1870 }
1871 
1872 template <class Duration>
castToMs(Duration d)1873 static constexpr auto castToMs(Duration d) {
1874     return std::chrono::duration_cast<std::chrono::milliseconds>(d);
1875 }
1876 
1877 // Extract lib files from zip, create new files in incfs and write data to them
1878 // Lib files should be placed next to the APK file in the following matter:
1879 // Example:
1880 // /path/to/base.apk
1881 // /path/to/lib/arm/first.so
1882 // /path/to/lib/arm/second.so
configureNativeBinaries(StorageId storage,std::string_view apkFullPath,std::string_view libDirRelativePath,std::string_view abi,bool extractNativeLibs)1883 bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
1884                                                  std::string_view libDirRelativePath,
1885                                                  std::string_view abi, bool extractNativeLibs) {
1886     auto start = Clock::now();
1887 
1888     const auto ifs = getIfs(storage);
1889     if (!ifs) {
1890         LOG(ERROR) << "Invalid storage " << storage;
1891         return false;
1892     }
1893 
1894     const auto targetLibPathRelativeToStorage =
1895             path::join(path::dirname(normalizePathToStorage(*ifs, storage, apkFullPath)),
1896                        libDirRelativePath);
1897 
1898     // First prepare target directories if they don't exist yet
1899     if (auto res = makeDirs(*ifs, storage, targetLibPathRelativeToStorage, 0755)) {
1900         LOG(ERROR) << "Failed to prepare target lib directory " << targetLibPathRelativeToStorage
1901                    << " errno: " << res;
1902         return false;
1903     }
1904 
1905     auto mkDirsTs = Clock::now();
1906     ZipArchiveHandle zipFileHandle;
1907     if (OpenArchive(path::c_str(apkFullPath), &zipFileHandle)) {
1908         LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
1909         return false;
1910     }
1911 
1912     // Need a shared pointer: will be passing it into all unpacking jobs.
1913     std::shared_ptr<ZipArchive> zipFile(zipFileHandle, [](ZipArchiveHandle h) { CloseArchive(h); });
1914     void* cookie = nullptr;
1915     const auto libFilePrefix = path::join(constants().libDir, abi) += "/";
1916     if (StartIteration(zipFile.get(), &cookie, libFilePrefix, constants().libSuffix)) {
1917         LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
1918         return false;
1919     }
1920     auto endIteration = [](void* cookie) { EndIteration(cookie); };
1921     auto iterationCleaner = std::unique_ptr<void, decltype(endIteration)>(cookie, endIteration);
1922 
1923     auto openZipTs = Clock::now();
1924 
1925     auto mapFiles = (mIncFs->features() & incfs::Features::v2);
1926     incfs::FileId sourceId;
1927     if (mapFiles) {
1928         sourceId = mIncFs->getFileId(ifs->control, apkFullPath);
1929         if (!incfs::isValidFileId(sourceId)) {
1930             LOG(WARNING) << "Error getting IncFS file ID for apk path '" << apkFullPath
1931                          << "', mapping disabled";
1932             mapFiles = false;
1933         }
1934     }
1935 
1936     std::vector<Job> jobQueue;
1937     ZipEntry entry;
1938     std::string_view fileName;
1939     while (!Next(cookie, &entry, &fileName)) {
1940         if (fileName.empty()) {
1941             continue;
1942         }
1943 
1944         const auto entryUncompressed = entry.method == kCompressStored;
1945         const auto entryPageAligned = isPageAligned(entry.offset);
1946 
1947         if (!extractNativeLibs) {
1948             // ensure the file is properly aligned and unpacked
1949             if (!entryUncompressed) {
1950                 LOG(WARNING) << "Library " << fileName << " must be uncompressed to mmap it";
1951                 return false;
1952             }
1953             if (!entryPageAligned) {
1954                 LOG(WARNING) << "Library " << fileName
1955                              << " must be page-aligned to mmap it, offset = 0x" << std::hex
1956                              << entry.offset;
1957                 return false;
1958             }
1959             continue;
1960         }
1961 
1962         auto startFileTs = Clock::now();
1963 
1964         const auto libName = path::basename(fileName);
1965         auto targetLibPath = path::join(targetLibPathRelativeToStorage, libName);
1966         const auto targetLibPathAbsolute = normalizePathToStorage(*ifs, storage, targetLibPath);
1967         // If the extract file already exists, skip
1968         if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) {
1969             if (perfLoggingEnabled()) {
1970                 LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath
1971                           << "; skipping extraction, spent "
1972                           << elapsedMcs(startFileTs, Clock::now()) << "mcs";
1973             }
1974             continue;
1975         }
1976 
1977         if (mapFiles && entryUncompressed && entryPageAligned && entry.uncompressed_length > 0) {
1978             incfs::NewMappedFileParams mappedFileParams = {
1979                     .sourceId = sourceId,
1980                     .sourceOffset = entry.offset,
1981                     .size = entry.uncompressed_length,
1982             };
1983 
1984             if (auto res = mIncFs->makeMappedFile(ifs->control, targetLibPathAbsolute, 0755,
1985                                                   mappedFileParams);
1986                 res == 0) {
1987                 if (perfLoggingEnabled()) {
1988                     auto doneTs = Clock::now();
1989                     LOG(INFO) << "incfs: Mapped " << libName << ": "
1990                               << elapsedMcs(startFileTs, doneTs) << "mcs";
1991                 }
1992                 continue;
1993             } else {
1994                 LOG(WARNING) << "Failed to map file for: '" << targetLibPath << "' errno: " << res
1995                              << "; falling back to full extraction";
1996             }
1997         }
1998 
1999         // Create new lib file without signature info
2000         incfs::NewFileParams libFileParams = {
2001                 .size = entry.uncompressed_length,
2002                 .signature = {},
2003                 // Metadata of the new lib file is its relative path
2004                 .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
2005         };
2006         incfs::FileId libFileId = idFromMetadata(targetLibPath);
2007         if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0755, libFileId,
2008                                         libFileParams)) {
2009             LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
2010             // If one lib file fails to be created, abort others as well
2011             return false;
2012         }
2013 
2014         auto makeFileTs = Clock::now();
2015 
2016         // If it is a zero-byte file, skip data writing
2017         if (entry.uncompressed_length == 0) {
2018             if (perfLoggingEnabled()) {
2019                 LOG(INFO) << "incfs: Extracted " << libName
2020                           << "(0 bytes): " << elapsedMcs(startFileTs, makeFileTs) << "mcs";
2021             }
2022             continue;
2023         }
2024 
2025         jobQueue.emplace_back([this, zipFile, entry, ifs = std::weak_ptr<IncFsMount>(ifs),
2026                                libFileId, libPath = std::move(targetLibPath),
2027                                makeFileTs]() mutable {
2028             extractZipFile(ifs.lock(), zipFile.get(), entry, libFileId, libPath, makeFileTs);
2029         });
2030 
2031         if (perfLoggingEnabled()) {
2032             auto prepareJobTs = Clock::now();
2033             LOG(INFO) << "incfs: Processed " << libName << ": "
2034                       << elapsedMcs(startFileTs, prepareJobTs)
2035                       << "mcs, make file: " << elapsedMcs(startFileTs, makeFileTs)
2036                       << " prepare job: " << elapsedMcs(makeFileTs, prepareJobTs);
2037         }
2038     }
2039 
2040     auto processedTs = Clock::now();
2041 
2042     if (!jobQueue.empty()) {
2043         {
2044             std::lock_guard lock(mJobMutex);
2045             if (mRunning) {
2046                 auto& existingJobs = mJobQueue[ifs->mountId];
2047                 if (existingJobs.empty()) {
2048                     existingJobs = std::move(jobQueue);
2049                 } else {
2050                     existingJobs.insert(existingJobs.end(), std::move_iterator(jobQueue.begin()),
2051                                         std::move_iterator(jobQueue.end()));
2052                 }
2053             }
2054         }
2055         mJobCondition.notify_all();
2056     }
2057 
2058     if (perfLoggingEnabled()) {
2059         auto end = Clock::now();
2060         LOG(INFO) << "incfs: configureNativeBinaries complete in " << elapsedMcs(start, end)
2061                   << "mcs, make dirs: " << elapsedMcs(start, mkDirsTs)
2062                   << " open zip: " << elapsedMcs(mkDirsTs, openZipTs)
2063                   << " make files: " << elapsedMcs(openZipTs, processedTs)
2064                   << " schedule jobs: " << elapsedMcs(processedTs, end);
2065     }
2066 
2067     return true;
2068 }
2069 
extractZipFile(const IfsMountPtr & ifs,ZipArchiveHandle zipFile,ZipEntry & entry,const incfs::FileId & libFileId,std::string_view debugLibPath,Clock::time_point scheduledTs)2070 void IncrementalService::extractZipFile(const IfsMountPtr& ifs, ZipArchiveHandle zipFile,
2071                                         ZipEntry& entry, const incfs::FileId& libFileId,
2072                                         std::string_view debugLibPath,
2073                                         Clock::time_point scheduledTs) {
2074     if (!ifs) {
2075         LOG(INFO) << "Skipping zip file " << debugLibPath << " extraction for an expired mount";
2076         return;
2077     }
2078 
2079     auto startedTs = Clock::now();
2080 
2081     // Write extracted data to new file
2082     // NOTE: don't zero-initialize memory, it may take a while for nothing
2083     auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[entry.uncompressed_length]);
2084     if (ExtractToMemory(zipFile, &entry, libData.get(), entry.uncompressed_length)) {
2085         LOG(ERROR) << "Failed to extract native lib zip entry: " << path::basename(debugLibPath);
2086         return;
2087     }
2088 
2089     auto extractFileTs = Clock::now();
2090 
2091     if (setFileContent(ifs, libFileId, debugLibPath,
2092                        std::span(libData.get(), entry.uncompressed_length))) {
2093         return;
2094     }
2095 
2096     if (perfLoggingEnabled()) {
2097         auto endFileTs = Clock::now();
2098         LOG(INFO) << "incfs: Extracted " << path::basename(debugLibPath) << "("
2099                   << entry.compressed_length << " -> " << entry.uncompressed_length
2100                   << " bytes): " << elapsedMcs(startedTs, endFileTs)
2101                   << "mcs, scheduling delay: " << elapsedMcs(scheduledTs, startedTs)
2102                   << " extract: " << elapsedMcs(startedTs, extractFileTs)
2103                   << " open/prepare/write: " << elapsedMcs(extractFileTs, endFileTs);
2104     }
2105 }
2106 
waitForNativeBinariesExtraction(StorageId storage)2107 bool IncrementalService::waitForNativeBinariesExtraction(StorageId storage) {
2108     struct WaitPrinter {
2109         const Clock::time_point startTs = Clock::now();
2110         ~WaitPrinter() noexcept {
2111             if (perfLoggingEnabled()) {
2112                 const auto endTs = Clock::now();
2113                 LOG(INFO) << "incfs: waitForNativeBinariesExtraction() complete in "
2114                           << elapsedMcs(startTs, endTs) << "mcs";
2115             }
2116         }
2117     } waitPrinter;
2118 
2119     MountId mount;
2120     {
2121         auto ifs = getIfs(storage);
2122         if (!ifs) {
2123             return true;
2124         }
2125         mount = ifs->mountId;
2126     }
2127 
2128     std::unique_lock lock(mJobMutex);
2129     mJobCondition.wait(lock, [this, mount] {
2130         return !mRunning ||
2131                 (mPendingJobsMount != mount && mJobQueue.find(mount) == mJobQueue.end());
2132     });
2133     return mRunning;
2134 }
2135 
setFileContent(const IfsMountPtr & ifs,const incfs::FileId & fileId,std::string_view debugFilePath,std::span<const uint8_t> data) const2136 int IncrementalService::setFileContent(const IfsMountPtr& ifs, const incfs::FileId& fileId,
2137                                        std::string_view debugFilePath,
2138                                        std::span<const uint8_t> data) const {
2139     auto startTs = Clock::now();
2140 
2141     const auto writeFd = mIncFs->openForSpecialOps(ifs->control, fileId);
2142     if (!writeFd.ok()) {
2143         LOG(ERROR) << "Failed to open write fd for: " << debugFilePath
2144                    << " errno: " << writeFd.get();
2145         return writeFd.get();
2146     }
2147 
2148     const auto dataLength = data.size();
2149 
2150     auto openFileTs = Clock::now();
2151     const int numBlocks = (data.size() + constants().blockSize - 1) / constants().blockSize;
2152     std::vector<IncFsDataBlock> instructions(numBlocks);
2153     for (int i = 0; i < numBlocks; i++) {
2154         const auto blockSize = std::min<long>(constants().blockSize, data.size());
2155         instructions[i] = IncFsDataBlock{
2156                 .fileFd = writeFd.get(),
2157                 .pageIndex = static_cast<IncFsBlockIndex>(i),
2158                 .compression = INCFS_COMPRESSION_KIND_NONE,
2159                 .kind = INCFS_BLOCK_KIND_DATA,
2160                 .dataSize = static_cast<uint32_t>(blockSize),
2161                 .data = reinterpret_cast<const char*>(data.data()),
2162         };
2163         data = data.subspan(blockSize);
2164     }
2165     auto prepareInstsTs = Clock::now();
2166 
2167     size_t res = mIncFs->writeBlocks(instructions);
2168     if (res != instructions.size()) {
2169         LOG(ERROR) << "Failed to write data into: " << debugFilePath;
2170         return res;
2171     }
2172 
2173     if (perfLoggingEnabled()) {
2174         auto endTs = Clock::now();
2175         LOG(INFO) << "incfs: Set file content " << debugFilePath << "(" << dataLength
2176                   << " bytes): " << elapsedMcs(startTs, endTs)
2177                   << "mcs, open: " << elapsedMcs(startTs, openFileTs)
2178                   << " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
2179                   << " write: " << elapsedMcs(prepareInstsTs, endTs);
2180     }
2181 
2182     return 0;
2183 }
2184 
isFileFullyLoaded(StorageId storage,std::string_view filePath) const2185 incfs::LoadingState IncrementalService::isFileFullyLoaded(StorageId storage,
2186                                                           std::string_view filePath) const {
2187     std::unique_lock l(mLock);
2188     const auto ifs = getIfsLocked(storage);
2189     if (!ifs) {
2190         LOG(ERROR) << "isFileFullyLoaded failed, invalid storageId: " << storage;
2191         return incfs::LoadingState(-EINVAL);
2192     }
2193     const auto storageInfo = ifs->storages.find(storage);
2194     if (storageInfo == ifs->storages.end()) {
2195         LOG(ERROR) << "isFileFullyLoaded failed, no storage: " << storage;
2196         return incfs::LoadingState(-EINVAL);
2197     }
2198     l.unlock();
2199     return mIncFs->isFileFullyLoaded(ifs->control, filePath);
2200 }
2201 
isMountFullyLoaded(StorageId storage) const2202 incfs::LoadingState IncrementalService::isMountFullyLoaded(StorageId storage) const {
2203     const auto ifs = getIfs(storage);
2204     if (!ifs) {
2205         LOG(ERROR) << "isMountFullyLoaded failed, invalid storageId: " << storage;
2206         return incfs::LoadingState(-EINVAL);
2207     }
2208     return mIncFs->isEverythingFullyLoaded(ifs->control);
2209 }
2210 
getLoadingProgress(StorageId storage) const2211 IncrementalService::LoadingProgress IncrementalService::getLoadingProgress(
2212         StorageId storage) const {
2213     std::unique_lock l(mLock);
2214     const auto ifs = getIfsLocked(storage);
2215     if (!ifs) {
2216         LOG(ERROR) << "getLoadingProgress failed, invalid storageId: " << storage;
2217         return {-EINVAL, -EINVAL};
2218     }
2219     const auto storageInfo = ifs->storages.find(storage);
2220     if (storageInfo == ifs->storages.end()) {
2221         LOG(ERROR) << "getLoadingProgress failed, no storage: " << storage;
2222         return {-EINVAL, -EINVAL};
2223     }
2224     l.unlock();
2225     return getLoadingProgressFromPath(*ifs, storageInfo->second.name);
2226 }
2227 
getLoadingProgressFromPath(const IncFsMount & ifs,std::string_view storagePath) const2228 IncrementalService::LoadingProgress IncrementalService::getLoadingProgressFromPath(
2229         const IncFsMount& ifs, std::string_view storagePath) const {
2230     ssize_t totalBlocks = 0, filledBlocks = 0, error = 0;
2231     mFs->listFilesRecursive(storagePath, [&, this](auto filePath) {
2232         const auto [filledBlocksCount, totalBlocksCount] =
2233                 mIncFs->countFilledBlocks(ifs.control, filePath);
2234         if (filledBlocksCount == -EOPNOTSUPP || filledBlocksCount == -ENOTSUP ||
2235             filledBlocksCount == -ENOENT) {
2236             // a kind of a file that's not really being loaded, e.g. a mapped range
2237             // an older IncFS used to return ENOENT in this case, so handle it the same way
2238             return true;
2239         }
2240         if (filledBlocksCount < 0) {
2241             LOG(ERROR) << "getLoadingProgress failed to get filled blocks count for: " << filePath
2242                        << ", errno: " << filledBlocksCount;
2243             error = filledBlocksCount;
2244             return false;
2245         }
2246         totalBlocks += totalBlocksCount;
2247         filledBlocks += filledBlocksCount;
2248         return true;
2249     });
2250 
2251     return error ? LoadingProgress{error, error} : LoadingProgress{filledBlocks, totalBlocks};
2252 }
2253 
updateLoadingProgress(StorageId storage,StorageLoadingProgressListener && progressListener)2254 bool IncrementalService::updateLoadingProgress(StorageId storage,
2255                                                StorageLoadingProgressListener&& progressListener) {
2256     const auto progress = getLoadingProgress(storage);
2257     if (progress.isError()) {
2258         // Failed to get progress from incfs, abort.
2259         return false;
2260     }
2261     progressListener->onStorageLoadingProgressChanged(storage, progress.getProgress());
2262     if (progress.fullyLoaded()) {
2263         // Stop updating progress once it is fully loaded
2264         return true;
2265     }
2266     addTimedJob(*mProgressUpdateJobQueue, storage,
2267                 Constants::progressUpdateInterval /* repeat after 1s */,
2268                 [storage, progressListener = std::move(progressListener), this]() mutable {
2269                     updateLoadingProgress(storage, std::move(progressListener));
2270                 });
2271     return true;
2272 }
2273 
registerLoadingProgressListener(StorageId storage,StorageLoadingProgressListener progressListener)2274 bool IncrementalService::registerLoadingProgressListener(
2275         StorageId storage, StorageLoadingProgressListener progressListener) {
2276     return updateLoadingProgress(storage, std::move(progressListener));
2277 }
2278 
unregisterLoadingProgressListener(StorageId storage)2279 bool IncrementalService::unregisterLoadingProgressListener(StorageId storage) {
2280     return removeTimedJobs(*mProgressUpdateJobQueue, storage);
2281 }
2282 
perfLoggingEnabled()2283 bool IncrementalService::perfLoggingEnabled() {
2284     static const bool enabled = base::GetBoolProperty("incremental.perflogging", false);
2285     return enabled;
2286 }
2287 
runJobProcessing()2288 void IncrementalService::runJobProcessing() {
2289     for (;;) {
2290         std::unique_lock lock(mJobMutex);
2291         mJobCondition.wait(lock, [this]() { return !mRunning || !mJobQueue.empty(); });
2292         if (!mRunning) {
2293             return;
2294         }
2295 
2296         auto it = mJobQueue.begin();
2297         mPendingJobsMount = it->first;
2298         auto queue = std::move(it->second);
2299         mJobQueue.erase(it);
2300         lock.unlock();
2301 
2302         for (auto&& job : queue) {
2303             job();
2304         }
2305 
2306         lock.lock();
2307         mPendingJobsMount = kInvalidStorageId;
2308         lock.unlock();
2309         mJobCondition.notify_all();
2310     }
2311 }
2312 
registerAppOpsCallback(const std::string & packageName)2313 void IncrementalService::registerAppOpsCallback(const std::string& packageName) {
2314     sp<IAppOpsCallback> listener;
2315     {
2316         std::unique_lock lock{mCallbacksLock};
2317         auto& cb = mCallbackRegistered[packageName];
2318         if (cb) {
2319             return;
2320         }
2321         cb = new AppOpsListener(*this, packageName);
2322         listener = cb;
2323     }
2324 
2325     mAppOpsManager->startWatchingMode(AppOpsManager::OP_GET_USAGE_STATS,
2326                                       String16(packageName.c_str()), listener);
2327 }
2328 
unregisterAppOpsCallback(const std::string & packageName)2329 bool IncrementalService::unregisterAppOpsCallback(const std::string& packageName) {
2330     sp<IAppOpsCallback> listener;
2331     {
2332         std::unique_lock lock{mCallbacksLock};
2333         auto found = mCallbackRegistered.find(packageName);
2334         if (found == mCallbackRegistered.end()) {
2335             return false;
2336         }
2337         listener = found->second;
2338         mCallbackRegistered.erase(found);
2339     }
2340 
2341     mAppOpsManager->stopWatchingMode(listener);
2342     return true;
2343 }
2344 
onAppOpChanged(const std::string & packageName)2345 void IncrementalService::onAppOpChanged(const std::string& packageName) {
2346     if (!unregisterAppOpsCallback(packageName)) {
2347         return;
2348     }
2349 
2350     std::vector<IfsMountPtr> affected;
2351     {
2352         std::lock_guard l(mLock);
2353         affected.reserve(mMounts.size());
2354         for (auto&& [id, ifs] : mMounts) {
2355             std::unique_lock ll(ifs->lock);
2356             if (ifs->mountId == id && ifs->dataLoaderStub &&
2357                 ifs->dataLoaderStub->params().packageName == packageName) {
2358                 affected.push_back(ifs);
2359             }
2360         }
2361     }
2362     for (auto&& ifs : affected) {
2363         std::unique_lock ll(ifs->lock);
2364         disableReadLogsLocked(*ifs);
2365     }
2366 }
2367 
addTimedJob(TimedQueueWrapper & timedQueue,MountId id,Milliseconds after,Job what)2368 bool IncrementalService::addTimedJob(TimedQueueWrapper& timedQueue, MountId id, Milliseconds after,
2369                                      Job what) {
2370     if (id == kInvalidStorageId) {
2371         return false;
2372     }
2373     timedQueue.addJob(id, after, std::move(what));
2374     return true;
2375 }
2376 
removeTimedJobs(TimedQueueWrapper & timedQueue,MountId id)2377 bool IncrementalService::removeTimedJobs(TimedQueueWrapper& timedQueue, MountId id) {
2378     if (id == kInvalidStorageId) {
2379         return false;
2380     }
2381     timedQueue.removeJobs(id);
2382     return true;
2383 }
2384 
addIfsStateCallback(StorageId storageId,IfsStateCallback callback)2385 void IncrementalService::addIfsStateCallback(StorageId storageId, IfsStateCallback callback) {
2386     bool wasEmpty;
2387     {
2388         std::lock_guard l(mIfsStateCallbacksLock);
2389         wasEmpty = mIfsStateCallbacks.empty();
2390         mIfsStateCallbacks[storageId].emplace_back(std::move(callback));
2391     }
2392     if (wasEmpty) {
2393         addTimedJob(*mTimedQueue, kAllStoragesId, Constants::progressUpdateInterval,
2394                     [this]() { processIfsStateCallbacks(); });
2395     }
2396 }
2397 
processIfsStateCallbacks()2398 void IncrementalService::processIfsStateCallbacks() {
2399     StorageId storageId = kInvalidStorageId;
2400     std::vector<IfsStateCallback> local;
2401     while (true) {
2402         {
2403             std::lock_guard l(mIfsStateCallbacksLock);
2404             if (mIfsStateCallbacks.empty()) {
2405                 return;
2406             }
2407             IfsStateCallbacks::iterator it;
2408             if (storageId == kInvalidStorageId) {
2409                 // First entry, initialize the |it|.
2410                 it = mIfsStateCallbacks.begin();
2411             } else {
2412                 // Subsequent entries, update the |storageId|, and shift to the new one (not that
2413                 // it guarantees much about updated items, but at least the loop will finish).
2414                 it = mIfsStateCallbacks.lower_bound(storageId);
2415                 if (it == mIfsStateCallbacks.end()) {
2416                     // Nothing else left, too bad.
2417                     break;
2418                 }
2419                 if (it->first != storageId) {
2420                     local.clear(); // Was removed during processing, forget the old callbacks.
2421                 } else {
2422                     // Put the 'surviving' callbacks back into the map and advance the position.
2423                     auto& callbacks = it->second;
2424                     if (callbacks.empty()) {
2425                         std::swap(callbacks, local);
2426                     } else {
2427                         callbacks.insert(callbacks.end(), std::move_iterator(local.begin()),
2428                                          std::move_iterator(local.end()));
2429                         local.clear();
2430                     }
2431                     if (callbacks.empty()) {
2432                         it = mIfsStateCallbacks.erase(it);
2433                         if (mIfsStateCallbacks.empty()) {
2434                             return;
2435                         }
2436                     } else {
2437                         ++it;
2438                     }
2439                 }
2440             }
2441 
2442             if (it == mIfsStateCallbacks.end()) {
2443                 break;
2444             }
2445 
2446             storageId = it->first;
2447             auto& callbacks = it->second;
2448             if (callbacks.empty()) {
2449                 // Invalid case, one extra lookup should be ok.
2450                 continue;
2451             }
2452             std::swap(callbacks, local);
2453         }
2454 
2455         processIfsStateCallbacks(storageId, local);
2456     }
2457 
2458     addTimedJob(*mTimedQueue, kAllStoragesId, Constants::progressUpdateInterval,
2459                 [this]() { processIfsStateCallbacks(); });
2460 }
2461 
processIfsStateCallbacks(StorageId storageId,std::vector<IfsStateCallback> & callbacks)2462 void IncrementalService::processIfsStateCallbacks(StorageId storageId,
2463                                                   std::vector<IfsStateCallback>& callbacks) {
2464     const auto state = isMountFullyLoaded(storageId);
2465     IfsState storageState = {};
2466     storageState.error = int(state) < 0;
2467     storageState.fullyLoaded = state == incfs::LoadingState::Full;
2468     if (storageState.fullyLoaded) {
2469         const auto ifs = getIfs(storageId);
2470         storageState.readLogsEnabled = ifs && ifs->readLogsEnabled();
2471     }
2472 
2473     for (auto cur = callbacks.begin(); cur != callbacks.end();) {
2474         if ((*cur)(storageId, storageState)) {
2475             ++cur;
2476         } else {
2477             cur = callbacks.erase(cur);
2478         }
2479     }
2480 }
2481 
removeIfsStateCallbacks(StorageId storageId)2482 void IncrementalService::removeIfsStateCallbacks(StorageId storageId) {
2483     std::lock_guard l(mIfsStateCallbacksLock);
2484     mIfsStateCallbacks.erase(storageId);
2485 }
2486 
getMetrics(StorageId storageId,android::os::PersistableBundle * result)2487 void IncrementalService::getMetrics(StorageId storageId, android::os::PersistableBundle* result) {
2488     const auto ifs = getIfs(storageId);
2489     if (!ifs) {
2490         LOG(ERROR) << "getMetrics failed, invalid storageId: " << storageId;
2491         return;
2492     }
2493     const auto& kMetricsReadLogsEnabled =
2494             os::incremental::BnIncrementalService::METRICS_READ_LOGS_ENABLED();
2495     result->putBoolean(String16(kMetricsReadLogsEnabled.c_str()), ifs->readLogsEnabled() != 0);
2496     const auto incfsMetrics = mIncFs->getMetrics(ifs->metricsKey);
2497     if (incfsMetrics) {
2498         const auto& kMetricsTotalDelayedReads =
2499                 os::incremental::BnIncrementalService::METRICS_TOTAL_DELAYED_READS();
2500         const auto totalDelayedReads =
2501                 incfsMetrics->readsDelayedMin + incfsMetrics->readsDelayedPending;
2502         result->putInt(String16(kMetricsTotalDelayedReads.c_str()), totalDelayedReads);
2503         const auto& kMetricsTotalFailedReads =
2504                 os::incremental::BnIncrementalService::METRICS_TOTAL_FAILED_READS();
2505         const auto totalFailedReads = incfsMetrics->readsFailedTimedOut +
2506                 incfsMetrics->readsFailedHashVerification + incfsMetrics->readsFailedOther;
2507         result->putInt(String16(kMetricsTotalFailedReads.c_str()), totalFailedReads);
2508         const auto& kMetricsTotalDelayedReadsMillis =
2509                 os::incremental::BnIncrementalService::METRICS_TOTAL_DELAYED_READS_MILLIS();
2510         const int64_t totalDelayedReadsMillis =
2511                 (incfsMetrics->readsDelayedMinUs + incfsMetrics->readsDelayedPendingUs) / 1000;
2512         result->putLong(String16(kMetricsTotalDelayedReadsMillis.c_str()), totalDelayedReadsMillis);
2513     }
2514     const auto lastReadError = mIncFs->getLastReadError(ifs->control);
2515     if (lastReadError && lastReadError->timestampUs != 0) {
2516         const auto& kMetricsMillisSinceLastReadError =
2517                 os::incremental::BnIncrementalService::METRICS_MILLIS_SINCE_LAST_READ_ERROR();
2518         result->putLong(String16(kMetricsMillisSinceLastReadError.c_str()),
2519                         (int64_t)elapsedUsSinceMonoTs(lastReadError->timestampUs) / 1000);
2520         const auto& kMetricsLastReadErrorNo =
2521                 os::incremental::BnIncrementalService::METRICS_LAST_READ_ERROR_NUMBER();
2522         result->putInt(String16(kMetricsLastReadErrorNo.c_str()), lastReadError->errorNo);
2523         const auto& kMetricsLastReadUid =
2524                 os::incremental::BnIncrementalService::METRICS_LAST_READ_ERROR_UID();
2525         result->putInt(String16(kMetricsLastReadUid.c_str()), lastReadError->uid);
2526     }
2527     std::unique_lock l(ifs->lock);
2528     if (!ifs->dataLoaderStub) {
2529         return;
2530     }
2531     ifs->dataLoaderStub->getMetrics(result);
2532 }
2533 
DataLoaderStub(IncrementalService & service,MountId id,DataLoaderParamsParcel && params,FileSystemControlParcel && control,DataLoaderStatusListener && statusListener,const StorageHealthCheckParams & healthCheckParams,StorageHealthListener && healthListener,std::string && healthPath)2534 IncrementalService::DataLoaderStub::DataLoaderStub(
2535         IncrementalService& service, MountId id, DataLoaderParamsParcel&& params,
2536         FileSystemControlParcel&& control, DataLoaderStatusListener&& statusListener,
2537         const StorageHealthCheckParams& healthCheckParams, StorageHealthListener&& healthListener,
2538         std::string&& healthPath)
2539       : mService(service),
2540         mId(id),
2541         mParams(std::move(params)),
2542         mControl(std::move(control)),
2543         mStatusListener(std::move(statusListener)),
2544         mHealthListener(std::move(healthListener)),
2545         mHealthPath(std::move(healthPath)),
2546         mHealthCheckParams(healthCheckParams) {
2547     if (mHealthListener && !isHealthParamsValid()) {
2548         mHealthListener = {};
2549     }
2550     if (!mHealthListener) {
2551         // Disable advanced health check statuses.
2552         mHealthCheckParams.blockedTimeoutMs = -1;
2553     }
2554     updateHealthStatus();
2555 }
2556 
~DataLoaderStub()2557 IncrementalService::DataLoaderStub::~DataLoaderStub() {
2558     if (isValid()) {
2559         cleanupResources();
2560     }
2561 }
2562 
cleanupResources()2563 void IncrementalService::DataLoaderStub::cleanupResources() {
2564     auto now = Clock::now();
2565     {
2566         std::unique_lock lock(mMutex);
2567         mHealthPath.clear();
2568         unregisterFromPendingReads();
2569         resetHealthControl();
2570         mService.removeTimedJobs(*mService.mTimedQueue, mId);
2571     }
2572     mService.removeIfsStateCallbacks(mId);
2573 
2574     requestDestroy();
2575 
2576     {
2577         std::unique_lock lock(mMutex);
2578         mParams = {};
2579         mControl = {};
2580         mHealthControl = {};
2581         mHealthListener = {};
2582         mStatusCondition.wait_until(lock, now + Constants::destroyTimeout, [this] {
2583             return mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED;
2584         });
2585         mStatusListener = {};
2586         mId = kInvalidStorageId;
2587     }
2588 }
2589 
getDataLoader()2590 sp<content::pm::IDataLoader> IncrementalService::DataLoaderStub::getDataLoader() {
2591     sp<IDataLoader> dataloader;
2592     auto status = mService.mDataLoaderManager->getDataLoader(id(), &dataloader);
2593     if (!status.isOk()) {
2594         LOG(ERROR) << "Failed to get dataloader: " << status.toString8();
2595         return {};
2596     }
2597     if (!dataloader) {
2598         LOG(ERROR) << "DataLoader is null: " << status.toString8();
2599         return {};
2600     }
2601     return dataloader;
2602 }
2603 
isSystemDataLoader() const2604 bool IncrementalService::DataLoaderStub::isSystemDataLoader() const {
2605     return (params().packageName == Constants::systemPackage);
2606 }
2607 
requestCreate()2608 bool IncrementalService::DataLoaderStub::requestCreate() {
2609     return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_CREATED);
2610 }
2611 
requestStart()2612 bool IncrementalService::DataLoaderStub::requestStart() {
2613     return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_STARTED);
2614 }
2615 
requestDestroy()2616 bool IncrementalService::DataLoaderStub::requestDestroy() {
2617     return setTargetStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
2618 }
2619 
setTargetStatus(int newStatus)2620 bool IncrementalService::DataLoaderStub::setTargetStatus(int newStatus) {
2621     {
2622         std::unique_lock lock(mMutex);
2623         setTargetStatusLocked(newStatus);
2624     }
2625     return fsmStep();
2626 }
2627 
setTargetStatusLocked(int status)2628 void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) {
2629     auto oldStatus = mTargetStatus;
2630     mTargetStatus = status;
2631     mTargetStatusTs = Clock::now();
2632     LOG(DEBUG) << "Target status update for DataLoader " << id() << ": " << oldStatus << " -> "
2633                << status << " (current " << mCurrentStatus << ")";
2634 }
2635 
needToBind()2636 std::optional<Milliseconds> IncrementalService::DataLoaderStub::needToBind() {
2637     std::unique_lock lock(mMutex);
2638 
2639     const auto now = mService.mClock->now();
2640     const bool healthy = (mPreviousBindDelay == 0ms);
2641 
2642     if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_BINDING &&
2643         now - mCurrentStatusTs <= Constants::bindingTimeout) {
2644         LOG(INFO) << "Binding still in progress. "
2645                   << (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times."
2646                               : "Already unhealthy, don't do anything.")
2647                   << " for storage " << mId;
2648         // Binding still in progress.
2649         if (!healthy) {
2650             // Already unhealthy, don't do anything.
2651             return {};
2652         }
2653         // The DL is healthy/freshly bound, ok to retry for a few times.
2654         if (now - mPreviousBindTs <= Constants::bindGracePeriod) {
2655             // Still within grace period.
2656             if (now - mCurrentStatusTs >= Constants::bindRetryInterval) {
2657                 // Retry interval passed, retrying.
2658                 mCurrentStatusTs = now;
2659                 mPreviousBindDelay = 0ms;
2660                 return 0ms;
2661             }
2662             return {};
2663         }
2664         // fallthrough, mark as unhealthy, and retry with delay
2665     }
2666 
2667     const auto previousBindTs = mPreviousBindTs;
2668     mPreviousBindTs = now;
2669 
2670     const auto nonCrashingInterval =
2671             std::max(castToMs(now - previousBindTs - mPreviousBindDelay), 100ms);
2672     if (previousBindTs.time_since_epoch() == Clock::duration::zero() ||
2673         nonCrashingInterval > Constants::healthyDataLoaderUptime) {
2674         mPreviousBindDelay = 0ms;
2675         return 0ms;
2676     }
2677 
2678     constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay);
2679     constexpr auto maxBindDelayMs = castToMs(Constants::maxBindDelay);
2680 
2681     const auto bindDelayMs =
2682             std::min(std::max(mPreviousBindDelay * Constants::bindDelayMultiplier, minBindDelayMs),
2683                      maxBindDelayMs)
2684                     .count();
2685     const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider;
2686     // rand() is enough, not worth maintaining a full-blown <rand> object for delay jitter
2687     const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - // NOLINT
2688             bindDelayJitterRangeMs;
2689     mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs);
2690     return mPreviousBindDelay;
2691 }
2692 
bind()2693 bool IncrementalService::DataLoaderStub::bind() {
2694     const auto maybeBindDelay = needToBind();
2695     if (!maybeBindDelay) {
2696         LOG(DEBUG) << "Skipping bind to " << mParams.packageName << " because of pending bind.";
2697         return true;
2698     }
2699     const auto bindDelay = *maybeBindDelay;
2700     if (bindDelay > 1s) {
2701         LOG(INFO) << "Delaying bind to " << mParams.packageName << " by "
2702                   << bindDelay.count() / 1000 << "s"
2703                   << " for storage " << mId;
2704     }
2705 
2706     bool result = false;
2707     auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(),
2708                                                                 this, &result);
2709     if (!status.isOk() || !result) {
2710         const bool healthy = (bindDelay == 0ms);
2711         LOG(ERROR) << "Failed to bind a data loader for mount " << id()
2712                    << (healthy ? ", retrying." : "");
2713 
2714         // Internal error, retry for healthy/new DLs.
2715         // Let needToBind migrate it to unhealthy after too many retries.
2716         if (healthy) {
2717             if (mService.addTimedJob(*mService.mTimedQueue, id(), Constants::bindRetryInterval,
2718                                      [this]() { fsmStep(); })) {
2719                 // Mark as binding so that we know it's not the DL's fault.
2720                 setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_BINDING);
2721                 return true;
2722             }
2723         }
2724 
2725         return false;
2726     }
2727     return true;
2728 }
2729 
create()2730 bool IncrementalService::DataLoaderStub::create() {
2731     auto dataloader = getDataLoader();
2732     if (!dataloader) {
2733         return false;
2734     }
2735     auto status = dataloader->create(id(), mParams, mControl, this);
2736     if (!status.isOk()) {
2737         LOG(ERROR) << "Failed to create DataLoader: " << status.toString8();
2738         return false;
2739     }
2740     return true;
2741 }
2742 
start()2743 bool IncrementalService::DataLoaderStub::start() {
2744     auto dataloader = getDataLoader();
2745     if (!dataloader) {
2746         return false;
2747     }
2748     auto status = dataloader->start(id());
2749     if (!status.isOk()) {
2750         LOG(ERROR) << "Failed to start DataLoader: " << status.toString8();
2751         return false;
2752     }
2753     return true;
2754 }
2755 
destroy()2756 bool IncrementalService::DataLoaderStub::destroy() {
2757     return mService.mDataLoaderManager->unbindFromDataLoader(id()).isOk();
2758 }
2759 
fsmStep()2760 bool IncrementalService::DataLoaderStub::fsmStep() {
2761     if (!isValid()) {
2762         return false;
2763     }
2764 
2765     int currentStatus;
2766     int targetStatus;
2767     {
2768         std::unique_lock lock(mMutex);
2769         currentStatus = mCurrentStatus;
2770         targetStatus = mTargetStatus;
2771     }
2772 
2773     LOG(DEBUG) << "fsmStep: " << id() << ": " << currentStatus << " -> " << targetStatus;
2774 
2775     if (currentStatus == targetStatus) {
2776         return true;
2777     }
2778 
2779     switch (targetStatus) {
2780         case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
2781             switch (currentStatus) {
2782                 case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
2783                 case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE:
2784                     destroy();
2785                     // DataLoader is broken, just assume it's destroyed.
2786                     compareAndSetCurrentStatus(currentStatus,
2787                                                IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
2788                     return true;
2789                 case IDataLoaderStatusListener::DATA_LOADER_BINDING:
2790                     compareAndSetCurrentStatus(currentStatus,
2791                                                IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
2792                     return true;
2793                 default:
2794                     return destroy();
2795             }
2796             break;
2797         }
2798         case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
2799             switch (currentStatus) {
2800                 case IDataLoaderStatusListener::DATA_LOADER_CREATED:
2801                 case IDataLoaderStatusListener::DATA_LOADER_STOPPED:
2802                     return start();
2803             }
2804             [[fallthrough]];
2805         }
2806         case IDataLoaderStatusListener::DATA_LOADER_CREATED:
2807             switch (currentStatus) {
2808                 case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
2809                 case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE:
2810                     // Before binding need to make sure we are unbound.
2811                     // Otherwise we'll get stuck binding.
2812                     destroy();
2813                     // DataLoader is broken, just assume it's destroyed.
2814                     compareAndSetCurrentStatus(currentStatus,
2815                                                IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
2816                     return true;
2817                 case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
2818                 case IDataLoaderStatusListener::DATA_LOADER_BINDING:
2819                     return bind();
2820                 case IDataLoaderStatusListener::DATA_LOADER_BOUND:
2821                     return create();
2822             }
2823             break;
2824         default:
2825             LOG(ERROR) << "Invalid target status: " << targetStatus
2826                        << ", current status: " << currentStatus;
2827             break;
2828     }
2829     return false;
2830 }
2831 
onStatusChanged(MountId mountId,int newStatus)2832 binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mountId, int newStatus) {
2833     if (!isValid()) {
2834         if (newStatus == IDataLoaderStatusListener::DATA_LOADER_BOUND) {
2835             // Async "bound" came to already destroyed stub.
2836             // Unbind immediately to avoid invalid stub sitting around in DataLoaderManagerService.
2837             mService.mDataLoaderManager->unbindFromDataLoader(mountId);
2838             return binder::Status::ok();
2839         }
2840         return binder::Status::
2841                 fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub");
2842     }
2843     if (id() != mountId) {
2844         LOG(ERROR) << "onStatusChanged: mount ID mismatch: expected " << id()
2845                    << ", but got: " << mountId;
2846         return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
2847     }
2848     if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
2849         newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
2850         // User-provided status, let's postpone the handling to avoid possible deadlocks.
2851         mService.addTimedJob(*mService.mTimedQueue, id(), Constants::userStatusDelay,
2852                              [this, newStatus]() { setCurrentStatus(newStatus); });
2853         return binder::Status::ok();
2854     }
2855 
2856     setCurrentStatus(newStatus);
2857     return binder::Status::ok();
2858 }
2859 
setCurrentStatus(int newStatus)2860 void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) {
2861     compareAndSetCurrentStatus(Constants::anyStatus, newStatus);
2862 }
2863 
compareAndSetCurrentStatus(int expectedStatus,int newStatus)2864 void IncrementalService::DataLoaderStub::compareAndSetCurrentStatus(int expectedStatus,
2865                                                                     int newStatus) {
2866     int oldStatus, oldTargetStatus, newTargetStatus;
2867     DataLoaderStatusListener listener;
2868     {
2869         std::unique_lock lock(mMutex);
2870         if (mCurrentStatus == newStatus) {
2871             return;
2872         }
2873         if (expectedStatus != Constants::anyStatus && expectedStatus != mCurrentStatus) {
2874             return;
2875         }
2876 
2877         oldStatus = mCurrentStatus;
2878         oldTargetStatus = mTargetStatus;
2879         listener = mStatusListener;
2880 
2881         // Change the status.
2882         mCurrentStatus = newStatus;
2883         mCurrentStatusTs = mService.mClock->now();
2884 
2885         switch (mCurrentStatus) {
2886             case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
2887                 // Unavailable, retry.
2888                 setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_STARTED);
2889                 break;
2890             case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE:
2891                 // Unrecoverable, just unbind.
2892                 setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
2893                 break;
2894             default:
2895                 break;
2896         }
2897 
2898         newTargetStatus = mTargetStatus;
2899     }
2900 
2901     LOG(DEBUG) << "Current status update for DataLoader " << id() << ": " << oldStatus << " -> "
2902                << newStatus << " (target " << oldTargetStatus << " -> " << newTargetStatus << ")";
2903 
2904     if (listener) {
2905         listener->onStatusChanged(id(), newStatus);
2906     }
2907 
2908     fsmStep();
2909 
2910     mStatusCondition.notify_all();
2911 }
2912 
isHealthParamsValid() const2913 bool IncrementalService::DataLoaderStub::isHealthParamsValid() const {
2914     return mHealthCheckParams.blockedTimeoutMs > 0 &&
2915             mHealthCheckParams.blockedTimeoutMs < mHealthCheckParams.unhealthyTimeoutMs;
2916 }
2917 
onHealthStatus(const StorageHealthListener & healthListener,int healthStatus)2918 void IncrementalService::DataLoaderStub::onHealthStatus(const StorageHealthListener& healthListener,
2919                                                         int healthStatus) {
2920     LOG(DEBUG) << id() << ": healthStatus: " << healthStatus;
2921     if (healthListener) {
2922         healthListener->onHealthStatus(id(), healthStatus);
2923     }
2924     mHealthStatus = healthStatus;
2925 }
2926 
updateHealthStatus(bool baseline)2927 void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) {
2928     LOG(DEBUG) << id() << ": updateHealthStatus" << (baseline ? " (baseline)" : "");
2929 
2930     int healthStatusToReport = -1;
2931     StorageHealthListener healthListener;
2932 
2933     {
2934         std::unique_lock lock(mMutex);
2935         unregisterFromPendingReads();
2936 
2937         healthListener = mHealthListener;
2938 
2939         // Healthcheck depends on timestamp of the oldest pending read.
2940         // To get it, we need to re-open a pendingReads FD to get a full list of reads.
2941         // Additionally we need to re-register for epoll with fresh FDs in case there are no
2942         // reads.
2943         const auto now = Clock::now();
2944         const auto kernelTsUs = getOldestPendingReadTs();
2945         if (baseline) {
2946             // Updating baseline only on looper/epoll callback, i.e. on new set of pending
2947             // reads.
2948             mHealthBase = {now, kernelTsUs};
2949         }
2950 
2951         if (kernelTsUs == kMaxBootClockTsUs || mHealthBase.kernelTsUs == kMaxBootClockTsUs ||
2952             mHealthBase.userTs > now) {
2953             LOG(DEBUG) << id() << ": No pending reads or invalid base, report Ok and wait.";
2954             registerForPendingReads();
2955             healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_OK;
2956             lock.unlock();
2957             onHealthStatus(healthListener, healthStatusToReport);
2958             return;
2959         }
2960 
2961         resetHealthControl();
2962 
2963         // Always make sure the data loader is started.
2964         setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_STARTED);
2965 
2966         // Skip any further processing if health check params are invalid.
2967         if (!isHealthParamsValid()) {
2968             LOG(DEBUG) << id()
2969                        << ": Skip any further processing if health check params are invalid.";
2970             healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_READS_PENDING;
2971             lock.unlock();
2972             onHealthStatus(healthListener, healthStatusToReport);
2973             // Triggering data loader start. This is a one-time action.
2974             fsmStep();
2975             return;
2976         }
2977 
2978         // Don't schedule timer job less than 500ms in advance.
2979         static constexpr auto kTolerance = 500ms;
2980 
2981         const auto blockedTimeout = std::chrono::milliseconds(mHealthCheckParams.blockedTimeoutMs);
2982         const auto unhealthyTimeout =
2983                 std::chrono::milliseconds(mHealthCheckParams.unhealthyTimeoutMs);
2984         const auto unhealthyMonitoring =
2985                 std::max(1000ms,
2986                          std::chrono::milliseconds(mHealthCheckParams.unhealthyMonitoringMs));
2987 
2988         const auto delta = elapsedMsSinceKernelTs(now, kernelTsUs);
2989 
2990         Milliseconds checkBackAfter;
2991         if (delta + kTolerance < blockedTimeout) {
2992             LOG(DEBUG) << id() << ": Report reads pending and wait for blocked status.";
2993             checkBackAfter = blockedTimeout - delta;
2994             healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_READS_PENDING;
2995         } else if (delta + kTolerance < unhealthyTimeout) {
2996             LOG(DEBUG) << id() << ": Report blocked and wait for unhealthy.";
2997             checkBackAfter = unhealthyTimeout - delta;
2998             healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_BLOCKED;
2999         } else {
3000             LOG(DEBUG) << id() << ": Report unhealthy and continue monitoring.";
3001             checkBackAfter = unhealthyMonitoring;
3002             healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY;
3003         }
3004         LOG(DEBUG) << id() << ": updateHealthStatus in " << double(checkBackAfter.count()) / 1000.0
3005                    << "secs";
3006         mService.addTimedJob(*mService.mTimedQueue, id(), checkBackAfter,
3007                              [this]() { updateHealthStatus(); });
3008     }
3009 
3010     // With kTolerance we are expecting these to execute before the next update.
3011     if (healthStatusToReport != -1) {
3012         onHealthStatus(healthListener, healthStatusToReport);
3013     }
3014 
3015     fsmStep();
3016 }
3017 
elapsedMsSinceKernelTs(TimePoint now,BootClockTsUs kernelTsUs)3018 Milliseconds IncrementalService::DataLoaderStub::elapsedMsSinceKernelTs(TimePoint now,
3019                                                                         BootClockTsUs kernelTsUs) {
3020     const auto kernelDeltaUs = kernelTsUs - mHealthBase.kernelTsUs;
3021     const auto userTs = mHealthBase.userTs + std::chrono::microseconds(kernelDeltaUs);
3022     return std::chrono::duration_cast<Milliseconds>(now - userTs);
3023 }
3024 
initializeHealthControl()3025 const incfs::UniqueControl& IncrementalService::DataLoaderStub::initializeHealthControl() {
3026     if (mHealthPath.empty()) {
3027         resetHealthControl();
3028         return mHealthControl;
3029     }
3030     if (mHealthControl.pendingReads() < 0) {
3031         mHealthControl = mService.mIncFs->openMount(mHealthPath);
3032     }
3033     if (mHealthControl.pendingReads() < 0) {
3034         LOG(ERROR) << "Failed to open health control for: " << id() << ", path: " << mHealthPath
3035                    << "(" << mHealthControl.cmd() << ":" << mHealthControl.pendingReads() << ":"
3036                    << mHealthControl.logs() << ")";
3037     }
3038     return mHealthControl;
3039 }
3040 
resetHealthControl()3041 void IncrementalService::DataLoaderStub::resetHealthControl() {
3042     mHealthControl = {};
3043 }
3044 
getOldestPendingReadTs()3045 BootClockTsUs IncrementalService::DataLoaderStub::getOldestPendingReadTs() {
3046     auto result = kMaxBootClockTsUs;
3047 
3048     const auto& control = initializeHealthControl();
3049     if (control.pendingReads() < 0) {
3050         return result;
3051     }
3052 
3053     if (mService.mIncFs->waitForPendingReads(control, 0ms, &mLastPendingReads) !=
3054                 android::incfs::WaitResult::HaveData ||
3055         mLastPendingReads.empty()) {
3056         // Clear previous pending reads
3057         mLastPendingReads.clear();
3058         return result;
3059     }
3060 
3061     LOG(DEBUG) << id() << ": pendingReads: fd(" << control.pendingReads() << "), count("
3062                << mLastPendingReads.size() << "), block: " << mLastPendingReads.front().block
3063                << ", time: " << mLastPendingReads.front().bootClockTsUs
3064                << ", uid: " << mLastPendingReads.front().uid;
3065 
3066     return getOldestTsFromLastPendingReads();
3067 }
3068 
registerForPendingReads()3069 void IncrementalService::DataLoaderStub::registerForPendingReads() {
3070     const auto pendingReadsFd = mHealthControl.pendingReads();
3071     if (pendingReadsFd < 0) {
3072         return;
3073     }
3074 
3075     LOG(DEBUG) << id() << ": addFd(pendingReadsFd): " << pendingReadsFd;
3076 
3077     mService.mLooper->addFd(
3078             pendingReadsFd, android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT,
3079             [](int, int, void* data) -> int {
3080                 auto self = (DataLoaderStub*)data;
3081                 self->updateHealthStatus(/*baseline=*/true);
3082                 return 0;
3083             },
3084             this);
3085     mService.mLooper->wake();
3086 }
3087 
getOldestTsFromLastPendingReads()3088 BootClockTsUs IncrementalService::DataLoaderStub::getOldestTsFromLastPendingReads() {
3089     auto result = kMaxBootClockTsUs;
3090     for (auto&& pendingRead : mLastPendingReads) {
3091         result = std::min(result, pendingRead.bootClockTsUs);
3092     }
3093     return result;
3094 }
3095 
getMetrics(android::os::PersistableBundle * result)3096 void IncrementalService::DataLoaderStub::getMetrics(android::os::PersistableBundle* result) {
3097     const auto duration = elapsedMsSinceOldestPendingRead();
3098     if (duration >= 0) {
3099         const auto& kMetricsMillisSinceOldestPendingRead =
3100                 os::incremental::BnIncrementalService::METRICS_MILLIS_SINCE_OLDEST_PENDING_READ();
3101         result->putLong(String16(kMetricsMillisSinceOldestPendingRead.c_str()), duration);
3102     }
3103     const auto& kMetricsStorageHealthStatusCode =
3104             os::incremental::BnIncrementalService::METRICS_STORAGE_HEALTH_STATUS_CODE();
3105     result->putInt(String16(kMetricsStorageHealthStatusCode.c_str()), mHealthStatus);
3106     const auto& kMetricsDataLoaderStatusCode =
3107             os::incremental::BnIncrementalService::METRICS_DATA_LOADER_STATUS_CODE();
3108     result->putInt(String16(kMetricsDataLoaderStatusCode.c_str()), mCurrentStatus);
3109     const auto& kMetricsMillisSinceLastDataLoaderBind =
3110             os::incremental::BnIncrementalService::METRICS_MILLIS_SINCE_LAST_DATA_LOADER_BIND();
3111     result->putLong(String16(kMetricsMillisSinceLastDataLoaderBind.c_str()),
3112                     elapsedMcs(mPreviousBindTs, mService.mClock->now()) / 1000);
3113     const auto& kMetricsDataLoaderBindDelayMillis =
3114             os::incremental::BnIncrementalService::METRICS_DATA_LOADER_BIND_DELAY_MILLIS();
3115     result->putLong(String16(kMetricsDataLoaderBindDelayMillis.c_str()),
3116                     mPreviousBindDelay.count());
3117 }
3118 
elapsedMsSinceOldestPendingRead()3119 long IncrementalService::DataLoaderStub::elapsedMsSinceOldestPendingRead() {
3120     const auto oldestPendingReadKernelTs = getOldestTsFromLastPendingReads();
3121     if (oldestPendingReadKernelTs == kMaxBootClockTsUs) {
3122         return 0;
3123     }
3124     return elapsedMsSinceKernelTs(Clock::now(), oldestPendingReadKernelTs).count();
3125 }
3126 
unregisterFromPendingReads()3127 void IncrementalService::DataLoaderStub::unregisterFromPendingReads() {
3128     const auto pendingReadsFd = mHealthControl.pendingReads();
3129     if (pendingReadsFd < 0) {
3130         return;
3131     }
3132 
3133     LOG(DEBUG) << id() << ": removeFd(pendingReadsFd): " << pendingReadsFd;
3134 
3135     mService.mLooper->removeFd(pendingReadsFd);
3136     mService.mLooper->wake();
3137 }
3138 
setHealthListener(const StorageHealthCheckParams & healthCheckParams,StorageHealthListener && healthListener)3139 void IncrementalService::DataLoaderStub::setHealthListener(
3140         const StorageHealthCheckParams& healthCheckParams, StorageHealthListener&& healthListener) {
3141     std::lock_guard lock(mMutex);
3142     mHealthCheckParams = healthCheckParams;
3143     mHealthListener = std::move(healthListener);
3144     if (!mHealthListener) {
3145         mHealthCheckParams.blockedTimeoutMs = -1;
3146     }
3147 }
3148 
toHexString(const RawMetadata & metadata)3149 static std::string toHexString(const RawMetadata& metadata) {
3150     int n = metadata.size();
3151     std::string res(n * 2, '\0');
3152     // Same as incfs::toString(fileId)
3153     static constexpr char kHexChar[] = "0123456789abcdef";
3154     for (int i = 0; i < n; ++i) {
3155         res[i * 2] = kHexChar[(metadata[i] & 0xf0) >> 4];
3156         res[i * 2 + 1] = kHexChar[(metadata[i] & 0x0f)];
3157     }
3158     return res;
3159 }
3160 
onDump(int fd)3161 void IncrementalService::DataLoaderStub::onDump(int fd) {
3162     dprintf(fd, "    dataLoader: {\n");
3163     dprintf(fd, "      currentStatus: %d\n", mCurrentStatus);
3164     dprintf(fd, "      currentStatusTs: %lldmcs\n",
3165             (long long)(elapsedMcs(mCurrentStatusTs, Clock::now())));
3166     dprintf(fd, "      targetStatus: %d\n", mTargetStatus);
3167     dprintf(fd, "      targetStatusTs: %lldmcs\n",
3168             (long long)(elapsedMcs(mTargetStatusTs, Clock::now())));
3169     dprintf(fd, "      health: {\n");
3170     dprintf(fd, "        path: %s\n", mHealthPath.c_str());
3171     dprintf(fd, "        base: %lldmcs (%lld)\n",
3172             (long long)(elapsedMcs(mHealthBase.userTs, Clock::now())),
3173             (long long)mHealthBase.kernelTsUs);
3174     dprintf(fd, "        blockedTimeoutMs: %d\n", int(mHealthCheckParams.blockedTimeoutMs));
3175     dprintf(fd, "        unhealthyTimeoutMs: %d\n", int(mHealthCheckParams.unhealthyTimeoutMs));
3176     dprintf(fd, "        unhealthyMonitoringMs: %d\n",
3177             int(mHealthCheckParams.unhealthyMonitoringMs));
3178     dprintf(fd, "        lastPendingReads: \n");
3179     const auto control = mService.mIncFs->openMount(mHealthPath);
3180     for (auto&& pendingRead : mLastPendingReads) {
3181         dprintf(fd, "          fileId: %s\n", IncFsWrapper::toString(pendingRead.id).c_str());
3182         const auto metadata = mService.mIncFs->getMetadata(control, pendingRead.id);
3183         dprintf(fd, "          metadataHex: %s\n", toHexString(metadata).c_str());
3184         dprintf(fd, "          blockIndex: %d\n", pendingRead.block);
3185         dprintf(fd, "          bootClockTsUs: %lld\n", (long long)pendingRead.bootClockTsUs);
3186     }
3187     dprintf(fd, "        bind: %llds ago (delay: %llds)\n",
3188             (long long)(elapsedMcs(mPreviousBindTs, mService.mClock->now()) / 1000000),
3189             (long long)(mPreviousBindDelay.count() / 1000));
3190     dprintf(fd, "      }\n");
3191     const auto& params = mParams;
3192     dprintf(fd, "      dataLoaderParams: {\n");
3193     dprintf(fd, "        type: %s\n", toString(params.type).c_str());
3194     dprintf(fd, "        packageName: %s\n", params.packageName.c_str());
3195     dprintf(fd, "        className: %s\n", params.className.c_str());
3196     dprintf(fd, "        arguments: %s\n", params.arguments.c_str());
3197     dprintf(fd, "      }\n");
3198     dprintf(fd, "    }\n");
3199 }
3200 
opChanged(int32_t,const String16 &)3201 void IncrementalService::AppOpsListener::opChanged(int32_t, const String16&) {
3202     incrementalService.onAppOpChanged(packageName);
3203 }
3204 
setStorageParams(bool enableReadLogs,int32_t * _aidl_return)3205 binder::Status IncrementalService::IncrementalServiceConnector::setStorageParams(
3206         bool enableReadLogs, int32_t* _aidl_return) {
3207     *_aidl_return = incrementalService.setStorageParams(storage, enableReadLogs);
3208     return binder::Status::ok();
3209 }
3210 
idFromMetadata(std::span<const uint8_t> metadata)3211 FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
3212     return IncFs_FileIdFromMetadata({(const char*)metadata.data(), (IncFsSize)metadata.size()});
3213 }
3214 
3215 } // namespace android::incremental
3216