/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include "incfs.h" namespace android::incfs { constexpr char kIdAttrName[] = INCFS_XATTR_ID_NAME; constexpr char kSizeAttrName[] = INCFS_XATTR_SIZE_NAME; constexpr char kMetadataAttrName[] = INCFS_XATTR_METADATA_NAME; namespace details { class CStrWrapper { public: CStrWrapper(std::string_view sv) { if (!sv.data()) { mCstr = ""; } else if (sv[sv.size()] == '\0') { mCstr = sv.data(); } else { mCopy.emplace(sv); mCstr = mCopy->c_str(); } } CStrWrapper(const CStrWrapper&) = delete; void operator=(const CStrWrapper&) = delete; CStrWrapper(CStrWrapper&&) = delete; void operator=(CStrWrapper&&) = delete; const char* get() const { return mCstr; } operator const char*() const { return get(); } private: const char* mCstr; std::optional mCopy; }; inline CStrWrapper c_str(std::string_view sv) { return {sv}; } } // namespace details inline bool enabled() { return IncFs_IsEnabled(); } inline Features features() { return Features(IncFs_Features()); } inline bool isIncFsFd(int fd) { return IncFs_IsIncFsFd(fd); } inline bool isIncFsPath(std::string_view path) { return IncFs_IsIncFsPath(details::c_str(path)); } inline bool isValidFileId(FileId fileId) { return IncFs_IsValidFileId(fileId); } inline std::string toString(FileId fileId) { std::string res(kIncFsFileIdStringLength, '\0'); auto err = IncFs_FileIdToString(fileId, res.data()); if (err) { errno = err; return {}; } return res; } inline IncFsFileId toFileId(std::string_view str) { if (str.size() != kIncFsFileIdStringLength) { return kIncFsInvalidFileId; } return IncFs_FileIdFromString(str.data()); } inline void UniqueControl::close() { IncFs_DeleteControl(mControl); mControl = nullptr; } inline IncFsFd UniqueControl::cmd() const { return IncFs_GetControlFd(mControl, CMD); } inline IncFsFd UniqueControl::pendingReads() const { return IncFs_GetControlFd(mControl, PENDING_READS); } inline IncFsFd UniqueControl::logs() const { return IncFs_GetControlFd(mControl, LOGS); } inline IncFsFd UniqueControl::blocksWritten() const { return IncFs_GetControlFd(mControl, BLOCKS_WRITTEN); } inline UniqueControl::Fds UniqueControl::releaseFds() { Fds result; IncFsFd fds[result.size()]; auto count = IncFs_ReleaseControlFds(mControl, fds, std::size(fds)); for (auto i = 0; i < count; ++i) { result[i] = UniqueFd(fds[i]); } return result; } inline UniqueControl mount(std::string_view backingPath, std::string_view targetDir, MountOptions options) { auto control = IncFs_Mount(details::c_str(backingPath), details::c_str(targetDir), options); return UniqueControl(control); } inline UniqueControl open(std::string_view dir) { auto control = IncFs_Open(details::c_str(dir)); return UniqueControl(control); } inline UniqueControl createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs, IncFsFd blocksWritten) { return UniqueControl(IncFs_CreateControl(cmd, pendingReads, logs, blocksWritten)); } inline ErrorCode setOptions(const Control& control, MountOptions newOptions) { return IncFs_SetOptions(control, newOptions); } inline ErrorCode bindMount(std::string_view sourceDir, std::string_view targetDir) { return IncFs_BindMount(details::c_str(sourceDir), details::c_str(targetDir)); } inline ErrorCode unmount(std::string_view dir) { return IncFs_Unmount(details::c_str(dir)); } inline std::string root(const Control& control) { std::string result; result.resize(PATH_MAX); size_t size = result.size(); if (auto err = IncFs_Root(control, result.data(), &size); err < 0) { errno = -err; return {}; } result.resize(size); return result; } inline ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId fileId, NewFileParams params) { return IncFs_MakeFile(control, details::c_str(path), mode, fileId, params); } inline ErrorCode makeMappedFile(const Control& control, std::string_view path, int mode, NewMappedFileParams params) { return IncFs_MakeMappedFile(control, details::c_str(path), mode, params); } inline ErrorCode makeDir(const Control& control, std::string_view path, int mode) { return IncFs_MakeDir(control, details::c_str(path), mode); } inline ErrorCode makeDirs(const Control& control, std::string_view path, int mode) { return IncFs_MakeDirs(control, details::c_str(path), mode); } inline RawMetadata getMetadata(const Control& control, FileId fileId) { RawMetadata metadata(INCFS_MAX_FILE_ATTR_SIZE); size_t size = metadata.size(); if (IncFs_GetMetadataById(control, fileId, metadata.data(), &size) < 0) { return {}; } metadata.resize(size); return metadata; } inline RawMetadata getMetadata(const Control& control, std::string_view path) { RawMetadata metadata(INCFS_MAX_FILE_ATTR_SIZE); size_t size = metadata.size(); if (IncFs_GetMetadataByPath(control, details::c_str(path), metadata.data(), &size) < 0) { return {}; } metadata.resize(size); return metadata; } inline RawSignature getSignature(const Control& control, FileId fileId) { RawSignature signature(INCFS_MAX_SIGNATURE_SIZE); size_t size = signature.size(); if (IncFs_GetSignatureById(control, fileId, signature.data(), &size) < 0) { return {}; } signature.resize(size); return signature; } inline RawSignature getSignature(const Control& control, std::string_view path) { RawSignature signature(INCFS_MAX_SIGNATURE_SIZE); size_t size = signature.size(); if (IncFs_GetSignatureByPath(control, details::c_str(path), signature.data(), &size) < 0) { return {}; } signature.resize(size); return signature; } inline FileId getFileId(const Control& control, std::string_view path) { return IncFs_GetId(control, details::c_str(path)); } inline ErrorCode link(const Control& control, std::string_view sourcePath, std::string_view targetPath) { return IncFs_Link(control, details::c_str(sourcePath), details::c_str(targetPath)); } inline ErrorCode unlink(const Control& control, std::string_view path) { return IncFs_Unlink(control, details::c_str(path)); } template WaitResult waitForReads(const Control& control, std::chrono::milliseconds timeout, std::vector* pendingReadsBuffer, size_t defaultBufferSize, Impl impl) { if (pendingReadsBuffer->empty()) { pendingReadsBuffer->resize(defaultBufferSize); } size_t size = pendingReadsBuffer->size(); IncFsErrorCode err = impl(control, timeout.count(), pendingReadsBuffer->data(), &size); pendingReadsBuffer->resize(size); switch (err) { case 0: return WaitResult::HaveData; case -ETIMEDOUT: return WaitResult::Timeout; } return WaitResult(err); } inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout, std::vector* pendingReadsBuffer) { return waitForReads(control, timeout, pendingReadsBuffer, INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE, IncFs_WaitForPendingReads); } inline WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout, std::vector* pendingReadsBuffer) { return waitForReads(control, timeout, pendingReadsBuffer, INCFS_DEFAULT_PENDING_READ_BUFFER_SIZE, IncFs_WaitForPendingReadsWithUid); } inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout, std::vector* pageReadsBuffer) { static constexpr auto kDefaultBufferSize = INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfo); return waitForReads(control, timeout, pageReadsBuffer, kDefaultBufferSize, IncFs_WaitForPageReads); } inline WaitResult waitForPageReads(const Control& control, std::chrono::milliseconds timeout, std::vector* pageReadsBuffer) { static constexpr auto kDefaultBufferSize = INCFS_DEFAULT_PAGE_READ_BUFFER_PAGES * PAGE_SIZE / sizeof(ReadInfoWithUid); return waitForReads(control, timeout, pageReadsBuffer, kDefaultBufferSize, IncFs_WaitForPageReadsWithUid); } inline UniqueFd openForSpecialOps(const Control& control, FileId fileId) { return UniqueFd(IncFs_OpenForSpecialOpsById(control, fileId)); } inline UniqueFd openForSpecialOps(const Control& control, std::string_view path) { return UniqueFd(IncFs_OpenForSpecialOpsByPath(control, details::c_str(path))); } inline ErrorCode writeBlocks(Span blocks) { return IncFs_WriteBlocks(blocks.data(), blocks.size()); } inline std::pair getFilledRanges(int fd) { return getFilledRanges(fd, FilledRanges()); } inline std::pair getFilledRanges(int fd, FilledRanges::RangeBuffer&& buffer) { return getFilledRanges(fd, FilledRanges(std::move(buffer), {})); } inline std::pair getFilledRanges(int fd, FilledRanges&& resumeFrom) { auto rawRanges = resumeFrom.internalRawRanges(); auto buffer = resumeFrom.extractInternalBufferAndClear(); auto totalRanges = resumeFrom.dataRanges().size() + resumeFrom.hashRanges().size(); auto remainingSpace = buffer.size() - totalRanges; const bool loadAll = remainingSpace == 0; int res; do { if (remainingSpace == 0) { remainingSpace = std::max(32, buffer.size() / 2); buffer.resize(buffer.size() + remainingSpace); } auto outBuffer = IncFsSpan{(const char*)(buffer.data() + rawRanges.dataRangesCount + rawRanges.hashRangesCount), IncFsSize(remainingSpace * sizeof(buffer[0]))}; IncFsFilledRanges newRanges; res = IncFs_GetFilledRangesStartingFrom(fd, rawRanges.endIndex, outBuffer, &newRanges); if (res && res != -ERANGE) { return {res, FilledRanges(std::move(buffer), {})}; } rawRanges.dataRangesCount += newRanges.dataRangesCount; rawRanges.hashRangesCount += newRanges.hashRangesCount; rawRanges.endIndex = newRanges.endIndex; remainingSpace = buffer.size() - rawRanges.dataRangesCount - rawRanges.hashRangesCount; } while (res && loadAll); rawRanges.dataRanges = buffer.data(); rawRanges.hashRanges = buffer.data() + rawRanges.dataRangesCount; return {res, FilledRanges(std::move(buffer), rawRanges)}; } inline LoadingState toLoadingState(IncFsErrorCode res) { switch (res) { case 0: return LoadingState::Full; case -ENODATA: return LoadingState::MissingBlocks; default: return LoadingState(res); } } inline LoadingState isFullyLoaded(int fd) { return toLoadingState(IncFs_IsFullyLoaded(fd)); } inline LoadingState isFullyLoaded(const Control& control, std::string_view path) { return toLoadingState(IncFs_IsFullyLoadedByPath(control, details::c_str(path))); } inline LoadingState isFullyLoaded(const Control& control, FileId fileId) { return toLoadingState(IncFs_IsFullyLoadedById(control, fileId)); } inline LoadingState isEverythingFullyLoaded(const Control& control) { return toLoadingState(IncFs_IsEverythingFullyLoaded(control)); } inline std::optional> listIncompleteFiles(const Control& control) { std::vector ids(32); size_t count = ids.size(); auto err = IncFs_ListIncompleteFiles(control, ids.data(), &count); if (err == -E2BIG) { ids.resize(count); err = IncFs_ListIncompleteFiles(control, ids.data(), &count); } if (err) { errno = -err; return {}; } ids.resize(count); return std::move(ids); } template inline ErrorCode forEachFile(const Control& control, Callback&& cb) { struct Context { const Control& c; const Callback& cb; } context = {control, cb}; return IncFs_ForEachFile(control, &context, [](void* pcontext, const IncFsControl*, FileId id) { const auto context = (Context*)pcontext; return context->cb(context->c, id); }); } template inline ErrorCode forEachIncompleteFile(const Control& control, Callback&& cb) { struct Context { const Control& c; const Callback& cb; } context = {control, cb}; return IncFs_ForEachIncompleteFile(control, &context, [](void* pcontext, const IncFsControl*, FileId id) { const auto context = (Context*)pcontext; return context->cb(context->c, id); }); } inline WaitResult waitForLoadingComplete(const Control& control, std::chrono::milliseconds timeout) { const auto res = IncFs_WaitForLoadingComplete(control, timeout.count()); switch (res) { case 0: return WaitResult::HaveData; case -ETIMEDOUT: return WaitResult::Timeout; default: return WaitResult(res); } } inline std::optional getBlockCount(const Control& control, FileId fileId) { BlockCounts counts; auto res = IncFs_GetFileBlockCountById(control, fileId, &counts); if (res) { errno = -res; return {}; } return counts; } inline std::optional getBlockCount(const Control& control, std::string_view path) { BlockCounts counts; auto res = IncFs_GetFileBlockCountByPath(control, details::c_str(path), &counts); if (res) { errno = -res; return {}; } return counts; } inline ErrorCode setUidReadTimeouts(const Control& control, Span timeouts) { return IncFs_SetUidReadTimeouts(control, timeouts.data(), timeouts.size()); } inline std::optional> getUidReadTimeouts(const Control& control) { std::vector timeouts(32); size_t count = timeouts.size(); auto res = IncFs_GetUidReadTimeouts(control, timeouts.data(), &count); if (res == -E2BIG) { timeouts.resize(count); res = IncFs_GetUidReadTimeouts(control, timeouts.data(), &count); } if (res) { errno = -res; return {}; } timeouts.resize(count); return std::move(timeouts); } inline ErrorCode reserveSpace(const Control& control, std::string_view path, Size size) { return IncFs_ReserveSpaceByPath(control, details::c_str(path), size); } inline ErrorCode reserveSpace(const Control& control, FileId id, Size size) { return IncFs_ReserveSpaceById(control, id, size); } inline std::optional getMetrics(std::string_view sysfsName) { Metrics metrics; if (const auto res = IncFs_GetMetrics(details::c_str(sysfsName), &metrics); res < 0) { errno = -res; return {}; } return metrics; } inline std::optional getLastReadError(const Control& control) { LastReadError lastReadError; if (const auto res = IncFs_GetLastReadError(control, &lastReadError); res < 0) { errno = -res; return {}; } return lastReadError; } } // namespace android::incfs inline bool operator==(const IncFsFileId& l, const IncFsFileId& r) { return memcmp(&l, &r, sizeof(l)) == 0; }