Merge "iorap: Remove toggling iorapd on/off when using 'start' command."
diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp
index 5280121..b3e81b0 100644
--- a/debuggerd/crash_dump.cpp
+++ b/debuggerd/crash_dump.cpp
@@ -299,11 +299,8 @@
process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
*siginfo = crash_info->data.s.siginfo;
if (signal_has_si_addr(siginfo)) {
- // Make a copy of the ucontext field because otherwise it is not aligned enough (due to
- // being in a packed struct) and clang complains about that.
- ucontext_t ucontext = crash_info->data.s.ucontext;
process_info->has_fault_address = true;
- process_info->fault_address = get_fault_address(siginfo, &ucontext);
+ process_info->fault_address = reinterpret_cast<uintptr_t>(siginfo->si_addr);
}
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
&crash_info->data.s.ucontext));
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 121a074..85ffc98 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -167,7 +167,7 @@
* mutex is being held, so we don't want to use any libc functions that
* could allocate memory or hold a lock.
*/
-static void log_signal_summary(const siginfo_t* info, const ucontext_t* ucontext) {
+static void log_signal_summary(const siginfo_t* info) {
char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
strcpy(thread_name, "<name unknown>");
@@ -186,8 +186,7 @@
// Many signals don't have an address or sender.
char addr_desc[32] = ""; // ", fault addr 0x1234"
if (signal_has_si_addr(info)) {
- async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p",
- reinterpret_cast<void*>(get_fault_address(info, ucontext)));
+ async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
}
pid_t self_pid = __getpid();
char sender_desc[32] = {}; // " from pid 1234, uid 666"
@@ -544,7 +543,7 @@
return;
}
- log_signal_summary(info, ucontext);
+ log_signal_summary(info);
debugger_thread_info thread_info = {
.crashing_tid = __gettid(),
@@ -638,5 +637,11 @@
// Use the alternate signal stack if available so we can catch stack overflows.
action.sa_flags |= SA_ONSTACK;
+
+#define SA_EXPOSE_TAGBITS 0x00000800
+ // Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE
+ // faults.
+ action.sa_flags |= SA_EXPOSE_TAGBITS;
+
debuggerd_register_handlers(&action);
}
diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
index 76155b1..29fb9a4 100644
--- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
+++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h
@@ -91,6 +91,4 @@
const char* get_signame(const siginfo_t*);
const char* get_sigcode(const siginfo_t*);
-uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext);
-
#endif // _DEBUGGERD_UTILITY_H
diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp
index 4e6df09..d7067ca 100644
--- a/debuggerd/libdebuggerd/utility.cpp
+++ b/debuggerd/libdebuggerd/utility.cpp
@@ -451,40 +451,3 @@
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
}
}
-
-#if defined(__aarch64__)
-#define FAR_MAGIC 0x46415201
-
-struct far_context {
- struct _aarch64_ctx head;
- __u64 far;
-};
-#endif
-
-uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext) {
- (void)ucontext;
-#if defined(__aarch64__)
- // This relies on a kernel patch:
- // https://patchwork.kernel.org/patch/11435077/
- // that hasn't been accepted into the kernel yet. TODO(pcc): Update this to
- // use the official interface once it lands.
- auto* begin = reinterpret_cast<const char*>(ucontext->uc_mcontext.__reserved);
- auto* end = begin + sizeof(ucontext->uc_mcontext.__reserved);
- auto* ptr = begin;
- while (1) {
- auto* ctx = reinterpret_cast<const _aarch64_ctx*>(ptr);
- if (ctx->magic == 0) {
- break;
- }
- if (ctx->magic == FAR_MAGIC) {
- auto* far_ctx = reinterpret_cast<const far_context*>(ctx);
- return far_ctx->far;
- }
- ptr += ctx->size;
- if (ctx->size % sizeof(void*) != 0 || ptr < begin || ptr >= end) {
- break;
- }
- }
-#endif
- return reinterpret_cast<uintptr_t>(siginfo->si_addr);
-}
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index e52d8d5..388c296 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -1097,7 +1097,7 @@
static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) {
*partition_exists = false;
- *change = false;
+ if (change) *change = false;
auto images = IImageManager::Open("remount", 10s);
if (!images) {
@@ -1117,7 +1117,7 @@
return false;
}
- *change = true;
+ if (change) *change = true;
// Note: calling RemoveDisabledImages here ensures that we do not race with
// clean_scratch_files and accidentally try to map an image that will be
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 9a44020..4db6584 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -264,10 +264,7 @@
ASSERT_EQ(size_before, size_after);
struct stat buf;
- if (fstat(cow_->fd, &buf) < 0) {
- perror("Fails to determine size of cow image written");
- FAIL();
- }
+ ASSERT_GE(fstat(cow_->fd, &buf), 0) << strerror(errno);
ASSERT_EQ(buf.st_size, writer.GetCowSize());
}
@@ -408,7 +405,7 @@
// Get the last known good label
CowReader label_reader;
uint64_t label;
- ASSERT_TRUE(label_reader.Parse(cow_->fd));
+ ASSERT_TRUE(label_reader.Parse(cow_->fd, {5}));
ASSERT_TRUE(label_reader.GetLastLabel(&label));
ASSERT_EQ(label, 5);
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index 81d1eee..6b7ada5 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -31,14 +31,7 @@
namespace android {
namespace snapshot {
-CowReader::CowReader()
- : fd_(-1),
- header_(),
- footer_(),
- fd_size_(0),
- has_footer_(false),
- last_label_(0),
- has_last_label_(false) {}
+CowReader::CowReader() : fd_(-1), header_(), fd_size_(0) {}
static void SHA256(const void*, size_t, uint8_t[]) {
#if 0
@@ -49,12 +42,12 @@
#endif
}
-bool CowReader::Parse(android::base::unique_fd&& fd) {
+bool CowReader::Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label) {
owned_fd_ = std::move(fd);
- return Parse(android::base::borrowed_fd{owned_fd_});
+ return Parse(android::base::borrowed_fd{owned_fd_}, label);
}
-bool CowReader::Parse(android::base::borrowed_fd fd) {
+bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label) {
fd_ = fd;
auto pos = lseek(fd_.get(), 0, SEEK_END);
@@ -99,105 +92,107 @@
return false;
}
- auto footer_pos = lseek(fd_.get(), -header_.footer_size, SEEK_END);
- if (footer_pos != fd_size_ - header_.footer_size) {
- LOG(ERROR) << "Failed to read full footer!";
- return false;
- }
- if (!android::base::ReadFully(fd_, &footer_, sizeof(footer_))) {
- PLOG(ERROR) << "read footer failed";
- return false;
- }
- has_footer_ = (footer_.op.type == kCowFooterOp);
- return ParseOps();
+ return ParseOps(label);
}
-bool CowReader::ParseOps() {
+bool CowReader::ParseOps(std::optional<uint64_t> label) {
uint64_t pos = lseek(fd_.get(), sizeof(header_), SEEK_SET);
if (pos != sizeof(header_)) {
PLOG(ERROR) << "lseek ops failed";
return false;
}
- std::optional<uint64_t> next_last_label;
+
auto ops_buffer = std::make_shared<std::vector<CowOperation>>();
- if (has_footer_) ops_buffer->reserve(footer_.op.num_ops);
- uint64_t current_op_num = 0;
- // Look until we reach the last possible non-footer position.
- uint64_t last_pos = fd_size_ - (has_footer_ ? sizeof(footer_) : sizeof(CowOperation));
// Alternating op and data
- while (pos < last_pos) {
- ops_buffer->resize(current_op_num + 1);
- if (!android::base::ReadFully(fd_, ops_buffer->data() + current_op_num,
- sizeof(CowOperation))) {
+ while (true) {
+ ops_buffer->emplace_back();
+ if (!android::base::ReadFully(fd_, &ops_buffer->back(), sizeof(CowOperation))) {
PLOG(ERROR) << "read op failed";
return false;
}
- auto& current_op = ops_buffer->data()[current_op_num];
- pos = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
- if (pos == uint64_t(-1)) {
+
+ auto& current_op = ops_buffer->back();
+ off_t offs = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
+ if (offs < 0) {
PLOG(ERROR) << "lseek next op failed";
return false;
}
- current_op_num++;
- if (next_last_label) {
- last_label_ = next_last_label.value();
- has_last_label_ = true;
- }
+ pos = static_cast<uint64_t>(offs);
+
if (current_op.type == kCowLabelOp) {
- // If we don't have a footer, the last label may be incomplete.
- // If we see any operation after it, we can infer the flush finished.
- if (has_footer_) {
- has_last_label_ = true;
- last_label_ = current_op.source;
- } else {
- next_last_label = {current_op.source};
+ last_label_ = {current_op.source};
+
+ // If we reach the requested label, stop reading.
+ if (label && label.value() == current_op.source) {
+ break;
}
} else if (current_op.type == kCowFooterOp) {
- memcpy(&footer_.op, ¤t_op, sizeof(footer_.op));
- // we don't consider this an operation for the checksum
- current_op_num--;
- if (android::base::ReadFully(fd_, &footer_.data, sizeof(footer_.data))) {
- has_footer_ = true;
- if (next_last_label) {
- last_label_ = next_last_label.value();
- has_last_label_ = true;
- }
+ footer_.emplace();
+
+ CowFooter* footer = &footer_.value();
+ memcpy(&footer_->op, ¤t_op, sizeof(footer->op));
+
+ if (!android::base::ReadFully(fd_, &footer->data, sizeof(footer->data))) {
+ LOG(ERROR) << "Could not read COW footer";
+ return false;
}
+
+ // Drop the footer from the op stream.
+ ops_buffer->pop_back();
break;
}
}
+ // To successfully parse a COW file, we need either:
+ // (1) a label to read up to, and for that label to be found, or
+ // (2) a valid footer.
+ if (label) {
+ if (!last_label_) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << " while reading COW (no labels found)";
+ return false;
+ }
+ if (last_label_.value() != label.value()) {
+ LOG(ERROR) << "Did not find label " << label.value()
+ << ", last label=" << last_label_.value();
+ return false;
+ }
+ } else if (!footer_) {
+ LOG(ERROR) << "No COW footer found";
+ return false;
+ }
+
uint8_t csum[32];
memset(csum, 0, sizeof(uint8_t) * 32);
- if (has_footer_) {
- if (ops_buffer->size() != footer_.op.num_ops) {
+ if (footer_) {
+ if (ops_buffer->size() != footer_->op.num_ops) {
LOG(ERROR) << "num ops does not match";
return false;
}
- if (ops_buffer->size() * sizeof(CowOperation) != footer_.op.ops_size) {
+ if (ops_buffer->size() * sizeof(CowOperation) != footer_->op.ops_size) {
LOG(ERROR) << "ops size does not match ";
return false;
}
- SHA256(&footer_.op, sizeof(footer_.op), footer_.data.footer_checksum);
- if (memcmp(csum, footer_.data.ops_checksum, sizeof(csum)) != 0) {
+ SHA256(&footer_->op, sizeof(footer_->op), footer_->data.footer_checksum);
+ if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
LOG(ERROR) << "ops checksum does not match";
return false;
}
- SHA256(ops_buffer.get()->data(), footer_.op.ops_size, csum);
- if (memcmp(csum, footer_.data.ops_checksum, sizeof(csum)) != 0) {
+ SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
+ if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
LOG(ERROR) << "ops checksum does not match";
return false;
}
} else {
- LOG(INFO) << "No Footer, recovered data";
+ LOG(INFO) << "No COW Footer, recovered data";
}
if (header_.num_merge_ops > 0) {
uint64_t merge_ops = header_.num_merge_ops;
uint64_t metadata_ops = 0;
- current_op_num = 0;
+ uint64_t current_op_num = 0;
CHECK(ops_buffer->size() >= merge_ops);
while (merge_ops) {
@@ -223,14 +218,14 @@
}
bool CowReader::GetFooter(CowFooter* footer) {
- if (!has_footer_) return false;
- *footer = footer_;
+ if (!footer_) return false;
+ *footer = footer_.value();
return true;
}
bool CowReader::GetLastLabel(uint64_t* label) {
- if (!has_last_label_) return false;
- *label = last_label_;
+ if (!last_label_) return false;
+ *label = last_label_.value();
return true;
}
@@ -308,8 +303,8 @@
bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
// Validate the offset, taking care to acknowledge possible overflow of offset+len.
- if (offset < sizeof(header_) || offset >= fd_size_ - sizeof(footer_) || len >= fd_size_ ||
- offset + len > fd_size_ - sizeof(footer_)) {
+ if (offset < sizeof(header_) || offset >= fd_size_ - sizeof(CowFooter) || len >= fd_size_ ||
+ offset + len > fd_size_ - sizeof(CowFooter)) {
LOG(ERROR) << "invalid data offset: " << offset << ", " << len << " bytes";
return false;
}
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index 589ae30..5483fd0 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -108,6 +108,8 @@
std::unique_ptr<uint8_t[]> product_buffer_;
void Init();
+ void InitCowDevices();
+ void InitDaemon();
void CreateCowDevice(std::unique_ptr<TemporaryFile>& cow);
void CreateSystemDmUser(std::unique_ptr<TemporaryFile>& cow);
void CreateProductDmUser(std::unique_ptr<TemporaryFile>& cow);
@@ -238,12 +240,6 @@
system_device_name_.clear();
system_device_ctrl_name_.clear();
- // Create a COW device. Number of sectors is chosen random which can
- // hold at least 400MB of data
-
- int err = ioctl(sys_fd_.get(), BLKGETSIZE, &system_blksize_);
- ASSERT_GE(err, 0);
-
std::string str(cow->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
@@ -280,12 +276,6 @@
product_device_name_.clear();
product_device_ctrl_name_.clear();
- // Create a COW device. Number of sectors is chosen random which can
- // hold at least 400MB of data
-
- int err = ioctl(product_fd_.get(), BLKGETSIZE, &product_blksize_);
- ASSERT_GE(err, 0);
-
std::string str(cow->path);
std::size_t found = str.find_last_of("/\\");
ASSERT_NE(found, std::string::npos);
@@ -297,19 +287,29 @@
system(cmd.c_str());
}
+void SnapuserdTest::InitCowDevices() {
+ system_blksize_ = client_->InitDmUserCow(system_device_ctrl_name_, cow_system_->path,
+ system_a_loop_->device());
+ ASSERT_NE(system_blksize_, 0);
+
+ product_blksize_ = client_->InitDmUserCow(product_device_ctrl_name_, cow_product_->path,
+ product_a_loop_->device());
+ ASSERT_NE(product_blksize_, 0);
+}
+
+void SnapuserdTest::InitDaemon() {
+ bool ok = client_->AttachDmUser(system_device_ctrl_name_);
+ ASSERT_TRUE(ok);
+
+ ok = client_->AttachDmUser(product_device_ctrl_name_);
+ ASSERT_TRUE(ok);
+}
+
void SnapuserdTest::StartSnapuserdDaemon() {
ASSERT_TRUE(EnsureSnapuserdStarted());
client_ = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
ASSERT_NE(client_, nullptr);
-
- bool ok = client_->InitializeSnapuserd(cow_system_->path, system_a_loop_->device(),
- GetSystemControlPath());
- ASSERT_TRUE(ok);
-
- ok = client_->InitializeSnapuserd(cow_product_->path, product_a_loop_->device(),
- GetProductControlPath());
- ASSERT_TRUE(ok);
}
void SnapuserdTest::CreateSnapshotDevices() {
@@ -435,10 +435,13 @@
CreateCowDevice(cow_system_);
CreateCowDevice(cow_product_);
+ StartSnapuserdDaemon();
+ InitCowDevices();
+
CreateSystemDmUser(cow_system_);
CreateProductDmUser(cow_product_);
- StartSnapuserdDaemon();
+ InitDaemon();
CreateSnapshotDevices();
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index dcf259c..957ba35 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -122,6 +122,13 @@
is_dev_null_ = true;
} else {
fd_ = fd;
+
+ struct stat stat;
+ if (fstat(fd.get(), &stat) < 0) {
+ PLOG(ERROR) << "fstat failed";
+ return false;
+ }
+ is_block_device_ = S_ISBLK(stat.st_mode);
}
return true;
}
@@ -184,12 +191,10 @@
bool CowWriter::OpenForAppend(uint64_t label) {
auto reader = std::make_unique<CowReader>();
std::queue<CowOperation> toAdd;
- bool found_label = false;
- if (!reader->Parse(fd_) || !reader->GetHeader(&header_)) {
+ if (!reader->Parse(fd_, {label}) || !reader->GetHeader(&header_)) {
return false;
}
- reader->GetFooter(&footer_);
options_.block_size = header_.block_size;
@@ -199,30 +204,19 @@
ops_.resize(0);
auto iter = reader->GetOpIter();
- while (!iter->Done() && !found_label) {
- const CowOperation& op = iter->Get();
-
- if (op.type == kCowFooterOp) break;
- if (op.type == kCowLabelOp && op.source == label) found_label = true;
- AddOperation(op);
-
+ while (!iter->Done()) {
+ AddOperation(iter->Get());
iter->Next();
}
- if (!found_label) {
- LOG(ERROR) << "Failed to find last label";
- return false;
- }
-
// Free reader so we own the descriptor position again.
reader = nullptr;
- // Position for new writing
- if (ftruncate(fd_.get(), next_op_pos_) != 0) {
- PLOG(ERROR) << "Failed to trim file";
+ // Remove excess data
+ if (!Truncate(next_op_pos_)) {
return false;
}
- if (lseek(fd_.get(), 0, SEEK_END) < 0) {
+ if (lseek(fd_.get(), next_op_pos_, SEEK_SET) < 0) {
PLOG(ERROR) << "lseek failed";
return false;
}
@@ -445,5 +439,16 @@
return Sync();
}
+bool CowWriter::Truncate(off_t length) {
+ if (is_dev_null_ || is_block_device_) {
+ return true;
+ }
+ if (ftruncate(fd_.get(), length) < 0) {
+ PLOG(ERROR) << "Failed to truncate.";
+ return false;
+ }
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.cpp b/fs_mgr/libsnapshot/device_info.cpp
index 0e90100..8770255 100644
--- a/fs_mgr/libsnapshot/device_info.cpp
+++ b/fs_mgr/libsnapshot/device_info.cpp
@@ -120,5 +120,9 @@
#endif
}
+std::string DeviceInfo::GetSnapuserdFirstStagePidVar() const {
+ return kSnapuserdFirstStagePidVar;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/device_info.h b/fs_mgr/libsnapshot/device_info.h
index d8d3d91..1f08860 100644
--- a/fs_mgr/libsnapshot/device_info.h
+++ b/fs_mgr/libsnapshot/device_info.h
@@ -39,6 +39,7 @@
bool SetBootControlMergeStatus(MergeStatus status) override;
bool SetSlotAsUnbootable(unsigned int slot) override;
bool IsRecovery() const override;
+ std::string GetSnapuserdFirstStagePidVar() const override;
private:
bool EnsureBootHal();
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index ad6b008..be69225 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -18,6 +18,7 @@
#include <functional>
#include <memory>
+#include <optional>
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_format.h>
@@ -116,8 +117,10 @@
public:
CowReader();
- bool Parse(android::base::unique_fd&& fd);
- bool Parse(android::base::borrowed_fd fd);
+ // Parse the COW, optionally, up to the given label. If no label is
+ // specified, the COW must have an intact footer.
+ bool Parse(android::base::unique_fd&& fd, std::optional<uint64_t> label = {});
+ bool Parse(android::base::borrowed_fd fd, std::optional<uint64_t> label = {});
bool GetHeader(CowHeader* header) override;
bool GetFooter(CowFooter* footer) override;
@@ -138,16 +141,14 @@
void UpdateMergeProgress(uint64_t merge_ops) { header_.num_merge_ops += merge_ops; }
private:
- bool ParseOps();
+ bool ParseOps(std::optional<uint64_t> label);
android::base::unique_fd owned_fd_;
android::base::borrowed_fd fd_;
CowHeader header_;
- CowFooter footer_;
+ std::optional<CowFooter> footer_;
uint64_t fd_size_;
- bool has_footer_;
- uint64_t last_label_;
- bool has_last_label_;
+ std::optional<uint64_t> last_label_;
std::shared_ptr<std::vector<CowOperation>> ops_;
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index d0a861b..e9320b0 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -123,6 +123,7 @@
bool SetFd(android::base::borrowed_fd fd);
bool Sync();
+ bool Truncate(off_t length);
private:
android::base::unique_fd owned_fd_;
@@ -133,6 +134,7 @@
uint64_t next_op_pos_ = 0;
bool is_dev_null_ = false;
bool merge_in_progress_ = false;
+ bool is_block_device_ = false;
// :TODO: this is not efficient, but stringstream ubsan aborts because some
// bytes overflow a signed char.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
index ef9d648..d5c263d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_device_info.h
@@ -32,6 +32,7 @@
MOCK_METHOD(bool, SetBootControlMergeStatus, (MergeStatus status), (override));
MOCK_METHOD(bool, SetSlotAsUnbootable, (unsigned int slot), (override));
MOCK_METHOD(bool, IsRecovery, (), (const, override));
+ MOCK_METHOD(std::string, GetSnapuserdFirstStagePidVar, (), (const, override));
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 8bed1b9..351dce7 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -107,6 +107,7 @@
virtual bool SetSlotAsUnbootable(unsigned int slot) = 0;
virtual bool IsRecovery() const = 0;
virtual bool IsTestDevice() const { return false; }
+ virtual std::string GetSnapuserdFirstStagePidVar() const = 0;
};
virtual ~ISnapshotManager() = default;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index 80cd9c7..aa7dc40 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -61,23 +61,22 @@
class Snapuserd final {
public:
- Snapuserd(const std::string& in_cow_device, const std::string& in_backing_store_device,
- const std::string& in_control_device)
- : cow_device_(in_cow_device),
- backing_store_device_(in_backing_store_device),
- control_device_(in_control_device),
- metadata_read_done_(false) {}
-
- bool Init();
+ Snapuserd(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device);
+ bool InitBackingAndControlDevice();
+ bool InitCowDevice();
int Run();
const std::string& GetControlDevicePath() { return control_device_; }
+ const std::string& GetMiscName() { return misc_name_; }
+ uint64_t GetNumSectors() { return num_sectors_; }
+ bool IsAttached() const { return ctrl_fd_ >= 0; }
private:
int ReadDmUserHeader();
bool ReadDmUserPayload(void* buffer, size_t size);
int WriteDmUserPayload(size_t size);
int ConstructKernelCowHeader();
- int ReadMetadata();
+ bool ReadMetadata();
int ZerofillDiskExceptions(size_t read_size);
int ReadDiskExceptions(chunk_t chunk, size_t size);
int ReadData(chunk_t chunk, size_t size);
@@ -94,16 +93,20 @@
int unmerged_exceptions);
bool AdvanceMergedOps(int merged_ops_cur_iter);
bool ProcessMergeComplete(chunk_t chunk, void* buffer);
+ sector_t ChunkToSector(chunk_t chunk) { return chunk << CHUNK_SHIFT; }
+ chunk_t SectorToChunk(sector_t sector) { return sector >> CHUNK_SHIFT; }
std::string cow_device_;
std::string backing_store_device_;
std::string control_device_;
+ std::string misc_name_;
unique_fd cow_fd_;
unique_fd backing_store_fd_;
unique_fd ctrl_fd_;
uint32_t exceptions_per_area_;
+ uint64_t num_sectors_;
std::unique_ptr<ICowOpIter> cowop_iter_;
std::unique_ptr<ICowOpReverseIter> cowop_riter_;
@@ -118,7 +121,7 @@
// Value - cow operation
std::unordered_map<chunk_t, const CowOperation*> chunk_map_;
- bool metadata_read_done_;
+ bool metadata_read_done_ = false;
BufferSink bufsink_;
};
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index aaec229..0e9ba9e 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -58,9 +58,19 @@
std::chrono::milliseconds timeout_ms);
bool StopSnapuserd();
- int RestartSnapuserd(std::vector<std::vector<std::string>>& vec);
- bool InitializeSnapuserd(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device);
+
+ // Initializing a snapuserd handler is a three-step process:
+ //
+ // 1. Client invokes InitDmUserCow. This creates the snapuserd handler and validates the
+ // COW. The number of sectors required for the dm-user target is returned.
+ // 2. Client creates the device-mapper device with the dm-user target.
+ // 3. Client calls AttachControlDevice.
+ //
+ // The misc_name must be the "misc_name" given to dm-user in step 2.
+ //
+ uint64_t InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device);
+ bool AttachDmUser(const std::string& misc_name);
// Wait for snapuserd to disassociate with a dm-user control device. This
// must ONLY be called if the control device has already been deleted.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
index 181ee33..cadfd71 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -36,6 +36,7 @@
static constexpr uint32_t MAX_PACKET_SIZE = 512;
enum class DaemonOperations {
+ INIT,
START,
QUERY,
STOP,
@@ -55,7 +56,7 @@
const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
std::thread& thread() { return thread_; }
- const std::string& GetControlDevice() const;
+ const std::string& GetMiscName() const;
};
class Stoppable {
@@ -84,7 +85,9 @@
std::vector<struct pollfd> watched_fds_;
std::mutex lock_;
- std::vector<std::unique_ptr<DmUserHandler>> dm_users_;
+
+ using HandlerList = std::vector<std::shared_ptr<DmUserHandler>>;
+ HandlerList dm_users_;
void AddWatchedFd(android::base::borrowed_fd fd);
void AcceptClient();
@@ -94,7 +97,7 @@
bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
void ShutdownThreads();
- bool WaitForDelete(const std::string& control_device);
+ bool RemoveHandler(const std::string& control_device, bool wait);
DaemonOperations Resolveop(std::string& input);
std::string GetDaemonStatus();
void Parsemsg(std::string const& msg, const char delim, std::vector<std::string>& out);
@@ -102,11 +105,11 @@
void SetTerminating() { terminating_ = true; }
bool IsTerminating() { return terminating_; }
- void RunThread(DmUserHandler* handler);
+ void RunThread(std::shared_ptr<DmUserHandler> handler);
- // Remove a DmUserHandler from dm_users_, searching by its control device.
- // If none is found, return nullptr.
- std::unique_ptr<DmUserHandler> RemoveHandler(const std::string& control_device);
+ // Find a DmUserHandler within a lock.
+ HandlerList::iterator FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::string& misc_name);
public:
SnapuserdServer() { terminating_ = false; }
diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
index 7aef086..1d1510a 100644
--- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
+++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h
@@ -96,6 +96,7 @@
return true;
}
bool IsTestDevice() const override { return true; }
+ std::string GetSnapuserdFirstStagePidVar() const override { return {}; }
bool IsSlotUnbootable(uint32_t slot) { return unbootable_slots_.count(slot) != 0; }
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 793680b..6595707 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -386,24 +386,6 @@
auto& dm = DeviceMapper::Instance();
- // Use the size of the base device for the COW device. It doesn't really
- // matter, it just needs to look similar enough so the kernel doesn't complain
- // about alignment or being too small.
- uint64_t base_sectors = 0;
- {
- unique_fd fd(open(base_device.c_str(), O_RDONLY | O_CLOEXEC));
- if (fd < 0) {
- PLOG(ERROR) << "open failed: " << base_device;
- return false;
- }
- auto dev_size = get_block_device_size(fd);
- if (!dev_size) {
- PLOG(ERROR) << "Could not determine block device size: " << base_device;
- return false;
- }
- base_sectors = dev_size / kSectorSize;
- }
-
// Use an extra decoration for first-stage init, so we can transition
// to a new table entry in second-stage.
std::string misc_name = name;
@@ -411,18 +393,29 @@
misc_name += "-init";
}
+ if (!EnsureSnapuserdConnected()) {
+ return false;
+ }
+
+ uint64_t base_sectors = snapuserd_client_->InitDmUserCow(misc_name, cow_file, base_device);
+ if (base_sectors == 0) {
+ LOG(ERROR) << "Failed to retrieve base_sectors from Snapuserd";
+ return false;
+ }
+
DmTable table;
table.Emplace<DmTargetUser>(0, base_sectors, misc_name);
if (!dm.CreateDevice(name, table, path, timeout_ms)) {
return false;
}
- if (!EnsureSnapuserdConnected()) {
+ auto control_device = "/dev/dm-user/" + misc_name;
+ if (!android::fs_mgr::WaitForFile(control_device, timeout_ms)) {
+ LOG(ERROR) << "Timed out waiting for dm-user misc device: " << control_device;
return false;
}
- auto control_device = "/dev/dm-user/" + misc_name;
- return snapuserd_client_->InitializeSnapuserd(cow_file, base_device, control_device);
+ return snapuserd_client_->AttachDmUser(misc_name);
}
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
@@ -1322,28 +1315,34 @@
size_t num_cows = 0;
size_t ok_cows = 0;
for (const auto& snapshot : snapshots) {
- std::string cow_name = GetDmUserCowName(snapshot);
- if (dm.GetState(cow_name) == DmDeviceState::INVALID) {
+ std::string user_cow_name = GetDmUserCowName(snapshot);
+ if (dm.GetState(user_cow_name) == DmDeviceState::INVALID) {
continue;
}
DeviceMapper::TargetInfo target;
- if (!GetSingleTarget(cow_name, TableQuery::Table, &target)) {
+ if (!GetSingleTarget(user_cow_name, TableQuery::Table, &target)) {
continue;
}
auto target_type = DeviceMapper::GetTargetType(target.spec);
if (target_type != "user") {
- LOG(ERROR) << "Unexpected target type for " << cow_name << ": " << target_type;
+ LOG(ERROR) << "Unexpected target type for " << user_cow_name << ": " << target_type;
continue;
}
num_cows++;
+ SnapshotStatus snapshot_status;
+ if (!ReadSnapshotStatus(lock.get(), snapshot, &snapshot_status)) {
+ LOG(ERROR) << "Unable to read snapshot status: " << snapshot;
+ continue;
+ }
+
DmTable table;
- table.Emplace<DmTargetUser>(0, target.spec.length, cow_name);
- if (!dm.LoadTableAndActivate(cow_name, table)) {
- LOG(ERROR) << "Unable to swap tables for " << cow_name;
+ table.Emplace<DmTargetUser>(0, target.spec.length, user_cow_name);
+ if (!dm.LoadTableAndActivate(user_cow_name, table)) {
+ LOG(ERROR) << "Unable to swap tables for " << user_cow_name;
continue;
}
@@ -1353,23 +1352,42 @@
continue;
}
- std::string cow_device;
- if (!dm.GetDmDevicePathByName(GetCowName(snapshot), &cow_device)) {
- LOG(ERROR) << "Could not get device path for " << GetCowName(snapshot);
+ // If no partition was created (the COW exists entirely on /data), the
+ // device-mapper layering is different than if we had a partition.
+ std::string cow_image_name;
+ if (snapshot_status.cow_partition_size() == 0) {
+ cow_image_name = GetCowImageDeviceName(snapshot);
+ } else {
+ cow_image_name = GetCowName(snapshot);
+ }
+
+ std::string cow_image_device;
+ if (!dm.GetDmDevicePathByName(cow_image_name, &cow_image_device)) {
+ LOG(ERROR) << "Could not get device path for " << cow_image_name;
continue;
}
// Wait for ueventd to acknowledge and create the control device node.
- std::string control_device = "/dev/dm-user/" + cow_name;
+ std::string control_device = "/dev/dm-user/" + user_cow_name;
if (!android::fs_mgr::WaitForFile(control_device, 10s)) {
LOG(ERROR) << "Could not find control device: " << control_device;
continue;
}
- if (!snapuserd_client_->InitializeSnapuserd(cow_device, backing_device, control_device)) {
+ uint64_t base_sectors =
+ snapuserd_client_->InitDmUserCow(user_cow_name, cow_image_device, backing_device);
+ if (base_sectors == 0) {
+ // Unrecoverable as metadata reads from cow device failed
+ LOG(FATAL) << "Failed to retrieve base_sectors from Snapuserd";
+ return false;
+ }
+
+ CHECK(base_sectors == target.spec.length);
+
+ if (!snapuserd_client_->AttachDmUser(user_cow_name)) {
// This error is unrecoverable. We cannot proceed because reads to
// the underlying device will fail.
- LOG(FATAL) << "Could not initialize snapuserd for " << cow_name;
+ LOG(FATAL) << "Could not initialize snapuserd for " << user_cow_name;
return false;
}
@@ -1381,8 +1399,13 @@
return false;
}
+ std::string pid_var = device_->GetSnapuserdFirstStagePidVar();
+ if (pid_var.empty()) {
+ return true;
+ }
+
int pid;
- const char* pid_str = getenv(kSnapuserdFirstStagePidVar);
+ const char* pid_str = getenv(pid_var.c_str());
if (pid_str && android::base::ParseInt(pid_str, &pid)) {
if (kill(pid, SIGTERM) < 0 && errno != ESRCH) {
LOG(ERROR) << "kill snapuserd failed";
@@ -2051,13 +2074,13 @@
return false;
}
- auto control_device = "/dev/dm-user/" + dm_user_name;
- if (!snapuserd_client_->WaitForDeviceDelete(control_device)) {
+ if (!snapuserd_client_->WaitForDeviceDelete(dm_user_name)) {
LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
return false;
}
// Ensure the control device is gone so we don't run into ABA problems.
+ auto control_device = "/dev/dm-user/" + dm_user_name;
if (!android::fs_mgr::WaitForFileDeleted(control_device, 10s)) {
LOG(ERROR) << "Timed out waiting for " << control_device << " to unlink";
return false;
diff --git a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
index 5319e69..092ee0b 100644
--- a/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
+++ b/fs_mgr/libsnapshot/snapshot_fuzz_utils.h
@@ -124,6 +124,7 @@
return data_->allow_set_slot_as_unbootable();
}
bool IsRecovery() const override { return data_->is_recovery(); }
+ std::string GetSnapuserdFirstStagePidVar() const override { return {}; }
void SwitchSlot() { switched_slot_ = !switched_slot_; }
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index c44f63c..80b5734 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -66,6 +66,14 @@
return header;
}
+Snapuserd::Snapuserd(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device) {
+ misc_name_ = misc_name;
+ cow_device_ = cow_device;
+ backing_store_device_ = backing_device;
+ control_device_ = "/dev/dm-user/" + misc_name;
+}
+
// Construct kernel COW header in memory
// This header will be in sector 0. The IO
// request will always be 4k. After constructing
@@ -505,7 +513,7 @@
* exceptions_per_area_
* 12: Kernel will stop issuing metadata IO request when new-chunk ID is 0.
*/
-int Snapuserd::ReadMetadata() {
+bool Snapuserd::ReadMetadata() {
reader_ = std::make_unique<CowReader>();
CowHeader header;
CowOptions options;
@@ -516,12 +524,12 @@
if (!reader_->Parse(cow_fd_)) {
LOG(ERROR) << "Failed to parse";
- return 1;
+ return false;
}
if (!reader_->GetHeader(&header)) {
LOG(ERROR) << "Failed to get header";
- return 1;
+ return false;
}
CHECK(header.block_size == BLOCK_SIZE);
@@ -563,7 +571,7 @@
if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
cow_op->type == kCowCopyOp)) {
LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- return 1;
+ return false;
}
metadata_found = true;
@@ -609,16 +617,24 @@
}
// Partially filled area or there is no metadata
+ // If there is no metadata, fill with zero so that kernel
+ // is aware that merge is completed.
if (num_ops || !metadata_found) {
vec_.push_back(std::move(de_ptr));
LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
<< "Areas : " << vec_.size();
}
+ LOG(DEBUG) << "ReadMetadata() completed. chunk_id: " << next_free
+ << "Num Sector: " << ChunkToSector(next_free);
+
// Initialize the iterator for merging
cowop_iter_ = reader_->GetOpIter();
- return 0;
+ // Total number of sectors required for creating dm-user device
+ num_sectors_ = ChunkToSector(next_free);
+ metadata_read_done_ = true;
+ return true;
}
void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
@@ -664,29 +680,13 @@
return true;
}
-bool Snapuserd::Init() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
+bool Snapuserd::InitCowDevice() {
cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
if (cow_fd_ < 0) {
PLOG(ERROR) << "Open Failed: " << cow_device_;
return false;
}
- std::string control_path = GetControlDevicePath();
-
- LOG(DEBUG) << "Opening control device " << control_path;
-
- ctrl_fd_.reset(open(control_path.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- PLOG(ERROR) << "Unable to open " << control_path;
- return false;
- }
-
// 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
@@ -694,6 +694,22 @@
size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_SIZE;
bufsink_.Initialize(buf_size);
+ return ReadMetadata();
+}
+
+bool Snapuserd::InitBackingAndControlDevice() {
+ backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
+ if (backing_store_fd_ < 0) {
+ PLOG(ERROR) << "Open Failed: " << backing_store_device_;
+ return false;
+ }
+
+ ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
+ if (ctrl_fd_ < 0) {
+ PLOG(ERROR) << "Unable to open " << control_device_;
+ return false;
+ }
+
return true;
}
@@ -729,15 +745,7 @@
// 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(metadata_read_done_ == true);
CHECK(read_size == BLOCK_SIZE);
ret = ConstructKernelCowHeader();
if (ret < 0) return ret;
@@ -747,7 +755,7 @@
// 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);
+ chunk_t chunk = SectorToChunk(header->sector);
if (chunk_map_.find(chunk) == chunk_map_.end()) {
ret = ReadDiskExceptions(chunk, read_size);
@@ -789,7 +797,7 @@
size_t read_size = std::min(PAYLOAD_SIZE, remaining_size);
CHECK(read_size == BLOCK_SIZE);
CHECK(header->sector > 0);
- chunk_t chunk = (header->sector >> CHUNK_SHIFT);
+ chunk_t chunk = SectorToChunk(header->sector);
CHECK(chunk_map_.find(chunk) == chunk_map_.end());
void* buffer = bufsink_.GetPayloadBuffer(read_size);
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
index 5650139..a5d2061 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -27,9 +27,12 @@
#include <unistd.h>
#include <chrono>
+#include <sstream>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <android-base/properties.h>
+#include <android-base/strings.h>
#include <libsnapshot/snapuserd_client.h>
namespace android {
@@ -114,7 +117,7 @@
std::string str = Receivemsg();
// If the daemon is passive then fallback to secondary active daemon. Daemon
- // is passive during transition phase. Please see RestartSnapuserd()
+ // is passive during transition phase.
if (str.find("passive") != std::string::npos) {
LOG(ERROR) << "Snapuserd is terminating";
return false;
@@ -180,10 +183,8 @@
return true;
}
-bool SnapuserdClient::InitializeSnapuserd(const std::string& cow_device,
- const std::string& backing_device,
- const std::string& control_device) {
- std::string msg = "start," + cow_device + "," + backing_device + "," + control_device;
+bool SnapuserdClient::AttachDmUser(const std::string& misc_name) {
+ std::string msg = "start," + misc_name;
if (!Sendmsg(msg)) {
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
return false;
@@ -199,77 +200,33 @@
return true;
}
-/*
- * 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::vector<std::string>>& vec) {
- std::string msg = "terminate-request";
+uint64_t SnapuserdClient::InitDmUserCow(const std::string& misc_name, const std::string& cow_device,
+ const std::string& backing_device) {
+ std::vector<std::string> parts = {"init", misc_name, cow_device, backing_device};
+ std::string msg = android::base::Join(parts, ",");
if (!Sendmsg(msg)) {
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
- return -1;
+ return 0;
}
std::string str = Receivemsg();
- if (str.find("fail") != std::string::npos) {
- LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
- return -1;
+ std::vector<std::string> input = android::base::Split(str, ",");
+
+ if (input.empty() || input[0] != "success") {
+ LOG(ERROR) << "Failed to receive number of sectors for " << msg << " from snapuserd daemon";
+ return 0;
}
- CHECK(str.find("success") != std::string::npos);
+ LOG(DEBUG) << "Snapuserd daemon COW device initialized: " << cow_device
+ << " Num-sectors: " << input[1];
- // Start the new daemon
- if (!EnsureSnapuserdStarted()) {
- LOG(ERROR) << "Failed to start new daemon";
- return -1;
+ uint64_t num_sectors = 0;
+ if (!android::base::ParseUint(input[1], &num_sectors)) {
+ LOG(ERROR) << "Failed to parse input string to sectors";
+ return 0;
}
-
- LOG(DEBUG) << "Second stage Snapuserd daemon created successfully";
-
- // Vector contains all the device information to be passed to the new
- // daemon. Note that the caller can choose to initialize separately
- // by calling InitializeSnapuserd() API as well. In that case, vector
- // should be empty
- for (int i = 0; i < vec.size(); i++) {
- std::string& cow_device = vec[i][0];
- std::string& base_device = vec[i][1];
- std::string& control_device = vec[i][2];
-
- InitializeSnapuserd(cow_device, base_device, control_device);
- LOG(DEBUG) << "Daemon initialized with " << cow_device << ", " << base_device << " and "
- << control_device;
- }
-
- return 0;
+ return num_sectors;
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 6a89218..9d065b6 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -33,6 +33,7 @@
namespace snapshot {
DaemonOperations SnapuserdServer::Resolveop(std::string& input) {
+ if (input == "init") return DaemonOperations::INIT;
if (input == "start") return DaemonOperations::START;
if (input == "stop") return DaemonOperations::STOP;
if (input == "query") return DaemonOperations::QUERY;
@@ -73,7 +74,7 @@
StopThreads();
// Acquire the thread list within the lock.
- std::vector<std::unique_ptr<DmUserHandler>> dm_users;
+ std::vector<std::shared_ptr<DmUserHandler>> dm_users;
{
std::lock_guard<std::mutex> guard(lock_);
dm_users = std::move(dm_users_);
@@ -86,8 +87,8 @@
}
}
-const std::string& DmUserHandler::GetControlDevice() const {
- return snapuserd_->GetControlDevicePath();
+const std::string& DmUserHandler::GetMiscName() const {
+ return snapuserd_->GetMiscName();
}
bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
@@ -123,30 +124,61 @@
DaemonOperations op = Resolveop(out[0]);
switch (op) {
- case DaemonOperations::START: {
+ case DaemonOperations::INIT: {
// Message format:
- // start,<cow_device_path>,<source_device_path>,<control_device>
+ // init,<misc_name>,<cow_device_path>,<control_device>
//
- // Start the new thread which binds to dm-user misc device
+ // Reads the metadata and send the number of sectors
if (out.size() != 4) {
- LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+ LOG(ERROR) << "Malformed init message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
auto snapuserd = std::make_unique<Snapuserd>(out[1], out[2], out[3]);
- if (!snapuserd->Init()) {
+ if (!snapuserd->InitCowDevice()) {
LOG(ERROR) << "Failed to initialize Snapuserd";
return Sendmsg(fd, "fail");
}
+ std::string retval = "success," + std::to_string(snapuserd->GetNumSectors());
+
auto handler = std::make_unique<DmUserHandler>(std::move(snapuserd));
{
std::lock_guard<std::mutex> lock(lock_);
-
- handler->thread() =
- std::thread(std::bind(&SnapuserdServer::RunThread, this, handler.get()));
+ if (FindHandler(&lock, out[1]) != dm_users_.end()) {
+ LOG(ERROR) << "Handler already exists: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
dm_users_.push_back(std::move(handler));
}
+
+ return Sendmsg(fd, retval);
+ }
+ case DaemonOperations::START: {
+ // Message format:
+ // start,<misc_name>
+ //
+ // Start the new thread which binds to dm-user misc device
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+
+ std::lock_guard<std::mutex> lock(lock_);
+ auto iter = FindHandler(&lock, out[1]);
+ if (iter == dm_users_.end()) {
+ LOG(ERROR) << "Could not find handler: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
+ if ((*iter)->snapuserd()->IsAttached()) {
+ LOG(ERROR) << "Tried to re-attach control device: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
+ if (!((*iter)->snapuserd()->InitBackingAndControlDevice())) {
+ LOG(ERROR) << "Failed to initialize control device: " << out[1];
+ return Sendmsg(fd, "fail");
+ }
+ (*iter)->thread() = std::thread(std::bind(&SnapuserdServer::RunThread, this, *iter));
return Sendmsg(fd, "success");
}
case DaemonOperations::STOP: {
@@ -172,12 +204,12 @@
}
case DaemonOperations::DELETE: {
// Message format:
- // delete,<cow_device_path>
+ // delete,<misc_name>
if (out.size() != 2) {
LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
return Sendmsg(fd, "fail");
}
- if (!WaitForDelete(out[1])) {
+ if (!RemoveHandler(out[1], true)) {
return Sendmsg(fd, "fail");
}
return Sendmsg(fd, "success");
@@ -190,8 +222,8 @@
}
}
-void SnapuserdServer::RunThread(DmUserHandler* handler) {
- LOG(INFO) << "Entering thread for handler: " << handler->GetControlDevice();
+void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> handler) {
+ LOG(INFO) << "Entering thread for handler: " << handler->GetMiscName();
while (!StopRequested()) {
if (handler->snapuserd()->Run() < 0) {
@@ -200,15 +232,11 @@
}
}
- LOG(INFO) << "Exiting thread for handler: " << handler->GetControlDevice();
+ LOG(INFO) << "Exiting thread for handler: " << handler->GetMiscName();
- if (auto client = RemoveHandler(handler->GetControlDevice())) {
- // The main thread did not receive a WaitForDelete request for this
- // control device. Since we transferred ownership within the lock,
- // we know join() was never called, and will never be called. We can
- // safely detach here.
- client->thread().detach();
- }
+ // If the main thread called /emoveHandler, the handler was already removed
+ // from within the lock, and calling RemoveHandler again has no effect.
+ RemoveHandler(handler->GetMiscName(), false);
}
bool SnapuserdServer::Start(const std::string& socketname) {
@@ -301,34 +329,37 @@
SetTerminating();
}
-std::unique_ptr<DmUserHandler> SnapuserdServer::RemoveHandler(const std::string& control_device) {
- std::unique_ptr<DmUserHandler> client;
- {
- std::lock_guard<std::mutex> lock(lock_);
- auto iter = dm_users_.begin();
- while (iter != dm_users_.end()) {
- if ((*iter)->GetControlDevice() == control_device) {
- client = std::move(*iter);
- iter = dm_users_.erase(iter);
- break;
- }
- iter++;
+auto SnapuserdServer::FindHandler(std::lock_guard<std::mutex>* proof_of_lock,
+ const std::string& misc_name) -> HandlerList::iterator {
+ CHECK(proof_of_lock);
+
+ for (auto iter = dm_users_.begin(); iter != dm_users_.end(); iter++) {
+ if ((*iter)->GetMiscName() == misc_name) {
+ return iter;
}
}
- return client;
+ return dm_users_.end();
}
-bool SnapuserdServer::WaitForDelete(const std::string& control_device) {
- auto client = RemoveHandler(control_device);
+bool SnapuserdServer::RemoveHandler(const std::string& misc_name, bool wait) {
+ std::shared_ptr<DmUserHandler> handler;
+ {
+ std::lock_guard<std::mutex> lock(lock_);
- // Client already deleted.
- if (!client) {
- return true;
+ auto iter = FindHandler(&lock, misc_name);
+ if (iter == dm_users_.end()) {
+ // Client already deleted.
+ return true;
+ }
+ handler = std::move(*iter);
+ dm_users_.erase(iter);
}
- auto& th = client->thread();
- if (th.joinable()) {
+ auto& th = handler->thread();
+ if (th.joinable() && wait) {
th.join();
+ } else if (handler->snapuserd()->IsAttached()) {
+ th.detach();
}
return true;
}
diff --git a/init/devices.cpp b/init/devices.cpp
index f8eb16a..5888c06 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -440,13 +440,6 @@
}
}
unlink(devpath.c_str());
-
- if (android::base::StartsWith(devpath, "/dev/dm-user/")) {
- std::error_code ec;
- if (std::filesystem::is_empty("/dev/dm-user/", ec)) {
- rmdir("/dev/dm-user");
- }
- }
}
}
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 843ac5c..91aaffd 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -221,6 +221,7 @@
CHECKCALL(mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755"));
CHECKCALL(mkdir("/dev/pts", 0755));
CHECKCALL(mkdir("/dev/socket", 0755));
+ CHECKCALL(mkdir("/dev/dm-user", 0755));
CHECKCALL(mount("devpts", "/dev/pts", "devpts", 0, NULL));
#define MAKE_STR(x) __STRING(x)
CHECKCALL(mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC)));
diff --git a/init/selinux.cpp b/init/selinux.cpp
index 5a0255a..f03ca6b 100644
--- a/init/selinux.cpp
+++ b/init/selinux.cpp
@@ -534,6 +534,7 @@
selinux_android_restorecon("/dev/__properties__", 0);
selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ selinux_android_restorecon("/dev/dm-user", SELINUX_ANDROID_RESTORECON_RECURSE);
selinux_android_restorecon("/dev/device-mapper", 0);
selinux_android_restorecon("/apex", 0);
diff --git a/libutils/String16_test.cpp b/libutils/String16_test.cpp
index f1f24c3..2505f44 100644
--- a/libutils/String16_test.cpp
+++ b/libutils/String16_test.cpp
@@ -215,4 +215,16 @@
EXPECT_TRUE(tmp.isStaticString());
}
+TEST(String16Test, OverreadUtf8Conversion) {
+ char tmp[] = {'a', static_cast<char>(0xe0), '\0'};
+ String16 another(tmp);
+ EXPECT_TRUE(another.size() == 0);
+}
+
+TEST(String16Test, ValidUtf8Conversion) {
+ String16 another("abcdef");
+ EXPECT_EQ(6U, another.size());
+ EXPECT_STR16EQ(another, u"abcdef");
+}
+
} // namespace android
diff --git a/libutils/String8_test.cpp b/libutils/String8_test.cpp
index 3947a5f..9efcc6f 100644
--- a/libutils/String8_test.cpp
+++ b/libutils/String8_test.cpp
@@ -96,4 +96,9 @@
EXPECT_EQ(10U, string8.length());
}
+TEST_F(String8Test, ValidUtf16Conversion) {
+ char16_t tmp[] = u"abcdef";
+ String8 valid = String8(String16(tmp));
+ EXPECT_STREQ(valid, "abcdef");
+}
}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 2bceb75..c4c8768 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -69,7 +69,7 @@
EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS :=
ifeq ($(CLANG_COVERAGE),true)
- EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%p-%m.profraw
+ EXPORT_GLOBAL_CLANG_COVERAGE_OPTIONS := export LLVM_PROFILE_FILE /data/misc/trace/clang-%20m.profraw
endif
# Put it here instead of in init.rc module definition,
diff --git a/rootdir/avb/Android.mk b/rootdir/avb/Android.mk
index 9892ae7..c8fc1d6 100644
--- a/rootdir/avb/Android.mk
+++ b/rootdir/avb/Android.mk
@@ -1,11 +1,17 @@
LOCAL_PATH:= $(call my-dir)
-ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
- my_gsi_avb_keys_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
-else ifeq ($(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT),true)
- my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/avb
+ifeq ($(BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT),true) # AVB keys are installed to vendor ramdisk
+ ifeq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true) # no dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/first_stage_ramdisk/avb
+ else # device has a dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_VENDOR_RAMDISK_OUT)/avb
+ endif
else
- my_gsi_avb_keys_path := $(TARGET_RAMDISK_OUT)/avb
+ ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) # no dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
+ else # device has a dedicated recovery partition
+ my_gsi_avb_keys_path := $(TARGET_RAMDISK_OUT)/avb
+ endif
endif
#######################################
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 746fc61..fbb48e8 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -792,6 +792,7 @@
# Delete these if need be, per b/139193659
mkdir /data/rollback 0700 system system encryption=DeleteIfNecessary
mkdir /data/rollback-observer 0700 system system encryption=DeleteIfNecessary
+ mkdir /data/rollback-history 0700 system system encryption=DeleteIfNecessary
# Create root dir for Incremental Service
mkdir /data/incremental 0771 system system encryption=Require