Loading include/cutils/fs.h +8 −0 Original line number Diff line number Diff line Loading @@ -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 Loading libcutils/fs.c +90 −0 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 Loading Loading @@ -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; } rootdir/init.rc +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading sdcard/sdcard.c +59 −51 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <sys/resource.h> #include <sys/inotify.h> #include <cutils/fs.h> #include <cutils/hashmap.h> #include <cutils/multiuser.h> Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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; } } Loading @@ -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; } Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -752,37 +770,8 @@ 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; } } pthread_mutex_lock(&fuse->lock); node = acquire_or_create_child_locked(fuse, parent, name, actual_name); Loading Loading @@ -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); } Loading Loading
include/cutils/fs.h +8 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
libcutils/fs.c +90 −0 Original line number Diff line number Diff line Loading @@ -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> Loading @@ -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 Loading Loading @@ -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; }
rootdir/init.rc +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
sdcard/sdcard.c +59 −51 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ #include <sys/resource.h> #include <sys/inotify.h> #include <cutils/fs.h> #include <cutils/hashmap.h> #include <cutils/multiuser.h> Loading Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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. */ Loading Loading @@ -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; } } Loading @@ -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; } Loading Loading @@ -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 Loading @@ -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; Loading Loading @@ -752,37 +770,8 @@ 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; } } pthread_mutex_lock(&fuse->lock); node = acquire_or_create_child_locked(fuse, parent, name, actual_name); Loading Loading @@ -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); } Loading