You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
488 lines
16 KiB
488 lines
16 KiB
/*
|
|
* 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 <errno.h>
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
#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<std::string> 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 <class ReadInfoStruct, class Impl>
|
|
WaitResult waitForReads(const Control& control, std::chrono::milliseconds timeout,
|
|
std::vector<ReadInfoStruct>* 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<ReadInfo>* 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<ReadInfoWithUid>* 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<ReadInfo>* 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<ReadInfoWithUid>* 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<const DataBlock> blocks) {
|
|
return IncFs_WriteBlocks(blocks.data(), blocks.size());
|
|
}
|
|
|
|
inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd) {
|
|
return getFilledRanges(fd, FilledRanges());
|
|
}
|
|
|
|
inline std::pair<ErrorCode, FilledRanges> getFilledRanges(int fd,
|
|
FilledRanges::RangeBuffer&& buffer) {
|
|
return getFilledRanges(fd, FilledRanges(std::move(buffer), {}));
|
|
}
|
|
|
|
inline std::pair<ErrorCode, FilledRanges> 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<size_t>(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<std::vector<FileId>> listIncompleteFiles(const Control& control) {
|
|
std::vector<FileId> 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 <class Callback>
|
|
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 <class Callback>
|
|
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<BlockCounts> 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<BlockCounts> 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<const UidReadTimeouts> timeouts) {
|
|
return IncFs_SetUidReadTimeouts(control, timeouts.data(), timeouts.size());
|
|
}
|
|
|
|
inline std::optional<std::vector<UidReadTimeouts>> getUidReadTimeouts(const Control& control) {
|
|
std::vector<UidReadTimeouts> 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<Metrics> 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<LastReadError> 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;
|
|
}
|