diff options
Diffstat (limited to 'init/builtins.c')
| -rw-r--r-- | init/builtins.c | 261 |
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; } |
