Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 65fc38a0 authored by Yurii Zubrytskyi's avatar Yurii Zubrytskyi
Browse files

[incfs] Preallocate space for IncFS files

Use the new libincfs APIs to preallocate space for all files
created via the public makeFile() API. This way we ensure
the device won't run out of space much later

Bug: 182185202
Test: atest libincfs-test PackageManagerShellCommandTest \
 PackageManagerShellCommandIncrementalTest \
 IncrementalServiceTest
Change-Id: I70af97949b29ff5db63201b0e3487fe026e23160
parent d96c8193
Loading
Loading
Loading
Loading
+43 −11
Original line number Diff line number Diff line
@@ -96,6 +96,10 @@ static const Constants& constants() {
    return c;
}

static bool isPageAligned(IncFsSize s) {
    return (s & (Constants::blockSize - 1)) == 0;
}

template <base::LogSeverity level = base::ERROR>
bool mkdirOrLog(std::string_view name, int mode = 0770, bool allowExisting = true) {
    auto cstr = path::c_str(name);
@@ -1001,25 +1005,53 @@ std::string IncrementalService::normalizePathToStorage(const IncFsMount& ifs, St

int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
                                 incfs::NewFileParams params, std::span<const uint8_t> data) {
    if (auto ifs = getIfs(storage)) {
        std::string normPath = normalizePathToStorage(*ifs, storage, path);
    const auto ifs = getIfs(storage);
    if (!ifs) {
        return -EINVAL;
    }
    if (data.size() > params.size) {
        LOG(ERROR) << "Bad data size - bigger than file size";
        return -EINVAL;
    }
    if (!data.empty() && data.size() != params.size) {
        // Writing a page is an irreversible operation, and it can't be updated with additional
        // data later. Check that the last written page is complete, or we may break the file.
        if (!isPageAligned(data.size())) {
            LOG(ERROR) << "Bad data size - tried to write half a page?";
            return -EINVAL;
        }
    }
    const std::string normPath = normalizePathToStorage(*ifs, storage, path);
    if (normPath.empty()) {
            LOG(ERROR) << "Internal error: storageId " << storage
                       << " failed to normalize: " << path;
        LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path;
        return -EINVAL;
    }
    if (auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); err) {
        LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
        return err;
    }
    if (params.size > 0) {
        // Only v2+ incfs supports automatically trimming file over-reserved sizes
        if (mIncFs->features() & incfs::Features::v2) {
            if (auto err = mIncFs->reserveSpace(ifs->control, normPath, params.size)) {
                if (err != -EOPNOTSUPP) {
                    LOG(ERROR) << "Failed to reserve space for a new file: " << err;
                    (void)mIncFs->unlink(ifs->control, normPath);
                    return err;
                } else {
                    LOG(WARNING) << "Reserving space for backing file isn't supported, "
                                    "may run out of disk later";
                }
            }
        }
        if (!data.empty()) {
            if (auto err = setFileContent(ifs, id, path, data); err) {
                (void)mIncFs->unlink(ifs->control, normPath);
                return err;
            }
        }
        return 0;
    }
    return -EINVAL;
    return 0;
}

int IncrementalService::makeDir(StorageId storageId, std::string_view path, int mode) {
@@ -1708,7 +1740,7 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
        }

        const auto entryUncompressed = entry.method == kCompressStored;
        const auto entryPageAligned = (entry.offset & (constants().blockSize - 1)) == 0;
        const auto entryPageAligned = isPageAligned(entry.offset);

        if (!extractNativeLibs) {
            // ensure the file is properly aligned and unpacked
+4 −0
Original line number Diff line number Diff line
@@ -209,6 +209,10 @@ public:
    ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
        return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
    }
    ErrorCode reserveSpace(const Control& control, std::string_view path,
                           IncFsSize size) const final {
        return incfs::reserveSpace(control, path, size);
    }
    WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
                                   std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
        return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
+2 −0
Original line number Diff line number Diff line
@@ -107,6 +107,8 @@ public:
    virtual ErrorCode unlink(const Control& control, std::string_view path) const = 0;
    virtual UniqueFd openForSpecialOps(const Control& control, FileId id) const = 0;
    virtual ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const = 0;
    virtual ErrorCode reserveSpace(const Control& control, std::string_view path,
                                   IncFsSize size) const = 0;
    virtual WaitResult waitForPendingReads(
            const Control& control, std::chrono::milliseconds timeout,
            std::vector<incfs::ReadInfo>* pendingReadsBuffer) const = 0;
+6 −1
Original line number Diff line number Diff line
@@ -372,6 +372,8 @@ public:
    MOCK_CONST_METHOD2(unlink, ErrorCode(const Control& control, std::string_view path));
    MOCK_CONST_METHOD2(openForSpecialOps, UniqueFd(const Control& control, FileId id));
    MOCK_CONST_METHOD1(writeBlocks, ErrorCode(std::span<const DataBlock> blocks));
    MOCK_CONST_METHOD3(reserveSpace,
                       ErrorCode(const Control& control, std::string_view path, IncFsSize size));
    MOCK_CONST_METHOD3(waitForPendingReads,
                       WaitResult(const Control& control, std::chrono::milliseconds timeout,
                                  std::vector<incfs::ReadInfo>* pendingReadsBuffer));
@@ -379,7 +381,10 @@ public:
                       ErrorCode(const Control& control,
                                 const std::vector<PerUidReadTimeouts>& perUidReadTimeouts));

    MockIncFs() { ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return()); }
    MockIncFs() {
        ON_CALL(*this, listExistingMounts(_)).WillByDefault(Return());
        ON_CALL(*this, reserveSpace(_, _, _)).WillByDefault(Return(0));
    }

    void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
    void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }