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