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