summaryrefslogtreecommitdiffstats
path: root/cmds/installd/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/installd/utils.c')
-rw-r--r--cmds/installd/utils.c278
1 files changed, 220 insertions, 58 deletions
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index ef634c6..e381aef 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -1,16 +1,16 @@
/*
** Copyright 2008, 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.
-** You may obtain a copy of the License at
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
@@ -149,6 +149,17 @@ int create_user_media_path(char path[PATH_MAX], userid_t userid) {
return 0;
}
+/**
+ * Create the path name for config for a certain userid.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_user_config_path(char path[PATH_MAX], userid_t userid) {
+ if (snprintf(path, PATH_MAX, "%s%d", "/data/misc/user/", userid) > PATH_MAX) {
+ return -1;
+ }
+ return 0;
+}
+
int create_move_path(char path[PKG_PATH_MAX],
const char* pkgname,
const char* leaf,
@@ -208,7 +219,8 @@ int is_valid_package_name(const char* pkgname) {
return 0;
}
-static int _delete_dir_contents(DIR *d, const char *ignore)
+static int _delete_dir_contents(DIR *d,
+ int (*exclusion_predicate)(const char *name, const int is_dir))
{
int result = 0;
struct dirent *de;
@@ -221,8 +233,10 @@ static int _delete_dir_contents(DIR *d, const char *ignore)
while ((de = readdir(d))) {
const char *name = de->d_name;
- /* skip the ignore name if provided */
- if (ignore && !strcmp(name, ignore)) continue;
+ /* check using the exclusion predicate, if provided */
+ if (exclusion_predicate && exclusion_predicate(name, (de->d_type == DT_DIR))) {
+ continue;
+ }
if (de->d_type == DT_DIR) {
int r, subfd;
@@ -247,7 +261,7 @@ static int _delete_dir_contents(DIR *d, const char *ignore)
result = -1;
continue;
}
- if (_delete_dir_contents(subdir, 0)) {
+ if (_delete_dir_contents(subdir, exclusion_predicate)) {
result = -1;
}
closedir(subdir);
@@ -268,7 +282,7 @@ static int _delete_dir_contents(DIR *d, const char *ignore)
int delete_dir_contents(const char *pathname,
int also_delete_dir,
- const char *ignore)
+ int (*exclusion_predicate)(const char*, const int))
{
int res = 0;
DIR *d;
@@ -278,7 +292,7 @@ int delete_dir_contents(const char *pathname,
ALOGE("Couldn't opendir %s: %s\n", pathname, strerror(errno));
return -errno;
}
- res = _delete_dir_contents(d, ignore);
+ res = _delete_dir_contents(d, exclusion_predicate);
closedir(d);
if (also_delete_dir) {
if (rmdir(pathname)) {
@@ -310,6 +324,104 @@ int delete_dir_contents_fd(int dfd, const char *name)
return res;
}
+static int _copy_owner_permissions(int srcfd, int dstfd)
+{
+ struct stat st;
+ if (fstat(srcfd, &st) != 0) {
+ return -1;
+ }
+ if (fchmod(dstfd, st.st_mode) != 0) {
+ return -1;
+ }
+ return 0;
+}
+
+static int _copy_dir_files(int sdfd, int ddfd, uid_t owner, gid_t group)
+{
+ int result = 0;
+ if (_copy_owner_permissions(sdfd, ddfd) != 0) {
+ ALOGE("_copy_dir_files failed to copy dir permissions\n");
+ }
+ if (fchown(ddfd, owner, group) != 0) {
+ ALOGE("_copy_dir_files failed to change dir owner\n");
+ }
+
+ DIR *ds = fdopendir(sdfd);
+ if (ds == NULL) {
+ ALOGE("Couldn't fdopendir: %s\n", strerror(errno));
+ return -1;
+ }
+ struct dirent *de;
+ while ((de = readdir(ds))) {
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+
+ const char *name = de->d_name;
+ int fsfd = openat(sdfd, name, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ int fdfd = openat(ddfd, name, O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_CREAT, 0600);
+ if (fsfd == -1 || fdfd == -1) {
+ ALOGW("Couldn't copy %s: %s\n", name, strerror(errno));
+ } else {
+ if (_copy_owner_permissions(fsfd, fdfd) != 0) {
+ ALOGE("Failed to change file permissions\n");
+ }
+ if (fchown(fdfd, owner, group) != 0) {
+ ALOGE("Failed to change file owner\n");
+ }
+
+ char buf[8192];
+ ssize_t size;
+ while ((size = read(fsfd, buf, sizeof(buf))) > 0) {
+ write(fdfd, buf, size);
+ }
+ if (size < 0) {
+ ALOGW("Couldn't copy %s: %s\n", name, strerror(errno));
+ result = -1;
+ }
+ }
+ close(fdfd);
+ close(fsfd);
+ }
+
+ return result;
+}
+
+int copy_dir_files(const char *srcname,
+ const char *dstname,
+ uid_t owner,
+ uid_t group)
+{
+ int res = 0;
+ DIR *ds = NULL;
+ DIR *dd = NULL;
+
+ ds = opendir(srcname);
+ if (ds == NULL) {
+ ALOGE("Couldn't opendir %s: %s\n", srcname, strerror(errno));
+ return -errno;
+ }
+
+ mkdir(dstname, 0600);
+ dd = opendir(dstname);
+ if (dd == NULL) {
+ ALOGE("Couldn't opendir %s: %s\n", dstname, strerror(errno));
+ closedir(ds);
+ return -errno;
+ }
+
+ int sdfd = dirfd(ds);
+ int ddfd = dirfd(dd);
+ if (sdfd != -1 && ddfd != -1) {
+ res = _copy_dir_files(sdfd, ddfd, owner, group);
+ } else {
+ res = -errno;
+ }
+ closedir(dd);
+ closedir(ds);
+ return res;
+}
+
int lookup_media_dir(char basepath[PATH_MAX], const char *dir)
{
DIR *d;
@@ -435,7 +547,7 @@ static void _inc_num_cache_collected(cache_t* cache)
{
cache->numCollected++;
if ((cache->numCollected%20000) == 0) {
- ALOGI("Collected cache so far: %d directories, %d files",
+ ALOGI("Collected cache so far: %zd directories, %zd files",
cache->numDirs, cache->numFiles);
}
}
@@ -730,7 +842,7 @@ void clear_cache_files(cache_t* cache, int64_t free_size)
int skip = 0;
char path[PATH_MAX];
- ALOGI("Collected cache files: %d directories, %d files",
+ ALOGI("Collected cache files: %zd directories, %zd files",
cache->numDirs, cache->numFiles);
CACHE_NOISY(ALOGI("Sorting files..."));
@@ -794,6 +906,33 @@ void finish_cache_collection(cache_t* cache)
}
/**
+ * Validate that the path is valid in the context of the provided directory.
+ * The path is allowed to have at most one subdirectory and no indirections
+ * to top level directories (i.e. have "..").
+ */
+static int validate_path(const dir_rec_t* dir, const char* path) {
+ size_t dir_len = dir->len;
+ const char* subdir = strchr(path + dir_len, '/');
+
+ // Only allow the path to have at most one subdirectory.
+ if (subdir != NULL) {
+ ++subdir;
+ if (strchr(subdir, '/') != NULL) {
+ ALOGE("invalid apk path '%s' (subdir?)\n", path);
+ return -1;
+ }
+ }
+
+ // Directories can't have a period directly after the directory markers to prevent "..".
+ if ((path[dir_len] == '.') || ((subdir != NULL) && (*subdir == '.'))) {
+ ALOGE("invalid apk path '%s' (trickery)\n", path);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
* Checks whether a path points to a system app (.apk file). Returns 0
* if it is a system app or -1 if it is not.
*/
@@ -803,11 +942,7 @@ int validate_system_app_path(const char* path) {
for (i = 0; i < android_system_dirs.count; i++) {
const size_t dir_len = android_system_dirs.dirs[i].len;
if (!strncmp(path, android_system_dirs.dirs[i].path, dir_len)) {
- if (path[dir_len] == '.' || strchr(path + dir_len, '/') != NULL) {
- ALOGE("invalid system apk path '%s' (trickery)\n", path);
- return -1;
- }
- return 0;
+ return validate_path(android_system_dirs.dirs + i, path);
}
}
@@ -900,54 +1035,25 @@ int copy_and_append(dir_rec_t* dst, const dir_rec_t* src, const char* suffix) {
}
/**
- * Check whether path points to a valid path for an APK file. An ASEC
- * directory is allowed to have one level of subdirectory names. Returns -1
- * when an invalid path is encountered and 0 when a valid path is encountered.
+ * Check whether path points to a valid path for an APK file. Only one level of
+ * subdirectory names is allowed. Returns -1 when an invalid path is encountered
+ * and 0 when a valid path is encountered.
*/
int validate_apk_path(const char *path)
{
- int allowsubdir = 0;
- char *subdir = NULL;
- size_t dir_len;
- size_t path_len;
+ const dir_rec_t* dir = NULL;
if (!strncmp(path, android_app_dir.path, android_app_dir.len)) {
- dir_len = android_app_dir.len;
+ dir = &android_app_dir;
} else if (!strncmp(path, android_app_private_dir.path, android_app_private_dir.len)) {
- dir_len = android_app_private_dir.len;
+ dir = &android_app_private_dir;
} else if (!strncmp(path, android_asec_dir.path, android_asec_dir.len)) {
- dir_len = android_asec_dir.len;
- allowsubdir = 1;
+ dir = &android_asec_dir;
} else {
- ALOGE("invalid apk path '%s' (bad prefix)\n", path);
return -1;
}
- path_len = strlen(path);
-
- /*
- * Only allow the path to have a subdirectory if it's been marked as being allowed.
- */
- if ((subdir = strchr(path + dir_len, '/')) != NULL) {
- ++subdir;
- if (!allowsubdir
- || (path_len > (size_t) (subdir - path) && (strchr(subdir, '/') != NULL))) {
- ALOGE("invalid apk path '%s' (subdir?)\n", path);
- return -1;
- }
- }
-
- /*
- * Directories can't have a period directly after the directory markers
- * to prevent ".."
- */
- if (path[dir_len] == '.'
- || (subdir != NULL && ((*subdir == '.') || (strchr(subdir, '/') != NULL)))) {
- ALOGE("invalid apk path '%s' (trickery)\n", path);
- return -1;
- }
-
- return 0;
+ return validate_path(dir, path);
}
int append_and_increment(char** dst, const char* src, size_t* dst_size) {
@@ -1005,3 +1111,59 @@ int ensure_media_user_dirs(userid_t userid) {
return 0;
}
+
+int ensure_config_user_dirs(userid_t userid) {
+ char config_user_path[PATH_MAX];
+ char path[PATH_MAX];
+
+ // writable by system, readable by any app within the same user
+ const int uid = multiuser_get_uid(userid, AID_SYSTEM);
+ const int gid = multiuser_get_uid(userid, AID_EVERYBODY);
+
+ // Ensure /data/misc/user/<userid> exists
+ create_user_config_path(config_user_path, userid);
+ if (fs_prepare_dir(config_user_path, 0750, uid, gid) == -1) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int create_profile_file(const char *pkgname, gid_t gid) {
+ const char *profile_dir = DALVIK_CACHE_PREFIX "profiles";
+ char profile_file[PKG_PATH_MAX];
+
+ snprintf(profile_file, sizeof(profile_file), "%s/%s", profile_dir, pkgname);
+
+ // The 'system' user needs to be able to read the profile to determine if dex2oat
+ // needs to be run. This is done in dalvik.system.DexFile.isDexOptNeededInternal(). So
+ // we assign ownership to AID_SYSTEM and ensure it's not world-readable.
+
+ int fd = open(profile_file, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0660);
+
+ // Always set the uid/gid/permissions. The file could have been previously created
+ // with different permissions.
+ if (fd >= 0) {
+ if (fchown(fd, AID_SYSTEM, gid) < 0) {
+ ALOGE("cannot chown profile file '%s': %s\n", profile_file, strerror(errno));
+ close(fd);
+ unlink(profile_file);
+ return -1;
+ }
+
+ if (fchmod(fd, 0660) < 0) {
+ ALOGE("cannot chmod profile file '%s': %s\n", profile_file, strerror(errno));
+ close(fd);
+ unlink(profile_file);
+ return -1;
+ }
+ close(fd);
+ }
+ return 0;
+}
+
+void remove_profile_file(const char *pkgname) {
+ char profile_file[PKG_PATH_MAX];
+ snprintf(profile_file, sizeof(profile_file), "%s/%s", DALVIK_CACHE_PREFIX "profiles", pkgname);
+ unlink(profile_file);
+}