Merge UP1A.230119.002

Change-Id: I7ed93ddd712083f5a5e93811c576ba600c104794
This commit is contained in:
Deyao Ren 2023-02-13 00:07:01 +00:00
commit 65f954819e
26 changed files with 502 additions and 186 deletions

View File

@ -40,15 +40,6 @@ on post-fs-data
chown system log /data/misc/bootstat/time_since_last_boot chown system log /data/misc/bootstat/time_since_last_boot
# end ota transitional support # end ota transitional support
# Record the time at which the user has successfully entered the pin to decrypt
# the device, /data is decrypted, and the system is entering the main boot phase.
#
# post-fs-data: /data is writable
# property:init.svc.bootanim=running: The boot animation is running
# property:ro.crypto.type=block: FDE device
on post-fs-data && property:init.svc.bootanim=running && property:ro.crypto.type=block
exec_background - system log -- /system/bin/bootstat -r post_decrypt_time_elapsed
# Initialize bootstat state machine. # Initialize bootstat state machine.
# #
# sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens # sys.bootstat.first_boot_completed: responsible for making sure that record_boot_complete happens

View File

@ -88,7 +88,7 @@ GwpAsanCrashData::GwpAsanCrashData(unwindstack::Memory* process_memory,
thread_id_ = thread_info.tid; thread_id_ = thread_info.tid;
// Grab the internal error address, if it exists. // Grab the internal error address, if it exists.
uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_); uintptr_t internal_crash_address = __gwp_asan_get_internal_crash_address(&state_, crash_address_);
if (internal_crash_address) { if (internal_crash_address) {
crash_address_ = internal_crash_address; crash_address_ = internal_crash_address;
} }

View File

@ -44,9 +44,12 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
__scudo_get_stack_depot_size()); __scudo_get_stack_depot_size());
auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info, auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
__scudo_get_region_info_size()); __scudo_get_region_info_size());
auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer, std::unique_ptr<char[]> ring_buffer;
process_info.scudo_ring_buffer_size); if (process_info.scudo_ring_buffer_size != 0) {
if (!stack_depot || !region_info || !ring_buffer) { ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
process_info.scudo_ring_buffer_size);
}
if (!stack_depot || !region_info) {
return; return;
} }

View File

@ -1082,7 +1082,8 @@ static void flash_buf(const std::string& partition, struct fastboot_buffer* buf)
sparse_file** s; sparse_file** s;
if (partition == "boot" || partition == "boot_a" || partition == "boot_b" || if (partition == "boot" || partition == "boot_a" || partition == "boot_b" ||
partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b") { partition == "init_boot" || partition == "init_boot_a" || partition == "init_boot_b" ||
partition == "recovery" || partition == "recovery_a" || partition == "recovery_b") {
copy_avb_footer(partition, buf); copy_avb_footer(partition, buf);
} }

View File

@ -27,5 +27,16 @@
{ {
"name": "cow_api_test" "name": "cow_api_test"
} }
],
"kernel-presubmit": [
{
"name": "vts_libdm_test"
},
{
"name": "vts_core_liblp_test"
},
{
"name": "vts_libsnapshot_test"
}
] ]
} }

View File

