Merge "Create a separate copy of the fsck logs" into klp-dev
diff --git a/include/cutils/fs.h b/include/cutils/fs.h
index fd5296b..d1d4cf2 100644
--- a/include/cutils/fs.h
+++ b/include/cutils/fs.h
@@ -55,6 +55,14 @@
  */
 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 116526d..286a8eb 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,91 @@
     unlink(temp);
     return -1;
 }
+
+#ifndef __APPLE__
+
+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;
+}
+
+#endif
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9eb78d8..8150a73 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -61,7 +61,7 @@
 
     # 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 9a1dd17..3f1e268 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 @@
     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 @@
     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 @@
     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 @@
         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 @@
         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 @@
         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 @@
         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 @@
     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 @@
     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);
 }