Merge "libsnapshot:snapuserd: Transitions for snapuserd" am: e19df50b75
Original change: https://android-review.googlesource.com/c/platform/system/core/+/1437277 Change-Id: I50d4f6d0bc2ad83cf7c8782df637ce027206dabd
This commit is contained in:
commit
47014a7514
11 changed files with 1200 additions and 354 deletions
|
|
@ -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: [
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
using android::base::unique_fd;
|
||||
|
||||
static constexpr uint32_t SNAPSHOT_DISK_VERSION = 1;
|
||||
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; }
|
||||
|
||||
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[];
|
||||
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
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
Normal file
73
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
// 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
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
Normal file
47
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
// 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
|
||||
97
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
Normal file
97
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
// 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
|
||||
|
||||
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[];
|
||||
};
|
||||
|
||||
// Message comprising both header and payload
|
||||
struct dm_user_message {
|
||||
struct dm_user_header header;
|
||||
struct dm_user_payload payload;
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
115
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
Normal file
115
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
// 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 <stdint.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
static constexpr uint32_t MAX_PACKET_SIZE = 512;
|
||||
|
||||
enum class DaemonOperations {
|
||||
START,
|
||||
QUERY,
|
||||
TERMINATING,
|
||||
STOP,
|
||||
INVALID,
|
||||
};
|
||||
|
||||
class Client {
|
||||
private:
|
||||
std::unique_ptr<std::thread> threadHandler_;
|
||||
|
||||
public:
|
||||
void SetThreadHandler(std::function<void(void)> func) {
|
||||
threadHandler_ = std::make_unique<std::thread>(func);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::thread>& GetThreadHandler() { return threadHandler_; }
|
||||
};
|
||||
|
||||
class Stoppable {
|
||||
std::promise<void> exitSignal_;
|
||||
std::future<void> futureObj_;
|
||||
|
||||
public:
|
||||
Stoppable() : futureObj_(exitSignal_.get_future()) {}
|
||||
|
||||
virtual ~Stoppable() {}
|
||||
|
||||
virtual void ThreadStart(std::string cow_device, std::string backing_device) = 0;
|
||||
|
||||
bool StopRequested() {
|
||||
// checks if value in future object is available
|
||||
if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
// Request the thread to stop by setting value in promise object
|
||||
void StopThreads() { exitSignal_.set_value(); }
|
||||
};
|
||||
|
||||
class SnapuserdServer : public Stoppable {
|
||||
private:
|
||||
android::base::unique_fd sockfd_;
|
||||
bool terminating_;
|
||||
std::vector<std::unique_ptr<Client>> clients_vec_;
|
||||
void ThreadStart(std::string cow_device, std::string backing_device) override;
|
||||
void ShutdownThreads();
|
||||
DaemonOperations Resolveop(std::string& input);
|
||||
std::string GetDaemonStatus();
|
||||
void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
|
||||
|
||||
void SetTerminating() { terminating_ = true; }
|
||||
|
||||
bool IsTerminating() { return terminating_; }
|
||||
|
||||
public:
|
||||
~SnapuserdServer() { clients_vec_.clear(); }
|
||||
|
||||
SnapuserdServer() { terminating_ = false; }
|
||||
|
||||
int Start(std::string socketname);
|
||||
int AcceptClient();
|
||||
int Receivemsg(int fd);
|
||||
int Sendmsg(int fd, char* msg, size_t len);
|
||||
std::string Recvmsg(int fd, int* ret);
|
||||
};
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
@ -14,25 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <linux/types.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.h>
|
||||
#include <libsnapshot/snapuserd_daemon.h>
|
||||
#include <libsnapshot/snapuserd_server.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
|
@ -60,140 +46,36 @@ class Target {
|
|||
const std::string uuid_;
|
||||
};
|
||||
|
||||
class Daemon {
|
||||
// The Daemon class is a singleton to avoid
|
||||
// instantiating more than once
|
||||
public:
|
||||
static Daemon& Instance() {
|
||||
static Daemon instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool IsRunning();
|
||||
|
||||
private:
|
||||
bool is_running_;
|
||||
|
||||
Daemon();
|
||||
Daemon(Daemon const&) = delete;
|
||||
void operator=(Daemon const&) = delete;
|
||||
|
||||
static void SignalHandler(int signal);
|
||||
};
|
||||
|
||||
Daemon::Daemon() {
|
||||
is_running_ = true;
|
||||
signal(SIGINT, Daemon::SignalHandler);
|
||||
signal(SIGTERM, Daemon::SignalHandler);
|
||||
void BufferSink::Initialize(size_t size) {
|
||||
buffer_size_ = size;
|
||||
buffer_offset_ = 0;
|
||||
buffer_ = std::make_unique<uint8_t[]>(size);
|
||||
}
|
||||
|
||||
bool Daemon::IsRunning() {
|
||||
return is_running_;
|
||||
void* BufferSink::GetPayloadBuffer(size_t size) {
|
||||
if ((buffer_size_ - buffer_offset_) < size) return nullptr;
|
||||
|
||||
char* buffer = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
|
||||
return (char*)msg->payload.buf + buffer_offset_;
|
||||
}
|
||||
|
||||
void Daemon::SignalHandler(int signal) {
|
||||
LOG(DEBUG) << "Snapuserd received signal: " << signal;
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
case SIGTERM: {
|
||||
Daemon::Instance().is_running_ = false;
|
||||
break;
|
||||
}
|
||||
void* BufferSink::GetBuffer(size_t requested, size_t* actual) {
|
||||
void* buf = GetPayloadBuffer(requested);
|
||||
if (!buf) {
|
||||
*actual = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*actual = requested;
|
||||
return buf;
|
||||
}
|
||||
|
||||
class BufferSink : public IByteSink {
|
||||
public:
|
||||
void Initialize(size_t size) {
|
||||
buffer_size_ = size;
|
||||
buffer_offset_ = 0;
|
||||
buffer_ = std::make_unique<uint8_t[]>(size);
|
||||
}
|
||||
|
||||
void* GetBufPtr() { return buffer_.get(); }
|
||||
|
||||
void Clear() { memset(GetBufPtr(), 0, buffer_size_); }
|
||||
|
||||
void* GetPayloadBuffer(size_t size) {
|
||||
if ((buffer_size_ - buffer_offset_) < size) return nullptr;
|
||||
|
||||
char* buffer = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
|
||||
return (char*)msg->payload.buf + buffer_offset_;
|
||||
}
|
||||
|
||||
void* GetBuffer(size_t requested, size_t* actual) override {
|
||||
void* buf = GetPayloadBuffer(requested);
|
||||
if (!buf) {
|
||||
*actual = 0;
|
||||
return nullptr;
|
||||
}
|
||||
*actual = requested;
|
||||
return buf;
|
||||
}
|
||||
|
||||
void UpdateBufferOffset(size_t size) { buffer_offset_ += size; }
|
||||
|
||||
struct dm_user_header* GetHeaderPtr() {
|
||||
CHECK(sizeof(struct dm_user_header) <= buffer_size_);
|
||||
char* buf = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
|
||||
return header;
|
||||
}
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
class Snapuserd final {
|
||||
public:
|
||||
Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device)
|
||||
: in_cow_device_(in_cow_device),
|
||||
in_backing_store_device_(in_backing_store_device),
|
||||
metadata_read_done_(false) {}
|
||||
|
||||
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 in_cow_device_;
|
||||
std::string in_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_;
|
||||
};
|
||||
struct dm_user_header* BufferSink::GetHeaderPtr() {
|
||||
CHECK(sizeof(struct dm_user_header) <= buffer_size_);
|
||||
char* buf = reinterpret_cast<char*>(GetBufPtr());
|
||||
struct dm_user_header* header = (struct dm_user_header*)(&(buf[0]));
|
||||
return header;
|
||||
}
|
||||
|
||||
// Construct kernel COW header in memory
|
||||
// This header will be in sector 0. The IO
|
||||
|
|
@ -581,9 +463,12 @@ void MyLogger(android::base::LogId, android::base::LogSeverity severity, const c
|
|||
// Read Header from dm-user misc device. This gives
|
||||
// us the sector number for which IO is issued by dm-snapshot device
|
||||
int Snapuserd::ReadDmUserHeader() {
|
||||
if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
|
||||
PLOG(ERROR) << "Control read failed";
|
||||
return -1;
|
||||
int ret;
|
||||
|
||||
ret = read(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header));
|
||||
if (ret < 0) {
|
||||
PLOG(ERROR) << "Control-read failed with: " << ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return sizeof(struct dm_user_header);
|
||||
|
|
@ -600,22 +485,20 @@ int Snapuserd::WriteDmUserPayload(size_t size) {
|
|||
return sizeof(struct dm_user_header) + size;
|
||||
}
|
||||
|
||||
// Start the daemon.
|
||||
// TODO: Handle signals
|
||||
int Snapuserd::Run() {
|
||||
backing_store_fd_.reset(open(in_backing_store_device_.c_str(), O_RDONLY));
|
||||
int Snapuserd::Init() {
|
||||
backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
|
||||
if (backing_store_fd_ < 0) {
|
||||
LOG(ERROR) << "Open Failed: " << in_backing_store_device_;
|
||||
LOG(ERROR) << "Open Failed: " << backing_store_device_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
cow_fd_.reset(open(in_cow_device_.c_str(), O_RDWR));
|
||||
cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
|
||||
if (cow_fd_ < 0) {
|
||||
LOG(ERROR) << "Open Failed: " << in_cow_device_;
|
||||
LOG(ERROR) << "Open Failed: " << cow_device_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string str(in_cow_device_);
|
||||
std::string str(cow_device_);
|
||||
std::size_t found = str.find_last_of("/\\");
|
||||
CHECK(found != std::string::npos);
|
||||
std::string device_name = str.substr(found + 1);
|
||||
|
|
@ -625,7 +508,7 @@ int Snapuserd::Run() {
|
|||
auto& dm = dm::DeviceMapper::Instance();
|
||||
std::string uuid;
|
||||
if (!dm.GetDmDeviceUuidByName(device_name, &uuid)) {
|
||||
LOG(ERROR) << "Unable to find UUID for " << in_cow_device_;
|
||||
LOG(ERROR) << "Unable to find UUID for " << cow_device_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -638,8 +521,6 @@ int Snapuserd::Run() {
|
|||
return 1;
|
||||
}
|
||||
|
||||
int ret = 0;
|
||||
|
||||
// Allocate the buffer which is used to communicate between
|
||||
// daemon and dm-user. The buffer comprises of header and a fixed payload.
|
||||
// If the dm-user requests a big IO, the IO will be broken into chunks
|
||||
|
|
@ -647,138 +528,125 @@ int Snapuserd::Run() {
|
|||
size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
|
||||
bufsink_.Initialize(buf_size);
|
||||
|
||||
while (true) {
|
||||
struct dm_user_header* header = bufsink_.GetHeaderPtr();
|
||||
return 0;
|
||||
}
|
||||
|
||||
bufsink_.Clear();
|
||||
int Snapuserd::Run() {
|
||||
int ret = 0;
|
||||
|
||||
ret = ReadDmUserHeader();
|
||||
if (ret < 0) return ret;
|
||||
struct dm_user_header* header = bufsink_.GetHeaderPtr();
|
||||
|
||||
LOG(DEBUG) << "dm-user returned " << ret << " bytes";
|
||||
bufsink_.Clear();
|
||||
|
||||
LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
|
||||
LOG(DEBUG) << "msg->type: " << std::hex << header->type;
|
||||
LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
|
||||
LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
|
||||
LOG(DEBUG) << "msg->len: " << std::hex << header->len;
|
||||
ret = ReadDmUserHeader();
|
||||
if (ret < 0) return ret;
|
||||
|
||||
switch (header->type) {
|
||||
case DM_USER_MAP_READ: {
|
||||
size_t remaining_size = header->len;
|
||||
loff_t offset = 0;
|
||||
header->io_in_progress = 0;
|
||||
ret = 0;
|
||||
do {
|
||||
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
|
||||
LOG(DEBUG) << "dm-user returned " << ret << " bytes";
|
||||
|
||||
// Request to sector 0 is always for kernel
|
||||
// representation of COW header. This IO should be only
|
||||
// once during dm-snapshot device creation. We should
|
||||
// never see multiple IO requests. Additionally this IO
|
||||
// will always be a single 4k.
|
||||
if (header->sector == 0) {
|
||||
// Read the metadata from internal COW device
|
||||
// and build the in-memory data structures
|
||||
// for all the operations in the internal COW.
|
||||
if (!metadata_read_done_ && ReadMetadata()) {
|
||||
LOG(ERROR) << "Metadata read failed";
|
||||
return 1;
|
||||
LOG(DEBUG) << "msg->seq: " << std::hex << header->seq;
|
||||
LOG(DEBUG) << "msg->type: " << std::hex << header->type;
|
||||
LOG(DEBUG) << "msg->flags: " << std::hex << header->flags;
|
||||
LOG(DEBUG) << "msg->sector: " << std::hex << header->sector;
|
||||
LOG(DEBUG) << "msg->len: " << std::hex << header->len;
|
||||
|
||||
switch (header->type) {
|
||||
case DM_USER_MAP_READ: {
|
||||
size_t remaining_size = header->len;
|
||||
loff_t offset = 0;
|
||||
header->io_in_progress = 0;
|
||||
ret = 0;
|
||||
do {
|
||||
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
|
||||
|
||||
// Request to sector 0 is always for kernel
|
||||
// representation of COW header. This IO should be only
|
||||
// once during dm-snapshot device creation. We should
|
||||
// never see multiple IO requests. Additionally this IO
|
||||
// will always be a single 4k.
|
||||
if (header->sector == 0) {
|
||||
// Read the metadata from internal COW device
|
||||
// and build the in-memory data structures
|
||||
// for all the operations in the internal COW.
|
||||
if (!metadata_read_done_ && ReadMetadata()) {
|
||||
LOG(ERROR) << "Metadata read failed";
|
||||
return 1;
|
||||
}
|
||||
metadata_read_done_ = true;
|
||||
|
||||
CHECK(read_size == BLOCK_SIZE);
|
||||
ret = ConstructKernelCowHeader();
|
||||
if (ret < 0) return ret;
|
||||
} else {
|
||||
// Convert the sector number to a chunk ID.
|
||||
//
|
||||
// Check if the chunk ID represents a metadata
|
||||
// page. If the chunk ID is not found in the
|
||||
// vector, then it points to a metadata page.
|
||||
chunk_t chunk = (header->sector >> CHUNK_SHIFT);
|
||||
|
||||
if (chunk >= chunk_vec_.size()) {
|
||||
ret = ZerofillDiskExceptions(read_size);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "ZerofillDiskExceptions failed";
|
||||
return ret;
|
||||
}
|
||||
} else if (chunk_vec_[chunk] == nullptr) {
|
||||
ret = ReadDiskExceptions(chunk, read_size);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "ReadDiskExceptions failed";
|
||||
return ret;
|
||||
}
|
||||
metadata_read_done_ = true;
|
||||
|
||||
CHECK(read_size == BLOCK_SIZE);
|
||||
ret = ConstructKernelCowHeader();
|
||||
if (ret < 0) return ret;
|
||||
} else {
|
||||
// Convert the sector number to a chunk ID.
|
||||
//
|
||||
// Check if the chunk ID represents a metadata
|
||||
// page. If the chunk ID is not found in the
|
||||
// vector, then it points to a metadata page.
|
||||
chunk_t chunk = (header->sector >> CHUNK_SHIFT);
|
||||
|
||||
if (chunk >= chunk_vec_.size()) {
|
||||
ret = ZerofillDiskExceptions(read_size);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "ZerofillDiskExceptions failed";
|
||||
return ret;
|
||||
}
|
||||
} else if (chunk_vec_[chunk] == nullptr) {
|
||||
ret = ReadDiskExceptions(chunk, read_size);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "ReadDiskExceptions failed";
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
|
||||
ret = ReadData(chunk + num_chunks_read, read_size);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "ReadData failed";
|
||||
return ret;
|
||||
}
|
||||
chunk_t num_chunks_read = (offset >> BLOCK_SHIFT);
|
||||
ret = ReadData(chunk + num_chunks_read, read_size);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "ReadData failed";
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t written = WriteDmUserPayload(ret);
|
||||
if (written < 0) return written;
|
||||
ssize_t written = WriteDmUserPayload(ret);
|
||||
if (written < 0) return written;
|
||||
|
||||
remaining_size -= ret;
|
||||
offset += ret;
|
||||
if (remaining_size) {
|
||||
LOG(DEBUG) << "Write done ret: " << ret
|
||||
<< " remaining size: " << remaining_size;
|
||||
bufsink_.GetHeaderPtr()->io_in_progress = 1;
|
||||
}
|
||||
} while (remaining_size);
|
||||
remaining_size -= ret;
|
||||
offset += ret;
|
||||
if (remaining_size) {
|
||||
LOG(DEBUG) << "Write done ret: " << ret
|
||||
<< " remaining size: " << remaining_size;
|
||||
bufsink_.GetHeaderPtr()->io_in_progress = 1;
|
||||
}
|
||||
} while (remaining_size);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case DM_USER_MAP_WRITE: {
|
||||
// TODO: After merge operation is completed, kernel issues write
|
||||
// to flush all the exception mappings where the merge is
|
||||
// completed. If dm-user routes the WRITE IO, we need to clear
|
||||
// in-memory data structures representing those exception
|
||||
// mappings.
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "read() finished, next message";
|
||||
case DM_USER_MAP_WRITE: {
|
||||
// TODO: After merge operation is completed, kernel issues write
|
||||
// to flush all the exception mappings where the merge is
|
||||
// completed. If dm-user routes the WRITE IO, we need to clear
|
||||
// in-memory data structures representing those exception
|
||||
// mappings.
|
||||
abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "read() finished, next message";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
||||
void run_thread(std::string cow_device, std::string backing_device) {
|
||||
android::snapshot::Snapuserd snapd(cow_device, backing_device);
|
||||
snapd.Run();
|
||||
}
|
||||
|
||||
int main([[maybe_unused]] int argc, char** argv) {
|
||||
android::base::InitLogging(argv, &android::base::KernelLogger);
|
||||
|
||||
android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
|
||||
|
||||
while (daemon.IsRunning()) {
|
||||
// TODO: This is hardcoded wherein:
|
||||
// argv[1] = system_cow, argv[2] = /dev/block/mapper/system_a
|
||||
// argv[3] = product_cow, argv[4] = /dev/block/mapper/product_a
|
||||
//
|
||||
// This should be fixed based on some kind of IPC or setup a
|
||||
// command socket and spin up the thread based when a new
|
||||
// partition is visible.
|
||||
std::thread system_a(run_thread, argv[1], argv[2]);
|
||||
std::thread product_a(run_thread, argv[3], argv[4]);
|
||||
|
||||
system_a.join();
|
||||
product_a.join();
|
||||
}
|
||||
daemon.StartServer(argv[1]);
|
||||
daemon.Run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
261
fs_mgr/libsnapshot/snapuserd_client.cpp
Normal file
261
fs_mgr/libsnapshot/snapuserd_client.cpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <libsnapshot/snapuserd_client.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
bool SnapuserdClient::ConnectToServerSocket(std::string socketname) {
|
||||
sockfd_ = 0;
|
||||
|
||||
sockfd_ =
|
||||
socket_local_client(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
if (sockfd_ < 0) {
|
||||
LOG(ERROR) << "Failed to connect to " << socketname;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string msg = "query";
|
||||
|
||||
int sendRet = Sendmsg(msg.c_str(), msg.size());
|
||||
if (sendRet < 0) {
|
||||
LOG(ERROR) << "Failed to send query message to snapuserd daemon with socket " << socketname;
|
||||
DisconnectFromServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string str = Receivemsg();
|
||||
|
||||
if (str.find("fail") != std::string::npos) {
|
||||
LOG(ERROR) << "Failed to receive message from snapuserd daemon with socket " << socketname;
|
||||
DisconnectFromServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
// If the daemon is passive then fallback to secondary active daemon. Daemon
|
||||
// is passive during transition phase. Please see RestartSnapuserd()
|
||||
if (str.find("passive") != std::string::npos) {
|
||||
LOG(DEBUG) << "Snapuserd is passive with socket " << socketname;
|
||||
DisconnectFromServer();
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK(str.find("active") != std::string::npos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SnapuserdClient::ConnectToServer() {
|
||||
if (ConnectToServerSocket(GetSocketNameFirstStage())) return true;
|
||||
|
||||
if (ConnectToServerSocket(GetSocketNameSecondStage())) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int SnapuserdClient::Sendmsg(const char* msg, size_t size) {
|
||||
int numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg, size, 0));
|
||||
if (numBytesSent < 0) {
|
||||
LOG(ERROR) << "Send failed " << strerror(errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((uint)numBytesSent < size) {
|
||||
LOG(ERROR) << "Partial data sent " << strerror(errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string SnapuserdClient::Receivemsg() {
|
||||
char msg[PACKET_SIZE];
|
||||
std::string msgStr("fail");
|
||||
int ret;
|
||||
|
||||
ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, PACKET_SIZE, 0));
|
||||
if (ret <= 0) {
|
||||
LOG(ERROR) << "recv failed " << strerror(errno);
|
||||
return msgStr;
|
||||
}
|
||||
|
||||
msgStr.clear();
|
||||
msgStr = msg;
|
||||
return msgStr;
|
||||
}
|
||||
|
||||
int SnapuserdClient::StopSnapuserd(bool firstStageDaemon) {
|
||||
if (firstStageDaemon) {
|
||||
sockfd_ = socket_local_client(GetSocketNameFirstStage().c_str(),
|
||||
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM);
|
||||
if (sockfd_ < 0) {
|
||||
LOG(ERROR) << "Failed to connect to " << GetSocketNameFirstStage();
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (!ConnectToServer()) {
|
||||
LOG(ERROR) << "Failed to connect to socket " << GetSocketNameSecondStage();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
std::string msg = "stop";
|
||||
|
||||
int sendRet = Sendmsg(msg.c_str(), msg.size());
|
||||
if (sendRet < 0) {
|
||||
LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
|
||||
DisconnectFromServer();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SnapuserdClient::StartSnapuserdaemon(std::string socketname) {
|
||||
int retry_count = 0;
|
||||
|
||||
if (fork() == 0) {
|
||||
const char* argv[] = {"/system/bin/snapuserd", socketname.c_str(), nullptr};
|
||||
if (execv(argv[0], const_cast<char**>(argv))) {
|
||||
LOG(ERROR) << "Failed to exec snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// snapuserd is a daemon and will never exit; parent can't wait here
|
||||
// to get the return code. Since Snapuserd starts the socket server,
|
||||
// give it some time to fully launch.
|
||||
//
|
||||
// Try to connect to server to verify snapuserd server is started
|
||||
while (retry_count < MAX_CONNECT_RETRY_COUNT) {
|
||||
if (!ConnectToServer()) {
|
||||
retry_count++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
} else {
|
||||
close(sockfd_);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
LOG(ERROR) << "Failed to start snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
|
||||
int SnapuserdClient::StartSnapuserd() {
|
||||
if (StartSnapuserdaemon(GetSocketNameFirstStage()) < 0) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SnapuserdClient::InitializeSnapuserd(std::string cow_device, std::string backing_device) {
|
||||
int ret = 0;
|
||||
|
||||
if (!ConnectToServer()) {
|
||||
LOG(ERROR) << "Failed to connect to server ";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string msg = "start," + cow_device + "," + backing_device;
|
||||
|
||||
ret = Sendmsg(msg.c_str(), msg.size());
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string str = Receivemsg();
|
||||
|
||||
if (str.find("fail") != std::string::npos) {
|
||||
LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
|
||||
DisconnectFromServer();
|
||||
|
||||
LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Transition from first stage snapuserd daemon to second stage daemon involves
|
||||
* series of steps viz:
|
||||
*
|
||||
* 1: Create new dm-user devices - This is done by libsnapshot
|
||||
*
|
||||
* 2: Spawn the new snapuserd daemon - This is the second stage daemon which
|
||||
* will start the server but the dm-user misc devices is not binded yet.
|
||||
*
|
||||
* 3: Vector to this function contains pair of cow_device and source device.
|
||||
* Ex: {{system_cow,system_a}, {product_cow, product_a}, {vendor_cow,
|
||||
* vendor_a}}. This vector will be populated by the libsnapshot.
|
||||
*
|
||||
* 4: Initialize the Second stage daemon passing the information from the
|
||||
* vector. This will bind the daemon with dm-user misc device and will be ready
|
||||
* to serve the IO. Up until this point, first stage daemon is still active.
|
||||
* However, client library will mark the first stage daemon as passive and hence
|
||||
* all the control message from hereon will be sent to active second stage
|
||||
* daemon.
|
||||
*
|
||||
* 5: Create new dm-snapshot table. This is done by libsnapshot. When new table
|
||||
* is created, kernel will issue metadata read once again which will be served
|
||||
* by second stage daemon. However, any active IO will still be served by first
|
||||
* stage daemon.
|
||||
*
|
||||
* 6: Swap the snapshot table atomically - This is done by libsnapshot. Once
|
||||
* the swapping is done, all the IO will be served by second stage daemon.
|
||||
*
|
||||
* 7: Stop the first stage daemon. After this point second stage daemon is
|
||||
* completely active to serve the IO and merging process.
|
||||
*
|
||||
*/
|
||||
int SnapuserdClient::RestartSnapuserd(std::vector<std::pair<std::string, std::string>>& vec) {
|
||||
// Connect to first-stage daemon and send a terminate-request control
|
||||
// message. This will not terminate the daemon but will mark the daemon as
|
||||
// passive.
|
||||
if (!ConnectToServer()) {
|
||||
LOG(ERROR) << "Failed to connect to server ";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string msg = "terminate-request";
|
||||
|
||||
int sendRet = Sendmsg(msg.c_str(), msg.size());
|
||||
if (sendRet < 0) {
|
||||
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string str = Receivemsg();
|
||||
|
||||
if (str.find("fail") != std::string::npos) {
|
||||
LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
|
||||
return -1;
|
||||
}
|
||||
|
||||
CHECK(str.find("success") != std::string::npos);
|
||||
|
||||
DisconnectFromServer();
|
||||
|
||||
// Start the new daemon
|
||||
if (StartSnapuserdaemon(GetSocketNameSecondStage()) < 0) {
|
||||
LOG(ERROR) << "Failed to start new daemon at socket " << GetSocketNameSecondStage();
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Second stage Snapuserd daemon created successfully at socket "
|
||||
<< GetSocketNameSecondStage();
|
||||
CHECK(vec.size() % 2 == 0);
|
||||
|
||||
for (int i = 0; i < vec.size(); i++) {
|
||||
std::string& cow_device = vec[i].first;
|
||||
std::string& base_device = vec[i].second;
|
||||
|
||||
InitializeSnapuserd(cow_device, base_device);
|
||||
LOG(DEBUG) << "Daemon initialized with " << cow_device << " and " << base_device;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
53
fs_mgr/libsnapshot/snapuserd_daemon.cpp
Normal file
53
fs_mgr/libsnapshot/snapuserd_daemon.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <libsnapshot/snapuserd_daemon.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
int Daemon::StartServer(std::string socketname) {
|
||||
int ret;
|
||||
|
||||
ret = server_.Start(socketname);
|
||||
if (ret < 0) {
|
||||
LOG(ERROR) << "Snapuserd daemon failed to start...";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Daemon::Daemon() {
|
||||
is_running_ = true;
|
||||
// TODO: Mask other signals - Bug 168258493
|
||||
signal(SIGINT, Daemon::SignalHandler);
|
||||
signal(SIGTERM, Daemon::SignalHandler);
|
||||
}
|
||||
|
||||
bool Daemon::IsRunning() {
|
||||
return is_running_;
|
||||
}
|
||||
|
||||
void Daemon::Run() {
|
||||
while (IsRunning()) {
|
||||
if (server_.AcceptClient() == static_cast<int>(DaemonOperations::STOP)) {
|
||||
Daemon::Instance().is_running_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Daemon::SignalHandler(int signal) {
|
||||
LOG(DEBUG) << "Snapuserd received signal: " << signal;
|
||||
switch (signal) {
|
||||
case SIGINT:
|
||||
case SIGTERM: {
|
||||
Daemon::Instance().is_running_ = false;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG(ERROR) << "Received unknown signal " << signal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
215
fs_mgr/libsnapshot/snapuserd_server.cpp
Normal file
215
fs_mgr/libsnapshot/snapuserd_server.cpp
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
#include <android-base/logging.h>
|
||||
#include <libsnapshot/snapuserd.h>
|
||||
#include <libsnapshot/snapuserd_server.h>
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
||||
DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
|
||||
if (input == "start") return DaemonOperations::START;
|
||||
if (input == "stop") return DaemonOperations::STOP;
|
||||
if (input == "terminate-request") return DaemonOperations::TERMINATING;
|
||||
if (input == "query") return DaemonOperations::QUERY;
|
||||
|
||||
return DaemonOperations::INVALID;
|
||||
}
|
||||
|
||||
std::string SnapuserdServer::GetDaemonStatus() {
|
||||
std::string msg = "";
|
||||
|
||||
if (IsTerminating())
|
||||
msg = "passive";
|
||||
else
|
||||
msg = "active";
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
void SnapuserdServer::Parsemsg(std::string const& msg, const char delim,
|
||||
std::vector<std::string>& out) {
|
||||
std::stringstream ss(msg);
|
||||
std::string s;
|
||||
|
||||
while (std::getline(ss, s, delim)) {
|
||||
out.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
// new thread
|
||||
void SnapuserdServer::ThreadStart(std::string cow_device, std::string backing_device) {
|
||||
Snapuserd snapd(cow_device, backing_device);
|
||||
if (snapd.Init()) {
|
||||
PLOG(ERROR) << "Snapuserd: Init failed";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
while (StopRequested() == false) {
|
||||
int ret = snapd.Run();
|
||||
|
||||
if (ret == -ETIMEDOUT) continue;
|
||||
|
||||
if (ret < 0) {
|
||||
PLOG(ERROR) << "snapd.Run() failed..." << ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapuserdServer::ShutdownThreads() {
|
||||
StopThreads();
|
||||
|
||||
for (auto& client : clients_vec_) {
|
||||
auto& th = client->GetThreadHandler();
|
||||
|
||||
if (th->joinable()) th->join();
|
||||
}
|
||||
}
|
||||
|
||||
int SnapuserdServer::Sendmsg(int fd, char* msg, size_t size) {
|
||||
int ret = TEMP_FAILURE_RETRY(send(fd, (char*)msg, size, 0));
|
||||
if (ret < 0) {
|
||||
PLOG(ERROR) << "Snapuserd:server: send() failed";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret < size) {
|
||||
PLOG(ERROR) << "Partial data sent";
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string SnapuserdServer::Recvmsg(int fd, int* ret) {
|
||||
struct timeval tv;
|
||||
fd_set set;
|
||||
char msg[MAX_PACKET_SIZE];
|
||||
|
||||
tv.tv_sec = 2;
|
||||
tv.tv_usec = 0;
|
||||
FD_ZERO(&set);
|
||||
FD_SET(fd, &set);
|
||||
*ret = select(fd + 1, &set, NULL, NULL, &tv);
|
||||
if (*ret == -1) { // select failed
|
||||
return {};
|
||||
} else if (*ret == 0) { // timeout
|
||||
return {};
|
||||
} else {
|
||||
*ret = TEMP_FAILURE_RETRY(recv(fd, msg, MAX_PACKET_SIZE, 0));
|
||||
if (*ret < 0) {
|
||||
PLOG(ERROR) << "Snapuserd:server: recv failed";
|
||||
return {};
|
||||
} else if (*ret == 0) {
|
||||
LOG(DEBUG) << "Snapuserd client disconnected";
|
||||
return {};
|
||||
} else {
|
||||
std::string str(msg);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SnapuserdServer::Receivemsg(int fd) {
|
||||
char msg[MAX_PACKET_SIZE];
|
||||
std::unique_ptr<Client> newClient;
|
||||
int ret = 0;
|
||||
|
||||
while (1) {
|
||||
memset(msg, '\0', MAX_PACKET_SIZE);
|
||||
std::string str = Recvmsg(fd, &ret);
|
||||
|
||||
if (ret <= 0) {
|
||||
LOG(DEBUG) << "recv failed with ret: " << ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char delim = ',';
|
||||
|
||||
std::vector<std::string> out;
|
||||
Parsemsg(str, delim, out);
|
||||
DaemonOperations op = Resolveop(out[0]);
|
||||
memset(msg, '\0', MAX_PACKET_SIZE);
|
||||
|
||||
switch (op) {
|
||||
case DaemonOperations::START: {
|
||||
// Message format:
|
||||
// start,<cow_device_path>,<source_device_path>
|
||||
//
|
||||
// Start the new thread which binds to dm-user misc device
|
||||
newClient = std::make_unique<Client>();
|
||||
newClient->SetThreadHandler(
|
||||
std::bind(&SnapuserdServer::ThreadStart, this, out[1], out[2]));
|
||||
clients_vec_.push_back(std::move(newClient));
|
||||
sprintf(msg, "success");
|
||||
Sendmsg(fd, msg, MAX_PACKET_SIZE);
|
||||
return 0;
|
||||
}
|
||||
case DaemonOperations::STOP: {
|
||||
// Message format: stop
|
||||
//
|
||||
// Stop all the threads gracefully and then shutdown the
|
||||
// main thread
|
||||
ShutdownThreads();
|
||||
return static_cast<int>(DaemonOperations::STOP);
|
||||
}
|
||||
case DaemonOperations::TERMINATING: {
|
||||
// Message format: terminate-request
|
||||
//
|
||||
// This is invoked during transition. First stage
|
||||
// daemon will receive this request. First stage daemon
|
||||
// will be considered as a passive daemon from hereon.
|
||||
SetTerminating();
|
||||
sprintf(msg, "success");
|
||||
Sendmsg(fd, msg, MAX_PACKET_SIZE);
|
||||
return 0;
|
||||
}
|
||||
case DaemonOperations::QUERY: {
|
||||
// Message format: query
|
||||
//
|
||||
// As part of transition, Second stage daemon will be
|
||||
// created before terminating the first stage daemon. Hence,
|
||||
// for a brief period client may have to distiguish between
|
||||
// first stage daemon and second stage daemon.
|
||||
//
|
||||
// Second stage daemon is marked as active and hence will
|
||||
// be ready to receive control message.
|
||||
std::string dstr = GetDaemonStatus();
|
||||
memcpy(msg, dstr.c_str(), dstr.size());
|
||||
Sendmsg(fd, msg, MAX_PACKET_SIZE);
|
||||
if (dstr == "active")
|
||||
break;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
default: {
|
||||
sprintf(msg, "fail");
|
||||
Sendmsg(fd, msg, MAX_PACKET_SIZE);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int SnapuserdServer::Start(std::string socketname) {
|
||||
sockfd_.reset(socket_local_server(socketname.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
|
||||
SOCK_STREAM));
|
||||
if (sockfd_ < 0) {
|
||||
PLOG(ERROR) << "Failed to create server socket " << socketname;
|
||||
return -1;
|
||||
}
|
||||
|
||||
LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SnapuserdServer::AcceptClient() {
|
||||
int fd = accept(sockfd_.get(), NULL, NULL);
|
||||
if (fd < 0) {
|
||||
PLOG(ERROR) << "Socket accept failed: " << strerror(errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return Receivemsg(fd);
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
Loading…
Add table
Reference in a new issue