@ -305,19 +305,16 @@ bool ParseFsMgrFlags(const std::string& flags, FstabEntry* entry) {
if (!ParseByteCount(arg, &entry->zram_backingdev_size)) { if (!ParseByteCount(arg, &entry->zram_backingdev_size)) {
LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg; LWARNING << "Warning: zram_backingdev_size= flag malformed: " << arg;
} }
} else if (StartsWith(flag, "zoned_device=")) { } else if (flag == "zoned_device") {
std::string zoned; if (access("/dev/block/by-name/zoned_device", F_OK) == 0) {
if (ReadFileToString("/sys/class/block/" + arg + "/queue/zoned", &zoned) && entry->zoned_device = "/dev/block/by-name/zoned_device";
android::base::StartsWith(zoned, "host-managed")) {
entry->zoned_device = "/dev/block/" + arg;
// atgc in f2fs does not support a zoned device // atgc in f2fs does not support a zoned device
auto options = Split(entry->fs_options, ","); auto options = Split(entry->fs_options, ",");
options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end()); options.erase(std::remove(options.begin(), options.end(), "atgc"), options.end());
entry->fs_options = android::base::Join(options, ","); entry->fs_options = android::base::Join(options, ",");
LINFO << "Removed ATGC in fs_options as " << entry->fs_options; LINFO << "Removed ATGC in fs_options as " << entry->fs_options
} else { << " for zoned device=" << entry->zoned_device;
LWARNING << "Warning: cannot find the zoned device: " << arg;
} }
} else { } else {
LWARNING << "Warning: unknown flag: " << flag; LWARNING << "Warning: unknown flag: " << flag;

View File

@ -35,18 +35,18 @@ images. After `/data` is mounted however, there are two problems:
We break the problem down into three scenarios. We break the problem down into three scenarios.
### FDE and Metadata Encrypted Devices ### Metadata Encrypted Devices
When FDE or metadata encryption is used, `/data` is not mounted from When metadata encryption is used, `/data` is not mounted from
`/dev/block/by-name/data`. Instead, it is mounted from an intermediate `/dev/block/by-name/data`. Instead, it is mounted from an intermediate
`dm-crypt` or `dm-default-key` device. This means the underlying device is `dm-default-key` device. This means the underlying device is not marked in use,
not marked in use, and we can create new dm-linear devices on top of it. and we can create new dm-linear devices on top of it.
On these devices, a block device for an image will consist of a single On these devices, a block device for an image will consist of a single
device-mapper device with a `dm-linear` table entry for each extent in the device-mapper device with a `dm-linear` table entry for each extent in the
backing file. backing file.
### Unencrypted and FBE-encrypted Devices ### Unencrypted and FBE-only Devices
When a device is unencrypted, or is encrypted with FBE but not metadata When a device is unencrypted, or is encrypted with FBE but not metadata
encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled. encryption, we instead use a loop device with `LOOP_SET_DIRECT_IO` enabled.

View File

@ -1498,6 +1498,7 @@ void SnapshotManager::AcknowledgeMergeSuccess(LockedFile* lock) {
if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) { if (UpdateUsesUserSnapshots(lock) && !device()->IsTestDevice()) {
if (snapuserd_client_) { if (snapuserd_client_) {
snapuserd_client_->DetachSnapuserd(); snapuserd_client_->DetachSnapuserd();
snapuserd_client_->RemoveTransitionedDaemonIndicator();
snapuserd_client_ = nullptr; snapuserd_client_ = nullptr;
} }
} }

View File

@ -37,11 +37,13 @@ cc_defaults {
cc_library_static { cc_library_static {
name: "libsnapshot_snapuserd", name: "libsnapshot_snapuserd",
defaults: [ defaults: [
"fs_mgr_defaults",
"libsnapshot_snapuserd_defaults", "libsnapshot_snapuserd_defaults",
], ],
recovery_available: true, recovery_available: true,
static_libs: [ static_libs: [
"libcutils_sockets", "libcutils_sockets",
"libfs_mgr",
], ],
shared_libs: [ shared_libs: [
"libbase", "libbase",
@ -49,6 +51,7 @@ cc_library_static {
], ],
export_include_dirs: ["include"], export_include_dirs: ["include"],
ramdisk_available: true, ramdisk_available: true,
vendor_ramdisk_available: true,
} }
cc_defaults { cc_defaults {
@ -86,6 +89,7 @@ cc_defaults {
"libgflags", "libgflags",
"liblog", "liblog",
"libsnapshot_cow", "libsnapshot_cow",
"libsnapshot_snapuserd",
"libz", "libz",
"liblz4", "liblz4",
"libext4_utils", "libext4_utils",

View File

@ -32,6 +32,7 @@ static constexpr uint32_t PACKET_SIZE = 512;
static constexpr char kSnapuserdSocket[] = "snapuserd"; static constexpr char kSnapuserdSocket[] = "snapuserd";
static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy"; static constexpr char kSnapuserdSocketProxy[] = "snapuserd_proxy";
static constexpr char kDaemonAliveIndicator[] = "daemon-alive-indicator";
// Ensure that the second-stage daemon for snapuserd is running. // Ensure that the second-stage daemon for snapuserd is running.
bool EnsureSnapuserdStarted(); bool EnsureSnapuserdStarted();
@ -44,9 +45,11 @@ class SnapuserdClient {
std::string Receivemsg(); std::string Receivemsg();
bool ValidateConnection(); bool ValidateConnection();
std::string GetDaemonAliveIndicatorPath();
public: public:
explicit SnapuserdClient(android::base::unique_fd&& sockfd); explicit SnapuserdClient(android::base::unique_fd&& sockfd);
SnapuserdClient(){};
static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name, static std::unique_ptr<SnapuserdClient> Connect(const std::string& socket_name,
std::chrono::milliseconds timeout_ms); std::chrono::milliseconds timeout_ms);
@ -91,6 +94,17 @@ class SnapuserdClient {
// Check the update verification status - invoked by update_verifier during // Check the update verification status - invoked by update_verifier during
// boot // boot
bool QueryUpdateVerification(); bool QueryUpdateVerification();
// Check if Snapuser daemon is ready post selinux transition after OTA boot
// This is invoked only by init as there is no sockets setup yet during
// selinux transition
bool IsTransitionedDaemonReady();
// Remove the daemon-alive-indicator path post snapshot merge
bool RemoveTransitionedDaemonIndicator();
// Notify init that snapuserd daemon is ready post selinux transition
void NotifyTransitionDaemonIsReady();
}; };
} // namespace snapshot } // namespace snapshot

View File

@ -29,10 +29,12 @@
#include <chrono> #include <chrono>
#include <sstream> #include <sstream>
#include <android-base/file.h>
#include <android-base/logging.h> #include <android-base/logging.h>
#include <android-base/parseint.h> #include <android-base/parseint.h>
#include <android-base/properties.h> #include <android-base/properties.h>
#include <android-base/strings.h> #include <android-base/strings.h>
#include <fs_mgr/file_wait.h>
#include <snapuserd/snapuserd_client.h> #include <snapuserd/snapuserd_client.h>
namespace android { namespace android {
@ -279,5 +281,42 @@ bool SnapuserdClient::QueryUpdateVerification() {
return response == "success"; return response == "success";
} }
std::string SnapuserdClient::GetDaemonAliveIndicatorPath() {
return "/metadata/ota/" + std::string(kDaemonAliveIndicator);
}
bool SnapuserdClient::IsTransitionedDaemonReady() {
if (!android::fs_mgr::WaitForFile(GetDaemonAliveIndicatorPath(), 10s)) {
LOG(ERROR) << "Timed out waiting for daemon indicator path: "
<< GetDaemonAliveIndicatorPath();
return false;
}
return true;
}
bool SnapuserdClient::RemoveTransitionedDaemonIndicator() {
std::string error;
std::string filePath = GetDaemonAliveIndicatorPath();
if (!android::base::RemoveFileIfExists(filePath, &error)) {
LOG(ERROR) << "Failed to remove DaemonAliveIndicatorPath - error: " << error;
return false;
}
if (!android::fs_mgr::WaitForFileDeleted(filePath, 5s)) {
LOG(ERROR) << "Timed out waiting for " << filePath << " to unlink";
return false;
}
return true;
}
void SnapuserdClient::NotifyTransitionDaemonIsReady() {
if (!android::base::WriteStringToFile("1", GetDaemonAliveIndicatorPath())) {
PLOG(ERROR) << "Unable to write daemon alive indicator path: "
<< GetDaemonAliveIndicatorPath();
}
}
} // namespace snapshot } // namespace snapshot
} // namespace android } // namespace android

View File

@ -119,6 +119,12 @@ bool Daemon::StartServerForUserspaceSnapshots(int arg_start, int argc, char** ar
} }
} }
// We reach this point only during selinux transition during device boot.
// At this point, all threads are spin up and are ready to serve the I/O
// requests for dm-user. Lets inform init.
auto client = std::make_unique<SnapuserdClient>();
client->NotifyTransitionDaemonIsReady();
// Skip the accept() call to avoid spurious log spam. The server will still // Skip the accept() call to avoid spurious log spam. The server will still
// run until all handlers have completed. // run until all handlers have completed.
return user_server_.WaitForSocket(); return user_server_.WaitForSocket();

