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

Commit 7e8dab77 authored by Thurston Dang's avatar Thurston Dang
Browse files

storageproxyd: discard writes when checkpointing, if necessary

If a checkpointing operation is in progress, discard any write operations
that are flagged as STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT. In tandem
with trusty-side changes that set the flag appropriately, this avoids
the awkward case where the checkpoint is rolled back, which potentially
leads to inconsistency between the data and the superblock.

Based on Stephen's CL/1845477 "Add helper to check checkpoint state of
mounts".

Test: m storageproxyd
Bug: 194313068
Change-Id: Ib6a432db1bc1b034f803b743b0d7322e3f31d814
parent 11329772
Loading
Loading
Loading
Loading
+22 −18
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ enum storage_file_open_flag {
 * @STORAGE_MSG_FLAG_BATCH:                 if set, command belongs to a batch transaction.
 *                                          No response will be sent by the server until
 *                                          it receives a command with this flag unset, at
 *                                      which point a cummulative result for all messages
 *                                          which point a cumulative result for all messages
 *                                          sent with STORAGE_MSG_FLAG_BATCH will be sent.
 *                                          This is only supported by the non-secure disk proxy
 *                                          server.
@@ -126,12 +126,16 @@ enum storage_file_open_flag {
 * @STORAGE_MSG_FLAG_TRANSACT_COMPLETE:     if set, indicates that server need to commit
 *                                          current transaction after processing this message.
 *                                          It is an alias for STORAGE_MSG_FLAG_POST_COMMIT.
 * @STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT: if set, indicates that server needs to ensure
 *                                          that there is not a pending checkpoint for
 *                                          userdata before processing this message.
 */
enum storage_msg_flag {
    STORAGE_MSG_FLAG_BATCH = 0x1,
    STORAGE_MSG_FLAG_PRE_COMMIT = 0x2,
    STORAGE_MSG_FLAG_POST_COMMIT = 0x4,
    STORAGE_MSG_FLAG_TRANSACT_COMPLETE = STORAGE_MSG_FLAG_POST_COMMIT,
    STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT = 0x8,
};

/*
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ cc_binary {
    vendor: true,

    srcs: [
        "checkpoint_handling.cpp",
        "ipc.c",
        "rpmb.c",
        "storage.c",
@@ -30,12 +31,14 @@ cc_binary {
    ],

    shared_libs: [
        "libbase",
        "liblog",
        "libhardware_legacy",
    ],
    header_libs: ["libcutils_headers"],

    static_libs: [
        "libfstab",
        "libtrustystorageinterface",
        "libtrusty",
    ],
+77 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

#include "checkpoint_handling.h"
#include "log.h"

#include <fstab/fstab.h>
#include <cstring>
#include <string>

namespace {

bool checkpointingDoneForever = false;

}  // namespace

int is_data_checkpoint_active(bool* active) {
    if (!active) {
        ALOGE("active out parameter is null");
        return 0;
    }

    *active = false;

    if (checkpointingDoneForever) {
        return 0;
    }

    android::fs_mgr::Fstab procMounts;
    bool success = android::fs_mgr::ReadFstabFromFile("/proc/mounts", &procMounts);
    if (!success) {
        ALOGE("Could not parse /proc/mounts\n");
        /* Really bad. Tell the caller to abort the write. */
        return -1;
    }

    android::fs_mgr::FstabEntry* dataEntry =
            android::fs_mgr::GetEntryForMountPoint(&procMounts, "/data");
    if (dataEntry == NULL) {
        ALOGE("/data is not mounted yet\n");
        return 0;
    }

    /* We can't handle e.g., ext4. Nothing we can do about it for now. */
    if (dataEntry->fs_type != "f2fs") {
        ALOGW("Checkpoint status not supported for filesystem %s\n", dataEntry->fs_type.c_str());
        checkpointingDoneForever = true;
        return 0;
    }

    /*
     * The data entry looks like "... blah,checkpoint=disable:0,blah ...".
     * checkpoint=disable means checkpointing is on (yes, arguably reversed).
     */
    size_t checkpointPos = dataEntry->fs_options.find("checkpoint=disable");
    if (checkpointPos == std::string::npos) {
        /* Assumption is that once checkpointing turns off, it stays off */
        checkpointingDoneForever = true;
    } else {
        *active = true;
    }

    return 0;
}
+37 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * is_data_checkpoint_active() - Check for an active, uncommitted checkpoint of
 * /data. If a checkpoint is active, storage should not commit any
 * rollback-protected writes to /data.
 * @active: Out parameter that will be set to the result of the check.
 *
 * Return: 0 if active was set and is valid, non-zero otherwise.
 */
int is_data_checkpoint_active(bool* active);

#ifdef __cplusplus
}
#endif
+16 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@

#include <cutils/android_filesystem_config.h>

#include "checkpoint_handling.h"
#include "ipc.h"
#include "log.h"
#include "rpmb.h"
@@ -130,6 +131,21 @@ static int handle_req(struct storage_msg* msg, const void* req, size_t req_len)
        }
    }

    if (msg->flags & STORAGE_MSG_FLAG_PRE_COMMIT_CHECKPOINT) {
        bool is_checkpoint_active = false;

        rc = is_data_checkpoint_active(&is_checkpoint_active);
        if (rc != 0) {
            ALOGE("is_data_checkpoint_active failed in an unexpected way. Aborting.\n");
            msg->result = STORAGE_ERR_GENERIC;
            return ipc_respond(msg, NULL, 0);
        } else if (is_checkpoint_active) {
            ALOGE("Checkpoint in progress, dropping write ...\n");
            msg->result = STORAGE_ERR_GENERIC;
            return ipc_respond(msg, NULL, 0);
        }
    }

    switch (msg->cmd) {
        case STORAGE_FILE_DELETE:
            rc = storage_file_delete(msg, req, req_len);