Merge "Switch root to /system in first stage mount"
diff --git a/adb/daemon/remount_service.cpp b/adb/daemon/remount_service.cpp
index f7017dd..ed5f944 100644
--- a/adb/daemon/remount_service.cpp
+++ b/adb/daemon/remount_service.cpp
@@ -38,7 +38,6 @@
#include <android-base/properties.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <ext4_utils/ext4_utils.h>
#include <fs_mgr.h>
#include <fs_mgr_overlayfs.h>
@@ -96,27 +95,6 @@
return result;
}
-static bool fs_has_shared_blocks(const std::string& mount_point, const std::string& device) {
- std::string path = mount_point + "/lost+found";
- struct statfs fs;
- if (statfs(path.c_str(), &fs) == -1 || fs.f_type != EXT4_SUPER_MAGIC) {
- return false;
- }
- unique_fd fd(unix_open(device.c_str(), O_RDONLY));
- if (fd < 0) {
- return false;
- }
- struct ext4_super_block sb;
- if (lseek64(fd, 1024, SEEK_SET) < 0 || unix_read(fd, &sb, sizeof(sb)) < 0) {
- return false;
- }
- struct fs_info info;
- if (ext4_parse_sb(&sb, &info) < 0) {
- return false;
- }
- return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
-}
-
static bool can_unshare_blocks(int fd, const char* dev) {
const char* E2FSCK_BIN = "/system/bin/e2fsck";
if (access(E2FSCK_BIN, X_OK)) {
@@ -250,7 +228,7 @@
std::set<std::string> dedup;
for (const auto& partition : partitions) {
std::string dev = find_mount(partition.c_str(), partition == "/");
- if (dev.empty() || !fs_has_shared_blocks(partition, dev)) {
+ if (dev.empty() || !fs_mgr_has_shared_blocks(partition, dev)) {
continue;
}
if (can_unshare_blocks(fd.get(), dev.c_str())) {
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index dfb7a6a..e2ea480 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -29,6 +29,7 @@
#include <regex>
#include <thread>
+#include <android/fdsan.h>
#include <android/set_abort_message.h>
#include <android-base/file.h>
@@ -801,6 +802,31 @@
AssertDeath(SIGABRT);
}
+TEST_F(CrasherTest, fdsan_warning_abort_message) {
+ int intercept_result;
+ unique_fd output_fd;
+
+ StartProcess([]() {
+ android_fdsan_set_error_level(ANDROID_FDSAN_ERROR_LEVEL_WARN_ONCE);
+ unique_fd fd(open("/dev/null", O_RDONLY | O_CLOEXEC));
+ if (fd == -1) {
+ abort();
+ }
+ close(fd.get());
+ _exit(0);
+ });
+
+ StartIntercept(&output_fd);
+ FinishCrasher();
+ AssertDeath(0);
+ FinishIntercept(&intercept_result);
+ ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
+
+ std::string result;
+ ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, "Abort message: 'attempted to close");
+}
+
TEST(crash_dump, zombie) {
pid_t forkpid = fork();
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index 91e6f71..15557b6 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -457,14 +457,14 @@
info = nullptr;
}
- struct siginfo si = {};
+ struct siginfo dummy_info = {};
if (!info) {
- memset(&si, 0, sizeof(si));
- si.si_signo = signal_number;
- si.si_code = SI_USER;
- si.si_pid = __getpid();
- si.si_uid = getuid();
- info = &si;
+ memset(&dummy_info, 0, sizeof(dummy_info));
+ dummy_info.si_signo = signal_number;
+ dummy_info.si_code = SI_USER;
+ dummy_info.si_pid = __getpid();
+ dummy_info.si_uid = getuid();
+ info = &dummy_info;
} else if (info->si_code >= 0 || info->si_code == SI_TKILL) {
// rt_tgsigqueueinfo(2)'s documentation appears to be incorrect on kernels
// that contain commit 66dd34a (3.9+). The manpage claims to only allow
@@ -473,8 +473,18 @@
}
void* abort_message = nullptr;
- if (signal_number != DEBUGGER_SIGNAL && g_callbacks.get_abort_message) {
- abort_message = g_callbacks.get_abort_message();
+ if (signal_number == DEBUGGER_SIGNAL) {
+ if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) {
+ // Allow for the abort message to be explicitly specified via the sigqueue value.
+ // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone.
+ uintptr_t value = reinterpret_cast<uintptr_t>(info->si_ptr);
+ abort_message = reinterpret_cast<void*>(value & ~1);
+ info->si_ptr = reinterpret_cast<void*>(value & 1);
+ }
+ } else {
+ if (g_callbacks.get_abort_message) {
+ abort_message = g_callbacks.get_abort_message();
+ }
}
// If sival_int is ~0, it means that the fallback handler has been called
diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp
index 03e4e8e..743a2e7 100644
--- a/debuggerd/libdebuggerd/open_files_list.cpp
+++ b/debuggerd/libdebuggerd/open_files_list.cpp
@@ -18,6 +18,7 @@
#include "libdebuggerd/open_files_list.h"
+#include <android/fdsan.h>
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
@@ -122,8 +123,10 @@
const std::optional<std::string>& path = entry.path;
const std::optional<uint64_t>& fdsan_owner = entry.fdsan_owner;
if (path && fdsan_owner) {
- _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %#" PRIx64 ")\n", prefix, fd,
- path->c_str(), *fdsan_owner);
+ const char* type = android_fdsan_get_tag_type(*fdsan_owner);
+ uint64_t value = android_fdsan_get_tag_value(*fdsan_owner);
+ _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %s %#" PRIx64 ")\n", prefix, fd,
+ path->c_str(), type, value);
} else if (path && !fdsan_owner) {
_LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (unowned)\n", prefix, fd, path->c_str());
} else if (!path && fdsan_owner) {
diff --git a/fastboot/Android.bp b/fastboot/Android.bp
index 3414d53..f32b5d5 100644
--- a/fastboot/Android.bp
+++ b/fastboot/Android.bp
@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// This is required because no Android.bp can include a library defined in an
+// Android.mk. Eventually should kill libfastboot (defined in Android.mk)
cc_library_host_static {
name: "libfastboot2",
@@ -56,6 +58,23 @@
linux: {
srcs: ["usb_linux.cpp"],
},
+
+ darwin: {
+ srcs: ["usb_osx.cpp"],
+
+ host_ldlibs: [
+ "-framework CoreFoundation",
+ "-framework IOKit",
+ ],
+ },
+
+ windows: {
+ srcs: ["usb_windows.cpp"],
+
+ host_ldlibs: [
+ "-lws2_32",
+ ],
+ },
},
cflags: [
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index a679143..7da0a9f 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -64,6 +64,7 @@
LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
LOCAL_CFLAGS := $(fastboot_cflags)
LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
+LOCAL_CPP_STD := c++17
LOCAL_CXX_STL := $(fastboot_stl)
LOCAL_HEADER_LIBRARIES := bootimg_headers
LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
@@ -82,7 +83,6 @@
LOCAL_CFLAGS := $(fastboot_cflags)
LOCAL_CFLAGS_darwin := $(fastboot_cflags_darwin)
-LOCAL_CPP_STD := c++17
LOCAL_CXX_STL := $(fastboot_stl)
LOCAL_HEADER_LIBRARIES := bootimg_headers
LOCAL_LDLIBS_darwin := $(fastboot_ldlibs_darwin)
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index 6890643..6a52b12 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -79,12 +79,18 @@
static std::vector<std::unique_ptr<Action>> action_list;
static fastboot::FastBootDriver* fb = nullptr;
+static constexpr char kStatusFormat[] = "%-50s ";
+
void fb_init(fastboot::FastBootDriver& fbi) {
fb = &fbi;
auto cb = [](std::string& info) { fprintf(stderr, "(bootloader) %s\n", info.c_str()); };
fb->SetInfoCallback(cb);
}
+void fb_reinit(Transport* transport) {
+ fb->set_transport(transport);
+}
+
const std::string fb_get_error() {
return fb->Error();
}
@@ -332,7 +338,7 @@
for (auto& a : action_list) {
a->start = now();
if (!a->msg.empty()) {
- fprintf(stderr, "%-50s ", a->msg.c_str());
+ fprintf(stderr, kStatusFormat, a->msg.c_str());
verbose("\n");
}
if (a->op == OP_DOWNLOAD) {
@@ -372,3 +378,20 @@
action_list.clear();
return status;
}
+
+bool fb_reboot_to_userspace() {
+ // First ensure that the queue is flushed.
+ fb_execute_queue();
+
+ fprintf(stderr, kStatusFormat, "Rebooting to userspace fastboot");
+ verbose("\n");
+
+ if (fb->RebootTo("fastboot") != fastboot::RetCode::SUCCESS) {
+ fprintf(stderr, "FAILED (%s)\n", fb->Error().c_str());
+ return false;
+ }
+ fprintf(stderr, "OKAY\n");
+
+ fb->set_transport(nullptr);
+ return true;
+}
diff --git a/fastboot/engine.h b/fastboot/engine.h
index 8aebdd7..f098ca7 100644
--- a/fastboot/engine.h
+++ b/fastboot/engine.h
@@ -50,6 +50,7 @@
/* engine.c - high level command queue engine */
void fb_init(fastboot::FastBootDriver& fbi);
+void fb_reinit(Transport* transport);
bool fb_getvar(const std::string& key, std::string* value);
void fb_queue_flash(const std::string& partition, void* data, uint32_t sz);
@@ -74,6 +75,7 @@
void fb_queue_resize_partition(const std::string& partition, const std::string& size);
int64_t fb_execute_queue();
void fb_set_active(const std::string& slot);
+bool fb_reboot_to_userspace();
/* Current product */
extern char cur_product[FB_RESPONSE_SZ + 1];
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index db6d5d6..331b0c8 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -104,45 +104,59 @@
void* data;
int64_t sz;
int fd;
+ int64_t image_size;
};
-static struct {
+enum class ImageType {
+ // Must be flashed for device to boot into the kernel.
+ BootCritical,
+ // Normal partition to be flashed during "flashall".
+ Normal,
+ // Partition that is never flashed during "flashall".
+ Extra
+};
+
+struct Image {
const char* nickname;
const char* img_name;
const char* sig_name;
const char* part_name;
bool optional_if_no_image;
- bool optional_if_no_partition;
+ ImageType type;
bool IsSecondary() const { return nickname == nullptr; }
-} images[] = {
+};
+
+static Image images[] = {
// clang-format off
- { "boot", "boot.img", "boot.sig", "boot", false, false },
- { nullptr, "boot_other.img", "boot.sig", "boot", true, false },
- { "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, false },
- { "dts", "dt.img", "dt.sig", "dts", true, false },
- { "odm", "odm.img", "odm.sig", "odm", true, false },
- { "product", "product.img", "product.sig", "product", true, false },
+ { "boot", "boot.img", "boot.sig", "boot", false, ImageType::BootCritical },
+ { nullptr, "boot_other.img", "boot.sig", "boot", true, ImageType::Normal },
+ { "cache", "cache.img", "cache.sig", "cache", true, ImageType::Extra },
+ { "dtbo", "dtbo.img", "dtbo.sig", "dtbo", true, ImageType::BootCritical },
+ { "dts", "dt.img", "dt.sig", "dts", true, ImageType::BootCritical },
+ { "odm", "odm.img", "odm.sig", "odm", true, ImageType::Normal },
+ { "product", "product.img", "product.sig", "product", true, ImageType::Normal },
{ "product_services",
"product_services.img",
"product_services.sig",
"product_services",
- true, true },
- { "recovery", "recovery.img", "recovery.sig", "recovery", true, false },
- { "super", "super.img", "super.sig", "super", true, true },
- { "system", "system.img", "system.sig", "system", false, true },
- { nullptr, "system_other.img", "system.sig", "system", true, false },
- { "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, false },
- { "vendor", "vendor.img", "vendor.sig", "vendor", true, true },
- { nullptr, "vendor_other.img", "vendor.sig", "vendor", true, false },
+ true, ImageType::Normal },
+ { "recovery", "recovery.img", "recovery.sig", "recovery", true, ImageType::BootCritical },
+ { "super", "super.img", "super.sig", "super", true, ImageType::Extra },
+ { "system", "system.img", "system.sig", "system", false, ImageType::Normal },
+ { nullptr, "system_other.img", "system.sig", "system", true, ImageType::Normal },
+ { "userdata", "userdata.img", "userdata.sig", "userdata", true, ImageType::Extra },
+ { "vbmeta", "vbmeta.img", "vbmeta.sig", "vbmeta", true, ImageType::BootCritical },
+ { "vendor", "vendor.img", "vendor.sig", "vendor", true, ImageType::Normal },
+ { nullptr, "vendor_other.img", "vendor.sig", "vendor", true, ImageType::Normal },
// clang-format on
};
-static std::string find_item_given_name(const char* img_name) {
+static std::string find_item_given_name(const std::string& img_name) {
char* dir = getenv("ANDROID_PRODUCT_OUT");
if (dir == nullptr || dir[0] == '\0') {
die("ANDROID_PRODUCT_OUT not set");
}
- return android::base::StringPrintf("%s/%s", dir, img_name);
+ return std::string(dir) + "/" + img_name;
}
static std::string find_item(const std::string& item) {
@@ -152,9 +166,6 @@
}
}
- if (item == "userdata") return find_item_given_name("userdata.img");
- if (item == "cache") return find_item_given_name("cache.img");
-
fprintf(stderr, "unknown partition '%s'\n", item.c_str());
return "";
}
@@ -242,13 +253,8 @@
// The returned Transport is a singleton, so multiple calls to this function will return the same
// object, and the caller should not attempt to delete the returned Transport.
static Transport* open_device() {
- static Transport* transport = nullptr;
bool announce = true;
- if (transport != nullptr) {
- return transport;
- }
-
Socket::Protocol protocol = Socket::Protocol::kTcp;
std::string host;
int port = 0;
@@ -273,6 +279,7 @@
}
}
+ Transport* transport = nullptr;
while (true) {
if (!host.empty()) {
std::string error;
@@ -638,8 +645,7 @@
// "require partition-exists=x" is a special case, added because of the trouble we had when
// Pixel 2 shipped with new partitions and users used old versions of fastboot to flash them,
// missing out new partitions. A device with new partitions can use "partition-exists" to
- // override the fields `optional_if_no_image` and 'optional_if_no_partition' in the `images`
- // array.
+ // override the fields `optional_if_no_image` in the `images` array.
if (!strcmp(name, "partition-exists")) {
const char* partition_name = val[0];
std::string has_slot;
@@ -651,7 +657,6 @@
for (size_t i = 0; i < arraysize(images); ++i) {
if (images[i].nickname && !strcmp(images[i].nickname, partition_name)) {
images[i].optional_if_no_image = false;
- images[i].optional_if_no_partition = false;
known_partition = true;
}
}
@@ -773,6 +778,13 @@
return false;
}
+ if (sparse_file* s = sparse_file_import_auto(fd, false, false)) {
+ buf->image_size = sparse_file_len(s, false, false);
+ sparse_file_destroy(s);
+ } else {
+ buf->image_size = sz;
+ }
+
lseek64(fd, 0, SEEK_SET);
int64_t limit = get_sparse_limit(sz);
if (limit) {
@@ -1044,6 +1056,11 @@
}
}
+static bool is_userspace_fastboot() {
+ std::string value;
+ return fb_getvar("is-userspace", &value) && value == "yes";
+}
+
static bool if_partition_exists(const std::string& partition, const std::string& slot) {
std::string has_slot;
std::string partition_name = partition;
@@ -1114,11 +1131,6 @@
die("non-optional file %s missing", images[i].img_name);
}
- if (images[i].optional_if_no_partition &&
- !if_partition_exists(images[i].part_name, slot)) {
- continue;
- }
-
fastboot_buffer buf;
if (!load_buf_fd(fd, &buf)) {
die("cannot load %s from flash: %s", images[i].img_name, strerror(errno));
@@ -1158,7 +1170,73 @@
fb_queue_command("signature", "installing signature");
}
-static void do_flashall(const std::string& slot_override, bool skip_secondary) {
+static bool is_logical(const std::string& partition) {
+ std::string value;
+ return fb_getvar("is-logical:" + partition, &value) && value == "yes";
+}
+
+static void reboot_to_userspace_fastboot() {
+ if (!fb_reboot_to_userspace()) {
+ die("Must reboot to userspace fastboot to flash logical partitions");
+ }
+
+ // Give the current connection time to close.
+ std::this_thread::sleep_for(std::chrono::milliseconds(1000));
+
+ fb_reinit(open_device());
+}
+
+static void update_super_partition(bool force_wipe) {
+ if (!if_partition_exists("super", "")) {
+ return;
+ }
+ std::string image = find_item_given_name("super_empty.img");
+ if (access(image.c_str(), R_OK) < 0) {
+ return;
+ }
+
+ if (!is_userspace_fastboot()) {
+ reboot_to_userspace_fastboot();
+ }
+
+ int fd = open(image.c_str(), O_RDONLY);
+ if (fd < 0) {
+ die("could not open '%s': %s", image.c_str(), strerror(errno));
+ }
+ fb_queue_download_fd("super", fd, get_file_size(fd));
+
+ std::string command = "update-super:super";
+ if (force_wipe) {
+ command += ":wipe";
+ }
+ fb_queue_command(command, "Updating super partition");
+
+ // We need these commands to have finished before proceeding, since
+ // otherwise "getvar is-logical" may not return a correct answer below.
+ fb_execute_queue();
+}
+
+static void flash_images(const std::vector<std::pair<const Image*, std::string>>& images) {
+ // Flash each partition in the list if it has a corresponding image.
+ for (const auto& [image, slot] : images) {
+ auto fname = find_item_given_name(image->img_name);
+ fastboot_buffer buf;
+ if (!load_buf(fname.c_str(), &buf)) {
+ if (image->optional_if_no_image) continue;
+ die("could not load '%s': %s", image->img_name, strerror(errno));
+ }
+ auto flashall = [&](const std::string &partition) {
+ do_send_signature(fname.c_str());
+ if (is_logical(partition)) {
+ fb_queue_resize_partition(partition, std::to_string(buf.image_size));
+ }
+ flash_buf(partition.c_str(), &buf);
+ };
+ do_for_partitions(image->part_name, slot, flashall, false);
+ }
+}
+
+static void do_flashall(const std::string& slot_override, bool skip_secondary, bool wipe) {
std::string fname;
queue_info_dump();
@@ -1188,6 +1266,9 @@
}
}
+ // List of partitions to flash and their slots.
+ std::vector<std::pair<const Image*, std::string>> boot_images;
+ std::vector<std::pair<const Image*, std::string>> os_images;
for (size_t i = 0; i < arraysize(images); i++) {
const char* slot = NULL;
if (images[i].IsSecondary()) {
@@ -1196,23 +1277,34 @@
slot = slot_override.c_str();
}
if (!slot) continue;
- fname = find_item_given_name(images[i].img_name);
- fastboot_buffer buf;
- if (!load_buf(fname.c_str(), &buf)) {
- if (images[i].optional_if_no_image) continue;
- die("could not load '%s': %s", images[i].img_name, strerror(errno));
+ if (images[i].type == ImageType::BootCritical) {
+ boot_images.emplace_back(&images[i], slot);
+ } else if (images[i].type == ImageType::Normal) {
+ os_images.emplace_back(&images[i], slot);
}
- if (images[i].optional_if_no_partition &&
- !if_partition_exists(images[i].part_name, slot)) {
- continue;
- }
- auto flashall = [&](const std::string &partition) {
- do_send_signature(fname.c_str());
- flash_buf(partition.c_str(), &buf);
- };
- do_for_partitions(images[i].part_name, slot, flashall, false);
}
+ // First flash boot partitions. We allow this to happen either in userspace
+ // or in bootloader fastboot.
+ flash_images(boot_images);
+
+ // Sync the super partition. This will reboot to userspace fastboot if needed.
+ update_super_partition(wipe);
+
+ // Resize any logical partition to 0, so each partition is reset to 0
+ // extents, and will achieve more optimal allocation.
+ for (const auto& [image, slot] : os_images) {
+ auto resize_partition = [](const std::string& partition) -> void {
+ if (is_logical(partition)) {
+ fb_queue_resize_partition(partition, "0");
+ }
+ };
+ do_for_partitions(image->part_name, slot, resize_partition, false);
+ }
+
+ // Flash OS images, resizing logical partitions as needed.
+ flash_images(os_images);
+
if (slot_override == "all") {
set_active("a");
} else {
@@ -1648,9 +1740,9 @@
} else if (command == "flashall") {
if (slot_override == "all") {
fprintf(stderr, "Warning: slot set to 'all'. Secondary slots will not be flashed.\n");
- do_flashall(slot_override, true);
+ do_flashall(slot_override, true, wants_wipe);
} else {
- do_flashall(slot_override, skip_secondary);
+ do_flashall(slot_override, skip_secondary, wants_wipe);
}
wants_reboot = true;
} else if (command == "update") {
diff --git a/fastboot/fastboot_driver.cpp b/fastboot/fastboot_driver.cpp
index c508abe..e8587c7 100644
--- a/fastboot/fastboot_driver.cpp
+++ b/fastboot/fastboot_driver.cpp
@@ -52,11 +52,15 @@
/*************************** PUBLIC *******************************/
FastBootDriver::FastBootDriver(Transport* transport, std::function<void(std::string&)> info,
bool no_checks)
- : transport(transport) {
+ : transport_(transport) {
info_cb_ = info;
disable_checks_ = no_checks;
}
+FastBootDriver::~FastBootDriver() {
+ set_transport(nullptr);
+}
+
RetCode FastBootDriver::Boot(std::string* response, std::vector<std::string>* info) {
return RawCommand(Commands::BOOT, response, info);
}
@@ -93,6 +97,11 @@
return RawCommand(Commands::REBOOT, response, info);
}
+RetCode FastBootDriver::RebootTo(std::string target, std::string* response,
+ std::vector<std::string>* info) {
+ return RawCommand("reboot-" + target, response, info);
+}
+
RetCode FastBootDriver::SetActive(const std::string& part, std::string* response,
std::vector<std::string>* info) {
return RawCommand(Commands::SET_ACTIVE + part, response, info);
@@ -332,7 +341,7 @@
}
RetCode FastBootDriver::WaitForDisconnect() {
- return transport->WaitForDisconnect() ? IO_ERROR : SUCCESS;
+ return transport_->WaitForDisconnect() ? IO_ERROR : SUCCESS;
}
/****************************** PROTECTED *************************************/
@@ -344,7 +353,7 @@
return BAD_ARG;
}
- if (transport->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
+ if (transport_->Write(cmd.c_str(), cmd.size()) != static_cast<int>(cmd.size())) {
error_ = ErrnoStr("Write to device failed");
return IO_ERROR;
}
@@ -378,7 +387,7 @@
// erase response
set_response("");
while ((std::chrono::system_clock::now() - start) < std::chrono::seconds(RESP_TIMEOUT)) {
- int r = transport->Read(status, FB_RESPONSE_SZ);
+ int r = transport_->Read(status, FB_RESPONSE_SZ);
if (r < 0) {
error_ = ErrnoStr("Status read failed");
return IO_ERROR;
@@ -472,7 +481,7 @@
return BAD_ARG;
}
// Write the buffer
- ssize_t tmp = transport->Write(buf, size);
+ ssize_t tmp = transport_->Write(buf, size);
if (tmp < 0) {
error_ = ErrnoStr("Write to device failed in SendBuffer()");
@@ -493,7 +502,7 @@
RetCode FastBootDriver::ReadBuffer(void* buf, size_t size) {
// Read the buffer
- ssize_t tmp = transport->Read(buf, size);
+ ssize_t tmp = transport_->Read(buf, size);
if (tmp < 0) {
error_ = ErrnoStr("Read from device failed in ReadBuffer()");
@@ -539,4 +548,12 @@
return 0;
}
+void FastBootDriver::set_transport(Transport* transport) {
+ if (transport_) {
+ transport_->Close();
+ delete transport_;
+ }
+ transport_ = transport;
+}
+
} // End namespace fastboot
diff --git a/fastboot/fastboot_driver.h b/fastboot/fastboot_driver.h
index 51be3db..a97ed2c 100644
--- a/fastboot/fastboot_driver.h
+++ b/fastboot/fastboot_driver.h
@@ -66,6 +66,7 @@
FastBootDriver(Transport* transport,
std::function<void(std::string&)> info = [](std::string&) {},
bool no_checks = false);
+ ~FastBootDriver();
RetCode Boot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode Continue(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
@@ -87,6 +88,8 @@
RetCode GetVarAll(std::vector<std::string>* response);
RetCode Powerdown(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
RetCode Reboot(std::string* response = nullptr, std::vector<std::string>* info = nullptr);
+ RetCode RebootTo(std::string target, std::string* response = nullptr,
+ std::vector<std::string>* info = nullptr);
RetCode SetActive(const std::string& part, std::string* response = nullptr,
std::vector<std::string>* info = nullptr);
RetCode Upload(const std::string& outfile, std::string* response = nullptr,
@@ -109,6 +112,10 @@
std::string Error();
RetCode WaitForDisconnect();
+ // Note: changing the transport will close and delete the existing one.
+ void set_transport(Transport* transport);
+ Transport* transport() const { return transport_; }
+
// This is temporarily public for engine.cpp
RetCode RawCommand(const std::string& cmd, std::string* response = nullptr,
std::vector<std::string>* info = nullptr, int* dsize = nullptr);
@@ -136,7 +143,7 @@
static const std::string VERIFY;
};
- Transport* const transport;
+ Transport* transport_;
private:
RetCode SendBuffer(int fd, size_t size);
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index b0ac2ef..fc6a16b 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -8,7 +8,7 @@
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
-#ifndef WIN32
+#ifndef _WIN32
#include <sys/wait.h>
#else
#include <tchar.h>
@@ -27,7 +27,7 @@
using android::base::StringPrintf;
using android::base::unique_fd;
-#ifdef WIN32
+#ifdef _WIN32
static int exec_cmd(const char* path, const char** argv, const char** envp) {
std::string cmd;
int i = 0;
diff --git a/fastboot/fuzzy_fastboot/Android.bp b/fastboot/fuzzy_fastboot/Android.bp
index 9a68ff3..301534b 100644
--- a/fastboot/fuzzy_fastboot/Android.bp
+++ b/fastboot/fuzzy_fastboot/Android.bp
@@ -28,4 +28,14 @@
"libsparse",
],
+ // Static libs (libfastboot2) shared library dependencies are not transitively included
+ // This is needed to avoid link time errors when building for mac
+ target: {
+ darwin: {
+ host_ldlibs: [
+ "-framework CoreFoundation",
+ "-framework IOKit",
+ ],
+ },
+ }
}
diff --git a/fastboot/fuzzy_fastboot/fixtures.h b/fastboot/fuzzy_fastboot/fixtures.h
index efbf43c..e47d0fd 100644
--- a/fastboot/fuzzy_fastboot/fixtures.h
+++ b/fastboot/fuzzy_fastboot/fixtures.h
@@ -133,4 +133,6 @@
class UserdataPartition : public ExtensionsPartition<true> {};
+class SparseTestPartition : public ExtensionsPartition<true> {};
+
} // end namespace fastboot
diff --git a/fastboot/fuzzy_fastboot/main.cpp b/fastboot/fuzzy_fastboot/main.cpp
index 8dd46bc..14bf5bf 100644
--- a/fastboot/fuzzy_fastboot/main.cpp
+++ b/fastboot/fuzzy_fastboot/main.cpp
@@ -80,6 +80,9 @@
std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>>
PACKED_XML_SUCCESS_TESTS;
std::vector<std::pair<std::string, extension::Configuration::PackedInfoTest>> PACKED_XML_FAIL_TESTS;
+// This only has 1 or zero elements so it will disappear from gtest when empty
+std::vector<std::pair<std::string, extension::Configuration::PartitionInfo>>
+ SINGLE_PARTITION_XML_WRITE_HASHABLE;
const std::string DEFAULT_OUPUT_NAME = "out.img";
// const char scratch_partition[] = "userdata";
@@ -135,6 +138,22 @@
return true;
}
+bool SparseToBuf(sparse_file* sf, std::vector<char>* out, bool with_crc = false) {
+ int64_t len = sparse_file_len(sf, true, with_crc);
+ if (len <= 0) {
+ return false;
+ }
+ out->clear();
+ auto cb = [](void* priv, const void* data, size_t len) {
+ auto vec = static_cast<std::vector<char>*>(priv);
+ const char* cbuf = static_cast<const char*>(data);
+ vec->insert(vec->end(), cbuf, cbuf + len);
+ return 0;
+ };
+
+ return !sparse_file_callback(sf, true, with_crc, cb, out);
+}
+
// Only allow alphanumeric, _, -, and .
const auto not_allowed = [](char c) -> int {
return !(isalnum(c) || c == '_' || c == '-' || c == '.');
@@ -524,6 +543,42 @@
EXPECT_EQ(fb->Flash("userdata"), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
}
+TEST_F(Conformance, SparseVersionCheck) {
+ SparseWrapper sparse(4096, 4096);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ std::vector<char> buf;
+ ASSERT_TRUE(SparseToBuf(*sparse, &buf)) << "Sparse buffer creation failed";
+ // Invalid, right after magic
+ buf[4] = 0xff;
+ ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+ ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+
+ // It can either reject this download or reject it during flash
+ if (HandleResponse() != DEVICE_FAIL) {
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing an invalid sparse version should fail " << sparse.Rep();
+ }
+}
+
+TEST_F(Conformance, SparseCRCCheck) {
+ SparseWrapper sparse(4096, 4096);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ std::vector<char> buf = RandomBuf(4096);
+ ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+ << "Adding data failed to sparse file: " << sparse.Rep();
+ ASSERT_TRUE(SparseToBuf(*sparse, &buf, true)) << "Sparse buffer creation failed";
+ // Flip a bit in the crc
+ buf.back() = buf.back() ^ 0x01;
+ ASSERT_EQ(DownloadCommand(buf.size()), SUCCESS) << "Device rejected download command";
+ ASSERT_EQ(SendBuffer(buf), SUCCESS) << "Downloading payload failed";
+ printf("%02x\n", (unsigned char)buf.back());
+ // It can either reject this download or reject it during flash
+ if (HandleResponse() != DEVICE_FAIL) {
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing an invalid sparse version should fail " << sparse.Rep();
+ }
+}
+
TEST_F(UnlockPermissions, Download) {
std::vector<char> buf{'a', 'o', 's', 'p'};
EXPECT_EQ(fb->Download(buf), SUCCESS) << "Download 4-byte payload failed";
@@ -767,6 +822,47 @@
}
}
+TEST_F(Fuzz, SparseZeroLength) {
+ SparseWrapper sparse(4096, 0);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ RetCode ret = fb->Download(*sparse);
+ // Two ways to handle it
+ if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing zero length sparse image did not fail: " << sparse.Rep();
+ }
+ ret = fb->Download(*sparse, true);
+ if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing zero length sparse image did not fail " << sparse.Rep();
+ }
+}
+
+TEST_F(Fuzz, SparseTooManyChunks) {
+ SparseWrapper sparse(4096, 4096); // 1 block, but we send two chunks that will use 2 blocks
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ std::vector<char> buf = RandomBuf(4096);
+ ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+ << "Adding data failed to sparse file: " << sparse.Rep();
+ // We take advantage of the fact the sparse library does not check this
+ ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, 4096, 1), 0)
+ << "Adding fill to sparse file failed: " << sparse.Rep();
+
+ RetCode ret = fb->Download(*sparse);
+ // Two ways to handle it
+ if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+ << sparse.Rep();
+ }
+ ret = fb->Download(*sparse, true);
+ if (ret != DEVICE_FAIL) { // if lazily parsed it better fail on a flash
+ EXPECT_EQ(fb->Flash("userdata"), DEVICE_FAIL)
+ << "Flashing sparse image with 'total_blks' in header 1 too small did not fail "
+ << sparse.Rep();
+ }
+}
+
TEST_F(Fuzz, USBResetSpam) {
auto start = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> elapsed;
@@ -1379,6 +1475,109 @@
INSTANTIATE_TEST_CASE_P(XMLOEM, ExtensionsOemConformance, ::testing::ValuesIn(OEM_XML_TESTS));
+// Sparse Tests
+TEST_P(SparseTestPartition, SparseSingleBlock) {
+ const std::string name = GetParam().first;
+ auto part_info = GetParam().second;
+ const std::string part_name = name + (part_info.slots ? "_a" : "");
+ SparseWrapper sparse(4096, 4096);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ std::vector<char> buf = RandomBuf(4096);
+ ASSERT_EQ(sparse_file_add_data(*sparse, buf.data(), buf.size(), 0), 0)
+ << "Adding data failed to sparse file: " << sparse.Rep();
+
+ EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+ EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+ std::string hash, hash_new, err_msg;
+ int retcode;
+ ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+ ASSERT_EQ(retcode, 0) << err_msg;
+ // Now flash it the non-sparse way
+ EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+ ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+ ASSERT_EQ(retcode, 0) << err_msg;
+
+ EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+ "methods did not result in the same hash";
+}
+
+TEST_P(SparseTestPartition, SparseFill) {
+ const std::string name = GetParam().first;
+ auto part_info = GetParam().second;
+ const std::string part_name = name + (part_info.slots ? "_a" : "");
+ int64_t size = (max_dl / 4096) * 4096;
+ SparseWrapper sparse(4096, size);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size, 0), 0)
+ << "Adding data failed to sparse file: " << sparse.Rep();
+
+ EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+ EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+ std::string hash, hash_new, err_msg;
+ int retcode;
+ ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+ ASSERT_EQ(retcode, 0) << err_msg;
+ // Now flash it the non-sparse way
+ std::vector<char> buf(size);
+ for (auto iter = buf.begin(); iter < buf.end(); iter += 4) {
+ iter[0] = 0xef;
+ iter[1] = 0xbe;
+ iter[2] = 0xad;
+ iter[3] = 0xde;
+ }
+ EXPECT_EQ(fb->FlashPartition(part_name, buf), SUCCESS) << "Flashing image failed: ";
+ ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+ ASSERT_EQ(retcode, 0) << err_msg;
+
+ EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+ "methods did not result in the same hash";
+}
+
+// This tests to make sure it does not overwrite previous flashes
+TEST_P(SparseTestPartition, SparseMultiple) {
+ const std::string name = GetParam().first;
+ auto part_info = GetParam().second;
+ const std::string part_name = name + (part_info.slots ? "_a" : "");
+ int64_t size = (max_dl / 4096) * 4096;
+ SparseWrapper sparse(4096, size / 2);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ ASSERT_EQ(sparse_file_add_fill(*sparse, 0xdeadbeef, size / 2, 0), 0)
+ << "Adding data failed to sparse file: " << sparse.Rep();
+ EXPECT_EQ(fb->Download(*sparse), SUCCESS) << "Download sparse failed: " << sparse.Rep();
+ EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse.Rep();
+
+ SparseWrapper sparse2(4096, size / 2);
+ ASSERT_TRUE(*sparse) << "Sparse image creation failed";
+ std::vector<char> buf = RandomBuf(size / 2);
+ ASSERT_EQ(sparse_file_add_data(*sparse2, buf.data(), buf.size(), (size / 2) / 4096), 0)
+ << "Adding data failed to sparse file: " << sparse2.Rep();
+ EXPECT_EQ(fb->Download(*sparse2), SUCCESS) << "Download sparse failed: " << sparse2.Rep();
+ EXPECT_EQ(fb->Flash(part_name), SUCCESS) << "Flashing sparse failed: " << sparse2.Rep();
+
+ std::string hash, hash_new, err_msg;
+ int retcode;
+ ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash, &retcode, &err_msg)) << err_msg;
+ ASSERT_EQ(retcode, 0) << err_msg;
+ // Now flash it the non-sparse way
+ std::vector<char> fbuf(size);
+ for (auto iter = fbuf.begin(); iter < fbuf.begin() + size / 2; iter += 4) {
+ iter[0] = 0xef;
+ iter[1] = 0xbe;
+ iter[2] = 0xad;
+ iter[3] = 0xde;
+ }
+ fbuf.assign(buf.begin(), buf.end());
+ EXPECT_EQ(fb->FlashPartition(part_name, fbuf), SUCCESS) << "Flashing image failed: ";
+ ASSERT_TRUE(PartitionHash(fb.get(), part_name, &hash_new, &retcode, &err_msg)) << err_msg;
+ ASSERT_EQ(retcode, 0) << err_msg;
+
+ EXPECT_EQ(hash, hash_new) << "Flashing a random buffer of 4096 using sparse and non-sparse "
+ "methods did not result in the same hash";
+}
+
+INSTANTIATE_TEST_CASE_P(XMLSparseTest, SparseTestPartition,
+ ::testing::ValuesIn(SINGLE_PARTITION_XML_WRITE_HASHABLE));
+
void GenerateXmlTests(const extension::Configuration& config) {
// Build the getvar tests
for (const auto it : config.getvars) {
@@ -1430,6 +1629,10 @@
std::make_tuple(part_info->first, part_info->second));
}
+ if (!PARTITION_XML_WRITE_HASHABLE.empty()) {
+ SINGLE_PARTITION_XML_WRITE_HASHABLE.push_back(PARTITION_XML_WRITE_HASHABLE.front());
+ }
+
// Build oem tests
for (const auto it : config.oem) {
auto oem_cmd = it.second;
diff --git a/fs_mgr/fs_mgr.cpp b/fs_mgr/fs_mgr.cpp
index ea8995b..a4e6f4b 100644
--- a/fs_mgr/fs_mgr.cpp
+++ b/fs_mgr/fs_mgr.cpp
@@ -79,7 +79,8 @@
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
-using DeviceMapper = android::dm::DeviceMapper;
+using android::dm::DeviceMapper;
+using android::dm::DmDeviceState;
// record fs stat
enum FsStatFlags {
@@ -1401,8 +1402,12 @@
mount_point = basename(fstab->recs[i].mount_point);
}
- const char* status = nullptr;
+ if (dm.GetState(mount_point) == DmDeviceState::INVALID) {
+ PERROR << "Could not find verity device for mount point: " << mount_point;
+ continue;
+ }
+ const char* status = nullptr;
std::vector<DeviceMapper::TargetInfo> table;
if (!dm.GetTableStatus(mount_point, &table) || table.empty() || table[0].data.empty()) {
if (fstab->recs[i].fs_mgr_flags & MF_VERIFYATBOOT) {
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp
index 78151d5..720dcfd 100644
--- a/fs_mgr/fs_mgr_overlayfs.cpp
+++ b/fs_mgr/fs_mgr_overlayfs.cpp
@@ -16,6 +16,7 @@
#include <dirent.h>
#include <errno.h>
+#include <fcntl.h>
#include <linux/fs.h>
#include <selinux/selinux.h>
#include <stdio.h>
@@ -23,7 +24,9 @@
#include <sys/mount.h>
#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/statvfs.h>
#include <sys/types.h>
+#include <sys/vfs.h>
#include <unistd.h>
#include <map>
@@ -35,6 +38,8 @@
#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <ext4_utils/ext4_utils.h>
#include <fs_mgr_overlayfs.h>
#include <fstab/fstab.h>
@@ -96,9 +101,26 @@
return "";
}
+// At less than 1% free space return value of false,
+// means we will try to wrap with overlayfs.
+bool fs_mgr_filesystem_has_space(const char* mount_point) {
+ // If we have access issues to find out space remaining, return true
+ // to prevent us trying to override with overlayfs.
+ struct statvfs vst;
+ if (statvfs(mount_point, &vst)) return true;
+
+ static constexpr int percent = 1; // 1%
+
+ return (vst.f_bfree >= (vst.f_blocks * percent / 100));
+}
+
bool fs_mgr_overlayfs_enabled(const struct fstab_rec* fsrec) {
// readonly filesystem, can not be mount -o remount,rw
- return "squashfs"s == fsrec->fs_type;
+ // if squashfs or if free space is (near) zero making such a remount
+ // virtually useless, or if there are shared blocks that prevent remount,rw
+ return ("squashfs"s == fsrec->fs_type) ||
+ fs_mgr_has_shared_blocks(fsrec->mount_point, fsrec->blk_device) ||
+ !fs_mgr_filesystem_has_space(fsrec->mount_point);
}
constexpr char upper_name[] = "upper";
@@ -119,14 +141,13 @@
constexpr char upperdir_option[] = "upperdir=";
// default options for mount_point, returns empty string for none available.
-std::string fs_mgr_get_overlayfs_options(const char* mount_point) {
- auto fsrec_mount_point = std::string(mount_point);
- auto candidate = fs_mgr_get_overlayfs_candidate(fsrec_mount_point);
+std::string fs_mgr_get_overlayfs_options(const std::string& mount_point) {
+ auto candidate = fs_mgr_get_overlayfs_candidate(mount_point);
if (candidate.empty()) return "";
- auto context = fs_mgr_get_context(fsrec_mount_point);
+ auto context = fs_mgr_get_context(mount_point);
if (!context.empty()) context = ",rootcontext="s + context;
- return "override_creds=off,"s + lowerdir_option + fsrec_mount_point + "," + upperdir_option +
+ return "override_creds=off,"s + lowerdir_option + mount_point + "," + upperdir_option +
candidate + upper_name + ",workdir=" + candidate + work_name + context;
}
@@ -145,10 +166,11 @@
return true;
}
-std::string fs_mgr_get_overlayfs_options(const fstab* fstab, const char* mount_point) {
- if (fs_mgr_system_root_image(fstab) && ("/"s == mount_point)) mount_point = "/system";
-
- return fs_mgr_get_overlayfs_options(mount_point);
+const char* fs_mgr_mount_point(const fstab* fstab, const char* mount_point) {
+ if (!mount_point) return mount_point;
+ if ("/"s != mount_point) return mount_point;
+ if (!fs_mgr_system_root_image(fstab)) return mount_point;
+ return "/system";
}
// return true if system supports overlayfs
@@ -174,7 +196,8 @@
if (!fsrec) return false;
auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) return false;
+ if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
+ if (!fsrec->blk_device) return false;
if (!fsrec->fs_type) return false;
@@ -242,10 +265,17 @@
return ret;
}
+constexpr char overlayfs_file_context[] = "u:object_r:overlayfs_file:s0";
+
bool fs_mgr_overlayfs_setup_one(const std::string& overlay, const std::string& mount_point,
bool* change) {
auto ret = true;
auto fsrec_mount_point = overlay + android::base::Basename(mount_point) + "/";
+
+ if (setfscreatecon(overlayfs_file_context)) {
+ ret = false;
+ PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
+ }
auto save_errno = errno;
if (!mkdir(fsrec_mount_point.c_str(), 0755)) {
if (change) *change = true;
@@ -265,6 +295,7 @@
} else {
errno = save_errno;
}
+ setfscreatecon(nullptr);
auto new_context = fs_mgr_get_context(mount_point);
if (!new_context.empty() && setfscreatecon(new_context.c_str())) {
@@ -286,15 +317,12 @@
return ret;
}
-bool fs_mgr_overlayfs_mount(const fstab* fstab, const fstab_rec* fsrec) {
- if (!fs_mgr_wants_overlayfs(fsrec)) return false;
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point || !fsrec_mount_point[0]) return false;
- auto options = fs_mgr_get_overlayfs_options(fstab, fsrec_mount_point);
+bool fs_mgr_overlayfs_mount(const std::string& mount_point) {
+ auto options = fs_mgr_get_overlayfs_options(mount_point);
if (options.empty()) return false;
// hijack __mount() report format to help triage
- auto report = "__mount(source=overlay,target="s + fsrec_mount_point + ",type=overlay";
+ auto report = "__mount(source=overlay,target="s + mount_point + ",type=overlay";
const auto opt_list = android::base::Split(options, ",");
for (const auto opt : opt_list) {
if (android::base::StartsWith(opt, upperdir_option)) {
@@ -304,7 +332,7 @@
}
report = report + ")=";
- auto ret = mount("overlay", fsrec_mount_point, "overlay", MS_RDONLY | MS_RELATIME,
+ auto ret = mount("overlay", mount_point.c_str(), "overlay", MS_RDONLY | MS_RELATIME,
options.c_str());
if (ret) {
PERROR << report << ret;
@@ -315,8 +343,7 @@
}
}
-bool fs_mgr_overlayfs_already_mounted(const char* mount_point) {
- if (!mount_point) return false;
+bool fs_mgr_overlayfs_already_mounted(const std::string& mount_point) {
std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
fs_mgr_read_fstab("/proc/mounts"), fs_mgr_free_fstab);
if (!fstab) return false;
@@ -328,7 +355,7 @@
if (("overlay"s != fs_type) && ("overlayfs"s != fs_type)) continue;
auto fsrec_mount_point = fsrec->mount_point;
if (!fsrec_mount_point) continue;
- if (strcmp(fsrec_mount_point, mount_point)) continue;
+ if (mount_point != fsrec_mount_point) continue;
const auto fs_options = fsrec->fs_options;
if (!fs_options) continue;
const auto options = android::base::Split(fs_options, ",");
@@ -341,6 +368,34 @@
return false;
}
+std::vector<std::string> fs_mgr_candidate_list(const fstab* fstab,
+ const char* mount_point = nullptr) {
+ std::vector<std::string> mounts;
+ if (!fstab) return mounts;
+
+ for (auto i = 0; i < fstab->num_entries; i++) {
+ const auto fsrec = &fstab->recs[i];
+ if (!fs_mgr_wants_overlayfs(fsrec)) continue;
+ std::string new_mount_point(fs_mgr_mount_point(fstab, fsrec->mount_point));
+ if (mount_point && (new_mount_point != mount_point)) continue;
+ auto duplicate_or_more_specific = false;
+ for (auto it = mounts.begin(); it != mounts.end();) {
+ if ((*it == new_mount_point) ||
+ (android::base::StartsWith(new_mount_point, *it + "/"))) {
+ duplicate_or_more_specific = true;
+ break;
+ }
+ if (android::base::StartsWith(*it, new_mount_point + "/")) {
+ it = mounts.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ if (!duplicate_or_more_specific) mounts.emplace_back(new_mount_point);
+ }
+ return mounts;
+}
+
} // namespace
bool fs_mgr_overlayfs_mount_all() {
@@ -352,13 +407,9 @@
fs_mgr_free_fstab);
if (!fstab) return ret;
- for (auto i = 0; i < fstab->num_entries; i++) {
- const auto fsrec = &fstab->recs[i];
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) continue;
- if (fs_mgr_overlayfs_already_mounted(fsrec_mount_point)) continue;
-
- if (fs_mgr_overlayfs_mount(fstab.get(), fsrec)) ret = true;
+ for (const auto& mount_point : fs_mgr_candidate_list(fstab.get())) {
+ if (fs_mgr_overlayfs_already_mounted(mount_point)) continue;
+ if (fs_mgr_overlayfs_mount(mount_point)) ret = true;
}
return ret;
}
@@ -381,22 +432,12 @@
std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(fs_mgr_read_fstab_default(),
fs_mgr_free_fstab);
- std::vector<std::string> mounts;
- if (fstab) {
- if (!fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
- for (auto i = 0; i < fstab->num_entries; i++) {
- const auto fsrec = &fstab->recs[i];
- auto fsrec_mount_point = fsrec->mount_point;
- if (!fsrec_mount_point) continue;
- if (mount_point && strcmp(fsrec_mount_point, mount_point)) continue;
- if (!fs_mgr_wants_overlayfs(fsrec)) continue;
- mounts.emplace_back(fsrec_mount_point);
- }
- if (mounts.empty()) return ret;
- }
+ if (fstab && !fs_mgr_get_entry_for_mount_point(fstab.get(), kOverlayMountPoint)) return ret;
+ auto mounts = fs_mgr_candidate_list(fstab.get(), fs_mgr_mount_point(fstab.get(), mount_point));
+ if (fstab && mounts.empty()) return ret;
- if (mount_point && ("/"s == mount_point) && fs_mgr_system_root_image(fstab.get())) {
- mount_point = "/system";
+ if (setfscreatecon(overlayfs_file_context)) {
+ PERROR << "overlayfs setfscreatecon " << overlayfs_file_context;
}
auto overlay = kOverlayMountPoint + "/overlay/";
auto save_errno = errno;
@@ -407,6 +448,7 @@
} else {
errno = save_errno;
}
+ setfscreatecon(nullptr);
if (!fstab && mount_point && fs_mgr_overlayfs_setup_one(overlay, mount_point, change)) {
ret = true;
}
@@ -420,11 +462,10 @@
// If something is altered, set *change.
bool fs_mgr_overlayfs_teardown(const char* mount_point, bool* change) {
if (change) *change = false;
- if (mount_point && ("/"s == mount_point)) {
- std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)> fstab(
- fs_mgr_read_fstab_default(), fs_mgr_free_fstab);
- if (fs_mgr_system_root_image(fstab.get())) mount_point = "/system";
- }
+ mount_point = fs_mgr_mount_point(std::unique_ptr<struct fstab, decltype(&fs_mgr_free_fstab)>(
+ fs_mgr_read_fstab_default(), fs_mgr_free_fstab)
+ .get(),
+ mount_point);
auto ret = true;
const auto overlay = kOverlayMountPoint + "/overlay";
const auto oldpath = overlay + (mount_point ?: "");
@@ -477,3 +518,25 @@
}
#endif // ALLOW_ADBD_DISABLE_VERITY != 0
+
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev) {
+ struct statfs fs;
+ if ((statfs((mount_point + "/lost+found").c_str(), &fs) == -1) ||
+ (fs.f_type != EXT4_SUPER_MAGIC)) {
+ return false;
+ }
+
+ android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_CLOEXEC));
+ if (fd < 0) return false;
+
+ struct ext4_super_block sb;
+ if ((TEMP_FAILURE_RETRY(lseek64(fd, 1024, SEEK_SET)) < 0) ||
+ (TEMP_FAILURE_RETRY(read(fd, &sb, sizeof(sb))) < 0)) {
+ return false;
+ }
+
+ struct fs_info info;
+ if (ext4_parse_sb(&sb, &info) < 0) return false;
+
+ return (info.feat_ro_compat & EXT4_FEATURE_RO_COMPAT_SHARED_BLOCKS) != 0;
+}
diff --git a/fs_mgr/include/fs_mgr_overlayfs.h b/fs_mgr/include/fs_mgr_overlayfs.h
index 1d2ff03..ceb45de 100644
--- a/fs_mgr/include/fs_mgr_overlayfs.h
+++ b/fs_mgr/include/fs_mgr_overlayfs.h
@@ -18,7 +18,10 @@
#include <fstab/fstab.h>
+#include <string>
+
bool fs_mgr_overlayfs_mount_all();
bool fs_mgr_overlayfs_setup(const char* backing = nullptr, const char* mount_point = nullptr,
bool* change = nullptr);
bool fs_mgr_overlayfs_teardown(const char* mount_point = nullptr, bool* change = nullptr);
+bool fs_mgr_has_shared_blocks(const std::string& mount_point, const std::string& dev);
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index 8bcbec2..2b526f6 100644
--- a/fs_mgr/libdm/dm.cpp
+++ b/fs_mgr/libdm/dm.cpp
@@ -98,9 +98,16 @@
return nullptr;
}
-DmDeviceState DeviceMapper::state(const std::string& /* name */) const {
- // TODO(b/110035986): Return the state, as read from the kernel instead
- return DmDeviceState::INVALID;
+DmDeviceState DeviceMapper::GetState(const std::string& name) const {
+ struct dm_ioctl io;
+ InitIo(&io, name);
+ if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
+ return DmDeviceState::INVALID;
+ }
+ if ((io.flags & DM_ACTIVE_PRESENT_FLAG) && !(io.flags & DM_SUSPEND_FLAG)) {
+ return DmDeviceState::ACTIVE;
+ }
+ return DmDeviceState::SUSPENDED;
}
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) {
diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h
index 9b61ddc..91f8bb4 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -79,7 +79,7 @@
// Returns the current state of the underlying device mapper device
// with given name.
// One of INVALID, SUSPENDED or ACTIVE.
- DmDeviceState state(const std::string& name) const;
+ DmDeviceState GetState(const std::string& name) const;
// Creates a device, loads the given table, and activates it. If the device
// is not able to be activated, it is destroyed, and false is returned.
diff --git a/init/Android.bp b/init/Android.bp
index 84a78e2..a2c49d0 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -59,33 +59,24 @@
},
},
static_libs: [
- "libbootloader_message",
- "libfs_mgr",
- "libfec",
- "libfec_rs",
- "libhidl-gen-utils",
- "libsquashfs_utils",
- "liblogwrap",
- "libext4_utils",
"libseccomp_policy",
- "libcrypto_utils",
- "libsparse",
"libprocessgroup",
"libavb",
- "libkeyutils",
"libprotobuf-cpp-lite",
"libpropertyinfoserializer",
"libpropertyinfoparser",
],
shared_libs: [
- "libcutils",
"libbase",
- "libc",
- "liblog",
- "libcrypto",
- "libc++",
+ "libbootloader_message",
+ "libcutils",
"libdl",
- "libz",
+ "libext4_utils",
+ "libfs_mgr",
+ "libhidl-gen-utils",
+ "libkeyutils",
+ "liblog",
+ "liblogwrap",
"libselinux",
],
}
@@ -160,6 +151,7 @@
cc_test {
name: "init_tests",
defaults: ["init_defaults"],
+ compile_multilib: "first",
srcs: [
"devices_test.cpp",
"init_test.cpp",
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 1af06dd..b8c1cfd 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -192,7 +192,7 @@
void Keychords::GeteventOpenDevice(const std::string& device) {
if (registration_.count(device)) return;
- auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDWR | O_CLOEXEC));
+ auto fd = TEMP_FAILURE_RETRY(::open(device.c_str(), O_RDONLY | O_CLOEXEC));
if (fd == -1) {
PLOG(ERROR) << "Can not open " << device;
return;
diff --git a/libcutils/tests/sched_policy_test.cpp b/libcutils/tests/sched_policy_test.cpp
index 5942ee5..1f657e2 100644
--- a/libcutils/tests/sched_policy_test.cpp
+++ b/libcutils/tests/sched_policy_test.cpp
@@ -67,6 +67,21 @@
}
TEST(SchedPolicy, set_sched_policy) {
+ if (!schedboost_enabled()) {
+ // schedboost_enabled() (i.e. CONFIG_CGROUP_SCHEDTUNE) is optional;
+ // it's only needed on devices using energy-aware scheduler.
+ GTEST_LOG_(INFO) << "skipping test that requires CONFIG_CGROUP_SCHEDTUNE";
+ return;
+ }
+
+ ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
+ AssertPolicy(SP_BACKGROUND);
+
+ ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
+ AssertPolicy(SP_FOREGROUND);
+}
+
+TEST(SchedPolicy, set_sched_policy_timerslack) {
if (!hasCapSysNice()) {
GTEST_LOG_(INFO) << "skipping test that requires CAP_SYS_NICE";
return;
@@ -82,11 +97,9 @@
const unsigned int BG_FG_SLACK_FACTOR = 100;
ASSERT_EQ(0, set_sched_policy(0, SP_BACKGROUND));
- AssertPolicy(SP_BACKGROUND);
auto bgSleepTime = medianSleepTime();
ASSERT_EQ(0, set_sched_policy(0, SP_FOREGROUND));
- AssertPolicy(SP_FOREGROUND);
auto fgSleepTime = medianSleepTime();
ASSERT_GT(bgSleepTime, fgSleepTime * BG_FG_SLACK_FACTOR);
}
diff --git a/libsysutils/Android.bp b/libsysutils/Android.bp
index 3a12292..29d23c8 100644
--- a/libsysutils/Android.bp
+++ b/libsysutils/Android.bp
@@ -27,3 +27,16 @@
export_include_dirs: ["include"],
}
+
+cc_test {
+ name: "libsysutils_tests",
+ test_suites: ["device-tests"],
+ srcs: [
+ "src/SocketListener_test.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libsysutils",
+ ],
+}
diff --git a/libsysutils/include/sysutils/FrameworkListener.h b/libsysutils/include/sysutils/FrameworkListener.h
index 2137069..d9a561a 100644
--- a/libsysutils/include/sysutils/FrameworkListener.h
+++ b/libsysutils/include/sysutils/FrameworkListener.h
@@ -38,13 +38,13 @@
FrameworkListener(const char *socketName);
FrameworkListener(const char *socketName, bool withSeq);
FrameworkListener(int sock);
- virtual ~FrameworkListener() {}
+ ~FrameworkListener() override {}
-protected:
+ protected:
void registerCmd(FrameworkCommand *cmd);
- virtual bool onDataAvailable(SocketClient *c);
+ bool onDataAvailable(SocketClient* c) override;
-private:
+ private:
void dispatchCommand(SocketClient *c, char *data);
void init(const char *socketName, bool withSeq);
};
diff --git a/libsysutils/include/sysutils/OWNERS b/libsysutils/include/sysutils/OWNERS
index b78918e..645baf4 100644
--- a/libsysutils/include/sysutils/OWNERS
+++ b/libsysutils/include/sysutils/OWNERS
@@ -1,2 +1 @@
-per-file Netlink* = ek@google.com
-per-file Netlink* = lorenzo@google.com
+per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
diff --git a/libsysutils/include/sysutils/SocketClient.h b/libsysutils/include/sysutils/SocketClient.h
index 1004f06..c657526 100644
--- a/libsysutils/include/sysutils/SocketClient.h
+++ b/libsysutils/include/sysutils/SocketClient.h
@@ -1,8 +1,6 @@
#ifndef _SOCKET_CLIENT_H
#define _SOCKET_CLIENT_H
-#include "List.h"
-
#include <pthread.h>
#include <cutils/atomic.h>
#include <sys/types.h>
@@ -35,7 +33,7 @@
SocketClient(int sock, bool owned, bool useCmdNum);
virtual ~SocketClient();
- int getSocket() { return mSocket; }
+ int getSocket() const { return mSocket; }
pid_t getPid() const { return mPid; }
uid_t getUid() const { return mUid; }
gid_t getGid() const { return mGid; }
@@ -84,5 +82,4 @@
int sendDataLockedv(struct iovec *iov, int iovcnt);
};
-typedef android::sysutils::List<SocketClient *> SocketClientCollection;
#endif
diff --git a/libsysutils/include/sysutils/SocketListener.h b/libsysutils/include/sysutils/SocketListener.h
index bc93b86..67a691a 100644
--- a/libsysutils/include/sysutils/SocketListener.h
+++ b/libsysutils/include/sysutils/SocketListener.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 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.
@@ -18,6 +18,8 @@
#include <pthread.h>
+#include <unordered_map>
+
#include <sysutils/SocketClient.h>
#include "SocketClientCommand.h"
@@ -25,7 +27,7 @@
bool mListen;
const char *mSocketName;
int mSock;
- SocketClientCollection *mClients;
+ std::unordered_map<int, SocketClient*> mClients;
pthread_mutex_t mClientsLock;
int mCtrlPipe[2];
pthread_t mThread;
@@ -51,8 +53,13 @@
virtual bool onDataAvailable(SocketClient *c) = 0;
private:
- bool release(SocketClient *c, bool wakeup);
static void *threadStart(void *obj);
+
+ // Add all clients to a separate list, so we don't have to hold the lock
+ // while processing it.
+ std::vector<SocketClient*> snapshotClients();
+
+ bool release(SocketClient *c, bool wakeup);
void runListener();
void init(const char *socketName, int socketFd, bool listen, bool useCmdNum);
};
diff --git a/libsysutils/src/OWNERS b/libsysutils/src/OWNERS
index b78918e..645baf4 100644
--- a/libsysutils/src/OWNERS
+++ b/libsysutils/src/OWNERS
@@ -1,2 +1 @@
-per-file Netlink* = ek@google.com
-per-file Netlink* = lorenzo@google.com
+per-file OWNERS,Netlink* = ek@google.com,lorenzo@google.com
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 0c8a688..ded5adb 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008-2014 The Android Open Source Project
+ * Copyright (C) 2008 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.
@@ -19,13 +19,15 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
-#include <sys/select.h>
+#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
+#include <vector>
+
#include <cutils/sockets.h>
#include <log/log.h>
#include <sysutils/SocketListener.h>
@@ -52,7 +54,6 @@
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, nullptr);
- mClients = new SocketClientCollection();
}
SocketListener::~SocketListener() {
@@ -63,12 +64,9 @@
close(mCtrlPipe[0]);
close(mCtrlPipe[1]);
}
- SocketClientCollection::iterator it;
- for (it = mClients->begin(); it != mClients->end();) {
- (*it)->decRef();
- it = mClients->erase(it);
+ for (auto pair : mClients) {
+ pair.second->decRef();
}
- delete mClients;
}
int SocketListener::startListener() {
@@ -95,7 +93,7 @@
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
- mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));
+ mClients[mSock] = new SocketClient(mSock, false, mUseCmdNum);
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
@@ -135,11 +133,10 @@
mSock = -1;
}
- SocketClientCollection::iterator it;
- for (it = mClients->begin(); it != mClients->end();) {
- delete (*it);
- it = mClients->erase(it);
+ for (auto pair : mClients) {
+ delete pair.second;
}
+ mClients.clear();
return 0;
}
@@ -152,47 +149,30 @@
}
void SocketListener::runListener() {
-
- SocketClientCollection pendingList;
-
- while(1) {
- SocketClientCollection::iterator it;
- fd_set read_fds;
- int rc = 0;
- int max = -1;
-
- FD_ZERO(&read_fds);
-
- if (mListen) {
- max = mSock;
- FD_SET(mSock, &read_fds);
- }
-
- FD_SET(mCtrlPipe[0], &read_fds);
- if (mCtrlPipe[0] > max)
- max = mCtrlPipe[0];
+ while (true) {
+ std::vector<pollfd> fds;
pthread_mutex_lock(&mClientsLock);
- for (it = mClients->begin(); it != mClients->end(); ++it) {
+ fds.reserve(2 + mClients.size());
+ fds.push_back({.fd = mCtrlPipe[0], .events = POLLIN});
+ if (mListen) fds.push_back({.fd = mSock, .events = POLLIN});
+ for (auto pair : mClients) {
// NB: calling out to an other object with mClientsLock held (safe)
- int fd = (*it)->getSocket();
- FD_SET(fd, &read_fds);
- if (fd > max) {
- max = fd;
- }
+ const int fd = pair.second->getSocket();
+ if (fd != pair.first) SLOGE("fd mismatch: %d != %d", fd, pair.first);
+ fds.push_back({.fd = fd, .events = POLLIN});
}
pthread_mutex_unlock(&mClientsLock);
- SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);
- if ((rc = select(max + 1, &read_fds, nullptr, nullptr, nullptr)) < 0) {
- if (errno == EINTR)
- continue;
- SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
+
+ SLOGV("mListen=%d, mSocketName=%s", mListen, mSocketName);
+ int rc = TEMP_FAILURE_RETRY(poll(fds.data(), fds.size(), -1));
+ if (rc < 0) {
+ SLOGE("poll failed (%s) mListen=%d", strerror(errno), mListen);
sleep(1);
continue;
- } else if (!rc)
- continue;
+ }
- if (FD_ISSET(mCtrlPipe[0], &read_fds)) {
+ if (fds[0].revents & (POLLIN | POLLERR)) {
char c = CtrlPipe_Shutdown;
TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));
if (c == CtrlPipe_Shutdown) {
@@ -200,7 +180,7 @@
}
continue;
}
- if (mListen && FD_ISSET(mSock, &read_fds)) {
+ if (mListen && (fds[1].revents & (POLLIN | POLLERR))) {
int c = TEMP_FAILURE_RETRY(accept4(mSock, nullptr, nullptr, SOCK_CLOEXEC));
if (c < 0) {
SLOGE("accept failed (%s)", strerror(errno));
@@ -208,32 +188,33 @@
continue;
}
pthread_mutex_lock(&mClientsLock);
- mClients->push_back(new SocketClient(c, true, mUseCmdNum));
+ mClients[c] = new SocketClient(c, true, mUseCmdNum);
pthread_mutex_unlock(&mClientsLock);
}
- /* Add all active clients to the pending list first */
- pendingList.clear();
+ // Add all active clients to the pending list first, so we can release
+ // the lock before invoking the callbacks.
+ std::vector<SocketClient*> pending;
pthread_mutex_lock(&mClientsLock);
- for (it = mClients->begin(); it != mClients->end(); ++it) {
- SocketClient* c = *it;
- // NB: calling out to an other object with mClientsLock held (safe)
- int fd = c->getSocket();
- if (FD_ISSET(fd, &read_fds)) {
- pendingList.push_back(c);
+ const int size = fds.size();
+ for (int i = mListen ? 2 : 1; i < size; ++i) {
+ const struct pollfd& p = fds[i];
+ if (p.revents & (POLLIN | POLLERR)) {
+ auto it = mClients.find(p.fd);
+ if (it == mClients.end()) {
+ SLOGE("fd vanished: %d", p.fd);
+ continue;
+ }
+ SocketClient* c = it->second;
+ pending.push_back(c);
c->incRef();
}
}
pthread_mutex_unlock(&mClientsLock);
- /* Process the pending list, since it is owned by the thread,
- * there is no need to lock it */
- while (!pendingList.empty()) {
- /* Pop the first item from the list */
- it = pendingList.begin();
- SocketClient* c = *it;
- pendingList.erase(it);
- /* Process it, if false is returned, remove from list */
+ for (SocketClient* c : pending) {
+ // Process it, if false is returned, remove from the map
+ SLOGV("processing fd %d", c->getSocket());
if (!onDataAvailable(c)) {
release(c, false);
}
@@ -246,17 +227,10 @@
bool ret = false;
/* if our sockets are connection-based, remove and destroy it */
if (mListen && c) {
- /* Remove the client from our array */
+ /* Remove the client from our map */
SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
pthread_mutex_lock(&mClientsLock);
- SocketClientCollection::iterator it;
- for (it = mClients->begin(); it != mClients->end(); ++it) {
- if (*it == c) {
- mClients->erase(it);
- ret = true;
- break;
- }
- }
+ ret = (mClients.erase(c->getSocket()) != 0);
pthread_mutex_unlock(&mClientsLock);
if (ret) {
ret = c->decRef();
@@ -269,26 +243,22 @@
return ret;
}
-void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
- SocketClientCollection safeList;
-
- /* Add all active clients to the safe list first */
- safeList.clear();
+std::vector<SocketClient*> SocketListener::snapshotClients() {
+ std::vector<SocketClient*> clients;
pthread_mutex_lock(&mClientsLock);
- SocketClientCollection::iterator i;
-
- for (i = mClients->begin(); i != mClients->end(); ++i) {
- SocketClient* c = *i;
+ clients.reserve(mClients.size());
+ for (auto pair : mClients) {
+ SocketClient* c = pair.second;
c->incRef();
- safeList.push_back(c);
+ clients.push_back(c);
}
pthread_mutex_unlock(&mClientsLock);
- while (!safeList.empty()) {
- /* Pop the first item from the list */
- i = safeList.begin();
- SocketClient* c = *i;
- safeList.erase(i);
+ return clients;
+}
+
+void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
+ for (SocketClient* c : snapshotClients()) {
// broadcasts are unsolicited and should not include a cmd number
if (c->sendMsg(code, msg, addErrno, false)) {
SLOGW("Error sending broadcast (%s)", strerror(errno));
@@ -298,25 +268,7 @@
}
void SocketListener::runOnEachSocket(SocketClientCommand *command) {
- SocketClientCollection safeList;
-
- /* Add all active clients to the safe list first */
- safeList.clear();
- pthread_mutex_lock(&mClientsLock);
- SocketClientCollection::iterator i;
-
- for (i = mClients->begin(); i != mClients->end(); ++i) {
- SocketClient* c = *i;
- c->incRef();
- safeList.push_back(c);
- }
- pthread_mutex_unlock(&mClientsLock);
-
- while (!safeList.empty()) {
- /* Pop the first item from the list */
- i = safeList.begin();
- SocketClient* c = *i;
- safeList.erase(i);
+ for (SocketClient* c : snapshotClients()) {
command->runSocketCommand(c);
c->decRef();
}
diff --git a/libsysutils/src/SocketListener_test.cpp b/libsysutils/src/SocketListener_test.cpp
new file mode 100644
index 0000000..fed4546
--- /dev/null
+++ b/libsysutils/src/SocketListener_test.cpp
@@ -0,0 +1,182 @@
+/*
+ * 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 <sysutils/FrameworkListener.h>
+
+#include <poll.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <algorithm>
+#include <memory>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <cutils/sockets.h>
+#include <gtest/gtest.h>
+
+using android::base::unique_fd;
+
+namespace {
+
+std::string testSocketPath() {
+ const testing::TestInfo* const test_info =
+ testing::UnitTest::GetInstance()->current_test_info();
+ return std::string(ANDROID_SOCKET_DIR "/") + std::string(test_info->test_case_name()) +
+ std::string(".") + std::string(test_info->name());
+}
+
+unique_fd serverSocket(const std::string& path) {
+ unlink(path.c_str());
+
+ unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ EXPECT_GE(fd.get(), 0);
+
+ struct sockaddr_un addr = {.sun_family = AF_UNIX};
+ strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+ EXPECT_EQ(bind(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)), 0)
+ << "bind() to " << path << " failed: " << strerror(errno);
+ EXPECT_EQ(android_get_control_socket(path.c_str()), -1);
+
+ return fd;
+}
+
+unique_fd clientSocket(const std::string& path) {
+ unique_fd fd(socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
+ EXPECT_GE(fd.get(), 0);
+
+ struct sockaddr_un addr = {.sun_family = AF_UNIX};
+ strlcpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path));
+
+ EXPECT_EQ(0, connect(fd.get(), reinterpret_cast<sockaddr*>(&addr), sizeof(addr)))
+ << "connect() to " << path << " failed: " << strerror(errno);
+
+ return fd;
+}
+
+void sendCmd(int fd, const char* cmd) {
+ EXPECT_TRUE(android::base::WriteFully(fd, cmd, strlen(cmd) + 1))
+ << "write() to socket failed: " << strerror(errno);
+}
+
+std::string recvReply(int fd) {
+ pollfd fds = {.fd = fd, .events = POLLIN};
+ int poll_events = poll(&fds, 1, -1);
+ EXPECT_EQ(1, poll_events);
+
+ // Technically, this one-shot read() is incorrect: we should keep on
+ // reading the socket until we get a \0. But this is also how
+ // FrameworkListener::onDataAvailable() reads, and it works because
+ // replies are always send with a single write() call, and clients
+ // always read replies before queueing the next command.
+ char buf[1024];
+ ssize_t len = read(fd, buf, sizeof(buf));
+ EXPECT_GE(len, 0) << "read() from socket failed: " << strerror(errno);
+ return len > 0 ? std::string(buf, buf + len) : "";
+}
+
+// Test command which echoes back all its arguments as a comma-separated list.
+// Always returns error code 42
+//
+// TODO: enable testing replies with addErrno=true and useCmdNum=true
+class TestCommand : public FrameworkCommand {
+ public:
+ TestCommand() : FrameworkCommand("test") {}
+ ~TestCommand() override {}
+
+ int runCommand(SocketClient* cli, int argc, char** argv) {
+ std::vector<std::string> args(argv, argv + argc);
+ std::string reply = android::base::Join(args, ',');
+ cli->sendMsg(42, reply.c_str(), /*addErrno=*/false, /*useCmdNum=*/false);
+ return 0;
+ }
+};
+
+// A test listener with a single command.
+class TestListener : public FrameworkListener {
+ public:
+ TestListener(int fd) : FrameworkListener(fd) {
+ registerCmd(new TestCommand); // Leaked :-(
+ }
+};
+
+} // unnamed namespace
+
+class FrameworkListenerTest : public testing::Test {
+ public:
+ FrameworkListenerTest() {
+ mSocketPath = testSocketPath();
+ mSserverFd = serverSocket(mSocketPath);
+ mListener = std::make_unique<TestListener>(mSserverFd.get());
+ EXPECT_EQ(0, mListener->startListener());
+ }
+
+ ~FrameworkListenerTest() override {
+ EXPECT_EQ(0, mListener->stopListener());
+
+ // Wouldn't it be cool if unique_fd had an option for taking care of this?
+ unlink(mSocketPath.c_str());
+ }
+
+ void testCommand(const char* command, const char* expected) {
+ unique_fd client_fd = clientSocket(mSocketPath);
+ sendCmd(client_fd.get(), command);
+
+ std::string reply = recvReply(client_fd.get());
+ EXPECT_EQ(std::string(expected) + '\0', reply);
+ }
+
+ protected:
+ std::string mSocketPath;
+ unique_fd mSserverFd;
+ std::unique_ptr<TestListener> mListener;
+};
+
+TEST_F(FrameworkListenerTest, DoesNothing) {
+ // Let the test harness start and stop a FrameworkListener
+ // without sending any commands through it.
+}
+
+TEST_F(FrameworkListenerTest, DispatchesValidCommands) {
+ testCommand("test", "42 test");
+ testCommand("test arg1 arg2", "42 test,arg1,arg2");
+ testCommand("test \"arg1 still_arg1\" arg2", "42 test,arg1 still_arg1,arg2");
+ testCommand("test \"escaped quote: '\\\"'\"", "42 test,escaped quote: '\"'");
+
+ // Perhaps this behavior was unintended, but would be good to detect any
+ // changes, in case anyone depends on it.
+ testCommand("test ", "42 test,,,");
+}
+
+TEST_F(FrameworkListenerTest, RejectsInvalidCommands) {
+ testCommand("unknown arg1 arg2", "500 Command not recognized");
+ testCommand("test \"arg1 arg2", "500 Unclosed quotes error");
+ testCommand("test \\a", "500 Unsupported escape sequence");
+}
+
+TEST_F(FrameworkListenerTest, MultipleClients) {
+ unique_fd client1 = clientSocket(mSocketPath);
+ unique_fd client2 = clientSocket(mSocketPath);
+ sendCmd(client1.get(), "test 1");
+ sendCmd(client2.get(), "test 2");
+
+ EXPECT_EQ(std::string("42 test,2") + '\0', recvReply(client2.get()));
+ EXPECT_EQ(std::string("42 test,1") + '\0', recvReply(client1.get()));
+}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index d635e65..1c1bdf7 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -58,7 +58,6 @@
"-Wall",
"-Werror",
],
- include_dirs: ["external/safe-iop/include"],
header_libs: [
"libutils_headers",
],
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 00a904d..e16f88d 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -24,8 +24,6 @@
#include <log/log.h>
-#include <safe_iop.h>
-
#include "SharedBuffer.h"
/*****************************************************************************/
@@ -342,7 +340,7 @@
}
size_t new_allocation_size = 0;
- LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize));
+ LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_allocation_size));
SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);
if (sb) {
void* array = sb->data();
@@ -386,7 +384,7 @@
this, (int)where, (int)amount, (int)mCount); // caller already checked
size_t new_size;
- LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(mCount, amount, &new_size), "new_size overflow");
if (capacity() < new_size) {
// NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
@@ -397,17 +395,18 @@
//
// This approximates the old calculation, using (x + (x/2) + 1) instead.
size_t new_capacity = 0;
- LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
+ LOG_ALWAYS_FATAL_IF(__builtin_add_overflow(new_size, (new_size / 2), &new_capacity),
"new_capacity overflow");
- LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
- "new_capacity overflow");
+ LOG_ALWAYS_FATAL_IF(
+ __builtin_add_overflow(new_capacity, static_cast<size_t>(1u), &new_capacity),
+ "new_capacity overflow");
new_capacity = max(kMinVectorCapacity, new_capacity);
size_t new_alloc_size = 0;
- LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
+ LOG_ALWAYS_FATAL_IF(__builtin_mul_overflow(new_capacity, mItemSize, &new_alloc_size),
"new_alloc_size overflow");
-// ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
+ // ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
if ((mStorage) &&
(mCount==where) &&
(mFlags & HAS_TRIVIAL_COPY) &&
@@ -464,7 +463,7 @@
this, (int)where, (int)amount, (int)mCount); // caller already checked
size_t new_size;
- LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
+ LOG_ALWAYS_FATAL_IF(__builtin_sub_overflow(mCount, amount, &new_size));
if (new_size < (capacity() / 2)) {
// NOTE: (new_size * 2) is safe because capacity didn't overflow and
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 9536fc7..add6e14 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -41,6 +41,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h> // TEMP_FAILURE_RETRY may or may not be in unistd
#include <android-base/memory.h>
+#include <android-base/utf8.h>
#include <log/log.h>
#include <utils/Compat.h>
#include <utils/FileMap.h>
@@ -471,7 +472,7 @@
}
int32_t OpenArchive(const char* fileName, ZipArchiveHandle* handle) {
- const int fd = open(fileName, O_RDONLY | O_BINARY, 0);
+ const int fd = ::android::base::utf8::open(fileName, O_RDONLY | O_BINARY, 0);
ZipArchive* archive = new ZipArchive(fd, true);
*handle = archive;
diff --git a/llkd/libllkd.cpp b/llkd/libllkd.cpp
index 48551f2..bb55d1f 100644
--- a/llkd/libllkd.cpp
+++ b/llkd/libllkd.cpp
@@ -738,6 +738,8 @@
}
void* llkThread(void* obj) {
+ prctl(PR_SET_DUMPABLE, 0);
+
LOG(INFO) << "started";
std::string name = std::to_string(::gettid());
diff --git a/llkd/llkd.cpp b/llkd/llkd.cpp
index f10253d..1920198 100644
--- a/llkd/llkd.cpp
+++ b/llkd/llkd.cpp
@@ -17,6 +17,7 @@
#include "llkd.h"
#include <sched.h>
+#include <sys/prctl.h>
#include <unistd.h>
#include <chrono>
@@ -26,6 +27,8 @@
using namespace std::chrono;
int main(int, char**) {
+ prctl(PR_SET_DUMPABLE, 0);
+
LOG(INFO) << "started";
bool enabled = llkInit();
diff --git a/toolbox/getevent.c b/toolbox/getevent.c
index e6def6b..39033ad 100644
--- a/toolbox/getevent.c
+++ b/toolbox/getevent.c
@@ -321,7 +321,7 @@
char idstr[80];
struct input_id id;
- fd = open(device, O_RDWR);
+ fd = open(device, O_RDONLY | O_CLOEXEC);
if(fd < 0) {
if(print_flags & PRINT_DEVICE_ERRORS)
fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
diff --git a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
index 8c5cff6..0956fe6 100644
--- a/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
+++ b/trusty/keymaster/ipc/trusty_keymaster_ipc.cpp
@@ -167,7 +167,7 @@
// TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately.
return translate_error(rc);
} else {
- ALOGE("Received %d byte response\n", rsp_size);
+ ALOGV("Received %d byte response\n", rsp_size);
}
const uint8_t* p = recv_buf;