• 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/properties.h>
27 #include <android-base/stringprintf.h>
28 #include <android-base/strings.h>
29 #include <android-base/unique_fd.h>
30 #include <dirent.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <libgen.h>
34 #include <openssl/sha.h>
35 #include <selinux/android.h>
36 #include <selinux/selinux.h>
37 #include <sys/inotify.h>
38 #include <sys/mount.h>
39 #include <sys/poll.h>
40 #include <sys/stat.h>
41 #include <sys/syscall.h>
42 #include <sys/types.h>
43 #include <sys/vfs.h>
44 #include <sys/xattr.h>
45 #include <unistd.h>
46 
47 #include <charconv>
48 #include <chrono>
49 #include <iterator>
50 #include <mutex>
51 #include <optional>
52 #include <string_view>
53 
54 #include "MountRegistry.h"
55 #include "path.h"
56 
57 using namespace std::literals;
58 using namespace android::incfs;
59 using namespace android::sysprop;
60 namespace ab = android::base;
61 
62 struct IncFsControl final {
63     IncFsFd cmd;
64     IncFsFd pendingReads;
65     IncFsFd logs;
66     IncFsFd blocksWritten;
IncFsControlIncFsControl67     constexpr IncFsControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs, IncFsFd blocksWritten)
68           : cmd(cmd), pendingReads(pendingReads), logs(logs), blocksWritten(blocksWritten) {}
69 };
70 
registry()71 static MountRegistry& registry() {
72     static ab::NoDestructor<MountRegistry> instance{};
73     return *instance;
74 }
75 
openRaw(std::string_view file)76 static ab::unique_fd openRaw(std::string_view file) {
77     auto fd = ab::unique_fd(::open(details::c_str(file), O_RDONLY | O_CLOEXEC));
78     if (fd < 0) {
79         return ab::unique_fd{-errno};
80     }
81     return fd;
82 }
83 
openAt(int fd,std::string_view name,int flags=0)84 static ab::unique_fd openAt(int fd, std::string_view name, int flags = 0) {
85     auto res = ab::unique_fd(
86             ::openat(fd, details::c_str(name), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | flags));
87     if (res < 0) {
88         return ab::unique_fd{-errno};
89     }
90     return res;
91 }
92 
indexPath(std::string_view root,IncFsFileId fileId)93 static std::string indexPath(std::string_view root, IncFsFileId fileId) {
94     return path::join(root, INCFS_INDEX_NAME, toString(fileId));
95 }
96 
rootForCmd(int fd)97 static std::string rootForCmd(int fd) {
98     auto cmdFile = path::fromFd(fd);
99     if (cmdFile.empty()) {
100         LOG(INFO) << __func__ << "(): name empty for " << fd;
101         return {};
102     }
103     auto res = path::dirName(cmdFile);
104     if (res.empty()) {
105         LOG(INFO) << __func__ << "(): dirname empty for " << cmdFile;
106         return {};
107     }
108     if (!path::endsWith(cmdFile, INCFS_PENDING_READS_FILENAME)) {
109         LOG(INFO) << __func__ << "(): invalid file name " << cmdFile;
110         return {};
111     }
112     if (cmdFile.data() == res.data() || cmdFile.starts_with(res)) {
113         cmdFile.resize(res.size());
114         return cmdFile;
115     }
116     return std::string(res);
117 }
118 
isFsAvailable()119 static bool isFsAvailable() {
120     static const char kProcFilesystems[] = "/proc/filesystems";
121     std::string filesystems;
122     if (!ab::ReadFileToString(kProcFilesystems, &filesystems)) {
123         return false;
124     }
125     const auto result = filesystems.find("\t" INCFS_NAME "\n") != std::string::npos;
126     LOG(INFO) << "isFsAvailable: " << (result ? "true" : "false");
127     return result;
128 }
129 
getFirstApiLevel()130 static int getFirstApiLevel() {
131     uint64_t api_level = android::base::GetUintProperty<uint64_t>("ro.product.first_api_level", 0);
132     LOG(INFO) << "Initial API level of the device: " << api_level;
133     return api_level;
134 }
135 
incFsPropertyValue()136 static std::string_view incFsPropertyValue() {
137     constexpr const int R_API = 30;
138     static const auto kDefaultValue{getFirstApiLevel() > R_API ? "on" : ""};
139     static const ab::NoDestructor<std::string> kValue{
140             IncrementalProperties::enable().value_or(kDefaultValue)};
141     LOG(INFO) << "ro.incremental.enable: " << *kValue;
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 template <class Callback>
forEachFileIn(std::string_view dirPath,Callback cb)164 static IncFsErrorCode forEachFileIn(std::string_view dirPath, Callback cb) {
165     auto dir = path::openDir(details::c_str(dirPath));
166     if (!dir) {
167         return -EINVAL;
168     }
169 
170     int res = 0;
171     while (auto entry = (errno = 0, ::readdir(dir.get()))) {
172         if (entry->d_type != DT_REG) {
173             continue;
174         }
175         ++res;
176         if (!cb(entry->d_name)) {
177             break;
178         }
179     }
180     if (errno) {
181         return -errno;
182     }
183     return res;
184 }
185 
186 namespace {
187 
188 class IncFsInit {
189 public:
IncFsInit()190     IncFsInit() {
191         auto [featureEnabled, moduleName] = parseProperty(incFsPropertyValue());
192         featureEnabled_ = featureEnabled;
193         moduleName_ = moduleName;
194         loaded_ = featureEnabled_ && isFsAvailable();
195     }
196 
197     constexpr ~IncFsInit() = default;
198 
enabled() const199     bool enabled() const { return featureEnabled_; }
enabledAndReady() const200     bool enabledAndReady() const {
201         if (!featureEnabled_) {
202             return false;
203         }
204         if (moduleName_.empty()) {
205             return true;
206         }
207         if (loaded_) {
208             return true;
209         }
210         std::call_once(loadedFlag_, [this] {
211             if (isFsAvailable()) {
212                 // Loaded from a different process, I suppose.
213                 loaded_ = true;
214                 LOG(INFO) << "IncFS is already available, skipped loading";
215                 return;
216             }
217             const ab::unique_fd fd(TEMP_FAILURE_RETRY(
218                     ::open(details::c_str(moduleName_), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
219             if (fd < 0) {
220                 PLOG(ERROR) << "could not open IncFs kernel module \"" << moduleName_ << '"';
221                 return;
222             }
223 
224             const auto rc = syscall(__NR_finit_module, fd.get(), "", 0);
225             if (rc < 0) {
226                 PLOG(ERROR) << "finit_module for IncFs \"" << moduleName_ << "\" failed";
227                 return;
228             }
229             if (!isFsAvailable()) {
230                 LOG(ERROR) << "loaded IncFs kernel module \"" << moduleName_
231                            << "\" but incremental-fs is still not available";
232             }
233             loaded_ = true;
234             LOG(INFO) << "successfully loaded IncFs kernel module \"" << moduleName_ << '"';
235         });
236         return loaded_;
237     }
238 
239 private:
240     bool featureEnabled_;
241     std::string_view moduleName_;
242     mutable std::once_flag loadedFlag_;
243     mutable bool loaded_;
244 };
245 
246 } // namespace
247 
init()248 static IncFsInit& init() {
249     static IncFsInit initer;
250     return initer;
251 }
252 
IncFs_IsEnabled()253 bool IncFs_IsEnabled() {
254     return init().enabled();
255 }
256 
readIncFsFeatures()257 static Features readIncFsFeatures() {
258     init().enabledAndReady();
259 
260     int res = Features::none | Features::mappingFilesProgressFixed;
261 
262     static const char kSysfsFeaturesDir[] = "/sys/fs/" INCFS_NAME "/features";
263     const auto dir = path::openDir(kSysfsFeaturesDir);
264     if (!dir) {
265         PLOG(ERROR) << "IncFs_Features: failed to open features dir, assuming v1/none.";
266         return Features(res);
267     }
268 
269     while (auto entry = ::readdir(dir.get())) {
270         if (entry->d_type != DT_REG) {
271             continue;
272         }
273         if (entry->d_name == "corefs"sv) {
274             res |= Features::core;
275         } else if (entry->d_name == "v2"sv || entry->d_name == "report_uid"sv) {
276             res |= Features::v2;
277         }
278     }
279 
280     LOG(INFO) << "IncFs_Features: " << ((res & Features::v2) ? "v2" : "v1");
281 
282     return Features(res);
283 }
284 
IncFs_Features()285 IncFsFeatures IncFs_Features() {
286     static const auto features = IncFsFeatures(readIncFsFeatures());
287     return features;
288 }
289 
isIncFsFdImpl(int fd)290 bool isIncFsFdImpl(int fd) {
291     struct statfs fs = {};
292     if (::fstatfs(fd, &fs) != 0) {
293         PLOG(WARNING) << __func__ << "(): could not fstatfs fd " << fd;
294         return false;
295     }
296 
297     return fs.f_type == (decltype(fs.f_type))INCFS_MAGIC_NUMBER;
298 }
299 
isIncFsPathImpl(const char * path)300 bool isIncFsPathImpl(const char* path) {
301     struct statfs fs = {};
302     if (::statfs(path, &fs) != 0) {
303         PLOG(WARNING) << __func__ << "(): could not statfs " << path;
304         return false;
305     }
306 
307     return fs.f_type == (decltype(fs.f_type))INCFS_MAGIC_NUMBER;
308 }
309 
isDir(const char * path)310 static int isDir(const char* path) {
311     struct stat st;
312     if (::stat(path, &st) != 0) {
313         return -errno;
314     }
315     if (!S_ISDIR(st.st_mode)) {
316         return -ENOTDIR;
317     }
318     return 0;
319 }
320 
isAbsolute(const char * path)321 static bool isAbsolute(const char* path) {
322     return path && path[0] == '/';
323 }
324 
isValidMountTarget(const char * path)325 static int isValidMountTarget(const char* path) {
326     if (!isAbsolute(path)) {
327         return -EINVAL;
328     }
329     if (isIncFsPath(path)) {
330         LOG(ERROR) << "[incfs] mounting over existing incfs mount is not allowed";
331         return -EINVAL;
332     }
333     if (const auto err = isDir(path); err != 0) {
334         return err;
335     }
336     if (const auto err = path::isEmptyDir(path); err != 0) {
337         return err;
338     }
339     return 0;
340 }
341 
rmDirContent(int dirFd)342 static int rmDirContent(int dirFd) {
343     auto dir = path::openDir(dirFd);
344     if (!dir) {
345         return -errno;
346     }
347     while (auto entry = ::readdir(dir.get())) {
348         if (entry->d_name == "."sv || entry->d_name == ".."sv) {
349             continue;
350         }
351         if (entry->d_type == DT_DIR) {
352             auto fd = openAt(dirFd, entry->d_name, O_DIRECTORY);
353             if (!fd.ok()) {
354                 return -errno;
355             }
356             if (const auto err = rmDirContent(fd.get())) {
357                 return err;
358             }
359             if (::unlinkat(fd.get(), entry->d_name, AT_REMOVEDIR)) {
360                 return -errno;
361             }
362         } else {
363             auto fd = openAt(dirFd, entry->d_name);
364             if (!fd.ok()) {
365                 return -errno;
366             }
367             if (::unlinkat(fd.get(), entry->d_name, 0)) {
368                 return -errno;
369             }
370         }
371     }
372     return 0;
373 }
374 
rmDirContent(const char * path)375 static int rmDirContent(const char* path) {
376     auto fd = openAt(-1, path, O_DIRECTORY);
377     if (!fd.ok()) {
378         return -errno;
379     }
380     return rmDirContent(fd.get());
381 }
382 
makeMountOptionsString(IncFsMountOptions options)383 static std::string makeMountOptionsString(IncFsMountOptions options) {
384     auto opts = ab::StringPrintf("read_timeout_ms=%u,readahead=0,rlog_pages=%u,rlog_wakeup_cnt=1,",
385                                  unsigned(options.defaultReadTimeoutMs),
386                                  unsigned(options.readLogBufferPages < 0
387                                                   ? INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES
388                                                   : options.readLogBufferPages));
389     if (features() & Features::v2) {
390         ab::StringAppendF(&opts, "report_uid,");
391         if (options.sysfsName && *options.sysfsName) {
392             ab::StringAppendF(&opts, "sysfs_name=%s,", options.sysfsName);
393         }
394     }
395     return opts;
396 }
397 
makeControl(int fd)398 static IncFsControl* makeControl(int fd) {
399     auto cmd = openAt(fd, INCFS_PENDING_READS_FILENAME);
400     if (!cmd.ok()) {
401         return nullptr;
402     }
403     ab::unique_fd pendingReads(fcntl(cmd.get(), F_DUPFD_CLOEXEC, cmd.get()));
404     if (!pendingReads.ok()) {
405         return nullptr;
406     }
407     auto logs = openAt(fd, INCFS_LOG_FILENAME);
408     if (!logs.ok()) {
409         return nullptr;
410     }
411     ab::unique_fd blocksWritten;
412     if (features() & Features::v2) {
413         blocksWritten = openAt(fd, INCFS_BLOCKS_WRITTEN_FILENAME);
414         if (!blocksWritten.ok()) {
415             return nullptr;
416         }
417     }
418     auto control =
419             IncFs_CreateControl(cmd.get(), pendingReads.get(), logs.get(), blocksWritten.get());
420     if (control) {
421         (void)cmd.release();
422         (void)pendingReads.release();
423         (void)logs.release();
424         (void)blocksWritten.release();
425     } else {
426         errno = ENOMEM;
427     }
428     return control;
429 }
430 
makeCommandPath(std::string_view root,std::string_view item)431 static std::string makeCommandPath(std::string_view root, std::string_view item) {
432     auto [itemRoot, subpath] = registry().rootAndSubpathFor(item);
433     if (itemRoot != root) {
434         return {};
435     }
436     // TODO: add "/.cmd/" if we decide to use a separate control tree.
437     return path::join(itemRoot, subpath);
438 }
439 
toString(IncFsFileId id,char * out)440 static void toString(IncFsFileId id, char* out) {
441     // Make sure this function matches the one in the kernel (e.g. same case for a-f digits).
442     static constexpr char kHexChar[] = "0123456789abcdef";
443 
444     for (auto item = std::begin(id.data); item != std::end(id.data); ++item, out += 2) {
445         out[0] = kHexChar[(*item & 0xf0) >> 4];
446         out[1] = kHexChar[(*item & 0x0f)];
447     }
448 }
449 
toStringImpl(IncFsFileId id)450 static std::string toStringImpl(IncFsFileId id) {
451     std::string res(kIncFsFileIdStringLength, '\0');
452     toString(id, res.data());
453     return res;
454 }
455 
toFileIdImpl(std::string_view str)456 static IncFsFileId toFileIdImpl(std::string_view str) {
457     if (str.size() != kIncFsFileIdStringLength) {
458         return kIncFsInvalidFileId;
459     }
460 
461     IncFsFileId res;
462     auto out = (char*)&res;
463     for (auto it = str.begin(); it != str.end(); it += 2, ++out) {
464         static const auto fromChar = [](char src) -> int {
465             if (src >= '0' && src <= '9') {
466                 return src - '0';
467             }
468             if (src >= 'a' && src <= 'f') {
469                 return src - 'a' + 10;
470             }
471             return -1;
472         };
473 
474         const int c[2] = {fromChar(it[0]), fromChar(it[1])};
475         if (c[0] == -1 || c[1] == -1) {
476             errno = EINVAL;
477             return kIncFsInvalidFileId;
478         }
479         *out = (c[0] << 4) | c[1];
480     }
481     return res;
482 }
483 
IncFs_FileIdToString(IncFsFileId id,char * out)484 int IncFs_FileIdToString(IncFsFileId id, char* out) {
485     if (!out) {
486         return -EINVAL;
487     }
488     toString(id, out);
489     return 0;
490 }
491 
IncFs_FileIdFromString(const char * in)492 IncFsFileId IncFs_FileIdFromString(const char* in) {
493     return toFileIdImpl({in, kIncFsFileIdStringLength});
494 }
495 
IncFs_FileIdFromMetadata(IncFsSpan metadata)496 IncFsFileId IncFs_FileIdFromMetadata(IncFsSpan metadata) {
497     IncFsFileId id = {};
498     if (size_t(metadata.size) <= sizeof(id)) {
499         memcpy(&id, metadata.data, metadata.size);
500     } else {
501         uint8_t buffer[SHA_DIGEST_LENGTH];
502         static_assert(sizeof(buffer) >= sizeof(id));
503 
504         SHA_CTX ctx;
505         SHA1_Init(&ctx);
506         SHA1_Update(&ctx, metadata.data, metadata.size);
507         SHA1_Final(buffer, &ctx);
508         memcpy(&id, buffer, sizeof(id));
509     }
510     return id;
511 }
512 
restoreconControlFiles(std::string_view targetDir)513 static bool restoreconControlFiles(std::string_view targetDir) {
514     static constexpr auto restorecon = [](const char* name) {
515         if (const auto err = selinux_android_restorecon(name, SELINUX_ANDROID_RESTORECON_FORCE);
516             err != 0) {
517             errno = -err;
518             PLOG(ERROR) << "[incfs] Failed to restorecon: " << name;
519             return false;
520         }
521         return true;
522     };
523     if (!restorecon(path::join(targetDir, INCFS_PENDING_READS_FILENAME).c_str())) {
524         return false;
525     }
526     if (!restorecon(path::join(targetDir, INCFS_LOG_FILENAME).c_str())) {
527         return false;
528     }
529     if ((features() & Features::v2) &&
530         !restorecon(path::join(targetDir, INCFS_BLOCKS_WRITTEN_FILENAME).c_str())) {
531         return false;
532     }
533     return true;
534 }
535 
IncFs_Mount(const char * backingPath,const char * targetDir,IncFsMountOptions options)536 IncFsControl* IncFs_Mount(const char* backingPath, const char* targetDir,
537                           IncFsMountOptions options) {
538     if (!init().enabledAndReady()) {
539         LOG(WARNING) << "[incfs] Feature is not enabled";
540         errno = ENOTSUP;
541         return nullptr;
542     }
543 
544     if (auto err = isValidMountTarget(targetDir); err != 0) {
545         errno = -err;
546         return nullptr;
547     }
548     if (!isAbsolute(backingPath)) {
549         errno = EINVAL;
550         return nullptr;
551     }
552 
553     if (options.flags & createOnly) {
554         if (const auto err = path::isEmptyDir(backingPath); err != 0) {
555             errno = -err;
556             return nullptr;
557         }
558     } else if (options.flags & android::incfs::truncate) {
559         if (const auto err = rmDirContent(backingPath); err != 0) {
560             errno = -err;
561             return nullptr;
562         }
563     }
564 
565     const auto opts = makeMountOptionsString(options);
566     if (::mount(backingPath, targetDir, INCFS_NAME, MS_NOSUID | MS_NODEV | MS_NOATIME,
567                 opts.c_str())) {
568         PLOG(ERROR) << "[incfs] Failed to mount IncFS filesystem: " << targetDir;
569         return nullptr;
570     }
571 
572     // in case when the path is given in a form of a /proc/.../fd/ link, we need to update
573     // it here: old fd refers to the original empty directory, not to the mount
574     std::string updatedTargetDir;
575     if (path::dirName(targetDir) == path::procfsFdDir) {
576         updatedTargetDir = path::readlink(targetDir);
577     } else {
578         updatedTargetDir = targetDir;
579     }
580 
581     auto rootFd = ab::unique_fd(::open(updatedTargetDir.c_str(), O_PATH | O_CLOEXEC | O_DIRECTORY));
582     if (updatedTargetDir != targetDir) {
583         // ensure that the new directory is still the same after reopening
584         if (path::fromFd(rootFd) != updatedTargetDir) {
585             errno = EINVAL;
586             return nullptr;
587         }
588     }
589 
590     if (!restoreconControlFiles(path::procfsForFd(rootFd))) {
591         (void)IncFs_Unmount(targetDir);
592         return nullptr;
593     }
594 
595     auto control = makeControl(rootFd);
596     if (control == nullptr) {
597         (void)IncFs_Unmount(targetDir);
598         return nullptr;
599     }
600     return control;
601 }
602 
IncFs_Open(const char * dir)603 IncFsControl* IncFs_Open(const char* dir) {
604     auto root = registry().rootFor(dir);
605     if (root.empty()) {
606         errno = EINVAL;
607         return nullptr;
608     }
609     auto rootFd = ab::unique_fd(::open(details::c_str(root), O_PATH | O_CLOEXEC | O_DIRECTORY));
610     return makeControl(rootFd);
611 }
612 
IncFs_GetControlFd(const IncFsControl * control,IncFsFdType type)613 IncFsFd IncFs_GetControlFd(const IncFsControl* control, IncFsFdType type) {
614     if (!control) {
615         return -EINVAL;
616     }
617     switch (type) {
618         case CMD:
619             return control->cmd;
620         case PENDING_READS:
621             return control->pendingReads;
622         case LOGS:
623             return control->logs;
624         case BLOCKS_WRITTEN:
625             return control->blocksWritten;
626         default:
627             return -EINVAL;
628     }
629 }
630 
IncFs_ReleaseControlFds(IncFsControl * control,IncFsFd out[],IncFsSize outSize)631 IncFsSize IncFs_ReleaseControlFds(IncFsControl* control, IncFsFd out[], IncFsSize outSize) {
632     if (!control || !out) {
633         return -EINVAL;
634     }
635     if (outSize < IncFsFdType::FDS_COUNT) {
636         return -ERANGE;
637     }
638     out[CMD] = std::exchange(control->cmd, -1);
639     out[PENDING_READS] = std::exchange(control->pendingReads, -1);
640     out[LOGS] = std::exchange(control->logs, -1);
641     out[BLOCKS_WRITTEN] = std::exchange(control->blocksWritten, -1);
642     return IncFsFdType::FDS_COUNT;
643 }
644 
IncFs_CreateControl(IncFsFd cmd,IncFsFd pendingReads,IncFsFd logs,IncFsFd blocksWritten)645 IncFsControl* IncFs_CreateControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs,
646                                   IncFsFd blocksWritten) {
647     return new IncFsControl(cmd, pendingReads, logs, blocksWritten);
648 }
649 
IncFs_DeleteControl(IncFsControl * control)650 void IncFs_DeleteControl(IncFsControl* control) {
651     if (control) {
652         if (control->cmd >= 0) {
653             close(control->cmd);
654         }
655         if (control->pendingReads >= 0) {
656             close(control->pendingReads);
657         }
658         if (control->logs >= 0) {
659             close(control->logs);
660         }
661         if (control->blocksWritten >= 0) {
662             close(control->blocksWritten);
663         }
664         delete control;
665     }
666 }
667 
IncFs_SetOptions(const IncFsControl * control,IncFsMountOptions options)668 IncFsErrorCode IncFs_SetOptions(const IncFsControl* control, IncFsMountOptions options) {
669     if (!control) {
670         return -EINVAL;
671     }
672     auto root = rootForCmd(control->cmd);
673     if (root.empty()) {
674         return -EINVAL;
675     }
676     auto opts = makeMountOptionsString(options);
677     if (::mount(nullptr, root.c_str(), nullptr, MS_REMOUNT | MS_NOSUID | MS_NODEV | MS_NOATIME,
678                 opts.c_str()) != 0) {
679         const auto error = errno;
680         PLOG(ERROR) << "[incfs] Failed to remount IncFS filesystem: " << root;
681         return -error;
682     }
683     return 0;
684 }
685 
IncFs_Root(const IncFsControl * control,char buffer[],size_t * bufferSize)686 IncFsErrorCode IncFs_Root(const IncFsControl* control, char buffer[], size_t* bufferSize) {
687     if (!control) {
688         return -EINVAL;
689     }
690     std::string result = rootForCmd(control->cmd);
691     if (*bufferSize <= result.size()) {
692         *bufferSize = result.size() + 1;
693         return -EOVERFLOW;
694     }
695     result.copy(buffer, result.size());
696     buffer[result.size()] = '\0';
697     *bufferSize = result.size();
698     return 0;
699 }
700 
701 template <class T>
read(IncFsSpan & data)702 std::optional<T> read(IncFsSpan& data) {
703     if (data.size < (int32_t)sizeof(T)) {
704         return {};
705     }
706     T res;
707     memcpy(&res, data.data, sizeof(res));
708     data.data += sizeof(res);
709     data.size -= sizeof(res);
710     return res;
711 }
712 
validateSignatureFormat(IncFsSpan signature)713 static IncFsErrorCode validateSignatureFormat(IncFsSpan signature) {
714     if (signature.data == nullptr && signature.size == 0) {
715         return 0; // it's fine to have unverified files too
716     }
717     if ((signature.data == nullptr) != (signature.size == 0)) {
718         return -EINVAL;
719     }
720 
721     // These structs are here purely for checking the minimum size. Maybe will use them for
722     // parsing later.
723     struct __attribute__((packed)) Hashing {
724         int32_t size;
725         int32_t algorithm;
726         int8_t log2_blocksize;
727         int32_t salt_size;
728         int32_t raw_root_hash_size;
729     };
730     struct __attribute__((packed)) Signing {
731         int32_t size;
732         int32_t apk_digest_size;
733         int32_t certificate_size;
734         int32_t addl_data_size;
735         int32_t public_key_size;
736         int32_t algorithm;
737         int32_t signature_size;
738     };
739     struct __attribute__((packed)) MinSignature {
740         int32_t version;
741         Hashing hashing_info;
742         Signing signing_info;
743     };
744 
745     if (signature.size < (int32_t)sizeof(MinSignature)) {
746         return -ERANGE;
747     }
748     if (signature.size > INCFS_MAX_SIGNATURE_SIZE) {
749         return -ERANGE;
750     }
751 
752     auto version = read<int32_t>(signature);
753     if (version.value_or(-1) != INCFS_SIGNATURE_VERSION) {
754         return -EINVAL;
755     }
756     auto hashSize = read<int32_t>(signature);
757     if (!hashSize || signature.size < *hashSize) {
758         return -EINVAL;
759     }
760     auto hashAlgo = read<int32_t>(signature);
761     if (hashAlgo.value_or(-1) != INCFS_HASH_TREE_SHA256) {
762         return -EINVAL;
763     }
764     auto logBlockSize = read<int8_t>(signature);
765     if (logBlockSize.value_or(-1) != 12 /* 2^12 == 4096 */) {
766         return -EINVAL;
767     }
768     auto saltSize = read<int32_t>(signature);
769     if (saltSize.value_or(-1) != 0) {
770         return -EINVAL;
771     }
772     auto rootHashSize = read<int32_t>(signature);
773     if (rootHashSize.value_or(-1) != INCFS_MAX_HASH_SIZE) {
774         return -EINVAL;
775     }
776     if (signature.size < *rootHashSize) {
777         return -EINVAL;
778     }
779     signature.data += *rootHashSize;
780     signature.size -= *rootHashSize;
781     auto signingSize = read<int32_t>(signature);
782     // everything remaining has to be in the signing info
783     if (signingSize.value_or(-1) != signature.size) {
784         return -EINVAL;
785     }
786 
787     // TODO: validate the signature part too.
788     return 0;
789 }
790 
IncFs_MakeFile(const IncFsControl * control,const char * path,int32_t mode,IncFsFileId id,IncFsNewFileParams params)791 IncFsErrorCode IncFs_MakeFile(const IncFsControl* control, const char* path, int32_t mode,
792                               IncFsFileId id, IncFsNewFileParams params) {
793     if (!control) {
794         return -EINVAL;
795     }
796 
797     auto [root, subpath] = registry().rootAndSubpathFor(path);
798     if (root.empty()) {
799         PLOG(WARNING) << "[incfs] makeFile failed for path " << path << ", root is empty.";
800         return -EINVAL;
801     }
802     if (params.size < 0) {
803         LOG(WARNING) << "[incfs] makeFile failed for path " << path
804                      << ", size is invalid: " << params.size;
805         return -ERANGE;
806     }
807 
808     const auto [subdir, name] = path::splitDirBase(subpath);
809     incfs_new_file_args args = {
810             .size = (uint64_t)params.size,
811             .mode = (uint16_t)mode,
812             .directory_path = (uint64_t)subdir.data(),
813             .file_name = (uint64_t)name.data(),
814             .file_attr = (uint64_t)params.metadata.data,
815             .file_attr_len = (uint32_t)params.metadata.size,
816     };
817     static_assert(sizeof(args.file_id.bytes) == sizeof(id.data));
818     memcpy(args.file_id.bytes, id.data, sizeof(args.file_id.bytes));
819 
820     if (auto err = validateSignatureFormat(params.signature)) {
821         return err;
822     }
823     args.signature_info = (uint64_t)(uintptr_t)params.signature.data;
824     args.signature_size = (uint64_t)params.signature.size;
825 
826     if (::ioctl(control->cmd, INCFS_IOC_CREATE_FILE, &args)) {
827         PLOG(WARNING) << "[incfs] makeFile failed for " << root << " / " << subdir << " / " << name
828                       << " of " << params.size << " bytes";
829         return -errno;
830     }
831     if (::chmod(path::join(root, subdir, name).c_str(), mode)) {
832         PLOG(WARNING) << "[incfs] couldn't change file mode to 0" << std::oct << mode;
833     }
834 
835     return 0;
836 }
837 
IncFs_MakeMappedFile(const IncFsControl * control,const char * path,int32_t mode,IncFsNewMappedFileParams params)838 IncFsErrorCode IncFs_MakeMappedFile(const IncFsControl* control, const char* path, int32_t mode,
839                                     IncFsNewMappedFileParams params) {
840     if (!control) {
841         return -EINVAL;
842     }
843 
844     auto [root, subpath] = registry().rootAndSubpathFor(path);
845     if (root.empty()) {
846         PLOG(WARNING) << "[incfs] makeMappedFile failed for path " << path << ", root is empty.";
847         return -EINVAL;
848     }
849     if (params.size < 0) {
850         LOG(WARNING) << "[incfs] makeMappedFile failed for path " << path
851                      << ", size is invalid: " << params.size;
852         return -ERANGE;
853     }
854 
855     const auto [subdir, name] = path::splitDirBase(subpath);
856     incfs_create_mapped_file_args args = {
857             .size = (uint64_t)params.size,
858             .mode = (uint16_t)mode,
859             .directory_path = (uint64_t)subdir.data(),
860             .file_name = (uint64_t)name.data(),
861             .source_offset = (uint64_t)params.sourceOffset,
862     };
863     static_assert(sizeof(args.source_file_id.bytes) == sizeof(params.sourceId.data));
864     memcpy(args.source_file_id.bytes, params.sourceId.data, sizeof(args.source_file_id.bytes));
865 
866     if (::ioctl(control->cmd, INCFS_IOC_CREATE_MAPPED_FILE, &args)) {
867         PLOG(WARNING) << "[incfs] makeMappedFile failed for " << root << " / " << subdir << " / "
868                       << name << " of " << params.size << " bytes starting at "
869                       << params.sourceOffset;
870         return -errno;
871     }
872     if (::chmod(path::join(root, subpath).c_str(), mode)) {
873         PLOG(WARNING) << "[incfs] makeMappedFile error: couldn't change file mode to 0" << std::oct
874                       << mode;
875     }
876 
877     return 0;
878 }
879 
makeDir(const char * commandPath,int32_t mode,bool allowExisting)880 static IncFsErrorCode makeDir(const char* commandPath, int32_t mode, bool allowExisting) {
881     if (!::mkdir(commandPath, mode)) {
882         if (::chmod(commandPath, mode)) {
883             PLOG(WARNING) << "[incfs] couldn't change directory mode to 0" << std::oct << mode;
884         }
885         return 0;
886     }
887     // don't touch the existing dir's mode - mkdir(1) works that way.
888     return (allowExisting && errno == EEXIST) ? 0 : -errno;
889 }
890 
makeDirs(std::string_view commandPath,std::string_view path,std::string_view root,int32_t mode)891 static IncFsErrorCode makeDirs(std::string_view commandPath, std::string_view path,
892                                std::string_view root, int32_t mode) {
893     auto commandCPath = details::c_str(commandPath);
894     const auto mkdirRes = makeDir(commandCPath, mode, true);
895     if (!mkdirRes) {
896         return 0;
897     }
898     if (mkdirRes != -ENOENT) {
899         LOG(ERROR) << __func__ << "(): mkdir failed for " << path << " - " << mkdirRes;
900         return mkdirRes;
901     }
902 
903     const auto parent = path::dirName(commandPath);
904     if (!path::startsWith(parent, root)) {
905         // went too far, already out of the root mount
906         return -EINVAL;
907     }
908 
909     if (auto parentMkdirRes = makeDirs(parent, path::dirName(path), root, mode)) {
910         return parentMkdirRes;
911     }
912     return makeDir(commandCPath, mode, true);
913 }
914 
IncFs_MakeDir(const IncFsControl * control,const char * path,int32_t mode)915 IncFsErrorCode IncFs_MakeDir(const IncFsControl* control, const char* path, int32_t mode) {
916     if (!control) {
917         return -EINVAL;
918     }
919     const auto root = rootForCmd(control->cmd);
920     if (root.empty()) {
921         LOG(ERROR) << __func__ << "(): root is empty for " << path;
922         return -EINVAL;
923     }
924     auto commandPath = makeCommandPath(root, path);
925     if (commandPath.empty()) {
926         LOG(ERROR) << __func__ << "(): commandPath is empty for " << path;
927         return -EINVAL;
928     }
929     if (auto res = makeDir(commandPath.c_str(), mode, false)) {
930         LOG(ERROR) << __func__ << "(): mkdir failed for " << commandPath << " - " << res;
931         return res;
932     }
933     return 0;
934 }
935 
IncFs_MakeDirs(const IncFsControl * control,const char * path,int32_t mode)936 IncFsErrorCode IncFs_MakeDirs(const IncFsControl* control, const char* path, int32_t mode) {
937     if (!control) {
938         return -EINVAL;
939     }
940     const auto root = rootForCmd(control->cmd);
941     if (root.empty()) {
942         LOG(ERROR) << __func__ << "(): root is empty for " << path;
943         return -EINVAL;
944     }
945     auto commandPath = makeCommandPath(root, path);
946     if (commandPath.empty()) {
947         LOG(ERROR) << __func__ << "(): commandPath is empty for " << path;
948         return -EINVAL;
949     }
950     return makeDirs(commandPath, path, root, mode);
951 }
952 
getMetadata(const char * path,char buffer[],size_t * bufferSize)953 static IncFsErrorCode getMetadata(const char* path, char buffer[], size_t* bufferSize) {
954     const auto res = ::getxattr(path, kMetadataAttrName, buffer, *bufferSize);
955     if (res < 0) {
956         if (errno == ERANGE) {
957             auto neededSize = ::getxattr(path, kMetadataAttrName, buffer, 0);
958             if (neededSize >= 0) {
959                 *bufferSize = neededSize;
960                 return 0;
961             }
962         }
963         return -errno;
964     }
965     *bufferSize = res;
966     return 0;
967 }
968 
IncFs_GetMetadataById(const IncFsControl * control,IncFsFileId fileId,char buffer[],size_t * bufferSize)969 IncFsErrorCode IncFs_GetMetadataById(const IncFsControl* control, IncFsFileId fileId, char buffer[],
970                                      size_t* bufferSize) {
971     if (!control) {
972         return -EINVAL;
973     }
974 
975     const auto root = rootForCmd(control->cmd);
976     if (root.empty()) {
977         return -EINVAL;
978     }
979     auto name = indexPath(root, fileId);
980     return getMetadata(details::c_str(name), buffer, bufferSize);
981 }
982 
IncFs_GetMetadataByPath(const IncFsControl * control,const char * path,char buffer[],size_t * bufferSize)983 IncFsErrorCode IncFs_GetMetadataByPath(const IncFsControl* control, const char* path, char buffer[],
984                                        size_t* bufferSize) {
985     if (!control) {
986         return -EINVAL;
987     }
988     const auto pathRoot = registry().rootFor(path);
989     const auto root = rootForCmd(control->cmd);
990     if (root.empty() || root != pathRoot) {
991         return -EINVAL;
992     }
993 
994     return getMetadata(path, buffer, bufferSize);
995 }
996 
997 template <class GetterFunc, class Param>
getId(GetterFunc getter,Param param)998 static IncFsFileId getId(GetterFunc getter, Param param) {
999     char buffer[kIncFsFileIdStringLength];
1000     const auto res = getter(param, kIdAttrName, buffer, sizeof(buffer));
1001     if (res != sizeof(buffer)) {
1002         return kIncFsInvalidFileId;
1003     }
1004     return toFileIdImpl({buffer, std::size(buffer)});
1005 }
1006 
IncFs_GetId(const IncFsControl * control,const char * path)1007 IncFsFileId IncFs_GetId(const IncFsControl* control, const char* path) {
1008     if (!control) {
1009         return kIncFsInvalidFileId;
1010     }
1011     const auto pathRoot = registry().rootFor(path);
1012     const auto root = rootForCmd(control->cmd);
1013     if (root.empty() || root != pathRoot) {
1014         errno = EINVAL;
1015         return kIncFsInvalidFileId;
1016     }
1017     return getId(::getxattr, path);
1018 }
1019 
getSignature(int fd,char buffer[],size_t * bufferSize)1020 static IncFsErrorCode getSignature(int fd, char buffer[], size_t* bufferSize) {
1021     incfs_get_file_sig_args args = {
1022             .file_signature = (uint64_t)buffer,
1023             .file_signature_buf_size = (uint32_t)*bufferSize,
1024     };
1025 
1026     auto res = ::ioctl(fd, INCFS_IOC_READ_FILE_SIGNATURE, &args);
1027     if (res < 0) {
1028         if (errno == E2BIG) {
1029             *bufferSize = INCFS_MAX_SIGNATURE_SIZE;
1030         }
1031         return -errno;
1032     }
1033     *bufferSize = args.file_signature_len_out;
1034     return 0;
1035 }
1036 
IncFs_GetSignatureById(const IncFsControl * control,IncFsFileId fileId,char buffer[],size_t * bufferSize)1037 IncFsErrorCode IncFs_GetSignatureById(const IncFsControl* control, IncFsFileId fileId,
1038                                       char buffer[], size_t* bufferSize) {
1039     if (!control) {
1040         return -EINVAL;
1041     }
1042 
1043     const auto root = rootForCmd(control->cmd);
1044     if (root.empty()) {
1045         return -EINVAL;
1046     }
1047     auto file = indexPath(root, fileId);
1048     auto fd = openRaw(file);
1049     if (fd < 0) {
1050         return fd.get();
1051     }
1052     return getSignature(fd, buffer, bufferSize);
1053 }
1054 
IncFs_GetSignatureByPath(const IncFsControl * control,const char * path,char buffer[],size_t * bufferSize)1055 IncFsErrorCode IncFs_GetSignatureByPath(const IncFsControl* control, const char* path,
1056                                         char buffer[], size_t* bufferSize) {
1057     if (!control) {
1058         return -EINVAL;
1059     }
1060 
1061     const auto pathRoot = registry().rootFor(path);
1062     const auto root = rootForCmd(control->cmd);
1063     if (root.empty() || root != pathRoot) {
1064         return -EINVAL;
1065     }
1066     return IncFs_UnsafeGetSignatureByPath(path, buffer, bufferSize);
1067 }
1068 
IncFs_UnsafeGetSignatureByPath(const char * path,char buffer[],size_t * bufferSize)1069 IncFsErrorCode IncFs_UnsafeGetSignatureByPath(const char* path, char buffer[], size_t* bufferSize) {
1070     if (!isIncFsPath(path)) {
1071         return -EINVAL;
1072     }
1073     auto fd = openRaw(path);
1074     if (fd < 0) {
1075         return fd.get();
1076     }
1077     return getSignature(fd, buffer, bufferSize);
1078 }
1079 
IncFs_Link(const IncFsControl * control,const char * fromPath,const char * wherePath)1080 IncFsErrorCode IncFs_Link(const IncFsControl* control, const char* fromPath,
1081                           const char* wherePath) {
1082     if (!control) {
1083         return -EINVAL;
1084     }
1085 
1086     auto root = rootForCmd(control->cmd);
1087     if (root.empty()) {
1088         return -EINVAL;
1089     }
1090     auto cmdFrom = makeCommandPath(root, fromPath);
1091     if (cmdFrom.empty()) {
1092         return -EINVAL;
1093     }
1094     auto cmdWhere = makeCommandPath(root, wherePath);
1095     if (cmdWhere.empty()) {
1096         return -EINVAL;
1097     }
1098     if (::link(cmdFrom.c_str(), cmdWhere.c_str())) {
1099         return -errno;
1100     }
1101     return 0;
1102 }
1103 
IncFs_Unlink(const IncFsControl * control,const char * path)1104 IncFsErrorCode IncFs_Unlink(const IncFsControl* control, const char* path) {
1105     if (!control) {
1106         return -EINVAL;
1107     }
1108 
1109     auto root = rootForCmd(control->cmd);
1110     if (root.empty()) {
1111         return -EINVAL;
1112     }
1113     auto cmdPath = makeCommandPath(root, path);
1114     if (cmdPath.empty()) {
1115         return -EINVAL;
1116     }
1117     if (::unlink(cmdPath.c_str())) {
1118         if (errno == EISDIR) {
1119             if (!::rmdir(cmdPath.c_str())) {
1120                 return 0;
1121             }
1122         }
1123         return -errno;
1124     }
1125     return 0;
1126 }
1127 
1128 template <class RawPendingRead>
waitForReadsImpl(int fd,int32_t timeoutMs,RawPendingRead pendingReadsBuffer[],size_t * pendingReadsBufferSize)1129 static int waitForReadsImpl(int fd, int32_t timeoutMs, RawPendingRead pendingReadsBuffer[],
1130                             size_t* pendingReadsBufferSize) {
1131     using namespace std::chrono;
1132     auto hrTimeout = steady_clock::duration(milliseconds(timeoutMs));
1133 
1134     while (hrTimeout > hrTimeout.zero() || (!pendingReadsBuffer && hrTimeout == hrTimeout.zero())) {
1135         const auto startTs = steady_clock::now();
1136 
1137         pollfd pfd = {fd, POLLIN, 0};
1138         const auto res = ::poll(&pfd, 1, duration_cast<milliseconds>(hrTimeout).count());
1139         if (res > 0) {
1140             break;
1141         }
1142         if (res == 0) {
1143             if (pendingReadsBufferSize) {
1144                 *pendingReadsBufferSize = 0;
1145             }
1146             return -ETIMEDOUT;
1147         }
1148         const auto error = errno;
1149         if (error != EINTR) {
1150             PLOG(ERROR) << "poll() failed";
1151             return -error;
1152         }
1153         hrTimeout -= steady_clock::now() - startTs;
1154     }
1155     if (!pendingReadsBuffer) {
1156         return hrTimeout < hrTimeout.zero() ? -ETIMEDOUT : 0;
1157     }
1158 
1159     auto res =
1160             ::read(fd, pendingReadsBuffer, *pendingReadsBufferSize * sizeof(*pendingReadsBuffer));
1161     if (res < 0) {
1162         const auto error = errno;
1163         PLOG(ERROR) << "read() failed";
1164         return -error;
1165     }
1166     if (res == 0) {
1167         *pendingReadsBufferSize = 0;
1168         return -ETIMEDOUT;
1169     }
1170     if ((res % sizeof(*pendingReadsBuffer)) != 0) {
1171         PLOG(ERROR) << "read() returned half of a struct??";
1172         return -EFAULT;
1173     }
1174     *pendingReadsBufferSize = res / sizeof(*pendingReadsBuffer);
1175     return 0;
1176 }
1177 
1178 template <class PublicPendingRead, class RawPendingRead>
convertRead(RawPendingRead rawRead)1179 PublicPendingRead convertRead(RawPendingRead rawRead) {
1180     PublicPendingRead res = {
1181             .bootClockTsUs = rawRead.timestamp_us,
1182             .block = (IncFsBlockIndex)rawRead.block_index,
1183             .serialNo = rawRead.serial_number,
1184     };
1185     memcpy(&res.id.data, rawRead.file_id.bytes, sizeof(res.id.data));
1186 
1187     if constexpr (std::is_same_v<PublicPendingRead, IncFsReadInfoWithUid>) {
1188         if constexpr (std::is_same_v<RawPendingRead, incfs_pending_read_info2>) {
1189             res.uid = rawRead.uid;
1190         } else {
1191             res.uid = kIncFsNoUid;
1192         }
1193     }
1194     return res;
1195 }
1196 
1197 template <class RawPendingRead, class PublicPendingRead>
waitForReads(IncFsFd readFd,int32_t timeoutMs,PublicPendingRead buffer[],size_t * bufferSize)1198 static int waitForReads(IncFsFd readFd, int32_t timeoutMs, PublicPendingRead buffer[],
1199                         size_t* bufferSize) {
1200     std::vector<RawPendingRead> pendingReads(*bufferSize);
1201     if (const auto res = waitForReadsImpl(readFd, timeoutMs, pendingReads.data(), bufferSize)) {
1202         return res;
1203     }
1204     for (size_t i = 0; i != *bufferSize; ++i) {
1205         buffer[i] = convertRead<PublicPendingRead>(pendingReads[i]);
1206     }
1207     return 0;
1208 }
1209 
1210 template <class PublicPendingRead>
waitForReads(IncFsFd readFd,int32_t timeoutMs,PublicPendingRead buffer[],size_t * bufferSize)1211 static int waitForReads(IncFsFd readFd, int32_t timeoutMs, PublicPendingRead buffer[],
1212                         size_t* bufferSize) {
1213     if (features() & Features::v2) {
1214         return waitForReads<incfs_pending_read_info2>(readFd, timeoutMs, buffer, bufferSize);
1215     }
1216     return waitForReads<incfs_pending_read_info>(readFd, timeoutMs, buffer, bufferSize);
1217 }
1218 
IncFs_WaitForPendingReads(const IncFsControl * control,int32_t timeoutMs,IncFsReadInfo buffer[],size_t * bufferSize)1219 IncFsErrorCode IncFs_WaitForPendingReads(const IncFsControl* control, int32_t timeoutMs,
1220                                          IncFsReadInfo buffer[], size_t* bufferSize) {
1221     if (!control || control->pendingReads < 0) {
1222         return -EINVAL;
1223     }
1224 
1225     return waitForReads(control->pendingReads, timeoutMs, buffer, bufferSize);
1226 }
1227 
IncFs_WaitForPendingReadsWithUid(const IncFsControl * control,int32_t timeoutMs,IncFsReadInfoWithUid buffer[],size_t * bufferSize)1228 IncFsErrorCode IncFs_WaitForPendingReadsWithUid(const IncFsControl* control, int32_t timeoutMs,
1229                                                 IncFsReadInfoWithUid buffer[], size_t* bufferSize) {
1230     if (!control || control->pendingReads < 0) {
1231         return -EINVAL;
1232     }
1233 
1234     return waitForReads(control->pendingReads, timeoutMs, buffer, bufferSize);
1235 }
1236 
IncFs_WaitForPageReads(const IncFsControl * control,int32_t timeoutMs,IncFsReadInfo buffer[],size_t * bufferSize)1237 IncFsErrorCode IncFs_WaitForPageReads(const IncFsControl* control, int32_t timeoutMs,
1238                                       IncFsReadInfo buffer[], size_t* bufferSize) {
1239     if (!control || control->logs < 0) {
1240         return -EINVAL;
1241     }
1242 
1243     return waitForReads(control->logs, timeoutMs, buffer, bufferSize);
1244 }
1245 
IncFs_WaitForPageReadsWithUid(const IncFsControl * control,int32_t timeoutMs,IncFsReadInfoWithUid buffer[],size_t * bufferSize)1246 IncFsErrorCode IncFs_WaitForPageReadsWithUid(const IncFsControl* control, int32_t timeoutMs,
1247                                              IncFsReadInfoWithUid buffer[], size_t* bufferSize) {
1248     if (!control || control->logs < 0) {
1249         return -EINVAL;
1250     }
1251 
1252     return waitForReads(control->logs, timeoutMs, buffer, bufferSize);
1253 }
1254 
openForSpecialOps(int cmd,const char * path)1255 static IncFsFd openForSpecialOps(int cmd, const char* path) {
1256     ab::unique_fd fd(::open(path, O_RDONLY | O_CLOEXEC));
1257     if (fd < 0) {
1258         return -errno;
1259     }
1260     struct incfs_permit_fill args = {.file_descriptor = (uint32_t)fd.get()};
1261     auto err = ::ioctl(cmd, INCFS_IOC_PERMIT_FILL, &args);
1262     if (err < 0) {
1263         return -errno;
1264     }
1265     return fd.release();
1266 }
1267 
IncFs_OpenForSpecialOpsByPath(const IncFsControl * control,const char * path)1268 IncFsFd IncFs_OpenForSpecialOpsByPath(const IncFsControl* control, const char* path) {
1269     if (!control) {
1270         return -EINVAL;
1271     }
1272 
1273     const auto pathRoot = registry().rootFor(path);
1274     const auto cmd = control->cmd;
1275     const auto root = rootForCmd(cmd);
1276     if (root.empty() || root != pathRoot) {
1277         return -EINVAL;
1278     }
1279     return openForSpecialOps(cmd, makeCommandPath(root, path).c_str());
1280 }
1281 
IncFs_OpenForSpecialOpsById(const IncFsControl * control,IncFsFileId id)1282 IncFsFd IncFs_OpenForSpecialOpsById(const IncFsControl* control, IncFsFileId id) {
1283     if (!control) {
1284         return -EINVAL;
1285     }
1286 
1287     const auto cmd = control->cmd;
1288     const auto root = rootForCmd(cmd);
1289     if (root.empty()) {
1290         return -EINVAL;
1291     }
1292     auto name = indexPath(root, id);
1293     return openForSpecialOps(cmd, makeCommandPath(root, name).c_str());
1294 }
1295 
writeBlocks(int fd,const incfs_fill_block blocks[],int blocksCount)1296 static int writeBlocks(int fd, const incfs_fill_block blocks[], int blocksCount) {
1297     if (fd < 0 || blocksCount == 0) {
1298         return 0;
1299     }
1300     if (blocksCount < 0) {
1301         return -EINVAL;
1302     }
1303 
1304     auto ptr = blocks;
1305     const auto end = blocks + blocksCount;
1306     do {
1307         struct incfs_fill_blocks args = {.count = uint64_t(end - ptr),
1308                                          .fill_blocks = (uint64_t)(uintptr_t)ptr};
1309         const auto written = ::ioctl(fd, INCFS_IOC_FILL_BLOCKS, &args);
1310         if (written < 0) {
1311             if (errno == EINTR) {
1312                 continue;
1313             }
1314             const auto error = errno;
1315             PLOG(WARNING) << "writing IncFS blocks failed";
1316             if (ptr == blocks) {
1317                 return -error;
1318             }
1319             // something has been written, return a success here and let the
1320             // next call handle the error.
1321             break;
1322         }
1323         ptr += written;
1324     } while (ptr < end);
1325     return ptr - blocks;
1326 }
1327 
IncFs_WriteBlocks(const IncFsDataBlock blocks[],size_t blocksCount)1328 IncFsErrorCode IncFs_WriteBlocks(const IncFsDataBlock blocks[], size_t blocksCount) {
1329     incfs_fill_block incfsBlocks[128];
1330     int writtenCount = 0;
1331     int incfsBlocksUsed = 0;
1332     int lastBlockFd = -1;
1333     for (size_t i = 0; i < blocksCount; ++i) {
1334         if (lastBlockFd != blocks[i].fileFd || incfsBlocksUsed == std::size(incfsBlocks)) {
1335             auto count = writeBlocks(lastBlockFd, incfsBlocks, incfsBlocksUsed);
1336             if (count > 0) {
1337                 writtenCount += count;
1338             }
1339             if (count != incfsBlocksUsed) {
1340                 return writtenCount ? writtenCount : count;
1341             }
1342             lastBlockFd = blocks[i].fileFd;
1343             incfsBlocksUsed = 0;
1344         }
1345         incfsBlocks[incfsBlocksUsed] = incfs_fill_block{
1346                 .block_index = (uint32_t)blocks[i].pageIndex,
1347                 .data_len = blocks[i].dataSize,
1348                 .data = (uint64_t)blocks[i].data,
1349                 .compression = (uint8_t)blocks[i].compression,
1350                 .flags = uint8_t(blocks[i].kind == INCFS_BLOCK_KIND_HASH ? INCFS_BLOCK_FLAGS_HASH
1351                                                                          : 0),
1352         };
1353         ++incfsBlocksUsed;
1354     }
1355     auto count = writeBlocks(lastBlockFd, incfsBlocks, incfsBlocksUsed);
1356     if (count > 0) {
1357         writtenCount += count;
1358     }
1359     return writtenCount ? writtenCount : count;
1360 }
1361 
IncFs_BindMount(const char * sourceDir,const char * targetDir)1362 IncFsErrorCode IncFs_BindMount(const char* sourceDir, const char* targetDir) {
1363     if (!enabled()) {
1364         return -ENOTSUP;
1365     }
1366 
1367     if (path::dirName(sourceDir) == path::procfsFdDir) {
1368         // can't find such path in the mount registry, but still can verify the filesystem
1369         // via the stat() call
1370         if (!isIncFsPathImpl(sourceDir)) {
1371             return -EINVAL;
1372         }
1373     } else {
1374         auto [sourceRoot, subpath] = registry().rootAndSubpathFor(sourceDir);
1375         if (sourceRoot.empty()) {
1376             return -EINVAL;
1377         }
1378         if (subpath.empty()) {
1379             LOG(WARNING) << "[incfs] Binding the root mount '" << sourceRoot << "' is not allowed";
1380             return -EINVAL;
1381         }
1382     }
1383 
1384     if (auto err = isValidMountTarget(targetDir); err != 0) {
1385         return err;
1386     }
1387 
1388     if (::mount(sourceDir, targetDir, nullptr, MS_BIND, nullptr)) {
1389         PLOG(ERROR) << "[incfs] Failed to bind mount '" << sourceDir << "' to '" << targetDir
1390                     << '\'';
1391         return -errno;
1392     }
1393     return 0;
1394 }
1395 
IncFs_Unmount(const char * dir)1396 IncFsErrorCode IncFs_Unmount(const char* dir) {
1397     if (!enabled()) {
1398         return -ENOTSUP;
1399     }
1400     if (!isIncFsPathImpl(dir)) {
1401         LOG(WARNING) << __func__ << ": umount() called on non-incfs directory '" << dir << '\'';
1402         return -EINVAL;
1403     }
1404 
1405     errno = 0;
1406     if (::umount2(dir, MNT_FORCE) == 0 || errno == EINVAL || errno == ENOENT) {
1407         // EINVAL - not a mount point, ENOENT - doesn't exist at all
1408         return -errno;
1409     }
1410     PLOG(WARNING) << __func__ << ": umount(force) failed, detaching '" << dir << '\'';
1411     errno = 0;
1412     if (!::umount2(dir, MNT_DETACH)) {
1413         return 0;
1414     }
1415     PLOG(WARNING) << __func__ << ": umount(detach) returned non-zero for '" << dir << '\'';
1416     return 0;
1417 }
1418 
IncFs_IsIncFsFd(int fd)1419 bool IncFs_IsIncFsFd(int fd) {
1420     return isIncFsFdImpl(fd);
1421 }
1422 
IncFs_IsIncFsPath(const char * path)1423 bool IncFs_IsIncFsPath(const char* path) {
1424     return isIncFsPathImpl(path);
1425 }
1426 
IncFs_GetFilledRanges(int fd,IncFsSpan outBuffer,IncFsFilledRanges * filledRanges)1427 IncFsErrorCode IncFs_GetFilledRanges(int fd, IncFsSpan outBuffer, IncFsFilledRanges* filledRanges) {
1428     return IncFs_GetFilledRangesStartingFrom(fd, 0, outBuffer, filledRanges);
1429 }
1430 
IncFs_GetFilledRangesStartingFrom(int fd,int startBlockIndex,IncFsSpan outBuffer,IncFsFilledRanges * filledRanges)1431 IncFsErrorCode IncFs_GetFilledRangesStartingFrom(int fd, int startBlockIndex, IncFsSpan outBuffer,
1432                                                  IncFsFilledRanges* filledRanges) {
1433     if (fd < 0) {
1434         return -EBADF;
1435     }
1436     if (startBlockIndex < 0) {
1437         return -EINVAL;
1438     }
1439     if (!outBuffer.data && outBuffer.size > 0) {
1440         return -EINVAL;
1441     }
1442     if (!filledRanges) {
1443         return -EINVAL;
1444     }
1445     // Use this to optimize the incfs call and have the same buffer for both the incfs and the
1446     // public structs.
1447     static_assert(sizeof(IncFsBlockRange) == sizeof(incfs_filled_range));
1448 
1449     *filledRanges = {};
1450 
1451     auto outStart = (IncFsBlockRange*)outBuffer.data;
1452     auto outEnd = outStart + outBuffer.size / sizeof(*outStart);
1453 
1454     auto outPtr = outStart;
1455     int error = 0;
1456     int dataBlocks;
1457     incfs_get_filled_blocks_args args = {};
1458     for (;;) {
1459         auto start = args.index_out ? args.index_out : startBlockIndex;
1460         args = incfs_get_filled_blocks_args{
1461                 .range_buffer = (uint64_t)(uintptr_t)outPtr,
1462                 .range_buffer_size = uint32_t((outEnd - outPtr) * sizeof(*outPtr)),
1463                 .start_index = start,
1464         };
1465         errno = 0;
1466         auto res = ::ioctl(fd, INCFS_IOC_GET_FILLED_BLOCKS, &args);
1467         error = errno;
1468         if (res && error != EINTR && error != ERANGE) {
1469             return -error;
1470         }
1471 
1472         dataBlocks = args.data_blocks_out;
1473         outPtr += args.range_buffer_size_out / sizeof(incfs_filled_range);
1474         if (!res || error == ERANGE) {
1475             break;
1476         }
1477         // in case of EINTR we want to continue calling the function
1478     }
1479 
1480     if (outPtr > outEnd) {
1481         outPtr = outEnd;
1482         error = ERANGE;
1483     }
1484 
1485     filledRanges->endIndex = args.index_out;
1486     auto hashStartPtr = outPtr;
1487     if (outPtr != outStart) {
1488         // figure out the ranges for data block and hash blocks in the output
1489         for (; hashStartPtr != outStart; --hashStartPtr) {
1490             if ((hashStartPtr - 1)->begin < dataBlocks) {
1491                 break;
1492             }
1493         }
1494         auto lastDataPtr = hashStartPtr - 1;
1495         // here we go, this is the first block that's before or at the hashes
1496         if (lastDataPtr->end <= dataBlocks) {
1497             ; // we're good, the boundary is between the ranges - |hashStartPtr| is correct
1498         } else {
1499             // the hard part: split the |lastDataPtr| range into the data and the hash pieces
1500             if (outPtr == outEnd) {
1501                 // the buffer turned out to be too small, even though it actually wasn't
1502                 error = ERANGE;
1503                 if (hashStartPtr == outEnd) {
1504                     // this is even worse: there's no room to put even a single hash block into.
1505                     filledRanges->endIndex = lastDataPtr->end = dataBlocks;
1506                 } else {
1507                     std::copy_backward(lastDataPtr, outPtr - 1, outPtr);
1508                     lastDataPtr->end = hashStartPtr->begin = dataBlocks;
1509                     filledRanges->endIndex = (outPtr - 1)->end;
1510                 }
1511             } else {
1512                 std::copy_backward(lastDataPtr, outPtr, outPtr + 1);
1513                 lastDataPtr->end = hashStartPtr->begin = dataBlocks;
1514                 ++outPtr;
1515             }
1516         }
1517         // now fix the indices of all hash blocks - no one should know they're simply past the
1518         // regular data blocks in the file!
1519         for (auto ptr = hashStartPtr; ptr != outPtr; ++ptr) {
1520             ptr->begin -= dataBlocks;
1521             ptr->end -= dataBlocks;
1522         }
1523     }
1524 
1525     filledRanges->dataRanges = outStart;
1526     filledRanges->dataRangesCount = hashStartPtr - outStart;
1527     filledRanges->hashRanges = hashStartPtr;
1528     filledRanges->hashRangesCount = outPtr - hashStartPtr;
1529 
1530     return -error;
1531 }
1532 
isFullyLoadedV2(std::string_view root,IncFsFileId id)1533 static IncFsErrorCode isFullyLoadedV2(std::string_view root, IncFsFileId id) {
1534     if (::access(path::join(root, INCFS_INCOMPLETE_NAME, toStringImpl(id)).c_str(), F_OK)) {
1535         if (errno == ENOENT) {
1536             return 0; // no such incomplete file -> it's fully loaded.
1537         }
1538         return -errno;
1539     }
1540     return -ENODATA;
1541 }
1542 
isFullyLoadedSlow(int fd)1543 static IncFsErrorCode isFullyLoadedSlow(int fd) {
1544     char buffer[2 * sizeof(IncFsBlockRange)];
1545     IncFsFilledRanges ranges;
1546     auto res = IncFs_GetFilledRanges(fd, IncFsSpan{.data = buffer, .size = std::size(buffer)},
1547                                      &ranges);
1548     if (res == -ERANGE) {
1549         // need room for more than two ranges - definitely not fully loaded
1550         return -ENODATA;
1551     }
1552     if (res != 0) {
1553         return res;
1554     }
1555     // empty file
1556     if (ranges.endIndex == 0) {
1557         return 0;
1558     }
1559     // file with no hash tree
1560     if (ranges.dataRangesCount == 1 && ranges.hashRangesCount == 0) {
1561         return (ranges.dataRanges[0].begin == 0 && ranges.dataRanges[0].end == ranges.endIndex)
1562                 ? 0
1563                 : -ENODATA;
1564     }
1565     // file with a hash tree
1566     if (ranges.dataRangesCount == 1 && ranges.hashRangesCount == 1) {
1567         // calculate the expected data size from the size of the hash range and |endIndex|, which is
1568         // the total number of blocks in the file, both data and hash blocks together.
1569         if (ranges.hashRanges[0].begin != 0) {
1570             return -ENODATA;
1571         }
1572         const auto expectedDataBlocks =
1573                 ranges.endIndex - (ranges.hashRanges[0].end - ranges.hashRanges[0].begin);
1574         return (ranges.dataRanges[0].begin == 0 && ranges.dataRanges[0].end == expectedDataBlocks)
1575                 ? 0
1576                 : -ENODATA;
1577     }
1578     return -ENODATA;
1579 }
1580 
IncFs_IsFullyLoaded(int fd)1581 IncFsErrorCode IncFs_IsFullyLoaded(int fd) {
1582     if (features() & Features::v2) {
1583         const auto fdPath = path::fromFd(fd);
1584         if (fdPath.empty()) {
1585             return errno ? -errno : -EINVAL;
1586         }
1587         const auto id = getId(::fgetxattr, fd);
1588         if (id == kIncFsInvalidFileId) {
1589             return -errno;
1590         }
1591         return isFullyLoadedV2(registry().rootFor(fdPath), id);
1592     }
1593     return isFullyLoadedSlow(fd);
1594 }
IncFs_IsFullyLoadedByPath(const IncFsControl * control,const char * path)1595 IncFsErrorCode IncFs_IsFullyLoadedByPath(const IncFsControl* control, const char* path) {
1596     if (!control || !path) {
1597         return -EINVAL;
1598     }
1599     const auto root = rootForCmd(control->cmd);
1600     if (root.empty()) {
1601         return -EINVAL;
1602     }
1603     const auto pathRoot = registry().rootFor(path);
1604     if (pathRoot != root) {
1605         return -EINVAL;
1606     }
1607     if (features() & Features::v2) {
1608         const auto id = getId(::getxattr, path);
1609         if (id == kIncFsInvalidFileId) {
1610             return -ENOTSUP;
1611         }
1612         return isFullyLoadedV2(root, id);
1613     }
1614     auto fd = ab::unique_fd(openForSpecialOps(control->cmd, makeCommandPath(root, path).c_str()));
1615     return isFullyLoadedSlow(fd.get());
1616 }
IncFs_IsFullyLoadedById(const IncFsControl * control,IncFsFileId fileId)1617 IncFsErrorCode IncFs_IsFullyLoadedById(const IncFsControl* control, IncFsFileId fileId) {
1618     if (!control) {
1619         return -EINVAL;
1620     }
1621     const auto root = rootForCmd(control->cmd);
1622     if (root.empty()) {
1623         return -EINVAL;
1624     }
1625     if (features() & Features::v2) {
1626         return isFullyLoadedV2(root, fileId);
1627     }
1628     auto fd = ab::unique_fd(
1629             openForSpecialOps(control->cmd,
1630                               makeCommandPath(root, indexPath(root, fileId)).c_str()));
1631     return isFullyLoadedSlow(fd.get());
1632 }
1633 
isEverythingLoadedV2(const IncFsControl * control)1634 static IncFsErrorCode isEverythingLoadedV2(const IncFsControl* control) {
1635     const auto root = rootForCmd(control->cmd);
1636     if (root.empty()) {
1637         return -EINVAL;
1638     }
1639     auto res = forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [](auto) { return false; });
1640     return res < 0 ? res : res > 0 ? -ENODATA : 0;
1641 }
1642 
isEverythingLoadedSlow(const IncFsControl * control)1643 static IncFsErrorCode isEverythingLoadedSlow(const IncFsControl* control) {
1644     const auto root = rootForCmd(control->cmd);
1645     if (root.empty()) {
1646         return -EINVAL;
1647     }
1648     // No special API for this version of the driver, need to recurse and check each file
1649     // separately. Can at least speed it up by iterating over the .index/ dir and not dealing with
1650     // the directory tree.
1651     const auto indexPath = path::join(root, INCFS_INDEX_NAME);
1652     const auto dir = path::openDir(indexPath.c_str());
1653     if (!dir) {
1654         return -EINVAL;
1655     }
1656     while (const auto entry = ::readdir(dir.get())) {
1657         if (entry->d_type != DT_REG) {
1658             continue;
1659         }
1660         const auto name = path::join(indexPath, entry->d_name);
1661         auto fd =
1662                 ab::unique_fd(openForSpecialOps(control->cmd, makeCommandPath(root, name).c_str()));
1663         if (fd.get() < 0) {
1664             PLOG(WARNING) << __func__ << "(): can't open " << entry->d_name << " for special ops";
1665             return fd.release();
1666         }
1667         const auto checkFullyLoaded = IncFs_IsFullyLoaded(fd.get());
1668         if (checkFullyLoaded == 0 || checkFullyLoaded == -EOPNOTSUPP ||
1669             checkFullyLoaded == -ENOTSUP || checkFullyLoaded == -ENOENT) {
1670             // special kinds of files may return an error here, but it still means
1671             // _this_ file is OK - you simply need to check the rest. E.g. can't query
1672             // a mapped file, instead need to check its parent.
1673             continue;
1674         }
1675         return checkFullyLoaded;
1676     }
1677     return 0;
1678 }
1679 
IncFs_IsEverythingFullyLoaded(const IncFsControl * control)1680 IncFsErrorCode IncFs_IsEverythingFullyLoaded(const IncFsControl* control) {
1681     if (!control) {
1682         return -EINVAL;
1683     }
1684     if (features() & Features::v2) {
1685         return isEverythingLoadedV2(control);
1686     }
1687     return isEverythingLoadedSlow(control);
1688 }
1689 
IncFs_SetUidReadTimeouts(const IncFsControl * control,const IncFsUidReadTimeouts timeouts[],size_t count)1690 IncFsErrorCode IncFs_SetUidReadTimeouts(const IncFsControl* control,
1691                                         const IncFsUidReadTimeouts timeouts[], size_t count) {
1692     if (!control) {
1693         return -EINVAL;
1694     }
1695     if (!(features() & Features::v2)) {
1696         return -ENOTSUP;
1697     }
1698 
1699     std::vector<incfs_per_uid_read_timeouts> argTimeouts(count);
1700     for (size_t i = 0; i != count; ++i) {
1701         argTimeouts[i] = incfs_per_uid_read_timeouts{
1702                 .uid = (uint32_t)timeouts[i].uid,
1703                 .min_time_us = timeouts[i].minTimeUs,
1704                 .min_pending_time_us = timeouts[i].minPendingTimeUs,
1705                 .max_pending_time_us = timeouts[i].maxPendingTimeUs,
1706         };
1707     }
1708     incfs_set_read_timeouts_args args = {.timeouts_array = (uint64_t)(uintptr_t)argTimeouts.data(),
1709                                          .timeouts_array_size = uint32_t(
1710                                                  argTimeouts.size() * sizeof(*argTimeouts.data()))};
1711     if (::ioctl(control->cmd, INCFS_IOC_SET_READ_TIMEOUTS, &args)) {
1712         PLOG(WARNING) << "[incfs] setUidReadTimeouts failed";
1713         return -errno;
1714     }
1715     return 0;
1716 }
1717 
IncFs_GetUidReadTimeouts(const IncFsControl * control,IncFsUidReadTimeouts timeouts[],size_t * bufferSize)1718 IncFsErrorCode IncFs_GetUidReadTimeouts(const IncFsControl* control,
1719                                         IncFsUidReadTimeouts timeouts[], size_t* bufferSize) {
1720     if (!control || !bufferSize) {
1721         return -EINVAL;
1722     }
1723     if (!(features() & Features::v2)) {
1724         return -ENOTSUP;
1725     }
1726 
1727     std::vector<incfs_per_uid_read_timeouts> argTimeouts(*bufferSize);
1728     incfs_get_read_timeouts_args args = {.timeouts_array = (uint64_t)(uintptr_t)argTimeouts.data(),
1729                                          .timeouts_array_size = uint32_t(
1730                                                  argTimeouts.size() * sizeof(*argTimeouts.data())),
1731                                          .timeouts_array_size_out = args.timeouts_array_size};
1732     if (::ioctl(control->cmd, INCFS_IOC_GET_READ_TIMEOUTS, &args)) {
1733         if (errno == E2BIG) {
1734             *bufferSize = args.timeouts_array_size_out / sizeof(*argTimeouts.data());
1735         }
1736         return -errno;
1737     }
1738 
1739     *bufferSize = args.timeouts_array_size_out / sizeof(*argTimeouts.data());
1740     for (size_t i = 0; i != *bufferSize; ++i) {
1741         timeouts[i].uid = argTimeouts[i].uid;
1742         timeouts[i].minTimeUs = argTimeouts[i].min_time_us;
1743         timeouts[i].minPendingTimeUs = argTimeouts[i].min_pending_time_us;
1744         timeouts[i].maxPendingTimeUs = argTimeouts[i].max_pending_time_us;
1745     }
1746     return 0;
1747 }
1748 
1749 // Trying to detect if this is a mapped file.
1750 // Not the best way as it might return true for other system files.
1751 // TODO: remove after IncFS returns ENOTSUP for such files.
isMapped(int fd)1752 static bool isMapped(int fd) {
1753     char buffer[kIncFsFileIdStringLength];
1754     const auto res = ::fgetxattr(fd, kIdAttrName, buffer, sizeof(buffer));
1755     return res != sizeof(buffer);
1756 }
1757 
getFileBlockCount(int fd,IncFsBlockCounts * blockCount)1758 static IncFsErrorCode getFileBlockCount(int fd, IncFsBlockCounts* blockCount) {
1759     if (isMapped(fd)) {
1760         return -ENOTSUP;
1761     }
1762 
1763     incfs_get_block_count_args args = {};
1764     auto res = ::ioctl(fd, INCFS_IOC_GET_BLOCK_COUNT, &args);
1765     if (res < 0) {
1766         return -errno;
1767     }
1768     *blockCount = IncFsBlockCounts{
1769             .totalDataBlocks = args.total_data_blocks_out,
1770             .filledDataBlocks = args.filled_data_blocks_out,
1771             .totalHashBlocks = args.total_hash_blocks_out,
1772             .filledHashBlocks = args.filled_hash_blocks_out,
1773     };
1774     return 0;
1775 }
1776 
IncFs_GetFileBlockCountById(const IncFsControl * control,IncFsFileId id,IncFsBlockCounts * blockCount)1777 IncFsErrorCode IncFs_GetFileBlockCountById(const IncFsControl* control, IncFsFileId id,
1778                                            IncFsBlockCounts* blockCount) {
1779     if (!control) {
1780         return -EINVAL;
1781     }
1782     if (!(features() & Features::v2)) {
1783         return -ENOTSUP;
1784     }
1785     const auto root = rootForCmd(control->cmd);
1786     if (root.empty()) {
1787         return -EINVAL;
1788     }
1789     auto name = indexPath(root, id);
1790     auto fd = openRaw(name);
1791     if (fd < 0) {
1792         return fd.get();
1793     }
1794     return getFileBlockCount(fd, blockCount);
1795 }
1796 
IncFs_GetFileBlockCountByPath(const IncFsControl * control,const char * path,IncFsBlockCounts * blockCount)1797 IncFsErrorCode IncFs_GetFileBlockCountByPath(const IncFsControl* control, const char* path,
1798                                              IncFsBlockCounts* blockCount) {
1799     if (!control) {
1800         return -EINVAL;
1801     }
1802     if (!(features() & Features::v2)) {
1803         return -ENOTSUP;
1804     }
1805     const auto pathRoot = registry().rootFor(path);
1806     const auto root = rootForCmd(control->cmd);
1807     if (root.empty() || root != pathRoot) {
1808         return -EINVAL;
1809     }
1810     auto fd = openRaw(path);
1811     if (fd < 0) {
1812         return fd.get();
1813     }
1814     return getFileBlockCount(fd, blockCount);
1815 }
1816 
IncFs_ListIncompleteFiles(const IncFsControl * control,IncFsFileId ids[],size_t * bufferSize)1817 IncFsErrorCode IncFs_ListIncompleteFiles(const IncFsControl* control, IncFsFileId ids[],
1818                                          size_t* bufferSize) {
1819     if (!control || !bufferSize) {
1820         return -EINVAL;
1821     }
1822     if (!(features() & Features::v2)) {
1823         return -ENOTSUP;
1824     }
1825     const auto root = rootForCmd(control->cmd);
1826     if (root.empty()) {
1827         return -EINVAL;
1828     }
1829     size_t index = 0;
1830     int error = 0;
1831     const auto res = forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [&](const char* name) {
1832         if (index >= *bufferSize) {
1833             error = -E2BIG;
1834         } else {
1835             ids[index] = IncFs_FileIdFromString(name);
1836         }
1837         ++index;
1838         return true;
1839     });
1840     if (res < 0) {
1841         return res;
1842     }
1843     *bufferSize = index;
1844     return error ? error : 0;
1845 }
1846 
IncFs_ForEachFile(const IncFsControl * control,void * context,FileCallback cb)1847 IncFsErrorCode IncFs_ForEachFile(const IncFsControl* control, void* context, FileCallback cb) {
1848     if (!control || !cb) {
1849         return -EINVAL;
1850     }
1851     const auto root = rootForCmd(control->cmd);
1852     if (root.empty()) {
1853         return -EINVAL;
1854     }
1855     return forEachFileIn(path::join(root, INCFS_INDEX_NAME), [&](const char* name) {
1856         return cb(context, control, IncFs_FileIdFromString(name));
1857     });
1858 }
1859 
IncFs_ForEachIncompleteFile(const IncFsControl * control,void * context,FileCallback cb)1860 IncFsErrorCode IncFs_ForEachIncompleteFile(const IncFsControl* control, void* context,
1861                                            FileCallback cb) {
1862     if (!control || !cb) {
1863         return -EINVAL;
1864     }
1865     if (!(features() & Features::v2)) {
1866         return -ENOTSUP;
1867     }
1868     const auto root = rootForCmd(control->cmd);
1869     if (root.empty()) {
1870         return -EINVAL;
1871     }
1872     return forEachFileIn(path::join(root, INCFS_INCOMPLETE_NAME), [&](const char* name) {
1873         return cb(context, control, IncFs_FileIdFromString(name));
1874     });
1875 }
1876 
IncFs_WaitForLoadingComplete(const IncFsControl * control,int32_t timeoutMs)1877 IncFsErrorCode IncFs_WaitForLoadingComplete(const IncFsControl* control, int32_t timeoutMs) {
1878     if (!control) {
1879         return -EINVAL;
1880     }
1881     if (!(features() & Features::v2)) {
1882         return -ENOTSUP;
1883     }
1884 
1885     using namespace std::chrono;
1886     auto hrTimeout = steady_clock::duration(milliseconds(timeoutMs));
1887 
1888     const auto root = rootForCmd(control->cmd);
1889     if (root.empty()) {
1890         return -EINVAL;
1891     }
1892 
1893     ab::unique_fd fd(inotify_init1(IN_NONBLOCK | IN_CLOEXEC));
1894     if (!fd.ok()) {
1895         return -EFAULT;
1896     }
1897 
1898     // first create all the watches, and only then list existing files to prevent races
1899     auto dirPath = path::join(root, INCFS_INCOMPLETE_NAME);
1900     int watchFd = inotify_add_watch(fd.get(), dirPath.c_str(), IN_DELETE);
1901     if (watchFd < 0) {
1902         return -errno;
1903     }
1904 
1905     size_t count = 0;
1906     auto res = IncFs_ListIncompleteFiles(control, nullptr, &count);
1907     if (!res) {
1908         return 0;
1909     }
1910     if (res != -E2BIG) {
1911         return res;
1912     }
1913 
1914     while (hrTimeout > hrTimeout.zero()) {
1915         const auto startTs = steady_clock::now();
1916 
1917         pollfd pfd = {fd.get(), POLLIN, 0};
1918         const auto res = ::poll(&pfd, 1, duration_cast<milliseconds>(hrTimeout).count());
1919         if (res == 0) {
1920             return -ETIMEDOUT;
1921         }
1922         if (res < 0) {
1923             const auto error = errno;
1924             if (error != EINTR) {
1925                 PLOG(ERROR) << "poll() failed";
1926                 return -error;
1927             }
1928         } else {
1929             // empty the inotify fd first to not miss any new deletions,
1930             // then check if the directory is empty.
1931             char buffer[sizeof(inotify_event) + NAME_MAX + 1];
1932             for (;;) {
1933                 auto err = TEMP_FAILURE_RETRY(::read(fd.get(), buffer, sizeof(buffer)));
1934                 if (err < 0) {
1935                     if (errno == EAGAIN) { // no new events
1936                         break;
1937                     }
1938                     return -errno;
1939                 }
1940             }
1941 
1942             size_t count = 0;
1943             auto res = IncFs_ListIncompleteFiles(control, nullptr, &count);
1944             if (!res) {
1945                 return 0;
1946             }
1947             if (res != -E2BIG) {
1948                 return res;
1949             }
1950         }
1951         hrTimeout -= steady_clock::now() - startTs;
1952     }
1953 
1954     return -ETIMEDOUT;
1955 }
1956 
IncFs_WaitForFsWrittenBlocksChange(const IncFsControl * control,int32_t timeoutMs,IncFsSize * count)1957 IncFsErrorCode IncFs_WaitForFsWrittenBlocksChange(const IncFsControl* control, int32_t timeoutMs,
1958                                                   IncFsSize* count) {
1959     if (!control || !count) {
1960         return -EINVAL;
1961     }
1962     if (!(features() & Features::v2)) {
1963         return -ENOTSUP;
1964     }
1965 
1966     using namespace std::chrono;
1967     auto hrTimeout = steady_clock::duration(milliseconds(timeoutMs));
1968 
1969     while (hrTimeout > hrTimeout.zero()) {
1970         const auto startTs = steady_clock::now();
1971 
1972         pollfd pfd = {control->blocksWritten, POLLIN, 0};
1973         const auto res = ::poll(&pfd, 1, duration_cast<milliseconds>(hrTimeout).count());
1974         if (res > 0) {
1975             break;
1976         }
1977         if (res == 0) {
1978             return -ETIMEDOUT;
1979         }
1980         const auto error = errno;
1981         if (error != EINTR) {
1982             PLOG(ERROR) << "poll() failed";
1983             return -error;
1984         }
1985         hrTimeout -= steady_clock::now() - startTs;
1986     }
1987 
1988     char str[32];
1989     auto size = ::read(control->blocksWritten, str, sizeof(str));
1990     if (size < 0) {
1991         const auto error = errno;
1992         PLOG(ERROR) << "read() failed";
1993         return -error;
1994     }
1995     const auto res = std::from_chars(str, str + size, *count);
1996     if (res.ec != std::errc{}) {
1997         return res.ec == std::errc::invalid_argument ? -EINVAL : -ERANGE;
1998     }
1999 
2000     return 0;
2001 }
2002 
reserveSpace(const char * backingPath,IncFsSize size)2003 static IncFsErrorCode reserveSpace(const char* backingPath, IncFsSize size) {
2004     auto fd = ab::unique_fd(::open(backingPath, O_WRONLY | O_CLOEXEC));
2005     if (fd < 0) {
2006         return -errno;
2007     }
2008     struct stat st = {};
2009     if (::fstat(fd.get(), &st)) {
2010         return -errno;
2011     }
2012     if (size == kIncFsTrimReservedSpace) {
2013         if (::ftruncate(fd.get(), st.st_size)) {
2014             return -errno;
2015         }
2016     } else {
2017         // Add 1.5% of the size for the hash tree and the blockmap, and some more blocks
2018         // for fixed overhead.
2019         // hash tree is ~33 bytes / page, and blockmap is 10 bytes / page
2020         // no need to round to a page size as filesystems already do that.
2021         const auto backingSize = IncFsSize(size * 1.015) + INCFS_DATA_FILE_BLOCK_SIZE * 4;
2022         if (backingSize < st.st_size) {
2023             return -EPERM;
2024         }
2025         if (::fallocate(fd.get(), FALLOC_FL_KEEP_SIZE, 0, backingSize)) {
2026             return -errno;
2027         }
2028     }
2029     return 0;
2030 }
2031 
IncFs_ReserveSpaceByPath(const IncFsControl * control,const char * path,IncFsSize size)2032 IncFsErrorCode IncFs_ReserveSpaceByPath(const IncFsControl* control, const char* path,
2033                                         IncFsSize size) {
2034     if (!control || (size != kIncFsTrimReservedSpace && size < 0)) {
2035         return -EINVAL;
2036     }
2037     const auto [pathRoot, backingRoot, subpath] = registry().detailsFor(path);
2038     const auto root = rootForCmd(control->cmd);
2039     if (root.empty() || root != pathRoot) {
2040         return -EINVAL;
2041     }
2042     return reserveSpace(path::join(backingRoot, subpath).c_str(), size);
2043 }
2044 
IncFs_ReserveSpaceById(const IncFsControl * control,IncFsFileId id,IncFsSize size)2045 IncFsErrorCode IncFs_ReserveSpaceById(const IncFsControl* control, IncFsFileId id, IncFsSize size) {
2046     if (!control || (size != kIncFsTrimReservedSpace && size < 0)) {
2047         return -EINVAL;
2048     }
2049     const auto root = rootForCmd(control->cmd);
2050     if (root.empty()) {
2051         return -EINVAL;
2052     }
2053     auto path = indexPath(root, id);
2054     const auto [pathRoot, backingRoot, subpath] = registry().detailsFor(path);
2055     if (root != pathRoot) {
2056         return -EINVAL;
2057     }
2058     return reserveSpace(path::join(backingRoot, subpath).c_str(), size);
2059 }
2060 
2061 template <class IntType>
readIntFromFile(std::string_view rootDir,std::string_view subPath,IntType & result)2062 static int readIntFromFile(std::string_view rootDir, std::string_view subPath, IntType& result) {
2063     std::string content;
2064     if (!ab::ReadFileToString(path::join(rootDir, subPath), &content)) {
2065         PLOG(ERROR) << "IncFs_GetMetrics: failed to read file: " << rootDir << "/" << subPath;
2066         return -errno;
2067     }
2068     const auto res = std::from_chars(content.data(), content.data() + content.size(), result);
2069     if (res.ec != std::errc()) {
2070         return -static_cast<int>(res.ec);
2071     }
2072     return 0;
2073 }
2074 
IncFs_GetMetrics(const char * sysfsName,IncFsMetrics * metrics)2075 IncFsErrorCode IncFs_GetMetrics(const char* sysfsName, IncFsMetrics* metrics) {
2076     if (!sysfsName || !*sysfsName) {
2077         return -EINVAL;
2078     }
2079 
2080     const auto kSysfsMetricsDir =
2081             ab::StringPrintf("/sys/fs/%s/instances/%s", INCFS_NAME, sysfsName);
2082 
2083     int err;
2084     if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_min", metrics->readsDelayedMin);
2085         err != 0) {
2086         return err;
2087     }
2088     if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_min_us", metrics->readsDelayedMinUs);
2089         err != 0) {
2090         return err;
2091     }
2092     if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_pending",
2093                               metrics->readsDelayedPending);
2094         err != 0) {
2095         return err;
2096     }
2097     if (err = readIntFromFile(kSysfsMetricsDir, "reads_delayed_pending_us",
2098                               metrics->readsDelayedPendingUs);
2099         err != 0) {
2100         return err;
2101     }
2102     if (err = readIntFromFile(kSysfsMetricsDir, "reads_failed_hash_verification",
2103                               metrics->readsFailedHashVerification);
2104         err != 0) {
2105         return err;
2106     }
2107     if (err = readIntFromFile(kSysfsMetricsDir, "reads_failed_other", metrics->readsFailedOther);
2108         err != 0) {
2109         return err;
2110     }
2111     if (err = readIntFromFile(kSysfsMetricsDir, "reads_failed_timed_out",
2112                               metrics->readsFailedTimedOut);
2113         err != 0) {
2114         return err;
2115     }
2116     return 0;
2117 }
2118 
IncFs_GetLastReadError(const IncFsControl * control,IncFsLastReadError * lastReadError)2119 IncFsErrorCode IncFs_GetLastReadError(const IncFsControl* control,
2120                                       IncFsLastReadError* lastReadError) {
2121     if (!control) {
2122         return -EINVAL;
2123     }
2124     if (!(features() & Features::v2)) {
2125         return -ENOTSUP;
2126     }
2127     incfs_get_last_read_error_args args = {};
2128     auto res = ::ioctl(control->cmd, INCFS_IOC_GET_LAST_READ_ERROR, &args);
2129     if (res < 0) {
2130         PLOG(ERROR) << "[incfs] IncFs_GetLastReadError failed.";
2131         return -errno;
2132     }
2133     *lastReadError = IncFsLastReadError{
2134             .timestampUs = args.time_us_out,
2135             .block = static_cast<IncFsBlockIndex>(args.page_out),
2136             .errorNo = args.errno_out,
2137             .uid = static_cast<IncFsUid>(args.uid_out),
2138     };
2139     static_assert(sizeof(args.file_id_out.bytes) == sizeof(lastReadError->id.data));
2140     memcpy(lastReadError->id.data, args.file_id_out.bytes, sizeof(args.file_id_out.bytes));
2141     return 0;
2142 }
2143 
defaultMountRegistry()2144 MountRegistry& android::incfs::defaultMountRegistry() {
2145     return registry();
2146 }
2147