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