summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Sharkey <jsharkey@android.com>2013-09-12 09:44:48 -0700
committerJeff Sharkey <jsharkey@android.com>2013-09-20 14:21:09 -0700
commit44d6342caa0db1f613809e9ba1ea8d9af0183b74 (patch)
treeaba3444fdb6ae17f890eb043ec0af72715be3d84
parent7b445a8e3a21055a5735585ca0890d14977032f0 (diff)
downloadcore-44d6342caa0db1f613809e9ba1ea8d9af0183b74.tar.gz
Remove mkdir() side effect, add .nomedia, utils.
Before this change, FUSE lookup() would have the side effect of creating the directory on behalf of apps. This resulted in most directories being created just by Settings trying to measure disk space. Instead, we're switching to have vold do directory creation when an app doesn't have enough permissions. Create fs_mkdirs() utility to create all parent directories in a path as needed. Allow traversal (+x) into /storage directories. Fix FUSE derived permissions to be case insensitive. Mark well-known directories as .nomedia when created. Bug: 10577808, 10330221 Change-Id: I53114f2e63ffbe6de4ba6a72d94a232523231cad
-rw-r--r--include/cutils/fs.h8
-rw-r--r--libcutils/fs.c90
-rw-r--r--rootdir/init.rc2
-rw-r--r--sdcard/sdcard.c110
4 files changed, 158 insertions, 52 deletions
diff --git a/include/cutils/fs.h b/include/cutils/fs.h
index fd5296bb9..d1d4cf28d 100644
--- a/include/cutils/fs.h
+++ b/include/cutils/fs.h
@@ -55,6 +55,14 @@ extern int fs_read_atomic_int(const char* path, int* value);
*/
extern int fs_write_atomic_int(const char* path, int value);
+/*
+ * Ensure that all directories along given path exist, creating parent
+ * directories as needed. Validates that given path is absolute and that
+ * it contains no relative "." or ".." paths or symlinks. Last path segment
+ * is treated as filename and ignored, unless the path ends with "/".
+ */
+extern int fs_mkdirs(const char* path, mode_t mode);
+
#ifdef __cplusplus
}
#endif
diff --git a/libcutils/fs.c b/libcutils/fs.c
index 116526dca..8d1da21c8 100644
--- a/libcutils/fs.c
+++ b/libcutils/fs.c
@@ -16,6 +16,11 @@
#define LOG_TAG "cutils"
+/* These defines are only needed because prebuilt headers are out of date */
+#define __USE_XOPEN2K8 1
+#define _ATFILE_SOURCE 1
+#define _GNU_SOURCE 1
+
#include <cutils/fs.h>
#include <cutils/log.h>
@@ -27,6 +32,7 @@
#include <string.h>
#include <limits.h>
#include <stdlib.h>
+#include <dirent.h>
#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
#define BUF_SIZE 64
@@ -141,3 +147,87 @@ fail_closed:
unlink(temp);
return -1;
}
+
+int fs_mkdirs(const char* path, mode_t mode) {
+ int res = 0;
+ int fd = 0;
+ struct stat sb;
+ char* buf = strdup(path);
+
+ if (*buf != '/') {
+ ALOGE("Relative paths are not allowed: %s", buf);
+ res = -EINVAL;
+ goto done;
+ }
+
+ if ((fd = open("/", 0)) == -1) {
+ ALOGE("Failed to open(/): %s", strerror(errno));
+ res = -errno;
+ goto done;
+ }
+
+ char* segment = buf + 1;
+ char* p = segment;
+ while (*p != '\0') {
+ if (*p == '/') {
+ *p = '\0';
+
+ if (!strcmp(segment, "..") || !strcmp(segment, ".") || !strcmp(segment, "")) {
+ ALOGE("Invalid path: %s", buf);
+ res = -EINVAL;
+ goto done_close;
+ }
+
+ if (fstatat(fd, segment, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
+ if (errno == ENOENT) {
+ /* Nothing there yet; let's create it! */
+ if (mkdirat(fd, segment, mode) != 0) {
+ if (errno == EEXIST) {
+ /* We raced with someone; ignore */
+ } else {
+ ALOGE("Failed to mkdirat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+ }
+ } else {
+ ALOGE("Failed to fstatat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+ } else {
+ if (S_ISLNK(sb.st_mode)) {
+ ALOGE("Symbolic links are not allowed: %s", buf);
+ res = -ELOOP;
+ goto done_close;
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ ALOGE("Existing segment not a directory: %s", buf);
+ res = -ENOTDIR;
+ goto done_close;
+ }
+ }
+
+ /* Yay, segment is ready for us to step into */
+ int next_fd;
+ if ((next_fd = openat(fd, segment, 0)) == -1) {
+ ALOGE("Failed to openat(%s): %s", buf, strerror(errno));
+ res = -errno;
+ goto done_close;
+ }
+
+ close(fd);
+ fd = next_fd;
+
+ *p = '/';
+ segment = p + 1;
+ }
+ p++;
+ }
+
+done_close:
+ close(fd);
+done:
+ free(buf);
+ return res;
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index be74f6fc2..cd995742f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -61,7 +61,7 @@ loglevel 3
# See storage config details at http://source.android.com/tech/storage/
mkdir /mnt/shell 0700 shell shell
- mkdir /storage 0050 root sdcard_r
+ mkdir /storage 0751 root sdcard_r
# Directory for putting things only root should see.
mkdir /mnt/secure 0700 root root
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index 9a1dd17b7..3f1e268e6 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -32,6 +32,7 @@
#include <sys/resource.h>
#include <sys/inotify.h>
+#include <cutils/fs.h>
#include <cutils/hashmap.h>
#include <cutils/multiuser.h>
@@ -193,8 +194,9 @@ static int str_hash(void *key) {
return hashmapHash(key, strlen(key));
}
-static bool str_equals(void *keyA, void *keyB) {
- return strcmp(keyA, keyB) == 0;
+/** Test if two string keys are equal ignoring case */
+static bool str_icase_equals(void *keyA, void *keyB) {
+ return strcasecmp(keyA, keyB) == 0;
}
static int int_hash(void *key) {
@@ -401,6 +403,20 @@ static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const s
attr->mode = (attr->mode & S_IFMT) | filtered_mode;
}
+static int touch(char* path, mode_t mode) {
+ int fd = open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, mode);
+ if (fd == -1) {
+ if (errno == EEXIST) {
+ return 0;
+ } else {
+ ERROR("Failed to open(%s): %s\n", path, strerror(errno));
+ return -1;
+ }
+ }
+ close(fd);
+ return 0;
+}
+
static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
struct node *node) {
appid_t appid;
@@ -429,37 +445,37 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
case PERM_ROOT:
/* Assume masked off by default. */
node->mode = 0770;
- if (!strcmp(node->name, "Android")) {
+ if (!strcasecmp(node->name, "Android")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID;
node->mode = 0771;
} else if (fuse->split_perms) {
- if (!strcmp(node->name, "DCIM")
- || !strcmp(node->name, "Pictures")) {
+ if (!strcasecmp(node->name, "DCIM")
+ || !strcasecmp(node->name, "Pictures")) {
node->gid = AID_SDCARD_PICS;
- } else if (!strcmp(node->name, "Alarms")
- || !strcmp(node->name, "Movies")
- || !strcmp(node->name, "Music")
- || !strcmp(node->name, "Notifications")
- || !strcmp(node->name, "Podcasts")
- || !strcmp(node->name, "Ringtones")) {
+ } else if (!strcasecmp(node->name, "Alarms")
+ || !strcasecmp(node->name, "Movies")
+ || !strcasecmp(node->name, "Music")
+ || !strcasecmp(node->name, "Notifications")
+ || !strcasecmp(node->name, "Podcasts")
+ || !strcasecmp(node->name, "Ringtones")) {
node->gid = AID_SDCARD_AV;
}
}
break;
case PERM_ANDROID:
- if (!strcmp(node->name, "data")) {
+ if (!strcasecmp(node->name, "data")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID_DATA;
node->mode = 0771;
- } else if (!strcmp(node->name, "obb")) {
+ } else if (!strcasecmp(node->name, "obb")) {
/* App-specific directories inside; let anyone traverse */
node->perm = PERM_ANDROID_OBB;
node->mode = 0771;
/* Single OBB directory is always shared */
node->graft_path = fuse->obbpath;
node->graft_pathlen = strlen(fuse->obbpath);
- } else if (!strcmp(node->name, "user")) {
+ } else if (!strcasecmp(node->name, "user")) {
/* User directories must only be accessible to system, protected
* by sdcard_all. Zygote will bind mount the appropriate user-
* specific path. */
@@ -505,9 +521,9 @@ static bool check_caller_access_to_name(struct fuse* fuse,
const char* name, int mode, bool has_rw) {
/* Always block security-sensitive files at root */
if (parent_node && parent_node->perm == PERM_ROOT) {
- if (!strcmp(name, "autorun.inf")
- || !strcmp(name, ".android_secure")
- || !strcmp(name, "android_secure")) {
+ if (!strcasecmp(name, "autorun.inf")
+ || !strcasecmp(name, ".android_secure")
+ || !strcasecmp(name, "android_secure")) {
return false;
}
}
@@ -517,8 +533,9 @@ static bool check_caller_access_to_name(struct fuse* fuse,
return true;
}
- /* Root or shell always have access */
- if (hdr->uid == 0 || hdr->uid == AID_SHELL) {
+ /* Root always has access; access for any other UIDs should always
+ * be controlled through packages.list. */
+ if (hdr->uid == 0) {
return true;
}
@@ -696,9 +713,10 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
fuse->root.perm = PERM_LEGACY_PRE_ROOT;
fuse->root.mode = 0771;
fuse->root.gid = fs_gid;
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals);
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
+ fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
break;
case DERIVE_UNIFIED:
/* Unified multiuser layout which places secondary user_id under
@@ -706,7 +724,7 @@ static void fuse_init(struct fuse *fuse, int fd, const char *source_path,
fuse->root.perm = PERM_ROOT;
fuse->root.mode = 0771;
fuse->root.gid = fs_gid;
- fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals);
+ fuse->package_to_appid = hashmapCreate(256, str_hash, str_icase_equals);
fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
break;
@@ -752,36 +770,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
struct stat s;
if (lstat(path, &s) < 0) {
- /* But wait! We'll automatically create a directory if its
- * a valid package name under data or obb, since apps may not
- * have enough permissions to create for themselves. */
- if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA
- || parent->perm == PERM_ANDROID_OBB)) {
- TRACE("automatically creating %s\n", path);
-
- pthread_mutex_lock(&fuse->lock);
- bool validPackage = hashmapContainsKey(fuse->package_to_appid, (char*) name);
- pthread_mutex_unlock(&fuse->lock);
-
- if (!validPackage) {
- return -ENOENT;
- }
- if (mkdir(path, 0775) == -1) {
- /* We might have raced with ourselves and already created */
- if (errno != EEXIST) {
- ERROR("failed to mkdir(%s): %s\n", name, strerror(errno));
- return -ENOENT;
- }
- }
-
- /* It should exist this time around! */
- if (lstat(path, &s) < 0) {
- ERROR("failed to lstat(%s): %s\n", name, strerror(errno));
- return -errno;
- }
- } else {
- return -errno;
- }
+ return -errno;
}
pthread_mutex_lock(&fuse->lock);
@@ -1006,6 +995,25 @@ static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
if (mkdir(child_path, mode) < 0) {
return -errno;
}
+
+ /* When creating /Android/data and /Android/obb, mark them as .nomedia */
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
+ if (touch(nomedia, 0664) != 0) {
+ ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+ return -ENOENT;
+ }
+ }
+ if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
+ char nomedia[PATH_MAX];
+ snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->obbpath);
+ if (touch(nomedia, 0664) != 0) {
+ ERROR("Failed to touch(%s): %s\n", nomedia, strerror(errno));
+ return -ENOENT;
+ }
+ }
+
return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
}