updater: introduce and set_metadata and set_metadata_recursive
Introduce two new updater functions:
* set_metadata
* set_metadata_recursive
Long term, these functions are intended to be more flexible replacements
for the following methods:
* set_perm
* set_perm_recursive
Usage:
set_metadata("filename", "key1", "value1", "key2", "value2", ...)
set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...)
Description:
set_metadata() and set_metadata_recursive() set the attributes on a file/directory
according to the key/value pairs provided. Today, the following keys are
supported:
* uid
* gid
* mode (set_perm_extd only)
* fmode (set_perm_extd_recursive only)
* dmode (set_perm_extd_recursive only)
* selabel
* capabilities
Unknown keys are logged as warnings, but are not fatal errors.
Examples:
* set_metadata("/system/bin/netcfg", "selabel", "u:object_r:system_file:s0");
This sets the SELinux label of /system/bin/netcfg to u:object_r:system_file:s0.
No other changes occur.
* set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
This sets /system/bin/netcfg to uid=0, gid=3003, mode=02750,
selinux label=u:object_r:system_file:s0, and clears the capabilities
associated with the file.
* set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
All files and directories under /system are set to uid=0, gid=0,
and selinux label=u:object_r:system_file:s0. Directories are set to
mode=0755. Files are set to mode=0644 and all capabilities are cleared.
Bug: 10183961
Bug: 10186213
Bug: 8985290
Change-Id: Ifdcf186a7ed45265511dc493c4036e1ac5e3d0af
diff --git a/updater/install.c b/updater/install.c
index 9fa06a2..770dbd0 100644
--- a/updater/install.c
+++ b/updater/install.c
@@ -27,6 +27,12 @@
#include <unistd.h>
#include <fcntl.h>
#include <time.h>
+#include <selinux/selinux.h>
+#include <ftw.h>
+#include <sys/capability.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+#include <inttypes.h>
#include "cutils/misc.h"
#include "cutils/properties.h"
@@ -600,6 +606,259 @@
return StringValue(result);
}
+struct perm_parsed_args {
+ bool has_uid;
+ uid_t uid;
+ bool has_gid;
+ gid_t gid;
+ bool has_mode;
+ mode_t mode;
+ bool has_fmode;
+ mode_t fmode;
+ bool has_dmode;
+ mode_t dmode;
+ bool has_selabel;
+ char* selabel;
+ bool has_capabilities;
+ uint64_t capabilities;
+};
+
+static struct perm_parsed_args ParsePermArgs(int argc, char** args) {
+ int i;
+ struct perm_parsed_args parsed;
+ int bad = 0;
+ static int max_warnings = 20;
+
+ memset(&parsed, 0, sizeof(parsed));
+
+ for (i = 1; i < argc; i += 2) {
+ if (strcmp("uid", args[i]) == 0) {
+ int64_t uid;
+ if (sscanf(args[i+1], "%" SCNd64, &uid) == 1) {
+ parsed.uid = uid;
+ parsed.has_uid = true;
+ } else {
+ printf("ParsePermArgs: invalid UID \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (strcmp("gid", args[i]) == 0) {
+ int64_t gid;
+ if (sscanf(args[i+1], "%" SCNd64, &gid) == 1) {
+ parsed.gid = gid;
+ parsed.has_gid = true;
+ } else {
+ printf("ParsePermArgs: invalid GID \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (strcmp("mode", args[i]) == 0) {
+ int32_t mode;
+ if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
+ parsed.mode = mode;
+ parsed.has_mode = true;
+ } else {
+ printf("ParsePermArgs: invalid mode \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (strcmp("dmode", args[i]) == 0) {
+ int32_t mode;
+ if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
+ parsed.dmode = mode;
+ parsed.has_dmode = true;
+ } else {
+ printf("ParsePermArgs: invalid dmode \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (strcmp("fmode", args[i]) == 0) {
+ int32_t mode;
+ if (sscanf(args[i+1], "%" SCNi32, &mode) == 1) {
+ parsed.fmode = mode;
+ parsed.has_fmode = true;
+ } else {
+ printf("ParsePermArgs: invalid fmode \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (strcmp("capabilities", args[i]) == 0) {
+ int64_t capabilities;
+ if (sscanf(args[i+1], "%" SCNi64, &capabilities) == 1) {
+ parsed.capabilities = capabilities;
+ parsed.has_capabilities = true;
+ } else {
+ printf("ParsePermArgs: invalid capabilities \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (strcmp("selabel", args[i]) == 0) {
+ if (args[i+1][0] != '\0') {
+ parsed.selabel = args[i+1];
+ parsed.has_selabel = true;
+ } else {
+ printf("ParsePermArgs: invalid selabel \"%s\"\n", args[i + 1]);
+ bad++;
+ }
+ continue;
+ }
+ if (max_warnings != 0) {
+ printf("ParsedPermArgs: unknown key \"%s\", ignoring\n", args[i]);
+ max_warnings--;
+ if (max_warnings == 0) {
+ printf("ParsedPermArgs: suppressing further warnings\n");
+ }
+ }
+ }
+ return parsed;
+}
+
+static int ApplyParsedPerms(
+ const char* filename,
+ const struct stat *statptr,
+ struct perm_parsed_args parsed)
+{
+ int bad = 0;
+
+ if (parsed.has_uid) {
+ if (chown(filename, parsed.uid, -1) < 0) {
+ printf("ApplyParsedPerms: chown of %s to %d failed: %s\n",
+ filename, parsed.uid, strerror(errno));
+ bad++;
+ }
+ }
+
+ if (parsed.has_gid) {
+ if (chown(filename, -1, parsed.gid) < 0) {
+ printf("ApplyParsedPerms: chgrp of %s to %d failed: %s\n",
+ filename, parsed.gid, strerror(errno));
+ bad++;
+ }
+ }
+
+ if (parsed.has_mode) {
+ if (chmod(filename, parsed.mode) < 0) {
+ printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.mode, strerror(errno));
+ bad++;
+ }
+ }
+
+ if (parsed.has_dmode && S_ISDIR(statptr->st_mode)) {
+ if (chmod(filename, parsed.dmode) < 0) {
+ printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.dmode, strerror(errno));
+ bad++;
+ }
+ }
+
+ if (parsed.has_fmode && S_ISREG(statptr->st_mode)) {
+ if (chmod(filename, parsed.fmode) < 0) {
+ printf("ApplyParsedPerms: chmod of %s to %d failed: %s\n",
+ filename, parsed.fmode, strerror(errno));
+ bad++;
+ }
+ }
+
+ if (parsed.has_selabel) {
+ // TODO: Don't silently ignore ENOTSUP
+ if (lsetfilecon(filename, parsed.selabel) && (errno != ENOTSUP)) {
+ printf("ApplyParsedPerms: lsetfilecon of %s to %s failed: %s\n",
+ filename, parsed.selabel, strerror(errno));
+ bad++;
+ }
+ }
+
+ if (parsed.has_capabilities && S_ISREG(statptr->st_mode)) {
+ if (parsed.capabilities == 0) {
+ if ((removexattr(filename, XATTR_NAME_CAPS) == -1) && (errno != ENODATA)) {
+ // Report failure unless it's ENODATA (attribute not set)
+ printf("ApplyParsedPerms: removexattr of %s to %" PRIx64 " failed: %s\n",
+ filename, parsed.capabilities, strerror(errno));
+ bad++;
+ }
+ } else {
+ struct vfs_cap_data cap_data;
+ memset(&cap_data, 0, sizeof(cap_data));
+ cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+ cap_data.data[0].permitted = (uint32_t) (parsed.capabilities & 0xffffffff);
+ cap_data.data[0].inheritable = 0;
+ cap_data.data[1].permitted = (uint32_t) (parsed.capabilities >> 32);
+ cap_data.data[1].inheritable = 0;
+ if (setxattr(filename, XATTR_NAME_CAPS, &cap_data, sizeof(cap_data), 0) < 0) {
+ printf("ApplyParsedPerms: setcap of %s to %" PRIx64 " failed: %s\n",
+ filename, parsed.capabilities, strerror(errno));
+ bad++;
+ }
+ }
+ }
+
+ return bad;
+}
+
+// nftw doesn't allow us to pass along context, so we need to use
+// global variables. *sigh*
+static struct perm_parsed_args recursive_parsed_args;
+
+static int do_SetMetadataRecursive(const char* filename, const struct stat *statptr,
+ int fileflags, struct FTW *pfwt) {
+ return ApplyParsedPerms(filename, statptr, recursive_parsed_args);
+}
+
+static Value* SetMetadataFn(const char* name, State* state, int argc, Expr* argv[]) {
+ int i;
+ int bad = 0;
+ static int nwarnings = 0;
+ struct stat sb;
+ Value* result = NULL;
+
+ bool recursive = (strcmp(name, "set_metadata_recursive") == 0);
+
+ if ((argc % 2) != 1) {
+ return ErrorAbort(state, "%s() expects an odd number of arguments, got %d",
+ name, argc);
+ }
+
+ char** args = ReadVarArgs(state, argc, argv);
+ if (args == NULL) return NULL;
+
+ if (lstat(args[0], &sb) == -1) {
+ result = ErrorAbort(state, "%s: Error on lstat of \"%s\": %s", name, args[0], strerror(errno));
+ goto done;
+ }
+
+ struct perm_parsed_args parsed = ParsePermArgs(argc, args);
+
+ if (recursive) {
+ recursive_parsed_args = parsed;
+ bad += nftw(args[0], do_SetMetadataRecursive, 30, FTW_CHDIR | FTW_DEPTH | FTW_PHYS);
+ memset(&recursive_parsed_args, 0, sizeof(recursive_parsed_args));
+ } else {
+ bad += ApplyParsedPerms(args[0], &sb, parsed);
+ }
+
+done:
+ for (i = 0; i < argc; ++i) {
+ free(args[i]);
+ }
+ free(args);
+
+ if (result != NULL) {
+ return result;
+ }
+
+ if (bad > 0) {
+ return ErrorAbort(state, "%s: some changes failed", name);
+ }
+
+ return StringValue(strdup(""));
+}
Value* GetPropFn(const char* name, State* state, int argc, Expr* argv[]) {
if (argc != 1) {
@@ -1133,9 +1392,24 @@
RegisterFunction("package_extract_dir", PackageExtractDirFn);
RegisterFunction("package_extract_file", PackageExtractFileFn);
RegisterFunction("symlink", SymlinkFn);
+
+ // Maybe, at some future point, we can delete these functions? They have been
+ // replaced by perm_set and perm_set_recursive.
RegisterFunction("set_perm", SetPermFn);
RegisterFunction("set_perm_recursive", SetPermFn);
+ // Usage:
+ // set_metadata("filename", "key1", "value1", "key2", "value2", ...)
+ // Example:
+ // set_metadata("/system/bin/netcfg", "uid", 0, "gid", 3003, "mode", 02750, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
+ RegisterFunction("set_metadata", SetMetadataFn);
+
+ // Usage:
+ // set_metadata_recursive("dirname", "key1", "value1", "key2", "value2", ...)
+ // Example:
+ // set_metadata_recursive("/system", "uid", 0, "gid", 0, "fmode", 0644, "dmode", 0755, "selabel", "u:object_r:system_file:s0", "capabilities", 0x0);
+ RegisterFunction("set_metadata_recursive", SetMetadataFn);
+
RegisterFunction("getprop", GetPropFn);
RegisterFunction("file_getprop", FileGetPropFn);
RegisterFunction("write_raw_image", WriteRawImageFn);