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