View File

@ -454,15 +454,7 @@ cc_test {
defaults: ["init_defaults"], defaults: ["init_defaults"],
require_root: true, require_root: true,
compile_multilib: "both", compile_multilib: "first",
multilib: {
lib32: {
suffix: "32",
},
lib64: {
suffix: "64",
},
},
srcs: [ srcs: [
"devices_test.cpp", "devices_test.cpp",

View File

@ -22,7 +22,6 @@
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
<option name="cleanup" value="true" /> <option name="cleanup" value="true" />
<option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" /> <option name="push" value="CtsInitTestCases->/data/local/tmp/CtsInitTestCases" />
<option name="append-bitness" value="true" />
</target_preparer> </target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"> <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
<option name="throw-on-error" value="false" /> <option name="throw-on-error" value="false" />

View File

@ -174,6 +174,17 @@ If the property `true` becomes `true` *AFTER* `boot` was triggered, nothing will
be executed. The condition `boot && property:true=true` will be evaluated to be executed. The condition `boot && property:true=true` will be evaluated to
false because the `boot` trigger is a past event. false because the `boot` trigger is a past event.
Note that when `ro.property_service.async_persist_writes` is `true`, there is no
defined ordering between persistent setprops and non-persistent setprops. For
example:
on boot
setprop a 1
setprop persist.b 2
When `ro.property_service.async_persist_writes` is `true`, triggers for these
two properties may execute in any order.
Services Services
-------- --------
Services are programs which init launches and (optionally) restarts Services are programs which init launches and (optionally) restarts

View File

@ -431,6 +431,12 @@ std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uev
} }
} }
std::string model;
if (ReadFileToString("/sys/class/block/" + uevent.device_name + "/queue/zoned", &model) &&
!StartsWith(model, "none")) {
links.emplace_back("/dev/block/by-name/zoned_device");
}
auto last_slash = uevent.path.rfind('/'); auto last_slash = uevent.path.rfind('/');
links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1)); links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));

View File

