diff --git a/.gitmodules b/.gitmodules
index 483f82e26..40ee763b1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,12 +1,9 @@
[submodule "jni/sepolicy-inject"]
path = jni/sepolicy-inject
url = https://github.com/topjohnwu/sepolicy-inject
-[submodule "jni/resetprop"]
- path = jni/resetprop
- url = https://github.com/topjohnwu/resetprop.git
[submodule "jni/selinux"]
path = jni/selinux
url = https://github.com/topjohnwu/selinux.git
[submodule "jni/su"]
path = jni/su
- url = https://github.com/topjohnwu/Superuser.git
+ url = https://github.com/topjohnwu/MagiskSU.git
diff --git a/jni/resetprop b/jni/resetprop
deleted file mode 160000
index 3a6db7dba..000000000
--- a/jni/resetprop
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 3a6db7dbafdfb46fd09705ba1ab5477cacb87155
diff --git a/jni/resetprop/Android.mk b/jni/resetprop/Android.mk
new file mode 100644
index 000000000..bc29e4892
--- /dev/null
+++ b/jni/resetprop/Android.mk
@@ -0,0 +1,8 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := resetprop
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := resetprop.cpp system_properties.cpp libc_logging.cpp
+LOCAL_LDLIBS += -latomic
+include $(BUILD_EXECUTABLE)
diff --git a/jni/resetprop/LICENSE b/jni/resetprop/LICENSE
new file mode 100644
index 000000000..23cb79033
--- /dev/null
+++ b/jni/resetprop/LICENSE
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {description}
+ Copyright (C) {year} {fullname}
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ {signature of Ty Coon}, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/jni/resetprop/_system_properties.h b/jni/resetprop/_system_properties.h
new file mode 100644
index 000000000..e82c3c5ca
--- /dev/null
+++ b/jni/resetprop/_system_properties.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef _INCLUDE_SYS__SYSTEM_PROPERTIES_H
+#define _INCLUDE_SYS__SYSTEM_PROPERTIES_H
+
+#ifndef _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#error you should #include instead
+#else
+#include
+
+typedef struct prop_msg prop_msg;
+
+#define PROP_AREA_MAGIC 0x504f5250
+#define PROP_AREA_VERSION 0xfc6ed0ab
+#define PROP_AREA_VERSION_COMPAT 0x45434f76
+
+#define PROP_SERVICE_NAME "property_service"
+#define PROP_FILENAME_MAX 1024
+#define PROP_FILENAME "/dev/__properties__"
+
+#define PA_SIZE (128 * 1024)
+
+#define SERIAL_VALUE_LEN(serial) ((serial) >> 24)
+#define SERIAL_DIRTY(serial) ((serial) & 1)
+
+__BEGIN_DECLS
+
+struct prop_msg
+{
+ unsigned cmd;
+ char name[PROP_NAME_MAX];
+ char value[PROP_VALUE_MAX];
+};
+
+#define PROP_MSG_SETPROP 1
+
+/*
+** Rules:
+**
+** - there is only one writer, but many readers
+** - prop_area.count will never decrease in value
+** - once allocated, a prop_info's name will not change
+** - once allocated, a prop_info's offset will not change
+** - reading a value requires the following steps
+** 1. serial = pi->serial
+** 2. if SERIAL_DIRTY(serial), wait*, then goto 1
+** 3. memcpy(local, pi->value, SERIAL_VALUE_LEN(serial) + 1)
+** 4. if pi->serial != serial, goto 2
+**
+** - writing a value requires the following steps
+** 1. pi->serial = pi->serial | 1
+** 2. memcpy(pi->value, local_value, value_len)
+** 3. pi->serial = (value_len << 24) | ((pi->serial + 1) & 0xffffff)
+*/
+
+#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
+#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
+#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop"
+#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
+#define PROP_PATH_FACTORY "/factory/factory.prop"
+
+/*
+** Map the property area from the specified filename. This
+** method is for testing only.
+*/
+int __system_property_set_filename(const char *filename);
+
+/*
+** Initialize the area to be used to store properties. Can
+** only be done by a single process that has write access to
+** the property area.
+*/
+int __system_property_area_init();
+
+/* Read the global serial number of the system properties
+**
+** Called to predict if a series of cached __system_property_find
+** objects will have seen __system_property_serial values change.
+** But also aids the converse, as changes in the global serial can
+** also be used to predict if a failed __system_property_find
+** could in-turn now find a new object; thus preventing the
+** cycles of effort to poll __system_property_find.
+**
+** Typically called at beginning of a cache cycle to signal if _any_ possible
+** changes have occurred since last. If there is, one may check each individual
+** __system_property_serial to confirm dirty, or __system_property_find
+** to check if the property now exists. If a call to __system_property_add
+** or __system_property_update has completed between two calls to
+** __system_property_area_serial then the second call will return a larger
+** value than the first call. Beware of race conditions as changes to the
+** properties are not atomic, the main value of this call is to determine
+** whether the expensive __system_property_find is worth retrying to see if
+** a property now exists.
+**
+** Returns the serial number on success, -1 on error.
+*/
+unsigned int __system_property_area_serial();
+
+/* Add a new system property. Can only be done by a single
+** process that has write access to the property area, and
+** that process must handle sequencing to ensure the property
+** does not already exist and that only one property is added
+** or updated at a time.
+**
+** Returns 0 on success, -1 if the property area is full.
+*/
+int __system_property_add(const char *name, unsigned int namelen,
+ const char *value, unsigned int valuelen);
+
+int __system_property_del(const char *name);
+
+/* Update the value of a system property returned by
+** __system_property_find. Can only be done by a single process
+** that has write access to the property area, and that process
+** must handle sequencing to ensure that only one property is
+** updated at a time.
+**
+** Returns 0 on success, -1 if the parameters are incorrect.
+*/
+int __system_property_update(prop_info *pi, const char *value, unsigned int len);
+
+/* Read the serial number of a system property returned by
+** __system_property_find.
+**
+** Returns the serial number on success, -1 on error.
+*/
+unsigned int __system_property_serial(const prop_info *pi);
+
+/* Wait for any system property to be updated. Caller must pass
+** in 0 the first time, and the previous return value on each
+** successive call. */
+unsigned int __system_property_wait_any(unsigned int serial);
+
+/* Compatibility functions to support using an old init with a new libc,
+ ** mostly for the OTA updater binary. These can be deleted once OTAs from
+ ** a pre-K release no longer needed to be supported. */
+// const prop_info *__system_property_find_compat(const char *name);
+// int __system_property_read_compat(const prop_info *pi, char *name, char *value);
+// int __system_property_foreach_compat(
+// void (*propfn)(const prop_info *pi, void *cookie),
+// void *cookie);
+
+/* Initialize the system properties area in read only mode.
+ * Should be done by all processes that need to read system
+ * properties.
+ *
+ * Returns 0 on success, -1 otherwise.
+ */
+int __system_properties_init();
+
+__END_DECLS
+
+#endif
+#endif
diff --git a/jni/resetprop/bionic_futex.h b/jni/resetprop/bionic_futex.h
new file mode 100644
index 000000000..946d9dd1f
--- /dev/null
+++ b/jni/resetprop/bionic_futex.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#ifndef _BIONIC_FUTEX_H
+#define _BIONIC_FUTEX_H
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+__BEGIN_DECLS
+
+struct timespec;
+
+static inline __always_inline int __futex(volatile void* ftx, int op, int value,
+ const struct timespec* timeout,
+ int bitset) {
+ // Our generated syscall assembler sets errno, but our callers (pthread functions) don't want to.
+ int saved_errno = errno;
+ int result = syscall(__NR_futex, ftx, op, value, timeout, NULL, bitset);
+ if (__predict_false(result == -1)) {
+ result = -errno;
+ errno = saved_errno;
+ }
+ return result;
+}
+
+static inline int __futex_wake(volatile void* ftx, int count) {
+ return __futex(ftx, FUTEX_WAKE, count, NULL, 0);
+}
+
+static inline int __futex_wake_ex(volatile void* ftx, bool shared, int count) {
+ return __futex(ftx, shared ? FUTEX_WAKE : FUTEX_WAKE_PRIVATE, count, NULL, 0);
+}
+
+static inline int __futex_wait(volatile void* ftx, int value, const struct timespec* timeout) {
+ return __futex(ftx, FUTEX_WAIT, value, timeout, 0);
+}
+
+static inline int __futex_wait_ex(volatile void* ftx, bool shared, int value,
+ bool use_realtime_clock, const struct timespec* abs_timeout) {
+ return __futex(ftx, (shared ? FUTEX_WAIT_BITSET : FUTEX_WAIT_BITSET_PRIVATE) |
+ (use_realtime_clock ? FUTEX_CLOCK_REALTIME : 0), value, abs_timeout,
+ FUTEX_BITSET_MATCH_ANY);
+}
+
+__END_DECLS
+
+#endif /* _BIONIC_FUTEX_H */
diff --git a/jni/resetprop/bionic_lock.h b/jni/resetprop/bionic_lock.h
new file mode 100644
index 000000000..60c440b00
--- /dev/null
+++ b/jni/resetprop/bionic_lock.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+#ifndef _BIONIC_LOCK_H
+#define _BIONIC_LOCK_H
+
+#include
+#include "bionic_futex.h"
+#include "bionic_macros.h"
+
+// Lock is used in places like pthread_rwlock_t, which can be initialized without calling
+// an initialization function. So make sure Lock can be initialized by setting its memory to 0.
+class Lock {
+ private:
+ enum LockState {
+ Unlocked = 0,
+ LockedWithoutWaiter,
+ LockedWithWaiter,
+ };
+ _Atomic(LockState) state;
+ bool process_shared;
+
+ public:
+ void init(bool process_shared) {
+ atomic_init(&state, Unlocked);
+ this->process_shared = process_shared;
+ }
+
+ bool trylock() {
+ LockState old_state = Unlocked;
+ return __predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
+ LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed));
+ }
+
+ void lock() {
+ LockState old_state = Unlocked;
+ if (__predict_true(atomic_compare_exchange_strong_explicit(&state, &old_state,
+ LockedWithoutWaiter, memory_order_acquire, memory_order_relaxed))) {
+ return;
+ }
+ while (atomic_exchange_explicit(&state, LockedWithWaiter, memory_order_acquire) != Unlocked) {
+ // TODO: As the critical section is brief, it is a better choice to spin a few times befor sleeping.
+ __futex_wait_ex(&state, process_shared, LockedWithWaiter, false, nullptr);
+ }
+ return;
+ }
+
+ void unlock() {
+ if (atomic_exchange_explicit(&state, Unlocked, memory_order_release) == LockedWithWaiter) {
+ __futex_wake_ex(&state, process_shared, 1);
+ }
+ }
+};
+
+#endif // _BIONIC_LOCK_H
diff --git a/jni/resetprop/bionic_macros.h b/jni/resetprop/bionic_macros.h
new file mode 100644
index 000000000..4969bd95f
--- /dev/null
+++ b/jni/resetprop/bionic_macros.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef _BIONIC_MACROS_H_
+#define _BIONIC_MACROS_H_
+
+// Frameworks OpenGL code currently leaks this header and allows
+// collisions with other declarations, e.g., from libnativehelper.
+// TODO: Remove once cleaned up. b/18334516
+#if !defined(DISALLOW_COPY_AND_ASSIGN)
+// DISALLOW_COPY_AND_ASSIGN disallows the copy and operator= functions.
+// It goes in the private: declarations in a class.
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&) = delete; \
+ void operator=(const TypeName&) = delete
+#endif // !defined(DISALLOW_COPY_AND_ASSIGN)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName() = delete; \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+#define BIONIC_ALIGN(value, alignment) \
+ (((value) + (alignment) - 1) & ~((alignment) - 1))
+
+#define BIONIC_ROUND_UP_POWER_OF_2(value) \
+ ((sizeof(value) == 8) \
+ ? (1UL << (64 - __builtin_clzl(static_cast(value)))) \
+ : (1UL << (32 - __builtin_clz(static_cast(value)))))
+
+#endif // _BIONIC_MACROS_H_
diff --git a/jni/resetprop/libc_logging.cpp b/jni/resetprop/libc_logging.cpp
new file mode 100644
index 000000000..72098fad7
--- /dev/null
+++ b/jni/resetprop/libc_logging.cpp
@@ -0,0 +1,659 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "libc_logging.h" // Relative path so we can #include this .cpp file for testing.
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include "_system_properties.h"
+
+static pthread_mutex_t g_abort_msg_lock = PTHREAD_MUTEX_INITIALIZER;
+
+__LIBC_HIDDEN__ abort_msg_t** __abort_message_ptr; // Accessible to __libc_init_common.
+
+// Must be kept in sync with frameworks/base/core/java/android/util/EventLog.java.
+enum AndroidEventLogType {
+ EVENT_TYPE_INT = 0,
+ EVENT_TYPE_LONG = 1,
+ EVENT_TYPE_STRING = 2,
+ EVENT_TYPE_LIST = 3,
+ EVENT_TYPE_FLOAT = 4,
+};
+
+struct BufferOutputStream {
+ public:
+ BufferOutputStream(char* buffer, size_t size) : total(0) {
+ buffer_ = buffer;
+ end_ = buffer + size - 1;
+ pos_ = buffer_;
+ pos_[0] = '\0';
+ }
+
+ ~BufferOutputStream() {
+ }
+
+ void Send(const char* data, int len) {
+ if (len < 0) {
+ len = strlen(data);
+ }
+
+ total += len;
+
+ while (len > 0) {
+ int avail = end_ - pos_;
+ if (avail == 0) {
+ return;
+ }
+ if (avail > len) {
+ avail = len;
+ }
+ memcpy(pos_, data, avail);
+ pos_ += avail;
+ pos_[0] = '\0';
+ len -= avail;
+ }
+ }
+
+ size_t total;
+
+ private:
+ char* buffer_;
+ char* pos_;
+ char* end_;
+};
+
+struct FdOutputStream {
+ public:
+ FdOutputStream(int fd) : total(0), fd_(fd) {
+ }
+
+ void Send(const char* data, int len) {
+ if (len < 0) {
+ len = strlen(data);
+ }
+
+ total += len;
+
+ while (len > 0) {
+ int rc = TEMP_FAILURE_RETRY(write(fd_, data, len));
+ if (rc == -1) {
+ return;
+ }
+ data += rc;
+ len -= rc;
+ }
+ }
+
+ size_t total;
+
+ private:
+ int fd_;
+};
+
+/*** formatted output implementation
+ ***/
+
+/* Parse a decimal string from 'format + *ppos',
+ * return the value, and writes the new position past
+ * the decimal string in '*ppos' on exit.
+ *
+ * NOTE: Does *not* handle a sign prefix.
+ */
+static unsigned parse_decimal(const char *format, int *ppos) {
+ const char* p = format + *ppos;
+ unsigned result = 0;
+
+ for (;;) {
+ int ch = *p;
+ unsigned d = static_cast(ch - '0');
+
+ if (d >= 10U) {
+ break;
+ }
+
+ result = result*10 + d;
+ p++;
+ }
+ *ppos = p - format;
+ return result;
+}
+
+// Writes number 'value' in base 'base' into buffer 'buf' of size 'buf_size' bytes.
+// Assumes that buf_size > 0.
+static void format_unsigned(char* buf, size_t buf_size, uint64_t value, int base, bool caps) {
+ char* p = buf;
+ char* end = buf + buf_size - 1;
+
+ // Generate digit string in reverse order.
+ while (value) {
+ unsigned d = value % base;
+ value /= base;
+ if (p != end) {
+ char ch;
+ if (d < 10) {
+ ch = '0' + d;
+ } else {
+ ch = (caps ? 'A' : 'a') + (d - 10);
+ }
+ *p++ = ch;
+ }
+ }
+
+ // Special case for 0.
+ if (p == buf) {
+ if (p != end) {
+ *p++ = '0';
+ }
+ }
+ *p = '\0';
+
+ // Reverse digit string in-place.
+ size_t length = p - buf;
+ for (size_t i = 0, j = length - 1; i < j; ++i, --j) {
+ char ch = buf[i];
+ buf[i] = buf[j];
+ buf[j] = ch;
+ }
+}
+
+static void format_integer(char* buf, size_t buf_size, uint64_t value, char conversion) {
+ // Decode the conversion specifier.
+ int is_signed = (conversion == 'd' || conversion == 'i' || conversion == 'o');
+ int base = 10;
+ if (conversion == 'x' || conversion == 'X') {
+ base = 16;
+ } else if (conversion == 'o') {
+ base = 8;
+ }
+ bool caps = (conversion == 'X');
+
+ if (is_signed && static_cast(value) < 0) {
+ buf[0] = '-';
+ buf += 1;
+ buf_size -= 1;
+ value = static_cast(-static_cast(value));
+ }
+ format_unsigned(buf, buf_size, value, base, caps);
+}
+
+template
+static void SendRepeat(Out& o, char ch, int count) {
+ char pad[8];
+ memset(pad, ch, sizeof(pad));
+
+ const int pad_size = static_cast(sizeof(pad));
+ while (count > 0) {
+ int avail = count;
+ if (avail > pad_size) {
+ avail = pad_size;
+ }
+ o.Send(pad, avail);
+ count -= avail;
+ }
+}
+
+/* Perform formatted output to an output target 'o' */
+template
+static void out_vformat(Out& o, const char* format, va_list args) {
+ int nn = 0;
+
+ for (;;) {
+ int mm;
+ int padZero = 0;
+ int padLeft = 0;
+ char sign = '\0';
+ int width = -1;
+ int prec = -1;
+ size_t bytelen = sizeof(int);
+ int slen;
+ char buffer[32]; /* temporary buffer used to format numbers */
+
+ char c;
+
+ /* first, find all characters that are not 0 or '%' */
+ /* then send them to the output directly */
+ mm = nn;
+ do {
+ c = format[mm];
+ if (c == '\0' || c == '%')
+ break;
+ mm++;
+ } while (1);
+
+ if (mm > nn) {
+ o.Send(format+nn, mm-nn);
+ nn = mm;
+ }
+
+ /* is this it ? then exit */
+ if (c == '\0')
+ break;
+
+ /* nope, we are at a '%' modifier */
+ nn++; // skip it
+
+ /* parse flags */
+ for (;;) {
+ c = format[nn++];
+ if (c == '\0') { /* single trailing '%' ? */
+ c = '%';
+ o.Send(&c, 1);
+ return;
+ }
+ else if (c == '0') {
+ padZero = 1;
+ continue;
+ }
+ else if (c == '-') {
+ padLeft = 1;
+ continue;
+ }
+ else if (c == ' ' || c == '+') {
+ sign = c;
+ continue;
+ }
+ break;
+ }
+
+ /* parse field width */
+ if ((c >= '0' && c <= '9')) {
+ nn --;
+ width = static_cast(parse_decimal(format, &nn));
+ c = format[nn++];
+ }
+
+ /* parse precision */
+ if (c == '.') {
+ prec = static_cast(parse_decimal(format, &nn));
+ c = format[nn++];
+ }
+
+ /* length modifier */
+ switch (c) {
+ case 'h':
+ bytelen = sizeof(short);
+ if (format[nn] == 'h') {
+ bytelen = sizeof(char);
+ nn += 1;
+ }
+ c = format[nn++];
+ break;
+ case 'l':
+ bytelen = sizeof(long);
+ if (format[nn] == 'l') {
+ bytelen = sizeof(long long);
+ nn += 1;
+ }
+ c = format[nn++];
+ break;
+ case 'z':
+ bytelen = sizeof(size_t);
+ c = format[nn++];
+ break;
+ case 't':
+ bytelen = sizeof(ptrdiff_t);
+ c = format[nn++];
+ break;
+ default:
+ ;
+ }
+
+ /* conversion specifier */
+ const char* str = buffer;
+ if (c == 's') {
+ /* string */
+ str = va_arg(args, const char*);
+ if (str == NULL) {
+ str = "(null)";
+ }
+ } else if (c == 'c') {
+ /* character */
+ /* NOTE: char is promoted to int when passed through the stack */
+ buffer[0] = static_cast(va_arg(args, int));
+ buffer[1] = '\0';
+ } else if (c == 'p') {
+ uint64_t value = reinterpret_cast(va_arg(args, void*));
+ buffer[0] = '0';
+ buffer[1] = 'x';
+ format_integer(buffer + 2, sizeof(buffer) - 2, value, 'x');
+ } else if (c == 'd' || c == 'i' || c == 'o' || c == 'u' || c == 'x' || c == 'X') {
+ /* integers - first read value from stack */
+ uint64_t value;
+ int is_signed = (c == 'd' || c == 'i' || c == 'o');
+
+ /* NOTE: int8_t and int16_t are promoted to int when passed
+ * through the stack
+ */
+ switch (bytelen) {
+ case 1: value = static_cast(va_arg(args, int)); break;
+ case 2: value = static_cast(va_arg(args, int)); break;
+ case 4: value = va_arg(args, uint32_t); break;
+ case 8: value = va_arg(args, uint64_t); break;
+ default: return; /* should not happen */
+ }
+
+ /* sign extension, if needed */
+ if (is_signed) {
+ int shift = 64 - 8*bytelen;
+ value = static_cast((static_cast(value << shift)) >> shift);
+ }
+
+ /* format the number properly into our buffer */
+ format_integer(buffer, sizeof(buffer), value, c);
+ } else if (c == '%') {
+ buffer[0] = '%';
+ buffer[1] = '\0';
+ } else {
+ __assert(__FILE__, __LINE__, "conversion specifier unsupported");
+ }
+
+ /* if we are here, 'str' points to the content that must be
+ * outputted. handle padding and alignment now */
+
+ slen = strlen(str);
+
+ if (sign != '\0' || prec != -1) {
+ __assert(__FILE__, __LINE__, "sign/precision unsupported");
+ }
+
+ if (slen < width && !padLeft) {
+ char padChar = padZero ? '0' : ' ';
+ SendRepeat(o, padChar, width - slen);
+ }
+
+ o.Send(str, slen);
+
+ if (slen < width && padLeft) {
+ char padChar = padZero ? '0' : ' ';
+ SendRepeat(o, padChar, width - slen);
+ }
+ }
+}
+
+int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...) {
+ BufferOutputStream os(buffer, buffer_size);
+ va_list args;
+ va_start(args, format);
+ out_vformat(os, format, args);
+ va_end(args);
+ return os.total;
+}
+
+int __libc_format_fd(int fd, const char* format, ...) {
+ FdOutputStream os(fd);
+ va_list args;
+ va_start(args, format);
+ out_vformat(os, format, args);
+ va_end(args);
+ return os.total;
+}
+
+static int __libc_write_stderr(const char* tag, const char* msg) {
+ int fd = TEMP_FAILURE_RETRY(open("/dev/stderr", O_CLOEXEC | O_WRONLY | O_APPEND));
+ if (fd == -1) {
+ return -1;
+ }
+
+ iovec vec[4];
+ vec[0].iov_base = const_cast(tag);
+ vec[0].iov_len = strlen(tag);
+ vec[1].iov_base = const_cast(": ");
+ vec[1].iov_len = 2;
+ vec[2].iov_base = const_cast(msg);
+ vec[2].iov_len = strlen(msg);
+ vec[3].iov_base = const_cast("\n");
+ vec[3].iov_len = 1;
+
+ int result = TEMP_FAILURE_RETRY(writev(fd, vec, 4));
+ close(fd);
+ return result;
+}
+
+static int __libc_open_log_socket() {
+ // ToDo: Ideally we want this to fail if the gid of the current
+ // process is AID_LOGD, but will have to wait until we have
+ // registered this in private/android_filesystem_config.h. We have
+ // found that all logd crashes thus far have had no problem stuffing
+ // the UNIX domain socket and moving on so not critical *today*.
+
+ int log_fd = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));
+ if (log_fd < 0) {
+ return -1;
+ }
+
+ if (fcntl(log_fd, F_SETFL, O_NONBLOCK) == -1) {
+ close(log_fd);
+ return -1;
+ }
+
+ union {
+ struct sockaddr addr;
+ struct sockaddr_un addrUn;
+ } u;
+ memset(&u, 0, sizeof(u));
+ u.addrUn.sun_family = AF_UNIX;
+ strlcpy(u.addrUn.sun_path, "/dev/socket/logdw", sizeof(u.addrUn.sun_path));
+
+ if (TEMP_FAILURE_RETRY(connect(log_fd, &u.addr, sizeof(u.addrUn))) != 0) {
+ close(log_fd);
+ return -1;
+ }
+
+ return log_fd;
+}
+
+struct cache {
+ const prop_info* pinfo;
+ uint32_t serial;
+ char c;
+};
+
+static void refresh_cache(struct cache *cache, const char *key)
+{
+ if (!cache->pinfo) {
+ cache->pinfo = __system_property_find(key);
+ if (!cache->pinfo) {
+ return;
+ }
+ }
+ uint32_t serial = __system_property_serial(cache->pinfo);
+ if (serial == cache->serial) {
+ return;
+ }
+ cache->serial = serial;
+
+ char buf[PROP_VALUE_MAX];
+ __system_property_read(cache->pinfo, 0, buf);
+ cache->c = buf[0];
+}
+
+// Timestamp state generally remains constant, since a change is
+// rare, we can accept a trylock failure gracefully.
+static pthread_mutex_t lock_clockid = PTHREAD_MUTEX_INITIALIZER;
+
+static clockid_t __android_log_clockid()
+{
+ static struct cache r_time_cache = { NULL, static_cast(-1), 0 };
+ static struct cache p_time_cache = { NULL, static_cast(-1), 0 };
+ char c;
+
+ if (pthread_mutex_trylock(&lock_clockid)) {
+ // We are willing to accept some race in this context
+ if (!(c = p_time_cache.c)) {
+ c = r_time_cache.c;
+ }
+ } else {
+ static uint32_t serial;
+ uint32_t current_serial = __system_property_area_serial();
+ if (current_serial != serial) {
+ refresh_cache(&r_time_cache, "ro.logd.timestamp");
+ refresh_cache(&p_time_cache, "persist.logd.timestamp");
+ serial = current_serial;
+ }
+ if (!(c = p_time_cache.c)) {
+ c = r_time_cache.c;
+ }
+
+ pthread_mutex_unlock(&lock_clockid);
+ }
+
+ return (tolower(c) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+}
+
+struct log_time { // Wire format
+ uint32_t tv_sec;
+ uint32_t tv_nsec;
+};
+
+int __libc_write_log(int priority, const char* tag, const char* msg) {
+ int main_log_fd = __libc_open_log_socket();
+ if (main_log_fd == -1) {
+ // Try stderr instead.
+ return __libc_write_stderr(tag, msg);
+ }
+
+ iovec vec[6];
+ char log_id = (priority == ANDROID_LOG_FATAL) ? LOG_ID_CRASH : LOG_ID_MAIN;
+ vec[0].iov_base = &log_id;
+ vec[0].iov_len = sizeof(log_id);
+ uint16_t tid = gettid();
+ vec[1].iov_base = &tid;
+ vec[1].iov_len = sizeof(tid);
+ timespec ts;
+ clock_gettime(__android_log_clockid(), &ts);
+ log_time realtime_ts;
+ realtime_ts.tv_sec = ts.tv_sec;
+ realtime_ts.tv_nsec = ts.tv_nsec;
+ vec[2].iov_base = &realtime_ts;
+ vec[2].iov_len = sizeof(realtime_ts);
+
+ vec[3].iov_base = &priority;
+ vec[3].iov_len = 1;
+ vec[4].iov_base = const_cast(tag);
+ vec[4].iov_len = strlen(tag) + 1;
+ vec[5].iov_base = const_cast(msg);
+ vec[5].iov_len = strlen(msg) + 1;
+
+ int result = TEMP_FAILURE_RETRY(writev(main_log_fd, vec, sizeof(vec) / sizeof(vec[0])));
+ close(main_log_fd);
+ return result;
+}
+
+int __libc_format_log_va_list(int priority, const char* tag, const char* format, va_list args) {
+ char buffer[1024];
+ BufferOutputStream os(buffer, sizeof(buffer));
+ out_vformat(os, format, args);
+ return __libc_write_log(priority, tag, buffer);
+}
+
+int __libc_format_log(int priority, const char* tag, const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ int result = __libc_format_log_va_list(priority, tag, format, args);
+ va_end(args);
+ return result;
+}
+
+static int __libc_android_log_event(int32_t tag, char type, const void* payload, size_t len) {
+ iovec vec[6];
+ char log_id = LOG_ID_EVENTS;
+ vec[0].iov_base = &log_id;
+ vec[0].iov_len = sizeof(log_id);
+ uint16_t tid = gettid();
+ vec[1].iov_base = &tid;
+ vec[1].iov_len = sizeof(tid);
+ timespec ts;
+ clock_gettime(__android_log_clockid(), &ts);
+ log_time realtime_ts;
+ realtime_ts.tv_sec = ts.tv_sec;
+ realtime_ts.tv_nsec = ts.tv_nsec;
+ vec[2].iov_base = &realtime_ts;
+ vec[2].iov_len = sizeof(realtime_ts);
+
+ vec[3].iov_base = &tag;
+ vec[3].iov_len = sizeof(tag);
+ vec[4].iov_base = &type;
+ vec[4].iov_len = sizeof(type);
+ vec[5].iov_base = const_cast(payload);
+ vec[5].iov_len = len;
+
+ int event_log_fd = __libc_open_log_socket();
+
+ if (event_log_fd == -1) {
+ return -1;
+ }
+ int result = TEMP_FAILURE_RETRY(writev(event_log_fd, vec, sizeof(vec) / sizeof(vec[0])));
+ close(event_log_fd);
+ return result;
+}
+
+void __libc_android_log_event_int(int32_t tag, int value) {
+ __libc_android_log_event(tag, EVENT_TYPE_INT, &value, sizeof(value));
+}
+
+void __libc_android_log_event_uid(int32_t tag) {
+ __libc_android_log_event_int(tag, getuid());
+}
+
+void __fortify_chk_fail(const char* msg, uint32_t tag) {
+ if (tag != 0) {
+ __libc_android_log_event_uid(tag);
+ }
+ __libc_fatal("FORTIFY: %s", msg);
+}
+
+void __libc_fatal_no_abort(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ __libc_fatal(format, args);
+ va_end(args);
+}
+
+void __libc_fatal(const char* format, ...) {
+ va_list args;
+ va_start(args, format);
+ __libc_fatal(format, args);
+ va_end(args);
+ abort();
+}
\ No newline at end of file
diff --git a/jni/resetprop/libc_logging.h b/jni/resetprop/libc_logging.h
new file mode 100644
index 000000000..31abc38a5
--- /dev/null
+++ b/jni/resetprop/libc_logging.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef _LIBC_LOGGING_H
+#define _LIBC_LOGGING_H
+
+#include
+#include
+#include
+#include
+
+__BEGIN_DECLS
+
+enum {
+ ANDROID_LOG_UNKNOWN = 0,
+ ANDROID_LOG_DEFAULT, /* only for SetMinPriority() */
+
+ ANDROID_LOG_VERBOSE,
+ ANDROID_LOG_DEBUG,
+ ANDROID_LOG_INFO,
+ ANDROID_LOG_WARN,
+ ANDROID_LOG_ERROR,
+ ANDROID_LOG_FATAL,
+
+ ANDROID_LOG_SILENT, /* only for SetMinPriority(); must be last */
+};
+
+enum {
+ LOG_ID_MIN = 0,
+
+ LOG_ID_MAIN = 0,
+ LOG_ID_RADIO = 1,
+ LOG_ID_EVENTS = 2,
+ LOG_ID_SYSTEM = 3,
+ LOG_ID_CRASH = 4,
+
+ LOG_ID_MAX
+};
+
+struct abort_msg_t {
+ size_t size;
+ char msg[0];
+};
+
+//
+// Formats a message to the log (priority 'fatal'), then aborts.
+//
+
+__LIBC_HIDDEN__ __noreturn void __libc_fatal(const char* format, ...) __printflike(1, 2);
+
+//
+// Formats a message to the log (priority 'fatal'), but doesn't abort.
+// Used by the malloc implementation to ensure that debuggerd dumps memory
+// around the bad address.
+//
+
+__LIBC_HIDDEN__ void __libc_fatal_no_abort(const char* format, ...)
+ __printflike(1, 2);
+
+//
+// Formatting routines for the C library's internal debugging.
+// Unlike the usual alternatives, these don't allocate, and they don't drag in all of stdio.
+//
+
+__LIBC_HIDDEN__ int __libc_format_buffer(char* buffer, size_t buffer_size, const char* format, ...)
+ __printflike(3, 4);
+
+__LIBC_HIDDEN__ int __libc_format_fd(int fd, const char* format, ...)
+ __printflike(2, 3);
+
+__LIBC_HIDDEN__ int __libc_format_log(int priority, const char* tag, const char* format, ...)
+ __printflike(3, 4);
+
+__LIBC_HIDDEN__ int __libc_format_log_va_list(int priority, const char* tag, const char* format,
+ va_list ap);
+
+__LIBC_HIDDEN__ int __libc_write_log(int priority, const char* tag, const char* msg);
+
+//
+// Event logging.
+//
+
+__LIBC_HIDDEN__ void __libc_android_log_event_int(int32_t tag, int value);
+__LIBC_HIDDEN__ void __libc_android_log_event_uid(int32_t tag);
+
+__LIBC_HIDDEN__ __noreturn void __fortify_chk_fail(const char* msg, uint32_t event_tag);
+
+__END_DECLS
+
+#endif
diff --git a/jni/resetprop/resetprop.cpp b/jni/resetprop/resetprop.cpp
new file mode 100644
index 000000000..ecbb75838
--- /dev/null
+++ b/jni/resetprop/resetprop.cpp
@@ -0,0 +1,280 @@
+/*
+ *
+ * resetprop.cpp
+ *
+ * Copyright 2016 nkk71
+ * Copyright 2016 topjohnwu
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include "_system_properties.h"
+#include
+
+/* Info:
+ *
+ * all changes are in
+ *
+ * bionic/libc/bionic/system_properties.cpp
+ *
+ * Functions that need to be patched/added in system_properties.cpp
+ *
+ * int __system_properties_init()
+ * on android 7, first tear down the everything then let it initialize again:
+ * if (initialized) {
+ * //list_foreach(contexts, [](context_node* l) { l->reset_access(); });
+ * //return 0;
+ * free_and_unmap_contexts();
+ * initialized = false;
+ * }
+ *
+ *
+ * static prop_area* map_prop_area(const char* filename, bool is_legacy)
+ * we dont want this read only so change: 'O_RDONLY' to 'O_RDWR'
+ *
+ * static prop_area* map_fd_ro(const int fd)
+ * we dont want this read only so change: 'PROT_READ' to 'PROT_READ | PROT_WRITE'
+ *
+ *
+ * Copy the code of prop_info *prop_area::find_property, and modify to delete props
+ * const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name)
+ * {
+ * ...
+ * ... Do not alloc a new prop_bt here, remove all code involve alloc_if_needed
+ * ...
+ *
+ * if (prop_offset != 0) {
+ * atomic_store_explicit(¤t->prop, 0, memory_order_release); // Add this line to nullify the prop entry
+ * return to_prop_info(¤t->prop);
+ * } else {
+ *
+ * ....
+ * }
+ *
+ *
+ * by patching just those functions directly, all other functions should be ok
+ * as is.
+ *
+ *
+ */
+
+int verbose = 0, del = 0, file = 0, trigger = 1;
+
+static bool is_legal_property_name(const char* name, size_t namelen)
+{
+ size_t i;
+ if (namelen >= PROP_NAME_MAX) return false;
+ if (namelen < 1) return false;
+ if (name[0] == '.') return false;
+ if (name[namelen - 1] == '.') return false;
+
+ /* Only allow alphanumeric, plus '.', '-', or '_' */
+ /* Don't allow ".." to appear in a property name */
+ for (i = 0; i < namelen; i++) {
+ if (name[i] == '.') {
+ // i=0 is guaranteed to never have a dot. See above.
+ if (name[i-1] == '.') return false;
+ continue;
+ }
+ if (name[i] == '_' || name[i] == '-') continue;
+ if (name[i] >= 'a' && name[i] <= 'z') continue;
+ if (name[i] >= 'A' && name[i] <= 'Z') continue;
+ if (name[i] >= '0' && name[i] <= '9') continue;
+ return false;
+ }
+
+ return true;
+}
+
+int x_property_set(const char *name, const char *value)
+{
+ int ret;
+ char value_read[PROP_VALUE_MAX];
+
+ size_t namelen = strlen(name);
+ size_t valuelen = strlen(value);
+
+ if (trigger) {
+ printf("Set with property_service: '%s'='%s'\n", name, value);
+ } else {
+ printf("Modify data structure: '%s'='%s'\n", name, value);
+ }
+
+ __system_property_get(name, value_read);
+
+ if(strlen(value_read)) {
+ printf("Existing property: '%s'='%s'\n", name, value_read);
+ if (trigger) {
+ if (!strncmp(name, "ro.", 3)) __system_property_del(name); // Only delete ro props
+ ret = __system_property_set(name, value);
+ } else {
+ ret = __system_property_update((prop_info*) __system_property_find(name), value, valuelen);
+ }
+ } else {
+ if (trigger) {
+ ret = __system_property_set(name, value);
+ } else {
+ ret = __system_property_add(name, namelen, value, valuelen);
+ }
+ }
+
+ if (ret != 0) {
+ fprintf(stderr, "Failed to set '%s'='%s'\n", name, value);
+ return ret;
+ }
+
+ __system_property_get(name, value_read);
+ printf("Recheck property: '%s'='%s'\n", name, value_read);
+
+ return 0;
+}
+
+int read_prop_file(const char* filename) {
+ printf(" Attempting to read props from \'%s\'\n", filename);
+ FILE *fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Cannot open \'%s\'\n", filename);
+ return 1;
+ }
+ char *line = NULL, *pch, name[PROP_NAME_MAX], value[PROP_VALUE_MAX];
+ size_t len;
+ ssize_t read;
+ int comment = 0, i;
+ while ((read = getline(&line, &len, fp)) != -1) {
+ // Remove the trailing newline
+ if (line[read - 1] == '\n') {
+ line[read - 1] = '\0';
+ --read;
+ }
+ comment = 0;
+ for (i = 0; i < read; ++i) {
+ // Ignore starting spaces
+ if (line[i] == ' ') continue;
+ else {
+ // A line starting with # is ignored
+ if (line[i] == '#') comment = 1;
+ break;
+ }
+ }
+ if (comment) continue;
+ pch = strchr(line, '=');
+ // Ignore ivalid formats
+ if ( ((pch == NULL) || (i >= (pch - line))) || (pch >= line + read - 1) ) continue;
+ // Separate the string
+ *pch = '\0';
+ x_property_set(line + i, pch + 1);
+ }
+ free(line);
+ fclose(fp);
+ return 0;
+}
+
+int usage(char* name) {
+ fprintf(stderr, "usage: %s [-v] [-n] [--file propfile] [--delete name] [ name value ] \n", name);
+ fprintf(stderr, " -v :\n");
+ fprintf(stderr, " verbose output (Default: Disabled)\n");
+ fprintf(stderr, " -n :\n");
+ fprintf(stderr, " no event triggers when changing props (Default: Will trigger events)\n");
+ fprintf(stderr, " --file propfile :\n");
+ fprintf(stderr, " Read props from prop files (e.g. build.prop)\n");
+ fprintf(stderr, " --delete name :\n");
+ fprintf(stderr, " Remove a prop entry\n\n");
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+
+ int exp_arg = 2, stdout_bak, null;
+ char *name, *value, *filename;
+
+ if (argc < 3) {
+ return usage(argv[0]);
+ }
+
+ for (int i = 1; i < argc; ++i) {
+ if (!strcmp("-v", argv[i])) {
+ verbose = 1;
+ } else if (!strcmp("-n", argv[i])) {
+ trigger = 0;
+ } else if (!strcmp("--file", argv[i])) {
+ file = 1;
+ exp_arg = 1;
+ } else if (!strcmp("--delete", argv[i])) {
+ del = 1;
+ exp_arg = 1;
+ } else {
+ if (i + exp_arg > argc) {
+ return usage(argv[0]);
+ }
+ if (file) {
+ filename = argv[i];
+ break;
+ } else {
+ if(!is_legal_property_name(argv[i], strlen(argv[i]))) {
+ fprintf(stderr, "Illegal property name \'%s\'\n", argv[i]);
+ return 1;
+ }
+ name = argv[i];
+ if (exp_arg > 1) {
+ if (strlen(argv[i + 1]) >= PROP_VALUE_MAX) {
+ fprintf(stderr, "Value too long \'%s\'\n", argv[i + 1]);
+ return 1;
+ }
+ value = argv[i + 1];
+ }
+ break;
+ }
+ }
+ }
+
+ if (!verbose) {
+ fflush(stdout);
+ stdout_bak = dup(1);
+ null = open("/dev/null", O_WRONLY);
+ dup2(null, 1);
+ }
+
+ printf("resetprop by nkk71 & topjohnwu\n");
+
+ printf("Initializing...\n");
+ if (__system_properties_init()) {
+ fprintf(stderr, "Error during init\n");
+ return 1;
+ }
+
+ if (file) {
+ if (read_prop_file(filename)) return 1;
+ } else if (del) {
+ printf("Attempting to delete '%s'\n", name);
+ if (__system_property_del(name)) return 1;
+ } else {
+ if(x_property_set(name, value)) return 1;
+ }
+ printf("Done!\n\n");
+ return 0;
+
+}
diff --git a/jni/resetprop/system_properties.cpp b/jni/resetprop/system_properties.cpp
new file mode 100644
index 000000000..4e99e15ef
--- /dev/null
+++ b/jni/resetprop/system_properties.cpp
@@ -0,0 +1,1385 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include "_system_properties.h"
+#include
+
+#include "bionic_futex.h"
+#include "bionic_lock.h"
+#include "bionic_macros.h"
+#include "libc_logging.h"
+
+static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
+
+
+/*
+ * Properties are stored in a hybrid trie/binary tree structure.
+ * Each property's name is delimited at '.' characters, and the tokens are put
+ * into a trie structure. Siblings at each level of the trie are stored in a
+ * binary tree. For instance, "ro.secure"="1" could be stored as follows:
+ *
+ * +-----+ children +----+ children +--------+
+ * | |-------------->| ro |-------------->| secure |
+ * +-----+ +----+ +--------+
+ * / \ / |
+ * left / \ right left / | prop +===========+
+ * v v v +-------->| ro.secure |
+ * +-----+ +-----+ +-----+ +-----------+
+ * | net | | sys | | com | | 1 |
+ * +-----+ +-----+ +-----+ +===========+
+ */
+
+// Represents a node in the trie.
+struct prop_bt {
+ uint8_t namelen;
+ uint8_t reserved[3];
+
+ // 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
+ // 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.
+ // Its situation is similar to the left, right, children "pointers". So we use
+ // atomic_uint_least32_t and release-consume ordering to protect it as well.
+
+ // We should also avoid rereading these fields redundantly, since not
+ // all processor implementations ensure that multiple loads from the
+ // same field are carried out in the right order.
+ atomic_uint_least32_t prop;
+
+ atomic_uint_least32_t left;
+ atomic_uint_least32_t right;
+
+ atomic_uint_least32_t children;
+
+ char name[0];
+
+ prop_bt(const char *name, const uint8_t name_length) {
+ this->namelen = name_length;
+ memcpy(this->name, name, name_length);
+ this->name[name_length] = '\0';
+ }
+
+private:
+ DISALLOW_COPY_AND_ASSIGN(prop_bt);
+};
+
+class prop_area {
+public:
+
+ prop_area(const uint32_t magic, const uint32_t version) :
+ magic_(magic), version_(version) {
+ atomic_init(&serial_, 0);
+ memset(reserved_, 0, sizeof(reserved_));
+ // Allocate enough space for the root node.
+ bytes_used_ = sizeof(prop_bt);
+ }
+
+ const prop_info *find(const char *name);
+ bool add(const char *name, unsigned int namelen,
+ const char *value, unsigned int valuelen);
+ const prop_info *del(const char *name);
+
+ bool foreach(void (*propfn)(const prop_info *pi, void *cookie), void *cookie);
+
+ atomic_uint_least32_t *serial() { return &serial_; }
+ uint32_t magic() const { return magic_; }
+ uint32_t version() const { return version_; }
+
+private:
+ void *allocate_obj(const size_t size, uint_least32_t *const off);
+ prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off);
+ prop_info *new_prop_info(const char *name, uint8_t namelen,
+ const char *value, uint8_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_info *to_prop_info(atomic_uint_least32_t *off_p);
+
+ prop_bt *root_node();
+
+ prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
+ uint8_t namelen, bool alloc_if_needed);
+
+ const prop_info *find_property(prop_bt *const trie, const char *name,
+ uint8_t namelen, const char *value,
+ uint8_t valuelen, bool alloc_if_needed);
+
+ const prop_info *find_property_and_del(prop_bt *const trie, const char *name);
+
+ bool foreach_property(prop_bt *const trie,
+ void (*propfn)(const prop_info *pi, void *cookie),
+ void *cookie);
+
+ uint32_t bytes_used_;
+ atomic_uint_least32_t serial_;
+ uint32_t magic_;
+ uint32_t version_;
+ uint32_t reserved_[28];
+ char data_[0];
+
+ DISALLOW_COPY_AND_ASSIGN(prop_area);
+};
+
+struct prop_info {
+ atomic_uint_least32_t serial;
+ char value[PROP_VALUE_MAX];
+ char name[0];
+
+ prop_info(const char *name, const uint8_t namelen, const char *value,
+ const uint8_t valuelen) {
+ memcpy(this->name, name, namelen);
+ this->name[namelen] = '\0';
+ atomic_init(&this->serial, valuelen << 24);
+ memcpy(this->value, value, valuelen);
+ this->value[valuelen] = '\0';
+ }
+private:
+ DISALLOW_COPY_AND_ASSIGN(prop_info);
+};
+
+struct find_nth_cookie {
+ uint32_t count;
+ const uint32_t n;
+ const prop_info *pi;
+
+ find_nth_cookie(uint32_t n) : count(0), n(n), pi(NULL) {
+ }
+};
+
+static char property_filename[PROP_FILENAME_MAX] = PROP_FILENAME;
+static bool compat_mode = false;
+static size_t pa_data_size;
+static size_t pa_size;
+static bool initialized = false;
+
+// NOTE: This isn't static because system_properties_compat.c
+// requires it.
+prop_area *__system_property_area__ = NULL;
+
+static int get_fd_from_env(void)
+{
+ // This environment variable consistes of two decimal integer
+ // values separated by a ",". The first value is a file descriptor
+ // and the second is the size of the system properties area. The
+ // size is currently unused.
+ char *env = getenv("ANDROID_PROPERTY_WORKSPACE");
+
+ if (!env) {
+ return -1;
+ }
+
+ return atoi(env);
+}
+
+static prop_area* map_prop_area_rw(const char* filename, const char* context,
+ bool* fsetxattr_failed) {
+ /* dev is a tmpfs that we can use to carve a shared workspace
+ * out of, so let's do that...
+ */
+ const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
+
+ if (fd < 0) {
+ if (errno == EACCES) {
+ /* for consistency with the case where the process has already
+ * mapped the page in and segfaults when trying to write to it
+ */
+ abort();
+ }
+ return nullptr;
+ }
+
+ if (context) {
+ if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc",
+ "fsetxattr failed to set context (%s) for \"%s\"", context, filename);
+ /*
+ * fsetxattr() will fail during system properties tests due to selinux policy.
+ * We do not want to create a custom policy for the tester, so we will continue in
+ * this function but set a flag that an error has occurred.
+ * Init, which is the only daemon that should ever call this function will abort
+ * when this error occurs.
+ * Otherwise, the tester will ignore it and continue, albeit without any selinux
+ * property separation.
+ */
+ if (fsetxattr_failed) {
+ *fsetxattr_failed = true;
+ }
+ }
+ }
+
+ if (ftruncate(fd, PA_SIZE) < 0) {
+ close(fd);
+ return nullptr;
+ }
+
+ pa_size = PA_SIZE;
+ pa_data_size = pa_size - sizeof(prop_area);
+ compat_mode = false;
+
+ void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+ if (memory_area == MAP_FAILED) {
+ close(fd);
+ return nullptr;
+ }
+
+ prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
+
+ close(fd);
+ return pa;
+}
+
+static prop_area* map_fd_ro(const int fd) {
+ struct stat fd_stat;
+ if (fstat(fd, &fd_stat) < 0) {
+ return nullptr;
+ }
+
+ if ((fd_stat.st_uid != 0)
+ || (fd_stat.st_gid != 0)
+ || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
+ || (fd_stat.st_size < static_cast(sizeof(prop_area))) ) {
+ return nullptr;
+ }
+
+ pa_size = fd_stat.st_size;
+ pa_data_size = pa_size - sizeof(prop_area);
+
+ void* const map_result = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // xsetprop changed PROT_READ to PROT_READ | PROT_WRITE
+ if (map_result == MAP_FAILED) {
+ return nullptr;
+ }
+
+ prop_area* pa = reinterpret_cast(map_result);
+ if ((pa->magic() != PROP_AREA_MAGIC) ||
+ (pa->version() != PROP_AREA_VERSION &&
+ pa->version() != PROP_AREA_VERSION_COMPAT)) {
+ munmap(pa, pa_size);
+ return nullptr;
+ }
+
+ if (pa->version() == PROP_AREA_VERSION_COMPAT) {
+ compat_mode = true;
+ }
+
+ return pa;
+}
+
+static prop_area* map_prop_area(const char* filename, bool is_legacy) {
+ int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDWR); // xsetprop changed O_RDONLY to O_RDWR
+ bool close_fd = true;
+ if (fd == -1 && errno == ENOENT && is_legacy) {
+ /*
+ * For backwards compatibility, if the file doesn't
+ * exist, we use the environment to get the file descriptor.
+ * For security reasons, we only use this backup if the kernel
+ * returns ENOENT. We don't want to use the backup if the kernel
+ * returns other errors such as ENOMEM or ENFILE, since it
+ * might be possible for an external program to trigger this
+ * condition.
+ * Only do this for the legacy prop file, secured prop files
+ * do not have a backup
+ */
+ fd = get_fd_from_env();
+ close_fd = false;
+ }
+
+ if (fd < 0) {
+ return nullptr;
+ }
+
+ prop_area* map_result = map_fd_ro(fd);
+ if (close_fd) {
+ close(fd);
+ }
+
+ return map_result;
+}
+
+void *prop_area::allocate_obj(const size_t size, uint_least32_t *const off)
+{
+ const size_t aligned = BIONIC_ALIGN(size, sizeof(uint_least32_t));
+ if (bytes_used_ + aligned > pa_data_size) {
+ return NULL;
+ }
+
+ *off = bytes_used_;
+ bytes_used_ += aligned;
+ return data_ + *off;
+}
+
+prop_bt *prop_area::new_prop_bt(const char *name, uint8_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 != NULL) {
+ prop_bt* bt = new(p) prop_bt(name, namelen);
+ *off = new_offset;
+ return bt;
+ }
+
+ return NULL;
+}
+
+prop_info *prop_area::new_prop_info(const char *name, uint8_t namelen,
+ const char *value, uint8_t valuelen, uint_least32_t *const off)
+{
+ uint_least32_t new_offset;
+ void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
+ if (p != NULL) {
+ prop_info* info = new(p) prop_info(name, namelen, value, valuelen);
+ *off = new_offset;
+ return info;
+ }
+
+ return NULL;
+}
+
+void *prop_area::to_prop_obj(uint_least32_t off)
+{
+ if (off > pa_data_size)
+ return NULL;
+
+ return (data_ + off);
+}
+
+inline prop_bt *prop_area::to_prop_bt(atomic_uint_least32_t* off_p) {
+ uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
+ return reinterpret_cast(to_prop_obj(off));
+}
+
+inline prop_info *prop_area::to_prop_info(atomic_uint_least32_t* off_p) {
+ uint_least32_t off = atomic_load_explicit(off_p, memory_order_consume);
+ return reinterpret_cast(to_prop_obj(off));
+}
+
+inline prop_bt *prop_area::root_node()
+{
+ return reinterpret_cast(to_prop_obj(0));
+}
+
+static int cmp_prop_name(const char *one, uint8_t one_len, const char *two,
+ uint8_t two_len)
+{
+ if (one_len < two_len)
+ return -1;
+ else if (one_len > two_len)
+ return 1;
+ else
+ return strncmp(one, two, one_len);
+}
+
+prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name,
+ uint8_t namelen, bool alloc_if_needed)
+{
+
+ prop_bt* current = bt;
+ while (true) {
+ if (!current) {
+ return NULL;
+ }
+
+ const int ret = cmp_prop_name(name, namelen, current->name, current->namelen);
+ if (ret == 0) {
+ return current;
+ }
+
+ 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);
+ } else {
+ if (!alloc_if_needed) {
+ return NULL;
+ }
+
+ uint_least32_t new_offset;
+ prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
+ if (new_bt) {
+ atomic_store_explicit(¤t->left, new_offset, memory_order_release);
+ }
+ return new_bt;
+ }
+ } else {
+ uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
+ if (right_offset != 0) {
+ current = to_prop_bt(¤t->right);
+ } else {
+ if (!alloc_if_needed) {
+ return NULL;
+ }
+
+ uint_least32_t new_offset;
+ prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
+ if (new_bt) {
+ atomic_store_explicit(¤t->right, new_offset, memory_order_release);
+ }
+ return new_bt;
+ }
+ }
+ }
+}
+
+const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
+ uint8_t namelen, const char *value, uint8_t valuelen,
+ bool alloc_if_needed)
+{
+ if (!trie) return NULL;
+
+ const char *remaining_name = name;
+ prop_bt* current = trie;
+ while (true) {
+ const char *sep = strchr(remaining_name, '.');
+ const bool want_subtree = (sep != NULL);
+ const uint8_t substr_size = (want_subtree) ?
+ sep - remaining_name : strlen(remaining_name);
+
+ if (!substr_size) {
+ return NULL;
+ }
+
+ prop_bt* root = NULL;
+ uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
+ if (children_offset != 0) {
+ root = to_prop_bt(¤t->children);
+ } else if (alloc_if_needed) {
+ uint_least32_t new_offset;
+ root = new_prop_bt(remaining_name, substr_size, &new_offset);
+ if (root) {
+ atomic_store_explicit(¤t->children, new_offset, memory_order_release);
+ }
+ }
+
+ if (!root) {
+ return NULL;
+ }
+
+ current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
+ if (!current) {
+ return NULL;
+ }
+
+ if (!want_subtree)
+ break;
+
+ remaining_name = sep + 1;
+ }
+
+ uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
+ if (prop_offset != 0) {
+ return to_prop_info(¤t->prop);
+ } else if (alloc_if_needed) {
+ uint_least32_t new_offset;
+ prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
+ if (new_info) {
+ atomic_store_explicit(¤t->prop, new_offset, memory_order_release);
+ }
+
+ return new_info;
+ } else {
+ return NULL;
+ }
+}
+
+const prop_info *prop_area::find_property_and_del(prop_bt *const trie, const char *name)
+{
+ if (!trie) return NULL;
+
+ const char *remaining_name = name;
+ prop_bt* current = trie;
+ while (true) {
+ const char *sep = strchr(remaining_name, '.');
+ const bool want_subtree = (sep != NULL);
+ const uint8_t substr_size = (want_subtree) ?
+ sep - remaining_name : strlen(remaining_name);
+
+ if (!substr_size) {
+ return NULL;
+ }
+
+ prop_bt* root = NULL;
+ uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
+ if (children_offset != 0) {
+ root = to_prop_bt(¤t->children);
+ }
+
+ if (!root) {
+ return NULL;
+ }
+
+ current = find_prop_bt(root, remaining_name, substr_size, false);
+ if (!current) {
+ return NULL;
+ }
+
+ if (!want_subtree)
+ break;
+
+ remaining_name = sep + 1;
+ }
+
+ uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
+ if (prop_offset != 0) {
+ printf("[bt] found at %p: name='%s' prop='%llu' left='%llu' right='%llu' children='%llu'\n",
+ static_cast(current), current->name,
+ static_cast(atomic_load_explicit(¤t->prop, memory_order_relaxed)),
+ static_cast(atomic_load_explicit(¤t->left, memory_order_relaxed)),
+ static_cast(atomic_load_explicit(¤t->right, memory_order_relaxed)),
+ static_cast(atomic_load_explicit(¤t->children, memory_order_relaxed))
+ );
+ atomic_store_explicit(¤t->prop, 0, memory_order_release);
+ return to_prop_info(¤t->prop);
+ } else {
+ printf("[bt] property not found\n");
+ return NULL;
+ }
+}
+
+static int send_prop_msg(const prop_msg *msg)
+{
+ const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (fd == -1) {
+ return -1;
+ }
+
+ const size_t namelen = strlen(property_service_socket);
+
+ sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+ strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
+ addr.sun_family = AF_LOCAL;
+ socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
+ if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast(&addr), alen)) < 0) {
+ close(fd);
+ return -1;
+ }
+
+ const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
+
+ int result = -1;
+ if (num_bytes == sizeof(prop_msg)) {
+ // We successfully wrote to the property server but now we
+ // wait for the property server to finish its work. It
+ // acknowledges its completion by closing the socket so we
+ // poll here (on nothing), waiting for the socket to close.
+ // If you 'adb shell setprop foo bar' you'll see the POLLHUP
+ // once the socket closes. Out of paranoia we cap our poll
+ // at 250 ms.
+ pollfd pollfds[1];
+ pollfds[0].fd = fd;
+ pollfds[0].events = 0;
+ const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
+ if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) {
+ result = 0;
+ } else {
+ // Ignore the timeout and treat it like a success anyway.
+ // The init process is single-threaded and its property
+ // service is sometimes slow to respond (perhaps it's off
+ // starting a child process or something) and thus this
+ // times out and the caller thinks it failed, even though
+ // it's still getting around to it. So we fake it here,
+ // mostly for ctl.* properties, but we do try and wait 250
+ // ms so callers who do read-after-write can reliably see
+ // what they've written. Most of the time.
+ // TODO: fix the system properties design.
+ result = 0;
+ }
+ }
+
+ close(fd);
+ return result;
+}
+
+static void find_nth_fn(const prop_info *pi, void *ptr)
+{
+ find_nth_cookie *cookie = reinterpret_cast(ptr);
+
+ if (cookie->n == cookie->count)
+ cookie->pi = pi;
+
+ cookie->count++;
+}
+
+bool prop_area::foreach_property(prop_bt *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);
+ if (err < 0)
+ return false;
+ }
+ uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
+ if (prop_offset != 0) {
+ prop_info *info = to_prop_info(&trie->prop);
+ if (!info)
+ return false;
+ propfn(info, cookie);
+ }
+ 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);
+ 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);
+ if (err < 0)
+ return false;
+ }
+
+ return true;
+}
+
+const prop_info *prop_area::find(const char *name) {
+ return find_property(root_node(), name, strlen(name), nullptr, 0, false);
+}
+
+bool prop_area::add(const char *name, unsigned int namelen,
+ const char *value, unsigned int valuelen) {
+ return find_property(root_node(), name, namelen, value, valuelen, true);
+}
+
+const prop_info *prop_area::del(const char *name) {
+ return find_property_and_del(root_node(), name);
+}
+
+bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
+ return foreach_property(root_node(), propfn, cookie);
+}
+
+class context_node {
+public:
+ context_node(context_node* next, const char* context, prop_area* pa)
+ : next(next), context_(strdup(context)), pa_(pa), no_access_(false) {
+ lock_.init(false);
+ }
+ ~context_node() {
+ unmap();
+ free(context_);
+ }
+ bool open(bool access_rw, bool* fsetxattr_failed);
+ bool check_access_and_open();
+ void reset_access();
+
+ const char* context() const { return context_; }
+ prop_area* pa() { return pa_; }
+
+ context_node* next;
+
+private:
+ bool check_access();
+ void unmap();
+
+ Lock lock_;
+ char* context_;
+ prop_area* pa_;
+ bool no_access_;
+};
+
+struct prefix_node {
+ prefix_node(struct prefix_node* next, const char* prefix, context_node* context)
+ : prefix(strdup(prefix)), prefix_len(strlen(prefix)), context(context), next(next) {
+ }
+ ~prefix_node() {
+ free(prefix);
+ }
+ char* prefix;
+ const size_t prefix_len;
+ context_node* context;
+ struct prefix_node* next;
+};
+
+template
+static inline void list_add(List** list, Args... args) {
+ *list = new List(*list, args...);
+}
+
+static void list_add_after_len(prefix_node** list, const char* prefix, context_node* context) {
+ size_t prefix_len = strlen(prefix);
+
+ auto next_list = list;
+
+ while (*next_list) {
+ if ((*next_list)->prefix_len < prefix_len || (*next_list)->prefix[0] == '*') {
+ list_add(next_list, prefix, context);
+ return;
+ }
+ next_list = &(*next_list)->next;
+ }
+ list_add(next_list, prefix, context);
+}
+
+template
+static void list_foreach(List* list, Func func) {
+ while (list) {
+ func(list);
+ list = list->next;
+ }
+}
+
+template
+static List* list_find(List* list, Func func) {
+ while (list) {
+ if (func(list)) {
+ return list;
+ }
+ list = list->next;
+ }
+ return nullptr;
+}
+
+template
+static void list_free(List** list) {
+ while (*list) {
+ auto old_list = *list;
+ *list = old_list->next;
+ delete old_list;
+ }
+}
+
+static prefix_node* prefixes = nullptr;
+static context_node* contexts = nullptr;
+
+/*
+ * pthread_mutex_lock() calls into system_properties in the case of contention.
+ * This creates a risk of dead lock if any system_properties functions
+ * use pthread locks after system_property initialization.
+ *
+ * For this reason, the below three functions use a bionic Lock and static
+ * allocation of memory for each filename.
+ */
+
+bool context_node::open(bool access_rw, bool* fsetxattr_failed) {
+ lock_.lock();
+ if (pa_) {
+ lock_.unlock();
+ return true;
+ }
+
+ char filename[PROP_FILENAME_MAX];
+ int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s",
+ property_filename, context_);
+ if (len < 0 || len > PROP_FILENAME_MAX) {
+ lock_.unlock();
+ return false;
+ }
+
+ if (access_rw) {
+ pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed);
+ } else {
+ pa_ = map_prop_area(filename, false);
+ }
+ lock_.unlock();
+ return pa_;
+}
+
+bool context_node::check_access_and_open() {
+ if (!pa_ && !no_access_) {
+ if (!check_access() || !open(false, nullptr)) {
+ no_access_ = true;
+ }
+ }
+ return pa_;
+}
+
+void context_node::reset_access() {
+ if (!check_access()) {
+ unmap();
+ no_access_ = true;
+ } else {
+ no_access_ = false;
+ }
+}
+
+bool context_node::check_access() {
+ char filename[PROP_FILENAME_MAX];
+ int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s",
+ property_filename, context_);
+ if (len < 0 || len > PROP_FILENAME_MAX) {
+ return false;
+ }
+
+ return access(filename, R_OK) == 0;
+}
+
+void context_node::unmap() {
+ if (!pa_) {
+ return;
+ }
+
+ munmap(pa_, pa_size);
+ if (pa_ == __system_property_area__) {
+ __system_property_area__ = nullptr;
+ }
+ pa_ = nullptr;
+}
+
+static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
+ char filename[PROP_FILENAME_MAX];
+ int len = __libc_format_buffer(filename, sizeof(filename),
+ "%s/properties_serial", property_filename);
+ if (len < 0 || len > PROP_FILENAME_MAX) {
+ __system_property_area__ = nullptr;
+ return false;
+ }
+
+ if (access_rw) {
+ __system_property_area__ =
+ map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
+ } else {
+ __system_property_area__ = map_prop_area(filename, false);
+ }
+ return __system_property_area__;
+}
+
+static prop_area* get_prop_area_for_name(const char* name) {
+ auto entry = list_find(prefixes, [name](prefix_node* l) {
+ return l->prefix[0] == '*' || !strncmp(l->prefix, name, l->prefix_len);
+ });
+ if (!entry) {
+ return nullptr;
+ }
+
+ auto cnode = entry->context;
+ if (!cnode->pa()) {
+ /*
+ * We explicitly do not check no_access_ in this case because unlike the
+ * case of foreach(), we want to generate an selinux audit for each
+ * non-permitted property access in this function.
+ */
+ cnode->open(false, nullptr);
+ }
+ return cnode->pa();
+}
+
+/*
+ * The below two functions are duplicated from label_support.c in libselinux.
+ * TODO: Find a location suitable for these functions such that both libc and
+ * libselinux can share a common source file.
+ */
+
+/*
+ * The read_spec_entries and read_spec_entry functions may be used to
+ * replace sscanf to read entries from spec files. The file and
+ * property services now use these.
+ */
+
+/* Read an entry from a spec file (e.g. file_contexts) */
+static inline int read_spec_entry(char **entry, char **ptr, int *len)
+{
+ *entry = NULL;
+ char *tmp_buf = NULL;
+
+ while (isspace(**ptr) && **ptr != '\0')
+ (*ptr)++;
+
+ tmp_buf = *ptr;
+ *len = 0;
+
+ while (!isspace(**ptr) && **ptr != '\0') {
+ (*ptr)++;
+ (*len)++;
+ }
+
+ if (*len) {
+ *entry = strndup(tmp_buf, *len);
+ if (!*entry)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * line_buf - Buffer containing the spec entries .
+ * num_args - The number of spec parameter entries to process.
+ * ... - A 'char **spec_entry' for each parameter.
+ * returns - The number of items processed.
+ *
+ * This function calls read_spec_entry() to do the actual string processing.
+ */
+static int read_spec_entries(char *line_buf, int num_args, ...)
+{
+ char **spec_entry, *buf_p;
+ int len, rc, items, entry_len = 0;
+ va_list ap;
+
+ len = strlen(line_buf);
+ if (line_buf[len - 1] == '\n')
+ line_buf[len - 1] = '\0';
+ else
+ /* Handle case if line not \n terminated by bumping
+ * the len for the check below (as the line is NUL
+ * terminated by getline(3)) */
+ len++;
+
+ buf_p = line_buf;
+ while (isspace(*buf_p))
+ buf_p++;
+
+ /* Skip comment lines and empty lines. */
+ if (*buf_p == '#' || *buf_p == '\0')
+ return 0;
+
+ /* Process the spec file entries */
+ va_start(ap, num_args);
+
+ items = 0;
+ while (items < num_args) {
+ spec_entry = va_arg(ap, char **);
+
+ if (len - 1 == buf_p - line_buf) {
+ va_end(ap);
+ return items;
+ }
+
+ rc = read_spec_entry(spec_entry, &buf_p, &entry_len);
+ if (rc < 0) {
+ va_end(ap);
+ return rc;
+ }
+ if (entry_len)
+ items++;
+ }
+ va_end(ap);
+ return items;
+}
+
+static bool initialize_properties() {
+ FILE* file = fopen("/property_contexts", "re");
+
+ if (!file) {
+ return false;
+ }
+
+ char* buffer = nullptr;
+ size_t line_len;
+ char* prop_prefix = nullptr;
+ char* context = nullptr;
+
+ while (getline(&buffer, &line_len, file) > 0) {
+ int items = read_spec_entries(buffer, 2, &prop_prefix, &context);
+ if (items <= 0) {
+ continue;
+ }
+ if (items == 1) {
+ free(prop_prefix);
+ continue;
+ }
+ /*
+ * init uses ctl.* properties as an IPC mechanism and does not write them
+ * to a property file, therefore we do not need to create property files
+ * to store them.
+ */
+ if (!strncmp(prop_prefix, "ctl.", 4)) {
+ free(prop_prefix);
+ free(context);
+ continue;
+ }
+
+ auto old_context = list_find(
+ contexts, [context](context_node* l) { return !strcmp(l->context(), context); });
+ if (old_context) {
+ list_add_after_len(&prefixes, prop_prefix, old_context);
+ } else {
+ list_add(&contexts, context, nullptr);
+ list_add_after_len(&prefixes, prop_prefix, contexts);
+ }
+ free(prop_prefix);
+ free(context);
+ }
+
+ free(buffer);
+ fclose(file);
+ return true;
+}
+
+static bool is_dir(const char* pathname) {
+ struct stat info;
+ if (stat(pathname, &info) == -1) {
+ return false;
+ }
+ return S_ISDIR(info.st_mode);
+}
+
+static void free_and_unmap_contexts() {
+ list_free(&prefixes);
+ list_free(&contexts);
+ if (__system_property_area__) {
+ munmap(__system_property_area__, pa_size);
+ __system_property_area__ = nullptr;
+ }
+}
+
+int __system_properties_init()
+{
+ if (initialized) {
+ //list_foreach(contexts, [](context_node* l) { l->reset_access(); }); // xsetprop removed
+ //return 0; // xsetprop removed
+ free_and_unmap_contexts(); // xsetprop added
+ initialized = false; // xsetprop added
+ }
+ if (is_dir(property_filename)) {
+ if (!initialize_properties()) {
+ return -1;
+ }
+ if (!map_system_property_area(false, nullptr)) {
+ free_and_unmap_contexts();
+ return -1;
+ }
+ } else {
+ __system_property_area__ = map_prop_area(property_filename, true);
+ if (!__system_property_area__) {
+ return -1;
+ }
+ list_add(&contexts, "legacy_system_prop_area", __system_property_area__);
+ list_add_after_len(&prefixes, "*", contexts);
+ }
+ initialized = true;
+ return 0;
+}
+
+int __system_property_set_filename(const char *filename)
+{
+ size_t len = strlen(filename);
+ if (len >= sizeof(property_filename))
+ return -1;
+
+ strcpy(property_filename, filename);
+ return 0;
+}
+
+int __system_property_area_init()
+{
+ free_and_unmap_contexts();
+ mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
+ if (!initialize_properties()) {
+ return -1;
+ }
+ bool open_failed = false;
+ bool fsetxattr_failed = false;
+ list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
+ if (!l->open(true, &fsetxattr_failed)) {
+ open_failed = true;
+ }
+ });
+ if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
+ free_and_unmap_contexts();
+ return -1;
+ }
+ initialized = true;
+ return fsetxattr_failed ? -2 : 0;
+}
+
+unsigned int __system_property_area_serial()
+{
+ prop_area *pa = __system_property_area__;
+ if (!pa) {
+ return -1;
+ }
+ // Make sure this read fulfilled before __system_property_serial
+ return atomic_load_explicit(pa->serial(), memory_order_acquire);
+}
+
+const prop_info *__system_property_find(const char *name)
+{
+ if (!__system_property_area__) {
+ return nullptr;
+ }
+
+ // if (__predict_false(compat_mode)) {
+ // return __system_property_find_compat(name);
+ // }
+
+ prop_area* pa = get_prop_area_for_name(name);
+ if (!pa) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
+ return nullptr;
+ }
+
+ return pa->find(name);
+}
+
+int __system_property_del(const char *name)
+{
+ if (!__system_property_area__) {
+ return -1;
+ }
+
+ prop_area* pa = get_prop_area_for_name(name);
+ if (!pa) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied deleting property \"%s\"", name);
+ return -1;
+ }
+
+ bool ret = pa->del(name);
+ if (!ret)
+ return -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(
+ __system_property_area__->serial(),
+ atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1,
+ memory_order_release);
+ __futex_wake(__system_property_area__->serial(), INT32_MAX);
+ return 0;
+
+}
+
+// The C11 standard doesn't allow atomic loads from const fields,
+// though C++11 does. Fudge it until standards get straightened out.
+static inline uint_least32_t load_const_atomic(const atomic_uint_least32_t* s,
+ memory_order mo) {
+ atomic_uint_least32_t* non_const_s = const_cast(s);
+ return atomic_load_explicit(non_const_s, mo);
+}
+
+int __system_property_read(const prop_info *pi, char *name, char *value)
+{
+ // if (__predict_false(compat_mode)) {
+ // return __system_property_read_compat(pi, name, value);
+ // }
+
+ while (true) {
+ uint32_t serial = __system_property_serial(pi); // acquire semantics
+ size_t len = SERIAL_VALUE_LEN(serial);
+ memcpy(value, pi->value, len + 1);
+ // TODO: Fix the synchronization scheme here.
+ // There is no fully supported way to implement this kind
+ // of synchronization in C++11, since the memcpy races with
+ // updates to pi, and the data being accessed is not atomic.
+ // The following fence is unintuitive, but would be the
+ // correct one if memcpy used memory_order_relaxed atomic accesses.
+ // In practice it seems unlikely that the generated code would
+ // would be any different, so this should be OK.
+ atomic_thread_fence(memory_order_acquire);
+ if (serial ==
+ load_const_atomic(&(pi->serial), memory_order_relaxed)) {
+ if (name != 0) {
+ strcpy(name, pi->name);
+ }
+ return len;
+ }
+ }
+}
+
+int __system_property_get(const char *name, char *value)
+{
+ const prop_info *pi = __system_property_find(name);
+
+ if (pi != 0) {
+ return __system_property_read(pi, 0, value);
+ } else {
+ value[0] = 0;
+ return 0;
+ }
+}
+
+int __system_property_set(const char *key, const char *value)
+{
+ if (key == 0) return -1;
+ if (value == 0) value = "";
+ if (strlen(key) >= PROP_NAME_MAX) return -1;
+ if (strlen(value) >= PROP_VALUE_MAX) return -1;
+
+ prop_msg msg;
+ memset(&msg, 0, sizeof msg);
+ msg.cmd = PROP_MSG_SETPROP;
+ strlcpy(msg.name, key, sizeof msg.name);
+ strlcpy(msg.value, value, sizeof msg.value);
+
+ const int err = send_prop_msg(&msg);
+ if (err < 0) {
+ return err;
+ }
+
+ return 0;
+}
+
+int __system_property_update(prop_info *pi, const char *value, unsigned int len)
+{
+ if (len >= PROP_VALUE_MAX)
+ return -1;
+
+ prop_area* pa = __system_property_area__;
+
+ if (!pa) {
+ return -1;
+ }
+
+ uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
+ serial |= 1;
+ atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
+ // The memcpy call here also races. Again pretend it
+ // used memory_order_relaxed atomics, and use the analogous
+ // counterintuitive fence.
+ atomic_thread_fence(memory_order_release);
+ memcpy(pi->value, value, len + 1);
+ atomic_store_explicit(
+ &pi->serial,
+ (len << 24) | ((serial + 1) & 0xffffff),
+ memory_order_release);
+ __futex_wake(&pi->serial, INT32_MAX);
+
+ atomic_store_explicit(
+ pa->serial(),
+ atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1,
+ memory_order_release);
+ __futex_wake(pa->serial(), INT32_MAX);
+
+ return 0;
+}
+
+int __system_property_add(const char *name, unsigned int namelen,
+ const char *value, unsigned int valuelen)
+{
+ if (namelen >= PROP_NAME_MAX)
+ return -1;
+ if (valuelen >= PROP_VALUE_MAX)
+ return -1;
+ if (namelen < 1)
+ return -1;
+
+ if (!__system_property_area__) {
+ return -1;
+ }
+
+ prop_area* pa = get_prop_area_for_name(name);
+
+ if (!pa) {
+ __libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
+ return -1;
+ }
+
+ bool ret = pa->add(name, namelen, value, valuelen);
+ if (!ret)
+ return -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(
+ __system_property_area__->serial(),
+ atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1,
+ memory_order_release);
+ __futex_wake(__system_property_area__->serial(), INT32_MAX);
+ return 0;
+}
+
+// Wait for non-locked serial, and retrieve it with acquire semantics.
+unsigned int __system_property_serial(const prop_info *pi)
+{
+ uint32_t serial = load_const_atomic(&pi->serial, memory_order_acquire);
+ while (SERIAL_DIRTY(serial)) {
+ __futex_wait(const_cast(
+ reinterpret_cast(&pi->serial)),
+ serial, NULL);
+ serial = load_const_atomic(&pi->serial, memory_order_acquire);
+ }
+ return serial;
+}
+
+unsigned int __system_property_wait_any(unsigned int serial)
+{
+ prop_area *pa = __system_property_area__;
+ uint32_t my_serial;
+
+ if (!pa) {
+ return 0;
+ }
+
+ do {
+ __futex_wait(pa->serial(), serial, NULL);
+ my_serial = atomic_load_explicit(pa->serial(), memory_order_acquire);
+ } while (my_serial == serial);
+
+ return my_serial;
+}
+
+const prop_info *__system_property_find_nth(unsigned n)
+{
+ find_nth_cookie cookie(n);
+
+ const int err = __system_property_foreach(find_nth_fn, &cookie);
+ if (err < 0) {
+ return NULL;
+ }
+
+ return cookie.pi;
+}
+
+int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
+ void *cookie)
+{
+ if (!__system_property_area__) {
+ return -1;
+ }
+
+ // if (__predict_false(compat_mode)) {
+ // return __system_property_foreach_compat(propfn, cookie);
+ // }
+
+ list_foreach(contexts, [propfn, cookie](context_node* l) {
+ if (l->check_access_and_open()) {
+ l->pa()->foreach(propfn, cookie);
+ }
+ });
+ return 0;
+}