mirror of
https://github.com/topjohnwu/system_properties.git
synced 2024-11-26 21:40:31 +00:00
Sync to upstream AOSP (Android 15)
This commit is contained in:
parent
38c90f4baa
commit
906dfd9139
@ -49,17 +49,11 @@ bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
char filename[PROP_FILENAME_MAX];
|
||||
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
|
||||
if (len < 0 || len >= PROP_FILENAME_MAX) {
|
||||
lock_.unlock();
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertiesFilename filename(filename_, context_);
|
||||
if (access_rw) {
|
||||
pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed);
|
||||
pa_ = prop_area::map_prop_area_rw(filename.c_str(), context_, fsetxattr_failed);
|
||||
} else {
|
||||
pa_ = prop_area::map_prop_area(filename);
|
||||
pa_ = prop_area::map_prop_area(filename.c_str());
|
||||
}
|
||||
lock_.unlock();
|
||||
return pa_;
|
||||
@ -84,13 +78,8 @@ void ContextNode::ResetAccess() {
|
||||
}
|
||||
|
||||
bool ContextNode::CheckAccess() {
|
||||
char filename[PROP_FILENAME_MAX];
|
||||
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
|
||||
if (len < 0 || len >= PROP_FILENAME_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return access(filename, R_OK) == 0;
|
||||
PropertiesFilename filename(filename_, context_);
|
||||
return access(filename.c_str(), R_OK) == 0;
|
||||
}
|
||||
|
||||
void ContextNode::Unmap() {
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <new>
|
||||
|
||||
#include <async_safe/log.h>
|
||||
#include <private/android_filesystem_config.h>
|
||||
|
||||
#include "system_properties/system_properties.h"
|
||||
|
||||
@ -59,31 +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) {
|
||||
char filename[PROP_FILENAME_MAX];
|
||||
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", filename_);
|
||||
if (len < 0 || len >= PROP_FILENAME_MAX) {
|
||||
serial_prop_area_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (access_rw) {
|
||||
serial_prop_area_ =
|
||||
prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
|
||||
serial_prop_area_ = prop_area::map_prop_area_rw(
|
||||
serial_filename_.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
|
||||
} else {
|
||||
serial_prop_area_ = prop_area::map_prop_area(filename);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -95,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;
|
||||
|
@ -192,18 +192,12 @@ static int read_spec_entries(char* line_buf, int num_args, ...) {
|
||||
}
|
||||
|
||||
bool ContextsSplit::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed) {
|
||||
char filename[PROP_FILENAME_MAX];
|
||||
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", filename_);
|
||||
if (len < 0 || len >= PROP_FILENAME_MAX) {
|
||||
serial_prop_area_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
PropertiesFilename filename(filename_, "properties_serial");
|
||||
if (access_rw) {
|
||||
serial_prop_area_ =
|
||||
prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
|
||||
serial_prop_area_ = prop_area::map_prop_area_rw(
|
||||
filename.c_str(), "u:object_r:properties_serial:s0", fsetxattr_failed);
|
||||
} else {
|
||||
serial_prop_area_ = prop_area::map_prop_area(filename);
|
||||
serial_prop_area_ = prop_area::map_prop_area(filename.c_str());
|
||||
}
|
||||
return serial_prop_area_;
|
||||
}
|
||||
@ -287,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;
|
||||
|
@ -41,7 +41,8 @@
|
||||
__BEGIN_DECLS
|
||||
|
||||
#define PROP_SERVICE_NAME "property_service"
|
||||
#define PROP_FILENAME "/dev/__properties__"
|
||||
#define PROP_SERVICE_FOR_SYSTEM_NAME "property_service_for_system"
|
||||
#define PROP_DIRNAME "/dev/__properties__"
|
||||
|
||||
#define PROP_MSG_SETPROP 1
|
||||
#define PROP_MSG_SETPROP2 0x00020001
|
||||
@ -61,7 +62,7 @@ __BEGIN_DECLS
|
||||
** This was previously for testing, but now that SystemProperties is its own testable class,
|
||||
** there is never a reason to call this function and its implementation simply returns -1.
|
||||
*/
|
||||
int __system_property_set_filename(const char* __filename);
|
||||
int __system_property_set_filename(const char* __unused __filename);
|
||||
|
||||
/*
|
||||
** Initialize the area to be used to store properties. Can
|
||||
@ -102,7 +103,7 @@ uint32_t __system_property_area_serial(void);
|
||||
**
|
||||
** Returns 0 on success, -1 if the property area is full.
|
||||
*/
|
||||
int __system_property_add(const char* __name, unsigned int __name_length, const char* __value, unsigned int __value_length);
|
||||
int __system_property_add(const char* _Nonnull __name, unsigned int __name_length, const char* _Nonnull __value, unsigned int __value_length);
|
||||
|
||||
/* Update the value of a system property returned by
|
||||
** __system_property_find. Can only be done by a single process
|
||||
@ -112,14 +113,14 @@ int __system_property_add(const char* __name, unsigned int __name_length, const
|
||||
**
|
||||
** Returns 0 on success, -1 if the parameters are incorrect.
|
||||
*/
|
||||
int __system_property_update(prop_info* __pi, const char* __value, unsigned int __value_length);
|
||||
int __system_property_update(prop_info* _Nonnull __pi, const char* _Nonnull __value, unsigned int __value_length);
|
||||
|
||||
/* Read the serial number of a system property returned by
|
||||
** __system_property_find.
|
||||
**
|
||||
** Returns the serial number on success, -1 on error.
|
||||
*/
|
||||
uint32_t __system_property_serial(const prop_info* __pi);
|
||||
uint32_t __system_property_serial(const prop_info* _Nonnull __pi);
|
||||
|
||||
/* Initialize the system properties area in read only mode.
|
||||
* Should be done by all processes that need to read system
|
||||
@ -129,6 +130,18 @@ uint32_t __system_property_serial(const prop_info* __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);
|
||||
|
||||
|
@ -26,8 +26,12 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
#define _INCLUDE_SYS_SYSTEM_PROPERTIES_H
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @file system_properties.h
|
||||
* @brief System properties.
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
#include <stdbool.h>
|
||||
@ -36,43 +40,56 @@
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
/** An opaque structure representing a system property. */
|
||||
typedef struct prop_info prop_info;
|
||||
|
||||
/**
|
||||
* The limit on the length of a property value.
|
||||
* (See PROP_NAME_MAX for property names.)
|
||||
*/
|
||||
#define PROP_VALUE_MAX 92
|
||||
|
||||
/*
|
||||
/**
|
||||
* Sets system property `name` to `value`, creating the system property if it doesn't already exist.
|
||||
*/
|
||||
int __system_property_set(const char* __name, const char* __value);
|
||||
|
||||
/*
|
||||
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
|
||||
* Use __system_property_read_callback to query the current value.
|
||||
*
|
||||
* Property lookup is expensive, so it can be useful to cache the result of this function.
|
||||
* Returns 0 on success, or -1 on failure.
|
||||
*/
|
||||
const prop_info* __system_property_find(const char* __name);
|
||||
int __system_property_set(const char* _Nonnull __name, const char* _Nonnull __value);
|
||||
|
||||
/*
|
||||
* Calls `callback` with a consistent trio of name, value, and serial number for property `pi`.
|
||||
/**
|
||||
* Returns a `prop_info` corresponding system property `name`, or nullptr if it doesn't exist.
|
||||
* Use __system_property_read_callback() to query the current value.
|
||||
*
|
||||
* Property lookup is expensive, so it can be useful to cache the result of this
|
||||
* function rather than using __system_property_get().
|
||||
*/
|
||||
void __system_property_read_callback(const prop_info* __pi,
|
||||
void (*__callback)(void* __cookie, const char* __name, const char* __value, uint32_t __serial),
|
||||
void* __cookie) __INTRODUCED_IN(26);
|
||||
const prop_info* _Nullable __system_property_find(const char* _Nonnull __name);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Calls `callback` with a consistent trio of name, value, and serial number
|
||||
* for property `pi`.
|
||||
*
|
||||
* Available since API level 26.
|
||||
*/
|
||||
void __system_property_read_callback(const prop_info* _Nonnull __pi,
|
||||
void (* _Nonnull __callback)(void* _Nullable __cookie, const char* _Nonnull __name, const char* _Nonnull __value, uint32_t __serial),
|
||||
void* _Nullable __cookie) __INTRODUCED_IN(26);
|
||||
|
||||
/**
|
||||
* Passes a `prop_info` for each system property to the provided
|
||||
* callback. Use __system_property_read_callback() to read the value.
|
||||
* callback. Use __system_property_read_callback() to read the value of
|
||||
* any of the properties.
|
||||
*
|
||||
* This method is for inspecting and debugging the property system, and not generally useful.
|
||||
*
|
||||
* Returns 0 on success, or -1 on failure.
|
||||
*/
|
||||
int __system_property_foreach(void (*__callback)(const prop_info* __pi, void* __cookie), void* __cookie)
|
||||
__INTRODUCED_IN(19);
|
||||
int __system_property_foreach(void (* _Nonnull __callback)(const prop_info* _Nonnull __pi, void* _Nullable __cookie), void* _Nullable __cookie);
|
||||
|
||||
/*
|
||||
/**
|
||||
* Waits for the specific system property identified by `pi` to be updated
|
||||
* past `old_serial`. Waits no longer than `relative_timeout`, or forever
|
||||
* if `relaive_timeout` is null.
|
||||
* if `relative_timeout` is null.
|
||||
*
|
||||
* If `pi` is null, waits for the global serial number instead.
|
||||
*
|
||||
@ -80,20 +97,24 @@ int __system_property_foreach(void (*__callback)(const prop_info* __pi, void* __
|
||||
*
|
||||
* Returns true and updates `*new_serial_ptr` on success, or false if the call
|
||||
* timed out.
|
||||
*
|
||||
* Available since API level 26.
|
||||
*/
|
||||
struct timespec;
|
||||
bool __system_property_wait(const prop_info* __pi, uint32_t __old_serial, uint32_t* __new_serial_ptr, const struct timespec* __relative_timeout)
|
||||
bool __system_property_wait(const prop_info* _Nullable __pi, uint32_t __old_serial, uint32_t* _Nonnull __new_serial_ptr, const struct timespec* _Nullable __relative_timeout)
|
||||
__INTRODUCED_IN(26);
|
||||
|
||||
/* Deprecated. In Android O and above, there's no limit on property name length. */
|
||||
/**
|
||||
* Deprecated: there's no limit on the length of a property name since
|
||||
* API level 26, though the limit on property values (PROP_VALUE_MAX) remains.
|
||||
*/
|
||||
#define PROP_NAME_MAX 32
|
||||
/* Deprecated. Use __system_property_read_callback instead. */
|
||||
int __system_property_read(const prop_info* __pi, char* __name, char* __value);
|
||||
/* Deprecated. Use __system_property_read_callback instead. */
|
||||
int __system_property_get(const char* __name, char* __value);
|
||||
/* Deprecated. Use __system_property_foreach instead. */
|
||||
const prop_info* __system_property_find_nth(unsigned __n);
|
||||
|
||||
/** Deprecated. Use __system_property_read_callback() instead. */
|
||||
int __system_property_read(const prop_info* _Nonnull __pi, char* _Nullable __name, char* _Nonnull __value);
|
||||
/** Deprecated. Use __system_property_read_callback() instead. */
|
||||
int __system_property_get(const char* _Nonnull __name, char* _Nonnull __value);
|
||||
/** Deprecated. Use __system_property_foreach() instead. */
|
||||
const prop_info* _Nullable __system_property_find_nth(unsigned __n);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
@ -40,12 +40,12 @@ static constexpr uintptr_t align_up(uintptr_t p, size_t align) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* align_down(T* p, size_t align) {
|
||||
static inline T* _Nonnull align_down(T* _Nonnull p, size_t align) {
|
||||
return reinterpret_cast<T*>(align_down(reinterpret_cast<uintptr_t>(p), align));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* align_up(T* p, size_t align) {
|
||||
static inline T* _Nonnull align_up(T* _Nonnull p, size_t align) {
|
||||
return reinterpret_cast<T*>(align_up(reinterpret_cast<uintptr_t>(p), align));
|
||||
}
|
||||
|
||||
@ -94,6 +94,6 @@ static inline uintptr_t untag_address(uintptr_t p) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T* untag_address(T* p) {
|
||||
static inline T* _Nonnull untag_address(T* _Nonnull p) {
|
||||
return reinterpret_cast<T*>(untag_address(reinterpret_cast<uintptr_t>(p)));
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
static constexpr char PROP_TREE_FILE[] = "/dev/__properties__/property_info";
|
||||
|
||||
namespace android {
|
||||
namespace properties {
|
||||
|
||||
|
@ -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_;
|
||||
|
@ -53,14 +53,14 @@
|
||||
// +-----+ +-----+ +-----+ +===========+
|
||||
|
||||
// Represents a node in the trie.
|
||||
struct prop_bt {
|
||||
struct prop_trie_node {
|
||||
uint32_t namelen;
|
||||
|
||||
// The property trie is updated only by the init process (single threaded) which provides
|
||||
// property service. And it can be read by multiple threads at the same time.
|
||||
// As the property trie is not protected by locks, we use atomic_uint_least32_t types for the
|
||||
// left, right, children "pointers" in the trie node. To make sure readers who see the
|
||||
// change of "pointers" can also notice the change of prop_bt structure contents pointed by
|
||||
// change of "pointers" can also notice the change of prop_trie_node structure contents pointed by
|
||||
// the "pointers", we always use release-consume ordering pair when accessing these "pointers".
|
||||
|
||||
// prop "points" to prop_info structure if there is a propery associated with the trie node.
|
||||
@ -79,14 +79,14 @@ struct prop_bt {
|
||||
|
||||
char name[0];
|
||||
|
||||
prop_bt(const char* name, const uint32_t name_length) {
|
||||
prop_trie_node(const char* name, const uint32_t name_length) {
|
||||
this->namelen = name_length;
|
||||
memcpy(this->name, name, name_length);
|
||||
this->name[name_length] = '\0';
|
||||
}
|
||||
|
||||
private:
|
||||
BIONIC_DISALLOW_COPY_AND_ASSIGN(prop_bt);
|
||||
BIONIC_DISALLOW_COPY_AND_ASSIGN(prop_trie_node);
|
||||
};
|
||||
|
||||
class prop_area {
|
||||
@ -105,7 +105,7 @@ class prop_area {
|
||||
atomic_init(&serial_, 0u);
|
||||
memset(reserved_, 0, sizeof(reserved_));
|
||||
// Allocate enough space for the root node.
|
||||
bytes_used_ = sizeof(prop_bt);
|
||||
bytes_used_ = sizeof(prop_trie_node);
|
||||
// To make property reads wait-free, we reserve a
|
||||
// PROP_VALUE_MAX-sized block of memory, the "dirty backup area",
|
||||
// just after the root node. When we're about to modify a
|
||||
@ -136,30 +136,29 @@ class prop_area {
|
||||
uint32_t version() const {
|
||||
return version_;
|
||||
}
|
||||
char* dirty_backup_area() {
|
||||
return data_ + sizeof (prop_bt);
|
||||
}
|
||||
char* dirty_backup_area() { return data_ + sizeof(prop_trie_node); }
|
||||
|
||||
private:
|
||||
static prop_area* map_fd_ro(const int fd);
|
||||
|
||||
void* allocate_obj(const size_t size, uint_least32_t* const off);
|
||||
prop_bt* new_prop_bt(const char* name, uint32_t namelen, uint_least32_t* const off);
|
||||
prop_trie_node* new_prop_trie_node(const char* name, uint32_t namelen, uint_least32_t* const off);
|
||||
prop_info* new_prop_info(const char* name, uint32_t namelen, const char* value, uint32_t valuelen,
|
||||
uint_least32_t* const off);
|
||||
void* to_prop_obj(uint_least32_t off);
|
||||
prop_bt* to_prop_bt(atomic_uint_least32_t* off_p);
|
||||
prop_trie_node* to_prop_trie_node(atomic_uint_least32_t* off_p);
|
||||
prop_info* to_prop_info(atomic_uint_least32_t* off_p);
|
||||
|
||||
prop_bt* root_node();
|
||||
prop_trie_node* root_node();
|
||||
|
||||
prop_bt* find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen, bool alloc_if_needed);
|
||||
prop_trie_node* find_prop_trie_node(prop_trie_node* const trie, const char* name,
|
||||
uint32_t namelen, bool alloc_if_needed);
|
||||
|
||||
const prop_info* find_property(prop_bt* const trie, const char* name, uint32_t namelen,
|
||||
const prop_info* find_property(prop_trie_node* const trie, const char* name, uint32_t namelen,
|
||||
const char* value, uint32_t valuelen, bool alloc_if_needed);
|
||||
|
||||
bool foreach_property(prop_bt* const trie, void (*propfn)(const prop_info* pi, void* cookie),
|
||||
void* cookie);
|
||||
bool foreach_property(prop_trie_node* const trie,
|
||||
void (*propfn)(const prop_info* pi, void* cookie), void* cookie);
|
||||
|
||||
// The original design doesn't include pa_size or pa_data_size in the prop_area struct itself.
|
||||
// Since we'll need to be backwards compatible with that design, we don't gain much by adding it
|
||||
|
@ -45,14 +45,14 @@ struct prop_info {
|
||||
// Read only properties will not set anything but the bottom most bit of serial and the top byte.
|
||||
// We borrow the 2nd from the top byte for extra flags, and use the bottom most bit of that for
|
||||
// our first user, kLongFlag.
|
||||
constexpr static uint32_t kLongFlag = 1 << 16;
|
||||
static constexpr uint32_t kLongFlag = 1 << 16;
|
||||
|
||||
// The error message fits in part of a union with the previous 92 char property value so there
|
||||
// must be room left over after the error message for the offset to the new longer property value
|
||||
// and future expansion fields if needed. Note that this value cannot ever increase. The offset
|
||||
// to the new longer property value appears immediately after it, so an increase of this size will
|
||||
// break compatibility.
|
||||
constexpr static size_t kLongLegacyErrorBufferSize = 56;
|
||||
static constexpr size_t kLongLegacyErrorBufferSize = 56;
|
||||
|
||||
public:
|
||||
atomic_uint_least32_t serial;
|
||||
|
51
include/system_properties/properties_filename.h
Normal file
51
include/system_properties/properties_filename.h
Normal file
@ -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,8 +36,6 @@
|
||||
#include "contexts_serialized.h"
|
||||
#include "contexts_split.h"
|
||||
|
||||
constexpr int PROP_FILENAME_MAX = 1024;
|
||||
|
||||
class SystemProperties {
|
||||
public:
|
||||
friend struct LocalPropertyTestState;
|
||||
@ -55,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);
|
||||
@ -83,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_;
|
||||
char property_filename_[PROP_FILENAME_MAX];
|
||||
PropertiesFilename properties_filename_;
|
||||
PropertiesFilename appcompat_filename_;
|
||||
};
|
||||
|
@ -154,16 +154,15 @@ void* prop_area::allocate_obj(const size_t size, uint_least32_t* const off) {
|
||||
return data_ + *off;
|
||||
}
|
||||
|
||||
prop_bt* prop_area::new_prop_bt(const char* name, uint32_t namelen, uint_least32_t* const off) {
|
||||
prop_trie_node* prop_area::new_prop_trie_node(const char* name, uint32_t namelen,
|
||||
uint_least32_t* const off) {
|
||||
uint_least32_t new_offset;
|
||||
void* const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
|
||||
if (p != nullptr) {
|
||||
prop_bt* bt = new (p) prop_bt(name, namelen);
|
||||
*off = new_offset;
|
||||
return bt;
|
||||
}
|
||||
void* const p = allocate_obj(sizeof(prop_trie_node) + namelen + 1, &new_offset);
|
||||
if (p == nullptr) return nullptr;
|
||||
|
||||
return nullptr;
|
||||
prop_trie_node* node = new (p) prop_trie_node(name, namelen);
|
||||
*off = new_offset;
|
||||
return node;
|
||||
}
|
||||
|
||||
prop_info* prop_area::new_prop_info(const char* name, uint32_t namelen, const char* value,
|
||||
@ -200,9 +199,9 @@ void* prop_area::to_prop_obj(uint_least32_t off) {
|
||||
return (data_ + off);
|
||||
}
|
||||
|
||||
inline prop_bt* prop_area::to_prop_bt(atomic_uint_least32_t* off_p) {
|
||||
inline prop_trie_node* prop_area::to_prop_trie_node(atomic_uint_least32_t* off_p) {
|
||||
uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
|
||||
return reinterpret_cast<prop_bt*>(to_prop_obj(off));
|
||||
return reinterpret_cast<prop_trie_node*>(to_prop_obj(off));
|
||||
}
|
||||
|
||||
inline prop_info* prop_area::to_prop_info(atomic_uint_least32_t* off_p) {
|
||||
@ -210,8 +209,8 @@ inline prop_info* prop_area::to_prop_info(atomic_uint_least32_t* off_p) {
|
||||
return reinterpret_cast<prop_info*>(to_prop_obj(off));
|
||||
}
|
||||
|
||||
inline prop_bt* prop_area::root_node() {
|
||||
return reinterpret_cast<prop_bt*>(to_prop_obj(0));
|
||||
inline prop_trie_node* prop_area::root_node() {
|
||||
return reinterpret_cast<prop_trie_node*>(to_prop_obj(0));
|
||||
}
|
||||
|
||||
static int cmp_prop_name(const char* one, uint32_t one_len, const char* two, uint32_t two_len) {
|
||||
@ -223,9 +222,9 @@ static int cmp_prop_name(const char* one, uint32_t one_len, const char* two, uin
|
||||
return strncmp(one, two, one_len);
|
||||
}
|
||||
|
||||
prop_bt* prop_area::find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen,
|
||||
bool alloc_if_needed) {
|
||||
prop_bt* current = bt;
|
||||
prop_trie_node* prop_area::find_prop_trie_node(prop_trie_node* const trie, const char* name,
|
||||
uint32_t namelen, bool alloc_if_needed) {
|
||||
prop_trie_node* current = trie;
|
||||
while (true) {
|
||||
if (!current) {
|
||||
return nullptr;
|
||||
@ -239,46 +238,46 @@ prop_bt* prop_area::find_prop_bt(prop_bt* const bt, const char* name, uint32_t n
|
||||
if (ret < 0) {
|
||||
uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed);
|
||||
if (left_offset != 0) {
|
||||
current = to_prop_bt(¤t->left);
|
||||
current = to_prop_trie_node(¤t->left);
|
||||
} else {
|
||||
if (!alloc_if_needed) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint_least32_t new_offset;
|
||||
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
|
||||
if (new_bt) {
|
||||
prop_trie_node* new_node = new_prop_trie_node(name, namelen, &new_offset);
|
||||
if (new_node) {
|
||||
atomic_store_explicit(¤t->left, new_offset, memory_order_release);
|
||||
}
|
||||
return new_bt;
|
||||
return new_node;
|
||||
}
|
||||
} else {
|
||||
uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
|
||||
if (right_offset != 0) {
|
||||
current = to_prop_bt(¤t->right);
|
||||
current = to_prop_trie_node(¤t->right);
|
||||
} else {
|
||||
if (!alloc_if_needed) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint_least32_t new_offset;
|
||||
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
|
||||
if (new_bt) {
|
||||
prop_trie_node* new_node = new_prop_trie_node(name, namelen, &new_offset);
|
||||
if (new_node) {
|
||||
atomic_store_explicit(¤t->right, new_offset, memory_order_release);
|
||||
}
|
||||
return new_bt;
|
||||
return new_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const prop_info* prop_area::find_property(prop_bt* const trie, const char* name, uint32_t namelen,
|
||||
const char* value, uint32_t valuelen,
|
||||
const prop_info* prop_area::find_property(prop_trie_node* const trie, const char* name,
|
||||
uint32_t namelen, const char* value, uint32_t valuelen,
|
||||
bool alloc_if_needed) {
|
||||
if (!trie) return nullptr;
|
||||
|
||||
const char* remaining_name = name;
|
||||
prop_bt* current = trie;
|
||||
prop_trie_node* current = trie;
|
||||
while (true) {
|
||||
const char* sep = strchr(remaining_name, '.');
|
||||
const bool want_subtree = (sep != nullptr);
|
||||
@ -288,13 +287,13 @@ const prop_info* prop_area::find_property(prop_bt* const trie, const char* name,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
prop_bt* root = nullptr;
|
||||
prop_trie_node* root = nullptr;
|
||||
uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
|
||||
if (children_offset != 0) {
|
||||
root = to_prop_bt(¤t->children);
|
||||
root = to_prop_trie_node(¤t->children);
|
||||
} else if (alloc_if_needed) {
|
||||
uint_least32_t new_offset;
|
||||
root = new_prop_bt(remaining_name, substr_size, &new_offset);
|
||||
root = new_prop_trie_node(remaining_name, substr_size, &new_offset);
|
||||
if (root) {
|
||||
atomic_store_explicit(¤t->children, new_offset, memory_order_release);
|
||||
}
|
||||
@ -304,7 +303,7 @@ const prop_info* prop_area::find_property(prop_bt* const trie, const char* name,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
|
||||
current = find_prop_trie_node(root, remaining_name, substr_size, alloc_if_needed);
|
||||
if (!current) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -330,13 +329,13 @@ const prop_info* prop_area::find_property(prop_bt* const trie, const char* name,
|
||||
}
|
||||
}
|
||||
|
||||
bool prop_area::foreach_property(prop_bt* const trie,
|
||||
bool prop_area::foreach_property(prop_trie_node* const trie,
|
||||
void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
|
||||
if (!trie) return false;
|
||||
|
||||
uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed);
|
||||
if (left_offset != 0) {
|
||||
const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie);
|
||||
const int err = foreach_property(to_prop_trie_node(&trie->left), propfn, cookie);
|
||||
if (err < 0) return false;
|
||||
}
|
||||
uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
|
||||
@ -347,12 +346,12 @@ bool prop_area::foreach_property(prop_bt* const trie,
|
||||
}
|
||||
uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed);
|
||||
if (children_offset != 0) {
|
||||
const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie);
|
||||
const int err = foreach_property(to_prop_trie_node(&trie->children), propfn, cookie);
|
||||
if (err < 0) return false;
|
||||
}
|
||||
uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed);
|
||||
if (right_offset != 0) {
|
||||
const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie);
|
||||
const int err = foreach_property(to_prop_trie_node(&trie->right), propfn, cookie);
|
||||
if (err < 0) return false;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
constexpr static const char kLongLegacyError[] =
|
||||
static constexpr const char kLongLegacyError[] =
|
||||
"Must use __system_property_read_callback() to read";
|
||||
static_assert(sizeof(kLongLegacyError) < prop_info::kLongLegacyErrorBufferSize,
|
||||
"Error message for long properties read by legacy libc must fit within 56 chars");
|
||||
|
@ -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;
|
||||
@ -67,47 +70,81 @@ bool SystemProperties::Init(const char* filename) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strlen(filename) >= PROP_FILENAME_MAX) {
|
||||
properties_filename_ = filename;
|
||||
|
||||
if (!InitContexts(false)) {
|
||||
return false;
|
||||
}
|
||||
strcpy(property_filename_, filename);
|
||||
|
||||
if (is_dir(property_filename_)) {
|
||||
if (access("/dev/__properties__/property_info", R_OK) == 0) {
|
||||
contexts_ = new (contexts_data_) ContextsSerialized();
|
||||
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemProperties::InitContexts(bool load_default_path) {
|
||||
if (is_dir(properties_filename_.c_str())) {
|
||||
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 {
|
||||
contexts_ = new (contexts_data_) ContextsSplit();
|
||||
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
|
||||
if (!contexts_->Initialize(false, properties_filename_.c_str(), nullptr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
contexts_ = new (contexts_data_) ContextsPreSplit();
|
||||
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
|
||||
if (!contexts_->Initialize(false, properties_filename_.c_str(), nullptr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
initialized_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
|
||||
if (strlen(filename) >= PROP_FILENAME_MAX) {
|
||||
return false;
|
||||
}
|
||||
strcpy(property_filename_, filename);
|
||||
return AreaInit(filename, fsetxattr_failed, false);
|
||||
}
|
||||
|
||||
contexts_ = new (contexts_data_) ContextsSerialized();
|
||||
if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
appcompat_filename_ = PropertiesFilename(properties_filename_.c_str(), "appcompat_override");
|
||||
appcompat_override_contexts_ = nullptr;
|
||||
if (access(appcompat_filename_.c_str(), F_OK) != -1) {
|
||||
auto* appcompat_contexts = new (appcompat_override_contexts_data_) ContextsSerialized();
|
||||
if (!appcompat_contexts->Initialize(true, appcompat_filename_.c_str(), fsetxattr_failed,
|
||||
load_default_path)) {
|
||||
// The appcompat folder exists, but initializing it failed
|
||||
return false;
|
||||
} else {
|
||||
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;
|
||||
@ -136,6 +173,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;
|
||||
}
|
||||
@ -234,16 +275,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);
|
||||
@ -253,18 +302,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;
|
||||
@ -272,34 +337,73 @@ int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len)
|
||||
|
||||
int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
|
||||
unsigned int valuelen) {
|
||||
if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
|
||||
if (namelen < 1) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"__system_property_add failed: name length 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (namelen < 1) {
|
||||
if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"__system_property_add failed: \"%s\" value too long: %d >= PROP_VALUE_MAX",
|
||||
name, valuelen);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!initialized_) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"__system_property_add failed: properties not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
prop_area* serial_pa = contexts_->GetSerialPropArea();
|
||||
if (serial_pa == nullptr) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"__system_property_add failed: property area not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
prop_area* pa = contexts_->GetPropAreaForName(name);
|
||||
if (!pa) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"__system_property_add failed: access denied for \"%s\"", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ret = pa->add(name, namelen, value, valuelen);
|
||||
if (!ret) {
|
||||
if (!pa->add(name, namelen, value, valuelen)) {
|
||||
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
|
||||
"__system_property_add failed: add failed for \"%s\"", name);
|
||||
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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -49,21 +49,34 @@
|
||||
#include "private/ScopedFd.h"
|
||||
|
||||
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
|
||||
static const char property_service_for_system_socket[] =
|
||||
"/dev/socket/" PROP_SERVICE_FOR_SYSTEM_NAME;
|
||||
static const char* kServiceVersionPropertyName = "ro.property_service.version";
|
||||
|
||||
class PropertyServiceConnection {
|
||||
public:
|
||||
PropertyServiceConnection() : last_error_(0) {
|
||||
PropertyServiceConnection(const char* name) : last_error_(0) {
|
||||
socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));
|
||||
if (socket_.get() == -1) {
|
||||
last_error_ = errno;
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t namelen = strlen(property_service_socket);
|
||||
// If we're trying to set "sys.powerctl" from a privileged process, use the special
|
||||
// socket. Because this socket is only accessible to privileged processes, it can't
|
||||
// be DoSed directly by malicious apps. (The shell user should be able to reboot,
|
||||
// though, so we don't just always use the special socket for "sys.powerctl".)
|
||||
// See b/262237198 for context
|
||||
const char* socket = property_service_socket;
|
||||
if (strcmp(name, "sys.powerctl") == 0 &&
|
||||
access(property_service_for_system_socket, W_OK) == 0) {
|
||||
socket = property_service_for_system_socket;
|
||||
}
|
||||
|
||||
const size_t namelen = strlen(socket);
|
||||
sockaddr_un addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
|
||||
strlcpy(addr.sun_path, socket, sizeof(addr.sun_path));
|
||||
addr.sun_family = AF_LOCAL;
|
||||
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
|
||||
|
||||
@ -176,7 +189,7 @@ struct prop_msg {
|
||||
};
|
||||
|
||||
static int send_prop_msg(const prop_msg* msg) {
|
||||
PropertyServiceConnection connection;
|
||||
PropertyServiceConnection connection(msg->name);
|
||||
if (!connection.IsValid()) {
|
||||
return connection.GetLastError();
|
||||
}
|
||||
@ -244,6 +257,21 @@ static void detect_protocol_version() {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* __prop_error_to_string(int error) {
|
||||
switch (error) {
|
||||
case PROP_ERROR_READ_CMD: return "PROP_ERROR_READ_CMD";
|
||||
case PROP_ERROR_READ_DATA: return "PROP_ERROR_READ_DATA";
|
||||
case PROP_ERROR_READ_ONLY_PROPERTY: return "PROP_ERROR_READ_ONLY_PROPERTY";
|
||||
case PROP_ERROR_INVALID_NAME: return "PROP_ERROR_INVALID_NAME";
|
||||
case PROP_ERROR_INVALID_VALUE: return "PROP_ERROR_INVALID_VALUE";
|
||||
case PROP_ERROR_PERMISSION_DENIED: return "PROP_ERROR_PERMISSION_DENIED";
|
||||
case PROP_ERROR_INVALID_CMD: return "PROP_ERROR_INVALID_CMD";
|
||||
case PROP_ERROR_HANDLE_CONTROL_MESSAGE: return "PROP_ERROR_HANDLE_CONTROL_MESSAGE";
|
||||
case PROP_ERROR_SET_FAILED: return "PROP_ERROR_SET_FAILED";
|
||||
}
|
||||
return "<unknown>";
|
||||
}
|
||||
|
||||
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
|
||||
int __system_property_set(const char* key, const char* value) {
|
||||
if (key == nullptr) return -1;
|
||||
@ -269,13 +297,12 @@ int __system_property_set(const char* key, const char* value) {
|
||||
// New protocol only allows long values for ro. properties only.
|
||||
if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
|
||||
// Use proper protocol
|
||||
PropertyServiceConnection connection;
|
||||
PropertyServiceConnection connection(key);
|
||||
if (!connection.IsValid()) {
|
||||
errno = connection.GetLastError();
|
||||
async_safe_format_log(
|
||||
ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
|
||||
errno, strerror(errno));
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": connection failed: %m", key,
|
||||
value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -283,8 +310,8 @@ int __system_property_set(const char* key, const char* value) {
|
||||
if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
|
||||
errno = connection.GetLastError();
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
|
||||
key, value, errno, strerror(errno));
|
||||
"Unable to set property \"%s\" to \"%s\": write failed: %m", key,
|
||||
value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -292,15 +319,14 @@ int __system_property_set(const char* key, const char* value) {
|
||||
if (!connection.RecvInt32(&result)) {
|
||||
errno = connection.GetLastError();
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
|
||||
key, value, errno, strerror(errno));
|
||||
"Unable to set property \"%s\" to \"%s\": recv failed: %m", key, value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (result != PROP_SUCCESS) {
|
||||
async_safe_format_log(ANDROID_LOG_WARN, "libc",
|
||||
"Unable to set property \"%s\" to \"%s\": error code: 0x%x", key, value,
|
||||
result);
|
||||
"Unable to set property \"%s\" to \"%s\": %s (0x%x)", key, value,
|
||||
__prop_error_to_string(result), result);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user