Merge "Set up user directory crypto in init." into mnc-dr-dev
diff --git a/adb/Android.mk b/adb/Android.mk
index 1613a88..425bf9b 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -232,12 +232,11 @@
-D_GNU_SOURCE \
-Wno-deprecated-declarations \
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
-endif
+LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
+LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
endif
LOCAL_MODULE := adbd
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 8a7b9c9..f64b19f 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -421,9 +421,9 @@
parse_banner(reinterpret_cast<const char*>(p->data), t);
- if (HOST || !auth_enabled) {
+ if (HOST || !auth_required) {
handle_online(t);
- if(!HOST) send_connect(t);
+ if (!HOST) send_connect(t);
} else {
send_auth_request(t);
}
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
index dc01825..cff26d6 100644
--- a/adb/adb_auth.cpp
+++ b/adb/adb_auth.cpp
@@ -28,7 +28,7 @@
#include "adb.h"
#include "transport.h"
-int auth_enabled = 0;
+bool auth_required = true;
void send_auth_request(atransport *t)
{
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 1e1978d..a13604a 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -19,7 +19,7 @@
#include "adb.h"
-extern int auth_enabled;
+extern bool auth_required;
int adb_auth_keygen(const char* filename);
void adb_auth_verified(atransport *t);
diff --git a/adb/adb_main.cpp b/adb/adb_main.cpp
index 3f88d13..45a2158 100644
--- a/adb/adb_main.cpp
+++ b/adb/adb_main.cpp
@@ -239,10 +239,11 @@
// descriptor will always be open.
adbd_cloexec_auth_socket();
- property_get("ro.adb.secure", value, "0");
- auth_enabled = !strcmp(value, "1");
- if (auth_enabled)
- adbd_auth_init();
+ if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
+ auth_required = false;
+ }
+
+ adbd_auth_init();
// Our external storage path may be different than apps, since
// we aren't able to bind mount after dropping root.
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index e8e9a0f..5d17335 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -57,7 +57,7 @@
if(x == 0) return 0;
*x = 0;
if (should_use_fs_config(name)) {
- fs_config(name, 1, &uid, &gid, &mode, &cap);
+ fs_config(name, 1, NULL, &uid, &gid, &mode, &cap);
}
ret = adb_mkdir(name, mode);
if((ret < 0) && (errno != EEXIST)) {
@@ -366,7 +366,7 @@
tmp++;
}
if (should_use_fs_config(path)) {
- fs_config(tmp, 0, &uid, &gid, &mode, &cap);
+ fs_config(tmp, 0, NULL, &uid, &gid, &mode, &cap);
}
return handle_send_file(s, path, uid, gid, mode, buffer, do_unlink);
}
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index 7175749..0e35323 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -41,6 +41,7 @@
};
static struct fs_config_entry* canned_config = NULL;
+static char *target_out_path = NULL;
/* Each line in the canned file should be a path plus three ints (uid,
* gid, mode). */
@@ -79,7 +80,8 @@
} else {
// Use the compiled-in fs_config() function.
unsigned st_mode = s->st_mode;
- fs_config(path, S_ISDIR(s->st_mode), &s->st_uid, &s->st_gid, &st_mode, &capabilities);
+ fs_config(path, S_ISDIR(s->st_mode), target_out_path,
+ &s->st_uid, &s->st_gid, &st_mode, &capabilities);
s->st_mode = (typeof(s->st_mode)) st_mode;
}
}
@@ -328,6 +330,12 @@
argc--;
argv++;
+ if (argc > 1 && strcmp(argv[0], "-d") == 0) {
+ target_out_path = argv[1];
+ argc -= 2;
+ argv += 2;
+ }
+
if (argc > 1 && strcmp(argv[0], "-f") == 0) {
read_canned_config(argv[1]);
argc -= 2;
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
index a55f30a..c310160 100644
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -134,6 +134,10 @@
return mDevice->pre_enroll(mDevice);
}
+int32_t FingerprintDaemonProxy::postEnroll() {
+ return mDevice->post_enroll(mDevice);
+}
+
int32_t FingerprintDaemonProxy::stopEnrollment() {
ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
return mDevice->cancel(mDevice);
@@ -160,8 +164,8 @@
int32_t FingerprintDaemonProxy::setActiveGroup(int32_t groupId, const uint8_t* path,
ssize_t pathlen) {
- if (pathlen >= PATH_MAX) {
- ALOGE("Path name is too long\n");
+ if (pathlen >= PATH_MAX || pathlen <= 0) {
+ ALOGE("Bad path length: %zd", pathlen);
return -1;
}
// Convert to null-terminated string
@@ -170,7 +174,6 @@
path_name[pathlen] = '\0';
ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, path_name, pathlen);
return mDevice->set_active_group(mDevice, groupId, path_name);
- return -1;
}
int64_t FingerprintDaemonProxy::openHal() {
diff --git a/fingerprintd/FingerprintDaemonProxy.h b/fingerprintd/FingerprintDaemonProxy.h
index 50d30ef..871c0e6 100644
--- a/fingerprintd/FingerprintDaemonProxy.h
+++ b/fingerprintd/FingerprintDaemonProxy.h
@@ -35,6 +35,7 @@
virtual void init(const sp<IFingerprintDaemonCallback>& callback);
virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId, int32_t timeout);
virtual uint64_t preEnroll();
+ virtual int32_t postEnroll();
virtual int32_t stopEnrollment();
virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId);
virtual int32_t stopAuthentication();
diff --git a/fingerprintd/IFingerprintDaemon.cpp b/fingerprintd/IFingerprintDaemon.cpp
index 5f9d30c..7131793 100644
--- a/fingerprintd/IFingerprintDaemon.cpp
+++ b/fingerprintd/IFingerprintDaemon.cpp
@@ -103,6 +103,16 @@
reply->writeInt64(ret);
return NO_ERROR;
}
+ case POST_ENROLL: {
+ CHECK_INTERFACE(IFingerprintDaemon, data, reply);
+ if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
+ return PERMISSION_DENIED;
+ }
+ const int32_t ret = postEnroll();
+ reply->writeNoException();
+ reply->writeInt32(ret);
+ return NO_ERROR;
+ }
case REMOVE: {
CHECK_INTERFACE(IFingerprintDaemon, data, reply);
if (!checkPermission(HAL_FINGERPRINT_PERMISSION)) {
diff --git a/fingerprintd/IFingerprintDaemon.h b/fingerprintd/IFingerprintDaemon.h
index 08cb008..1eb4ac1 100644
--- a/fingerprintd/IFingerprintDaemon.h
+++ b/fingerprintd/IFingerprintDaemon.h
@@ -43,6 +43,7 @@
OPEN_HAL = IBinder::FIRST_CALL_TRANSACTION + 8,
CLOSE_HAL = IBinder::FIRST_CALL_TRANSACTION + 9,
INIT = IBinder::FIRST_CALL_TRANSACTION + 10,
+ POST_ENROLL = IBinder::FIRST_CALL_TRANSACTION + 11,
};
IFingerprintDaemon() { }
@@ -54,6 +55,7 @@
virtual int32_t enroll(const uint8_t* token, ssize_t tokenLength, int32_t groupId,
int32_t timeout) = 0;
virtual uint64_t preEnroll() = 0;
+ virtual int32_t postEnroll() = 0;
virtual int32_t stopEnrollment() = 0;
virtual int32_t authenticate(uint64_t sessionId, uint32_t groupId) = 0;
virtual int32_t stopAuthentication() = 0;
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index 95c6a74..c73045d 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -52,7 +52,7 @@
info.len = ((off64_t)nr_sec * 512);
/* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
- rc = make_ext4fs_internal(fd, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
+ rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
if (rc) {
ERROR("make_ext4fs returned %d.\n", rc);
}
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index cc8c57e..2d1abbe 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -767,8 +767,24 @@
static int load_verity_state(struct fstab_rec *fstab, int *mode)
{
- off64_t offset = 0;
+ char propbuf[PROPERTY_VALUE_MAX];
int match = 0;
+ off64_t offset = 0;
+
+ /* use the kernel parameter if set */
+ property_get("ro.boot.veritymode", propbuf, "");
+
+ if (*propbuf != '\0') {
+ if (!strcmp(propbuf, "enforcing")) {
+ *mode = VERITY_MODE_DEFAULT;
+ return 0;
+ } else if (!strcmp(propbuf, "logging")) {
+ *mode = VERITY_MODE_LOGGING;
+ return 0;
+ } else {
+ INFO("Unknown value %s for veritymode; ignoring", propbuf);
+ }
+ }
if (get_verity_state_offset(fstab, &offset) < 0) {
/* fall back to stateless behavior */
@@ -855,6 +871,13 @@
struct dm_ioctl *io = (struct dm_ioctl *) buffer;
struct fstab *fstab = NULL;
+ /* check if we need to store the state */
+ property_get("ro.boot.veritymode", propbuf, "");
+
+ if (*propbuf != '\0') {
+ return 0; /* state is kept by the bootloader */
+ }
+
fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
if (fd == -1) {
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index e554411..75fe11d 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -20,6 +20,8 @@
extern "C" {
#include <openssl/rand.h>
+#include <openssl/sha.h>
+
#include <crypto_scrypt.h>
}
@@ -30,6 +32,10 @@
namespace gatekeeper {
+struct fast_hash_t {
+ uint64_t salt;
+ uint8_t digest[SHA256_DIGEST_LENGTH];
+};
class SoftGateKeeper : public GateKeeper {
public:
@@ -125,9 +131,48 @@
return true;
}
+ fast_hash_t ComputeFastHash(const SizedBuffer &password, uint64_t salt) {
+ fast_hash_t fast_hash;
+ size_t digest_size = password.length + sizeof(salt);
+ std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
+ memcpy(digest.get(), &salt, sizeof(salt));
+ memcpy(digest.get() + sizeof(salt), password.buffer.get(), password.length);
+
+ SHA256(digest.get(), digest_size, (uint8_t *) &fast_hash.digest);
+
+ fast_hash.salt = salt;
+ return fast_hash;
+ }
+
+ bool VerifyFast(const fast_hash_t &fast_hash, const SizedBuffer &password) {
+ fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
+ return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
+ }
+
+ bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
+ FastHashMap::const_iterator it = fast_hash_map_.find(expected_handle->user_id);
+ if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
+ return true;
+ } else {
+ if (GateKeeper::DoVerify(expected_handle, password)) {
+ uint64_t salt;
+ GetRandom(&salt, sizeof(salt));
+ fast_hash_map_[expected_handle->user_id] = ComputeFastHash(password, salt);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private:
+
+ typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
+ typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
+
UniquePtr<uint8_t[]> key_;
- std::unordered_map<uint32_t, failure_record_t> failure_map_;
+ FailureRecordMap failure_map_;
+ FastHashMap fast_hash_map_;
};
}
diff --git a/gatekeeperd/SoftGateKeeperDevice.h b/gatekeeperd/SoftGateKeeperDevice.h
index 51a8511..3463c29 100644
--- a/gatekeeperd/SoftGateKeeperDevice.h
+++ b/gatekeeperd/SoftGateKeeperDevice.h
@@ -68,7 +68,7 @@
const uint8_t *provided_password, uint32_t provided_password_length,
uint8_t **auth_token, uint32_t *auth_token_length, bool *request_reenroll);
private:
- UniquePtr<GateKeeper> impl_;
+ UniquePtr<SoftGateKeeper> impl_;
};
} // namespace gatekeeper
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index c0f2279..9788681 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -31,6 +31,7 @@
#include <binder/IServiceManager.h>
#include <binder/PermissionCache.h>
#include <utils/String16.h>
+#include <utils/Log.h>
#include <keystore/IKeystoreService.h>
#include <keystore/keystore.h> // For error code
@@ -49,6 +50,8 @@
public:
GateKeeperProxy() {
int ret = hw_get_module_by_class(GATEKEEPER_HARDWARE_MODULE_ID, NULL, &module);
+ device = NULL;
+
if (ret < 0) {
ALOGW("falling back to software GateKeeper");
soft_device.reset(new SoftGateKeeperDevice());
@@ -57,6 +60,13 @@
if (ret < 0)
LOG_ALWAYS_FATAL_IF(ret < 0, "Unable to open GateKeeper HAL");
}
+
+ if (mark_cold_boot()) {
+ ALOGI("cold boot: clearing state");
+ if (device != NULL && device->delete_all_users != NULL) {
+ device->delete_all_users(device);
+ }
+ }
}
virtual ~GateKeeperProxy() {
@@ -75,6 +85,20 @@
close(fd);
}
+ bool mark_cold_boot() {
+ const char *filename = ".coldboot";
+ if (access(filename, F_OK) == -1) {
+ int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ ALOGE("could not open file: %s : %s", filename, strerror(errno));
+ return false;
+ }
+ close(fd);
+ return true;
+ }
+ return false;
+ }
+
void maybe_store_sid(uint32_t uid, uint64_t sid) {
char filename[21];
sprintf(filename, "%u", uid);
@@ -90,6 +114,7 @@
int fd = open(filename, O_RDONLY);
if (fd < 0) return 0;
read(fd, &sid, sizeof(sid));
+ close(fd);
return sid;
}
@@ -119,8 +144,19 @@
int ret;
if (device) {
- ret = device->enroll(device, uid,
- current_password_handle, current_password_handle_length,
+ const gatekeeper::password_handle_t *handle =
+ reinterpret_cast<const gatekeeper::password_handle_t *>(current_password_handle);
+
+ if (handle != NULL && handle->version != 0 && !handle->hardware_backed) {
+ // handle is being re-enrolled from a software version. HAL probably won't accept
+ // the handle as valid, so we nullify it and enroll from scratch
+ current_password_handle = NULL;
+ current_password_handle_length = 0;
+ current_password = NULL;
+ current_password_length = 0;
+ }
+
+ ret = device->enroll(device, uid, current_password_handle, current_password_handle_length,
current_password, current_password_length,
desired_password, desired_password_length,
enrolled_password_handle, enrolled_password_handle_length);
@@ -174,10 +210,28 @@
int ret;
if (device) {
- ret = device->verify(device, uid, challenge,
- enrolled_password_handle, enrolled_password_handle_length,
- provided_password, provided_password_length, auth_token, auth_token_length,
- request_reenroll);
+ const gatekeeper::password_handle_t *handle =
+ reinterpret_cast<const gatekeeper::password_handle_t *>(enrolled_password_handle);
+ // handle version 0 does not have hardware backed flag, and thus cannot be upgraded to
+ // a HAL if there was none before
+ if (handle->version == 0 || handle->hardware_backed) {
+ ret = device->verify(device, uid, challenge,
+ enrolled_password_handle, enrolled_password_handle_length,
+ provided_password, provided_password_length, auth_token, auth_token_length,
+ request_reenroll);
+ } else {
+ // upgrade scenario, a HAL has been added to this device where there was none before
+ SoftGateKeeperDevice soft_dev;
+ ret = soft_dev.verify(uid, challenge,
+ enrolled_password_handle, enrolled_password_handle_length,
+ provided_password, provided_password_length, auth_token, auth_token_length,
+ request_reenroll);
+
+ if (ret == 0) {
+ // success! re-enroll with HAL
+ *request_reenroll = true;
+ }
+ }
} else {
ret = soft_device->verify(uid, challenge,
enrolled_password_handle, enrolled_password_handle_length,
@@ -221,6 +275,10 @@
return;
}
clear_sid(uid);
+
+ if (device != NULL && device->delete_user != NULL) {
+ device->delete_user(device, uid);
+ }
}
virtual status_t dump(int fd, const Vector<String16> &) {
diff --git a/gatekeeperd/tests/gatekeeper_test.cpp b/gatekeeperd/tests/gatekeeper_test.cpp
index 15b2b69..c504f92 100644
--- a/gatekeeperd/tests/gatekeeper_test.cpp
+++ b/gatekeeperd/tests/gatekeeper_test.cpp
@@ -14,9 +14,11 @@
* limitations under the License.
*/
+#include <arpa/inet.h>
+#include <iostream>
+
#include <gtest/gtest.h>
#include <UniquePtr.h>
-#include <iostream>
#include <hardware/hw_auth_token.h>
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 02fe2b5..2ed27dc 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -206,13 +206,13 @@
* Used in:
* build/tools/fs_config/fs_config.c
* build/tools/fs_get_stats/fs_get_stats.c
- * external/genext2fs/genext2fs.c
+ * system/extras/ext4_utils/make_ext4fs_main.c
* external/squashfs-tools/squashfs-tools/android.c
* system/core/cpio/mkbootfs.c
* system/core/adb/file_sync_service.cpp
* system/extras/ext4_utils/canned_fs_config.c
*/
-void fs_config(const char *path, int dir,
+void fs_config(const char *path, int dir, const char *target_out_path,
unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities);
ssize_t fs_config_generate(char *buffer, size_t length, const struct fs_path_config *pc);
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 386a390..3b00683 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -153,7 +153,9 @@
* Returns 0 on success and negative values on failure.
*/
int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
- const ZipEntryName* optional_prefix);
+ const ZipEntryName* optional_prefix,
+ // TODO: Remove the default parameter.
+ const ZipEntryName* optional_suffix = NULL);
/*
* Advance to the next element in the zipfile in iteration order.
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 335f371..8eb5b5b 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -803,9 +803,9 @@
return -1;
}
-int do_load_all_props(int nargs, char **args) {
+int do_load_system_props(int nargs, char **args) {
if (nargs == 1) {
- load_all_props();
+ load_system_props();
return 0;
}
return -1;
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 35b22d7..62e8b10 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -159,7 +159,7 @@
case 'l':
if (!strcmp(s, "oglevel")) return K_loglevel;
if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
- if (!strcmp(s, "oad_all_props")) return K_load_all_props;
+ if (!strcmp(s, "oad_system_props")) return K_load_system_props;
break;
case 'm':
if (!strcmp(s, "kdir")) return K_mkdir;
diff --git a/init/keywords.h b/init/keywords.h
index d5c7667..0910f60 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -35,7 +35,7 @@
int do_chmod(int nargs, char **args);
int do_loglevel(int nargs, char **args);
int do_load_persist_props(int nargs, char **args);
-int do_load_all_props(int nargs, char **args);
+int do_load_system_props(int nargs, char **args);
int do_verity_load_state(int nargs, char **args);
int do_verity_update_state(int nargs, char **args);
int do_wait(int nargs, char **args);
@@ -67,7 +67,7 @@
KEYWORD(installkey, COMMAND, 1, do_installkey)
KEYWORD(ioprio, OPTION, 0, 0)
KEYWORD(keycodes, OPTION, 0, 0)
- KEYWORD(load_all_props, COMMAND, 0, do_load_all_props)
+ KEYWORD(load_system_props, COMMAND, 0, do_load_system_props)
KEYWORD(load_persist_props, COMMAND, 0, do_load_persist_props)
KEYWORD(loglevel, COMMAND, 1, do_loglevel)
KEYWORD(mkdir, COMMAND, 1, do_mkdir)
diff --git a/init/property_service.cpp b/init/property_service.cpp
index c2881ae..52f6b98 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -560,16 +560,10 @@
close(fd);
}
-void load_all_props() {
+void load_system_props() {
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
-
- load_override_properties();
-
- /* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();
-
load_recovery_id_prop();
}
diff --git a/init/property_service.h b/init/property_service.h
index a27053d..303f251 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -23,7 +23,7 @@
extern void property_init(void);
extern void property_load_boot_defaults(void);
extern void load_persist_props(void);
-extern void load_all_props(void);
+extern void load_system_props(void);
extern void start_property_service(void);
void get_property_workspace(int *fd, int *sz);
extern int __property_get(const char *name, char *value);
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 9f8023e..9a1ad19 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -149,14 +149,21 @@
{ 00644, AID_ROOT, AID_ROOT, 0, 0 },
};
-static int fs_config_open(int dir)
+static int fs_config_open(int dir, const char *target_out_path)
{
int fd = -1;
- const char *out = getenv("OUT");
- if (out && *out) {
+ if (target_out_path && *target_out_path) {
+ /* target_out_path is the path to the directory holding content of system partition
+ but as we cannot guaranty it ends with '/system' we need this below skip_len logic */
char *name = NULL;
- asprintf(&name, "%s%s", out, dir ? conf_dir : conf_file);
+ int target_out_path_len = strlen(target_out_path);
+ int skip_len = strlen("/system");
+
+ if (target_out_path[target_out_path_len] == '/') {
+ skip_len++;
+ }
+ asprintf(&name, "%s%s", target_out_path, (dir ? conf_dir : conf_file) + skip_len);
if (name) {
fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
free(name);
@@ -187,7 +194,7 @@
return !strncmp(prefix, path, len);
}
-void fs_config(const char *path, int dir,
+void fs_config(const char *path, int dir, const char *target_out_path,
unsigned *uid, unsigned *gid, unsigned *mode, uint64_t *capabilities)
{
const struct fs_path_config *pc;
@@ -199,7 +206,7 @@
plen = strlen(path);
- fd = fs_config_open(dir);
+ fd = fs_config_open(dir, target_out_path);
if (fd >= 0) {
struct fs_path_config_from_file header;
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index a7ff85e..83222f4 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -50,6 +50,7 @@
// timer slack value in nS enforced when the thread moves to background
#define TIMER_SLACK_BG 40000000
+#define TIMER_SLACK_FG 50000
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
@@ -269,10 +270,7 @@
return -errno;
}
- // we do both setting of cpuset and setting of cgroup
- // ensures that backgrounded apps are actually deprioritized
- // including on core 0
- return set_sched_policy(tid, policy);
+ return 0;
#endif
}
@@ -356,7 +354,8 @@
¶m);
}
- prctl(PR_SET_TIMERSLACK_PID, policy == SP_BACKGROUND ? TIMER_SLACK_BG : 0, tid);
+ prctl(PR_SET_TIMERSLACK_PID,
+ policy == SP_BACKGROUND ? TIMER_SLACK_BG : TIMER_SLACK_FG, tid);
return 0;
}
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index ee4ebe7..23a0290 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -19,6 +19,7 @@
#include <pthread.h>
#include <semaphore.h>
#include <stddef.h>
+#include <stdbool.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -38,7 +39,7 @@
static pthread_t suspend_thread;
static sem_t suspend_lockout;
static const char *sleep_state = "mem";
-static void (*wakeup_func)(void) = NULL;
+static void (*wakeup_func)(bool success) = NULL;
static void *suspend_thread_func(void *arg __attribute__((unused)))
{
@@ -46,6 +47,7 @@
char wakeup_count[20];
int wakeup_count_len;
int ret;
+ bool success;
while (1) {
usleep(100000);
@@ -72,6 +74,7 @@
continue;
}
+ success = true;
ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
if (ret < 0) {
@@ -81,13 +84,11 @@
ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
if (ret < 0) {
- strerror_r(errno, buf, sizeof(buf));
- ALOGE("Error writing to %s: %s\n", SYS_POWER_STATE, buf);
- } else {
- void (*func)(void) = wakeup_func;
- if (func != NULL) {
- (*func)();
- }
+ success = false;
+ }
+ void (*func)(bool success) = wakeup_func;
+ if (func != NULL) {
+ (*func)(success);
}
}
@@ -139,7 +140,7 @@
return ret;
}
-void set_wakeup_callback(void (*func)(void))
+void set_wakeup_callback(void (*func)(bool success))
{
if (wakeup_func != NULL) {
ALOGE("Duplicate wakeup callback applied, keeping original");
diff --git a/libsuspend/include/suspend/autosuspend.h b/libsuspend/include/suspend/autosuspend.h
index 10e3d27..59188a8 100644
--- a/libsuspend/include/suspend/autosuspend.h
+++ b/libsuspend/include/suspend/autosuspend.h
@@ -18,6 +18,7 @@
#define _LIBSUSPEND_AUTOSUSPEND_H_
#include <sys/cdefs.h>
+#include <stdbool.h>
__BEGIN_DECLS
@@ -46,9 +47,11 @@
/*
* set_wakeup_callback
*
- * Set a function to be called each time the device wakes up from suspend.
+ * Set a function to be called each time the device returns from suspend.
+ * success is true if the suspend was sucessful and false if the suspend
+ * aborted due to some reason.
*/
-void set_wakeup_callback(void (*func)(void));
+void set_wakeup_callback(void (*func)(bool success));
__END_DECLS
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index ef30017..2347028 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -455,18 +455,20 @@
SLOGE("Invalid optlen %d for RDNSS option\n", optlen);
return false;
}
- int numaddrs = (optlen - 1) / 2;
+ const int numaddrs = (optlen - 1) / 2;
// Find the lifetime.
struct nd_opt_rdnss *rndss_opt = (struct nd_opt_rdnss *) opthdr;
- uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
+ const uint32_t lifetime = ntohl(rndss_opt->nd_opt_rdnss_lifetime);
// Construct "SERVERS=<comma-separated string of DNS addresses>".
- // Reserve (INET6_ADDRSTRLEN + 1) chars for each address: all but the
- // the last address are followed by ','; the last is followed by '\0'.
static const char kServerTag[] = "SERVERS=";
- static const int kTagLength = sizeof(kServerTag) - 1;
- int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
+ static const int kTagLength = strlen(kServerTag);
+ // Reserve sufficient space for an IPv6 link-local address: all but the
+ // last address are followed by ','; the last is followed by '\0'.
+ static const int kMaxSingleAddressLength =
+ INET6_ADDRSTRLEN + strlen("%") + IFNAMSIZ + strlen(",");
+ const int bufsize = kTagLength + numaddrs * (INET6_ADDRSTRLEN + 1);
char *buf = (char *) malloc(bufsize);
if (!buf) {
SLOGE("RDNSS option: out of memory\n");
@@ -482,6 +484,10 @@
}
inet_ntop(AF_INET6, addrs + i, buf + pos, bufsize - pos);
pos += strlen(buf + pos);
+ if (IN6_IS_ADDR_LINKLOCAL(addrs + i)) {
+ buf[pos++] = '%';
+ pos += strlcpy(buf + pos, ifname, bufsize - pos);
+ }
}
buf[pos] = '\0';
diff --git a/libusbhost/usbhost.c b/libusbhost/usbhost.c
index 40b8b9f..b8e3215 100644
--- a/libusbhost/usbhost.c
+++ b/libusbhost/usbhost.c
@@ -56,6 +56,9 @@
#define USB_FS_ID_SCANNER USB_FS_DIR "/%d/%d"
#define USB_FS_ID_FORMAT USB_FS_DIR "/%03d/%03d"
+// Some devices fail to send string descriptors if we attempt reading > 255 bytes
+#define MAX_STRING_DESCRIPTOR_LENGTH 255
+
// From drivers/usb/core/devio.c
// I don't know why this isn't in a kernel header
#define MAX_USBFS_BUFFER_SIZE 16384
@@ -449,8 +452,8 @@
char* usb_device_get_string(struct usb_device *device, int id)
{
char string[256];
- __u16 buffer[128];
- __u16 languages[128];
+ __u16 buffer[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
+ __u16 languages[MAX_STRING_DESCRIPTOR_LENGTH / sizeof(__u16)];
int i, result;
int languageCount = 0;
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index b2a9f88..cc39aa5 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -852,25 +852,38 @@
// We're not using vector here because this code is used in the Windows SDK
// where the STL is not available.
const uint8_t* prefix;
- uint16_t prefix_len;
+ const uint16_t prefix_len;
+ const uint8_t* suffix;
+ const uint16_t suffix_len;
ZipArchive* archive;
- IterationHandle() : prefix(NULL), prefix_len(0) {}
-
- IterationHandle(const ZipEntryName& prefix_name)
- : prefix_len(prefix_name.name_length) {
- uint8_t* prefix_copy = new uint8_t[prefix_len];
- memcpy(prefix_copy, prefix_name.name, prefix_len);
- prefix = prefix_copy;
+ IterationHandle(const ZipEntryName* prefix_name,
+ const ZipEntryName* suffix_name)
+ : prefix(NULL),
+ prefix_len(prefix_name ? prefix_name->name_length : 0),
+ suffix(NULL),
+ suffix_len(suffix_name ? suffix_name->name_length : 0) {
+ if (prefix_name) {
+ uint8_t* prefix_copy = new uint8_t[prefix_len];
+ memcpy(prefix_copy, prefix_name->name, prefix_len);
+ prefix = prefix_copy;
+ }
+ if (suffix_name) {
+ uint8_t* suffix_copy = new uint8_t[suffix_len];
+ memcpy(suffix_copy, suffix_name->name, suffix_len);
+ suffix = suffix_copy;
+ }
}
~IterationHandle() {
delete[] prefix;
+ delete[] suffix;
}
};
int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
- const ZipEntryName* optional_prefix) {
+ const ZipEntryName* optional_prefix,
+ const ZipEntryName* optional_suffix) {
ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
if (archive == NULL || archive->hash_table == NULL) {
@@ -878,8 +891,7 @@
return kInvalidHandle;
}
- IterationHandle* cookie =
- optional_prefix != NULL ? new IterationHandle(*optional_prefix) : new IterationHandle();
+ IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
cookie->position = 0;
cookie->archive = archive;
@@ -929,7 +941,13 @@
for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
if (hash_table[i].name != NULL &&
(handle->prefix_len == 0 ||
- (memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0))) {
+ (hash_table[i].name_length >= handle->prefix_len &&
+ memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0)) &&
+ (handle->suffix_len == 0 ||
+ (hash_table[i].name_length >= handle->suffix_len &&
+ memcmp(handle->suffix,
+ hash_table[i].name + hash_table[i].name_length - handle->suffix_len,
+ handle->suffix_len) == 0))) {
handle->position = (i + 1);
const int error = FindEntry(archive, i, data);
if (!error) {
@@ -1265,4 +1283,3 @@
int GetFileDescriptor(const ZipArchiveHandle handle) {
return reinterpret_cast<ZipArchive*>(handle)->fd;
}
-
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index f8952ce..c799869 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -115,7 +115,7 @@
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
void* iteration_cookie;
- ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
ZipEntry data;
ZipEntryName name;
@@ -146,6 +146,116 @@
CloseArchive(handle);
}
+TEST(ziparchive, IterationWithPrefix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipEntryName prefix("b/");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
+
+ ZipEntry data;
+ ZipEntryName name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // b/
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipEntryName suffix(".txt");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
+
+ ZipEntry data;
+ ZipEntryName name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // a.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("a.txt", name);
+
+ // b.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b.txt", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithPrefixAndSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipEntryName prefix("b");
+ ZipEntryName suffix(".txt");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+
+ ZipEntry data;
+ ZipEntryName name;
+
+ // b/c.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/c.txt", name);
+
+ // b/d.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b/d.txt", name);
+
+ // b.txt
+ ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+ AssertNameEquals("b.txt", name);
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
+ ZipArchiveHandle handle;
+ ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+ void* iteration_cookie;
+ ZipEntryName prefix("x");
+ ZipEntryName suffix("y");
+ ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+
+ ZipEntry data;
+ ZipEntryName name;
+
+ // End of iteration.
+ ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+ CloseArchive(handle);
+}
+
TEST(ziparchive, FindEntry) {
ZipArchiveHandle handle;
ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 4ec2e59..4b3547c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -145,7 +145,9 @@
++cp;
}
tid = pid;
+ logbuf->lock();
uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
memmove(pidptr, cp, strlen(cp) + 1);
}
@@ -180,14 +182,20 @@
static const char comm_str[] = " comm=\"";
const char *comm = strstr(str, comm_str);
const char *estr = str + strlen(str);
+ char *commfree = NULL;
if (comm) {
estr = comm;
comm += sizeof(comm_str) - 1;
} else if (pid == getpid()) {
pid = tid;
comm = "auditd";
- } else if (!(comm = logbuf->pidToName(pid))) {
- comm = "unknown";
+ } else {
+ logbuf->lock();
+ comm = commfree = logbuf->pidToName(pid);
+ logbuf->unlock();
+ if (!comm) {
+ comm = "unknown";
+ }
}
const char *ecomm = strchr(comm, '"');
@@ -218,6 +226,7 @@
}
}
+ free(commfree);
free(str);
if (notify) {
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 00b19b6..a13fded 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -71,10 +71,12 @@
// *strp uses malloc, use free to release.
void formatPrune(char **strp) { mPrune.format(strp); }
- // helper
+ // helper must be protected directly or implicitly by lock()/unlock()
char *pidToName(pid_t pid) { return stats.pidToName(pid); }
uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+ void lock() { pthread_mutex_lock(&mLogElementsLock); }
+ void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
private:
void maybePrune(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 3f5fdce..9fb1439 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -111,13 +111,17 @@
}
static const char format_uid[] = "uid=%u%s%s expire %u line%s";
+ parent->lock();
char *name = parent->uidToName(mUid);
+ parent->unlock();
char *commName = android::tidToName(mTid);
if (!commName && (mTid != mPid)) {
commName = android::tidToName(mPid);
}
if (!commName) {
+ parent->lock();
commName = parent->pidToName(mPid);
+ parent->unlock();
}
size_t len = name ? strlen(name) : 0;
if (len && commName && !strncmp(name, commName, len)) {
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index b9e9650..760d6b2 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -334,7 +334,7 @@
// *strp = malloc, balance with free
void format(char **strp, uid_t uid, unsigned int logMask);
- // helper
+ // helper (must be locked directly or implicitly by mLogElementsLock)
char *pidToName(pid_t pid);
uid_t pidToUid(pid_t pid);
char *uidToName(uid_t uid);
diff --git a/rootdir/init.rc b/rootdir/init.rc
index c2d5a09..4e23354 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -67,15 +67,18 @@
mkdir /mnt/user/0 0755 root root
mkdir /mnt/expand 0771 system system
- # sdcard_r is GID 1028
- mkdir /storage 0751 root sdcard_r
- mount tmpfs tmpfs /storage mode=0751,uid=0,gid=1028
- restorecon_recursive /storage
+ # Storage views to support runtime permissions
+ mkdir /storage 0755 root root
+ mkdir /mnt/runtime_default 0755 root root
+ mkdir /mnt/runtime_default/self 0755 root root
+ mkdir /mnt/runtime_read 0755 root root
+ mkdir /mnt/runtime_read/self 0755 root root
+ mkdir /mnt/runtime_write 0755 root root
+ mkdir /mnt/runtime_write/self 0755 root root
# Symlink to keep legacy apps working in multi-user world
- mkdir /storage/self 0751 root sdcard_r
symlink /storage/self/primary /sdcard
- symlink /mnt/user/0/primary /storage/self/primary
+ symlink /mnt/user/0/primary /mnt/runtime_default/self/primary
# memory control cgroup
mkdir /dev/memcg 0700 root system
@@ -178,8 +181,11 @@
trigger late-init
# Load properties from /system/ + /factory after fs mount.
-on load_all_props_action
- load_all_props
+on load_system_props_action
+ load_system_props
+
+on load_persist_props_action
+ load_persist_props
start logd
start logd-reinit
@@ -192,12 +198,16 @@
trigger early-fs
trigger fs
trigger post-fs
- trigger post-fs-data
# Load properties from /system/ + /factory after fs mount. Place
# this in another action so that the load will be scheduled after the prior
# issued fs triggers have completed.
- trigger load_all_props_action
+ trigger load_system_props_action
+
+ # Now we can mount /data. File encryption requires keymaster to decrypt
+ # /data, which in turn can only be loaded when system properties are present
+ trigger post-fs-data
+ trigger load_persist_props_action
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
@@ -210,8 +220,10 @@
start logd
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
- # mount shared so changes propagate into child namespaces
+ # Mount shared so changes propagate into child namespaces
mount rootfs rootfs / shared rec
+ # Mount default storage into root namespace
+ mount none /mnt/runtime_default /storage slave bind rec
# We chown/chmod /cache again so because mount is run as root + defaults
chown system cache /cache
@@ -270,7 +282,10 @@
# create basic filesystem structure
mkdir /data/misc 01771 system misc
mkdir /data/misc/adb 02750 system shell
- mkdir /data/misc/bluedroid 0770 bluetooth net_bt_stack
+ mkdir /data/misc/bluedroid 02770 bluetooth net_bt_stack
+ # Fix the access permissions and group ownership for 'bt_config.conf'
+ chmod 0660 /data/misc/bluedroid/bt_config.conf
+ chown bluetooth net_bt_stack /data/misc/bluedroid/bt_config.conf
mkdir /data/misc/bluetooth 0770 system system
mkdir /data/misc/keystore 0700 keystore keystore
mkdir /data/misc/gatekeeper 0700 system system
@@ -633,7 +648,7 @@
oneshot
service gatekeeperd /system/bin/gatekeeperd /data/misc/gatekeeper
- class main
+ class late_start
user system
service installd /system/bin/installd
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index f8b23a3..4b8e0c0 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -74,22 +74,6 @@
* requiring any additional GIDs.
* - Separate permissions for protecting directories like Pictures and Music.
* - Multi-user separation on the same physical device.
- *
- * The derived permissions look like this:
- *
- * rwxrwx--x root:sdcard_rw /
- * rwxrwx--- root:sdcard_pics /Pictures
- * rwxrwx--- root:sdcard_av /Music
- *
- * rwxrwx--x root:sdcard_rw /Android
- * rwxrwx--x root:sdcard_rw /Android/data
- * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example
- * rwxrwx--x root:sdcard_rw /Android/obb/
- * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example
- *
- * rwxrwx--- root:sdcard_all /Android/user
- * rwxrwx--x root:sdcard_rw /Android/user/10
- * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example
*/
#define FUSE_TRACE 0
@@ -115,9 +99,6 @@
* the largest possible data payload. */
#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
-/* Default number of threads. */
-#define DEFAULT_NUM_THREADS 2
-
/* Pseudo-error constant used to indicate that no fuse status is needed
* or that a reply has already been written. */
#define NO_STATUS 1
@@ -135,7 +116,7 @@
PERM_INHERIT,
/* This node is one level above a normal root; used for legacy layouts
* which use the first level to represent user_id. */
- PERM_LEGACY_PRE_ROOT,
+ PERM_PRE_ROOT,
/* This node is "/" */
PERM_ROOT,
/* This node is "/Android" */
@@ -148,13 +129,6 @@
PERM_ANDROID_MEDIA,
} perm_t;
-/* Permissions structure to derive */
-typedef enum {
- DERIVE_NONE,
- DERIVE_LEGACY,
- DERIVE_UNIFIED,
-} derive_t;
-
struct handle {
int fd;
};
@@ -210,25 +184,30 @@
return strcasecmp(keyA, keyB) == 0;
}
-static int int_hash(void *key) {
- return (int) (uintptr_t) key;
-}
-
-static bool int_equals(void *keyA, void *keyB) {
- return keyA == keyB;
-}
-
-/* Global data structure shared by all fuse handlers. */
-struct fuse {
+/* Global data for all FUSE mounts */
+struct fuse_global {
pthread_mutex_t lock;
+ uid_t uid;
+ gid_t gid;
+ bool multi_user;
+
+ Hashmap* package_to_appid;
+};
+
+/* Single FUSE mount */
+struct fuse {
+ struct fuse_global* global;
+
+ char source_path[PATH_MAX];
+ char dest_path[PATH_MAX];
+ char obb_path[PATH_MAX];
+
__u64 next_generation;
int fd;
- derive_t derive;
- bool split_perms;
- gid_t write_gid;
struct node root;
- char obbpath[PATH_MAX];
+ gid_t gid;
+ mode_t mask;
/* Used to allocate unique inode numbers for fuse nodes. We use
* a simple counter based scheme where inode numbers from deleted
@@ -248,12 +227,9 @@
* Accesses must be guarded by |lock|.
*/
__u32 inode_ctr;
-
- Hashmap* package_to_appid;
- Hashmap* uid_with_rw;
};
-/* Private data used by a single fuse handler. */
+/* Private data used by a single FUSE handler */
struct fuse_handler {
struct fuse* fuse;
int token;
@@ -459,20 +435,25 @@
node->gid = parent->gid;
node->mode = parent->mode;
- if (fuse->derive == DERIVE_NONE) {
- return;
- }
-
/* Derive custom permissions based on parent and current node */
switch (parent->perm) {
case PERM_INHERIT:
/* Already inherited above */
break;
- case PERM_LEGACY_PRE_ROOT:
+ case PERM_PRE_ROOT:
/* Legacy internal layout places users at top level */
node->perm = PERM_ROOT;
node->userid = strtoul(node->name, NULL, 10);
- node->gid = multiuser_get_uid(node->userid, AID_SDCARD_R);
+ if (fuse->gid == AID_SDCARD_RW) {
+ /* As an optimization, certain trusted system components only run
+ * as owner but operate across all users. Since we're now handing
+ * out the sdcard_rw GID only to trusted apps, we're okay relaxing
+ * the user boundary enforcement for the default view. The UIDs
+ * assigned to app directories are still multiuser aware. */
+ node->gid = fuse->gid;
+ } else {
+ node->gid = multiuser_get_uid(node->userid, fuse->gid);
+ }
node->mode = 0771;
break;
case PERM_ROOT:
@@ -482,18 +463,6 @@
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID;
node->mode = 0771;
- } else if (fuse->split_perms) {
- if (!strcasecmp(node->name, "DCIM")
- || !strcasecmp(node->name, "Pictures")) {
- node->gid = multiuser_get_uid(node->userid, AID_SDCARD_PICS);
- } else if (!strcasecmp(node->name, "Alarms")
- || !strcasecmp(node->name, "Movies")
- || !strcasecmp(node->name, "Music")
- || !strcasecmp(node->name, "Notifications")
- || !strcasecmp(node->name, "Podcasts")
- || !strcasecmp(node->name, "Ringtones")) {
- node->gid = multiuser_get_uid(node->userid, AID_SDCARD_AV);
- }
}
break;
case PERM_ANDROID:
@@ -506,8 +475,8 @@
node->perm = PERM_ANDROID_OBB;
node->mode = 0771;
/* Single OBB directory is always shared */
- node->graft_path = fuse->obbpath;
- node->graft_pathlen = strlen(fuse->obbpath);
+ node->graft_path = fuse->obb_path;
+ node->graft_pathlen = strlen(fuse->obb_path);
} else if (!strcasecmp(node->name, "media")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID_MEDIA;
@@ -517,23 +486,15 @@
case PERM_ANDROID_DATA:
case PERM_ANDROID_OBB:
case PERM_ANDROID_MEDIA:
- appid = (appid_t) (uintptr_t) hashmapGet(fuse->package_to_appid, node->name);
+ appid = (appid_t) (uintptr_t) hashmapGet(fuse->global->package_to_appid, node->name);
if (appid != 0) {
node->uid = multiuser_get_uid(parent->userid, appid);
}
node->mode = 0770;
break;
}
-}
-/* Return if the calling UID holds sdcard_rw. */
-static bool get_caller_has_rw_locked(struct fuse* fuse, const struct fuse_in_header *hdr) {
- /* No additional permissions enforcement */
- if (fuse->derive == DERIVE_NONE) {
- return true;
- }
-
- return hashmapContainsKey(fuse->uid_with_rw, (void*) (uintptr_t) hdr->uid);
+ node->mode = node->mode & ~fuse->mask;
}
/* Kernel has already enforced everything we returned through
@@ -541,7 +502,7 @@
* even further, such as enforcing that apps hold sdcard_rw. */
static bool check_caller_access_to_name(struct fuse* fuse,
const struct fuse_in_header *hdr, const struct node* parent_node,
- const char* name, int mode, bool has_rw) {
+ const char* name, int mode) {
/* Always block security-sensitive files at root */
if (parent_node && parent_node->perm == PERM_ROOT) {
if (!strcasecmp(name, "autorun.inf")
@@ -551,34 +512,19 @@
}
}
- /* No additional permissions enforcement */
- if (fuse->derive == DERIVE_NONE) {
- return true;
- }
-
/* Root always has access; access for any other UIDs should always
* be controlled through packages.list. */
if (hdr->uid == 0) {
return true;
}
- /* If asking to write, verify that caller either owns the
- * parent or holds sdcard_rw. */
- if (mode & W_OK) {
- if (parent_node && hdr->uid == parent_node->uid) {
- return true;
- }
-
- return has_rw;
- }
-
/* No extra permissions to enforce */
return true;
}
static bool check_caller_access_to_node(struct fuse* fuse,
- const struct fuse_in_header *hdr, const struct node* node, int mode, bool has_rw) {
- return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode, has_rw);
+ const struct fuse_in_header *hdr, const struct node* node, int mode) {
+ return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode);
}
struct node *create_node_locked(struct fuse* fuse,
@@ -713,60 +659,6 @@
return child;
}
-static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
- gid_t write_gid, userid_t owner_user, derive_t derive, bool split_perms) {
- pthread_mutex_init(&fuse->lock, NULL);
-
- fuse->fd = fd;
- fuse->next_generation = 0;
- fuse->derive = derive;
- fuse->split_perms = split_perms;
- fuse->write_gid = write_gid;
- fuse->inode_ctr = 1;
-
- memset(&fuse->root, 0, sizeof(fuse->root));
- fuse->root.nid = FUSE_ROOT_ID; /* 1 */
- fuse->root.refcount = 2;
- fuse->root.namelen = strlen(source_path);
- fuse->root.name = strdup(source_path);
- fuse->root.userid = 0;
- fuse->root.uid = AID_ROOT;
-
- /* Set up root node for various modes of operation */
- switch (derive) {
- case DERIVE_NONE:
- /* Traditional behavior that treats entire device as being accessible
- * to sdcard_rw, and no permissions are derived. */
- fuse->root.perm = PERM_ROOT;
- fuse->root.mode = 0775;
- fuse->root.gid = AID_SDCARD_RW;
- break;
- case DERIVE_LEGACY:
- /* Legacy behavior used to support internal multiuser layout which
- * places user_id at the top directory level, with the actual roots
- * just below that. Shared OBB path is also at top level. */
- fuse->root.perm = PERM_LEGACY_PRE_ROOT;
- fuse->root.mode = 0711;
- fuse->root.gid = AID_SDCARD_R;
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
- fuse->uid_with_rw = hashmapCreate(128, int_hash, int_equals);
- snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
- fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
- break;
- case DERIVE_UNIFIED:
- /* Unified multiuser layout which places secondary user_id under
- * /Android/user and shared OBB path under /Android/obb. */
- fuse->root.perm = PERM_ROOT;
- fuse->root.mode = 0771;
- fuse->root.userid = owner_user;
- fuse->root.gid = multiuser_get_uid(owner_user, AID_SDCARD_R);
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
- fuse->uid_with_rw = hashmapCreate(128, int_hash, int_equals);
- snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
- break;
- }
-}
-
static void fuse_status(struct fuse *fuse, __u64 unique, int err)
{
struct fuse_out_header hdr;
@@ -809,10 +701,10 @@
return -errno;
}
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
if (!node) {
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
return -ENOMEM;
}
memset(&out, 0, sizeof(out));
@@ -821,7 +713,7 @@
out.entry_valid = 10;
out.nodeid = node->nid;
out.generation = node->gen;
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
fuse_reply(fuse, unique, &out, sizeof(out));
return NO_STATUS;
}
@@ -850,18 +742,18 @@
char child_path[PATH_MAX];
const char* actual_name;
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] LOOKUP %s @ %"PRIx64" (%s)\n", handler->token, name, hdr->nodeid,
parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!parent_node || !(actual_name = find_file_within(parent_path, name,
child_path, sizeof(child_path), 1))) {
return -ENOENT;
}
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK, false)) {
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) {
return -EACCES;
}
@@ -873,7 +765,7 @@
{
struct node* node;
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
node = lookup_node_by_id_locked(fuse, hdr->nodeid);
TRACE("[%d] FORGET #%"PRIu64" @ %"PRIx64" (%s)\n", handler->token, req->nlookup,
hdr->nodeid, node ? node->name : "?");
@@ -883,7 +775,7 @@
release_node_locked(node);
}
}
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
return NO_STATUS; /* no reply */
}
@@ -893,16 +785,16 @@
struct node* node;
char path[PATH_MAX];
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
TRACE("[%d] GETATTR flags=%x fh=%"PRIx64" @ %"PRIx64" (%s)\n", handler->token,
req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!node) {
return -ENOENT;
}
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
return -EACCES;
}
@@ -912,24 +804,22 @@
static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
{
- bool has_rw;
struct node* node;
char path[PATH_MAX];
struct timespec times[2];
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
TRACE("[%d] SETATTR fh=%"PRIx64" valid=%x @ %"PRIx64" (%s)\n", handler->token,
req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!node) {
return -ENOENT;
}
if (!(req->valid & FATTR_FH) &&
- !check_caller_access_to_node(fuse, hdr, node, W_OK, has_rw)) {
+ !check_caller_access_to_node(fuse, hdr, node, W_OK)) {
return -EACCES;
}
@@ -977,25 +867,23 @@
static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
{
- bool has_rw;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
const char* actual_name;
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] MKNOD %s 0%o @ %"PRIx64" (%s)\n", handler->token,
name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!parent_node || !(actual_name = find_file_within(parent_path, name,
child_path, sizeof(child_path), 1))) {
return -ENOENT;
}
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
return -EACCES;
}
__u32 mode = (req->mode & (~0777)) | 0664;
@@ -1008,25 +896,23 @@
static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
{
- bool has_rw;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
const char* actual_name;
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] MKDIR %s 0%o @ %"PRIx64" (%s)\n", handler->token,
name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!parent_node || !(actual_name = find_file_within(parent_path, name,
child_path, sizeof(child_path), 1))) {
return -ENOENT;
}
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
return -EACCES;
}
__u32 mode = (req->mode & (~0777)) | 0775;
@@ -1045,7 +931,7 @@
}
if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
char nomedia[PATH_MAX];
- snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath);
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obb_path);
if (touch(nomedia, 0664) != 0) {
ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
return -ENOENT;
@@ -1058,72 +944,68 @@
static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const char* name)
{
- bool has_rw;
struct node* parent_node;
struct node* child_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] UNLINK %s @ %"PRIx64" (%s)\n", handler->token,
name, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!parent_node || !find_file_within(parent_path, name,
child_path, sizeof(child_path), 1)) {
return -ENOENT;
}
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
return -EACCES;
}
if (unlink(child_path) < 0) {
return -errno;
}
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
child_node = lookup_child_by_name_locked(parent_node, name);
if (child_node) {
child_node->deleted = true;
}
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
return 0;
}
static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const char* name)
{
- bool has_rw;
struct node* child_node;
struct node* parent_node;
char parent_path[PATH_MAX];
char child_path[PATH_MAX];
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
parent_path, sizeof(parent_path));
TRACE("[%d] RMDIR %s @ %"PRIx64" (%s)\n", handler->token,
name, hdr->nodeid, parent_node ? parent_node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!parent_node || !find_file_within(parent_path, name,
child_path, sizeof(child_path), 1)) {
return -ENOENT;
}
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK, has_rw)) {
+ if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
return -EACCES;
}
if (rmdir(child_path) < 0) {
return -errno;
}
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
child_node = lookup_child_by_name_locked(parent_node, name);
if (child_node) {
child_node->deleted = true;
}
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
return 0;
}
@@ -1131,7 +1013,6 @@
const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
const char* old_name, const char* new_name)
{
- bool has_rw;
struct node* old_parent_node;
struct node* new_parent_node;
struct node* child_node;
@@ -1142,8 +1023,7 @@
const char* new_actual_name;
int res;
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
old_parent_path, sizeof(old_parent_path));
new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
@@ -1156,11 +1036,11 @@
res = -ENOENT;
goto lookup_error;
}
- if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK, has_rw)) {
+ if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) {
res = -EACCES;
goto lookup_error;
}
- if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK, has_rw)) {
+ if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) {
res = -EACCES;
goto lookup_error;
}
@@ -1171,7 +1051,7 @@
goto lookup_error;
}
acquire_node_locked(child_node);
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
/* Special case for renaming a file where destination is same path
* differing only by case. In this case we don't want to look for a case
@@ -1192,7 +1072,7 @@
goto io_error;
}
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
res = rename_node_locked(child_node, new_name, new_actual_name);
if (!res) {
remove_node_from_parent_locked(child_node);
@@ -1201,11 +1081,11 @@
goto done;
io_error:
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
done:
release_node_locked(child_node);
lookup_error:
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
return res;
}
@@ -1223,24 +1103,22 @@
static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
const struct fuse_in_header* hdr, const struct fuse_open_in* req)
{
- bool has_rw;
struct node* node;
char path[PATH_MAX];
struct fuse_open_out out;
struct handle *h;
- pthread_mutex_lock(&fuse->lock);
- has_rw = get_caller_has_rw_locked(fuse, hdr);
+ pthread_mutex_lock(&fuse->global->lock);
node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
TRACE("[%d] OPEN 0%o @ %"PRIx64" (%s)\n", handler->token,
req->flags, hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!node) {
return -ENOENT;
}
if (!check_caller_access_to_node(fuse, hdr, node,
- open_flags_to_access_mode(req->flags), has_rw)) {
+ open_flags_to_access_mode(req->flags))) {
return -EACCES;
}
h = malloc(sizeof(*h));
@@ -1321,10 +1199,10 @@
struct fuse_statfs_out out;
int res;
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
TRACE("[%d] STATFS\n", handler->token);
res = get_node_path_locked(&fuse->root, path, sizeof(path));
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (res < 0) {
return -ENOENT;
}
@@ -1395,16 +1273,16 @@
struct fuse_open_out out;
struct dirhandle *h;
- pthread_mutex_lock(&fuse->lock);
+ pthread_mutex_lock(&fuse->global->lock);
node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
TRACE("[%d] OPENDIR @ %"PRIx64" (%s)\n", handler->token,
hdr->nodeid, node ? node->name : "?");
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&fuse->global->lock);
if (!node) {
return -ENOENT;
}
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK, false)) {
+ if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
return -EACCES;
}
h = malloc(sizeof(*h));
@@ -1484,7 +1362,8 @@
return -1;
}
- out.minor = MIN(req->minor, FUSE_KERNEL_MINOR_VERSION);
+ /* We limit ourselves to 15 because we don't handle BATCH_FORGET yet */
+ out.minor = MIN(req->minor, 15);
fuse_struct_size = sizeof(out);
#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
/* FUSE_KERNEL_VERSION >= 23. */
@@ -1634,12 +1513,14 @@
{
struct fuse* fuse = handler->fuse;
for (;;) {
- ssize_t len = read(fuse->fd,
- handler->request_buffer, sizeof(handler->request_buffer));
+ ssize_t len = TEMP_FAILURE_RETRY(read(fuse->fd,
+ handler->request_buffer, sizeof(handler->request_buffer)));
if (len < 0) {
- if (errno != EINTR) {
- ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
+ if (errno == ENODEV) {
+ ERROR("[%d] someone stole our marbles!\n", handler->token);
+ exit(2);
}
+ ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
continue;
}
@@ -1686,22 +1567,15 @@
return true;
}
-static bool remove_int_to_null(void *key, void *value, void *context) {
- Hashmap* map = context;
- hashmapRemove(map, key);
- return true;
-}
+static int read_package_list(struct fuse_global* global) {
+ pthread_mutex_lock(&global->lock);
-static int read_package_list(struct fuse *fuse) {
- pthread_mutex_lock(&fuse->lock);
-
- hashmapForEach(fuse->package_to_appid, remove_str_to_int, fuse->package_to_appid);
- hashmapForEach(fuse->uid_with_rw, remove_int_to_null, fuse->uid_with_rw);
+ hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
FILE* file = fopen(kPackagesListFile, "r");
if (!file) {
ERROR("failed to open package list: %s\n", strerror(errno));
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&global->lock);
return -1;
}
@@ -1713,33 +1587,18 @@
if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
char* package_name_dup = strdup(package_name);
- hashmapPut(fuse->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
-
- char* token = strtok(gids, ",");
- while (token != NULL) {
- // Current packages.list format is a bit funky; it blends per
- // user GID membership into a single per-app line. Here we
- // work backwards from the groups to build the per-user UIDs
- // that have write permission.
- gid_t gid = strtoul(token, NULL, 10);
- if (multiuser_get_app_id(gid) == fuse->write_gid) {
- uid_t uid = multiuser_get_uid(multiuser_get_user_id(gid), appid);
- hashmapPut(fuse->uid_with_rw, (void*) (uintptr_t) uid, (void*) (uintptr_t) 1);
- }
- token = strtok(NULL, ",");
- }
+ hashmapPut(global->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
}
}
- TRACE("read_package_list: found %zu packages, %zu with write_gid\n",
- hashmapSize(fuse->package_to_appid),
- hashmapSize(fuse->uid_with_rw));
+ TRACE("read_package_list: found %zu packages\n",
+ hashmapSize(global->package_to_appid));
fclose(file);
- pthread_mutex_unlock(&fuse->lock);
+ pthread_mutex_unlock(&global->lock);
return 0;
}
-static void watch_package_list(struct fuse* fuse) {
+static void watch_package_list(struct fuse_global* global) {
struct inotify_event *event;
char event_buf[512];
@@ -1767,7 +1626,7 @@
/* Watch above will tell us about any future changes, so
* read the current state. */
- if (read_package_list(fuse) == -1) {
+ if (read_package_list(global) == -1) {
ERROR("read_package_list failed: %s\n", strerror(errno));
return;
}
@@ -1801,139 +1660,160 @@
}
}
-static int ignite_fuse(struct fuse* fuse, int num_threads)
-{
- struct fuse_handler* handlers;
- int i;
-
- handlers = malloc(num_threads * sizeof(struct fuse_handler));
- if (!handlers) {
- ERROR("cannot allocate storage for threads\n");
- return -ENOMEM;
- }
-
- for (i = 0; i < num_threads; i++) {
- handlers[i].fuse = fuse;
- handlers[i].token = i;
- }
-
- /* When deriving permissions, this thread is used to process inotify events,
- * otherwise it becomes one of the FUSE handlers. */
- i = (fuse->derive == DERIVE_NONE) ? 1 : 0;
- for (; i < num_threads; i++) {
- pthread_t thread;
- int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
- if (res) {
- ERROR("failed to start thread #%d, error=%d\n", i, res);
- goto quit;
- }
- }
-
- if (fuse->derive == DERIVE_NONE) {
- handle_fuse_requests(&handlers[0]);
- } else {
- watch_package_list(fuse);
- }
-
- ERROR("terminated prematurely\n");
-
- /* don't bother killing all of the other threads or freeing anything,
- * should never get here anyhow */
-quit:
- exit(1);
-}
-
-static int usage()
-{
- ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
+static int usage() {
+ ERROR("usage: sdcard [OPTIONS] <source_path> <label>\n"
" -u: specify UID to run as\n"
" -g: specify GID to run as\n"
- " -w: specify GID required to write (default sdcard_rw, requires -d or -l)\n"
- " -t: specify number of threads to use (default %d)\n"
- " -d: derive file permissions based on path\n"
- " -l: derive file permissions based on legacy internal layout\n"
- " -s: split derived permissions for pics, av\n"
- "\n", DEFAULT_NUM_THREADS);
+ " -m: source_path is multi-user\n"
+ " -w: runtime_write mount has full write access\n"
+ "\n");
return 1;
}
-static int run(const char* source_path, const char* dest_path, uid_t uid,
- gid_t gid, gid_t write_gid, userid_t owner_user, int num_threads, derive_t derive,
- bool split_perms) {
- int fd;
+static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
char opts[256];
- int res;
- struct fuse fuse;
- /* cleanup from previous instance, if necessary */
- umount2(dest_path, MNT_DETACH);
-
- fd = open("/dev/fuse", O_RDWR);
- if (fd < 0){
- ERROR("cannot open fuse device: %s\n", strerror(errno));
+ fuse->fd = open("/dev/fuse", O_RDWR);
+ if (fuse->fd == -1) {
+ ERROR("failed to open fuse device: %s\n", strerror(errno));
return -1;
}
+ umount2(fuse->dest_path, MNT_DETACH);
+
snprintf(opts, sizeof(opts),
"fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
- fd, uid, gid);
-
- res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
- MS_NOATIME, opts);
- if (res < 0) {
- ERROR("cannot mount fuse filesystem: %s\n", strerror(errno));
- goto error;
+ fuse->fd, fuse->global->uid, fuse->global->gid);
+ if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC |
+ MS_NOATIME, opts) != 0) {
+ ERROR("failed to mount fuse filesystem: %s\n", strerror(errno));
+ return -1;
}
- res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups);
- if (res < 0) {
- ERROR("cannot setgroups: %s\n", strerror(errno));
- goto error;
+ fuse->next_generation = 0;
+ fuse->inode_ctr = 1;
+ fuse->gid = gid;
+ fuse->mask = mask;
+
+ memset(&fuse->root, 0, sizeof(fuse->root));
+ fuse->root.nid = FUSE_ROOT_ID; /* 1 */
+ fuse->root.refcount = 2;
+ fuse->root.namelen = strlen(fuse->source_path);
+ fuse->root.name = strdup(fuse->source_path);
+ fuse->root.userid = 0;
+ fuse->root.uid = AID_ROOT;
+ fuse->root.gid = fuse->gid;
+
+ if (fuse->global->multi_user) {
+ fuse->root.perm = PERM_PRE_ROOT;
+ fuse->root.mode = 0711;
+ snprintf(fuse->obb_path, sizeof(fuse->obb_path), "%s/obb", fuse->source_path);
+ } else {
+ fuse->root.perm = PERM_ROOT;
+ fuse->root.mode = 0771 & ~mask;
+ snprintf(fuse->obb_path, sizeof(fuse->obb_path), "%s/Android/obb", fuse->source_path);
}
- res = setgid(gid);
- if (res < 0) {
- ERROR("cannot setgid: %s\n", strerror(errno));
- goto error;
- }
-
- res = setuid(uid);
- if (res < 0) {
- ERROR("cannot setuid: %s\n", strerror(errno));
- goto error;
- }
-
- fuse_init(&fuse, fd, source_path, write_gid, owner_user, derive, split_perms);
-
- umask(0);
- res = ignite_fuse(&fuse, num_threads);
-
- /* we do not attempt to umount the file system here because we are no longer
- * running as the root user */
-
-error:
- close(fd);
- return res;
+ return 0;
}
-int main(int argc, char **argv)
-{
- int res;
+static void run(const char* source_path, const char* label, uid_t uid,
+ gid_t gid, bool multi_user, bool full_write) {
+ struct fuse_global global;
+ struct fuse fuse_default;
+ struct fuse fuse_read;
+ struct fuse fuse_write;
+ struct fuse_handler handler_default;
+ struct fuse_handler handler_read;
+ struct fuse_handler handler_write;
+ pthread_t thread_default;
+ pthread_t thread_read;
+ pthread_t thread_write;
+
+ memset(&global, 0, sizeof(global));
+ memset(&fuse_default, 0, sizeof(fuse_default));
+ memset(&fuse_read, 0, sizeof(fuse_read));
+ memset(&fuse_write, 0, sizeof(fuse_write));
+ memset(&handler_default, 0, sizeof(handler_default));
+ memset(&handler_read, 0, sizeof(handler_read));
+ memset(&handler_write, 0, sizeof(handler_write));
+
+ pthread_mutex_init(&global.lock, NULL);
+ global.package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
+ global.uid = uid;
+ global.gid = gid;
+ global.multi_user = multi_user;
+
+ fuse_default.global = &global;
+ strcpy(fuse_default.source_path, source_path);
+ snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime_default/%s", label);
+
+ fuse_read.global = &global;
+ strcpy(fuse_read.source_path, source_path);
+ snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime_read/%s", label);
+
+ fuse_write.global = &global;
+ strcpy(fuse_write.source_path, source_path);
+ snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime_write/%s", label);
+
+ handler_default.fuse = &fuse_default;
+ handler_read.fuse = &fuse_read;
+ handler_write.fuse = &fuse_write;
+
+ umask(0);
+
+ if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
+ || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
+ || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
+ ERROR("failed to fuse_setup\n");
+ exit(1);
+ }
+
+ /* Drop privs */
+ if (setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups) < 0) {
+ ERROR("cannot setgroups: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (setgid(gid) < 0) {
+ ERROR("cannot setgid: %s\n", strerror(errno));
+ exit(1);
+ }
+ if (setuid(uid) < 0) {
+ ERROR("cannot setuid: %s\n", strerror(errno));
+ exit(1);
+ }
+
+ if (multi_user) {
+ fs_prepare_dir(fuse_default.obb_path, 0775, uid, gid);
+ fs_prepare_dir(fuse_read.obb_path, 0775, uid, gid);
+ fs_prepare_dir(fuse_write.obb_path, 0775, uid, gid);
+ }
+
+ if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
+ || pthread_create(&thread_read, NULL, start_handler, &handler_read)
+ || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
+ ERROR("failed to pthread_create\n");
+ exit(1);
+ }
+
+ watch_package_list(&global);
+ ERROR("terminated prematurely\n");
+ exit(1);
+}
+
+int main(int argc, char **argv) {
const char *source_path = NULL;
- const char *dest_path = NULL;
+ const char *label = NULL;
uid_t uid = 0;
gid_t gid = 0;
- gid_t write_gid = AID_SDCARD_RW;
- userid_t owner_user = 0;
- int num_threads = DEFAULT_NUM_THREADS;
- derive_t derive = DERIVE_NONE;
- bool split_perms = false;
+ bool multi_user = false;
+ bool full_write = false;
int i;
struct rlimit rlim;
int fs_version;
int opt;
- while ((opt = getopt(argc, argv, "u:g:w:o:t:dls")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:mw")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -1941,23 +1821,11 @@
case 'g':
gid = strtoul(optarg, NULL, 10);
break;
+ case 'm':
+ multi_user = true;
+ break;
case 'w':
- write_gid = strtoul(optarg, NULL, 10);
- break;
- case 'o':
- owner_user = strtoul(optarg, NULL, 10);
- break;
- case 't':
- num_threads = strtoul(optarg, NULL, 10);
- break;
- case 'd':
- derive = DERIVE_UNIFIED;
- break;
- case 'l':
- derive = DERIVE_LEGACY;
- break;
- case 's':
- split_perms = true;
+ full_write = true;
break;
case '?':
default:
@@ -1969,12 +1837,8 @@
char* arg = argv[i];
if (!source_path) {
source_path = arg;
- } else if (!dest_path) {
- dest_path = arg;
- } else if (!uid) {
- uid = strtoul(arg, NULL, 10);
- } else if (!gid) {
- gid = strtoul(arg, NULL, 10);
+ } else if (!label) {
+ label = arg;
} else {
ERROR("too many arguments\n");
return usage();
@@ -1985,22 +1849,14 @@
ERROR("no source path specified\n");
return usage();
}
- if (!dest_path) {
- ERROR("no dest path specified\n");
+ if (!label) {
+ ERROR("no label specified\n");
return usage();
}
if (!uid || !gid) {
ERROR("uid and gid must be nonzero\n");
return usage();
}
- if (num_threads < 1) {
- ERROR("number of threads must be at least 1\n");
- return usage();
- }
- if (split_perms && derive == DERIVE_NONE) {
- ERROR("cannot split permissions without deriving\n");
- return usage();
- }
rlim.rlim_cur = 8192;
rlim.rlim_max = 8192;
@@ -2013,7 +1869,6 @@
sleep(1);
}
- res = run(source_path, dest_path, uid, gid, write_gid, owner_user,
- num_threads, derive, split_perms);
- return res < 0 ? 1 : 0;
+ run(source_path, label, uid, gid, multi_user, full_write);
+ return 1;
}