summaryrefslogtreecommitdiffstats
path: root/init/builtins.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/builtins.c')
-rw-r--r--init/builtins.c261
1 files changed, 155 insertions, 106 deletions
diff --git a/init/builtins.c b/init/builtins.c
index ad73e90..07bd6d3 100644
--- a/init/builtins.c
+++ b/init/builtins.c
@@ -29,14 +29,14 @@
#include <stdlib.h>
#include <sys/mount.h>
#include <sys/resource.h>
+#include <sys/wait.h>
#include <linux/loop.h>
#include <cutils/partition_utils.h>
#include <sys/system_properties.h>
+#include <fs_mgr.h>
-#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#include <selinux/label.h>
-#endif
#include "init.h"
#include "keywords.h"
@@ -75,6 +75,63 @@ static int write_file(const char *path, const char *value)
}
}
+static int _open(const char *path)
+{
+ int fd;
+
+ fd = open(path, O_RDONLY | O_NOFOLLOW);
+ if (fd < 0)
+ fd = open(path, O_WRONLY | O_NOFOLLOW);
+
+ return fd;
+}
+
+static int _chown(const char *path, unsigned int uid, unsigned int gid)
+{
+ int fd;
+ int ret;
+
+ fd = _open(path);
+ if (fd < 0) {
+ return -1;
+ }
+
+ ret = fchown(fd, uid, gid);
+ if (ret < 0) {
+ int errno_copy = errno;
+ close(fd);
+ errno = errno_copy;
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
+static int _chmod(const char *path, mode_t mode)
+{
+ int fd;
+ int ret;
+
+ fd = _open(path);
+ if (fd < 0) {
+ return -1;
+ }
+
+ ret = fchmod(fd, mode);
+ if (ret < 0) {
+ int errno_copy = errno;
+ close(fd);
+ errno = errno_copy;
+ return -1;
+ }
+
+ close(fd);
+
+ return 0;
+}
+
static int insmod(const char *filename, char *options)
{
void *module;
@@ -243,10 +300,10 @@ int do_mkdir(int nargs, char **args)
mode = strtoul(args[2], 0, 8);
}
- ret = mkdir(args[1], mode);
+ ret = make_dir(args[1], mode);
/* chmod in case the directory already exists */
if (ret == -1 && errno == EEXIST) {
- ret = chmod(args[1], mode);
+ ret = _chmod(args[1], mode);
}
if (ret == -1) {
return -errno;
@@ -260,9 +317,17 @@ int do_mkdir(int nargs, char **args)
gid = decode_uid(args[4]);
}
- if (chown(args[1], uid, gid)) {
+ if (_chown(args[1], uid, gid) < 0) {
return -errno;
}
+
+ /* chown may have cleared S_ISUID and S_ISGID, chmod again */
+ if (mode & (S_ISUID | S_ISGID)) {
+ ret = _chmod(args[1], mode);
+ if (ret == -1) {
+ return -errno;
+ }
+ }
}
return 0;
@@ -273,12 +338,19 @@ static struct {
unsigned flag;
} mount_flags[] = {
{ "noatime", MS_NOATIME },
+ { "noexec", MS_NOEXEC },
{ "nosuid", MS_NOSUID },
{ "nodev", MS_NODEV },
{ "nodiratime", MS_NODIRATIME },
{ "ro", MS_RDONLY },
{ "rw", 0 },
{ "remount", MS_REMOUNT },
+ { "bind", MS_BIND },
+ { "rec", MS_REC },
+ { "unbindable", MS_UNBINDABLE },
+ { "private", MS_PRIVATE },
+ { "slave", MS_SLAVE },
+ { "shared", MS_SHARED },
{ "defaults", 0 },
{ 0, 0 },
};
@@ -375,91 +447,86 @@ int do_mount(int nargs, char **args)
if (wait)
wait_for_file(source, COMMAND_RETRY_TIMEOUT);
if (mount(source, target, system, flags, options) < 0) {
- /* If this fails, it may be an encrypted filesystem
- * or it could just be wiped. If wiped, that will be
- * handled later in the boot process.
- * We only support encrypting /data. Check
- * if we're trying to mount it, and if so,
- * assume it's encrypted, mount a tmpfs instead.
- * Then save the orig mount parms in properties
- * for vold to query when it mounts the real
- * encrypted /data.
- */
- if (!strcmp(target, DATA_MNT_POINT) && !partition_wiped(source)) {
- const char *tmpfs_options;
-
- tmpfs_options = property_get("ro.crypto.tmpfs_options");
-
- if (mount("tmpfs", target, "tmpfs", MS_NOATIME | MS_NOSUID | MS_NODEV,
- tmpfs_options) < 0) {
- return -1;
- }
-
- /* Set the property that triggers the framework to do a minimal
- * startup and ask the user for a password
- */
- property_set("ro.crypto.state", "encrypted");
- property_set("vold.decrypt", "1");
- } else {
- return -1;
- }
+ return -1;
}
- if (!strcmp(target, DATA_MNT_POINT)) {
- char fs_flags[32];
-
- /* Save the original mount options */
- property_set("ro.crypto.fs_type", system);
- property_set("ro.crypto.fs_real_blkdev", source);
- property_set("ro.crypto.fs_mnt_point", target);
- if (options) {
- property_set("ro.crypto.fs_options", options);
- }
- snprintf(fs_flags, sizeof(fs_flags), "0x%8.8x", flags);
- property_set("ro.crypto.fs_flags", fs_flags);
- }
}
exit_success:
- /* If not running encrypted, then set the property saying we are
- * unencrypted, and also trigger the action for a nonencrypted system.
- */
- if (!strcmp(target, DATA_MNT_POINT)) {
- const char *prop;
+ return 0;
- prop = property_get("ro.crypto.state");
- if (! prop) {
- prop = "notset";
+}
+
+int do_mount_all(int nargs, char **args)
+{
+ pid_t pid;
+ int ret = -1;
+ int child_ret = -1;
+ int status;
+ const char *prop;
+
+ if (nargs != 2) {
+ return -1;
+ }
+
+ /*
+ * Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
+ * do the call in the child to provide protection to the main init
+ * process if anything goes wrong (crash or memory leak), and wait for
+ * the child to finish in the parent.
+ */
+ pid = fork();
+ if (pid > 0) {
+ /* Parent. Wait for the child to return */
+ waitpid(pid, &status, 0);
+ if (WIFEXITED(status)) {
+ ret = WEXITSTATUS(status);
+ } else {
+ ret = -1;
}
- if (strcmp(prop, "encrypted")) {
- property_set("ro.crypto.state", "unencrypted");
- action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ } else if (pid == 0) {
+ /* child, call fs_mgr_mount_all() */
+ klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */
+ child_ret = fs_mgr_mount_all(args[1]);
+ if (child_ret == -1) {
+ ERROR("fs_mgr_mount_all returned an error\n");
}
+ exit(child_ret);
+ } else {
+ /* fork failed, return an error */
+ return -1;
}
- return 0;
+ /* ret is 1 if the device is encrypted, 0 if not, and -1 on error */
+ if (ret == 1) {
+ property_set("ro.crypto.state", "encrypted");
+ property_set("vold.decrypt", "1");
+ } else if (ret == 0) {
+ property_set("ro.crypto.state", "unencrypted");
+ /* If fs_mgr determined this is an unencrypted device, then trigger
+ * that action.
+ */
+ action_for_each_trigger("nonencrypted", action_add_queue_tail);
+ }
+ return ret;
}
int do_setcon(int nargs, char **args) {
-#ifdef HAVE_SELINUX
if (is_selinux_enabled() <= 0)
return 0;
if (setcon(args[1]) < 0) {
return -errno;
}
-#endif
return 0;
}
int do_setenforce(int nargs, char **args) {
-#ifdef HAVE_SELINUX
if (is_selinux_enabled() <= 0)
return 0;
if (security_setenforce(atoi(args[1])) < 0) {
return -errno;
}
-#endif
return 0;
}
@@ -643,10 +710,10 @@ out:
int do_chown(int nargs, char **args) {
/* GID is optional. */
if (nargs == 3) {
- if (chown(args[2], decode_uid(args[1]), -1) < 0)
+ if (_chown(args[2], decode_uid(args[1]), -1) < 0)
return -errno;
} else if (nargs == 4) {
- if (chown(args[3], decode_uid(args[1]), decode_uid(args[2])))
+ if (_chown(args[3], decode_uid(args[1]), decode_uid(args[2])) < 0)
return -errno;
} else {
return -1;
@@ -669,67 +736,47 @@ static mode_t get_mode(const char *s) {
int do_chmod(int nargs, char **args) {
mode_t mode = get_mode(args[1]);
- if (chmod(args[2], mode) < 0) {
+ if (_chmod(args[2], mode) < 0) {
return -errno;
}
return 0;
}
int do_restorecon(int nargs, char **args) {
-#ifdef HAVE_SELINUX
- char *secontext = NULL;
- struct stat sb;
int i;
- if (is_selinux_enabled() <= 0 || !sehandle)
- return 0;
-
for (i = 1; i < nargs; i++) {
- if (lstat(args[i], &sb) < 0)
- return -errno;
- if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0)
- return -errno;
- if (lsetfilecon(args[i], secontext) < 0) {
- freecon(secontext);
+ if (restorecon(args[i]) < 0)
return -errno;
- }
- freecon(secontext);
}
-#endif
return 0;
}
int do_setsebool(int nargs, char **args) {
-#ifdef HAVE_SELINUX
- SELboolean *b = alloca(nargs * sizeof(SELboolean));
- char *v;
- int i;
+ const char *name = args[1];
+ const char *value = args[2];
+ SELboolean b;
+ int ret;
if (is_selinux_enabled() <= 0)
return 0;
- for (i = 1; i < nargs; i++) {
- char *name = args[i];
- v = strchr(name, '=');
- if (!v) {
- ERROR("setsebool: argument %s had no =\n", name);
- return -EINVAL;
- }
- *v++ = 0;
- b[i-1].name = name;
- if (!strcmp(v, "1") || !strcasecmp(v, "true") || !strcasecmp(v, "on"))
- b[i-1].value = 1;
- else if (!strcmp(v, "0") || !strcasecmp(v, "false") || !strcasecmp(v, "off"))
- b[i-1].value = 0;
- else {
- ERROR("setsebool: invalid value %s\n", v);
- return -EINVAL;
- }
+ b.name = name;
+ if (!strcmp(value, "1") || !strcasecmp(value, "true") || !strcasecmp(value, "on"))
+ b.value = 1;
+ else if (!strcmp(value, "0") || !strcasecmp(value, "false") || !strcasecmp(value, "off"))
+ b.value = 0;
+ else {
+ ERROR("setsebool: invalid value %s\n", value);
+ return -EINVAL;
+ }
+
+ if (security_set_boolean_list(1, &b, 0) < 0) {
+ ret = -errno;
+ ERROR("setsebool: could not set %s to %s\n", name, value);
+ return ret;
}
- if (security_set_boolean_list(nargs - 1, b, 0) < 0)
- return -errno;
-#endif
return 0;
}
@@ -753,6 +800,8 @@ int do_wait(int nargs, char **args)
{
if (nargs == 2) {
return wait_for_file(args[1], COMMAND_RETRY_TIMEOUT);
- }
- return -1;
+ } else if (nargs == 3) {
+ return wait_for_file(args[1], atoi(args[2]));
+ } else
+ return -1;
}