summaryrefslogtreecommitdiffstats
path: root/init/devices.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/devices.c')
-rw-r--r--init/devices.c157
1 files changed, 120 insertions, 37 deletions
diff --git a/init/devices.c b/init/devices.c
index f7df453..e27c311 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2007-2014 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.
@@ -15,6 +15,7 @@
*/
#include <errno.h>
+#include <fnmatch.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -37,7 +38,6 @@
#include <private/android_filesystem_config.h>
#include <sys/time.h>
-#include <asm/page.h>
#include <sys/wait.h>
#include <cutils/list.h>
@@ -48,6 +48,8 @@
#include "util.h"
#include "log.h"
+#define UNUSED __attribute__((__unused__))
+
#define SYSFS_PREFIX "/sys"
#define FIRMWARE_DIR1 "/etc/firmware"
#define FIRMWARE_DIR2 "/vendor/firmware"
@@ -76,6 +78,7 @@ struct perms_ {
unsigned int uid;
unsigned int gid;
unsigned short prefix;
+ unsigned short wildcard;
};
struct perm_node {
@@ -96,7 +99,8 @@ static list_declare(platform_names);
int add_dev_perms(const char *name, const char *attr,
mode_t perm, unsigned int uid, unsigned int gid,
- unsigned short prefix) {
+ unsigned short prefix,
+ unsigned short wildcard) {
struct perm_node *node = calloc(1, sizeof(*node));
if (!node)
return -ENOMEM;
@@ -115,6 +119,7 @@ int add_dev_perms(const char *name, const char *attr,
node->dp.uid = uid;
node->dp.gid = gid;
node->dp.prefix = prefix;
+ node->dp.wildcard = wildcard;
if (attr)
list_add_tail(&sys_perms, &node->plist);
@@ -129,40 +134,62 @@ void fixup_sys_perms(const char *upath)
char buf[512];
struct listnode *node;
struct perms_ *dp;
- char *secontext;
- /* upaths omit the "/sys" that paths in this list
- * contain, so we add 4 when comparing...
- */
+ /* upaths omit the "/sys" that paths in this list
+ * contain, so we add 4 when comparing...
+ */
list_for_each(node, &sys_perms) {
dp = &(node_to_item(node, struct perm_node, plist))->dp;
if (dp->prefix) {
if (strncmp(upath, dp->name + 4, strlen(dp->name + 4)))
continue;
+ } else if (dp->wildcard) {
+ if (fnmatch(dp->name + 4, upath, FNM_PATHNAME) != 0)
+ continue;
} else {
if (strcmp(upath, dp->name + 4))
continue;
}
if ((strlen(upath) + strlen(dp->attr) + 6) > sizeof(buf))
- return;
+ break;
sprintf(buf,"/sys%s/%s", upath, dp->attr);
INFO("fixup %s %d %d 0%o\n", buf, dp->uid, dp->gid, dp->perm);
chown(buf, dp->uid, dp->gid);
chmod(buf, dp->perm);
- if (sehandle) {
- secontext = NULL;
- selabel_lookup(sehandle, &secontext, buf, 0);
- if (secontext) {
- setfilecon(buf, secontext);
- freecon(secontext);
- }
- }
}
+
+ // Now fixup SELinux file labels
+ int len = snprintf(buf, sizeof(buf), "/sys%s", upath);
+ if ((len < 0) || ((size_t) len >= sizeof(buf))) {
+ // Overflow
+ return;
+ }
+ if (access(buf, F_OK) == 0) {
+ INFO("restorecon_recursive: %s\n", buf);
+ restorecon_recursive(buf);
+ }
+}
+
+static bool perm_path_matches(const char *path, struct perms_ *dp)
+{
+ if (dp->prefix) {
+ if (strncmp(path, dp->name, strlen(dp->name)) == 0)
+ return true;
+ } else if (dp->wildcard) {
+ if (fnmatch(dp->name, path, FNM_PATHNAME) == 0)
+ return true;
+ } else {
+ if (strcmp(path, dp->name) == 0)
+ return true;
+ }
+
+ return false;
}
-static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
+static mode_t get_device_perm(const char *path, const char **links,
+ unsigned *uid, unsigned *gid)
{
mode_t perm;
struct listnode *node;
@@ -173,19 +200,30 @@ static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
* override ueventd.rc
*/
list_for_each_reverse(node, &dev_perms) {
+ bool match = false;
+
perm_node = node_to_item(node, struct perm_node, plist);
dp = &perm_node->dp;
- if (dp->prefix) {
- if (strncmp(path, dp->name, strlen(dp->name)))
- continue;
+ if (perm_path_matches(path, dp)) {
+ match = true;
} else {
- if (strcmp(path, dp->name))
- continue;
+ if (links) {
+ int i;
+ for (i = 0; links[i]; i++) {
+ if (perm_path_matches(links[i], dp)) {
+ match = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (match) {
+ *uid = dp->uid;
+ *gid = dp->gid;
+ return dp->perm;
}
- *uid = dp->uid;
- *gid = dp->gid;
- return dp->perm;
}
/* Default if nothing found. */
*uid = 0;
@@ -194,8 +232,9 @@ static mode_t get_device_perm(const char *path, unsigned *uid, unsigned *gid)
}
static void make_device(const char *path,
- const char *upath,
- int block, int major, int minor)
+ const char *upath UNUSED,
+ int block, int major, int minor,
+ const char **links)
{
unsigned uid;
unsigned gid;
@@ -203,10 +242,10 @@ static void make_device(const char *path,
dev_t dev;
char *secontext = NULL;
- mode = get_device_perm(path, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
+ mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
if (sehandle) {
- selabel_lookup(sehandle, &secontext, path, mode);
+ selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
setfscreatecon(secontext);
}
@@ -297,6 +336,37 @@ static void remove_platform_device(const char *path)
}
}
+/* Given a path that may start with a PCI device, populate the supplied buffer
+ * with the PCI domain/bus number and the peripheral ID and return 0.
+ * If it doesn't start with a PCI device, or there is some error, return -1 */
+static int find_pci_device_prefix(const char *path, char *buf, ssize_t buf_sz)
+{
+ const char *start, *end;
+
+ if (strncmp(path, "/devices/pci", 12))
+ return -1;
+
+ /* Beginning of the prefix is the initial "pci" after "/devices/" */
+ start = path + 9;
+
+ /* End of the prefix is two path '/' later, capturing the domain/bus number
+ * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
+ end = strchr(start, '/');
+ if (!end)
+ return -1;
+ end = strchr(end + 1, '/');
+ if (!end)
+ return -1;
+
+ /* Make sure we have enough room for the string plus null terminator */
+ if (end - start + 1 > buf_sz)
+ return -1;
+
+ strncpy(buf, start, end - start);
+ buf[end - start] = '\0';
+ return 0;
+}
+
#if LOG_UEVENTS
static inline suseconds_t get_usecs(void)
@@ -421,11 +491,12 @@ err:
return NULL;
}
-static char **parse_platform_block_device(struct uevent *uevent)
+static char **get_block_device_symlinks(struct uevent *uevent)
{
const char *device;
struct platform_node *pdev;
char *slash;
+ const char *type;
int width;
char buf[256];
char link_path[256];
@@ -437,18 +508,24 @@ static char **parse_platform_block_device(struct uevent *uevent)
struct stat info;
pdev = find_platform_device(uevent->path);
- if (!pdev)
+ if (pdev) {
+ device = pdev->name;
+ type = "platform";
+ } else if (!find_pci_device_prefix(uevent->path, buf, sizeof(buf))) {
+ device = buf;
+ type = "pci";
+ } else {
return NULL;
- device = pdev->name;
+ }
char **links = malloc(sizeof(char *) * 4);
if (!links)
return NULL;
memset(links, 0, sizeof(char *) * 4);
- INFO("found platform device %s\n", device);
+ INFO("found %s device %s\n", type, device);
- snprintf(link_path, sizeof(link_path), "/dev/block/platform/%s", device);
+ snprintf(link_path, sizeof(link_path), "/dev/block/%s/%s", type, device);
if (uevent->partition_name) {
p = strdup(uevent->partition_name);
@@ -484,7 +561,7 @@ static void handle_device(const char *action, const char *devpath,
int i;
if(!strcmp(action, "add")) {
- make_device(devpath, path, block, major, minor);
+ make_device(devpath, path, block, major, minor, (const char **)links);
if (links) {
for (i = 0; links[i]; i++)
make_link(devpath, links[i]);
@@ -555,7 +632,7 @@ static void handle_block_device_event(struct uevent *uevent)
make_dir(base, 0755);
if (!strncmp(uevent->path, "/devices/", 9))
- links = parse_platform_block_device(uevent);
+ links = get_block_device_symlinks(uevent);
handle_device(uevent->action, devpath, uevent->path, 1,
uevent->major, uevent->minor, links);
@@ -590,6 +667,11 @@ static void mkdir_recursive_for_devpath(const char *devpath)
mkdir_recursive(dir, 0755);
}
+static inline void __attribute__((__deprecated__)) kernel_logger()
+{
+ INFO("kernel logger is deprecated\n");
+}
+
static void handle_generic_device_event(struct uevent *uevent)
{
char *base;
@@ -676,6 +758,7 @@ static void handle_generic_device_event(struct uevent *uevent)
make_dir(base, 0755);
} else if(!strncmp(uevent->subsystem, "misc", 4) &&
!strncmp(name, "log_", 4)) {
+ kernel_logger();
base = "/dev/log/";
make_dir(base, 0755);
name += 4;
@@ -692,7 +775,7 @@ static void handle_generic_device_event(struct uevent *uevent)
static void handle_device_event(struct uevent *uevent)
{
- if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change"))
+ if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
fixup_sys_perms(uevent->path);
if (!strncmp(uevent->subsystem, "block", 5)) {