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