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 "incfs"
18
19 #include "incfs.h"
20
21 #include <IncrementalProperties.sysprop.h>
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/no_destructor.h>
25 #include <android-base/parsebool.h>
26 #include <android-base/stringprintf.h>
27 #include <android-base/strings.h>
28 #include <android-base/unique_fd.h>
29 #include <dirent.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <libgen.h>
33 #include <openssl/sha.h>
34 #include <selinux/android.h>
35 #include <selinux/selinux.h>
36 #include <sys/mount.h>
37 #include <sys/poll.h>
38 #include <sys/stat.h>
39 #include <sys/syscall.h>
40 #include <sys/types.h>
41 #include <sys/vfs.h>
42 #include <sys/xattr.h>
43 #include <unistd.h>
44
45 #include <chrono>
46 #include <fstream>
47 #include <iterator>
48 #include <mutex>
49 #include <optional>
50 #include <string_view>
51
52 #include "MountRegistry.h"
53 #include "path.h"
54
55 using namespace std::literals;
56
57 using android::base::StringPrintf;
58 using android::base::unique_fd;
59
60 struct IncFsControl final {
61 IncFsFd cmd;
62 IncFsFd pendingReads;
63 IncFsFd logs;
IncFsControlIncFsControl64 constexpr IncFsControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs)
65 : cmd(cmd), pendingReads(pendingReads), logs(logs) {}
66 };
67
registry()68 static android::incfs::MountRegistry& registry() {
69 static android::base::NoDestructor<android::incfs::MountRegistry> instance{};
70 return *instance;
71 }
72
openRaw(std::string_view file)73 static unique_fd openRaw(std::string_view file) {
74 auto fd = unique_fd(::open(android::incfs::details::c_str(file), O_RDONLY | O_CLOEXEC));
75 if (fd < 0) {
76 return unique_fd{-errno};
77 }
78 return fd;
79 }
80
openRaw(std::string_view dir,std::string_view name)81 static unique_fd openRaw(std::string_view dir, std::string_view name) {
82 return openRaw(android::incfs::path::join(dir, name));
83 }
84
rootForCmd(int fd)85 static std::string rootForCmd(int fd) {
86 auto cmdFile = android::incfs::path::fromFd(fd);
87 if (cmdFile.empty()) {
88 LOG(INFO) << __func__ << "(): name empty for " << fd;
89 return {};
90 }
91 auto res = android::incfs::path::dirName(cmdFile);
92 if (res.empty()) {
93 LOG(INFO) << __func__ << "(): dirname empty for " << cmdFile;
94 return {};
95 }
96 if (!android::incfs::path::endsWith(cmdFile, INCFS_PENDING_READS_FILENAME)) {
97 LOG(INFO) << __func__ << "(): invalid file name " << cmdFile;
98 return {};
99 }
100 if (cmdFile.data() == res.data() || cmdFile.starts_with(res)) {
101 cmdFile.resize(res.size());
102 return cmdFile;
103 }
104 return std::string(res);
105 }
106
readIncFsFeatures()107 static android::incfs::Features readIncFsFeatures() {
108 static const char kSysfsFeaturesDir[] = "/sys/fs/" INCFS_NAME "/features";
109 const auto dir = android::incfs::path::openDir(kSysfsFeaturesDir);
110 if (!dir) {
111 return android::incfs::Features::none;
112 }
113
114 int res = android::incfs::Features::none;
115 while (auto entry = ::readdir(dir.get())) {
116 if (entry->d_type != DT_REG) {
117 continue;
118 }
119 if (entry->d_name == "corefs"sv) {
120 res |= android::incfs::Features::core;
121 }
122 }
123
124 return android::incfs::Features(res);
125 }
126
IncFs_Features()127 IncFsFeatures IncFs_Features() {
128 return IncFsFeatures(readIncFsFeatures());
129 }
130
isFsAvailable()131 static bool isFsAvailable() {
132 static const char kProcFilesystems[] = "/proc/filesystems";
133 std::string filesystems;
134 if (!android::base::ReadFileToString(kProcFilesystems, &filesystems)) {
135 return false;
136 }
137 return filesystems.find("\t" INCFS_NAME "\n") != std::string::npos;
138 }
139
incFsPropertyValue()140 static std::string_view incFsPropertyValue() {
141 static const android::base::NoDestructor<std::string> kValue{
142 android::sysprop::IncrementalProperties::enable().value_or("")};
143 return *kValue;
144 }
145
parseProperty(std::string_view property)146 static std::pair<bool, std::string_view> parseProperty(std::string_view property) {
147 auto boolVal = android::base::ParseBool(property);
148 if (boolVal == android::base::ParseBoolResult::kTrue) {
149 return {isFsAvailable(), {}};
150 }
151 if (boolVal == android::base::ParseBoolResult::kFalse) {
152 return {false, {}};
153 }
154
155 // Don't load the module at once, but instead only check if it is loadable.
156 static const auto kModulePrefix = "module:"sv;
157 if (property.starts_with(kModulePrefix)) {
158 const auto modulePath = property.substr(kModulePrefix.size());
159 return {::access(android::incfs::details::c_str(modulePath), R_OK | X_OK), modulePath};
160 }
161 return {false, {}};
162 }
163
164 namespace {
165
166 class IncFsInit {
167 public:
IncFsInit()168 IncFsInit() {
169 auto [featureEnabled, moduleName] = parseProperty(incFsPropertyValue());
170 featureEnabled_ = featureEnabled;
171 moduleName_ = moduleName;
172 loaded_ = featureEnabled_ && isFsAvailable();
173 }
174
175 constexpr ~IncFsInit() = default;
176
enabled() const177 bool enabled() const { return featureEnabled_; }
enabledAndReady() const178 bool enabledAndReady() const {
179 if (!featureEnabled_) {
180 return false;
181 }
182 if (moduleName_.empty()) {
183 return true;
184 }
185 if (loaded_) {
186 return true;
187 }
188 std::call_once(loadedFlag_, [this] {
189 const unique_fd fd(
190 TEMP_FAILURE_RETRY(::open(android::incfs::details::c_str(moduleName_),
191 O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
192 if (fd < 0) {
193 PLOG(ERROR) << "could not open IncFs kernel module \"" << moduleName_ << '"';
194 return;
195 }
196
197 const auto rc = syscall(__NR_finit_module, fd.get(), "", 0);
198 if (rc < 0) {
199 PLOG(ERROR) << "finit_module for IncFs \"" << moduleName_ << "\" failed";
200 return;
201 }
202 if (!isFsAvailable()) {
203 LOG(ERROR) << "loaded IncFs kernel module \"" << moduleName_
204 << "\" but incremental-fs is still not available";
205 }
206 loaded_ = true;
207 LOG(INFO) << "successfully loaded IncFs kernel module \"" << moduleName_ << '"';
208 });
209 return loaded_;
210 }
211
212 private:
213 bool featureEnabled_;
214 std::string_view moduleName_;
215 mutable std::once_flag loadedFlag_;
216 mutable bool loaded_;
217 };
218
219 } // namespace
220
init()221 static IncFsInit& init() {
222 static IncFsInit initer;
223 return initer;
224 }
225
IncFs_IsEnabled()226 bool IncFs_IsEnabled() {
227 return init().enabled();
228 }
229
isIncFsPath(const char * path)230 bool isIncFsPath(const char* path) {
231 struct statfs fs = {};
232 if (::statfs(path, &fs) != 0) {
233 PLOG(WARNING) << __func__ << "(): could not statfs " << path;
234 return false;
235 }
236
237 return fs.f_type == (decltype(fs.f_type))INCFS_MAGIC_NUMBER;
238 }
239
isDir(const char * path)240 static int isDir(const char* path) {
241 struct stat st;
242 if (::stat(path, &st) != 0) {
243 return -errno;
244 }
245 if (!S_ISDIR(st.st_mode)) {
246 return -ENOTDIR;
247 }
248 return 0;
249 }
250
isAbsolute(const char * path)251 static bool isAbsolute(const char* path) {
252 return path && path[0] == '/';
253 }
254
isValidMountTarget(const char * path)255 static int isValidMountTarget(const char* path) {
256 if (!isAbsolute(path)) {
257 return -EINVAL;
258 }
259 if (isIncFsPath(path)) {
260 LOG(ERROR) << "[incfs] mounting over existing incfs mount is not allowed";
261 return -EINVAL;
262 }
263 if (const auto err = isDir(path); err != 0) {
264 return err;
265 }
266 if (const auto err = android::incfs::path::isEmptyDir(path); err != 0) {
267 return err;
268 }
269 return 0;
270 }
271
rmDirContent(const char * path)272 static int rmDirContent(const char* path) {
273 auto dir = android::incfs::path::openDir(path);
274 if (!dir) {
275 return -EINVAL;
276 }
277 while (auto entry = ::readdir(dir.get())) {
278 if (entry->d_name == "."sv || entry->d_name == ".."sv) {
279 continue;
280 }
281 auto fullPath = StringPrintf("%s/%s", path, entry->d_name);
282 if (entry->d_type == DT_DIR) {
283 if (const auto err = rmDirContent(fullPath.c_str()); err != 0) {
284 return err;
285 }
286 if (const auto err = ::rmdir(fullPath.c_str()); err != 0) {
287 return err;
288 }
289 } else {
290 if (const auto err = ::unlink(fullPath.c_str()); err != 0) {
291 return err;
292 }
293 }
294 }
295 return 0;
296 }
297
makeMountOptionsString(IncFsMountOptions options)298 static std::string makeMountOptionsString(IncFsMountOptions options) {
299 return StringPrintf("read_timeout_ms=%u,readahead=0,rlog_pages=%u,rlog_wakeup_cnt=1",
300 unsigned(options.defaultReadTimeoutMs),
301 unsigned(options.readLogBufferPages < 0
302 ? INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES
303 : options.readLogBufferPages));
304 }
305
makeControl(const char * root)306 static IncFsControl* makeControl(const char* root) {
307 auto cmd = openRaw(root, INCFS_PENDING_READS_FILENAME);
308 if (!cmd.ok()) {
309 return nullptr;
310 }
311 unique_fd pendingReads(fcntl(cmd.get(), F_DUPFD_CLOEXEC, cmd.get()));
312 if (!pendingReads.ok()) {
313 return nullptr;
314 }
315 auto logs = openRaw(root, INCFS_LOG_FILENAME);
316 // logs may be absent, that's fine
317 auto control = IncFs_CreateControl(cmd.get(), pendingReads.get(), logs.get());
318 if (control) {
319 (void)cmd.release();
320 (void)pendingReads.release();
321 (void)logs.release();
322 }
323 return control;
324 }
325
makeCommandPath(std::string_view root,std::string_view item)326 static std::string makeCommandPath(std::string_view root, std::string_view item) {
327 auto [itemRoot, subpath] = registry().rootAndSubpathFor(item);
328 if (itemRoot != root) {
329 return {};
330 }
331 // TODO: add "/.cmd/" if we decide to use a separate control tree.
332 return android::incfs::path::join(itemRoot, subpath);
333 }
334
toString(IncFsFileId id,char * out)335 static void toString(IncFsFileId id, char* out) {
336 // Make sure this function matches the one in the kernel (e.g. same case for a-f digits).
337 static constexpr char kHexChar[] = "0123456789abcdef";
338
339 for (auto item = std::begin(id.data); item != std::end(id.data); ++item, out += 2) {
340 out[0] = kHexChar[(*item & 0xf0) >> 4];
341 out[1] = kHexChar[(*item & 0x0f)];
342 }
343 }
344
toStringImpl(IncFsFileId id)345 static std::string toStringImpl(IncFsFileId id) {
346 std::string res(kIncFsFileIdStringLength, '\0');
347 toString(id, res.data());
348 return res;
349 }
350
toFileIdImpl(std::string_view str)351 static IncFsFileId toFileIdImpl(std::string_view str) {
352 if (str.size() != kIncFsFileIdStringLength) {
353 return kIncFsInvalidFileId;
354 }
355
356 IncFsFileId res;
357 auto out = (char*)&res;
358 for (auto it = str.begin(); it != str.end(); it += 2, ++out) {
359 static const auto fromChar = [](char src) -> int {
360 if (src >= '0' && src <= '9') {
361 return src - '0';
362 }
363 if (src >= 'a' && src <= 'f') {
364 return src - 'a' + 10;
365 }
366 return -1;
367 };
368
369 const int c[2] = {fromChar(it[0]), fromChar(it[1])};
370 if (c[0] == -1 || c[1] == -1) {
371 errno = EINVAL;
372 return kIncFsInvalidFileId;
373 }
374 *out = (c[0] << 4) | c[1];
375 }
376 return res;
377 }
378
IncFs_FileIdToString(IncFsFileId id,char * out)379 int IncFs_FileIdToString(IncFsFileId id, char* out) {
380 if (!out) {
381 return -EINVAL;
382 }
383 toString(id, out);
384 return 0;
385 }
386
IncFs_FileIdFromString(const char * in)387 IncFsFileId IncFs_FileIdFromString(const char* in) {
388 return toFileIdImpl({in, kIncFsFileIdStringLength});
389 }
390
IncFs_FileIdFromMetadata(IncFsSpan metadata)391 IncFsFileId IncFs_FileIdFromMetadata(IncFsSpan metadata) {
392 IncFsFileId id = {};
393 if (size_t(metadata.size) <= sizeof(id)) {
394 memcpy(&id, metadata.data, metadata.size);
395 } else {
396 uint8_t buffer[SHA_DIGEST_LENGTH];
397 static_assert(sizeof(buffer) >= sizeof(id));
398
399 SHA_CTX ctx;
400 SHA1_Init(&ctx);
401 SHA1_Update(&ctx, metadata.data, metadata.size);
402 SHA1_Final(buffer, &ctx);
403 memcpy(&id, buffer, sizeof(id));
404 }
405 return id;
406 }
407
restoreconControlFiles(std::string_view targetDir)408 static bool restoreconControlFiles(std::string_view targetDir) {
409 const std::string controlFilePaths[] =
410 {android::incfs::path::join(targetDir, INCFS_PENDING_READS_FILENAME),
411 android::incfs::path::join(targetDir, INCFS_LOG_FILENAME)};
412 for (size_t i = 0; i < std::size(controlFilePaths); i++) {
413 if (const auto err = selinux_android_restorecon(controlFilePaths[i].c_str(),
414 SELINUX_ANDROID_RESTORECON_FORCE);
415 err != 0) {
416 PLOG(ERROR) << "[incfs] Failed to restorecon: " << controlFilePaths[i]
417 << " error code: " << err;
418 errno = -err;
419 return false;
420 }
421 }
422 return true;
423 }
424
IncFs_Mount(const char * backingPath,const char * targetDir,IncFsMountOptions options)425 IncFsControl* IncFs_Mount(const char* backingPath, const char* targetDir,
426 IncFsMountOptions options) {
427 if (!init().enabledAndReady()) {
428 LOG(WARNING) << "[incfs] Feature is not enabled";
429 errno = ENOTSUP;
430 return nullptr;
431 }
432
433 if (auto err = isValidMountTarget(targetDir); err != 0) {
434 errno = -err;
435 return nullptr;
436 }
437 if (!isAbsolute(backingPath)) {
438 errno = EINVAL;
439 return nullptr;
440 }
441
442 if (options.flags & android::incfs::createOnly) {
443 if (const auto err = android::incfs::path::isEmptyDir(backingPath); err != 0) {
444 errno = -err;
445 return nullptr;
446 }
447 } else if (options.flags & android::incfs::truncate) {
448 if (const auto err = rmDirContent(backingPath); err != 0) {
449 errno = -err;
450 return nullptr;
451 }
452 }
453
454 const auto opts = makeMountOptionsString(options);
455 if (::mount(backingPath, targetDir, INCFS_NAME, MS_NOSUID | MS_NODEV | MS_NOATIME,
456 opts.c_str())) {
457 PLOG(ERROR) << "[incfs] Failed to mount IncFS filesystem: " << targetDir
458 << " errno: " << errno;
459 return nullptr;
460 }
461
462 if (!restoreconControlFiles(targetDir)) {
463 return nullptr;
464 }
465
466 auto control = makeControl(targetDir);
467 if (control == nullptr) {
468 return nullptr;
469 }
470 return control;
471 }
472
IncFs_Open(const char * dir)473 IncFsControl* IncFs_Open(const char* dir) {
474 auto root = registry().rootFor(dir);
475 if (root.empty()) {
476 errno = EINVAL;
477 return nullptr;
478 }
479 return makeControl(android::incfs::details::c_str(root));
480 }
481
IncFs_GetControlFd(const IncFsControl * control,IncFsFdType type)482 IncFsFd IncFs_GetControlFd(const IncFsControl* control, IncFsFdType type) {
483 if (!control) {
484 return -EINVAL;
485 }
486 switch (type) {
487 case CMD:
488 return control->cmd;
489 case PENDING_READS:
490 return control->pendingReads;
491 case LOGS:
492 return control->logs;
493 default:
494 return -EINVAL;
495 }
496 }
497
IncFs_ReleaseControlFds(IncFsControl * control,IncFsFd out[],IncFsSize outSize)498 IncFsSize IncFs_ReleaseControlFds(IncFsControl* control, IncFsFd out[], IncFsSize outSize) {
499 if (!control || !out) {
500 return -EINVAL;
501 }
502 if (outSize < IncFsFdType::FDS_COUNT) {
503 return -ERANGE;
504 }
505 out[CMD] = std::exchange(control->cmd, -1);
506 out[PENDING_READS] = std::exchange(control->pendingReads, -1);
507 out[LOGS] = std::exchange(control->logs, -1);
508 return IncFsFdType::FDS_COUNT;
509 }
510
IncFs_CreateControl(IncFsFd cmd,IncFsFd pendingReads,IncFsFd logs)511 IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) {
512 return new IncFsControl(cmd, pendingReads, logs);
513 }
514
IncFs_DeleteControl(IncFsControl * control)515 void IncFs_DeleteControl(IncFsControl* control) {
516 if (control) {
517 if (control->cmd >= 0) {
518 close(control->cmd);
519 }
520 if (control->pendingReads >= 0) {
521 close(control->pendingReads);
522 }
523 if (control->logs >= 0) {
524 close(control->logs);
525 }
526 delete control;
527 }
528 }
529
IncFs_SetOptions(const IncFsControl * control,IncFsMountOptions options)530 IncFsErrorCode IncFs_SetOptions(const IncFsControl* control, IncFsMountOptions options) {
531 if (!control) {
532 return -EINVAL;
533 }
534 auto root = rootForCmd(control->cmd);
535 if (root.empty()) {
536 return -EINVAL;
537 }
538 auto opts = makeMountOptionsString(options);
539 if (::mount(nullptr, root.c_str(), nullptr, MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_NOATIME,
540 opts.c_str()) != 0) {
541 const auto error = errno;
542 PLOG(ERROR) << "[incfs] Failed to remount IncFS filesystem: " << root;
543 return -error;
544 }
545 return 0;
546 }
547
IncFs_Root(const IncFsControl * control,char buffer[],size_t * bufferSize)548 IncFsErrorCode IncFs_Root(const IncFsControl* control, char buffer[], size_t* bufferSize) {
549 if (!control) {
550 return -EINVAL;
551 }
552 std::string result = rootForCmd(control->cmd);
553 if (*bufferSize <= result.size()) {
554 *bufferSize = result.size() + 1;
555 return -EOVERFLOW;
556 }
557 result.copy(buffer, result.size());
558 buffer[result.size()] = '\0';
559 *bufferSize = result.size();
560 return 0;
561 }
562
563 template <class T>
read(IncFsSpan & data)564 std::optional<T> read(IncFsSpan& data) {
565 if (data.size < (int32_t)sizeof(T)) {
566 return {};
567 }
568 T res;
569 memcpy(&res, data.data, sizeof(res));
570 data.data += sizeof(res);
571 data.size -= sizeof(res);
572 return res;
573 }
574
validateSignatureFormat(IncFsSpan signature)575 static IncFsErrorCode validateSignatureFormat(IncFsSpan signature) {
576 if (signature.data == nullptr && signature.size == 0) {
577 return 0; // it's fine to have unverified files too
578 }
579 if ((signature.data == nullptr) != (signature.size == 0)) {
580 return -EINVAL;
581 }
582
583 // These structs are here purely for checking the minimum size. Maybe will use them for
584 // parsing later.
585 struct __attribute__((packed)) Hashing {
586 int32_t size;
587 int32_t algorithm;
588 int8_t log2_blocksize;
589 int32_t salt_size;
590 int32_t raw_root_hash_size;
591 };
592 struct __attribute__((packed)) Signing {
593 int32_t size;
594 int32_t apk_digest_size;
595 int32_t certificate_size;
596 int32_t addl_data_size;
597 int32_t public_key_size;
598 int32_t algorithm;
599 int32_t signature_size;
600 };
601 struct __attribute__((packed)) MinSignature {
602 int32_t version;
603 Hashing hashing_info;
604 Signing signing_info;
605 };
606
607 if (signature.size < (int32_t)sizeof(MinSignature)) {
608 return -ERANGE;
609 }
610 if (signature.size > INCFS_MAX_SIGNATURE_SIZE) {
611 return -ERANGE;
612 }
613
614 auto version = read<int32_t>(signature);
615 if (version.value_or(-1) != INCFS_SIGNATURE_VERSION) {
616 return -EINVAL;
617 }
618 auto hashSize = read<int32_t>(signature);
619 if (!hashSize || signature.size < *hashSize) {
620 return -EINVAL;
621 }
622 auto hashAlgo = read<int32_t>(signature);
623 if (hashAlgo.value_or(-1) != INCFS_HASH_TREE_SHA256) {
624 return -EINVAL;
625 }
626 auto logBlockSize = read<int8_t>(signature);
627 if (logBlockSize.value_or(-1) != 12 /* 2^12 == 4096 */) {
628 return -EINVAL;
629 }
630 auto saltSize = read<int32_t>(signature);
631 if (saltSize.value_or(-1) != 0) {
632 return -EINVAL;
633 }
634 auto rootHashSize = read<int32_t>(signature);
635 if (rootHashSize.value_or(-1) != INCFS_MAX_HASH_SIZE) {
636 return -EINVAL;
637 }
638 if (signature.size < *rootHashSize) {
639 return -EINVAL;
640 }
641 signature.data += *rootHashSize;
642 signature.size -= *rootHashSize;
643 auto signingSize = read<int32_t>(signature);
644 // everything remaining has to be in the signing info
645 if (signingSize.value_or(-1) != signature.size) {
646 return -EINVAL;
647 }
648
649 // TODO: validate the signature part too.
650 return 0;
651 }
652
IncFs_MakeFile(const IncFsControl * control,const char * path,int32_t mode,IncFsFileId id,IncFsNewFileParams params)653 IncFsErrorCode IncFs_MakeFile(const IncFsControl* control, const char* path, int32_t mode,
654 IncFsFileId id, IncFsNewFileParams params) {
655 if (!control) {
656 return -EINVAL;
657 }
658
659 auto [root, subpath] = registry().rootAndSubpathFor(path);
660 if (root.empty()) {
661 PLOG(WARNING) << "[incfs] makeFile failed for path " << path << ", root is empty.";
662 return -EINVAL;
663 }
664 if (params.size < 0) {
665 LOG(WARNING) << "[incfs] makeFile failed for path " << path
666 << ", size is invalid: " << params.size;
667 return -ERANGE;
668 }
669
670 const auto [subdir, name] = android::incfs::path::splitDirBase(subpath);
671 incfs_new_file_args args = {
672 .size = (uint64_t)params.size,
673 .mode = (uint16_t)mode,
674 .directory_path = (uint64_t)subdir.data(),
675 .file_name = (uint64_t)name.data(),
676 .file_attr = (uint64_t)params.metadata.data,
677 .file_attr_len = (uint32_t)params.metadata.size,
678 };
679 static_assert(sizeof(args.file_id.bytes) == sizeof(id.data));
680 memcpy(args.file_id.bytes, id.data, sizeof(args.file_id.bytes));
681
682 if (auto err = validateSignatureFormat(params.signature)) {
683 return err;
684 }
685 args.signature_info = (uint64_t)(uintptr_t)params.signature.data;
686 args.signature_size = (uint64_t)params.signature.size;
687
688 if (::ioctl(control->cmd, INCFS_IOC_CREATE_FILE, &args)) {
689 PLOG(WARNING) << "[incfs] makeFile failed for " << root << " / " << subdir << " / " << name
690 << " of " << params.size << " bytes";
691 return -errno;
692 }
693 if (::chmod(android::incfs::path::join(root, subpath).c_str(), mode)) {
694 PLOG(WARNING) << "[incfs] couldn't change file mode to 0" << std::oct << mode;
695 }
696
697 return 0;
698 }
699
makeDir(const char * commandPath,int32_t mode,bool allowExisting)700 static IncFsErrorCode makeDir(const char* commandPath, int32_t mode, bool allowExisting) {
701 if (!::mkdir(commandPath, mode)) {
702 if (::chmod(commandPath, mode)) {
703 PLOG(WARNING) << "[incfs] couldn't change directory mode to 0" << std::oct << mode;
704 }
705 return 0;
706 }
707 // don't touch the existing dir's mode - mkdir(1) works that way.
708 return (allowExisting && errno == EEXIST) ? 0 : -errno;
709 }
710
makeDirs(std::string_view commandPath,std::string_view path,std::string_view root,int32_t mode)711 static IncFsErrorCode makeDirs(std::string_view commandPath, std::string_view path,
712 std::string_view root, int32_t mode) {
713 auto commandCPath = android::incfs::details::c_str(commandPath);
714 const auto mkdirRes = makeDir(commandCPath, mode, true);
715 if (!mkdirRes) {
716 return 0;
717 }
718 if (mkdirRes != -ENOENT) {
719 LOG(ERROR) << __func__ << "(): mkdir failed for " << path << " - " << mkdirRes;
720 return mkdirRes;
721 }
722
723 const auto parent = android::incfs::path::dirName(commandPath);
724 if (!android::incfs::path::startsWith(parent, root)) {
725 // went too far, already out of the root mount
726 return -EINVAL;
727 }
728
729 if (auto parentMkdirRes = makeDirs(parent, android::incfs::path::dirName(path), root, mode)) {
730 return parentMkdirRes;
731 }
732 return makeDir(commandCPath, mode, true);
733 }
734
IncFs_MakeDir(const IncFsControl * control,const char * path,int32_t mode)735 IncFsErrorCode IncFs_MakeDir(const IncFsControl* control, const char* path, int32_t mode) {
736 if (!control) {
737 return -EINVAL;
738 }
739 const auto root = rootForCmd(control->cmd);
740 if (root.empty()) {
741 LOG(ERROR) << __func__ << "(): root is empty for " << path;
742 return -EINVAL;
743 }
744 auto commandPath = makeCommandPath(root, path);
745 if (commandPath.empty()) {
746 LOG(ERROR) << __func__ << "(): commandPath is empty for " << path;
747 return -EINVAL;
748 }
749 if (auto res = makeDir(commandPath.c_str(), mode, false)) {
750 LOG(ERROR) << __func__ << "(): mkdir failed for " << commandPath << " - " << res;
751 return res;
752 }
753 return 0;
754 }
755
IncFs_MakeDirs(const IncFsControl * control,const char * path,int32_t mode)756 IncFsErrorCode IncFs_MakeDirs(const IncFsControl* control, const char* path, int32_t mode) {
757 if (!control) {
758 return -EINVAL;
759 }
760 const auto root = rootForCmd(control->cmd);
761 if (root.empty()) {
762 LOG(ERROR) << __func__ << "(): root is empty for " << path;
763 return -EINVAL;
764 }
765 auto commandPath = makeCommandPath(root, path);
766 if (commandPath.empty()) {
767 LOG(ERROR) << __func__ << "(): commandPath is empty for " << path;
768 return -EINVAL;
769 }
770 return makeDirs(commandPath, path, root, mode);
771 }
772
getMetadata(const char * path,char buffer[],size_t * bufferSize)773 static IncFsErrorCode getMetadata(const char* path, char buffer[], size_t* bufferSize) {
774 const auto res = ::getxattr(path, android::incfs::kMetadataAttrName, buffer, *bufferSize);
775 if (res < 0) {
776 if (errno == ERANGE) {
777 auto neededSize = ::getxattr(path, android::incfs::kMetadataAttrName, buffer, 0);
778 if (neededSize >= 0) {
779 *bufferSize = neededSize;
780 return 0;
781 }
782 }
783 return -errno;
784 }
785 *bufferSize = res;
786 return 0;
787 }
788
IncFs_GetMetadataById(const IncFsControl * control,IncFsFileId fileId,char buffer[],size_t * bufferSize)789 IncFsErrorCode IncFs_GetMetadataById(const IncFsControl* control, IncFsFileId fileId, char buffer[],
790 size_t* bufferSize) {
791 if (!control) {
792 return -EINVAL;
793 }
794
795 const auto root = rootForCmd(control->cmd);
796 if (root.empty()) {
797 return -EINVAL;
798 }
799 auto name = android::incfs::path::join(root, android::incfs::kIndexDir, toStringImpl(fileId));
800 return getMetadata(android::incfs::details::c_str(name), buffer, bufferSize);
801 }
802
IncFs_GetMetadataByPath(const IncFsControl * control,const char * path,char buffer[],size_t * bufferSize)803 IncFsErrorCode IncFs_GetMetadataByPath(const IncFsControl* control, const char* path, char buffer[],
804 size_t* bufferSize) {
805 if (!control) {
806 return -EINVAL;
807 }
808 const auto pathRoot = registry().rootFor(path);
809 const auto root = rootForCmd(control->cmd);
810 if (root.empty() || root != pathRoot) {
811 return -EINVAL;
812 }
813
814 return getMetadata(path, buffer, bufferSize);
815 }
816
IncFs_GetId(const IncFsControl * control,const char * path)817 IncFsFileId IncFs_GetId(const IncFsControl* control, const char* path) {
818 if (!control) {
819 return kIncFsInvalidFileId;
820 }
821 const auto pathRoot = registry().rootFor(path);
822 const auto root = rootForCmd(control->cmd);
823 if (root.empty() || root != pathRoot) {
824 errno = EINVAL;
825 return kIncFsInvalidFileId;
826 }
827 char buffer[kIncFsFileIdStringLength];
828 const auto res = ::getxattr(path, android::incfs::kIdAttrName, buffer, sizeof(buffer));
829 if (res != sizeof(buffer)) {
830 return kIncFsInvalidFileId;
831 }
832 return toFileIdImpl({buffer, std::size(buffer)});
833 }
834
getSignature(int fd,char buffer[],size_t * bufferSize)835 static IncFsErrorCode getSignature(int fd, char buffer[], size_t* bufferSize) {
836 incfs_get_file_sig_args args = {
837 .file_signature = (uint64_t)buffer,
838 .file_signature_buf_size = (uint32_t)*bufferSize,
839 };
840
841 auto res = ::ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args);
842 if (res < 0) {
843 if (errno == E2BIG) {
844 *bufferSize = INCFS_MAX_SIGNATURE_SIZE;
845 }
846 return -errno;
847 }
848 *bufferSize = args.file_signature_len_out;
849 return 0;
850 }
851
IncFs_GetSignatureById(const IncFsControl * control,IncFsFileId fileId,char buffer[],size_t * bufferSize)852 IncFsErrorCode IncFs_GetSignatureById(const IncFsControl* control, IncFsFileId fileId,
853 char buffer[], size_t* bufferSize) {
854 if (!control) {
855 return -EINVAL;
856 }
857
858 const auto root = rootForCmd(control->cmd);
859 if (root.empty()) {
860 return -EINVAL;
861 }
862 auto file = android::incfs::path::join(root, android::incfs::kIndexDir, toStringImpl(fileId));
863 auto fd = openRaw(file);
864 if (fd < 0) {
865 return fd.get();
866 }
867 return getSignature(fd, buffer, bufferSize);
868 }
869
IncFs_GetSignatureByPath(const IncFsControl * control,const char * path,char buffer[],size_t * bufferSize)870 IncFsErrorCode IncFs_GetSignatureByPath(const IncFsControl* control, const char* path,
871 char buffer[], size_t* bufferSize) {
872 if (!control) {
873 return -EINVAL;
874 }
875
876 const auto pathRoot = registry().rootFor(path);
877 const auto root = rootForCmd(control->cmd);
878 if (root.empty() || root != pathRoot) {
879 return -EINVAL;
880 }
881 return IncFs_UnsafeGetSignatureByPath(path, buffer, bufferSize);
882 }
883
IncFs_UnsafeGetSignatureByPath(const char * path,char buffer[],size_t * bufferSize)884 IncFsErrorCode IncFs_UnsafeGetSignatureByPath(const char* path, char buffer[], size_t* bufferSize) {
885 if (!isIncFsPath(path)) {
886 return -EINVAL;
887 }
888 auto fd = openRaw(path);
889 if (fd < 0) {
890 return fd.get();
891 }
892 return getSignature(fd, buffer, bufferSize);
893 }
894
IncFs_Link(const IncFsControl * control,const char * fromPath,const char * wherePath)895 IncFsErrorCode IncFs_Link(const IncFsControl* control, const char* fromPath,
896 const char* wherePath) {
897 if (!control) {
898 return -EINVAL;
899 }
900
901 auto root = rootForCmd(control->cmd);
902 if (root.empty()) {
903 return -EINVAL;
904 }
905 auto cmdFrom = makeCommandPath(root, fromPath);
906 if (cmdFrom.empty()) {
907 return -EINVAL;
908 }
909 auto cmdWhere = makeCommandPath(root, wherePath);
910 if (cmdWhere.empty()) {
911 return -EINVAL;
912 }
913 if (::link(cmdFrom.c_str(), cmdWhere.c_str())) {
914 return -errno;
915 }
916 return 0;
917 }
918
IncFs_Unlink(const IncFsControl * control,const char * path)919 IncFsErrorCode IncFs_Unlink(const IncFsControl* control, const char* path) {
920 if (!control) {
921 return -EINVAL;
922 }
923
924 auto root = rootForCmd(control->cmd);
925 if (root.empty()) {
926 return -EINVAL;
927 }
928 auto cmdPath = makeCommandPath(root, path);
929 if (cmdPath.empty()) {
930 return -EINVAL;
931 }
932 if (::unlink(cmdPath.c_str())) {
933 if (errno == EISDIR) {
934 if (!::rmdir(cmdPath.c_str())) {
935 return 0;
936 }
937 }
938 return -errno;
939 }
940 return 0;
941 }
942
waitForReads(int fd,int32_t timeoutMs,incfs_pending_read_info pendingReadsBuffer[],size_t * pendingReadsBufferSize)943 static int waitForReads(int fd, int32_t timeoutMs, incfs_pending_read_info pendingReadsBuffer[],
944 size_t* pendingReadsBufferSize) {
945 auto hrTimeout = std::chrono::steady_clock::duration(std::chrono::milliseconds(timeoutMs));
946
947 while (hrTimeout > hrTimeout.zero() || (!pendingReadsBuffer && hrTimeout == hrTimeout.zero())) {
948 const auto startTs = std::chrono::steady_clock::now();
949
950 pollfd pfd = {fd, POLLIN, 0};
951 const auto res =
952 ::poll(&pfd, 1, duration_cast<std::chrono::milliseconds>(hrTimeout).count());
953 if (res > 0) {
954 break;
955 }
956 if (res == 0) {
957 if (pendingReadsBufferSize) {
958 *pendingReadsBufferSize = 0;
959 }
960 return -ETIMEDOUT;
961 }
962 const auto error = errno;
963 if (error != EINTR) {
964 PLOG(ERROR) << "poll() failed";
965 return -error;
966 }
967 hrTimeout -= std::chrono::steady_clock::now() - startTs;
968 }
969 if (!pendingReadsBuffer) {
970 return hrTimeout < hrTimeout.zero() ? -ETIMEDOUT : 0;
971 }
972
973 auto res =
974 ::read(fd, pendingReadsBuffer, *pendingReadsBufferSize * sizeof(*pendingReadsBuffer));
975 if (res < 0) {
976 const auto error = errno;
977 PLOG(ERROR) << "read() failed";
978 return -error;
979 }
980 if (res == 0) {
981 *pendingReadsBufferSize = 0;
982 return -ETIMEDOUT;
983 }
984 if ((res % sizeof(*pendingReadsBuffer)) != 0) {
985 PLOG(ERROR) << "read() returned half of a struct??";
986 return -EFAULT;
987 }
988 *pendingReadsBufferSize = res / sizeof(*pendingReadsBuffer);
989 return 0;
990 }
991
IncFs_WaitForPendingReads(const IncFsControl * control,int32_t timeoutMs,IncFsReadInfo buffer[],size_t * bufferSize)992 IncFsErrorCode IncFs_WaitForPendingReads(const IncFsControl* control, int32_t timeoutMs,
993 IncFsReadInfo buffer[], size_t* bufferSize) {
994 if (!control || control->pendingReads < 0) {
995 return -EINVAL;
996 }
997
998 std::vector<incfs_pending_read_info> pendingReads;
999 pendingReads.resize(*bufferSize);
1000 if (const auto res =
1001 waitForReads(control->pendingReads, timeoutMs, pendingReads.data(), bufferSize)) {
1002 return res;
1003 }
1004 for (size_t i = 0; i != *bufferSize; ++i) {
1005 buffer[i] = IncFsReadInfo{
1006 .bootClockTsUs = pendingReads[i].timestamp_us,
1007 .block = (IncFsBlockIndex)pendingReads[i].block_index,
1008 .serialNo = pendingReads[i].serial_number,
1009 };
1010 memcpy(&buffer[i].id.data, pendingReads[i].file_id.bytes, sizeof(buffer[i].id.data));
1011 }
1012 return 0;
1013 }
1014
IncFs_WaitForPageReads(const IncFsControl * control,int32_t timeoutMs,IncFsReadInfo buffer[],size_t * bufferSize)1015 IncFsErrorCode IncFs_WaitForPageReads(const IncFsControl* control, int32_t timeoutMs,
1016 IncFsReadInfo buffer[], size_t* bufferSize) {
1017 if (!control) {
1018 return -EINVAL;
1019 }
1020
1021 auto logsFd = control->logs;
1022 if (logsFd < 0) {
1023 return -EINVAL;
1024 }
1025 std::vector<incfs_pending_read_info> pendingReads;
1026 pendingReads.resize(*bufferSize);
1027 if (const auto res = waitForReads(logsFd, timeoutMs, pendingReads.data(), bufferSize)) {
1028 return res;
1029 }
1030 for (size_t i = 0; i != *bufferSize; ++i) {
1031 buffer[i] = IncFsReadInfo{
1032 .bootClockTsUs = pendingReads[i].timestamp_us,
1033 .block = (IncFsBlockIndex)pendingReads[i].block_index,
1034 .serialNo = pendingReads[i].serial_number,
1035 };
1036 memcpy(&buffer[i].id.data, pendingReads[i].file_id.bytes, sizeof(buffer[i].id.data));
1037 }
1038 return 0;
1039 }
1040
openForSpecialOps(int cmd,const char * path)1041 static IncFsFd openForSpecialOps(int cmd, const char* path) {
1042 unique_fd fd(::open(path, O_RDONLY | O_CLOEXEC));
1043 if (fd < 0) {
1044 return -errno;
1045 }
1046 struct incfs_permit_fill args = {.file_descriptor = (uint32_t)fd.get()};
1047 auto err = ::ioctl(cmd, INCFS_IOC_PERMIT_FILL, &args);
1048 if (err < 0) {
1049 return -errno;
1050 }
1051 return fd.release();
1052 }
1053
IncFs_OpenForSpecialOpsByPath(const IncFsControl * control,const char * path)1054 IncFsFd IncFs_OpenForSpecialOpsByPath(const IncFsControl* control, const char* path) {
1055 if (!control) {
1056 return -EINVAL;
1057 }
1058
1059 const auto pathRoot = registry().rootFor(path);
1060 const auto cmd = control->cmd;
1061 const auto root = rootForCmd(cmd);
1062 if (root.empty() || root != pathRoot) {
1063 return -EINVAL;
1064 }
1065 return openForSpecialOps(cmd, makeCommandPath(root, path).c_str());
1066 }
1067
IncFs_OpenForSpecialOpsById(const IncFsControl * control,IncFsFileId id)1068 IncFsFd IncFs_OpenForSpecialOpsById(const IncFsControl* control, IncFsFileId id) {
1069 if (!control) {
1070 return -EINVAL;
1071 }
1072
1073 const auto cmd = control->cmd;
1074 const auto root = rootForCmd(cmd);
1075 if (root.empty()) {
1076 return -EINVAL;
1077 }
1078 auto name = android::incfs::path::join(root, android::incfs::kIndexDir, toStringImpl(id));
1079 return openForSpecialOps(cmd, makeCommandPath(root, name).c_str());
1080 }
1081
writeBlocks(int fd,const incfs_fill_block blocks[],int blocksCount)1082 static int writeBlocks(int fd, const incfs_fill_block blocks[], int blocksCount) {
1083 if (fd < 0 || blocksCount == 0) {
1084 return 0;
1085 }
1086 if (blocksCount < 0) {
1087 return -EINVAL;
1088 }
1089
1090 auto ptr = blocks;
1091 const auto end = blocks + blocksCount;
1092 do {
1093 struct incfs_fill_blocks args = {.count = uint64_t(end - ptr),
1094 .fill_blocks = (uint64_t)(uintptr_t)ptr};
1095 const auto written = ::ioctl(fd, INCFS_IOC_FILL_BLOCKS, &args);
1096 if (written < 0) {
1097 if (errno == EINTR) {
1098 continue;
1099 }
1100 const auto error = errno;
1101 PLOG(WARNING) << "writing IncFS blocks failed";
1102 if (ptr == blocks) {
1103 return -error;
1104 }
1105 // something has been written, return a success here and let the
1106 // next call handle the error.
1107 break;
1108 }
1109 ptr += written;
1110 } while (ptr < end);
1111 return ptr - blocks;
1112 }
1113
IncFs_WriteBlocks(const IncFsDataBlock blocks[],size_t blocksCount)1114 IncFsErrorCode IncFs_WriteBlocks(const IncFsDataBlock blocks[], size_t blocksCount) {
1115 incfs_fill_block incfsBlocks[128];
1116 int writtenCount = 0;
1117 int incfsBlocksUsed = 0;
1118 int lastBlockFd = -1;
1119 for (size_t i = 0; i < blocksCount; ++i) {
1120 if (lastBlockFd != blocks[i].fileFd || incfsBlocksUsed == std::size(incfsBlocks)) {
1121 auto count = writeBlocks(lastBlockFd, incfsBlocks, incfsBlocksUsed);
1122 if (count > 0) {
1123 writtenCount += count;
1124 }
1125 if (count != incfsBlocksUsed) {
1126 return writtenCount ? writtenCount : count;
1127 }
1128 lastBlockFd = blocks[i].fileFd;
1129 incfsBlocksUsed = 0;
1130 }
1131 incfsBlocks[incfsBlocksUsed] = incfs_fill_block{
1132 .block_index = (uint32_t)blocks[i].pageIndex,
1133 .data_len = blocks[i].dataSize,
1134 .data = (uint64_t)blocks[i].data,
1135 .compression = (uint8_t)blocks[i].compression,
1136 .flags = uint8_t(blocks[i].kind == INCFS_BLOCK_KIND_HASH ? INCFS_BLOCK_FLAGS_HASH
1137 : 0),
1138 };
1139 ++incfsBlocksUsed;
1140 }
1141 auto count = writeBlocks(lastBlockFd, incfsBlocks, incfsBlocksUsed);
1142 if (count > 0) {
1143 writtenCount += count;
1144 }
1145 return writtenCount ? writtenCount : count;
1146 }
1147
IncFs_BindMount(const char * sourceDir,const char * targetDir)1148 IncFsErrorCode IncFs_BindMount(const char* sourceDir, const char* targetDir) {
1149 if (!android::incfs::enabled()) {
1150 return -ENOTSUP;
1151 }
1152
1153 auto [sourceRoot, subpath] = registry().rootAndSubpathFor(sourceDir);
1154 if (sourceRoot.empty()) {
1155 return -EINVAL;
1156 }
1157 if (subpath.empty()) {
1158 LOG(WARNING) << "[incfs] Binding the root mount '" << sourceRoot << "' is not allowed";
1159 return -EINVAL;
1160 }
1161
1162 if (auto err = isValidMountTarget(targetDir); err != 0) {
1163 return err;
1164 }
1165
1166 if (::mount(sourceDir, targetDir, nullptr, MS_BIND, nullptr)) {
1167 PLOG(ERROR) << "[incfs] Failed to bind mount '" << sourceDir << "' to '" << targetDir
1168 << '\'';
1169 return -errno;
1170 }
1171 return 0;
1172 }
1173
IncFs_Unmount(const char * dir)1174 IncFsErrorCode IncFs_Unmount(const char* dir) {
1175 if (!android::incfs::enabled()) {
1176 return -ENOTSUP;
1177 }
1178
1179 errno = 0;
1180 if (::umount2(dir, MNT_FORCE) == 0 || errno == EINVAL || errno == ENOENT) {
1181 // EINVAL - not a mount point, ENOENT - doesn't exist at all
1182 return -errno;
1183 }
1184 PLOG(WARNING) << __func__ << ": umount(force) failed, detaching '" << dir << '\'';
1185 errno = 0;
1186 if (!::umount2(dir, MNT_DETACH)) {
1187 return 0;
1188 }
1189 PLOG(WARNING) << __func__ << ": umount(detach) returned non-zero for '" << dir << '\'';
1190 return 0;
1191 }
1192
IncFs_IsIncFsPath(const char * path)1193 bool IncFs_IsIncFsPath(const char* path) {
1194 return isIncFsPath(path);
1195 }
1196
IncFs_GetFilledRanges(int fd,IncFsSpan outBuffer,IncFsFilledRanges * filledRanges)1197 IncFsErrorCode IncFs_GetFilledRanges(int fd, IncFsSpan outBuffer, IncFsFilledRanges* filledRanges) {
1198 return IncFs_GetFilledRangesStartingFrom(fd, 0, outBuffer, filledRanges);
1199 }
1200
IncFs_GetFilledRangesStartingFrom(int fd,int startBlockIndex,IncFsSpan outBuffer,IncFsFilledRanges * filledRanges)1201 IncFsErrorCode IncFs_GetFilledRangesStartingFrom(int fd, int startBlockIndex, IncFsSpan outBuffer,
1202 IncFsFilledRanges* filledRanges) {
1203 if (fd < 0) {
1204 return -EBADF;
1205 }
1206 if (startBlockIndex < 0) {
1207 return -EINVAL;
1208 }
1209 if (!outBuffer.data && outBuffer.size > 0) {
1210 return -EINVAL;
1211 }
1212 if (!filledRanges) {
1213 return -EINVAL;
1214 }
1215 // Use this to optimize the incfs call and have the same buffer for both the incfs and the
1216 // public structs.
1217 static_assert(sizeof(IncFsBlockRange) == sizeof(incfs_filled_range));
1218
1219 *filledRanges = {};
1220
1221 auto outStart = (IncFsBlockRange*)outBuffer.data;
1222 auto outEnd = outStart + outBuffer.size / sizeof(*outStart);
1223
1224 auto outPtr = outStart;
1225 int error = 0;
1226 int dataBlocks;
1227 incfs_get_filled_blocks_args args = {};
1228 for (;;) {
1229 auto start = args.index_out ? args.index_out : startBlockIndex;
1230 args = incfs_get_filled_blocks_args{
1231 .range_buffer = (uint64_t)(uintptr_t)outPtr,
1232 .range_buffer_size = uint32_t((outEnd - outPtr) * sizeof(*outPtr)),
1233 .start_index = start,
1234 };
1235 errno = 0;
1236 auto res = ::ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &args);
1237 error = errno;
1238 if (res && error != EINTR && error != ERANGE) {
1239 return -error;
1240 }
1241
1242 dataBlocks = args.data_blocks_out;
1243 outPtr += args.range_buffer_size_out / sizeof(incfs_filled_range);
1244 if (!res || error == ERANGE) {
1245 break;
1246 }
1247 // in case of EINTR we want to continue calling the function
1248 }
1249
1250 if (outPtr > outEnd) {
1251 outPtr = outEnd;
1252 error = ERANGE;
1253 }
1254
1255 filledRanges->endIndex = args.index_out;
1256 auto hashStartPtr = outPtr;
1257 if (outPtr != outStart) {
1258 // figure out the ranges for data block and hash blocks in the output
1259 for (; hashStartPtr != outStart; --hashStartPtr) {
1260 if ((hashStartPtr - 1)->begin < dataBlocks) {
1261 break;
1262 }
1263 }
1264 auto lastDataPtr = hashStartPtr - 1;
1265 // here we go, this is the first block that's before or at the hashes
1266 if (lastDataPtr->end <= dataBlocks) {
1267 ; // we're good, the boundary is between the ranges - |hashStartPtr| is correct
1268 } else {
1269 // the hard part: split the |lastDataPtr| range into the data and the hash pieces
1270 if (outPtr == outEnd) {
1271 // the buffer turned out to be too small, even though it actually wasn't
1272 error = ERANGE;
1273 if (hashStartPtr == outEnd) {
1274 // this is even worse: there's no room to put even a single hash block into.
1275 filledRanges->endIndex = lastDataPtr->end = dataBlocks;
1276 } else {
1277 std::copy_backward(lastDataPtr, outPtr - 1, outPtr);
1278 lastDataPtr->end = hashStartPtr->begin = dataBlocks;
1279 filledRanges->endIndex = (outPtr - 1)->end;
1280 }
1281 } else {
1282 std::copy_backward(lastDataPtr, outPtr, outPtr + 1);
1283 lastDataPtr->end = hashStartPtr->begin = dataBlocks;
1284 ++outPtr;
1285 }
1286 }
1287 // now fix the indices of all hash blocks - no one should know they're simply past the
1288 // regular data blocks in the file!
1289 for (auto ptr = hashStartPtr; ptr != outPtr; ++ptr) {
1290 ptr->begin -= dataBlocks;
1291 ptr->end -= dataBlocks;
1292 }
1293 }
1294
1295 filledRanges->dataRanges = outStart;
1296 filledRanges->dataRangesCount = hashStartPtr - outStart;
1297 filledRanges->hashRanges = hashStartPtr;
1298 filledRanges->hashRangesCount = outPtr - hashStartPtr;
1299
1300 return -error;
1301 }
1302
IncFs_IsFullyLoaded(int fd)1303 IncFsErrorCode IncFs_IsFullyLoaded(int fd) {
1304 char buffer[2 * sizeof(IncFsBlockRange)];
1305 IncFsFilledRanges ranges;
1306 auto res = IncFs_GetFilledRanges(fd, IncFsSpan{.data = buffer, .size = std::size(buffer)},
1307 &ranges);
1308 if (res == -ERANGE) {
1309 // need room for more than two ranges - definitely not fully loaded
1310 return -ENODATA;
1311 }
1312 if (res != 0) {
1313 return res;
1314 }
1315 // empty file
1316 if (ranges.endIndex == 0) {
1317 return 0;
1318 }
1319 // file with no hash tree
1320 if (ranges.dataRangesCount == 1 && ranges.hashRangesCount == 0) {
1321 return (ranges.dataRanges[0].begin == 0 && ranges.dataRanges[0].end == ranges.endIndex)
1322 ? 0
1323 : -ENODATA;
1324 }
1325 // file with a hash tree
1326 if (ranges.dataRangesCount == 1 && ranges.hashRangesCount == 1) {
1327 // calculate the expected data size from the size of the hash range and |endIndex|, which is
1328 // the total number of blocks in the file, both data and hash blocks together.
1329 if (ranges.hashRanges[0].begin != 0) {
1330 return -ENODATA;
1331 }
1332 const auto expectedDataBlocks =
1333 ranges.endIndex - (ranges.hashRanges[0].end - ranges.hashRanges[0].begin);
1334 return (ranges.dataRanges[0].begin == 0 && ranges.dataRanges[0].end == expectedDataBlocks)
1335 ? 0
1336 : -ENODATA;
1337 }
1338 return -ENODATA;
1339 }
1340
defaultMountRegistry()1341 android::incfs::MountRegistry& android::incfs::defaultMountRegistry() {
1342 return registry();
1343 }
1344