blob: afce260ed41e4b8461d1e4eabc6ef9e7fe670910 [file] [log] [blame]
Songchun Fan3c82a302019-11-29 14:23:45 -08001/*
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/file.h>
22#include <android-base/logging.h>
23#include <android-base/properties.h>
24#include <android-base/stringprintf.h>
25#include <android-base/strings.h>
26#include <android/content/pm/IDataLoaderStatusListener.h>
27#include <android/os/IVold.h>
28#include <androidfw/ZipFileRO.h>
29#include <androidfw/ZipUtils.h>
30#include <binder/BinderService.h>
31#include <binder/ParcelFileDescriptor.h>
32#include <binder/Status.h>
33#include <sys/stat.h>
34#include <uuid/uuid.h>
35#include <zlib.h>
36
37#include <iterator>
38#include <span>
39#include <stack>
40#include <thread>
41#include <type_traits>
42
43#include "Metadata.pb.h"
44
45using namespace std::literals;
46using namespace android::content::pm;
47
48namespace android::incremental {
49
50namespace {
51
52using IncrementalFileSystemControlParcel =
53 ::android::os::incremental::IncrementalFileSystemControlParcel;
54
55struct Constants {
56 static constexpr auto backing = "backing_store"sv;
57 static constexpr auto mount = "mount"sv;
58 static constexpr auto image = "incfs.img"sv;
59 static constexpr auto storagePrefix = "st"sv;
60 static constexpr auto mountpointMdPrefix = ".mountpoint."sv;
61 static constexpr auto infoMdName = ".info"sv;
62};
63
64static const Constants& constants() {
65 static Constants c;
66 return c;
67}
68
69template <base::LogSeverity level = base::ERROR>
70bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
71 auto cstr = path::c_str(name);
72 if (::mkdir(cstr, mode)) {
73 if (errno != EEXIST) {
74 PLOG(level) << "Can't create directory '" << name << '\'';
75 return false;
76 }
77 struct stat st;
78 if (::stat(cstr, &st) || !S_ISDIR(st.st_mode)) {
79 PLOG(level) << "Path exists but is not a directory: '" << name << '\'';
80 return false;
81 }
82 }
83 return true;
84}
85
86static std::string toMountKey(std::string_view path) {
87 if (path.empty()) {
88 return "@none";
89 }
90 if (path == "/"sv) {
91 return "@root";
92 }
93 if (path::isAbsolute(path)) {
94 path.remove_prefix(1);
95 }
96 std::string res(path);
97 std::replace(res.begin(), res.end(), '/', '_');
98 std::replace(res.begin(), res.end(), '@', '_');
99 return res;
100}
101
102static std::pair<std::string, std::string> makeMountDir(std::string_view incrementalDir,
103 std::string_view path) {
104 auto mountKey = toMountKey(path);
105 const auto prefixSize = mountKey.size();
106 for (int counter = 0; counter < 1000;
107 mountKey.resize(prefixSize), base::StringAppendF(&mountKey, "%d", counter++)) {
108 auto mountRoot = path::join(incrementalDir, mountKey);
109 if (mkdirOrLog(mountRoot, 0770, false)) {
110 return {mountKey, mountRoot};
111 }
112 }
113 return {};
114}
115
116template <class ProtoMessage, class Control>
117static ProtoMessage parseFromIncfs(const IncFsWrapper* incfs, Control&& control,
118 std::string_view path) {
119 struct stat st;
120 if (::stat(path::c_str(path), &st)) {
121 return {};
122 }
123 auto md = incfs->getMetadata(control, st.st_ino);
124 ProtoMessage message;
125 return message.ParseFromArray(md.data(), md.size()) ? message : ProtoMessage{};
126}
127
128static bool isValidMountTarget(std::string_view path) {
129 return path::isAbsolute(path) && path::isEmptyDir(path).value_or(true);
130}
131
132std::string makeBindMdName() {
133 static constexpr auto uuidStringSize = 36;
134
135 uuid_t guid;
136 uuid_generate(guid);
137
138 std::string name;
139 const auto prefixSize = constants().mountpointMdPrefix.size();
140 name.reserve(prefixSize + uuidStringSize);
141
142 name = constants().mountpointMdPrefix;
143 name.resize(prefixSize + uuidStringSize);
144 uuid_unparse(guid, name.data() + prefixSize);
145
146 return name;
147}
148} // namespace
149
150IncrementalService::IncFsMount::~IncFsMount() {
151 incrementalService.mIncrementalManager->destroyDataLoader(mountId);
152 control.reset();
153 LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
154 for (auto&& [target, _] : bindPoints) {
155 LOG(INFO) << "\tbind: " << target;
156 incrementalService.mVold->unmountIncFs(target);
157 }
158 LOG(INFO) << "\troot: " << root;
159 incrementalService.mVold->unmountIncFs(path::join(root, constants().mount));
160 cleanupFilesystem(root);
161}
162
163auto IncrementalService::IncFsMount::makeStorage(StorageId id) -> StorageMap::iterator {
164 metadata::Storage st;
165 st.set_id(id);
166 auto metadata = st.SerializeAsString();
167
168 std::string name;
169 for (int no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), i = 0;
170 i < 1024 && no >= 0; no = nextStorageDirNo.fetch_add(1, std::memory_order_relaxed), ++i) {
171 name.clear();
172 base::StringAppendF(&name, "%.*s%d", int(constants().storagePrefix.size()),
173 constants().storagePrefix.data(), no);
174 if (auto node =
175 incrementalService.mIncFs->makeDir(control, name, INCFS_ROOT_INODE, metadata);
176 node >= 0) {
177 std::lock_guard l(lock);
178 return storages.insert_or_assign(id, Storage{std::move(name), node}).first;
179 }
180 }
181 nextStorageDirNo = 0;
182 return storages.end();
183}
184
185void IncrementalService::IncFsMount::cleanupFilesystem(std::string_view root) {
186 ::unlink(path::join(root, constants().backing, constants().image).c_str());
187 ::rmdir(path::join(root, constants().backing).c_str());
188 ::rmdir(path::join(root, constants().mount).c_str());
189 ::rmdir(path::c_str(root));
190}
191
192IncrementalService::IncrementalService(const ServiceManagerWrapper& sm, std::string_view rootDir)
193 : mVold(sm.getVoldService()),
194 mIncrementalManager(sm.getIncrementalManager()),
195 mIncFs(sm.getIncFs()),
196 mIncrementalDir(rootDir) {
197 if (!mVold) {
198 LOG(FATAL) << "Vold service is unavailable";
199 }
200 if (!mIncrementalManager) {
201 LOG(FATAL) << "IncrementalManager service is unavailable";
202 }
203 // TODO(b/136132412): check that root dir should already exist
204 // TODO(b/136132412): enable mount existing dirs after SELinux rules are merged
205 // mountExistingImages();
206}
207
208IncrementalService::~IncrementalService() = default;
209
210std::optional<std::future<void>> IncrementalService::onSystemReady() {
211 std::promise<void> threadFinished;
212 if (mSystemReady.exchange(true)) {
213 return {};
214 }
215
216 std::vector<IfsMountPtr> mounts;
217 {
218 std::lock_guard l(mLock);
219 mounts.reserve(mMounts.size());
220 for (auto&& [id, ifs] : mMounts) {
221 if (ifs->mountId == id) {
222 mounts.push_back(ifs);
223 }
224 }
225 }
226
227 std::thread([this, mounts = std::move(mounts)]() {
228 std::vector<IfsMountPtr> failedLoaderMounts;
229 for (auto&& ifs : mounts) {
230 if (prepareDataLoader(*ifs, nullptr)) {
231 LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
232 } else {
233 LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
234 failedLoaderMounts.push_back(std::move(ifs));
235 }
236 }
237
238 while (!failedLoaderMounts.empty()) {
239 LOG(WARNING) << "Deleting failed mount " << failedLoaderMounts.back()->mountId;
240 deleteStorage(*failedLoaderMounts.back());
241 failedLoaderMounts.pop_back();
242 }
243 mPrepareDataLoaders.set_value_at_thread_exit();
244 }).detach();
245 return mPrepareDataLoaders.get_future();
246}
247
248auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator {
249 for (;;) {
250 if (mNextId == kMaxStorageId) {
251 mNextId = 0;
252 }
253 auto id = ++mNextId;
254 auto [it, inserted] = mMounts.try_emplace(id, nullptr);
255 if (inserted) {
256 return it;
257 }
258 }
259}
260
261StorageId IncrementalService::createStorage(std::string_view mountPoint,
262 DataLoaderParamsParcel&& dataLoaderParams,
263 CreateOptions options) {
264 LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
265 if (!path::isAbsolute(mountPoint)) {
266 LOG(ERROR) << "path is not absolute: " << mountPoint;
267 return kInvalidStorageId;
268 }
269
270 auto mountNorm = path::normalize(mountPoint);
271 {
272 const auto id = findStorageId(mountNorm);
273 if (id != kInvalidStorageId) {
274 if (options & CreateOptions::OpenExisting) {
275 LOG(INFO) << "Opened existing storage " << id;
276 return id;
277 }
278 LOG(ERROR) << "Directory " << mountPoint << " is already mounted at storage " << id;
279 return kInvalidStorageId;
280 }
281 }
282
283 if (!(options & CreateOptions::CreateNew)) {
284 LOG(ERROR) << "not requirested create new storage, and it doesn't exist: " << mountPoint;
285 return kInvalidStorageId;
286 }
287
288 if (!path::isEmptyDir(mountNorm)) {
289 LOG(ERROR) << "Mounting over existing non-empty directory is not supported: " << mountNorm;
290 return kInvalidStorageId;
291 }
292 auto [mountKey, mountRoot] = makeMountDir(mIncrementalDir, mountNorm);
293 if (mountRoot.empty()) {
294 LOG(ERROR) << "Bad mount point";
295 return kInvalidStorageId;
296 }
297 // Make sure the code removes all crap it may create while still failing.
298 auto firstCleanup = [](const std::string* ptr) { IncFsMount::cleanupFilesystem(*ptr); };
299 auto firstCleanupOnFailure =
300 std::unique_ptr<std::string, decltype(firstCleanup)>(&mountRoot, firstCleanup);
301
302 auto mountTarget = path::join(mountRoot, constants().mount);
303 if (!mkdirOrLog(path::join(mountRoot, constants().backing)) || !mkdirOrLog(mountTarget)) {
304 return kInvalidStorageId;
305 }
306
307 const auto image = path::join(mountRoot, constants().backing, constants().image);
308 IncFsMount::Control control;
309 {
310 std::lock_guard l(mMountOperationLock);
311 IncrementalFileSystemControlParcel controlParcel;
312 auto status = mVold->mountIncFs(image, mountTarget, incfs::truncate, &controlParcel);
313 if (!status.isOk()) {
314 LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
315 return kInvalidStorageId;
316 }
317 if (!controlParcel.cmd || !controlParcel.log) {
318 LOG(ERROR) << "Vold::mountIncFs() returned invalid control parcel.";
319 return kInvalidStorageId;
320 }
321 control.cmdFd = controlParcel.cmd->release();
322 control.logFd = controlParcel.log->release();
323 }
324
325 std::unique_lock l(mLock);
326 const auto mountIt = getStorageSlotLocked();
327 const auto mountId = mountIt->first;
328 l.unlock();
329
330 auto ifs =
331 std::make_shared<IncFsMount>(std::move(mountRoot), mountId, std::move(control), *this);
332 // Now it's the |ifs|'s responsibility to clean up after itself, and the only cleanup we need
333 // is the removal of the |ifs|.
334 firstCleanupOnFailure.release();
335
336 auto secondCleanup = [this, &l](auto itPtr) {
337 if (!l.owns_lock()) {
338 l.lock();
339 }
340 mMounts.erase(*itPtr);
341 };
342 auto secondCleanupOnFailure =
343 std::unique_ptr<decltype(mountIt), decltype(secondCleanup)>(&mountIt, secondCleanup);
344
345 const auto storageIt = ifs->makeStorage(ifs->mountId);
346 if (storageIt == ifs->storages.end()) {
347 LOG(ERROR) << "Can't create default storage directory";
348 return kInvalidStorageId;
349 }
350
351 {
352 metadata::Mount m;
353 m.mutable_storage()->set_id(ifs->mountId);
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800354 m.mutable_loader()->set_type((int)dataLoaderParams.type);
Songchun Fan3c82a302019-11-29 14:23:45 -0800355 m.mutable_loader()->set_package_name(dataLoaderParams.packageName);
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800356 m.mutable_loader()->set_class_name(dataLoaderParams.className);
357 m.mutable_loader()->set_arguments(dataLoaderParams.arguments);
Songchun Fan3c82a302019-11-29 14:23:45 -0800358 const auto metadata = m.SerializeAsString();
359 m.mutable_loader()->release_arguments();
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800360 m.mutable_loader()->release_class_name();
Songchun Fan3c82a302019-11-29 14:23:45 -0800361 m.mutable_loader()->release_package_name();
362 if (auto err = mIncFs->makeFile(ifs->control, constants().infoMdName, INCFS_ROOT_INODE, 0,
363 metadata);
364 err < 0) {
365 LOG(ERROR) << "Saving mount metadata failed: " << -err;
366 return kInvalidStorageId;
367 }
368 }
369
370 const auto bk =
371 (options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
372 if (auto err = addBindMount(*ifs, storageIt->first, std::string(storageIt->second.name),
373 std::move(mountNorm), bk, l);
374 err < 0) {
375 LOG(ERROR) << "adding bind mount failed: " << -err;
376 return kInvalidStorageId;
377 }
378
379 // Done here as well, all data structures are in good state.
380 secondCleanupOnFailure.release();
381
382 if (!prepareDataLoader(*ifs, &dataLoaderParams)) {
383 LOG(ERROR) << "prepareDataLoader() failed";
384 deleteStorageLocked(*ifs, std::move(l));
385 return kInvalidStorageId;
386 }
387
388 mountIt->second = std::move(ifs);
389 l.unlock();
390 LOG(INFO) << "created storage " << mountId;
391 return mountId;
392}
393
394StorageId IncrementalService::createLinkedStorage(std::string_view mountPoint,
395 StorageId linkedStorage,
396 IncrementalService::CreateOptions options) {
397 if (!isValidMountTarget(mountPoint)) {
398 LOG(ERROR) << "Mount point is invalid or missing";
399 return kInvalidStorageId;
400 }
401
402 std::unique_lock l(mLock);
403 const auto& ifs = getIfsLocked(linkedStorage);
404 if (!ifs) {
405 LOG(ERROR) << "Ifs unavailable";
406 return kInvalidStorageId;
407 }
408
409 const auto mountIt = getStorageSlotLocked();
410 const auto storageId = mountIt->first;
411 const auto storageIt = ifs->makeStorage(storageId);
412 if (storageIt == ifs->storages.end()) {
413 LOG(ERROR) << "Can't create a new storage";
414 mMounts.erase(mountIt);
415 return kInvalidStorageId;
416 }
417
418 l.unlock();
419
420 const auto bk =
421 (options & CreateOptions::PermanentBind) ? BindKind::Permanent : BindKind::Temporary;
422 if (auto err = addBindMount(*ifs, storageIt->first, std::string(storageIt->second.name),
423 path::normalize(mountPoint), bk, l);
424 err < 0) {
425 LOG(ERROR) << "bindMount failed with error: " << err;
426 return kInvalidStorageId;
427 }
428
429 mountIt->second = ifs;
430 return storageId;
431}
432
433IncrementalService::BindPathMap::const_iterator IncrementalService::findStorageLocked(
434 std::string_view path) const {
435 auto bindPointIt = mBindsByPath.upper_bound(path);
436 if (bindPointIt == mBindsByPath.begin()) {
437 return mBindsByPath.end();
438 }
439 --bindPointIt;
440 if (!path::startsWith(path, bindPointIt->first)) {
441 return mBindsByPath.end();
442 }
443 return bindPointIt;
444}
445
446StorageId IncrementalService::findStorageId(std::string_view path) const {
447 std::lock_guard l(mLock);
448 auto it = findStorageLocked(path);
449 if (it == mBindsByPath.end()) {
450 return kInvalidStorageId;
451 }
452 return it->second->second.storage;
453}
454
455void IncrementalService::deleteStorage(StorageId storageId) {
456 const auto ifs = getIfs(storageId);
457 if (!ifs) {
458 return;
459 }
460 deleteStorage(*ifs);
461}
462
463void IncrementalService::deleteStorage(IncrementalService::IncFsMount& ifs) {
464 std::unique_lock l(ifs.lock);
465 deleteStorageLocked(ifs, std::move(l));
466}
467
468void IncrementalService::deleteStorageLocked(IncrementalService::IncFsMount& ifs,
469 std::unique_lock<std::mutex>&& ifsLock) {
470 const auto storages = std::move(ifs.storages);
471 // Don't move the bind points out: Ifs's dtor will use them to unmount everything.
472 const auto bindPoints = ifs.bindPoints;
473 ifsLock.unlock();
474
475 std::lock_guard l(mLock);
476 for (auto&& [id, _] : storages) {
477 if (id != ifs.mountId) {
478 mMounts.erase(id);
479 }
480 }
481 for (auto&& [path, _] : bindPoints) {
482 mBindsByPath.erase(path);
483 }
484 mMounts.erase(ifs.mountId);
485}
486
487StorageId IncrementalService::openStorage(std::string_view pathInMount) {
488 if (!path::isAbsolute(pathInMount)) {
489 return kInvalidStorageId;
490 }
491
492 return findStorageId(path::normalize(pathInMount));
493}
494
495Inode IncrementalService::nodeFor(StorageId storage, std::string_view subpath) const {
496 const auto ifs = getIfs(storage);
497 if (!ifs) {
498 return -1;
499 }
500 std::unique_lock l(ifs->lock);
501 auto storageIt = ifs->storages.find(storage);
502 if (storageIt == ifs->storages.end()) {
503 return -1;
504 }
505 if (subpath.empty() || subpath == "."sv) {
506 return storageIt->second.node;
507 }
508 auto path = path::join(ifs->root, constants().mount, storageIt->second.name, subpath);
509 l.unlock();
510 struct stat st;
511 if (::stat(path.c_str(), &st)) {
512 return -1;
513 }
514 return st.st_ino;
515}
516
517std::pair<Inode, std::string_view> IncrementalService::parentAndNameFor(
518 StorageId storage, std::string_view subpath) const {
519 auto name = path::basename(subpath);
520 if (name.empty()) {
521 return {-1, {}};
522 }
523 auto dir = path::dirname(subpath);
524 if (dir.empty() || dir == "/"sv) {
525 return {-1, {}};
526 }
527 auto inode = nodeFor(storage, dir);
528 return {inode, name};
529}
530
531IncrementalService::IfsMountPtr IncrementalService::getIfs(StorageId storage) const {
532 std::lock_guard l(mLock);
533 return getIfsLocked(storage);
534}
535
536const IncrementalService::IfsMountPtr& IncrementalService::getIfsLocked(StorageId storage) const {
537 auto it = mMounts.find(storage);
538 if (it == mMounts.end()) {
539 static const IfsMountPtr kEmpty = {};
540 return kEmpty;
541 }
542 return it->second;
543}
544
545int IncrementalService::bind(StorageId storage, std::string_view sourceSubdir,
546 std::string_view target, BindKind kind) {
547 if (!isValidMountTarget(target)) {
548 return -EINVAL;
549 }
550
551 const auto ifs = getIfs(storage);
552 if (!ifs) {
553 return -EINVAL;
554 }
555 std::unique_lock l(ifs->lock);
556 const auto storageInfo = ifs->storages.find(storage);
557 if (storageInfo == ifs->storages.end()) {
558 return -EINVAL;
559 }
560 auto source = path::join(storageInfo->second.name, sourceSubdir);
561 l.unlock();
562 std::unique_lock l2(mLock, std::defer_lock);
563 return addBindMount(*ifs, storage, std::move(source), path::normalize(target), kind, l2);
564}
565
566int IncrementalService::unbind(StorageId storage, std::string_view target) {
567 if (!path::isAbsolute(target)) {
568 return -EINVAL;
569 }
570
571 LOG(INFO) << "Removing bind point " << target;
572
573 // Here we should only look up by the exact target, not by a subdirectory of any existing mount,
574 // otherwise there's a chance to unmount something completely unrelated
575 const auto norm = path::normalize(target);
576 std::unique_lock l(mLock);
577 const auto storageIt = mBindsByPath.find(norm);
578 if (storageIt == mBindsByPath.end() || storageIt->second->second.storage != storage) {
579 return -EINVAL;
580 }
581 const auto bindIt = storageIt->second;
582 const auto storageId = bindIt->second.storage;
583 const auto ifs = getIfsLocked(storageId);
584 if (!ifs) {
585 LOG(ERROR) << "Internal error: storageId " << storageId << " for bound path " << target
586 << " is missing";
587 return -EFAULT;
588 }
589 mBindsByPath.erase(storageIt);
590 l.unlock();
591
592 mVold->unmountIncFs(bindIt->first);
593 std::unique_lock l2(ifs->lock);
594 if (ifs->bindPoints.size() <= 1) {
595 ifs->bindPoints.clear();
596 deleteStorageLocked(*ifs, std::move(l2));
597 } else {
598 const std::string savedFile = std::move(bindIt->second.savedFilename);
599 ifs->bindPoints.erase(bindIt);
600 l2.unlock();
601 if (!savedFile.empty()) {
602 mIncFs->unlink(ifs->control, INCFS_ROOT_INODE, savedFile);
603 }
604 }
605 return 0;
606}
607
608Inode IncrementalService::makeFile(StorageId storageId, std::string_view pathUnderStorage,
609 long size, std::string_view metadata,
610 std::string_view signature) {
611 (void)signature;
612 auto [parentInode, name] = parentAndNameFor(storageId, pathUnderStorage);
613 if (parentInode < 0) {
614 return -EINVAL;
615 }
616 if (auto ifs = getIfs(storageId)) {
617 auto inode = mIncFs->makeFile(ifs->control, name, parentInode, size, metadata);
618 if (inode < 0) {
619 return inode;
620 }
621 auto metadataBytes = std::vector<uint8_t>();
622 if (metadata.data() != nullptr && metadata.size() > 0) {
623 metadataBytes.insert(metadataBytes.end(), &metadata.data()[0],
624 &metadata.data()[metadata.size()]);
625 }
626 mIncrementalManager->newFileForDataLoader(ifs->mountId, inode, metadataBytes);
627 return inode;
628 }
629 return -EINVAL;
630}
631
632Inode IncrementalService::makeDir(StorageId storageId, std::string_view pathUnderStorage,
633 std::string_view metadata) {
634 auto [parentInode, name] = parentAndNameFor(storageId, pathUnderStorage);
635 if (parentInode < 0) {
636 return -EINVAL;
637 }
638 if (auto ifs = getIfs(storageId)) {
639 return mIncFs->makeDir(ifs->control, name, parentInode, metadata);
640 }
641 return -EINVAL;
642}
643
644Inode IncrementalService::makeDirs(StorageId storageId, std::string_view pathUnderStorage,
645 std::string_view metadata) {
646 const auto ifs = getIfs(storageId);
647 if (!ifs) {
648 return -EINVAL;
649 }
650 std::string_view parentDir(pathUnderStorage);
651 auto p = parentAndNameFor(storageId, pathUnderStorage);
652 std::stack<std::string> pathsToCreate;
653 while (p.first < 0) {
654 parentDir = path::dirname(parentDir);
655 pathsToCreate.emplace(parentDir);
656 p = parentAndNameFor(storageId, parentDir);
657 }
658 Inode inode;
659 while (!pathsToCreate.empty()) {
660 p = parentAndNameFor(storageId, pathsToCreate.top());
661 pathsToCreate.pop();
662 inode = mIncFs->makeDir(ifs->control, p.second, p.first, metadata);
663 if (inode < 0) {
664 return inode;
665 }
666 }
667 return mIncFs->makeDir(ifs->control, path::basename(pathUnderStorage), inode, metadata);
668}
669
670int IncrementalService::link(StorageId storage, Inode item, Inode newParent,
671 std::string_view newName) {
672 if (auto ifs = getIfs(storage)) {
673 return mIncFs->link(ifs->control, item, newParent, newName);
674 }
675 return -EINVAL;
676}
677
678int IncrementalService::unlink(StorageId storage, Inode parent, std::string_view name) {
679 if (auto ifs = getIfs(storage)) {
680 return mIncFs->unlink(ifs->control, parent, name);
681 }
682 return -EINVAL;
683}
684
685int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage, std::string&& sourceSubdir,
686 std::string&& target, BindKind kind,
687 std::unique_lock<std::mutex>& mainLock) {
688 if (!isValidMountTarget(target)) {
689 return -EINVAL;
690 }
691
692 std::string mdFileName;
693 if (kind != BindKind::Temporary) {
694 metadata::BindPoint bp;
695 bp.set_storage_id(storage);
696 bp.set_allocated_dest_path(&target);
697 bp.set_allocated_source_subdir(&sourceSubdir);
698 const auto metadata = bp.SerializeAsString();
699 bp.release_source_subdir();
700 bp.release_dest_path();
701 mdFileName = makeBindMdName();
702 auto node = mIncFs->makeFile(ifs.control, mdFileName, INCFS_ROOT_INODE, 0, metadata);
703 if (node < 0) {
704 return int(node);
705 }
706 }
707
708 return addBindMountWithMd(ifs, storage, std::move(mdFileName), std::move(sourceSubdir),
709 std::move(target), kind, mainLock);
710}
711
712int IncrementalService::addBindMountWithMd(IncrementalService::IncFsMount& ifs, StorageId storage,
713 std::string&& metadataName, std::string&& sourceSubdir,
714 std::string&& target, BindKind kind,
715 std::unique_lock<std::mutex>& mainLock) {
716 LOG(INFO) << "Adding bind mount: " << sourceSubdir << " -> " << target;
717 {
718 auto path = path::join(ifs.root, constants().mount, sourceSubdir);
719 std::lock_guard l(mMountOperationLock);
720 const auto status = mVold->bindMount(path, target);
721 if (!status.isOk()) {
722 LOG(ERROR) << "Calling Vold::bindMount() failed: " << status.toString8();
723 return status.exceptionCode() == binder::Status::EX_SERVICE_SPECIFIC
724 ? status.serviceSpecificErrorCode() > 0 ? -status.serviceSpecificErrorCode()
725 : status.serviceSpecificErrorCode() == 0
726 ? -EFAULT
727 : status.serviceSpecificErrorCode()
728 : -EIO;
729 }
730 }
731
732 if (!mainLock.owns_lock()) {
733 mainLock.lock();
734 }
735 std::lock_guard l(ifs.lock);
736 const auto [it, _] =
737 ifs.bindPoints.insert_or_assign(target,
738 IncFsMount::Bind{storage, std::move(metadataName),
739 std::move(sourceSubdir), kind});
740 mBindsByPath[std::move(target)] = it;
741 return 0;
742}
743
744RawMetadata IncrementalService::getMetadata(StorageId storage, Inode node) const {
745 const auto ifs = getIfs(storage);
746 if (!ifs) {
747 return {};
748 }
749 return mIncFs->getMetadata(ifs->control, node);
750}
751
752std::vector<std::string> IncrementalService::listFiles(StorageId storage) const {
753 const auto ifs = getIfs(storage);
754 if (!ifs) {
755 return {};
756 }
757
758 std::unique_lock l(ifs->lock);
759 auto subdirIt = ifs->storages.find(storage);
760 if (subdirIt == ifs->storages.end()) {
761 return {};
762 }
763 auto dir = path::join(ifs->root, constants().mount, subdirIt->second.name);
764 l.unlock();
765
766 const auto prefixSize = dir.size() + 1;
767 std::vector<std::string> todoDirs{std::move(dir)};
768 std::vector<std::string> result;
769 do {
770 auto currDir = std::move(todoDirs.back());
771 todoDirs.pop_back();
772
773 auto d =
774 std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(currDir.c_str()), ::closedir);
775 while (auto e = ::readdir(d.get())) {
776 if (e->d_type == DT_REG) {
777 result.emplace_back(
778 path::join(std::string_view(currDir).substr(prefixSize), e->d_name));
779 continue;
780 }
781 if (e->d_type == DT_DIR) {
782 if (e->d_name == "."sv || e->d_name == ".."sv) {
783 continue;
784 }
785 todoDirs.emplace_back(path::join(currDir, e->d_name));
786 continue;
787 }
788 }
789 } while (!todoDirs.empty());
790 return result;
791}
792
793bool IncrementalService::startLoading(StorageId storage) const {
794 const auto ifs = getIfs(storage);
795 if (!ifs) {
796 return false;
797 }
798 bool started = false;
799 std::unique_lock l(ifs->lock);
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800800 if (ifs->dataLoaderStatus != IDataLoaderStatusListener::DATA_LOADER_CREATED) {
Songchun Fan3c82a302019-11-29 14:23:45 -0800801 if (ifs->dataLoaderReady.wait_for(l, Seconds(5)) == std::cv_status::timeout) {
802 LOG(ERROR) << "Timeout waiting for data loader to be ready";
803 return false;
804 }
805 }
806 auto status = mIncrementalManager->startDataLoader(ifs->mountId, &started);
807 if (!status.isOk()) {
808 return false;
809 }
810 return started;
811}
812
813void IncrementalService::mountExistingImages() {
814 auto d = std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(mIncrementalDir.c_str()),
815 ::closedir);
816 while (auto e = ::readdir(d.get())) {
817 if (e->d_type != DT_DIR) {
818 continue;
819 }
820 if (e->d_name == "."sv || e->d_name == ".."sv) {
821 continue;
822 }
823 auto root = path::join(mIncrementalDir, e->d_name);
824 if (!mountExistingImage(root, e->d_name)) {
825 IncFsMount::cleanupFilesystem(root);
826 }
827 }
828}
829
830bool IncrementalService::mountExistingImage(std::string_view root, std::string_view key) {
831 LOG(INFO) << "Trying to mount: " << key;
832
833 auto mountTarget = path::join(root, constants().mount);
834 const auto image = path::join(root, constants().backing, constants().image);
835
836 IncFsMount::Control control;
837 IncrementalFileSystemControlParcel controlParcel;
838 auto status = mVold->mountIncFs(image, mountTarget, 0, &controlParcel);
839 if (!status.isOk()) {
840 LOG(ERROR) << "Vold::mountIncFs() failed: " << status.toString8();
841 return false;
842 }
843 if (controlParcel.cmd) {
844 control.cmdFd = controlParcel.cmd->release();
845 }
846 if (controlParcel.log) {
847 control.logFd = controlParcel.log->release();
848 }
849
850 auto ifs = std::make_shared<IncFsMount>(std::string(root), -1, std::move(control), *this);
851
852 auto m = parseFromIncfs<metadata::Mount>(mIncFs.get(), ifs->control,
853 path::join(mountTarget, constants().infoMdName));
854 if (!m.has_loader() || !m.has_storage()) {
855 LOG(ERROR) << "Bad mount metadata in mount at " << root;
856 return false;
857 }
858
859 ifs->mountId = m.storage().id();
860 mNextId = std::max(mNextId, ifs->mountId + 1);
861
862 std::vector<std::pair<std::string, metadata::BindPoint>> bindPoints;
863 auto d = std::unique_ptr<DIR, decltype(&::closedir)>(::opendir(path::c_str(mountTarget)),
864 ::closedir);
865 while (auto e = ::readdir(d.get())) {
866 if (e->d_type == DT_REG) {
867 auto name = std::string_view(e->d_name);
868 if (name.starts_with(constants().mountpointMdPrefix)) {
869 bindPoints.emplace_back(name,
870 parseFromIncfs<metadata::BindPoint>(mIncFs.get(),
871 ifs->control,
872 path::join(mountTarget,
873 name)));
874 if (bindPoints.back().second.dest_path().empty() ||
875 bindPoints.back().second.source_subdir().empty()) {
876 bindPoints.pop_back();
877 mIncFs->unlink(ifs->control, INCFS_ROOT_INODE, name);
878 }
879 }
880 } else if (e->d_type == DT_DIR) {
881 if (e->d_name == "."sv || e->d_name == ".."sv) {
882 continue;
883 }
884 auto name = std::string_view(e->d_name);
885 if (name.starts_with(constants().storagePrefix)) {
886 auto md = parseFromIncfs<metadata::Storage>(mIncFs.get(), ifs->control,
887 path::join(mountTarget, name));
888 auto [_, inserted] = mMounts.try_emplace(md.id(), ifs);
889 if (!inserted) {
890 LOG(WARNING) << "Ignoring storage with duplicate id " << md.id()
891 << " for mount " << root;
892 continue;
893 }
894 ifs->storages.insert_or_assign(md.id(),
895 IncFsMount::Storage{std::string(name),
896 Inode(e->d_ino)});
897 mNextId = std::max(mNextId, md.id() + 1);
898 }
899 }
900 }
901
902 if (ifs->storages.empty()) {
903 LOG(WARNING) << "No valid storages in mount " << root;
904 return false;
905 }
906
907 int bindCount = 0;
908 for (auto&& bp : bindPoints) {
909 std::unique_lock l(mLock, std::defer_lock);
910 bindCount += !addBindMountWithMd(*ifs, bp.second.storage_id(), std::move(bp.first),
911 std::move(*bp.second.mutable_source_subdir()),
912 std::move(*bp.second.mutable_dest_path()),
913 BindKind::Permanent, l);
914 }
915
916 if (bindCount == 0) {
917 LOG(WARNING) << "No valid bind points for mount " << root;
918 deleteStorage(*ifs);
919 return false;
920 }
921
922 DataLoaderParamsParcel dlParams;
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800923 dlParams.type = (DataLoaderType)m.loader().type();
Songchun Fan3c82a302019-11-29 14:23:45 -0800924 dlParams.packageName = std::move(*m.mutable_loader()->mutable_package_name());
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800925 dlParams.className = std::move(*m.mutable_loader()->mutable_class_name());
926 dlParams.arguments = std::move(*m.mutable_loader()->mutable_arguments());
Songchun Fan3c82a302019-11-29 14:23:45 -0800927 if (!prepareDataLoader(*ifs, &dlParams)) {
928 deleteStorage(*ifs);
929 return false;
930 }
931
932 mMounts[ifs->mountId] = std::move(ifs);
933 return true;
934}
935
936bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
937 DataLoaderParamsParcel* params) {
938 if (!mSystemReady.load(std::memory_order_relaxed)) {
939 std::unique_lock l(ifs.lock);
940 if (params) {
941 if (ifs.savedDataLoaderParams) {
942 LOG(WARNING) << "Trying to pass second set of data loader parameters, ignored it";
943 } else {
944 ifs.savedDataLoaderParams = std::move(*params);
945 }
946 } else {
947 if (!ifs.savedDataLoaderParams) {
948 LOG(ERROR) << "Mount " << ifs.mountId
949 << " is broken: no data loader params (system is not ready yet)";
950 return false;
951 }
952 }
953 return true; // eventually...
954 }
955 if (base::GetBoolProperty("incremental.skip_loader", false)) {
956 LOG(INFO) << "Skipped data loader because of incremental.skip_loader property";
957 std::unique_lock l(ifs.lock);
958 ifs.savedDataLoaderParams.reset();
959 return true;
960 }
961
962 std::unique_lock l(ifs.lock);
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -0800963 if (ifs.dataLoaderStatus == IDataLoaderStatusListener::DATA_LOADER_CREATED) {
Songchun Fan3c82a302019-11-29 14:23:45 -0800964 LOG(INFO) << "Skipped data loader preparation because it already exists";
965 return true;
966 }
967
968 auto* dlp = params ? params
969 : ifs.savedDataLoaderParams ? &ifs.savedDataLoaderParams.value() : nullptr;
970 if (!dlp) {
971 LOG(ERROR) << "Mount " << ifs.mountId << " is broken: no data loader params";
972 return false;
973 }
974 FileSystemControlParcel fsControlParcel;
975 fsControlParcel.incremental = std::make_unique<IncrementalFileSystemControlParcel>();
976 fsControlParcel.incremental->cmd =
977 std::make_unique<os::ParcelFileDescriptor>(base::unique_fd(::dup(ifs.control.cmdFd)));
978 fsControlParcel.incremental->log =
979 std::make_unique<os::ParcelFileDescriptor>(base::unique_fd(::dup(ifs.control.logFd)));
980 sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this);
981 bool created = false;
982 auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,
983 listener, &created);
984 if (!status.isOk() || !created) {
985 LOG(ERROR) << "Failed to create a data loader for mount " << ifs.mountId;
986 return false;
987 }
988 ifs.savedDataLoaderParams.reset();
989 return true;
990}
991
992binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
993 int newStatus) {
994 std::unique_lock l(incrementalService.mLock);
995 const auto& ifs = incrementalService.getIfsLocked(mountId);
996 if (!ifs) {
997 LOG(WARNING) << "Received data loader status " << int(newStatus) << " for unknown mount "
998 << mountId;
999 return binder::Status::ok();
1000 }
1001 ifs->dataLoaderStatus = newStatus;
1002 switch (newStatus) {
1003 case IDataLoaderStatusListener::DATA_LOADER_NO_CONNECTION: {
1004 auto now = Clock::now();
1005 if (ifs->connectionLostTime.time_since_epoch().count() == 0) {
1006 ifs->connectionLostTime = now;
1007 break;
1008 }
1009 auto duration =
1010 std::chrono::duration_cast<Seconds>(now - ifs->connectionLostTime).count();
1011 if (duration >= 10) {
1012 incrementalService.mIncrementalManager->showHealthBlockedUI(mountId);
1013 }
1014 break;
1015 }
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -08001016 case IDataLoaderStatusListener::DATA_LOADER_CONNECTION_OK: {
1017 ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STARTED;
1018 break;
1019 }
1020 case IDataLoaderStatusListener::DATA_LOADER_CREATED: {
Songchun Fan3c82a302019-11-29 14:23:45 -08001021 ifs->dataLoaderReady.notify_one();
1022 break;
1023 }
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -08001024 case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
Songchun Fan3c82a302019-11-29 14:23:45 -08001025 ifs->dataLoaderStatus = IDataLoaderStatusListener::DATA_LOADER_STOPPED;
1026 incrementalService.deleteStorageLocked(*ifs, std::move(l));
1027 break;
1028 }
Alex Buynytskyy1ecfcec2019-12-17 12:10:41 -08001029 case IDataLoaderStatusListener::DATA_LOADER_STARTED: {
Songchun Fan3c82a302019-11-29 14:23:45 -08001030 break;
1031 }
1032 case IDataLoaderStatusListener::DATA_LOADER_STOPPED: {
1033 break;
1034 }
1035 default: {
1036 LOG(WARNING) << "Unknown data loader status: " << newStatus
1037 << " for mount: " << mountId;
1038 break;
1039 }
1040 }
1041
1042 return binder::Status::ok();
1043}
1044
1045} // namespace android::incremental