Merge changes from topic "appcompat_override" into main
* changes: Move __system_properties_reload to LIBC from LIBC_PLATFORM Write appcompat_override system properties
This commit is contained in:
commit
0207a6a186
16 changed files with 392 additions and 55 deletions
|
@ -29,6 +29,7 @@
|
|||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include <sys/_system_properties.h>
|
||||
|
||||
#include <async_safe/CHECK.h>
|
||||
#include <system_properties/prop_area.h>
|
||||
#include <system_properties/system_properties.h>
|
||||
|
||||
|
@ -45,7 +46,7 @@ prop_area* __system_property_area__ = nullptr;
|
|||
|
||||
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
||||
int __system_properties_init() {
|
||||
return system_properties.Init(PROP_FILENAME) ? 0 : -1;
|
||||
return system_properties.Init(PROP_DIRNAME) ? 0 : -1;
|
||||
}
|
||||
|
||||
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
||||
|
@ -55,8 +56,8 @@ int __system_property_set_filename(const char*) {
|
|||
|
||||
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
||||
int __system_property_area_init() {
|
||||
bool fsetxattr_failed = false;
|
||||
return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
|
||||
bool fsetxattr_fail = false;
|
||||
return system_properties.AreaInit(PROP_DIRNAME, &fsetxattr_fail) && !fsetxattr_fail ? 0 : -1;
|
||||
}
|
||||
|
||||
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
||||
|
@ -129,3 +130,9 @@ __BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
|||
int __system_property_foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
|
||||
return system_properties.Foreach(propfn, cookie);
|
||||
}
|
||||
|
||||
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
||||
int __system_properties_zygote_reload(void) {
|
||||
CHECK(getpid() == gettid());
|
||||
return system_properties.Reload(false) ? 0 : -1;
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
__BEGIN_DECLS
|
||||
|
||||
#define PROP_SERVICE_NAME "property_service"
|
||||
#define PROP_FILENAME "/dev/__properties__"
|
||||
#define PROP_DIRNAME "/dev/__properties__"
|
||||
|
||||
#define PROP_MSG_SETPROP 1
|
||||
#define PROP_MSG_SETPROP2 0x00020001
|
||||
|
@ -129,6 +129,18 @@ uint32_t __system_property_serial(const prop_info* _Nonnull __pi);
|
|||
*/
|
||||
int __system_properties_init(void);
|
||||
|
||||
/*
|
||||
* Reloads the system properties from disk.
|
||||
* Not intended for use by any apps except the Zygote. Should only be called from the main thread.
|
||||
*
|
||||
* NOTE: Any pointers received from methods such as __system_property_find should be assumed to be
|
||||
* invalid after this method is called.
|
||||
*
|
||||
* Returns 0 on success, -1 if the system properties failed to re-initialize (same conditions as
|
||||
* __system properties_init)
|
||||
*/
|
||||
int __system_properties_zygote_reload(void); __INTRODUCED_IN(__ANDROID_API_V__)
|
||||
|
||||
/* Deprecated: use __system_property_wait instead. */
|
||||
uint32_t __system_property_wait_any(uint32_t __old_serial);
|
||||
|
||||
|
|
|
@ -1600,6 +1600,7 @@ LIBC_V { # introduced=VanillaIceCream
|
|||
tzalloc;
|
||||
tzfree;
|
||||
wcsrtombs_l;
|
||||
__system_properties_zygote_reload; # apex
|
||||
} LIBC_U;
|
||||
|
||||
LIBC_PRIVATE {
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include <new>
|
||||
|
||||
#include <async_safe/log.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "system_properties/system_properties.h"
|
||||
|
||||
|
@ -59,25 +60,28 @@ bool ContextsSerialized::InitializeContextNodes() {
|
|||
context_nodes_mmap_size_ = context_nodes_mmap_size;
|
||||
|
||||
for (size_t i = 0; i < num_context_nodes; ++i) {
|
||||
new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
|
||||
new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), dirname_);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContextsSerialized::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed) {
|
||||
PropertiesFilename filename(filename_, "properties_serial");
|
||||
if (access_rw) {
|
||||
serial_prop_area_ = prop_area::map_prop_area_rw(
|
||||
filename.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
|
||||
serial_filename_.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
|
||||
} else {
|
||||
serial_prop_area_ = prop_area::map_prop_area(filename.c_str());
|
||||
serial_prop_area_ = prop_area::map_prop_area(serial_filename_.c_str());
|
||||
}
|
||||
return serial_prop_area_;
|
||||
}
|
||||
|
||||
bool ContextsSerialized::InitializeProperties() {
|
||||
if (!property_info_area_file_.LoadDefaultPath()) {
|
||||
// Note: load_default_path is only used for testing, as it will cause properties to be loaded from
|
||||
// one file (specified by PropertyInfoAreaFile.LoadDefaultPath), but be written to "filename".
|
||||
bool ContextsSerialized::InitializeProperties(bool load_default_path) {
|
||||
if (load_default_path && !property_info_area_file_.LoadDefaultPath()) {
|
||||
return false;
|
||||
} else if (!load_default_path && !property_info_area_file_.LoadPath(tree_filename_.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -89,14 +93,20 @@ bool ContextsSerialized::InitializeProperties() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
|
||||
filename_ = filename;
|
||||
if (!InitializeProperties()) {
|
||||
// Note: load_default_path is only used for testing, as it will cause properties to be loaded from
|
||||
// one file (specified by PropertyInfoAreaFile.LoadDefaultPath), but be written to "filename".
|
||||
bool ContextsSerialized::Initialize(bool writable, const char* dirname, bool* fsetxattr_failed,
|
||||
bool load_default_path) {
|
||||
dirname_ = dirname;
|
||||
tree_filename_ = PropertiesFilename(dirname, "property_info");
|
||||
serial_filename_ = PropertiesFilename(dirname, "properties_serial");
|
||||
|
||||
if (!InitializeProperties(load_default_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writable) {
|
||||
mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
|
||||
mkdir(dirname_, S_IRWXU | S_IXGRP | S_IXOTH);
|
||||
bool open_failed = false;
|
||||
if (fsetxattr_failed) {
|
||||
*fsetxattr_failed = false;
|
||||
|
|
|
@ -281,7 +281,7 @@ bool ContextsSplit::InitializeProperties() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ContextsSplit::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
|
||||
bool ContextsSplit::Initialize(bool writable, const char* filename, bool* fsetxattr_failed, bool) {
|
||||
filename_ = filename;
|
||||
if (!InitializeProperties()) {
|
||||
return false;
|
||||
|
|
|
@ -36,7 +36,8 @@ class Contexts {
|
|||
virtual ~Contexts() {
|
||||
}
|
||||
|
||||
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed) = 0;
|
||||
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed,
|
||||
bool load_default_path = false) = 0;
|
||||
virtual prop_area* GetPropAreaForName(const char* name) = 0;
|
||||
virtual prop_area* GetSerialPropArea() = 0;
|
||||
virtual void ForEach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) = 0;
|
||||
|
|
|
@ -38,7 +38,7 @@ class ContextsPreSplit : public Contexts {
|
|||
}
|
||||
|
||||
// We'll never initialize this legacy option as writable, so don't even check the arg.
|
||||
virtual bool Initialize(bool, const char* filename, bool*) override {
|
||||
virtual bool Initialize(bool, const char* filename, bool*, bool) override {
|
||||
pre_split_prop_area_ = prop_area::map_prop_area(filename);
|
||||
return pre_split_prop_area_ != nullptr;
|
||||
}
|
||||
|
|
|
@ -32,13 +32,15 @@
|
|||
|
||||
#include "context_node.h"
|
||||
#include "contexts.h"
|
||||
#include "properties_filename.h"
|
||||
|
||||
class ContextsSerialized : public Contexts {
|
||||
public:
|
||||
virtual ~ContextsSerialized() override {
|
||||
}
|
||||
|
||||
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed) override;
|
||||
virtual bool Initialize(bool writable, const char* dirname, bool* fsetxattr_failed,
|
||||
bool load_default_path) override;
|
||||
virtual prop_area* GetPropAreaForName(const char* name) override;
|
||||
virtual prop_area* GetSerialPropArea() override {
|
||||
return serial_prop_area_;
|
||||
|
@ -49,10 +51,12 @@ class ContextsSerialized : public Contexts {
|
|||
|
||||
private:
|
||||
bool InitializeContextNodes();
|
||||
bool InitializeProperties();
|
||||
bool InitializeProperties(bool load_default_path);
|
||||
bool MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed);
|
||||
|
||||
const char* filename_;
|
||||
const char* dirname_;
|
||||
PropertiesFilename tree_filename_;
|
||||
PropertiesFilename serial_filename_;
|
||||
android::properties::PropertyInfoAreaFile property_info_area_file_;
|
||||
ContextNode* context_nodes_ = nullptr;
|
||||
size_t num_context_nodes_ = 0;
|
||||
|
|
|
@ -38,7 +38,8 @@ class ContextsSplit : public Contexts {
|
|||
virtual ~ContextsSplit() override {
|
||||
}
|
||||
|
||||
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed) override;
|
||||
virtual bool Initialize(bool writable, const char* filename, bool* fsetxattr_failed,
|
||||
bool) override;
|
||||
virtual prop_area* GetPropAreaForName(const char* name) override;
|
||||
virtual prop_area* GetSerialPropArea() override {
|
||||
return serial_prop_area_;
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
||||
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
||||
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
||||
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
class PropertiesFilename {
|
||||
public:
|
||||
PropertiesFilename() = default;
|
||||
PropertiesFilename(const char* dir, const char* file) {
|
||||
if (snprintf(filename_, sizeof(filename_), "%s/%s", dir, file) >=
|
||||
static_cast<int>(sizeof(filename_))) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
void operator=(const char* value) {
|
||||
if (strlen(value) >= sizeof(filename_)) abort();
|
||||
strcpy(filename_, value);
|
||||
}
|
||||
const char* c_str() { return filename_; }
|
||||
|
||||
private:
|
||||
// Typically something like "/dev/__properties__/properties_serial".
|
||||
char filename_[128];
|
||||
};
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/system_properties.h>
|
||||
|
||||
|
@ -37,26 +36,6 @@
|
|||
#include "contexts_serialized.h"
|
||||
#include "contexts_split.h"
|
||||
|
||||
class PropertiesFilename {
|
||||
public:
|
||||
PropertiesFilename() = default;
|
||||
PropertiesFilename(const char* dir, const char* file) {
|
||||
if (snprintf(filename_, sizeof(filename_), "%s/%s", dir, file) >=
|
||||
static_cast<int>(sizeof(filename_))) {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
void operator=(const char* value) {
|
||||
if (strlen(value) >= sizeof(filename_)) abort();
|
||||
strcpy(filename_, value);
|
||||
}
|
||||
const char* c_str() { return filename_; }
|
||||
|
||||
private:
|
||||
// Typically something like "/dev/__properties__/properties_serial".
|
||||
char filename_[128];
|
||||
};
|
||||
|
||||
class SystemProperties {
|
||||
public:
|
||||
friend struct LocalPropertyTestState;
|
||||
|
@ -73,7 +52,9 @@ class SystemProperties {
|
|||
BIONIC_DISALLOW_COPY_AND_ASSIGN(SystemProperties);
|
||||
|
||||
bool Init(const char* filename);
|
||||
bool Reload(bool load_default_path);
|
||||
bool AreaInit(const char* filename, bool* fsetxattr_failed);
|
||||
bool AreaInit(const char* filename, bool* fsetxattr_failed, bool load_default_path);
|
||||
uint32_t AreaSerial();
|
||||
const prop_info* Find(const char* name);
|
||||
int Read(const prop_info* pi, char* name, char* value);
|
||||
|
@ -101,8 +82,14 @@ class SystemProperties {
|
|||
static constexpr size_t kMaxContextsSize =
|
||||
MAX(sizeof(ContextsSerialized), MAX(sizeof(ContextsSplit), sizeof(ContextsPreSplit)));
|
||||
alignas(kMaxContextsAlign) char contexts_data_[kMaxContextsSize];
|
||||
alignas(kMaxContextsAlign) char appcompat_override_contexts_data_[kMaxContextsSize];
|
||||
Contexts* contexts_;
|
||||
// See http://b/291816546#comment#3 for more explanation of appcompat_override
|
||||
Contexts* appcompat_override_contexts_;
|
||||
|
||||
bool InitContexts(bool load_default_path);
|
||||
|
||||
bool initialized_;
|
||||
PropertiesFilename properties_filename_;
|
||||
PropertiesFilename appcompat_filename_;
|
||||
};
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "system_properties/system_properties.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -38,6 +39,7 @@
|
|||
|
||||
#include <new>
|
||||
|
||||
#include <async_safe/CHECK.h>
|
||||
#include <async_safe/log.h>
|
||||
|
||||
#include "private/ErrnoRestorer.h"
|
||||
|
@ -49,6 +51,7 @@
|
|||
|
||||
#define SERIAL_DIRTY(serial) ((serial)&1)
|
||||
#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
|
||||
#define APPCOMPAT_PREFIX "ro.appcompat_override."
|
||||
|
||||
static bool is_dir(const char* pathname) {
|
||||
struct stat info;
|
||||
|
@ -69,10 +72,21 @@ bool SystemProperties::Init(const char* filename) {
|
|||
|
||||
properties_filename_ = filename;
|
||||
|
||||
if (!InitContexts(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemProperties::InitContexts(bool load_default_path) {
|
||||
if (is_dir(properties_filename_.c_str())) {
|
||||
if (access("/dev/__properties__/property_info", R_OK) == 0) {
|
||||
contexts_ = new (contexts_data_) ContextsSerialized();
|
||||
if (!contexts_->Initialize(false, properties_filename_.c_str(), nullptr)) {
|
||||
if (access(PROP_TREE_FILE, R_OK) == 0) {
|
||||
auto serial_contexts = new (contexts_data_) ContextsSerialized();
|
||||
contexts_ = serial_contexts;
|
||||
if (!serial_contexts->Initialize(false, properties_filename_.c_str(), nullptr,
|
||||
load_default_path)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -87,20 +101,46 @@ bool SystemProperties::Init(const char* filename) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
|
||||
return AreaInit(filename, fsetxattr_failed, false);
|
||||
}
|
||||
|
||||
// Note: load_default_path is only used for testing, as it will cause properties to be loaded from
|
||||
// one file (specified by PropertyInfoAreaFile.LoadDefaultPath), but be written to "filename".
|
||||
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed,
|
||||
bool load_default_path) {
|
||||
properties_filename_ = filename;
|
||||
contexts_ = new (contexts_data_) ContextsSerialized();
|
||||
if (!contexts_->Initialize(true, properties_filename_.c_str(), fsetxattr_failed)) {
|
||||
auto serial_contexts = new (contexts_data_) ContextsSerialized();
|
||||
contexts_ = serial_contexts;
|
||||
if (!serial_contexts->Initialize(true, properties_filename_.c_str(), fsetxattr_failed,
|
||||
load_default_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* appcompat_contexts = new (appcompat_override_contexts_data_) ContextsSerialized();
|
||||
appcompat_filename_ = PropertiesFilename(properties_filename_.c_str(), "appcompat_override");
|
||||
if (!appcompat_contexts->Initialize(true, appcompat_filename_.c_str(), fsetxattr_failed,
|
||||
load_default_path)) {
|
||||
appcompat_override_contexts_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
appcompat_override_contexts_ = appcompat_contexts;
|
||||
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemProperties::Reload(bool load_default_path) {
|
||||
if (!initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return InitContexts(load_default_path);
|
||||
}
|
||||
|
||||
uint32_t SystemProperties::AreaSerial() {
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
|
@ -129,6 +169,10 @@ const prop_info* SystemProperties::Find(const char* name) {
|
|||
return pa->find(name);
|
||||
}
|
||||
|
||||
static bool is_appcompat_override(const char* name) {
|
||||
return strncmp(name, APPCOMPAT_PREFIX, strlen(APPCOMPAT_PREFIX)) == 0;
|
||||
}
|
||||
|
||||
static bool is_read_only(const char* name) {
|
||||
return strncmp(name, "ro.", 3) == 0;
|
||||
}
|
||||
|
@ -227,16 +271,24 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
|
|||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
bool have_override = appcompat_override_contexts_ != nullptr;
|
||||
|
||||
prop_area* serial_pa = contexts_->GetSerialPropArea();
|
||||
prop_area* override_serial_pa =
|
||||
have_override ? appcompat_override_contexts_->GetSerialPropArea() : nullptr;
|
||||
if (!serial_pa) {
|
||||
return -1;
|
||||
}
|
||||
prop_area* pa = contexts_->GetPropAreaForName(pi->name);
|
||||
prop_area* override_pa =
|
||||
have_override ? appcompat_override_contexts_->GetPropAreaForName(pi->name) : nullptr;
|
||||
if (__predict_false(!pa)) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Could not find area for \"%s\"", pi->name);
|
||||
return -1;
|
||||
}
|
||||
CHECK(!have_override || (override_pa && override_serial_pa));
|
||||
|
||||
auto* override_pi = const_cast<prop_info*>(have_override ? override_pa->find(pi->name) : nullptr);
|
||||
|
||||
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
|
||||
unsigned int old_len = SERIAL_VALUE_LEN(serial);
|
||||
|
@ -246,18 +298,34 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
|
|||
// that we publish our dirty area update before allowing readers to see a
|
||||
// dirty serial.
|
||||
memcpy(pa->dirty_backup_area(), pi->value, old_len + 1);
|
||||
if (have_override) {
|
||||
memcpy(override_pa->dirty_backup_area(), override_pi->value, old_len + 1);
|
||||
}
|
||||
atomic_thread_fence(memory_order_release);
|
||||
serial |= 1;
|
||||
atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
|
||||
strlcpy(pi->value, value, len + 1);
|
||||
if (have_override) {
|
||||
atomic_store_explicit(&override_pi->serial, serial, memory_order_relaxed);
|
||||
strlcpy(override_pi->value, value, len + 1);
|
||||
}
|
||||
// Now the primary value property area is up-to-date. Let readers know that they should
|
||||
// look at the property value instead of the backup area.
|
||||
atomic_thread_fence(memory_order_release);
|
||||
atomic_store_explicit(&pi->serial, (len << 24) | ((serial + 1) & 0xffffff), memory_order_relaxed);
|
||||
int new_serial = (len << 24) | ((serial + 1) & 0xffffff);
|
||||
atomic_store_explicit(&pi->serial, new_serial, memory_order_relaxed);
|
||||
if (have_override) {
|
||||
atomic_store_explicit(&override_pi->serial, new_serial, memory_order_relaxed);
|
||||
}
|
||||
__futex_wake(&pi->serial, INT32_MAX); // Fence by side effect
|
||||
atomic_store_explicit(serial_pa->serial(),
|
||||
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
|
||||
memory_order_release);
|
||||
if (have_override) {
|
||||
atomic_store_explicit(override_serial_pa->serial(),
|
||||
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
|
||||
memory_order_release);
|
||||
}
|
||||
__futex_wake(serial_pa->serial(), INT32_MAX);
|
||||
|
||||
return 0;
|
||||
|
@ -293,6 +361,34 @@ int SystemProperties::Add(const char* name, unsigned int namelen, const char* va
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (appcompat_override_contexts_ != nullptr) {
|
||||
bool is_override = is_appcompat_override(name);
|
||||
const char* override_name = name;
|
||||
if (is_override) override_name += strlen(APPCOMPAT_PREFIX);
|
||||
prop_area* other_pa = appcompat_override_contexts_->GetPropAreaForName(override_name);
|
||||
prop_area* other_serial_pa = appcompat_override_contexts_->GetSerialPropArea();
|
||||
CHECK(other_pa && other_serial_pa);
|
||||
// We may write a property twice to overrides, once for the ro.*, and again for the
|
||||
// ro.appcompat_override.ro.* property. If we've already written, then we should essentially
|
||||
// perform an Update, not an Add.
|
||||
auto other_pi = const_cast<prop_info*>(other_pa->find(override_name));
|
||||
if (!other_pi) {
|
||||
if (other_pa->add(override_name, strlen(override_name), value, valuelen)) {
|
||||
atomic_store_explicit(
|
||||
other_serial_pa->serial(),
|
||||
atomic_load_explicit(other_serial_pa->serial(), memory_order_relaxed) + 1,
|
||||
memory_order_release);
|
||||
}
|
||||
} else if (is_override) {
|
||||
// We already wrote the ro.*, but appcompat_override.ro.* should override that. We don't
|
||||
// need to do the usual dirty bit setting, as this only happens during the init process,
|
||||
// before any readers are started. Check that only init or root can write appcompat props.
|
||||
CHECK(getpid() == 1 || getuid() == 0);
|
||||
atomic_thread_fence(memory_order_release);
|
||||
strlcpy(other_pi->value, value, valuelen + 1);
|
||||
}
|
||||
}
|
||||
|
||||
// There is only a single mutator, but we want to make sure that
|
||||
// updates are visible to a reader waiting for the update.
|
||||
atomic_store_explicit(serial_pa->serial(),
|
||||
|
|
|
@ -25,31 +25,53 @@
|
|||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/silent_death_test.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
#if defined(__BIONIC__)
|
||||
|
||||
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
|
||||
#include <stdlib.h>
|
||||
#include <sys/_system_properties.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include <system_properties/system_properties.h>
|
||||
|
||||
class SystemPropertiesTest : public SystemProperties {
|
||||
public:
|
||||
SystemPropertiesTest() : SystemProperties(false) {
|
||||
valid_ = AreaInit(dir_.path, nullptr);
|
||||
appcompat_path = android::base::StringPrintf("%s/appcompat_override", dir_.path);
|
||||
mount_path = android::base::StringPrintf("%s/__properties__", dir_.path);
|
||||
mkdir(appcompat_path.c_str(), S_IRWXU | S_IXGRP | S_IXOTH);
|
||||
valid_ = AreaInit(dir_.path, nullptr, true);
|
||||
}
|
||||
~SystemPropertiesTest() {
|
||||
if (valid_) {
|
||||
contexts_->FreeAndUnmap();
|
||||
}
|
||||
umount2(dir_.path, MNT_DETACH);
|
||||
umount2(real_sysprop_dir.c_str(), MNT_DETACH);
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
return valid_;
|
||||
}
|
||||
|
||||
const char* get_path() const { return dir_.path; }
|
||||
|
||||
const char* get_appcompat_path() const { return appcompat_path.c_str(); }
|
||||
|
||||
const char* get_mount_path() const { return mount_path.c_str(); }
|
||||
|
||||
const char* get_real_sysprop_dir() const { return real_sysprop_dir.c_str(); }
|
||||
|
||||
std::string appcompat_path;
|
||||
std::string mount_path;
|
||||
std::string real_sysprop_dir = "/dev/__properties__";
|
||||
|
||||
private:
|
||||
TemporaryDir dir_;
|
||||
bool valid_;
|
||||
|
@ -125,6 +147,58 @@ TEST(properties, __system_property_add) {
|
|||
#endif // __BIONIC__
|
||||
}
|
||||
|
||||
TEST(properties, __system_property_add_appcompat) {
|
||||
#if defined(__BIONIC__)
|
||||
if (getuid() != 0) GTEST_SKIP() << "test requires root";
|
||||
SystemPropertiesTest system_properties;
|
||||
ASSERT_TRUE(system_properties.valid());
|
||||
|
||||
char name[] = "ro.property";
|
||||
char override_name[] = "ro.appcompat_override.ro.property";
|
||||
char name_not_written[] = "ro.property_other";
|
||||
char override_with_no_real[] = "ro.appcompat_override.ro.property_other";
|
||||
ASSERT_EQ(0, system_properties.Add(name, strlen(name), "value1", 6));
|
||||
ASSERT_EQ(0, system_properties.Add(override_name, strlen(override_name), "value2", 6));
|
||||
ASSERT_EQ(0, system_properties.Add(override_with_no_real, strlen(override_with_no_real),
|
||||
"value3", 6));
|
||||
|
||||
char propvalue[PROP_VALUE_MAX];
|
||||
ASSERT_EQ(6, system_properties.Get(name, propvalue));
|
||||
ASSERT_STREQ(propvalue, "value1");
|
||||
|
||||
ASSERT_EQ(6, system_properties.Get(override_name, propvalue));
|
||||
ASSERT_STREQ(propvalue, "value2");
|
||||
|
||||
ASSERT_EQ(0, system_properties.Get(name_not_written, propvalue));
|
||||
ASSERT_STREQ(propvalue, "");
|
||||
|
||||
ASSERT_EQ(6, system_properties.Get(override_with_no_real, propvalue));
|
||||
ASSERT_STREQ(propvalue, "value3");
|
||||
|
||||
int ret = mount(system_properties.get_appcompat_path(), system_properties.get_path(), nullptr,
|
||||
MS_BIND | MS_REC, nullptr);
|
||||
if (ret != 0) {
|
||||
ASSERT_ERRNO(0);
|
||||
}
|
||||
system_properties.Reload(true);
|
||||
|
||||
ASSERT_EQ(6, system_properties.Get(name, propvalue));
|
||||
ASSERT_STREQ(propvalue, "value2");
|
||||
|
||||
ASSERT_EQ(0, system_properties.Get(override_name, propvalue));
|
||||
ASSERT_STREQ(propvalue, "");
|
||||
|
||||
ASSERT_EQ(6, system_properties.Get(name_not_written, propvalue));
|
||||
ASSERT_STREQ(propvalue, "value3");
|
||||
|
||||
ASSERT_EQ(0, system_properties.Get(override_with_no_real, propvalue));
|
||||
ASSERT_STREQ(propvalue, "");
|
||||
|
||||
#else // __BIONIC__
|
||||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
||||
TEST(properties, __system_property_update) {
|
||||
#if defined(__BIONIC__)
|
||||
SystemPropertiesTest system_properties;
|
||||
|
@ -432,7 +506,7 @@ TEST_F(properties_DeathTest, read_only) {
|
|||
|
||||
// This test only makes sense if we're talking to the real system property service.
|
||||
struct stat sb;
|
||||
ASSERT_FALSE(stat(PROP_FILENAME, &sb) == -1 && errno == ENOENT);
|
||||
ASSERT_FALSE(stat(PROP_DIRNAME, &sb) == -1 && errno == ENOENT);
|
||||
|
||||
ASSERT_EXIT(__system_property_add("property", 8, "value", 5), KilledByFault(), "");
|
||||
#else // __BIONIC__
|
||||
|
@ -526,3 +600,93 @@ TEST(properties, __system_property_extra_long_read_only_too_long) {
|
|||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
||||
// Note that this test affects global state of the system
|
||||
// this tests tries to mitigate this by using utime+pid
|
||||
// prefix for the property name. It is still results in
|
||||
// pollution of property service since properties cannot
|
||||
// be removed.
|
||||
//
|
||||
// Note that there is also possibility to run into "out-of-memory"
|
||||
// if this test if it is executed often enough without reboot.
|
||||
TEST(properties, __system_property_reload_no_op) {
|
||||
#if defined(__BIONIC__)
|
||||
std::string property_name =
|
||||
android::base::StringPrintf("debug.test.%d.%" PRId64 ".property", getpid(), NanoTime());
|
||||
ASSERT_EQ(0, __system_property_find(property_name.c_str()));
|
||||
ASSERT_EQ(0, __system_property_set(property_name.c_str(), "test value"));
|
||||
ASSERT_EQ(0, __system_properties_zygote_reload());
|
||||
const prop_info* readptr = __system_property_find(property_name.c_str());
|
||||
std::string expected_name = property_name;
|
||||
__system_property_read_callback(
|
||||
readptr,
|
||||
[](void*, const char*, const char* value, unsigned) { ASSERT_STREQ("test value", value); },
|
||||
&expected_name);
|
||||
#else // __BIONIC__
|
||||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
||||
TEST(properties, __system_property_reload_invalid) {
|
||||
#if defined(__BIONIC__)
|
||||
if (getuid() != 0) GTEST_SKIP() << "test requires root";
|
||||
SystemPropertiesTest system_properties;
|
||||
|
||||
// Create an invalid property_info file, so the system will attempt to initialize a
|
||||
// ContextSerialized
|
||||
std::string property_info_file =
|
||||
android::base::StringPrintf("%s/property_info", system_properties.get_path());
|
||||
fclose(fopen(property_info_file.c_str(), "w"));
|
||||
int ret = mount(system_properties.get_path(), system_properties.get_real_sysprop_dir(), nullptr,
|
||||
MS_BIND | MS_REC, nullptr);
|
||||
if (ret != 0) {
|
||||
ASSERT_ERRNO(0);
|
||||
}
|
||||
|
||||
ASSERT_EQ(-1, __system_properties_zygote_reload());
|
||||
#else // __BIONIC__
|
||||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
||||
// Note that this test affects global state of the system
|
||||
// this tests tries to mitigate this by using utime+pid
|
||||
// prefix for the property name. It is still results in
|
||||
// pollution of property service since properties cannot
|
||||
// be removed.
|
||||
//
|
||||
// Note that there is also possibility to run into "out-of-memory"
|
||||
// if this test if it is executed often enough without reboot.
|
||||
TEST(properties, __system_property_reload_valid) {
|
||||
#if defined(__BIONIC__)
|
||||
if (getuid() != 0) GTEST_SKIP() << "test requires root";
|
||||
SystemPropertiesTest system_properties;
|
||||
|
||||
// Copy the system properties files into the temp directory
|
||||
std::string shell_cmd = android::base::StringPrintf(
|
||||
"cp -r %s %s", system_properties.get_real_sysprop_dir(), system_properties.get_path());
|
||||
system(shell_cmd.c_str());
|
||||
|
||||
// Write a system property to the current set of system properties
|
||||
std::string property_name =
|
||||
android::base::StringPrintf("debug.test.%d.%" PRId64 ".property", getpid(), NanoTime());
|
||||
ASSERT_EQ(0, __system_property_find(property_name.c_str()));
|
||||
ASSERT_EQ(0, __system_property_set(property_name.c_str(), "test value"));
|
||||
|
||||
// Mount the temp directory (which doesn't have the property we just wrote) in place of the
|
||||
// real one
|
||||
int ret = mount(system_properties.get_mount_path(), system_properties.get_real_sysprop_dir(),
|
||||
nullptr, MS_BIND | MS_REC, nullptr);
|
||||
if (ret != 0) {
|
||||
ASSERT_ERRNO(0);
|
||||
}
|
||||
|
||||
// reload system properties in the new dir, and verify the property we wrote after we copied the
|
||||
// files isn't there
|
||||
ASSERT_EQ(0, __system_properties_zygote_reload());
|
||||
ASSERT_EQ(0, __system_property_find(property_name.c_str()));
|
||||
|
||||
#else // __BIONIC__
|
||||
GTEST_SKIP() << "bionic-only test";
|
||||
#endif // __BIONIC__
|
||||
}
|
||||
|
|
|
@ -28,10 +28,6 @@
|
|||
|
||||
#if defined(__BIONIC__)
|
||||
#include <sys/system_properties.h>
|
||||
int64_t NanoTime() {
|
||||
auto t = std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now());
|
||||
return t.time_since_epoch().count();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Note that this test affects global state of the system
|
||||
|
|
|
@ -89,6 +89,11 @@ void PrintTo(const Errno& e, std::ostream* os) {
|
|||
}
|
||||
}
|
||||
|
||||
int64_t NanoTime() {
|
||||
auto t = std::chrono::time_point_cast<std::chrono::nanoseconds>(std::chrono::steady_clock::now());
|
||||
return t.time_since_epoch().count();
|
||||
}
|
||||
|
||||
bool operator==(const Errno& lhs, const Errno& rhs) {
|
||||
return lhs.errno_ == rhs.errno_;
|
||||
}
|
||||
|
|
|
@ -316,6 +316,8 @@ static inline bool running_with_mte() {
|
|||
|
||||
bool IsLowRamDevice();
|
||||
|
||||
int64_t NanoTime();
|
||||
|
||||
class Errno {
|
||||
public:
|
||||
Errno(int e) : errno_(e) {}
|
||||
|
|
Loading…
Reference in a new issue