Merge changes I7741c7e5,I30bb2844,If55b93b1,Ife058ca2
* changes:
trusty: fuzz: dump trusty kernel logs on crash
trusty: Add corpus for gatekeeper fuzzer
trusty: Fuzzer for Gatekeeper TA
trusty: fuzz: Helper library
diff --git a/base b/base
deleted file mode 120000
index 622c552..0000000
--- a/base
+++ /dev/null
@@ -1 +0,0 @@
-../libbase
\ No newline at end of file
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 99cabdd..6391acc 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -17,6 +17,7 @@
name: "libdebuggerd_common_headers",
export_include_dirs: ["common/include"],
recovery_available: true,
+ vendor_ramdisk_available: true,
}
cc_library_shared {
@@ -47,6 +48,7 @@
name: "libtombstoned_client_static",
defaults: ["debuggerd_defaults"],
recovery_available: true,
+ vendor_ramdisk_available: true,
srcs: [
"tombstoned/tombstoned_client.cpp",
"util.cpp",
@@ -69,6 +71,7 @@
name: "libdebuggerd_handler_core",
defaults: ["debuggerd_defaults"],
recovery_available: true,
+ vendor_ramdisk_available: true,
srcs: ["handler/debuggerd_handler.cpp"],
header_libs: [
@@ -113,6 +116,7 @@
],
defaults: ["debuggerd_defaults"],
recovery_available: true,
+ vendor_ramdisk_available: true,
srcs: [
"handler/debuggerd_fallback.cpp",
],
@@ -164,6 +168,7 @@
name: "libdebuggerd",
defaults: ["debuggerd_defaults"],
recovery_available: true,
+ vendor_ramdisk_available: true,
srcs: [
"libdebuggerd/backtrace.cpp",
@@ -209,6 +214,11 @@
"libdexfile_support",
],
},
+ vendor_ramdisk: {
+ exclude_static_libs: [
+ "libdexfile_support",
+ ],
+ },
},
product_variables: {
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index 63b7ad2..616a06f 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -583,7 +583,8 @@
} // namespace
-void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions) {
+void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
+ const std::vector<std::string>& dsu_partitions) {
static constexpr char kDsuKeysDir[] = "/avb";
// Convert userdata
// Inherit fstab properties for userdata.
@@ -594,7 +595,7 @@
userdata.fs_mgr_flags.logical = true;
userdata.fs_mgr_flags.formattable = true;
if (!userdata.metadata_key_dir.empty()) {
- userdata.metadata_key_dir += "/gsi";
+ userdata.metadata_key_dir = android::gsi::GetDsuMetadataKeyDir(dsu_slot);
}
} else {
userdata = BuildDsuUserdataFstabEntry();
@@ -687,9 +688,14 @@
return false;
}
if (!is_proc_mounts && !access(android::gsi::kGsiBootedIndicatorFile, F_OK)) {
+ std::string dsu_slot;
+ if (!android::gsi::GetActiveDsu(&dsu_slot)) {
+ PERROR << __FUNCTION__ << "(): failed to get active dsu slot";
+ return false;
+ }
std::string lp_names;
ReadFileToString(gsi::kGsiLpNamesFile, &lp_names);
- TransformFstabForDsu(fstab, Split(lp_names, ","));
+ TransformFstabForDsu(fstab, dsu_slot, Split(lp_names, ","));
}
#ifndef NO_SKIP_MOUNT
diff --git a/fs_mgr/include_fstab/fstab/fstab.h b/fs_mgr/include_fstab/fstab/fstab.h
index 4093445..2d4de09 100644
--- a/fs_mgr/include_fstab/fstab/fstab.h
+++ b/fs_mgr/include_fstab/fstab/fstab.h
@@ -113,7 +113,8 @@
// dsu_partitions[0] = "system_gsi"
// dsu_partitions[1] = "userdata_gsi"
// dsu_partitions[2] = ...
-void TransformFstabForDsu(Fstab* fstab, const std::vector<std::string>& dsu_partitions);
+void TransformFstabForDsu(Fstab* fstab, const std::string& dsu_slot,
+ const std::vector<std::string>& dsu_partitions);
std::set<std::string> GetBootDevices();
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 5e5f06d..059a469 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -35,9 +35,11 @@
"update_metadata-protos",
],
whole_static_libs: [
+ "libcutils",
"libext2_uuid",
"libext4_utils",
"libfstab",
+ "libsnapshot_snapuserd",
],
header_libs: [
"libchrome",
@@ -239,6 +241,7 @@
srcs: [
"partition_cow_creator_test.cpp",
"snapshot_metadata_updater_test.cpp",
+ "snapshot_reader_test.cpp",
"snapshot_test.cpp",
],
shared_libs: [
@@ -403,7 +406,7 @@
],
srcs: [
"snapuserd_server.cpp",
- "snapuserd.cpp",
+ "snapuserd.cpp",
"snapuserd_daemon.cpp",
],
@@ -558,7 +561,7 @@
"libbrotli",
"libgtest",
"libsnapshot_cow",
- "libsnapshot_snapuserd",
+ "libsnapshot_snapuserd",
"libcutils_sockets",
"libz",
"libdm",
@@ -571,3 +574,27 @@
auto_gen_config: true,
require_root: false,
}
+
+cc_binary {
+ name: "inspect_cow",
+ host_supported: true,
+ device_supported: true,
+ cflags: [
+ "-D_FILE_OFFSET_BITS=64",
+ "-Wall",
+ "-Werror",
+ ],
+ static_libs: [
+ "libbase",
+ "libbrotli",
+ "libcrypto_static",
+ "liblog",
+ "libsnapshot_cow",
+ "libz",
+ ],
+ shared_libs: [
+ ],
+ srcs: [
+ "inspect_cow.cpp",
+ ],
+}
diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp
index b1667e3..f10ccb6 100644
--- a/fs_mgr/libsnapshot/cow_reader.cpp
+++ b/fs_mgr/libsnapshot/cow_reader.cpp
@@ -18,6 +18,7 @@
#include <unistd.h>
#include <limits>
+#include <optional>
#include <vector>
#include <android-base/file.h>
@@ -117,8 +118,7 @@
PLOG(ERROR) << "lseek ops failed";
return false;
}
- uint64_t next_last_label = 0;
- bool has_next = 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;
@@ -135,7 +135,7 @@
}
auto& current_op = ops_buffer->data()[current_op_num];
pos = lseek(fd_.get(), GetNextOpOffset(current_op), SEEK_CUR);
- if (pos < 0) {
+ if (pos == uint64_t(-1)) {
PLOG(ERROR) << "lseek next op failed";
return false;
}
@@ -146,11 +146,23 @@
has_last_label_ = true;
last_label_ = current_op.source;
} else {
- last_label_ = next_last_label;
- if (has_next) has_last_label_ = true;
- next_last_label = current_op.source;
- has_next = true;
+ if (next_last_label) {
+ last_label_ = next_last_label.value();
+ has_last_label_ = true;
+ }
+ next_last_label = {current_op.source};
}
+ } else if (current_op.type == kCowFooterOp) {
+ memcpy(&footer_.op, ¤t_op, sizeof(footer_.op));
+
+ 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;
+ }
+ }
+ break;
}
}
diff --git a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
index bbda552..ab15194 100644
--- a/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
+++ b/fs_mgr/libsnapshot/cow_snapuserd_test.cpp
@@ -65,7 +65,7 @@
product_a_ = std::make_unique<TemporaryFile>(path);
ASSERT_GE(product_a_->fd, 0) << strerror(errno);
- size_ = 100_MiB;
+ size_ = 1_MiB;
}
void TearDown() override {
@@ -123,7 +123,7 @@
}
void TestIO(unique_fd& snapshot_fd, std::unique_ptr<uint8_t[]>& buffer);
- SnapuserdClient client_;
+ std::unique_ptr<SnapuserdClient> client_;
};
void SnapuserdTest::Init() {
@@ -151,12 +151,12 @@
offset += 1_MiB;
}
- for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
+ for (size_t j = 0; j < (8_MiB / 1_MiB); j++) {
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
ASSERT_EQ(android::base::WriteFully(system_a_->fd, random_buffer.get(), 1_MiB), true);
}
- for (size_t j = 0; j < (800_MiB / 1_MiB); j++) {
+ for (size_t j = 0; j < (8_MiB / 1_MiB); j++) {
ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true);
ASSERT_EQ(android::base::WriteFully(product_a_->fd, random_buffer.get(), 1_MiB), true);
}
@@ -297,18 +297,18 @@
}
void SnapuserdTest::StartSnapuserdDaemon() {
- int ret;
+ ASSERT_TRUE(EnsureSnapuserdStarted());
- ret = client_.StartSnapuserd();
- ASSERT_EQ(ret, 0);
+ client_ = SnapuserdClient::Connect(kSnapuserdSocket, 5s);
+ ASSERT_NE(client_, nullptr);
- ret = client_.InitializeSnapuserd(cow_system_->path, system_a_loop_->device(),
- GetSystemControlPath());
- ASSERT_EQ(ret, 0);
+ bool ok = client_->InitializeSnapuserd(cow_system_->path, system_a_loop_->device(),
+ GetSystemControlPath());
+ ASSERT_TRUE(ok);
- ret = client_.InitializeSnapuserd(cow_product_->path, product_a_loop_->device(),
+ ok = client_->InitializeSnapuserd(cow_product_->path, product_a_loop_->device(),
GetProductControlPath());
- ASSERT_EQ(ret, 0);
+ ASSERT_TRUE(ok);
}
void SnapuserdTest::CreateSnapshotDevices() {
@@ -464,10 +464,6 @@
{cow_system_1_->path, system_a_loop_->device(), GetSystemControlPath()},
{cow_product_1_->path, product_a_loop_->device(), GetProductControlPath()}};
- // Start the second stage deamon and send the devices information through
- // vector.
- ASSERT_EQ(client_.RestartSnapuserd(vec), 0);
-
// TODO: This is not switching snapshot device but creates a new table;
// Second stage daemon will be ready to serve the IO request. From now
// onwards, we can go ahead and shutdown the first stage daemon
@@ -476,9 +472,6 @@
DeleteDmUser(cow_system_, "system-snapshot");
DeleteDmUser(cow_product_, "product-snapshot");
- // 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);
@@ -494,7 +487,7 @@
DeleteDmUser(cow_product_1_, "product-snapshot-1");
// Stop the second stage daemon
- ASSERT_EQ(client_.StopSnapuserd(false), 0);
+ ASSERT_TRUE(client_->StopSnapuserd());
}
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
index 814e104..b863ff2 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h
@@ -39,11 +39,17 @@
// maximum number of bytes that can be written to the returned buffer.
//
// The returned buffer is owned by IByteSink, but must remain valid until
- // the ready operation has completed (or the entire buffer has been
+ // the read operation has completed (or the entire buffer has been
// covered by calls to ReturnData).
//
// After calling GetBuffer(), all previous buffers returned are no longer
// valid.
+ //
+ // GetBuffer() is intended to be sequential. A returned size of N indicates
+ // that the output stream will advance by N bytes, and the ReturnData call
+ // indicates that those bytes have been fulfilled. Therefore, it is
+ // possible to have ReturnBuffer do nothing, if the implementation doesn't
+ // care about incremental writes.
virtual void* GetBuffer(size_t requested, size_t* actual) = 0;
// Called when a section returned by |GetBuffer| has been filled with data.
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
index 6dee3d4..92e7910 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/mock_snapshot.h
@@ -47,6 +47,8 @@
MOCK_METHOD(bool, CreateLogicalAndSnapshotPartitions,
(const std::string& super_device, const std::chrono::milliseconds& timeout_ms),
(override));
+ MOCK_METHOD(bool, MapAllSnapshots, (const std::chrono::milliseconds& timeout_ms), (override));
+ MOCK_METHOD(bool, UnmapAllSnapshots, (), (override));
MOCK_METHOD(bool, HandleImminentDataWipe, (const std::function<void()>& callback), (override));
MOCK_METHOD(bool, FinishMergeInRecovery, (), (override));
MOCK_METHOD(CreateResult, RecoveryCreateSnapshotDevices, (), (override));
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
index 4bbdca3..35ed04a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot.h
@@ -37,6 +37,7 @@
#include <libsnapshot/auto_device.h>
#include <libsnapshot/return.h>
#include <libsnapshot/snapshot_writer.h>
+#include <libsnapshot/snapuserd_client.h>
#ifndef FRIEND_TEST
#define FRIEND_TEST(test_set_name, individual_test) \
@@ -185,6 +186,9 @@
// must be suffixed. If a source partition exists, it must be specified as well. The source
// partition will only be used if raw bytes are needed. The source partition should be an
// absolute path to the device, not a partition name.
+ //
+ // After calling OpenSnapshotWriter, the caller must invoke Initialize or InitializeForAppend
+ // before invoking write operations.
virtual std::unique_ptr<ISnapshotWriter> OpenSnapshotWriter(
const android::fs_mgr::CreateLogicalPartitionParams& params,
const std::optional<std::string>& source_device) = 0;
@@ -203,6 +207,14 @@
virtual bool CreateLogicalAndSnapshotPartitions(
const std::string& super_device, const std::chrono::milliseconds& timeout_ms = {}) = 0;
+ // Map all snapshots. This is analogous to CreateLogicalAndSnapshotPartitions, except it maps
+ // the target slot rather than the current slot. It should only be used immediately after
+ // applying an update, before rebooting to the new slot.
+ virtual bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) = 0;
+
+ // Unmap all snapshots. This should be called to undo MapAllSnapshots().
+ virtual bool UnmapAllSnapshots() = 0;
+
// This method should be called preceding any wipe or flash of metadata or
// userdata. It is only valid in recovery or fastbootd, and it ensures that
// a merge has been completed.
@@ -318,6 +330,8 @@
bool Dump(std::ostream& os) override;
std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
+ bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms = {}) override;
+ bool UnmapAllSnapshots() override;
private:
FRIEND_TEST(SnapshotTest, CleanFirstStageMount);
@@ -355,6 +369,9 @@
// This is created lazily since it can connect via binder.
bool EnsureImageManager();
+ // Ensure we're connected to snapuserd.
+ bool EnsureSnapuserdConnected();
+
// Helper for first-stage init.
bool ForceLocalImageManager();
@@ -411,6 +428,11 @@
const std::string& cow_device, const std::chrono::milliseconds& timeout_ms,
std::string* dev_path);
+ // Create a dm-user device for a given snapshot.
+ bool MapDmUserCow(LockedFile* lock, const std::string& name, const std::string& cow_file,
+ const std::string& base_device, const std::chrono::milliseconds& timeout_ms,
+ std::string* path);
+
// Map a COW image that was previous created with CreateCowImage.
std::optional<std::string> MapCowImage(const std::string& name,
const std::chrono::milliseconds& timeout_ms);
@@ -639,6 +661,7 @@
std::unique_ptr<IImageManager> images_;
bool has_local_image_manager_ = false;
bool in_factory_data_reset_ = false;
+ std::unique_ptr<SnapuserdClient> snapuserd_client_;
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
index ed790a0..cba3560 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_stub.h
@@ -52,6 +52,8 @@
bool Dump(std::ostream& os) override;
std::unique_ptr<AutoDevice> EnsureMetadataMounted() override;
ISnapshotMergeStats* GetSnapshotMergeStatsInstance() override;
+ bool MapAllSnapshots(const std::chrono::milliseconds& timeout_ms) override;
+ bool UnmapAllSnapshots() override;
};
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
index f76f545..4732c2d 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapshot_writer.h
@@ -14,6 +14,8 @@
#pragma once
+#include <optional>
+
#include <android-base/unique_fd.h>
#include <libsnapshot/cow_writer.h>
@@ -37,14 +39,22 @@
// device is only opened on the first operation that requires it.
void SetSourceDevice(const std::string& source_device);
+ // Open the writer in write mode (no append).
+ virtual bool Initialize() = 0;
+
+ // Open the writer in append mode, optionally with the last label to resume
+ // from. See CowWriter::InitializeAppend.
+ virtual bool InitializeAppend(std::optional<uint64_t> label = {}) = 0;
+
virtual std::unique_ptr<FileDescriptor> OpenReader() = 0;
protected:
android::base::borrowed_fd GetSourceFd();
+ std::optional<std::string> source_device_;
+
private:
android::base::unique_fd source_fd_;
- std::optional<std::string> source_device_;
};
// Send writes to a COW or a raw device directly, based on a threshold.
@@ -52,9 +62,11 @@
public:
CompressedSnapshotWriter(const CowOptions& options);
- // Sets the COW device, if needed.
+ // Sets the COW device; this is required.
bool SetCowDevice(android::base::unique_fd&& cow_device);
+ bool Initialize() override;
+ bool InitializeAppend(std::optional<uint64_t> label = {}) override;
bool Finalize() override;
uint64_t GetCowSize() override;
std::unique_ptr<FileDescriptor> OpenReader() override;
@@ -79,6 +91,9 @@
// Set the device used for all writes.
void SetSnapshotDevice(android::base::unique_fd&& snapshot_fd, uint64_t cow_size);
+ bool Initialize() override { return true; }
+ bool InitializeAppend(std::optional<uint64_t>) override { return true; }
+
bool Finalize() override;
uint64_t GetCowSize() override { return cow_size_; }
std::unique_ptr<FileDescriptor> OpenReader() override;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
index 2f727d6..80f87d9 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd.h
@@ -77,7 +77,7 @@
int ReadDiskExceptions(chunk_t chunk, size_t size);
int ReadData(chunk_t chunk, size_t size);
- std::string GetControlDevicePath() { return control_device_; }
+ const std::string& GetControlDevicePath() { return control_device_; }
private:
int ProcessReplaceOp(const CowOperation* cow_op);
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
index ab2149e..0bbdaa5 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_client.h
@@ -14,46 +14,49 @@
#pragma once
+#include <chrono>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include <vector>
+#include <android-base/unique_fd.h>
+
namespace android {
namespace snapshot {
static constexpr uint32_t PACKET_SIZE = 512;
-static constexpr uint32_t MAX_CONNECT_RETRY_COUNT = 10;
+
+static constexpr char kSnapuserdSocketFirstStage[] = "snapuserd_first_stage";
+static constexpr char kSnapuserdSocket[] = "snapuserd";
+
+// Ensure that the second-stage daemon for snapuserd is running.
+bool EnsureSnapuserdStarted();
class SnapuserdClient {
private:
- int sockfd_ = 0;
+ android::base::unique_fd sockfd_;
- int Sendmsg(const char* msg, size_t size);
+ bool Sendmsg(const std::string& msg);
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;
- }
+ bool ValidateConnection();
public:
- int StartSnapuserd();
- int StopSnapuserd(bool firstStageDaemon);
+ explicit SnapuserdClient(android::base::unique_fd&& sockfd);
+
+ static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
+ std::chrono::milliseconds timeout_ms);
+
+ bool StopSnapuserd();
int RestartSnapuserd(std::vector<std::vector<std::string>>& vec);
- int InitializeSnapuserd(std::string cow_device, std::string backing_device,
- std::string control_device);
+ bool InitializeSnapuserd(const std::string& cow_device, const std::string& backing_device,
+ const std::string& control_device);
+
+ // Wait for snapuserd to disassociate with a dm-user control device. This
+ // must ONLY be called if the control device has already been deleted.
+ bool WaitForDeviceDelete(const std::string& control_device);
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
index 94542d7..c6779b8 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_daemon.h
@@ -25,22 +25,21 @@
// The Daemon class is a singleton to avoid
// instantiating more than once
public:
+ Daemon() {}
+
static Daemon& Instance() {
static Daemon instance;
return instance;
}
- int StartServer(std::string socketname);
- bool IsRunning();
+ bool StartServer(const std::string& socketname);
void Run();
+ void Interrupt();
private:
- bool is_running_;
- std::unique_ptr<struct pollfd> poll_fd_;
// Signal mask used with ppoll()
sigset_t signal_mask_;
- Daemon();
Daemon(Daemon const&) = delete;
void operator=(Daemon const&) = delete;
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
index a1ebd3a..181ee33 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/snapuserd_server.h
@@ -14,17 +14,21 @@
#pragma once
+#include <poll.h>
+
#include <cstdio>
#include <cstring>
#include <functional>
#include <future>
#include <iostream>
+#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include <android-base/unique_fd.h>
+#include <libsnapshot/snapuserd.h>
namespace android {
namespace snapshot {
@@ -34,21 +38,24 @@
enum class DaemonOperations {
START,
QUERY,
- TERMINATING,
STOP,
+ DELETE,
INVALID,
};
-class Client {
+class DmUserHandler {
private:
- std::unique_ptr<std::thread> threadHandler_;
+ std::thread thread_;
+ std::unique_ptr<Snapuserd> snapuserd_;
public:
- void SetThreadHandler(std::function<void(void)> func) {
- threadHandler_ = std::make_unique<std::thread>(func);
- }
+ explicit DmUserHandler(std::unique_ptr<Snapuserd>&& snapuserd)
+ : snapuserd_(std::move(snapuserd)) {}
- std::unique_ptr<std::thread>& GetThreadHandler() { return threadHandler_; }
+ const std::unique_ptr<Snapuserd>& snapuserd() const { return snapuserd_; }
+ std::thread& thread() { return thread_; }
+
+ const std::string& GetControlDevice() const;
};
class Stoppable {
@@ -60,9 +67,6 @@
virtual ~Stoppable() {}
- virtual void ThreadStart(std::string cow_device, std::string backing_device,
- std::string control_device) = 0;
-
bool StopRequested() {
// checks if value in future object is available
if (futureObj_.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
@@ -77,28 +81,40 @@
private:
android::base::unique_fd sockfd_;
bool terminating_;
- std::vector<std::unique_ptr<Client>> clients_vec_;
+ std::vector<struct pollfd> watched_fds_;
- void ThreadStart(std::string cow_device, std::string backing_device,
- std::string control_device) override;
+ std::mutex lock_;
+ std::vector<std::unique_ptr<DmUserHandler>> dm_users_;
+
+ void AddWatchedFd(android::base::borrowed_fd fd);
+ void AcceptClient();
+ bool HandleClient(android::base::borrowed_fd fd, int revents);
+ bool Recv(android::base::borrowed_fd fd, std::string* data);
+ bool Sendmsg(android::base::borrowed_fd fd, const std::string& msg);
+ bool Receivemsg(android::base::borrowed_fd fd, const std::string& str);
+
void ShutdownThreads();
+ bool WaitForDelete(const std::string& control_device);
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_; }
+ void RunThread(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);
+
public:
SnapuserdServer() { terminating_ = false; }
+ ~SnapuserdServer();
- 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);
- android::base::borrowed_fd GetSocketFd() { return sockfd_; }
+ bool Start(const std::string& socketname);
+ bool Run();
+ void Interrupt();
};
} // namespace snapshot
diff --git a/fs_mgr/libsnapshot/inspect_cow.cpp b/fs_mgr/libsnapshot/inspect_cow.cpp
new file mode 100644
index 0000000..6046bad
--- /dev/null
+++ b/fs_mgr/libsnapshot/inspect_cow.cpp
@@ -0,0 +1,90 @@
+//
+// 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.
+//
+#include <stdio.h>
+
+#include <iostream>
+#include <string>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <libsnapshot/cow_reader.h>
+
+namespace android {
+namespace snapshot {
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+ unsigned int, const char* message) {
+ if (severity == android::base::ERROR) {
+ fprintf(stderr, "%s\n", message);
+ } else {
+ fprintf(stdout, "%s\n", message);
+ }
+}
+
+static bool Inspect(const std::string& path) {
+ android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+ if (fd < 0) {
+ PLOG(ERROR) << "open failed: " << path;
+ return false;
+ }
+
+ CowReader reader;
+ if (!reader.Parse(fd)) {
+ LOG(ERROR) << "parse failed: " << path;
+ return false;
+ }
+
+ CowHeader header;
+ if (!reader.GetHeader(&header)) {
+ LOG(ERROR) << "could not get header: " << path;
+ return false;
+ }
+
+ std::cout << "Major version: " << header.major_version << "\n";
+ std::cout << "Minor version: " << header.minor_version << "\n";
+ std::cout << "Header size: " << header.header_size << "\n";
+ std::cout << "Footer size: " << header.footer_size << "\n";
+ std::cout << "Block size: " << header.block_size << "\n";
+ std::cout << "\n";
+
+ auto iter = reader.GetOpIter();
+ while (!iter->Done()) {
+ const CowOperation& op = iter->Get();
+
+ std::cout << op << "\n";
+
+ iter->Next();
+ }
+
+ return true;
+}
+
+} // namespace snapshot
+} // namespace android
+
+int main(int argc, char** argv) {
+ android::base::InitLogging(argv, android::snapshot::MyLogger);
+
+ if (argc < 2) {
+ LOG(ERROR) << "Usage: inspect_cow <COW_FILE>";
+ return 1;
+ }
+
+ if (!android::snapshot::Inspect(argv[1])) {
+ return 1;
+ }
+ return 0;
+}
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 6574457..7061d56 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -56,6 +56,7 @@
using android::dm::DmTable;
using android::dm::DmTargetLinear;
using android::dm::DmTargetSnapshot;
+using android::dm::DmTargetUser;
using android::dm::kSectorSize;
using android::dm::SnapshotStorageMode;
using android::fiemap::FiemapStatus;
@@ -115,6 +116,10 @@
return snapshot_name + "-cow";
}
+static std::string GetDmUserCowName(const std::string& snapshot_name) {
+ return snapshot_name + "-user-cow";
+}
+
static std::string GetCowImageDeviceName(const std::string& snapshot_name) {
return snapshot_name + "-cow-img";
}
@@ -370,6 +375,45 @@
return Return(images_->CreateBackingImage(cow_image_name, status.cow_file_size(), cow_flags));
}
+bool SnapshotManager::MapDmUserCow(LockedFile* lock, const std::string& name,
+ const std::string& cow_file, const std::string& base_device,
+ const std::chrono::milliseconds& timeout_ms, std::string* path) {
+ CHECK(lock);
+
+ 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;
+ }
+
+ DmTable table;
+ table.Emplace<DmTargetUser>(0, base_sectors, name);
+ if (!dm.CreateDevice(name, table, path, timeout_ms)) {
+ return false;
+ }
+
+ if (!EnsureSnapuserdConnected()) {
+ return false;
+ }
+
+ auto control_device = "/dev/dm-user/" + name;
+ return snapuserd_client_->InitializeSnapuserd(cow_file, base_device, control_device);
+}
+
bool SnapshotManager::MapSnapshot(LockedFile* lock, const std::string& name,
const std::string& base_device, const std::string& cow_device,
const std::chrono::milliseconds& timeout_ms,
@@ -1728,6 +1772,30 @@
return true;
}
+ if (IsCompressionEnabled()) {
+ auto name = GetDmUserCowName(params.GetPartitionName());
+
+ // :TODO: need to force init to process uevents for these in first-stage.
+ std::string cow_path;
+ if (!GetMappedImageDevicePath(cow_name, &cow_path)) {
+ LOG(ERROR) << "Could not determine path for: " << cow_name;
+ return false;
+ }
+
+ std::string new_cow_device;
+ if (!MapDmUserCow(lock, name, cow_path, base_path, remaining_time, &new_cow_device)) {
+ LOG(ERROR) << "Could not map dm-user device for partition "
+ << params.GetPartitionName();
+ return false;
+ }
+ created_devices.EmplaceBack<AutoUnmapDevice>(&dm, name);
+
+ remaining_time = GetRemainingTime(params.timeout_ms, begin);
+ if (remaining_time.count() < 0) return false;
+
+ cow_device = new_cow_device;
+ }
+
std::string path;
if (!MapSnapshot(lock, params.GetPartitionName(), base_device, cow_device, remaining_time,
&path)) {
@@ -1847,6 +1915,22 @@
if (!EnsureImageManager()) return false;
auto& dm = DeviceMapper::Instance();
+
+ auto dm_user_name = GetDmUserCowName(name);
+ if (IsCompressionEnabled() && dm.GetState(dm_user_name) != DmDeviceState::INVALID) {
+ if (!EnsureSnapuserdConnected()) {
+ return false;
+ }
+ if (!dm.DeleteDevice(dm_user_name)) {
+ LOG(ERROR) << "Cannot unmap " << dm_user_name;
+ return false;
+ }
+ if (!snapuserd_client_->WaitForDeviceDelete("/dev/dm-user/" + dm_user_name)) {
+ LOG(ERROR) << "Failed to wait for " << dm_user_name << " control device to delete";
+ return false;
+ }
+ }
+
auto cow_name = GetCowName(name);
if (!dm.DeleteDeviceIfExists(cow_name)) {
LOG(ERROR) << "Cannot unmap " << cow_name;
@@ -1861,6 +1945,16 @@
return true;
}
+bool SnapshotManager::MapAllSnapshots(const std::chrono::milliseconds&) {
+ LOG(ERROR) << "Not yet implemented.";
+ return false;
+}
+
+bool SnapshotManager::UnmapAllSnapshots() {
+ LOG(ERROR) << "Not yet implemented.";
+ return false;
+}
+
auto SnapshotManager::OpenFile(const std::string& file, int lock_flags)
-> std::unique_ptr<LockedFile> {
unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
@@ -2117,6 +2211,20 @@
return true;
}
+bool SnapshotManager::EnsureSnapuserdConnected() {
+ if (!snapuserd_client_) {
+ if (!EnsureSnapuserdStarted()) {
+ return false;
+ }
+ snapuserd_client_ = SnapuserdClient::Connect(kSnapuserdSocket, 10s);
+ if (!snapuserd_client_) {
+ LOG(ERROR) << "Unable to connect to snapuserd";
+ return false;
+ }
+ }
+ return true;
+}
+
bool SnapshotManager::ForceLocalImageManager() {
images_ = android::fiemap::ImageManager::Open(gsid_dir_);
if (!images_) {
diff --git a/fs_mgr/libsnapshot/snapshot_reader.cpp b/fs_mgr/libsnapshot/snapshot_reader.cpp
index 0d47468..a4a652a 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.cpp
+++ b/fs_mgr/libsnapshot/snapshot_reader.cpp
@@ -14,13 +14,17 @@
// limitations under the License.
//
-#include <ext4_utils/ext4_utils.h>
-
#include "snapshot_reader.h"
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <ext4_utils/ext4_utils.h>
+
namespace android {
namespace snapshot {
+using android::base::borrowed_fd;
+
// Not supported.
bool ReadOnlyFileDescriptor::Open(const char*, int, mode_t) {
errno = EINVAL;
@@ -73,5 +77,252 @@
return true;
}
+bool CompressedSnapshotReader::SetCow(std::unique_ptr<CowReader>&& cow) {
+ cow_ = std::move(cow);
+
+ CowHeader header;
+ if (!cow_->GetHeader(&header)) {
+ return false;
+ }
+ block_size_ = header.block_size;
+
+ // Populate the operation map.
+ op_iter_ = cow_->GetOpIter();
+ while (!op_iter_->Done()) {
+ const CowOperation* op = &op_iter_->Get();
+ if (op->new_block >= ops_.size()) {
+ ops_.resize(op->new_block + 1, nullptr);
+ }
+ ops_[op->new_block] = op;
+ op_iter_->Next();
+ }
+
+ return true;
+}
+
+void CompressedSnapshotReader::SetSourceDevice(const std::string& source_device) {
+ source_device_ = {source_device};
+}
+
+void CompressedSnapshotReader::SetBlockDeviceSize(uint64_t block_device_size) {
+ block_device_size_ = block_device_size;
+}
+
+borrowed_fd CompressedSnapshotReader::GetSourceFd() {
+ if (source_fd_ < 0) {
+ if (!source_device_) {
+ LOG(ERROR) << "CompressedSnapshotReader needs source device, but none was set";
+ errno = EINVAL;
+ return {-1};
+ }
+ source_fd_.reset(open(source_device_->c_str(), O_RDONLY | O_CLOEXEC));
+ if (source_fd_ < 0) {
+ PLOG(ERROR) << "open " << *source_device_;
+ return {-1};
+ }
+ }
+ return source_fd_;
+}
+
+class MemoryByteSink : public IByteSink {
+ public:
+ MemoryByteSink(void* buf, size_t count) {
+ buf_ = reinterpret_cast<uint8_t*>(buf);
+ pos_ = buf_;
+ end_ = buf_ + count;
+ }
+
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ *actual = std::min(remaining(), requested);
+ if (!*actual) {
+ return nullptr;
+ }
+
+ uint8_t* start = pos_;
+ pos_ += *actual;
+ return start;
+ }
+
+ bool ReturnData(void*, size_t) override { return true; }
+
+ uint8_t* buf() const { return buf_; }
+ uint8_t* pos() const { return pos_; }
+ size_t remaining() const { return end_ - pos_; }
+
+ private:
+ uint8_t* buf_;
+ uint8_t* pos_;
+ uint8_t* end_;
+};
+
+ssize_t CompressedSnapshotReader::Read(void* buf, size_t count) {
+ // Find the start and end chunks, inclusive.
+ uint64_t start_chunk = offset_ / block_size_;
+ uint64_t end_chunk = (offset_ + count - 1) / block_size_;
+
+ // Chop off the first N bytes if the position is not block-aligned.
+ size_t start_offset = offset_ % block_size_;
+
+ MemoryByteSink sink(buf, count);
+
+ size_t initial_bytes = std::min(block_size_ - start_offset, sink.remaining());
+ ssize_t rv = ReadBlock(start_chunk, &sink, start_offset, initial_bytes);
+ if (rv < 0) {
+ return -1;
+ }
+ offset_ += rv;
+
+ for (uint64_t chunk = start_chunk + 1; chunk < end_chunk; chunk++) {
+ ssize_t rv = ReadBlock(chunk, &sink, 0);
+ if (rv < 0) {
+ return -1;
+ }
+ offset_ += rv;
+ }
+
+ if (sink.remaining()) {
+ ssize_t rv = ReadBlock(end_chunk, &sink, 0, {sink.remaining()});
+ if (rv < 0) {
+ return -1;
+ }
+ offset_ += rv;
+ }
+
+ errno = 0;
+
+ DCHECK(sink.pos() - sink.buf() == count);
+ return count;
+}
+
+// Discard the first N bytes of a sink request, or any excess bytes.
+class PartialSink : public MemoryByteSink {
+ public:
+ PartialSink(void* buffer, size_t size, size_t ignore_start)
+ : MemoryByteSink(buffer, size), ignore_start_(ignore_start) {}
+
+ void* GetBuffer(size_t requested, size_t* actual) override {
+ // Throw away the first N bytes if needed.
+ if (ignore_start_) {
+ *actual = std::min({requested, ignore_start_, sizeof(discard_)});
+ ignore_start_ -= *actual;
+ return discard_;
+ }
+ // Throw away any excess bytes if needed.
+ if (remaining() == 0) {
+ *actual = std::min(requested, sizeof(discard_));
+ return discard_;
+ }
+ return MemoryByteSink::GetBuffer(requested, actual);
+ }
+
+ private:
+ size_t ignore_start_;
+ char discard_[4096];
+};
+
+ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
+ const std::optional<uint64_t>& max_bytes) {
+ size_t bytes_to_read = block_size_;
+ if (max_bytes) {
+ bytes_to_read = *max_bytes;
+ }
+
+ // The offset is relative to the chunk; we should be reading no more than
+ // one chunk.
+ CHECK(start_offset + bytes_to_read <= block_size_);
+
+ const CowOperation* op = nullptr;
+ if (chunk < ops_.size()) {
+ op = ops_[chunk];
+ }
+
+ size_t actual;
+ void* buffer = sink->GetBuffer(bytes_to_read, &actual);
+ if (!buffer || actual < bytes_to_read) {
+ // This should never happen unless we calculated the read size wrong
+ // somewhere. MemoryByteSink always fulfills the entire requested
+ // region unless there's not enough buffer remaining.
+ LOG(ERROR) << "Asked for buffer of size " << bytes_to_read << ", got " << actual;
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!op || op->type == kCowCopyOp) {
+ borrowed_fd fd = GetSourceFd();
+ if (fd < 0) {
+ // GetSourceFd sets errno.
+ return -1;
+ }
+
+ if (op) {
+ chunk = op->source;
+ }
+
+ off64_t offset = (chunk * block_size_) + start_offset;
+ if (!android::base::ReadFullyAtOffset(fd, buffer, bytes_to_read, offset)) {
+ PLOG(ERROR) << "read " << *source_device_;
+ // ReadFullyAtOffset sets errno.
+ return -1;
+ }
+ } else if (op->type == kCowZeroOp) {
+ memset(buffer, 0, bytes_to_read);
+ } else if (op->type == kCowReplaceOp) {
+ PartialSink partial_sink(buffer, bytes_to_read, start_offset);
+ if (!cow_->ReadData(*op, &partial_sink)) {
+ LOG(ERROR) << "CompressedSnapshotReader failed to read replace op";
+ errno = EIO;
+ return -1;
+ }
+ } else {
+ LOG(ERROR) << "CompressedSnapshotReader unknown op type: " << op->type;
+ errno = EINVAL;
+ return -1;
+ }
+
+ // MemoryByteSink doesn't do anything in ReturnBuffer, so don't bother calling it.
+ return bytes_to_read;
+}
+
+off64_t CompressedSnapshotReader::Seek(off64_t offset, int whence) {
+ switch (whence) {
+ case SEEK_SET:
+ offset_ = offset;
+ break;
+ case SEEK_END:
+ offset_ = static_cast<off64_t>(block_device_size_) + offset;
+ break;
+ case SEEK_CUR:
+ offset_ += offset;
+ break;
+ default:
+ LOG(ERROR) << "Unrecognized seek whence: " << whence;
+ errno = EINVAL;
+ return -1;
+ }
+ return offset_;
+}
+
+uint64_t CompressedSnapshotReader::BlockDevSize() {
+ return block_device_size_;
+}
+
+bool CompressedSnapshotReader::Close() {
+ cow_ = nullptr;
+ source_fd_ = {};
+ return true;
+}
+
+bool CompressedSnapshotReader::IsSettingErrno() {
+ return true;
+}
+
+bool CompressedSnapshotReader::IsOpen() {
+ return cow_ != nullptr;
+}
+
+bool CompressedSnapshotReader::Flush() {
+ return true;
+}
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader.h b/fs_mgr/libsnapshot/snapshot_reader.h
index 1f2ffe2..a3e7e22 100644
--- a/fs_mgr/libsnapshot/snapshot_reader.h
+++ b/fs_mgr/libsnapshot/snapshot_reader.h
@@ -16,7 +16,11 @@
#pragma once
+#include <optional>
+#include <vector>
+
#include <android-base/file.h>
+#include <libsnapshot/cow_reader.h>
#include <payload_consumer/file_descriptor.h>
namespace android {
@@ -46,5 +50,36 @@
android::base::unique_fd fd_;
};
+class CompressedSnapshotReader : public ReadOnlyFileDescriptor {
+ public:
+ bool SetCow(std::unique_ptr<CowReader>&& cow);
+ void SetSourceDevice(const std::string& source_device);
+ void SetBlockDeviceSize(uint64_t block_device_size);
+
+ ssize_t Read(void* buf, size_t count) override;
+ off64_t Seek(off64_t offset, int whence) override;
+ uint64_t BlockDevSize() override;
+ bool Close() override;
+ bool IsSettingErrno() override;
+ bool IsOpen() override;
+ bool Flush() override;
+
+ private:
+ ssize_t ReadBlock(uint64_t chunk, IByteSink* sink, size_t start_offset,
+ const std::optional<uint64_t>& max_bytes = {});
+ android::base::borrowed_fd GetSourceFd();
+
+ std::unique_ptr<CowReader> cow_;
+ std::unique_ptr<ICowOpIter> op_iter_;
+ uint32_t block_size_ = 0;
+
+ std::optional<std::string> source_device_;
+ android::base::unique_fd source_fd_;
+ uint64_t block_device_size_ = 0;
+ off64_t offset_ = 0;
+
+ std::vector<const CowOperation*> ops_;
+};
+
} // namespace snapshot
} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_reader_test.cpp b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
new file mode 100644
index 0000000..4202d22
--- /dev/null
+++ b/fs_mgr/libsnapshot/snapshot_reader_test.cpp
@@ -0,0 +1,166 @@
+// Copyright (C) 2018 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.
+
+#include <libsnapshot/snapshot.h>
+
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <libsnapshot/cow_writer.h>
+#include <payload_consumer/file_descriptor.h>
+
+namespace android {
+namespace snapshot {
+
+using android::base::unique_fd;
+using chromeos_update_engine::FileDescriptor;
+
+static constexpr uint32_t kBlockSize = 4096;
+static constexpr size_t kBlockCount = 10;
+
+class OfflineSnapshotTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() override {
+ base_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(base_->fd, 0) << strerror(errno);
+
+ cow_ = std::make_unique<TemporaryFile>();
+ ASSERT_GE(cow_->fd, 0) << strerror(errno);
+
+ WriteBaseDevice();
+ }
+
+ virtual void TearDown() override {
+ base_ = nullptr;
+ cow_ = nullptr;
+ base_blocks_ = {};
+ }
+
+ void WriteBaseDevice() {
+ unique_fd random(open("/dev/urandom", O_RDONLY));
+ ASSERT_GE(random, 0);
+
+ for (size_t i = 0; i < kBlockCount; i++) {
+ std::string block(kBlockSize, 0);
+ ASSERT_TRUE(android::base::ReadFully(random, block.data(), block.size()));
+ ASSERT_TRUE(android::base::WriteFully(base_->fd, block.data(), block.size()));
+ base_blocks_.emplace_back(std::move(block));
+ }
+ ASSERT_EQ(fsync(base_->fd), 0);
+ }
+
+ void WriteCow(ISnapshotWriter* writer) {
+ std::string new_block = MakeNewBlockString();
+
+ ASSERT_TRUE(writer->AddCopy(3, 0));
+ ASSERT_TRUE(writer->AddRawBlocks(5, new_block.data(), new_block.size()));
+ ASSERT_TRUE(writer->AddZeroBlocks(7, 2));
+ ASSERT_TRUE(writer->Finalize());
+ }
+
+ void TestBlockReads(ISnapshotWriter* writer) {
+ auto reader = writer->OpenReader();
+ ASSERT_NE(reader, nullptr);
+
+ // Test that unchanged blocks are not modified.
+ std::unordered_set<size_t> changed_blocks = {3, 5, 7, 8};
+ for (size_t i = 0; i < kBlockCount; i++) {
+ if (changed_blocks.count(i)) {
+ continue;
+ }
+
+ std::string block(kBlockSize, 0);
+ ASSERT_EQ(reader->Seek(i * kBlockSize, SEEK_SET), i * kBlockSize);
+ ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
+ ASSERT_EQ(block, base_blocks_[i]);
+ }
+
+ // Test that we can read back our modified blocks.
+ std::string block(kBlockSize, 0);
+ ASSERT_EQ(reader->Seek(3 * kBlockSize, SEEK_SET), 3 * kBlockSize);
+ ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
+ ASSERT_EQ(block, base_blocks_[0]);
+
+ ASSERT_EQ(reader->Seek(5 * kBlockSize, SEEK_SET), 5 * kBlockSize);
+ ASSERT_EQ(reader->Read(block.data(), block.size()), kBlockSize);
+ ASSERT_EQ(block, MakeNewBlockString());
+
+ std::string two_blocks(kBlockSize * 2, 0x7f);
+ std::string zeroes(kBlockSize * 2, 0);
+ ASSERT_EQ(reader->Seek(7 * kBlockSize, SEEK_SET), 7 * kBlockSize);
+ ASSERT_EQ(reader->Read(two_blocks.data(), two_blocks.size()), two_blocks.size());
+ ASSERT_EQ(two_blocks, zeroes);
+ }
+
+ void TestByteReads(ISnapshotWriter* writer) {
+ auto reader = writer->OpenReader();
+ ASSERT_NE(reader, nullptr);
+
+ std::string blob(kBlockSize * 3, 'x');
+
+ // Test that we can read in the middle of a block.
+ static constexpr size_t kOffset = 970;
+ off64_t offset = 3 * kBlockSize + kOffset;
+ ASSERT_EQ(reader->Seek(0, SEEK_SET), 0);
+ ASSERT_EQ(reader->Seek(offset, SEEK_CUR), offset);
+ ASSERT_EQ(reader->Read(blob.data(), blob.size()), blob.size());
+ ASSERT_EQ(blob.substr(0, 100), base_blocks_[0].substr(kOffset, 100));
+ ASSERT_EQ(blob.substr(kBlockSize - kOffset, kBlockSize), base_blocks_[4]);
+ ASSERT_EQ(blob.substr(kBlockSize * 2 - kOffset, 100), MakeNewBlockString().substr(0, 100));
+ ASSERT_EQ(blob.substr(blob.size() - kOffset), base_blocks_[6].substr(0, kOffset));
+
+ // Pull a random byte from the compressed block.
+ char value;
+ offset = 5 * kBlockSize + 1000;
+ ASSERT_EQ(reader->Seek(offset, SEEK_SET), offset);
+ ASSERT_EQ(reader->Read(&value, sizeof(value)), sizeof(value));
+ ASSERT_EQ(value, MakeNewBlockString()[1000]);
+ }
+
+ void TestReads(ISnapshotWriter* writer) {
+ ASSERT_NO_FATAL_FAILURE(TestBlockReads(writer));
+ ASSERT_NO_FATAL_FAILURE(TestByteReads(writer));
+ }
+
+ std::string MakeNewBlockString() {
+ std::string new_block = "This is a new block";
+ new_block.resize(kBlockSize / 2, '*');
+ new_block.resize(kBlockSize, '!');
+ return new_block;
+ }
+
+ std::unique_ptr<TemporaryFile> base_;
+ std::unique_ptr<TemporaryFile> cow_;
+ std::vector<std::string> base_blocks_;
+};
+
+TEST_F(OfflineSnapshotTest, CompressedSnapshot) {
+ CowOptions options;
+ options.compression = "gz";
+ options.max_blocks = {kBlockCount};
+
+ unique_fd cow_fd(dup(cow_->fd));
+ ASSERT_GE(cow_fd, 0);
+
+ auto writer = std::make_unique<CompressedSnapshotWriter>(options);
+ writer->SetSourceDevice(base_->path);
+ ASSERT_TRUE(writer->SetCowDevice(std::move(cow_fd)));
+ ASSERT_TRUE(writer->Initialize());
+ ASSERT_NO_FATAL_FAILURE(WriteCow(writer.get()));
+ ASSERT_NO_FATAL_FAILURE(TestReads(writer.get()));
+}
+
+} // namespace snapshot
+} // namespace android
diff --git a/fs_mgr/libsnapshot/snapshot_stub.cpp b/fs_mgr/libsnapshot/snapshot_stub.cpp
index 5be3c10..26b9129 100644
--- a/fs_mgr/libsnapshot/snapshot_stub.cpp
+++ b/fs_mgr/libsnapshot/snapshot_stub.cpp
@@ -136,4 +136,14 @@
return nullptr;
}
+bool SnapshotManagerStub::MapAllSnapshots(const std::chrono::milliseconds&) {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
+bool SnapshotManagerStub::UnmapAllSnapshots() {
+ LOG(ERROR) << __FUNCTION__ << " should never be called.";
+ return false;
+}
+
} // namespace android::snapshot
diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp
index 7327629..9660357 100644
--- a/fs_mgr/libsnapshot/snapshot_test.cpp
+++ b/fs_mgr/libsnapshot/snapshot_test.cpp
@@ -907,6 +907,9 @@
if (!result) {
return AssertionFailure() << "Cannot open snapshot for writing: " << name;
}
+ if (!result->Initialize()) {
+ return AssertionFailure() << "Cannot initialize snapshot for writing: " << name;
+ }
if (writer) {
*writer = std::move(result);
@@ -950,6 +953,9 @@
if (!WriteRandomData(writer.get(), &hashes_[name])) {
return AssertionFailure() << "Unable to write random data to snapshot " << name;
}
+ if (!writer->Finalize()) {
+ return AssertionFailure() << "Unable to finalize COW for " << name;
+ }
} else {
std::string path;
auto res = MapUpdateSnapshot(name, &path);
diff --git a/fs_mgr/libsnapshot/snapshot_writer.cpp b/fs_mgr/libsnapshot/snapshot_writer.cpp
index 9b1ab97..85ed156 100644
--- a/fs_mgr/libsnapshot/snapshot_writer.cpp
+++ b/fs_mgr/libsnapshot/snapshot_writer.cpp
@@ -56,9 +56,9 @@
bool CompressedSnapshotWriter::SetCowDevice(android::base::unique_fd&& cow_device) {
cow_device_ = std::move(cow_device);
cow_ = std::make_unique<CowWriter>(options_);
-
- return cow_->Initialize(cow_device_);
+ return true;
}
+
bool CompressedSnapshotWriter::Finalize() {
return cow_->Finalize();
}
@@ -68,7 +68,31 @@
}
std::unique_ptr<FileDescriptor> CompressedSnapshotWriter::OpenReader() {
- return nullptr;
+ unique_fd cow_fd(dup(cow_device_.get()));
+ if (cow_fd < 0) {
+ PLOG(ERROR) << "dup COW device";
+ return nullptr;
+ }
+
+ auto cow = std::make_unique<CowReader>();
+ if (!cow->Parse(std::move(cow_fd))) {
+ LOG(ERROR) << "Unable to read COW";
+ return nullptr;
+ }
+
+ auto reader = std::make_unique<CompressedSnapshotReader>();
+ if (!reader->SetCow(std::move(cow))) {
+ LOG(ERROR) << "Unable to initialize COW reader";
+ return nullptr;
+ }
+ if (source_device_) {
+ reader->SetSourceDevice(*source_device_);
+ }
+
+ const auto& cow_options = options();
+ reader->SetBlockDeviceSize(*cow_options.max_blocks * cow_options.block_size);
+
+ return reader;
}
bool CompressedSnapshotWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
@@ -88,6 +112,17 @@
return cow_->AddLabel(label);
}
+bool CompressedSnapshotWriter::Initialize() {
+ return cow_->Initialize(cow_device_, CowWriter::OpenMode::WRITE);
+}
+
+bool CompressedSnapshotWriter::InitializeAppend(std::optional<uint64_t> label) {
+ if (label) {
+ return cow_->InitializeAppend(cow_device_, *label);
+ }
+ return cow_->Initialize(cow_device_, CowWriter::OpenMode::APPEND);
+}
+
OnlineKernelSnapshotWriter::OnlineKernelSnapshotWriter(const CowOptions& options)
: ISnapshotWriter(options) {}
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
index 6a82a00..40f26d6 100644
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ b/fs_mgr/libsnapshot/snapuserd.cpp
@@ -17,6 +17,7 @@
#include <csignal>
#include <libsnapshot/snapuserd.h>
+#include <libsnapshot/snapuserd_client.h>
#include <libsnapshot/snapuserd_daemon.h>
#include <libsnapshot/snapuserd_server.h>
@@ -378,7 +379,11 @@
struct disk_exception* de =
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) continue;
+ if (cow_op->type == kCowFooterOp || cow_op->type == kCowLabelOp) {
+ cowop_iter_->Next();
+ continue;
+ }
+
if (!(cow_op->type == kCowReplaceOp || cow_op->type == kCowZeroOp ||
cow_op->type == kCowCopyOp)) {
LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
@@ -482,13 +487,13 @@
bool Snapuserd::Init() {
backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
if (backing_store_fd_ < 0) {
- LOG(ERROR) << "Open Failed: " << backing_store_device_;
+ PLOG(ERROR) << "Open Failed: " << backing_store_device_;
return false;
}
cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
if (cow_fd_ < 0) {
- LOG(ERROR) << "Open Failed: " << cow_device_;
+ PLOG(ERROR) << "Open Failed: " << cow_device_;
return false;
}
@@ -498,7 +503,7 @@
ctrl_fd_.reset(open(control_path.c_str(), O_RDWR));
if (ctrl_fd_ < 0) {
- LOG(ERROR) << "Unable to open " << control_path;
+ PLOG(ERROR) << "Unable to open " << control_path;
return false;
}
@@ -629,7 +634,11 @@
android::snapshot::Daemon& daemon = android::snapshot::Daemon::Instance();
- daemon.StartServer(argv[1]);
+ std::string socket = android::snapshot::kSnapuserdSocket;
+ if (argc >= 2) {
+ socket = argv[1];
+ }
+ daemon.StartServer(socket);
daemon.Run();
return 0;
diff --git a/fs_mgr/libsnapshot/snapuserd_client.cpp b/fs_mgr/libsnapshot/snapuserd_client.cpp
index 78dbada..35bb29b 100644
--- a/fs_mgr/libsnapshot/snapuserd_client.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_client.cpp
@@ -29,196 +29,155 @@
#include <chrono>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <libsnapshot/snapuserd_client.h>
namespace android {
namespace snapshot {
-bool SnapuserdClient::ConnectToServerSocket(std::string socketname) {
- sockfd_ = 0;
+using namespace std::chrono_literals;
+using android::base::unique_fd;
- 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;
+bool EnsureSnapuserdStarted() {
+ if (android::base::GetProperty("init.svc.snapuserd", "") == "running") {
+ return true;
}
- std::string msg = "query";
+ android::base::SetProperty("ctl.start", "snapuserd");
+ if (!android::base::WaitForProperty("init.svc.snapuserd", "running", 10s)) {
+ LOG(ERROR) << "Timed out waiting for snapuserd to start.";
+ return false;
+ }
+ return true;
+}
- 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();
+SnapuserdClient::SnapuserdClient(android::base::unique_fd&& sockfd) : sockfd_(std::move(sockfd)) {}
+
+static inline bool IsRetryErrno() {
+ return errno == ECONNREFUSED || errno == EINTR;
+}
+
+std::unique_ptr<SnapuserdClient> SnapuserdClient::Connect(const std::string& socket_name,
+ std::chrono::milliseconds timeout_ms) {
+ unique_fd fd;
+ auto start = std::chrono::steady_clock::now();
+ while (true) {
+ fd.reset(socket_local_client(socket_name.c_str(), ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM));
+ if (fd >= 0) break;
+ if (fd < 0 && !IsRetryErrno()) {
+ PLOG(ERROR) << "connect failed: " << socket_name;
+ return nullptr;
+ }
+
+ auto now = std::chrono::steady_clock::now();
+ auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start);
+ if (elapsed >= timeout_ms) {
+ LOG(ERROR) << "Timed out connecting to snapuserd socket: " << socket_name;
+ return nullptr;
+ }
+
+ std::this_thread::sleep_for(100ms);
+ }
+
+ auto client = std::make_unique<SnapuserdClient>(std::move(fd));
+ if (!client->ValidateConnection()) {
+ return nullptr;
+ }
+ return client;
+}
+
+bool SnapuserdClient::ValidateConnection() {
+ if (!Sendmsg("query")) {
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();
+ LOG(ERROR) << "Snapuserd is terminating";
return false;
}
- CHECK(str.find("active") != std::string::npos);
-
+ if (str != "active") {
+ LOG(ERROR) << "Received failure querying daemon";
+ return false;
+ }
return true;
}
-bool SnapuserdClient::ConnectToServer() {
- if (ConnectToServerSocket(GetSocketNameFirstStage())) return true;
+bool SnapuserdClient::Sendmsg(const std::string& msg) {
+ ssize_t numBytesSent = TEMP_FAILURE_RETRY(send(sockfd_, msg.data(), msg.size(), 0));
+ if (numBytesSent < 0) {
+ PLOG(ERROR) << "Send failed";
+ return false;
+ }
- if (ConnectToServerSocket(GetSocketNameSecondStage())) return true;
-
- return false;
+ if ((size_t)numBytesSent < msg.size()) {
+ LOG(ERROR) << "Partial data sent, expected " << msg.size() << " bytes, sent "
+ << numBytesSent;
+ return false;
+ }
+ return true;
}
-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;
+bool SnapuserdClient::WaitForDeviceDelete(const std::string& control_device) {
+ std::string msg = "delete," + control_device;
+ if (!Sendmsg(msg)) {
+ LOG(ERROR) << "Failed to send message " << msg << " to snapuserd";
+ return false;
}
-
- if ((uint)numBytesSent < size) {
- LOG(ERROR) << "Partial data sent " << strerror(errno);
- return -1;
+ std::string response = Receivemsg();
+ if (response != "success") {
+ LOG(ERROR) << "Failed waiting to delete device " << control_device;
+ return false;
}
-
- return 0;
+ return true;
}
std::string SnapuserdClient::Receivemsg() {
- int ret;
- struct timeval tv;
- fd_set set;
char msg[PACKET_SIZE];
- std::string msgStr("fail");
-
- tv.tv_sec = 2;
- tv.tv_usec = 0;
- FD_ZERO(&set);
- FD_SET(sockfd_, &set);
- ret = select(sockfd_ + 1, &set, NULL, NULL, &tv);
- if (ret == -1) { // select failed
- LOG(ERROR) << "Snapuserd:client: Select call failed";
- } else if (ret == 0) { // timeout
- LOG(ERROR) << "Snapuserd:client: Select call timeout";
- } else {
- ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, PACKET_SIZE, 0));
- if (ret < 0) {
- PLOG(ERROR) << "Snapuserd:client: recv failed";
- } else if (ret == 0) {
- LOG(DEBUG) << "Snapuserd:client disconnected";
- } else {
- 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,
- std::string control_device) {
- int ret = 0;
-
- if (!ConnectToServer()) {
- LOG(ERROR) << "Failed to connect to server ";
- return -1;
- }
-
- std::string msg = "start," + cow_device + "," + backing_device + "," + control_device;
-
- ret = Sendmsg(msg.c_str(), msg.size());
+ ssize_t ret = TEMP_FAILURE_RETRY(recv(sockfd_, msg, sizeof(msg), 0));
if (ret < 0) {
+ PLOG(ERROR) << "Snapuserd:client: recv failed";
+ return {};
+ }
+ if (ret == 0) {
+ LOG(DEBUG) << "Snapuserd:client disconnected";
+ return {};
+ }
+ return std::string(msg, ret);
+}
+
+bool SnapuserdClient::StopSnapuserd() {
+ if (!Sendmsg("stop")) {
+ LOG(ERROR) << "Failed to send stop message to snapuserd daemon";
+ return false;
+ }
+
+ sockfd_ = {};
+ 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;
+ if (!Sendmsg(msg)) {
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
- return -1;
+ return false;
}
std::string str = Receivemsg();
-
- if (str.find("fail") != std::string::npos) {
+ if (str != "success") {
LOG(ERROR) << "Failed to receive ack for " << msg << " from snapuserd daemon";
- return -1;
+ return false;
}
- DisconnectFromServer();
-
LOG(DEBUG) << "Snapuserd daemon initialized with " << msg;
- return 0;
+ return true;
}
/*
@@ -254,18 +213,8 @@
*
*/
int SnapuserdClient::RestartSnapuserd(std::vector<std::vector<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) {
+ if (!Sendmsg(msg)) {
LOG(ERROR) << "Failed to send message " << msg << " to snapuserd daemon";
return -1;
}
@@ -279,16 +228,13 @@
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();
+ if (!EnsureSnapuserdStarted()) {
+ LOG(ERROR) << "Failed to start new daemon";
return -1;
}
- LOG(DEBUG) << "Second stage Snapuserd daemon created successfully at socket "
- << GetSocketNameSecondStage();
+ 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
diff --git a/fs_mgr/libsnapshot/snapuserd_daemon.cpp b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
index 8e76618..4c8fa57 100644
--- a/fs_mgr/libsnapshot/snapuserd_daemon.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_daemon.cpp
@@ -20,16 +20,12 @@
namespace android {
namespace snapshot {
-int Daemon::StartServer(std::string socketname) {
- int ret;
-
- ret = server_.Start(socketname);
- if (ret < 0) {
+bool Daemon::StartServer(const std::string& socketname) {
+ if (!server_.Start(socketname)) {
LOG(ERROR) << "Snapuserd daemon failed to start...";
exit(EXIT_FAILURE);
}
-
- return ret;
+ return true;
}
void Daemon::MaskAllSignalsExceptIntAndTerm() {
@@ -51,51 +47,26 @@
}
}
-Daemon::Daemon() {
- is_running_ = true;
-}
-
-bool Daemon::IsRunning() {
- return is_running_;
-}
-
void Daemon::Run() {
- poll_fd_ = std::make_unique<struct pollfd>();
- poll_fd_->fd = server_.GetSocketFd().get();
- poll_fd_->events = POLLIN;
-
sigfillset(&signal_mask_);
sigdelset(&signal_mask_, SIGINT);
sigdelset(&signal_mask_, SIGTERM);
// Masking signals here ensure that after this point, we won't handle INT/TERM
// until after we call into ppoll()
- MaskAllSignals();
signal(SIGINT, Daemon::SignalHandler);
signal(SIGTERM, Daemon::SignalHandler);
signal(SIGPIPE, Daemon::SignalHandler);
LOG(DEBUG) << "Snapuserd-server: ready to accept connections";
- while (IsRunning()) {
- int ret = ppoll(poll_fd_.get(), 1, nullptr, &signal_mask_);
- MaskAllSignalsExceptIntAndTerm();
+ MaskAllSignalsExceptIntAndTerm();
- if (ret == -1) {
- PLOG(ERROR) << "Snapuserd:ppoll error";
- break;
- }
+ server_.Run();
+}
- if (poll_fd_->revents == POLLIN) {
- if (server_.AcceptClient() == static_cast<int>(DaemonOperations::STOP)) {
- Daemon::Instance().is_running_ = false;
- }
- }
-
- // Mask all signals to ensure that is_running_ can't become false between
- // checking it in the while condition and calling into ppoll()
- MaskAllSignals();
- }
+void Daemon::Interrupt() {
+ server_.Interrupt();
}
void Daemon::SignalHandler(int signal) {
@@ -103,7 +74,7 @@
switch (signal) {
case SIGINT:
case SIGTERM: {
- Daemon::Instance().is_running_ = false;
+ Daemon::Instance().Interrupt();
break;
}
case SIGPIPE: {
diff --git a/fs_mgr/libsnapshot/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd_server.cpp
index 53101aa..6b8cdd9 100644
--- a/fs_mgr/libsnapshot/snapuserd_server.cpp
+++ b/fs_mgr/libsnapshot/snapuserd_server.cpp
@@ -35,12 +35,19 @@
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;
+ if (input == "delete") return DaemonOperations::DELETE;
return DaemonOperations::INVALID;
}
+SnapuserdServer::~SnapuserdServer() {
+ // Close any client sockets that were added via AcceptClient().
+ for (size_t i = 1; i < watched_fds_.size(); i++) {
+ close(watched_fds_[i].fd);
+ }
+}
+
std::string SnapuserdServer::GetDaemonStatus() {
std::string msg = "";
@@ -62,180 +69,264 @@
}
}
-// new thread
-void SnapuserdServer::ThreadStart(std::string cow_device, std::string backing_device,
- std::string control_device) {
- Snapuserd snapd(cow_device, backing_device, control_device);
- if (!snapd.Init()) {
- PLOG(ERROR) << "Snapuserd: Init failed";
- return;
- }
-
- while (StopRequested() == false) {
- int ret = snapd.Run();
-
- if (ret < 0) {
- LOG(ERROR) << "Snapuserd: Thread terminating as control device is de-registered";
- break;
- }
- }
-}
-
void SnapuserdServer::ShutdownThreads() {
StopThreads();
- for (auto& client : clients_vec_) {
- auto& th = client->GetThreadHandler();
+ // Acquire the thread list within the lock.
+ std::vector<std::unique_ptr<DmUserHandler>> dm_users;
+ {
+ std::lock_guard<std::mutex> guard(lock_);
+ dm_users = std::move(dm_users_);
+ }
- if (th->joinable()) th->join();
+ for (auto& client : dm_users) {
+ auto& th = client->thread();
+
+ 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));
+const std::string& DmUserHandler::GetControlDevice() const {
+ return snapuserd_->GetControlDevicePath();
+}
+
+bool SnapuserdServer::Sendmsg(android::base::borrowed_fd fd, const std::string& msg) {
+ ssize_t ret = TEMP_FAILURE_RETRY(send(fd.get(), msg.data(), msg.size(), 0));
if (ret < 0) {
PLOG(ERROR) << "Snapuserd:server: send() failed";
- return -1;
+ return false;
}
- if (ret < size) {
- PLOG(ERROR) << "Partial data sent";
- return -1;
+ if (ret < msg.size()) {
+ LOG(ERROR) << "Partial send; expected " << msg.size() << " bytes, sent " << ret;
+ return false;
}
-
- return 0;
+ return true;
}
-std::string SnapuserdServer::Recvmsg(int fd, int* ret) {
- struct timeval tv;
- fd_set set;
+bool SnapuserdServer::Recv(android::base::borrowed_fd fd, std::string* data) {
char msg[MAX_PACKET_SIZE];
+ ssize_t rv = TEMP_FAILURE_RETRY(recv(fd.get(), msg, sizeof(msg), 0));
+ if (rv < 0) {
+ PLOG(ERROR) << "recv failed";
+ return false;
+ }
+ *data = std::string(msg, rv);
+ return true;
+}
- 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 {};
+bool SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::string& str) {
+ const char delim = ',';
+
+ std::vector<std::string> out;
+ Parsemsg(str, delim, out);
+ DaemonOperations op = Resolveop(out[0]);
+
+ switch (op) {
+ case DaemonOperations::START: {
+ // Message format:
+ // start,<cow_device_path>,<source_device_path>,<control_device>
+ //
+ // Start the new thread which binds to dm-user misc device
+ if (out.size() != 4) {
+ LOG(ERROR) << "Malformed start message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+
+ auto snapuserd = std::make_unique<Snapuserd>(out[1], out[2], out[3]);
+ if (!snapuserd->Init()) {
+ LOG(ERROR) << "Failed to initialize Snapuserd";
+ return Sendmsg(fd, "fail");
+ }
+
+ 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()));
+ dm_users_.push_back(std::move(handler));
+ }
+ return Sendmsg(fd, "success");
+ }
+ case DaemonOperations::STOP: {
+ // Message format: stop
+ //
+ // Stop all the threads gracefully and then shutdown the
+ // main thread
+ SetTerminating();
+ ShutdownThreads();
+ return true;
+ }
+ 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.
+ return Sendmsg(fd, GetDaemonStatus());
+ }
+ case DaemonOperations::DELETE: {
+ // Message format:
+ // delete,<cow_device_path>
+ if (out.size() != 2) {
+ LOG(ERROR) << "Malformed delete message, " << out.size() << " parts";
+ return Sendmsg(fd, "fail");
+ }
+ if (!WaitForDelete(out[1])) {
+ return Sendmsg(fd, "fail");
+ }
+ return Sendmsg(fd, "success");
+ }
+ default: {
+ LOG(ERROR) << "Received unknown message type from client";
+ Sendmsg(fd, "fail");
+ return false;
+ }
+ }
+}
+
+void SnapuserdServer::RunThread(DmUserHandler* handler) {
+ while (!StopRequested()) {
+ if (handler->snapuserd()->Run() < 0) {
+ LOG(INFO) << "Snapuserd: Thread terminating as control device is de-registered";
+ break;
+ }
+ }
+
+ 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();
+ }
+}
+
+bool SnapuserdServer::Start(const std::string& socketname) {
+ sockfd_.reset(android_get_control_socket(socketname.c_str()));
+ if (sockfd_ >= 0) {
+ if (listen(sockfd_.get(), 4) < 0) {
+ PLOG(ERROR) << "listen socket failed: " << socketname;
+ return false;
+ }
} 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;
+ 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 false;
}
}
-}
-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>,<control_device>
- //
- // 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], out[3]));
- 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;
- }
+ AddWatchedFd(sockfd_);
LOG(DEBUG) << "Snapuserd server successfully started with socket name " << socketname;
- return 0;
+ return true;
}
-int SnapuserdServer::AcceptClient() {
- int fd = accept(sockfd_.get(), NULL, NULL);
+bool SnapuserdServer::Run() {
+ while (!IsTerminating()) {
+ int rv = TEMP_FAILURE_RETRY(poll(watched_fds_.data(), watched_fds_.size(), -1));
+ if (rv < 0) {
+ PLOG(ERROR) << "poll failed";
+ return false;
+ }
+ if (!rv) {
+ continue;
+ }
+
+ if (watched_fds_[0].revents) {
+ AcceptClient();
+ }
+
+ auto iter = watched_fds_.begin() + 1;
+ while (iter != watched_fds_.end()) {
+ if (iter->revents && !HandleClient(iter->fd, iter->revents)) {
+ close(iter->fd);
+ iter = watched_fds_.erase(iter);
+ } else {
+ iter++;
+ }
+ }
+ }
+ return true;
+}
+
+void SnapuserdServer::AddWatchedFd(android::base::borrowed_fd fd) {
+ struct pollfd p = {};
+ p.fd = fd.get();
+ p.events = POLLIN;
+ watched_fds_.emplace_back(std::move(p));
+}
+
+void SnapuserdServer::AcceptClient() {
+ int fd = TEMP_FAILURE_RETRY(accept4(sockfd_.get(), nullptr, nullptr, SOCK_CLOEXEC));
if (fd < 0) {
- PLOG(ERROR) << "Socket accept failed: " << strerror(errno);
- return -1;
+ PLOG(ERROR) << "accept4 failed";
+ return;
}
- return Receivemsg(fd);
+ AddWatchedFd(fd);
+}
+
+bool SnapuserdServer::HandleClient(android::base::borrowed_fd fd, int revents) {
+ if (revents & POLLHUP) {
+ LOG(DEBUG) << "Snapuserd client disconnected";
+ return false;
+ }
+
+ std::string str;
+ if (!Recv(fd, &str)) {
+ return false;
+ }
+ if (!Receivemsg(fd, str)) {
+ LOG(ERROR) << "Encountered error handling client message, revents: " << revents;
+ return false;
+ }
+ return true;
+}
+
+void SnapuserdServer::Interrupt() {
+ // Force close the socket so poll() fails.
+ sockfd_ = {};
+ 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++;
+ }
+ }
+ return client;
+}
+
+bool SnapuserdServer::WaitForDelete(const std::string& control_device) {
+ auto client = RemoveHandler(control_device);
+
+ // Client already deleted.
+ if (!client) {
+ return true;
+ }
+
+ auto& th = client->thread();
+ if (th.joinable()) {
+ th.join();
+ }
+ return true;
}
} // namespace snapshot
diff --git a/init/Android.mk b/init/Android.mk
index 895f50f..ac31ef1 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -78,7 +78,6 @@
LOCAL_POST_INSTALL_CMD := mkdir -p \
$(TARGET_RAMDISK_OUT)/debug_ramdisk \
$(TARGET_RAMDISK_OUT)/dev \
- $(TARGET_RAMDISK_OUT)/first_stage_ramdisk \
$(TARGET_RAMDISK_OUT)/mnt \
$(TARGET_RAMDISK_OUT)/proc \
$(TARGET_RAMDISK_OUT)/second_stage_resources \
diff --git a/init/README.md b/init/README.md
index 6439393..ab6a885 100644
--- a/init/README.md
+++ b/init/README.md
@@ -172,9 +172,12 @@
This option connects stdin, stdout, and stderr to the console. It is mutually exclusive with the
stdio_to_kmsg option, which only connects stdout and stderr to kmsg.
-`critical`
+`critical [window=<fatal crash window mins>] [target=<fatal reboot target>]`
> This is a device-critical service. If it exits more than four times in
- four minutes or before boot completes, the device will reboot into bootloader.
+ _fatal crash window mins_ minutes or before boot completes, the device
+ will reboot into _fatal reboot target_.
+ The default value of _fatal crash window mins_ is 4, and default value
+ of _fatal reboot target_ is 'bootloader'.
`disabled`
> This service will not automatically start with its class.
diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp
index 554f301..d2a952f 100644
--- a/init/first_stage_init.cpp
+++ b/init/first_stage_init.cpp
@@ -99,6 +99,34 @@
return cmdline.find("androidboot.force_normal_boot=1") != std::string::npos;
}
+// Move e2fsck before switching root, so that it is available at the same path
+// after switching root.
+void PrepareSwitchRoot() {
+ constexpr const char* src = "/system/bin/e2fsck";
+ constexpr const char* dst = "/first_stage_ramdisk/system/bin/e2fsck";
+
+ if (access(dst, X_OK) == 0) {
+ LOG(INFO) << dst << " already exists and it can be executed";
+ return;
+ }
+
+ if (access(src, F_OK) != 0) {
+ PLOG(INFO) << "Not moving " << src << " because it cannot be accessed";
+ return;
+ }
+
+ auto dst_dir = android::base::Dirname(dst);
+ std::error_code ec;
+ if (!fs::create_directories(dst_dir, ec)) {
+ LOG(FATAL) << "Cannot create " << dst_dir << ": " << ec.message();
+ }
+ if (rename(src, dst) != 0) {
+ PLOG(FATAL) << "Cannot move " << src << " to " << dst
+ << ". Either install e2fsck.ramdisk so that it is at the correct place (" << dst
+ << "), or make ramdisk writable";
+ }
+}
+
} // namespace
std::string GetModuleLoadList(bool recovery, const std::string& dir_path) {
@@ -298,6 +326,7 @@
if (ForceNormalBoot(cmdline)) {
mkdir("/first_stage_ramdisk", 0755);
+ PrepareSwitchRoot();
// SwitchRoot() must be called with a mount point as the target, so we bind mount the
// target directory to itself here.
if (mount("/first_stage_ramdisk", "/first_stage_ramdisk", nullptr, MS_BIND, nullptr) != 0) {
diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp
index 8eb2f97..b7d50cf 100644
--- a/init/first_stage_mount.cpp
+++ b/init/first_stage_mount.cpp
@@ -613,7 +613,7 @@
}
// Publish the logical partition names for TransformFstabForDsu
WriteFile(gsi::kGsiLpNamesFile, lp_names);
- TransformFstabForDsu(&fstab_, dsu_partitions);
+ TransformFstabForDsu(&fstab_, active_dsu, dsu_partitions);
}
bool FirstStageMountVBootV1::GetDmVerityDevices(std::set<std::string>* devices) {
diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h
index caa8f8d..2a8bf6c 100644
--- a/init/host_init_stubs.h
+++ b/init/host_init_stubs.h
@@ -20,6 +20,7 @@
#include <sys/socket.h>
#include <sys/types.h>
+#include <optional>
#include <string>
#include <android-base/properties.h>
@@ -41,7 +42,7 @@
}
// reboot_utils.h
-inline void SetFatalRebootTarget() {}
+inline void SetFatalRebootTarget(const std::optional<std::string>& = std::nullopt) {}
inline void __attribute__((noreturn)) InitFatalReboot(int signal_number) {
abort();
}
diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp
index 76460a5..98f6857 100644
--- a/init/reboot_utils.cpp
+++ b/init/reboot_utils.cpp
@@ -19,6 +19,7 @@
#include <sys/syscall.h>
#include <unistd.h>
+#include <optional>
#include <string>
#include <android-base/file.h>
@@ -37,7 +38,7 @@
static std::string init_fatal_reboot_target = "bootloader";
static bool init_fatal_panic = false;
-void SetFatalRebootTarget() {
+void SetFatalRebootTarget(const std::optional<std::string>& reboot_target) {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
cmdline = android::base::Trim(cmdline);
@@ -45,6 +46,11 @@
const char kInitFatalPanicString[] = "androidboot.init_fatal_panic=true";
init_fatal_panic = cmdline.find(kInitFatalPanicString) != std::string::npos;
+ if (reboot_target) {
+ init_fatal_reboot_target = *reboot_target;
+ return;
+ }
+
const char kRebootTargetString[] = "androidboot.init_fatal_reboot_target=";
auto start_pos = cmdline.find(kRebootTargetString);
if (start_pos == std::string::npos) {
diff --git a/init/reboot_utils.h b/init/reboot_utils.h
index 05bb9ae..a0023b9 100644
--- a/init/reboot_utils.h
+++ b/init/reboot_utils.h
@@ -16,6 +16,7 @@
#pragma once
+#include <optional>
#include <string>
#define PROC_SYSRQ "/proc/sysrq-trigger"
@@ -23,7 +24,7 @@
namespace android {
namespace init {
-void SetFatalRebootTarget();
+void SetFatalRebootTarget(const std::optional<std::string>& reboot_target = std::nullopt);
// Determines whether the system is capable of rebooting. This is conservative,
// so if any of the attempts to determine this fail, it will still return true.
bool IsRebootCapable();
diff --git a/init/security.cpp b/init/security.cpp
index 2450d65..ac784a3 100644
--- a/init/security.cpp
+++ b/init/security.cpp
@@ -19,6 +19,7 @@
#include <errno.h>
#include <fcntl.h>
#include <linux/perf_event.h>
+#include <selinux/selinux.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>
@@ -222,6 +223,19 @@
// supporting kernels that precede the perf_event_open hooks (Android common
// kernels 4.4 and 4.9).
Result<void> TestPerfEventSelinuxAction(const BuiltinArguments&) {
+ // Special case: for *development devices* that boot with permissive
+ // SELinux, treat the LSM hooks as present for the effect of lowering the
+ // perf_event_paranoid sysctl. The sysprop is reused for pragmatic reasons,
+ // as there no existing way for init rules to check for permissive boot at
+ // the time of writing.
+ if (ALLOW_PERMISSIVE_SELINUX) {
+ if (!security_getenforce()) {
+ LOG(INFO) << "Permissive SELinux boot, forcing sys.init.perf_lsm_hooks to 1.";
+ SetProperty("sys.init.perf_lsm_hooks", "1");
+ return {};
+ }
+ }
+
// Use a trivial event that will be configured, but not started.
struct perf_event_attr pe = {
.type = PERF_TYPE_SOFTWARE,
diff --git a/init/service.cpp b/init/service.cpp
index 68365b3..ecc86d9 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -45,6 +45,7 @@
#include <android/api-level.h>
#include "mount_namespace.h"
+#include "reboot_utils.h"
#include "selinux.h"
#else
#include "host_init_stubs.h"
@@ -312,20 +313,24 @@
#endif
const bool is_process_updatable = !pre_apexd_ && is_apex_updatable;
- // If we crash > 4 times in 4 minutes or before boot_completed,
+ // If we crash > 4 times in 'fatal_crash_window_' minutes or before boot_completed,
// reboot into bootloader or set crashing property
boot_clock::time_point now = boot_clock::now();
if (((flags_ & SVC_CRITICAL) || is_process_updatable) && !(flags_ & SVC_RESTART)) {
bool boot_completed = android::base::GetBoolProperty("sys.boot_completed", false);
- if (now < time_crashed_ + 4min || !boot_completed) {
+ if (now < time_crashed_ + fatal_crash_window_ || !boot_completed) {
if (++crash_count_ > 4) {
+ auto exit_reason = boot_completed ?
+ "in " + std::to_string(fatal_crash_window_.count()) + " minutes" :
+ "before boot completed";
if (flags_ & SVC_CRITICAL) {
- // Aborts into bootloader
+ // Aborts into `fatal_reboot_target_'.
+ SetFatalRebootTarget(fatal_reboot_target_);
LOG(FATAL) << "critical process '" << name_ << "' exited 4 times "
- << (boot_completed ? "in 4 minutes" : "before boot completed");
+ << exit_reason;
} else {
LOG(ERROR) << "updatable process '" << name_ << "' exited 4 times "
- << (boot_completed ? "in 4 minutes" : "before boot completed");
+ << exit_reason;
// Notifies update_verifier and apexd
SetProperty("sys.init.updatable_crashing_process_name", name_);
SetProperty("sys.init.updatable_crashing", "1");
diff --git a/init/service.h b/init/service.h
index 34ed5ef..bc5c90f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -155,6 +155,8 @@
android::base::boot_clock::time_point time_started_; // time of last start
android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
+ std::chrono::minutes fatal_crash_window_ = 4min; // fatal() when more than 4 crashes in it
+ std::optional<std::string> fatal_reboot_target_; // reboot target of fatal handler
std::optional<CapSet> capabilities_;
ProcessAttributes proc_attr_;
diff --git a/init/service_parser.cpp b/init/service_parser.cpp
index bdac077..97621da 100644
--- a/init/service_parser.cpp
+++ b/init/service_parser.cpp
@@ -93,6 +93,39 @@
}
Result<void> ServiceParser::ParseCritical(std::vector<std::string>&& args) {
+ std::optional<std::string> fatal_reboot_target;
+ std::optional<std::chrono::minutes> fatal_crash_window;
+
+ for (auto it = args.begin() + 1; it != args.end(); ++it) {
+ auto arg = android::base::Split(*it, "=");
+ if (arg.size() != 2) {
+ return Error() << "critical: Argument '" << *it << "' is not supported";
+ } else if (arg[0] == "target") {
+ fatal_reboot_target = arg[1];
+ } else if (arg[0] == "window") {
+ int minutes;
+ auto window = ExpandProps(arg[1]);
+ if (!window.ok()) {
+ return Error() << "critical: Could not expand argument ': " << arg[1];
+ }
+ if (*window == "off") {
+ return {};
+ }
+ if (!ParseInt(*window, &minutes, 0)) {
+ return Error() << "critical: 'fatal_crash_window' must be an integer > 0";
+ }
+ fatal_crash_window = std::chrono::minutes(minutes);
+ } else {
+ return Error() << "critical: Argument '" << *it << "' is not supported";
+ }
+ }
+
+ if (fatal_reboot_target) {
+ service_->fatal_reboot_target_ = *fatal_reboot_target;
+ }
+ if (fatal_crash_window) {
+ service_->fatal_crash_window_ = *fatal_crash_window;
+ }
service_->flags_ |= SVC_CRITICAL;
return {};
}
@@ -506,7 +539,7 @@
{"capabilities", {0, kMax, &ServiceParser::ParseCapabilities}},
{"class", {1, kMax, &ServiceParser::ParseClass}},
{"console", {0, 1, &ServiceParser::ParseConsole}},
- {"critical", {0, 0, &ServiceParser::ParseCritical}},
+ {"critical", {0, 2, &ServiceParser::ParseCritical}},
{"disabled", {0, 0, &ServiceParser::ParseDisabled}},
{"enter_namespace", {2, 2, &ServiceParser::ParseEnterNamespace}},
{"file", {2, 2, &ServiceParser::ParseFile}},
diff --git a/libbacktrace b/libbacktrace
deleted file mode 120000
index 571194c..0000000
--- a/libbacktrace
+++ /dev/null
@@ -1 +0,0 @@
-../unwinding/libbacktrace
\ No newline at end of file
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 0c75dc7..284c0b9 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -34,6 +34,7 @@
vendor_available: true,
recovery_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
host_supported: true,
apex_available: [
"//apex_available:platform",
@@ -61,6 +62,7 @@
vendor_available: true,
recovery_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
@@ -146,6 +148,7 @@
support_system_process: true,
},
recovery_available: true,
+ vendor_ramdisk_available: true,
host_supported: true,
apex_available: [
"//apex_available:platform",
diff --git a/liblog b/liblog
deleted file mode 120000
index 71443ae..0000000
--- a/liblog
+++ /dev/null
@@ -1 +0,0 @@
-../logging/liblog
\ No newline at end of file
diff --git a/libpackagelistparser/Android.bp b/libpackagelistparser/Android.bp
index b56dcdb..c3f8692 100644
--- a/libpackagelistparser/Android.bp
+++ b/libpackagelistparser/Android.bp
@@ -1,5 +1,7 @@
cc_library {
name: "libpackagelistparser",
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
srcs: ["packagelistparser.cpp"],
shared_libs: ["liblog"],
diff --git a/libprocessgroup/Android.bp b/libprocessgroup/Android.bp
index bda11e9..71e2b91 100644
--- a/libprocessgroup/Android.bp
+++ b/libprocessgroup/Android.bp
@@ -1,6 +1,8 @@
cc_library_headers {
name: "libprocessgroup_headers",
vendor_available: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
host_supported: true,
native_bridge_supported: true,
@@ -30,6 +32,8 @@
name: "libprocessgroup",
host_supported: true,
native_bridge_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
vendor_available: true,
vndk: {
diff --git a/libprocessgroup/cgrouprc/Android.bp b/libprocessgroup/cgrouprc/Android.bp
index 0af75bb..bb59942 100644
--- a/libprocessgroup/cgrouprc/Android.bp
+++ b/libprocessgroup/cgrouprc/Android.bp
@@ -15,6 +15,8 @@
cc_library {
name: "libcgrouprc",
host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
// Do not ever mark this as vendor_available; otherwise, vendor modules
// that links to the static library will behave unexpectedly. All on-device
@@ -22,6 +24,7 @@
// defined below. The static library is built for tests.
vendor_available: false,
native_bridge_supported: true,
+ llndk_stubs: "libcgrouprc.llndk",
srcs: [
"cgroup_controller.cpp",
"cgroup_file.cpp",
@@ -54,7 +57,7 @@
}
llndk_library {
- name: "libcgrouprc",
+ name: "libcgrouprc.llndk",
symbol_file: "libcgrouprc.llndk.txt",
native_bridge_supported: true,
export_include_dirs: [
diff --git a/libprocessgroup/cgrouprc_format/Android.bp b/libprocessgroup/cgrouprc_format/Android.bp
index 559a869..6428930 100644
--- a/libprocessgroup/cgrouprc_format/Android.bp
+++ b/libprocessgroup/cgrouprc_format/Android.bp
@@ -15,6 +15,8 @@
cc_library_static {
name: "libcgrouprc_format",
host_supported: true,
+ ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
native_bridge_supported: true,
srcs: [
diff --git a/libprocessgroup/profiles/cgroups.json b/libprocessgroup/profiles/cgroups.json
index 4518487..5b7a28a 100644
--- a/libprocessgroup/profiles/cgroups.json
+++ b/libprocessgroup/profiles/cgroups.json
@@ -32,13 +32,6 @@
"Mode": "0700",
"UID": "root",
"GID": "system"
- },
- {
- "Controller": "schedtune",
- "Path": "/dev/stune",
- "Mode": "0755",
- "UID": "system",
- "GID": "system"
}
],
"Cgroups2": {
diff --git a/libprocessgroup/profiles/task_profiles.json b/libprocessgroup/profiles/task_profiles.json
index c4dbf8e..ea0064f 100644
--- a/libprocessgroup/profiles/task_profiles.json
+++ b/libprocessgroup/profiles/task_profiles.json
@@ -31,16 +31,6 @@
"File": "memory.swappiness"
},
{
- "Name": "STuneBoost",
- "Controller": "schedtune",
- "File": "schedtune.boost"
- },
- {
- "Name": "STunePreferIdle",
- "Controller": "schedtune",
- "File": "schedtune.prefer_idle"
- },
- {
"Name": "UClampMin",
"Controller": "cpu",
"File": "cpu.uclamp.min"
@@ -51,6 +41,11 @@
"File": "cpu.uclamp.max"
},
{
+ "Name": "UClampLatencySensitive",
+ "Controller": "cpu",
+ "File": "cpu.uclamp.latency_sensitive"
+ },
+ {
"Name": "FreezerState",
"Controller": "freezer",
"File": "cgroup.freeze"
@@ -65,7 +60,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "schedtune",
+ "Controller": "cpu",
"Path": "background"
}
}
@@ -104,7 +99,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "schedtune",
+ "Controller": "cpu",
"Path": ""
}
}
@@ -117,7 +112,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "schedtune",
+ "Controller": "cpu",
"Path": "foreground"
}
}
@@ -130,7 +125,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "schedtune",
+ "Controller": "cpu",
"Path": "top-app"
}
}
@@ -143,7 +138,7 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "schedtune",
+ "Controller": "cpu",
"Path": "rt"
}
}
@@ -156,12 +151,25 @@
"Name": "JoinCgroup",
"Params":
{
- "Controller": "schedtune",
+ "Controller": "cpu",
"Path": "camera-daemon"
}
}
]
},
+ {
+ "Name": "NNApiHALPerformance",
+ "Actions": [
+ {
+ "Name": "JoinCgroup",
+ "Params":
+ {
+ "Controller": "cpu",
+ "Path": "nnapi-hal"
+ }
+ }
+ ]
+ },
{
"Name": "CpuPolicySpread",
@@ -170,7 +178,7 @@
"Name": "SetAttribute",
"Params":
{
- "Name": "STunePreferIdle",
+ "Name": "UClampLatencySensitive",
"Value": "1"
}
}
@@ -183,7 +191,7 @@
"Name": "SetAttribute",
"Params":
{
- "Name": "STunePreferIdle",
+ "Name": "UClampLatencySensitive",
"Value": "0"
}
}
diff --git a/libsync/Android.bp b/libsync/Android.bp
index bad6230..4828892 100644
--- a/libsync/Android.bp
+++ b/libsync/Android.bp
@@ -25,6 +25,7 @@
recovery_available: true,
native_bridge_supported: true,
defaults: ["libsync_defaults"],
+ llndk_stubs: "libsync.llndk",
stubs: {
symbol_file: "libsync.map.txt",
versions: [
@@ -34,7 +35,7 @@
}
llndk_library {
- name: "libsync",
+ name: "libsync.llndk",
symbol_file: "libsync.map.txt",
export_include_dirs: ["include"],
}
diff --git a/libsystem/Android.bp b/libsystem/Android.bp
index db61669..12c946c 100644
--- a/libsystem/Android.bp
+++ b/libsystem/Android.bp
@@ -2,6 +2,7 @@
name: "libsystem_headers",
vendor_available: true,
recovery_available: true,
+ vendor_ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
diff --git a/libunwindstack b/libunwindstack
deleted file mode 120000
index 9a12403..0000000
--- a/libunwindstack
+++ /dev/null
@@ -1 +0,0 @@
-../unwinding/libunwindstack
\ No newline at end of file
diff --git a/libutils/Android.bp b/libutils/Android.bp
index e53e89b..8ee16f3 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -16,6 +16,7 @@
name: "libutils_headers",
vendor_available: true,
recovery_available: true,
+ vendor_ramdisk_available: true,
host_supported: true,
native_bridge_supported: true,
apex_available: [
diff --git a/libutils/Unicode.cpp b/libutils/Unicode.cpp
index b6e457b..843a81a 100644
--- a/libutils/Unicode.cpp
+++ b/libutils/Unicode.cpp
@@ -359,49 +359,6 @@
// UTF-8
// --------------------------------------------------------------------------
-ssize_t utf8_length(const char *src)
-{
- const char *cur = src;
- size_t ret = 0;
- while (*cur != '\0') {
- const char first_char = *cur++;
- if ((first_char & 0x80) == 0) { // ASCII
- ret += 1;
- continue;
- }
- // (UTF-8's character must not be like 10xxxxxx,
- // but 110xxxxx, 1110xxxx, ... or 1111110x)
- if ((first_char & 0x40) == 0) {
- return -1;
- }
-
- int32_t mask, to_ignore_mask;
- size_t num_to_read = 0;
- char32_t utf32 = 0;
- for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0x80;
- num_to_read < 5 && (first_char & mask);
- num_to_read++, to_ignore_mask |= mask, mask >>= 1) {
- if ((*cur & 0xC0) != 0x80) { // must be 10xxxxxx
- return -1;
- }
- // 0x3F == 00111111
- utf32 = (utf32 << 6) + (*cur++ & 0x3F);
- }
- // "first_char" must be (110xxxxx - 11110xxx)
- if (num_to_read == 5) {
- return -1;
- }
- to_ignore_mask |= mask;
- utf32 |= ((~to_ignore_mask) & first_char) << (6 * (num_to_read - 1));
- if (utf32 > kUnicodeMaxCodepoint) {
- return -1;
- }
-
- ret += num_to_read;
- }
- return ret;
-}
-
ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len)
{
if (src == nullptr || src_len == 0) {
diff --git a/libutils/include/utils/Unicode.h b/libutils/include/utils/Unicode.h
index fc6712d..0087383 100644
--- a/libutils/include/utils/Unicode.h
+++ b/libutils/include/utils/Unicode.h
@@ -111,24 +111,6 @@
void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len);
/**
- * Returns the length of "src" when "src" is valid UTF-8 string.
- * Returns 0 if src is NULL or 0-length string. Returns -1 when the source
- * is an invalid string.
- *
- * This function should be used to determine whether "src" is valid UTF-8
- * characters with valid unicode codepoints. "src" must be nul-terminated.
- *
- * If you are going to use other utf8_to_... functions defined in this header
- * with string which may not be valid UTF-8 with valid codepoint (form 0 to
- * 0x10FFFF), you should use this function before calling others, since the
- * other functions do not check whether the string is valid UTF-8 or not.
- *
- * If you do not care whether "src" is valid UTF-8 or not, you should use
- * strlen() as usual, which should be much faster.
- */
-ssize_t utf8_length(const char *src);
-
-/**
* Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case
* it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you
* can ask to log a message and fail in case the invalid utf8 could have caused an override if no
diff --git a/libvndksupport/Android.bp b/libvndksupport/Android.bp
index b92c76c..a5cece4 100644
--- a/libvndksupport/Android.bp
+++ b/libvndksupport/Android.bp
@@ -1,6 +1,7 @@
cc_library {
name: "libvndksupport",
native_bridge_supported: true,
+ llndk_stubs: "libvndksupport.llndk",
srcs: ["linker.cpp"],
cflags: [
"-Wall",
@@ -20,7 +21,7 @@
}
llndk_library {
- name: "libvndksupport",
+ name: "libvndksupport.llndk",
native_bridge_supported: true,
symbol_file: "libvndksupport.map.txt",
export_include_dirs: ["include"],
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 108d15a..2d7e9cb 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -3,6 +3,7 @@
host_supported: true,
vendor_available: true,
ramdisk_available: true,
+ vendor_ramdisk_available: true,
recovery_available: true,
native_bridge_supported: true,
srcs: ["property_info_parser.cpp"],
diff --git a/rootdir/init.rc b/rootdir/init.rc
index d5b1d72..746fc61 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -148,6 +148,27 @@
chmod 0664 /dev/stune/top-app/tasks
chmod 0664 /dev/stune/rt/tasks
+ # cpuctl hierarchy for devices using utilclamp
+ mkdir /dev/cpuctl/foreground
+ mkdir /dev/cpuctl/background
+ mkdir /dev/cpuctl/top-app
+ mkdir /dev/cpuctl/rt
+ chown system system /dev/cpuctl
+ chown system system /dev/cpuctl/foreground
+ chown system system /dev/cpuctl/background
+ chown system system /dev/cpuctl/top-app
+ chown system system /dev/cpuctl/rt
+ chown system system /dev/cpuctl/tasks
+ chown system system /dev/cpuctl/foreground/tasks
+ chown system system /dev/cpuctl/background/tasks
+ chown system system /dev/cpuctl/top-app/tasks
+ chown system system /dev/cpuctl/rt/tasks
+ chmod 0664 /dev/cpuctl/tasks
+ chmod 0664 /dev/cpuctl/foreground/tasks
+ chmod 0664 /dev/cpuctl/background/tasks
+ chmod 0664 /dev/cpuctl/top-app/tasks
+ chmod 0664 /dev/cpuctl/rt/tasks
+
# Create an stune group for NNAPI HAL processes
mkdir /dev/stune/nnapi-hal
chown system system /dev/stune/nnapi-hal
@@ -156,6 +177,14 @@
write /dev/stune/nnapi-hal/schedtune.boost 1
write /dev/stune/nnapi-hal/schedtune.prefer_idle 1
+ # cpuctl hierarchy for devices using utilclamp
+ mkdir /dev/cpuctl/nnapi-hal
+ chown system system /dev/cpuctl/nnapi-hal
+ chown system system /dev/cpuctl/nnapi-hal/tasks
+ chmod 0664 /dev/cpuctl/nnapi-hal/tasks
+ write /dev/cpuctl/nnapi-hal/cpu.uclamp.min 1
+ write /dev/cpuctl/nnapi-hal/cpu.uclamp.latency_sensitive 1
+
# Create blkio group and apply initial settings.
# This feature needs kernel to support it, and the
# device's init.rc must actually set the correct values.
@@ -1041,6 +1070,14 @@
write /proc/sys/kernel/perf_cpu_time_max_percent 25
write /proc/sys/kernel/perf_event_mlock_kb 516
+# This property can be set only on userdebug/eng. See neverallow rule in
+# /system/sepolicy/private/property.te .
+on property:security.lower_kptr_restrict=1
+ write /proc/sys/kernel/kptr_restrict 0
+
+on property:security.lower_kptr_restrict=0
+ write /proc/sys/kernel/kptr_restrict 2
+
# on shutdown
# In device's init.rc, this trigger can be used to do device-specific actions
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index e827cf5..9469a48 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -13,3 +13,4 @@
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
+ critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index adc7031..98dc088 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -13,3 +13,4 @@
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
+ critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index fb9e99b..3eee180 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -13,6 +13,7 @@
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance
+ critical window=${zygote.critical_window.minute:-off} target=zygote-fatal
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
index f83c43e..5e013fe 100644
--- a/shell_and_utilities/Android.bp
+++ b/shell_and_utilities/Android.bp
@@ -51,3 +51,13 @@
"toybox_vendor",
],
}
+
+// shell and utilities for first stage console. The list of binaries are
+// enough for debugging purposes.
+phony {
+ name: "shell_and_utilities_vendor_ramdisk",
+ required: [
+ "sh.vendor_ramdisk",
+ "toybox.vendor_ramdisk",
+ ],
+}