Loading fs_mgr/libsnapshot/Android.bp +37 −0 Original line number Diff line number Diff line Loading @@ -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"], Loading Loading @@ -363,7 +395,9 @@ cc_defaults { "fs_mgr_defaults", ], srcs: [ "snapuserd_server.cpp", "snapuserd.cpp", "snapuserd_daemon.cpp", ], cflags: [ Loading @@ -374,6 +408,7 @@ cc_defaults { static_libs: [ "libbase", "libbrotli", "libcutils_sockets", "liblog", "libdm", "libz", Loading Loading @@ -513,6 +548,8 @@ cc_test { "libbrotli", "libgtest", "libsnapshot_cow", "libsnapshot_snapuserd", "libcutils_sockets", "libz", ], header_libs: [ Loading fs_mgr/libsnapshot/cow_snapuserd_test.cpp +94 −23 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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_; Loading @@ -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() { Loading Loading @@ -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); } Loading Loading @@ -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 Loading @@ -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); Loading @@ -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 Loading @@ -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); Loading @@ -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() { Loading @@ -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_); Loading Loading @@ -326,8 +364,8 @@ TEST_F(SnapuserdTest, ReadWrite) { CreateCowDevice(cow_system_); CreateCowDevice(cow_product_); CreateSystemDmUser(); CreateProductDmUser(); CreateSystemDmUser(cow_system_); CreateProductDmUser(cow_product_); StartSnapuserdDaemon(); Loading @@ -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 Loading fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h +81 −72 Original line number Diff line number Diff line Loading @@ -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 Loading fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h 0 → 100644 +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 fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h 0 → 100644 +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
fs_mgr/libsnapshot/Android.bp +37 −0 Original line number Diff line number Diff line Loading @@ -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"], Loading Loading @@ -363,7 +395,9 @@ cc_defaults { "fs_mgr_defaults", ], srcs: [ "snapuserd_server.cpp", "snapuserd.cpp", "snapuserd_daemon.cpp", ], cflags: [ Loading @@ -374,6 +408,7 @@ cc_defaults { static_libs: [ "libbase", "libbrotli", "libcutils_sockets", "liblog", "libdm", "libz", Loading Loading @@ -513,6 +548,8 @@ cc_test { "libbrotli", "libgtest", "libsnapshot_cow", "libsnapshot_snapuserd", "libcutils_sockets", "libz", ], header_libs: [ Loading
fs_mgr/libsnapshot/cow_snapuserd_test.cpp +94 −23 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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_; Loading @@ -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() { Loading Loading @@ -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); } Loading Loading @@ -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 Loading @@ -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); Loading @@ -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 Loading @@ -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); Loading @@ -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() { Loading @@ -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_); Loading Loading @@ -326,8 +364,8 @@ TEST_F(SnapuserdTest, ReadWrite) { CreateCowDevice(cow_system_); CreateCowDevice(cow_product_); CreateSystemDmUser(); CreateProductDmUser(); CreateSystemDmUser(cow_system_); CreateProductDmUser(cow_product_); StartSnapuserdDaemon(); Loading @@ -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 Loading
fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h +81 −72 Original line number Diff line number Diff line Loading @@ -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 Loading
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h 0 → 100644 +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
fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h 0 → 100644 +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