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

Commit 5fcc2b5d authored by Yifan Hong's avatar Yifan Hong
Browse files

Convert SnapshotStatus to proto

Also, add a "name" field to SnapshotStatus, and delete
the "name" arg from CreateSnapshot / WriteSnapshotStatus.
ReadSnapshotStatus will warn if the name mismatches from
the file name, and auto-correct it.

Test: libsnapshot_test

Change-Id: I725cf39c07684b100b140a8a21ea9d23ab9d2241
parent 58ae8d47
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -49,11 +49,17 @@ cc_defaults {
        "libfiemap_headers",
    ],
    export_include_dirs: ["include"],
    proto: {
        type: "lite",
        export_proto_headers: true,
        canonical_path_from_root: false,
    },
}

filegroup {
    name: "libsnapshot_sources",
    srcs: [
        "android/snapshot/snapshot.proto",
        "snapshot.cpp",
        "snapshot_metadata_updater.cpp",
        "partition_cow_creator.cpp",
@@ -132,9 +138,10 @@ cc_binary {
        "libbinder",
        "libext4_utils",
        "libfs_mgr",
        "libutils",
        "liblog",
        "liblp",
        "libprotobuf-cpp-lite",
        "libutils",
    ],
    init_rc: [
        "snapshotctl.rc",
+87 −0
Original line number Diff line number Diff line
// 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.

syntax = "proto3";
package android.snapshot;

option optimize_for = LITE_RUNTIME;

// Next: 4
enum SnapshotState {
    // No snapshot is found.
    NONE = 0;

    // The snapshot has been created and possibly written to. Rollbacks are
    // possible by destroying the snapshot.
    CREATED = 1;

    // Changes are being merged. No rollbacks are possible beyond this point.
    MERGING = 2;

    // Changes have been merged, Future reboots may map the base device
    // directly.
    MERGE_COMPLETED = 3;
}

// Next: 9
message SnapshotStatus {
    // Name of the snapshot. This is usually the name of the snapshotted
    // logical partition; for example, "system_b".
    string name = 1;

    SnapshotState state = 2;

    // Size of the full (base) device.
    uint64 device_size = 3;

    // Size of the snapshot. This is the sum of lengths of ranges in the base
    // device that needs to be snapshotted during the update.
    // This must be less than or equal to |device_size|.
    // This value is 0 if no snapshot is needed for this device because
    // no changes
    uint64 snapshot_size = 4;

    // Size of the "COW partition". A COW partition is a special logical
    // partition represented in the super partition metadata. This partition and
    // the "COW image" form the "COW device" that supports the snapshot device.
    //
    // When SnapshotManager creates a COW device, it first searches for unused
    // blocks in the super partition, and use those before creating the COW
    // image if the COW partition is not big enough.
    //
    // This value is 0 if no space in super is left for the COW partition.
    // |cow_partition_size + cow_file_size| must not be zero if |snapshot_size|
    // is non-zero.
    uint64 cow_partition_size = 5;

    // Size of the "COW file", or "COW image". A COW file / image is created
    // when the "COW partition" is not big enough to store changes to the
    // snapshot device.
    //
    // This value is 0 if |cow_partition_size| is big enough to hold all changes
    // to the snapshot device.
    uint64 cow_file_size = 6;

    // Sectors allocated for the COW device. Recording this value right after
    // the update and before the merge allows us to infer the progress of the
    // merge process.
    // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
    uint64 sectors_allocated = 7;

    // Metadata sectors allocated for the COW device. Recording this value right
    // before the update and before the merge allows us to infer the progress of
    // the merge process.
    // This is non-zero when |state| == MERGING or MERGE_COMPLETED.
    uint64 metadata_sectors = 8;
}
+4 −20
Original line number Diff line number Diff line
@@ -53,8 +53,9 @@ namespace snapshot {

struct AutoDeleteCowImage;
struct AutoDeleteSnapshot;
struct PartitionCowCreator;
struct AutoDeviceList;
struct PartitionCowCreator;
class SnapshotStatus;

static constexpr const std::string_view kCowGroupName = "cow";

@@ -250,22 +251,6 @@ class SnapshotManager final {
    std::unique_ptr<LockedFile> OpenFile(const std::string& file, int open_flags, int lock_flags);
    bool Truncate(LockedFile* file);

    enum class SnapshotState : int { None, Created, Merging, MergeCompleted };
    static std::string to_string(SnapshotState state);

    // This state is persisted per-snapshot in /metadata/ota/snapshots/.
    struct SnapshotStatus {
        SnapshotState state = SnapshotState::None;
        uint64_t device_size = 0;
        uint64_t snapshot_size = 0;
        uint64_t cow_partition_size = 0;
        uint64_t cow_file_size = 0;

        // These are non-zero when merging.
        uint64_t sectors_allocated = 0;
        uint64_t metadata_sectors = 0;
    };

    // Create a new snapshot record. This creates the backing COW store and
    // persists information needed to map the device. The device can be mapped
    // with MapSnapshot().
@@ -282,7 +267,7 @@ class SnapshotManager final {
    //
    // All sizes are specified in bytes, and the device, snapshot, COW partition and COW file sizes
    // must be a multiple of the sector size (512 bytes).
    bool CreateSnapshot(LockedFile* lock, const std::string& name, SnapshotStatus status);
    bool CreateSnapshot(LockedFile* lock, SnapshotStatus* status);

    // |name| should be the base partition name (e.g. "system_a"). Create the
    // backing COW image using the size previously passed to CreateSnapshot().
@@ -363,8 +348,7 @@ class SnapshotManager final {
    UpdateState CheckTargetMergeState(LockedFile* lock, const std::string& name);

    // Interact with status files under /metadata/ota/snapshots.
    bool WriteSnapshotStatus(LockedFile* lock, const std::string& name,
                             const SnapshotStatus& status);
    bool WriteSnapshotStatus(LockedFile* lock, const SnapshotStatus& status);
    bool ReadSnapshotStatus(LockedFile* lock, const std::string& name, SnapshotStatus* status);
    std::string GetSnapshotStatusFilePath(const std::string& name);

+12 −8
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <android-base/logging.h>

#include <android/snapshot/snapshot.pb.h>
#include "utility.h"

using android::dm::kSectorSize;
@@ -84,13 +85,14 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
            << "logical_block_size is not power of 2";

    Return ret;
    ret.snapshot_status.device_size = target_partition->size();
    ret.snapshot_status.set_name(target_partition->name());
    ret.snapshot_status.set_device_size(target_partition->size());

    // TODO(b/141889746): Optimize by using a smaller snapshot. Some ranges in target_partition
    // may be written directly.
    ret.snapshot_status.snapshot_size = target_partition->size();
    ret.snapshot_status.set_snapshot_size(target_partition->size());

    auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size);
    auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size());
    if (!cow_size.has_value()) return std::nullopt;

    // Compute regions that are free in both current and target metadata. These are the regions
@@ -106,18 +108,20 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
    LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";

    // Compute the COW partition size.
    ret.snapshot_status.cow_partition_size = std::min(*cow_size, free_region_length);
    uint64_t cow_partition_size = std::min(*cow_size, free_region_length);
    // Round it down to the nearest logical block. Logical partitions must be a multiple
    // of logical blocks.
    ret.snapshot_status.cow_partition_size &= ~(logical_block_size - 1);
    cow_partition_size &= ~(logical_block_size - 1);
    ret.snapshot_status.set_cow_partition_size(cow_partition_size);
    // Assign cow_partition_usable_regions to indicate what regions should the COW partition uses.
    ret.cow_partition_usable_regions = std::move(free_regions);

    // The rest of the COW space is allocated on ImageManager.
    ret.snapshot_status.cow_file_size = (*cow_size) - ret.snapshot_status.cow_partition_size;
    uint64_t cow_file_size = (*cow_size) - ret.snapshot_status.cow_partition_size();
    // Round it up to the nearest sector.
    ret.snapshot_status.cow_file_size += kSectorSize - 1;
    ret.snapshot_status.cow_file_size &= ~(kSectorSize - 1);
    cow_file_size += kSectorSize - 1;
    cow_file_size &= ~(kSectorSize - 1);
    ret.snapshot_status.set_cow_file_size(cow_file_size);

    return ret;
}
+3 −2
Original line number Diff line number Diff line
@@ -20,8 +20,9 @@
#include <string>

#include <liblp/builder.h>
#include <update_engine/update_metadata.pb.h>

#include <libsnapshot/snapshot.h>
#include <android/snapshot/snapshot.pb.h>

namespace android {
namespace snapshot {
@@ -51,7 +52,7 @@ struct PartitionCowCreator {
    const RepeatedPtrField<InstallOperation>* operations = nullptr;

    struct Return {
        SnapshotManager::SnapshotStatus snapshot_status;
        SnapshotStatus snapshot_status;
        std::vector<Interval> cow_partition_usable_regions;
    };

Loading