am 44d6342c: Remove mkdir() side effect, add .nomedia, utils.
* commit '44d6342caa0db1f613809e9ba1ea8d9af0183b74': Remove mkdir() side effect, add .nomedia, utils.
This commit is contained in:
commit
9729b41574
4 changed files with 158 additions and 52 deletions
|
|
@ -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);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,11 @@
|
||||||
|
|
||||||
#define LOG_TAG "cutils"
|
#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/fs.h>
|
||||||
#include <cutils/log.h>
|
#include <cutils/log.h>
|
||||||
|
|
||||||
|
|
@ -27,6 +32,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
|
#define ALL_PERMS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
|
||||||
#define BUF_SIZE 64
|
#define BUF_SIZE 64
|
||||||
|
|
@ -141,3 +147,87 @@ fail_closed:
|
||||||
unlink(temp);
|
unlink(temp);
|
||||||
return -1;
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ loglevel 3
|
||||||
|
|
||||||
# See storage config details at http://source.android.com/tech/storage/
|
# See storage config details at http://source.android.com/tech/storage/
|
||||||
mkdir /mnt/shell 0700 shell shell
|
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.
|
# Directory for putting things only root should see.
|
||||||
mkdir /mnt/secure 0700 root root
|
mkdir /mnt/secure 0700 root root
|
||||||
|
|
|
||||||
110
sdcard/sdcard.c
110
sdcard/sdcard.c
|
|
@ -32,6 +32,7 @@
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
|
|
||||||
|
#include <cutils/fs.h>
|
||||||
#include <cutils/hashmap.h>
|
#include <cutils/hashmap.h>
|
||||||
#include <cutils/multiuser.h>
|
#include <cutils/multiuser.h>
|
||||||
|
|
||||||
|
|
@ -193,8 +194,9 @@ static int str_hash(void *key) {
|
||||||
return hashmapHash(key, strlen(key));
|
return hashmapHash(key, strlen(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool str_equals(void *keyA, void *keyB) {
|
/** Test if two string keys are equal ignoring case */
|
||||||
return strcmp(keyA, keyB) == 0;
|
static bool str_icase_equals(void *keyA, void *keyB) {
|
||||||
|
return strcasecmp(keyA, keyB) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int int_hash(void *key) {
|
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;
|
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,
|
static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
|
||||||
struct node *node) {
|
struct node *node) {
|
||||||
appid_t appid;
|
appid_t appid;
|
||||||
|
|
@ -429,37 +445,37 @@ static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
|
||||||
case PERM_ROOT:
|
case PERM_ROOT:
|
||||||
/* Assume masked off by default. */
|
/* Assume masked off by default. */
|
||||||
node->mode = 0770;
|
node->mode = 0770;
|
||||||
if (!strcmp(node->name, "Android")) {
|
if (!strcasecmp(node->name, "Android")) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
node->perm = PERM_ANDROID;
|
node->perm = PERM_ANDROID;
|
||||||
node->mode = 0771;
|
node->mode = 0771;
|
||||||
} else if (fuse->split_perms) {
|
} else if (fuse->split_perms) {
|
||||||
if (!strcmp(node->name, "DCIM")
|
if (!strcasecmp(node->name, "DCIM")
|
||||||
|| !strcmp(node->name, "Pictures")) {
|
|| !strcasecmp(node->name, "Pictures")) {
|
||||||
node->gid = AID_SDCARD_PICS;
|
node->gid = AID_SDCARD_PICS;
|
||||||
} else if (!strcmp(node->name, "Alarms")
|
} else if (!strcasecmp(node->name, "Alarms")
|
||||||
|| !strcmp(node->name, "Movies")
|
|| !strcasecmp(node->name, "Movies")
|
||||||
|| !strcmp(node->name, "Music")
|
|| !strcasecmp(node->name, "Music")
|
||||||
|| !strcmp(node->name, "Notifications")
|
|| !strcasecmp(node->name, "Notifications")
|
||||||
|| !strcmp(node->name, "Podcasts")
|
|| !strcasecmp(node->name, "Podcasts")
|
||||||
|| !strcmp(node->name, "Ringtones")) {
|
|| !strcasecmp(node->name, "Ringtones")) {
|
||||||
node->gid = AID_SDCARD_AV;
|
node->gid = AID_SDCARD_AV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PERM_ANDROID:
|
case PERM_ANDROID:
|
||||||
if (!strcmp(node->name, "data")) {
|
if (!strcasecmp(node->name, "data")) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
node->perm = PERM_ANDROID_DATA;
|
node->perm = PERM_ANDROID_DATA;
|
||||||
node->mode = 0771;
|
node->mode = 0771;
|
||||||
} else if (!strcmp(node->name, "obb")) {
|
} else if (!strcasecmp(node->name, "obb")) {
|
||||||
/* App-specific directories inside; let anyone traverse */
|
/* App-specific directories inside; let anyone traverse */
|
||||||
node->perm = PERM_ANDROID_OBB;
|
node->perm = PERM_ANDROID_OBB;
|
||||||
node->mode = 0771;
|
node->mode = 0771;
|
||||||
/* Single OBB directory is always shared */
|
/* Single OBB directory is always shared */
|
||||||
node->graft_path = fuse->obbpath;
|
node->graft_path = fuse->obbpath;
|
||||||
node->graft_pathlen = strlen(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
|
/* User directories must only be accessible to system, protected
|
||||||
* by sdcard_all. Zygote will bind mount the appropriate user-
|
* by sdcard_all. Zygote will bind mount the appropriate user-
|
||||||
* specific path. */
|
* specific path. */
|
||||||
|
|
@ -505,9 +521,9 @@ static bool check_caller_access_to_name(struct fuse* fuse,
|
||||||
const char* name, int mode, bool has_rw) {
|
const char* name, int mode, bool has_rw) {
|
||||||
/* Always block security-sensitive files at root */
|
/* Always block security-sensitive files at root */
|
||||||
if (parent_node && parent_node->perm == PERM_ROOT) {
|
if (parent_node && parent_node->perm == PERM_ROOT) {
|
||||||
if (!strcmp(name, "autorun.inf")
|
if (!strcasecmp(name, "autorun.inf")
|
||||||
|| !strcmp(name, ".android_secure")
|
|| !strcasecmp(name, ".android_secure")
|
||||||
|| !strcmp(name, "android_secure")) {
|
|| !strcasecmp(name, "android_secure")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -517,8 +533,9 @@ static bool check_caller_access_to_name(struct fuse* fuse,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Root or shell always have access */
|
/* Root always has access; access for any other UIDs should always
|
||||||
if (hdr->uid == 0 || hdr->uid == AID_SHELL) {
|
* be controlled through packages.list. */
|
||||||
|
if (hdr->uid == 0) {
|
||||||
return true;
|
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.perm = PERM_LEGACY_PRE_ROOT;
|
||||||
fuse->root.mode = 0771;
|
fuse->root.mode = 0771;
|
||||||
fuse->root.gid = fs_gid;
|
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);
|
fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
|
||||||
snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
|
snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/obb", source_path);
|
||||||
|
fs_prepare_dir(fuse->obbpath, 0775, getuid(), getgid());
|
||||||
break;
|
break;
|
||||||
case DERIVE_UNIFIED:
|
case DERIVE_UNIFIED:
|
||||||
/* Unified multiuser layout which places secondary user_id under
|
/* 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.perm = PERM_ROOT;
|
||||||
fuse->root.mode = 0771;
|
fuse->root.mode = 0771;
|
||||||
fuse->root.gid = fs_gid;
|
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);
|
fuse->appid_with_rw = hashmapCreate(128, int_hash, int_equals);
|
||||||
snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
|
snprintf(fuse->obbpath, sizeof(fuse->obbpath), "%s/Android/obb", source_path);
|
||||||
break;
|
break;
|
||||||
|
|
@ -752,36 +770,7 @@ static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
|
||||||
struct stat s;
|
struct stat s;
|
||||||
|
|
||||||
if (lstat(path, &s) < 0) {
|
if (lstat(path, &s) < 0) {
|
||||||
/* But wait! We'll automatically create a directory if its
|
return -errno;
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_lock(&fuse->lock);
|
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) {
|
if (mkdir(child_path, mode) < 0) {
|
||||||
return -errno;
|
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);
|
return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue