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

Commit 47014a75 authored by Akilesh Kailash's avatar Akilesh Kailash Committed by Automerger Merge Worker
Browse files

Merge "libsnapshot:snapuserd: Transitions for snapuserd" am: e19df50b

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1437277

Change-Id: I50d4f6d0bc2ad83cf7c8782df637ce027206dabd
parents fd43e423 e19df50b
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -163,6 +163,38 @@ cc_library_static {
    ramdisk_available: true,
}

cc_defaults {
    name: "libsnapshot_snapuserd_defaults",
    defaults: [
        "fs_mgr_defaults",
    ],
    cflags: [
        "-D_FILE_OFFSET_BITS=64",
        "-Wall",
        "-Werror",
    ],
    export_include_dirs: ["include"],
    srcs: [
        "snapuserd_client.cpp",
    ],
}

cc_library_static {
    name: "libsnapshot_snapuserd",
    defaults: [
        "libsnapshot_snapuserd_defaults",
    ],
    recovery_available: true,
    static_libs: [
        "libcutils_sockets",
    ],
    shared_libs: [
        "libbase",
        "liblog",
    ],
    ramdisk_available: true,
}

cc_library_static {
    name: "libsnapshot_test_helpers",
    defaults: ["libsnapshot_defaults"],
@@ -363,7 +395,9 @@ cc_defaults {
        "fs_mgr_defaults",
    ],
    srcs: [
	"snapuserd_server.cpp",
        "snapuserd.cpp",
	"snapuserd_daemon.cpp",
    ],

    cflags: [
@@ -374,6 +408,7 @@ cc_defaults {
    static_libs: [
        "libbase",
        "libbrotli",
	"libcutils_sockets",
        "liblog",
        "libdm",
        "libz",
@@ -513,6 +548,8 @@ cc_test {
        "libbrotli",
        "libgtest",
        "libsnapshot_cow",
	"libsnapshot_snapuserd",
        "libcutils_sockets",
        "libz",
    ],
    header_libs: [
+94 −23
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
#include <libsnapshot/cow_writer.h>
#include <libsnapshot/snapuserd_client.h>
#include <storage_literals/storage_literals.h>

namespace android {
@@ -43,17 +44,29 @@ class SnapuserdTest : public ::testing::Test {
        cow_product_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_product_->fd, 0) << strerror(errno);

        cow_system_1_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_system_1_->fd, 0) << strerror(errno);

        cow_product_1_ = std::make_unique<TemporaryFile>();
        ASSERT_GE(cow_product_1_->fd, 0) << strerror(errno);

        size_ = 100_MiB;
    }

    void TearDown() override {
        cow_system_ = nullptr;
        cow_product_ = nullptr;

        cow_system_1_ = nullptr;
        cow_product_1_ = nullptr;
    }

    std::unique_ptr<TemporaryFile> cow_system_;
    std::unique_ptr<TemporaryFile> cow_product_;

    std::unique_ptr<TemporaryFile> cow_system_1_;
    std::unique_ptr<TemporaryFile> cow_product_1_;

    unique_fd sys_fd_;
    unique_fd product_fd_;
    size_t size_;
@@ -71,12 +84,14 @@ class SnapuserdTest : public ::testing::Test {

    void Init();
    void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
    void CreateSystemDmUser();
    void CreateProductDmUser();
    void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
    void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
    void StartSnapuserdDaemon();
    void CreateSnapshotDevices();
    void SwitchSnapshotDevices();

    void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf);
    void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
    SnapuserdClient client_;
};

void SnapuserdTest::Init() {
@@ -112,7 +127,7 @@ void SnapuserdTest::Init() {
    // Read from system partition from offset 0 of size 100MB
    ASSERT_EQ(ReadFullyAtOffset(sys_fd_, system_buffer_.get(), size_, 0), true);

    // Read from system partition from offset 0 of size 100MB
    // Read from product partition from offset 0 of size 100MB
    ASSERT_EQ(ReadFullyAtOffset(product_fd_, product_buffer_.get(), size_, 0), true);
}

@@ -167,9 +182,10 @@ void SnapuserdTest::CreateCowDevice(std::unique_ptr<TemporaryFile>& cow) {
    ASSERT_EQ(lseek(cow->fd, 0, SEEK_SET), 0);
}

void SnapuserdTest::CreateSystemDmUser() {
void SnapuserdTest::CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow) {
    unique_fd system_a_fd;
    std::string cmd;
    system_device_name_.clear();

    // Create a COW device. Number of sectors is chosen random which can
    // hold at least 400MB of data
@@ -180,7 +196,7 @@ void SnapuserdTest::CreateSystemDmUser() {
    int err = ioctl(system_a_fd.get(), BLKGETSIZE, &system_blksize_);
    ASSERT_GE(err, 0);

    std::string str(cow_system_->path);
    std::string str(cow->path);
    std::size_t found = str.find_last_of("/\\");
    ASSERT_NE(found, std::string::npos);
    system_device_name_ = str.substr(found + 1);
@@ -189,9 +205,10 @@ void SnapuserdTest::CreateSystemDmUser() {
    system(cmd.c_str());
}

void SnapuserdTest::CreateProductDmUser() {
void SnapuserdTest::CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow) {
    unique_fd product_a_fd;
    std::string cmd;
    product_device_name_.clear();

    // Create a COW device. Number of sectors is chosen random which can
    // hold at least 400MB of data
@@ -202,7 +219,7 @@ void SnapuserdTest::CreateProductDmUser() {
    int err = ioctl(product_a_fd.get(), BLKGETSIZE, &product_blksize_);
    ASSERT_GE(err, 0);

    std::string str(cow_product_->path);
    std::string str(cow->path);
    std::size_t found = str.find_last_of("/\\");
    ASSERT_NE(found, std::string::npos);
    product_device_name_ = str.substr(found + 1);
@@ -212,15 +229,16 @@ void SnapuserdTest::CreateProductDmUser() {
}

void SnapuserdTest::StartSnapuserdDaemon() {
    // Start the snapuserd daemon
    if (fork() == 0) {
        const char* argv[] = {"/system/bin/snapuserd",       cow_system_->path,
                              "/dev/block/mapper/system_a",  cow_product_->path,
                              "/dev/block/mapper/product_a", nullptr};
        if (execv(argv[0], const_cast<char**>(argv))) {
            ASSERT_TRUE(0);
        }
    }
    int ret;

    ret = client_.StartSnapuserd();
    ASSERT_EQ(ret, 0);

    ret = client_.InitializeSnapuserd(cow_system_->path, "/dev/block/mapper/system_a");
    ASSERT_EQ(ret, 0);

    ret = client_.InitializeSnapuserd(cow_product_->path, "/dev/block/mapper/product_a");
    ASSERT_EQ(ret, 0);
}

void SnapuserdTest::CreateSnapshotDevices() {
@@ -243,9 +261,29 @@ void SnapuserdTest::CreateSnapshotDevices() {
    system(cmd.c_str());
}

void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>&& buf) {
void SnapuserdTest::SwitchSnapshotDevices() {
    std::string cmd;

    cmd = "dmctl create system-snapshot-1 -ro snapshot 0 " + std::to_string(system_blksize_);
    cmd += " /dev/block/mapper/system_a";
    cmd += " /dev/block/mapper/" + system_device_name_;
    cmd += " P 8";

    system(cmd.c_str());

    cmd.clear();

    cmd = "dmctl create product-snapshot-1 -ro snapshot 0 " + std::to_string(product_blksize_);
    cmd += " /dev/block/mapper/product_a";
    cmd += " /dev/block/mapper/" + product_device_name_;
    cmd += " P 8";

    system(cmd.c_str());
}

void SnapuserdTest::TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer) {
    loff_t offset = 0;
    std::unique_ptr<uint8_t[]> buffer = std::move(buf);
    // std::unique_ptr<uint8_t[]> buffer = std::move(buf);

    std::unique_ptr<uint8_t[]> snapuserd_buffer = std::make_unique<uint8_t[]>(size_);

@@ -326,8 +364,8 @@ TEST_F(SnapuserdTest, ReadWrite) {
    CreateCowDevice(cow_system_);
    CreateCowDevice(cow_product_);

    CreateSystemDmUser();
    CreateProductDmUser();
    CreateSystemDmUser(cow_system_);
    CreateProductDmUser(cow_product_);

    StartSnapuserdDaemon();

@@ -335,11 +373,44 @@ TEST_F(SnapuserdTest, ReadWrite) {

    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    TestIO(snapshot_fd, std::move(system_buffer_));
    TestIO(snapshot_fd, system_buffer_);

    snapshot_fd.reset(open("/dev/block/mapper/product-snapshot", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    TestIO(snapshot_fd, std::move(product_buffer_));
    TestIO(snapshot_fd, product_buffer_);

    // Sequence of operations for transition
    CreateCowDevice(cow_system_1_);
    CreateCowDevice(cow_product_1_);

    CreateSystemDmUser(cow_system_1_);
    CreateProductDmUser(cow_product_1_);

    std::vector<std::pair<std::string, std::string>> vec;
    vec.push_back(std::make_pair(cow_system_1_->path, "/dev/block/mapper/system_a"));
    vec.push_back(std::make_pair(cow_product_1_->path, "/dev/block/mapper/product_a"));

    // Start the second stage deamon and send the devices
    ASSERT_EQ(client_.RestartSnapuserd(vec), 0);

    // TODO: This is not switching snapshot device but creates a new table;
    // however, it should serve the testing purpose.
    SwitchSnapshotDevices();

    // Stop the first stage daemon
    ASSERT_EQ(client_.StopSnapuserd(true), 0);

    // Test the IO again with the second stage daemon
    snapshot_fd.reset(open("/dev/block/mapper/system-snapshot-1", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    TestIO(snapshot_fd, system_buffer_);

    snapshot_fd.reset(open("/dev/block/mapper/product-snapshot-1", O_RDONLY));
    ASSERT_TRUE(snapshot_fd > 0);
    TestIO(snapshot_fd, product_buffer_);

    // Stop the second stage daemon
    ASSERT_EQ(client_.StopSnapuserd(false), 0);
}

}  // namespace snapshot
+81 −72
Original line number Diff line number Diff line
@@ -14,85 +14,94 @@

#pragma once

#include <linux/types.h>
#include <stdint.h>
#include <stdlib.h>

#include <csignal>
#include <cstring>
#include <iostream>
#include <limits>
#include <string>
#include <thread>
#include <vector>

#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <libdm/dm.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
#include <libsnapshot/snapuserd_kernel.h>

namespace android {
namespace snapshot {

// Kernel COW header fields
static constexpr uint32_t SNAP_MAGIC = 0x70416e53;

static constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;

static constexpr uint32_t NUM_SNAPSHOT_HDR_CHUNKS = 1;

static constexpr uint32_t SNAPSHOT_VALID = 1;

/*
 * The basic unit of block I/O is a sector. It is used in a number of contexts
 * in Linux (blk, bio, genhd). The size of one sector is 512 = 2**9
 * bytes. Variables of type sector_t represent an offset or size that is a
 * multiple of 512 bytes. Hence these two constants.
 */
static constexpr uint32_t SECTOR_SHIFT = 9;

typedef __u64 sector_t;
typedef sector_t chunk_t;

static constexpr uint32_t CHUNK_SIZE = 8;
static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1);

static constexpr uint32_t BLOCK_SIZE = 4096;
static constexpr uint32_t BLOCK_SHIFT = (__builtin_ffs(BLOCK_SIZE) - 1);

// This structure represents the kernel COW header.
// All the below fields should be in Little Endian format.
struct disk_header {
    uint32_t magic;

    /*
     * Is this snapshot valid.  There is no way of recovering
     * an invalid snapshot.
     */
    uint32_t valid;

    /*
     * Simple, incrementing version. no backward
     * compatibility.
     */
    uint32_t version;

    /* In sectors */
    uint32_t chunk_size;
} __packed;

// A disk exception is a mapping of old_chunk to new_chunk
// old_chunk is the chunk ID of a dm-snapshot device.
// new_chunk is the chunk ID of the COW device.
struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
} __packed;

// Control structures to communicate with dm-user
// It comprises of header and a payload
struct dm_user_header {
    __u64 seq;
    __u64 type;
    __u64 flags;
    __u64 sector;
    __u64 len;
    __u64 io_in_progress;
} __attribute__((packed));

struct dm_user_payload {
    __u8 buf[];
using android::base::unique_fd;

class BufferSink : public IByteSink {
  public:
    void Initialize(size_t size);
    void* GetBufPtr() { return buffer_.get(); }
    void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
    void* GetPayloadBuffer(size_t size);
    void* GetBuffer(size_t requested, size_t* actual) override;
    void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
    struct dm_user_header* GetHeaderPtr();
    bool ReturnData(void*, size_t) override { return true; }
    void ResetBufferOffset() { buffer_offset_ = 0; }

  private:
    std::unique_ptr<uint8_t[]> buffer_;
    loff_t buffer_offset_;
    size_t buffer_size_;
};

// Message comprising both header and payload
struct dm_user_message {
    struct dm_user_header header;
    struct dm_user_payload payload;
class Snapuserd final {
  public:
    Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device)
        : cow_device_(in_cow_device),
          backing_store_device_(in_backing_store_device),
          metadata_read_done_(false) {}

    int Init();
    int Run();
    int ReadDmUserHeader();
    int WriteDmUserPayload(size_t size);
    int ConstructKernelCowHeader();
    int ReadMetadata();
    int ZerofillDiskExceptions(size_t read_size);
    int ReadDiskExceptions(chunk_t chunk, size_t size);
    int ReadData(chunk_t chunk, size_t size);

  private:
    int ProcessReplaceOp(const CowOperation* cow_op);
    int ProcessCopyOp(const CowOperation* cow_op);
    int ProcessZeroOp();

    std::string cow_device_;
    std::string backing_store_device_;

    unique_fd cow_fd_;
    unique_fd backing_store_fd_;
    unique_fd ctrl_fd_;

    uint32_t exceptions_per_area_;

    std::unique_ptr<ICowOpIter> cowop_iter_;
    std::unique_ptr<CowReader> reader_;

    // Vector of disk exception which is a
    // mapping of old-chunk to new-chunk
    std::vector<std::unique_ptr<uint8_t[]>> vec_;

    // Index - Chunk ID
    // Value - cow operation
    std::vector<const CowOperation*> chunk_vec_;

    bool metadata_read_done_;
    BufferSink bufsink_;
};

}  // namespace snapshot
+73 −0
Original line number Diff line number Diff line
// Copyright (C) 2020 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 <arpa/inet.h>
#include <cutils/sockets.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include <chrono>
#include <cstring>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

namespace android {
namespace snapshot {

static constexpr uint32_t PACKET_SIZE = 512;
static constexpr uint32_t MAX_CONNECT_RETRY_COUNT = 10;

class SnapuserdClient {
  private:
    int sockfd_ = 0;

    int Sendmsg(const char* msg, size_t size);
    std::string Receivemsg();
    int StartSnapuserdaemon(std::string socketname);
    bool ConnectToServerSocket(std::string socketname);
    bool ConnectToServer();

    void DisconnectFromServer() { close(sockfd_); }

    std::string GetSocketNameFirstStage() {
        static std::string snapd_one("snapdone");
        return snapd_one;
    }

    std::string GetSocketNameSecondStage() {
        static std::string snapd_two("snapdtwo");
        return snapd_two;
    }

  public:
    int StartSnapuserd();
    int StopSnapuserd(bool firstStageDaemon);
    int RestartSnapuserd(std::vector<std::pair<std::string, std::string>>& vec);
    int InitializeSnapuserd(std::string cow_device, std::string backing_device);
};

}  // namespace snapshot
}  // namespace android
+47 −0
Original line number Diff line number Diff line
// Copyright (C) 2020 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 <libsnapshot/snapuserd_server.h>

namespace android {
namespace snapshot {

class Daemon {
    // The Daemon class is a singleton to avoid
    // instantiating more than once
  public:
    static Daemon& Instance() {
        static Daemon instance;
        return instance;
    }

    int StartServer(std::string socketname);
    bool IsRunning();
    void Run();

  private:
    bool is_running_;

    Daemon();
    Daemon(Daemon const&) = delete;
    void operator=(Daemon const&) = delete;

    SnapuserdServer server_;
    static void SignalHandler(int signal);
};

}  // namespace snapshot
}  // namespace android
Loading