@ -98,6 +98,9 @@ using android::sysprop::InitProperties::is_userspace_reboot_supported;
namespace android { namespace android {
namespace init { namespace init {
class PersistWriteThread;
constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint"; constexpr auto FINGERPRINT_PROP = "ro.build.fingerprint";
constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint"; constexpr auto LEGACY_FINGERPRINT_PROP = "ro.build.legacy.fingerprint";
constexpr auto ID_PROP = "ro.build.id"; constexpr auto ID_PROP = "ro.build.id";
@ -115,6 +118,8 @@ static bool accept_messages = false;
static std::mutex accept_messages_lock; static std::mutex accept_messages_lock;
static std::thread property_service_thread; static std::thread property_service_thread;
static std::unique_ptr<PersistWriteThread> persist_write_thread;
static PropertyInfoAreaFile property_info_area; static PropertyInfoAreaFile property_info_area;
struct PropertyAuditData { struct PropertyAuditData {
@ -177,48 +182,13 @@ static bool CheckMacPerms(const std::string& name, const char* target_context,
return has_access; return has_access;
} }
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) { void NotifyPropertyChange(const std::string& name, const std::string& value) {
size_t valuelen = value.size();
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
*error = result.error().message();
return PROP_ERROR_INVALID_VALUE;
}
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) {
*error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
__system_property_update(pi, value.c_str(), valuelen);
} else {
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);
}
// If init hasn't started its main loop, then it won't be handling property changed messages // If init hasn't started its main loop, then it won't be handling property changed messages
// anyway, so there's no need to try to send them. // anyway, so there's no need to try to send them.
auto lock = std::lock_guard{accept_messages_lock}; auto lock = std::lock_guard{accept_messages_lock};
if (accept_messages) { if (accept_messages) {
PropertyChanged(name, value); PropertyChanged(name, value);
} }
return PROP_SUCCESS;
} }
class AsyncRestorecon { class AsyncRestorecon {
@ -259,7 +229,9 @@ class AsyncRestorecon {
class SocketConnection { class SocketConnection {
public: public:
SocketConnection() = default;
SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {} SocketConnection(int socket, const ucred& cred) : socket_(socket), cred_(cred) {}
SocketConnection(SocketConnection&&) = default;
bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) { bool RecvUint32(uint32_t* value, uint32_t* timeout_ms) {
return RecvFully(value, sizeof(*value), timeout_ms); return RecvFully(value, sizeof(*value), timeout_ms);
@ -318,6 +290,8 @@ class SocketConnection {
const ucred& cred() { return cred_; } const ucred& cred() { return cred_; }
SocketConnection& operator=(SocketConnection&&) = default;
private: private:
bool PollIn(uint32_t* timeout_ms) { bool PollIn(uint32_t* timeout_ms) {
struct pollfd ufd = { struct pollfd ufd = {
@ -388,9 +362,78 @@ class SocketConnection {
unique_fd socket_; unique_fd socket_;
ucred cred_; ucred cred_;
DISALLOW_IMPLICIT_CONSTRUCTORS(SocketConnection); DISALLOW_COPY_AND_ASSIGN(SocketConnection);
}; };
class PersistWriteThread {
public:
PersistWriteThread();
void Write(std::string name, std::string value, SocketConnection socket);
private:
void Work();
private:
std::thread thread_;
std::mutex mutex_;
std::condition_variable cv_;
std::deque<std::tuple<std::string, std::string, SocketConnection>> work_;
};
static std::optional<uint32_t> PropertySet(const std::string& name, const std::string& value,
SocketConnection* socket, std::string* error) {
size_t valuelen = value.size();
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return {PROP_ERROR_INVALID_NAME};
}
if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
*error = result.error().message();
return {PROP_ERROR_INVALID_VALUE};
}
prop_info* pi = (prop_info*)__system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) {
*error = "Read-only property was already set";
return {PROP_ERROR_READ_ONLY_PROPERTY};
}
__system_property_update(pi, value.c_str(), valuelen);
} else {
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
return {PROP_ERROR_SET_FAILED};
}
}
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
if (socket && persistent_properties_loaded && StartsWith(name, "persist.")) {
if (persist_write_thread) {
persist_write_thread->Write(name, value, std::move(*socket));
return {};
}
WritePersistentProperty(name, value);
}
NotifyPropertyChange(name, value);
return {PROP_SUCCESS};
}
// Helper for PropertySet, for the case where no socket is used, and therefore an asynchronous
// return is not possible.
static uint32_t PropertySetNoSocket(const std::string& name, const std::string& value,
std::string* error) {
auto ret = PropertySet(name, value, nullptr, error);
CHECK(ret.has_value());
return *ret;
}
static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid, static uint32_t SendControlMessage(const std::string& msg, const std::string& name, pid_t pid,
SocketConnection* socket, std::string* error) { SocketConnection* socket, std::string* error) {
auto lock = std::lock_guard{accept_messages_lock}; auto lock = std::lock_guard{accept_messages_lock};
@ -481,16 +524,17 @@ uint32_t CheckPermissions(const std::string& name, const std::string& value,
return PROP_SUCCESS; return PROP_SUCCESS;
} }
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*. // This returns one of the enum of PROP_SUCCESS or PROP_ERROR*, or std::nullopt
uint32_t HandlePropertySet(const std::string& name, const std::string& value, // if asynchronous.
const std::string& source_context, const ucred& cr, std::optional<uint32_t> HandlePropertySet(const std::string& name, const std::string& value,
SocketConnection* socket, std::string* error) { const std::string& source_context, const ucred& cr,
SocketConnection* socket, std::string* error) {
if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) { if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
return ret; return {ret};
} }
if (StartsWith(name, "ctl.")) { if (StartsWith(name, "ctl.")) {
return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error); return {SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error)};
} }
// sys.powerctl is a special property that is used to make the device reboot. We want to log // sys.powerctl is a special property that is used to make the device reboot. We want to log
@ -511,7 +555,7 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value,
} }
if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) { if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
*error = "Userspace reboot is not supported by this device"; *error = "Userspace reboot is not supported by this device";
return PROP_ERROR_INVALID_VALUE; return {PROP_ERROR_INVALID_VALUE};
} }
} }
@ -522,10 +566,20 @@ uint32_t HandlePropertySet(const std::string& name, const std::string& value,
if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) { if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
static AsyncRestorecon async_restorecon; static AsyncRestorecon async_restorecon;
async_restorecon.TriggerRestorecon(value); async_restorecon.TriggerRestorecon(value);
return PROP_SUCCESS; return {PROP_SUCCESS};
} }
return PropertySet(name, value, error); return PropertySet(name, value, socket, error);
}
// Helper for HandlePropertySet, for the case where no socket is used, and
// therefore an asynchronous return is not possible.
uint32_t HandlePropertySetNoSocket(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr,
std::string* error) {
auto ret = HandlePropertySet(name, value, source_context, cr, nullptr, error);
CHECK(ret.has_value());
return *ret;
} }
static void handle_property_set_fd() { static void handle_property_set_fd() {
@ -576,8 +630,7 @@ static void handle_property_set_fd() {
const auto& cr = socket.cred(); const auto& cr = socket.cred();
std::string error; std::string error;
uint32_t result = auto result = HandlePropertySetNoSocket(prop_name, prop_value, source_context, cr, &error);
HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
if (result != PROP_SUCCESS) { if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
@ -603,14 +656,19 @@ static void handle_property_set_fd() {
return; return;
} }
// HandlePropertySet takes ownership of the socket if the set is handled asynchronously.
const auto& cr = socket.cred(); const auto& cr = socket.cred();
std::string error; std::string error;
uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error); auto result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
if (result != PROP_SUCCESS) { if (!result) {
// Result will be sent after completion.
return;
}
if (*result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error; << " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
} }
socket.SendUint32(result); socket.SendUint32(*result);
break; break;
} }
@ -622,10 +680,9 @@ static void handle_property_set_fd() {
} }
uint32_t InitPropertySet(const std::string& name, const std::string& value) { uint32_t InitPropertySet(const std::string& name, const std::string& value) {
uint32_t result = 0;
ucred cr = {.pid = 1, .uid = 0, .gid = 0}; ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error; std::string error;
result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error); auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error);
if (result != PROP_SUCCESS) { if (result != PROP_SUCCESS) {
LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error; LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
} }
@ -795,7 +852,7 @@ static void load_override_properties() {
load_properties_from_file("/data/local.prop", nullptr, &properties); load_properties_from_file("/data/local.prop", nullptr, &properties);
for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) {
std::string error; std::string error;
if (PropertySet(name, value, &error) != PROP_SUCCESS) { if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {
LOG(ERROR) << "Could not set '" << name << "' to '" << value LOG(ERROR) << "Could not set '" << name << "' to '" << value
<< "' in /data/local.prop: " << error; << "' in /data/local.prop: " << error;
} }
@ -861,7 +918,7 @@ static void property_initialize_ro_product_props() {
LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val
<< "' (from " << target_prop << ")"; << "' (from " << target_prop << ")";
std::string error; std::string error;
uint32_t res = PropertySet(base_prop, target_prop_val, &error); auto res = PropertySetNoSocket(base_prop, target_prop_val, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res
<< " (" << error << ")"; << " (" << error << ")";
@ -890,7 +947,7 @@ static void property_initialize_build_id() {
} }
std::string error; std::string error;
auto res = PropertySet(ID_PROP, build_id, &error); auto res = PropertySetNoSocket(ID_PROP, build_id, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id; LOG(ERROR) << "Failed to set " << ID_PROP << " to " << build_id;
} }
@ -938,7 +995,7 @@ static void property_derive_legacy_build_fingerprint() {
<< legacy_build_fingerprint << "'"; << legacy_build_fingerprint << "'";
std::string error; std::string error;
uint32_t res = PropertySet(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error); auto res = PropertySetNoSocket(LEGACY_FINGERPRINT_PROP, legacy_build_fingerprint, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res LOG(ERROR) << "Error setting property '" << LEGACY_FINGERPRINT_PROP << "': err=" << res
<< " (" << error << ")"; << " (" << error << ")";
@ -956,7 +1013,7 @@ static void property_derive_build_fingerprint() {
LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'"; LOG(INFO) << "Setting property '" << FINGERPRINT_PROP << "' to '" << build_fingerprint << "'";
std::string error; std::string error;
uint32_t res = PropertySet(FINGERPRINT_PROP, build_fingerprint, &error); auto res = PropertySetNoSocket(FINGERPRINT_PROP, build_fingerprint, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " (" LOG(ERROR) << "Error setting property '" << FINGERPRINT_PROP << "': err=" << res << " ("
<< error << ")"; << error << ")";
@ -976,7 +1033,7 @@ static void property_derive_build_product() {
LOG(INFO) << "Setting property 'ro.build.product' to '" << build_product << "'"; LOG(INFO) << "Setting property 'ro.build.product' to '" << build_product << "'";
std::string error; std::string error;
uint32_t res = PropertySet("ro.build.product", build_product, &error); auto res = PropertySetNoSocket("ro.build.product", build_product, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property 'ro.build.product': err=" << res << " (" << error LOG(ERROR) << "Error setting property 'ro.build.product': err=" << res << " (" << error
<< ")"; << ")";
@ -1006,7 +1063,7 @@ static void property_derive_build_description() {
LOG(INFO) << "Setting property 'ro.build.description' to '" << build_description << "'"; LOG(INFO) << "Setting property 'ro.build.description' to '" << build_description << "'";
std::string error; std::string error;
uint32_t res = PropertySet("ro.build.description", build_description, &error); auto res = PropertySetNoSocket("ro.build.description", build_description, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property 'ro.build.description': err=" << res << " (" << error LOG(ERROR) << "Error setting property 'ro.build.description': err=" << res << " (" << error
<< ")"; << ")";
@ -1052,7 +1109,7 @@ static void property_derive_build_display_id() {
LOG(INFO) << "Setting property 'ro.build.display.id' to '" << build_display_id << "'"; LOG(INFO) << "Setting property 'ro.build.display.id' to '" << build_display_id << "'";
std::string error; std::string error;
uint32_t res = PropertySet("ro.build.display.id", build_display_id, &error); auto res = PropertySetNoSocket("ro.build.display.id", build_display_id, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property 'ro.build.display.id': err=" << res << " (" << error LOG(ERROR) << "Error setting property 'ro.build.display.id': err=" << res << " (" << error
<< ")"; << ")";
@ -1121,7 +1178,7 @@ static void property_initialize_ro_cpu_abilist() {
LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'"; LOG(INFO) << "Setting property '" << prop << "' to '" << prop_val << "'";
std::string error; std::string error;
uint32_t res = PropertySet(prop, prop_val, &error); auto res = PropertySetNoSocket(prop, prop_val, &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error LOG(ERROR) << "Error setting property '" << prop << "': err=" << res << " (" << error
<< ")"; << ")";
@ -1155,7 +1212,7 @@ static void property_initialize_ro_vendor_api_level() {
int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS), int api_level = std::min(read_api_level_props(BOARD_API_LEVEL_PROPS),
read_api_level_props(DEVICE_API_LEVEL_PROPS)); read_api_level_props(DEVICE_API_LEVEL_PROPS));
std::string error; std::string error;
uint32_t res = PropertySet(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error); auto res = PropertySetNoSocket(VENDOR_API_LEVEL_PROP, std::to_string(api_level), &error);
if (res != PROP_SUCCESS) { if (res != PROP_SUCCESS) {
LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": " LOG(ERROR) << "Failed to set " << VENDOR_API_LEVEL_PROP << " with " << api_level << ": "
<< error << "(" << res << ")"; << error << "(" << res << ")";
@ -1249,7 +1306,7 @@ void PropertyLoadBootDefaults() {
for (const auto& [name, value] : properties) { for (const auto& [name, value] : properties) {
std::string error; std::string error;
if (PropertySet(name, value, &error) != PROP_SUCCESS) { if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {
LOG(ERROR) << "Could not set '" << name << "' to '" << value LOG(ERROR) << "Could not set '" << name << "' to '" << value
<< "' while loading .prop files" << error; << "' while loading .prop files" << error;
} }
@ -1491,6 +1548,46 @@ static void PropertyServiceThread() {
} }
} }
PersistWriteThread::PersistWriteThread() {
auto new_thread = std::thread([this]() -> void { Work(); });
thread_.swap(new_thread);
}
void PersistWriteThread::Work() {
while (true) {
std::tuple<std::string, std::string, SocketConnection> item;
// Grab the next item within the lock.
{
std::unique_lock<std::mutex> lock(mutex_);
while (work_.empty()) {
cv_.wait(lock);
}
item = std::move(work_.front());
work_.pop_front();
}
std::this_thread::sleep_for(1s);
// Perform write/fsync outside the lock.
WritePersistentProperty(std::get<0>(item), std::get<1>(item));
NotifyPropertyChange(std::get<0>(item), std::get<1>(item));
SocketConnection& socket = std::get<2>(item);
socket.SendUint32(PROP_SUCCESS);
}
}
void PersistWriteThread::Write(std::string name, std::string value, SocketConnection socket) {
{
std::unique_lock<std::mutex> lock(mutex_);
work_.emplace_back(std::move(name), std::move(value), std::move(socket));
}
cv_.notify_all();
}
void StartPropertyService(int* epoll_socket) { void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2"); InitPropertySet("ro.property_service.version", "2");
@ -1515,6 +1612,13 @@ void StartPropertyService(int* epoll_socket) {
auto new_thread = std::thread{PropertyServiceThread}; auto new_thread = std::thread{PropertyServiceThread};
property_service_thread.swap(new_thread); property_service_thread.swap(new_thread);
auto async_persist_writes =
android::base::GetBoolProperty("ro.property_service.async_persist_writes", false);
if (async_persist_writes) {
persist_write_thread = std::make_unique<PersistWriteThread>();
}
} }
} // namespace init } // namespace init

View File

@ -18,7 +18,11 @@
#include <sys/socket.h> #include <sys/socket.h>
#include <condition_variable>
#include <deque>
#include <mutex>
#include <string> #include <string>
#include <thread>
#include "epoll.h" #include "epoll.h"

View File

@ -112,6 +112,10 @@ void LaunchFirstStageSnapuserd(SnapshotDriver driver) {
setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1); setenv(kSnapuserdFirstStagePidVar, std::to_string(pid).c_str(), 1);
if (!client->RemoveTransitionedDaemonIndicator()) {
LOG(ERROR) << "RemoveTransitionedDaemonIndicator failed";
}
LOG(INFO) << "Relaunched snapuserd with pid: " << pid; LOG(INFO) << "Relaunched snapuserd with pid: " << pid;
} }
@ -263,6 +267,19 @@ void SnapuserdSelinuxHelper::FinishTransition() {
* we may see audit logs. * we may see audit logs.
*/ */
bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() { bool SnapuserdSelinuxHelper::TestSnapuserdIsReady() {
// Wait for the daemon to be fully up. Daemon will write to path
// /metadata/ota/daemon-alive-indicator only when all the threads
// are ready and attached to dm-user.
//
// This check will fail for GRF devices with vendor on Android S.
// snapuserd binary from Android S won't be able to communicate
// and hence, we will fallback and issue I/O to verify
// the presence of daemon.
auto client = std::make_unique<SnapuserdClient>();
if (!client->IsTransitionedDaemonReady()) {
LOG(ERROR) << "IsTransitionedDaemonReady failed";
}
std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix(); std::string dev = "/dev/block/mapper/system"s + fs_mgr_get_slot_suffix();
android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT)); android::base::unique_fd fd(open(dev.c_str(), O_RDONLY | O_DIRECT));
if (fd < 0) { if (fd < 0) {

View File

@ -8,5 +8,10 @@
{ {
"name": "libcutils_test" "name": "libcutils_test"
} }
],
"kernel-presubmit": [
{
"name": "libcutils_test"
}
] ]
} }

View File

@ -0,0 +1,17 @@
BasedOnStyle: Google
AllowShortIfStatementsOnASingleLine: true
AllowShortFunctionsOnASingleLine: false
AllowShortLoopsOnASingleLine: true
BinPackArguments: true
BinPackParameters: true
ColumnLimit: 100
CommentPragmas: NOLINT:.*
ContinuationIndentWidth: 8
DerivePointerAlignment: false
IndentWidth: 4
PointerAlignment: Left
TabWidth: 4
AccessModifierOffset: -4
IncludeCategories:
- Regex: '^"Log\.h"'
Priority: -1

View File

@ -0,0 +1,71 @@
//
// Copyright (C) 2023 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.
//
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
cc_library {
name: "libexpresslog",
srcs: [
"Counter.cpp",
],
cflags: [
"-DNAMESPACE_FOR_HASH_FUNCTIONS=farmhash",
"-Wall",
"-Werror",
],
header_libs: [
"libtextclassifier_hash_headers",
],
static_libs: [
"libstatslog_express",
"libtextclassifier_hash_static",
],
shared_libs: [
"libbase",
"libstatssocket",
],
export_include_dirs: ["include"],
}
genrule {
name: "statslog_express.h",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_express.h --module expresslog --namespace android,expresslog",
out: [
"statslog_express.h",
],
}
genrule {
name: "statslog_express.cpp",
tools: ["stats-log-api-gen"],
cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_express.cpp --module expresslog --namespace android,expresslog --importHeader statslog_express.h",
out: [
"statslog_express.cpp",
],
}
cc_library_static {
name: "libstatslog_express",
generated_sources: ["statslog_express.cpp"],
generated_headers: ["statslog_express.h"],
export_generated_headers: ["statslog_express.h"],
shared_libs: [
"libstatssocket",
],
}

View File

@ -0,0 +1,32 @@
//
// Copyright (C) 2023 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 "include/Counter.h"
#include <statslog_express.h>
#include <string.h>
#include <utils/hash/farmhash.h>
namespace android {
namespace expresslog {
void Counter::logIncrement(const char* metricName, int64_t amount) {
const int64_t metricIdHash = farmhash::Fingerprint64(metricName, strlen(metricName));
stats_write(EXPRESS_EVENT_REPORTED, metricIdHash, amount);
}
} // namespace expresslog
} // namespace android

View File

@ -0,0 +1,30 @@
//
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#pragma once
#include <stdint.h>
namespace android {
namespace expresslog {
/** Counter encapsulates StatsD write API calls */
class Counter final {
public:
static void logIncrement(const char* metricId, int64_t amount = 1);
};
} // namespace expresslog
} // namespace android

View File

@ -1,20 +1,18 @@
#include <ctype.h> #include <ctype.h>
#include <dirent.h>
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <getopt.h> #include <getopt.h>
#include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/sysmacros.h> #include <sys/sysmacros.h>
#include <dirent.h> #include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <fcntl.h>
#include <linux/kdev_t.h> #include <linux/kdev_t.h>
@ -23,23 +21,12 @@
/* NOTES /* NOTES
** **
** - see buffer-format.txt from the linux kernel docs for ** - see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt
** an explanation of this file format ** for an explanation of this file format
** - dotfiles are ignored ** - dotfiles are ignored
** - directories named 'root' are ignored ** - directories named 'root' are ignored
*/ */
static void die(const char* why, ...) {
va_list ap;
va_start(ap, why);
fprintf(stderr,"error: ");
vfprintf(stderr, why, ap);
fprintf(stderr,"\n");
va_end(ap);
exit(1);
}
struct fs_config_entry { struct fs_config_entry {
char* name; char* name;
int uid, gid, mode; int uid, gid, mode;
@ -48,17 +35,8 @@ struct fs_config_entry {
static struct fs_config_entry* canned_config = NULL; static struct fs_config_entry* canned_config = NULL;
static const char* target_out_path = NULL; static const char* target_out_path = NULL;
/* Each line in the canned file should be a path plus three ints (uid,
* gid, mode). */
#ifdef PATH_MAX
#define CANNED_LINE_LENGTH (PATH_MAX+100)
#else
#define CANNED_LINE_LENGTH (1024)
#endif
#define TRAILER "TRAILER!!!" #define TRAILER "TRAILER!!!"
static int verbose = 0;
static int total_size = 0; static int total_size = 0;
static void fix_stat(const char *path, struct stat *s) static void fix_stat(const char *path, struct stat *s)
@ -134,7 +112,7 @@ static void _eject(struct stat *s, char *out, int olen, char *data, unsigned dat
total_size += 6 + 8*13 + olen + 1; total_size += 6 + 8*13 + olen + 1;
if(strlen(out) != (unsigned int)olen) die("ACK!"); if(strlen(out) != (unsigned int)olen) errx(1, "ACK!");
while(total_size & 3) { while(total_size & 3) {
total_size++; total_size++;
@ -168,23 +146,16 @@ static int compare(const void* a, const void* b) {
static void _archive_dir(char *in, char *out, int ilen, int olen) static void _archive_dir(char *in, char *out, int ilen, int olen)
{ {
int i, t; int i, t;
DIR *d;
struct dirent *de; struct dirent *de;
if(verbose) { DIR* d = opendir(in);
fprintf(stderr,"_archive_dir('%s','%s',%d,%d)\n", if (d == NULL) err(1, "cannot open directory '%s'", in);
in, out, ilen, olen);
}
d = opendir(in);
if(d == 0) die("cannot open directory '%s'", in);
int size = 32; int size = 32;
int entries = 0; int entries = 0;
char** names = malloc(size * sizeof(char*)); char** names = malloc(size * sizeof(char*));
if (names == NULL) { if (names == NULL) {
fprintf(stderr, "failed to allocate dir names array (size %d)\n", size); errx(1, "failed to allocate dir names array (size %d)", size);
exit(1);
} }
while((de = readdir(d)) != 0){ while((de = readdir(d)) != 0){
@ -198,16 +169,12 @@ static void _archive_dir(char *in, char *out, int ilen, int olen)
size *= 2; size *= 2;
names = realloc(names, size * sizeof(char*)); names = realloc(names, size * sizeof(char*));
if (names == NULL) { if (names == NULL) {
fprintf(stderr, "failed to reallocate dir names array (size %d)\n", errx(1, "failed to reallocate dir names array (size %d)", size);
size);
exit(1);
} }
} }
names[entries] = strdup(de->d_name); names[entries] = strdup(de->d_name);
if (names[entries] == NULL) { if (names[entries] == NULL) {
fprintf(stderr, "failed to strdup name \"%s\"\n", errx(1, "failed to strdup name \"%s\"", de->d_name);
de->d_name);
exit(1);
} }
++entries; ++entries;
} }
@ -241,26 +208,17 @@ static void _archive_dir(char *in, char *out, int ilen, int olen)
static void _archive(char *in, char *out, int ilen, int olen) static void _archive(char *in, char *out, int ilen, int olen)
{ {
struct stat s; struct stat s;
if(lstat(in, &s)) err(1, "could not stat '%s'", in);
if(verbose) {
fprintf(stderr,"_archive('%s','%s',%d,%d)\n",
in, out, ilen, olen);
}
if(lstat(in, &s)) die("could not stat '%s'\n", in);
if(S_ISREG(s.st_mode)){ if(S_ISREG(s.st_mode)){
char *tmp; int fd = open(in, O_RDONLY);
int fd; if(fd < 0) err(1, "cannot open '%s' for read", in);
fd = open(in, O_RDONLY); char* tmp = (char*) malloc(s.st_size);
if(fd < 0) die("cannot open '%s' for read", in); if(tmp == 0) errx(1, "cannot allocate %zd bytes", s.st_size);
tmp = (char*) malloc(s.st_size);
if(tmp == 0) die("cannot allocate %d bytes", s.st_size);
if(read(fd, tmp, s.st_size) != s.st_size) { if(read(fd, tmp, s.st_size) != s.st_size) {
die("cannot read %d bytes", s.st_size); err(1, "cannot read %zd bytes", s.st_size);
} }
_eject(&s, out, olen, tmp, s.st_size); _eject(&s, out, olen, tmp, s.st_size);
@ -274,13 +232,13 @@ static void _archive(char *in, char *out, int ilen, int olen)
char buf[1024]; char buf[1024];
int size; int size;
size = readlink(in, buf, 1024); size = readlink(in, buf, 1024);
if(size < 0) die("cannot read symlink '%s'", in); if(size < 0) err(1, "cannot read symlink '%s'", in);
_eject(&s, out, olen, buf, size); _eject(&s, out, olen, buf, size);
} else if(S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) || } else if(S_ISBLK(s.st_mode) || S_ISCHR(s.st_mode) ||
S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode)) { S_ISFIFO(s.st_mode) || S_ISSOCK(s.st_mode)) {
_eject(&s, out, olen, NULL, 0); _eject(&s, out, olen, NULL, 0);
} else { } else {
die("Unknown '%s' (mode %d)?\n", in, s.st_mode); errx(1, "Unknown '%s' (mode %d)?", in, s.st_mode);
} }
} }
@ -302,17 +260,18 @@ static void read_canned_config(char* filename)
canned_config = canned_config =
(struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry)); (struct fs_config_entry*)malloc(allocated * sizeof(struct fs_config_entry));
char line[CANNED_LINE_LENGTH]; FILE* fp = fopen(filename, "r");
FILE* f = fopen(filename, "r"); if (fp == NULL) err(1, "failed to open canned file '%s'", filename);
if (f == NULL) die("failed to open canned file '%s'", filename);
while (fgets(line, CANNED_LINE_LENGTH, f) != NULL) { char* line = NULL;
size_t allocated_len;
while (getline(&line, &allocated_len, fp) != -1) {
if (!line[0]) break; if (!line[0]) break;
if (used >= allocated) { if (used >= allocated) {
allocated *= 2; allocated *= 2;
canned_config = (struct fs_config_entry*)realloc( canned_config = (struct fs_config_entry*)realloc(
canned_config, allocated * sizeof(struct fs_config_entry)); canned_config, allocated * sizeof(struct fs_config_entry));
if (canned_config == NULL) die("failed to reallocate memory"); if (canned_config == NULL) errx(1, "failed to reallocate memory");
} }
struct fs_config_entry* cc = canned_config + used; struct fs_config_entry* cc = canned_config + used;
@ -332,17 +291,18 @@ static void read_canned_config(char* filename)
++allocated; ++allocated;
canned_config = (struct fs_config_entry*)realloc( canned_config = (struct fs_config_entry*)realloc(
canned_config, allocated * sizeof(struct fs_config_entry)); canned_config, allocated * sizeof(struct fs_config_entry));
if (canned_config == NULL) die("failed to reallocate memory"); if (canned_config == NULL) errx(1, "failed to reallocate memory");
} }
canned_config[used].name = NULL; canned_config[used].name = NULL;
fclose(f); free(line);
fclose(fp);
} }
static void devnodes_desc_error(const char* filename, unsigned long line_num, static void devnodes_desc_error(const char* filename, unsigned long line_num,
const char* msg) const char* msg)
{ {
errx(EXIT_FAILURE, "failed to read nodes desc file '%s' line %lu: %s", filename, line_num, msg); errx(1, "failed to read nodes desc file '%s' line %lu: %s", filename, line_num, msg);
} }
static int append_devnodes_desc_dir(char* path, char* args) static int append_devnodes_desc_dir(char* path, char* args)
@ -386,15 +346,15 @@ static int append_devnodes_desc_nod(char* path, char* args)
static void append_devnodes_desc(const char* filename) static void append_devnodes_desc(const char* filename)
{ {
FILE* f = fopen(filename, "re"); FILE* fp = fopen(filename, "re");
if (!f) err(EXIT_FAILURE, "failed to open nodes description file '%s'", filename); if (!fp) err(1, "failed to open nodes description file '%s'", filename);
char *line, *args, *type, *path;
unsigned long line_num = 0; unsigned long line_num = 0;
size_t allocated_len;
while (getline(&line, &allocated_len, f) != -1) { char* line = NULL;
char* type; size_t allocated_len;
while (getline(&line, &allocated_len, fp) != -1) {
char *type, *path, *args;
line_num++; line_num++;
@ -428,7 +388,7 @@ static void append_devnodes_desc(const char* filename)
} }
free(line); free(line);
fclose(f); fclose(fp);
} }
static const struct option long_options[] = { static const struct option long_options[] = {
@ -448,7 +408,8 @@ static void usage(void)
"\t-f, --file=FILE: Canned configuration file\n" "\t-f, --file=FILE: Canned configuration file\n"
"\t-h, --help: Print this help\n" "\t-h, --help: Print this help\n"
"\t-n, --nodes=FILE: Dev nodes description file\n" "\t-n, --nodes=FILE: Dev nodes description file\n"
"\nDev nodes description:\n" "\n"
"Dev nodes description:\n"
"\t[dir|nod] [perms] [uid] [gid] [c|b] [minor] [major]\n" "\t[dir|nod] [perms] [uid] [gid] [c|b] [minor] [major]\n"
"\tExample:\n" "\tExample:\n"
"\t\t# My device nodes\n" "\t\t# My device nodes\n"
@ -477,7 +438,7 @@ int main(int argc, char *argv[])
break; break;
default: default:
usage(); usage();
die("Unknown option %s", argv[optind - 1]); errx(1, "Unknown option %s", argv[optind - 1]);
} }
} }
@ -486,7 +447,7 @@ int main(int argc, char *argv[])
if (num_dirs <= 0) { if (num_dirs <= 0) {
usage(); usage();
die("no directories to process?!"); errx(1, "no directories to process?!");
} }
while(num_dirs-- > 0){ while(num_dirs-- > 0){

View File

@ -660,7 +660,7 @@ on late-fs
chmod 0755 /sys/kernel/tracing chmod 0755 /sys/kernel/tracing
chmod 0755 /sys/kernel/debug/tracing chmod 0755 /sys/kernel/debug/tracing
# HALs required before storage encryption can get unlocked (FBE/FDE) # HALs required before storage encryption can get unlocked (FBE)
class_start early_hal class_start early_hal
# Load trusted keys from dm-verity protected partitions # Load trusted keys from dm-verity protected partitions
@ -763,9 +763,8 @@ on post-fs-data
# /data/apex is now available. Start apexd to scan and activate APEXes. # /data/apex is now available. Start apexd to scan and activate APEXes.
# #
# To handle userspace reboots as well as devices that use FDE, make sure # To handle userspace reboots, make sure that apexd is started cleanly here
# that apexd is started cleanly here (set apexd.status="") and that it is # (set apexd.status="") and that it is restarted if it's already running.
# restarted if it's already running.
# #
# /data/apex uses encryption=None because direct I/O support is needed on # /data/apex uses encryption=None because direct I/O support is needed on
# APEX files, but some devices don't support direct I/O on encrypted files. # APEX files, but some devices don't support direct I/O on encrypted files.
@ -1303,6 +1302,7 @@ service console /system/bin/sh
group shell log readproc group shell log readproc
seclabel u:r:shell:s0 seclabel u:r:shell:s0
setenv HOSTNAME console setenv HOSTNAME console
shutdown critical
on property:ro.debuggable=1 on property:ro.debuggable=1
# Give writes to anyone for the trace folder on debug builds. # Give writes to anyone for the trace folder on debug builds.