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