Merge "Adds license header"
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 046ea74..ddffef2 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -163,6 +163,38 @@
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 @@
"fs_mgr_defaults",
],
srcs: [
+ "snapuserd_server.cpp",
"snapuserd.cpp",
+ "snapuserd_daemon.cpp",
],
cflags: [
@@ -374,6 +408,7 @@
static_libs: [
"libbase",
"libbrotli",
+ "libcutils_sockets",
"liblog",
"libdm",
"libz",
@@ -513,6 +548,8 @@
"libbrotli",
"libgtest",
"libsnapshot_cow",
+ "libsnapshot_snapuserd",
+ "libcutils_sockets",
"libz",
],
header_libs: [
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 80acb4a..75e54f7 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -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 @@
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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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::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 @@
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 @@
CreateCowDevice(cow_system_);
CreateCowDevice(cow_product_);
- CreateSystemDmUser();
- CreateProductDmUser();
+ CreateSystemDmUser(cow_system_);
+ CreateProductDmUser(cow_product_);
StartSnapuserdDaemon();
@@ -335,11 +373,44 @@
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
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index e757579..6331edb 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -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
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
new file mode 100644
index 0000000..2d9d729
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -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
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
new file mode 100644
index 0000000..c0d3c5e
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
@@ -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
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
new file mode 100644
index 0000000..1a6ba8f
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_kernel.h
@@ -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
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
new file mode 100644
index 0000000..79b883a
--- /dev/null
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -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
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index d3f4f70..34481b7 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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;
}
- metadata_read_done_ = true;
-
- CHECK(read_size == BLOCK_SIZE);
- ret = ConstructKernelCowHeader();
- if (ret < 0) return ret;
+ } else if (chunk_vec_[chunk] == nullptr) {
+ ret = ReadDiskExceptions(chunk, read_size);
+ if (ret < 0) {
+ LOG(ERROR) << "ReadDiskExceptions failed";
+ 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;
}
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
new file mode 100644
index 0000000..b10de35
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -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
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
new file mode 100644
index 0000000..c1008b9
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
@@ -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
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
new file mode 100644
index 0000000..1e8b642
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -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
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 524b715..1f775db 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -55,6 +55,7 @@
name: "libcutils_sockets",
vendor_available: true,
recovery_available: true,
+ ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [