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