am 575a2bbe: sdcard: Remove lower case squashing of file names

* commit '575a2bbee30d70a1940077a5b699aaf93cb4e2cd':
  sdcard: Remove lower case squashing of file names
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index cc2cce7..9bb9d4b 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -95,6 +95,12 @@
     __u32 namelen;
 
     char *name;
+    /* If non-null, this is the real name of the file in the underlying storage.
+     * This may differ from the field "name" only by case.
+     * strlen(actual_name) will always equal strlen(name), so it is safe to use
+     * namelen for both fields.
+     */
+    char *actual_name;
 };
 
 struct fuse {
@@ -109,21 +115,13 @@
     char rootpath[1024];
 };
 
-/* true if file names should be squashed to lower case */
-static int force_lower_case = 0;
 static unsigned uid = -1;
 static unsigned gid = -1;
 
 #define PATH_BUFFER_SIZE 1024
 
-static void normalize_name(char *name)
-{
-    if (force_lower_case) {
-        char ch;
-        while ((ch = *name) != 0)
-            *name++ = tolower(ch);
-    }
-}
+#define NO_CASE_SENSITIVE_MATCH 0
+#define CASE_SENSITIVE_MATCH 1
 
 /*
  * Get the real-life absolute path to a node.
@@ -131,8 +129,10 @@
  *   buf: storage for returned string
  *   name: append this string to path if set
  */
-char *node_get_path(struct node *node, char *buf, const char *name)
+char *do_node_get_path(struct node *node, char *buf, const char *name, int match_case_insensitive)
 {
+    struct node *in_node = node;
+    const char *in_name = name;
     char *out = buf + PATH_BUFFER_SIZE - 1;
     int len;
     out[0] = 0;
@@ -143,7 +143,7 @@
     }
 
     while (node) {
-        name = node->name;
+        name = (node->actual_name ? node->actual_name : node->name);
         len = node->namelen;
         node = node->parent;
     start:
@@ -151,12 +151,45 @@
             return 0;
         out -= len;
         memcpy(out, name, len);
-        out --;
-        out[0] = '/';
+        /* avoid double slash at beginning of path */
+        if (out[0] != '/') {
+            out --;
+            out[0] = '/';
+        }
     }
 
-    normalize_name(out);
-    return out;
+    /* If we are searching for a file within node (rather than computing node's path)
+     * and fail, then we need to look for a case insensitive match.
+     */
+    if (in_name && match_case_insensitive && access(out, F_OK) != 0) {
+        char *path, buffer[PATH_BUFFER_SIZE];
+        DIR* dir;
+        struct dirent* entry;
+        path = do_node_get_path(in_node, buffer, NULL, NO_CASE_SENSITIVE_MATCH);
+        dir = opendir(path);
+        if (!dir) {
+            ERROR("opendir %s failed: %s", path, strerror(errno));
+            return out;
+        }
+
+        while ((entry = readdir(dir))) {
+            if (!strcasecmp(entry->d_name, in_name)) {
+                /* we have a match - replace the name */
+                len = strlen(in_name);
+                memcpy(buf + PATH_BUFFER_SIZE - len - 1, entry->d_name, len);
+                break;
+            }
+        }
+        closedir(dir);
+    }
+
+   return out;
+}
+
+char *node_get_path(struct node *node, char *buf, const char *name)
+{
+    /* We look for case insensitive matches by default */
+    return do_node_get_path(node, buf, name, CASE_SENSITIVE_MATCH);
 }
 
 void attr_from_stat(struct fuse_attr *attr, struct stat *s)
@@ -214,6 +247,41 @@
     parent->refcount++;
 }
 
+/* Check to see if our parent directory already has a file with a name
+ * that differs only by case.  If we find one, store it in the actual_name
+ * field so node_get_path will map it to this file in the underlying storage.
+ */
+static void node_find_actual_name(struct node *node)
+{
+    char *path, buffer[PATH_BUFFER_SIZE];
+    const char *node_name = node->name;
+    DIR* dir;
+    struct dirent* entry;
+
+    if (!node->parent) return;
+
+    path = node_get_path(node->parent, buffer, 0);
+    dir = opendir(path);
+    if (!dir) {
+        ERROR("opendir %s failed: %s", path, strerror(errno));
+        return;
+    }
+
+    while ((entry = readdir(dir))) {
+        const char *test_name = entry->d_name;
+        if (strcmp(test_name, node_name) && !strcasecmp(test_name, node_name)) {
+            /* we have a match - differs but only by case */
+            node->actual_name = strdup(test_name);
+            if (!node->actual_name) {
+                ERROR("strdup failed - out of memory\n");
+                exit(1);
+            }
+            break;
+        }
+    }
+    closedir(dir);
+}
+
 struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
 {
     struct node *node;
@@ -234,7 +302,7 @@
     add_node_to_parent(node, parent);
     memcpy(node->name, name, namelen + 1);
     node->namelen = namelen;
-
+    node_find_actual_name(node);
     return node;
 }
 
@@ -246,6 +314,7 @@
         return 0;
     node->name = newname;
     memcpy(node->name, name, node->namelen + 1);
+    node_find_actual_name(node);
     return node->name;
 }
 
@@ -397,6 +466,7 @@
             /* TODO: remove debugging - poison memory */
         memset(node->name, 0xef, node->namelen);
         free(node->name);
+        free(node->actual_name);
         memset(node, 0xfc, sizeof(*node));
         free(node);
     }
@@ -458,75 +528,6 @@
     fuse_reply(fuse, unique, &out, sizeof(out));
 }
 
-static int name_needs_normalizing(const char* name) {
-    char ch;
-    while ((ch = *name++) != 0) {
-        if (ch != tolower(ch))
-            return 1;
-    }
-    return 0;
-}
-
-static void recursive_fix_files(const char* path) {
-    DIR* dir;
-    struct dirent* entry;
-    char pathbuf[PATH_MAX];
-    char oldpath[PATH_MAX];
-    int pathLength = strlen(path);
-    int pathRemaining;
-    char* fileSpot;
-
-    if (pathLength >= sizeof(pathbuf) - 1) {
-        ERROR("path too long: %s\n", path);
-        return;
-    }
-    strcpy(pathbuf, path);
-    if (pathbuf[pathLength - 1] != '/') {
-        pathbuf[pathLength++] = '/';
-    }
-    fileSpot = pathbuf + pathLength;
-    pathRemaining = sizeof(pathbuf) - pathLength - 1;
-
-    dir = opendir(path);
-    if (!dir) {
-        ERROR("opendir %s failed: %s", path, strerror(errno));
-        return;
-    }
-
-    while ((entry = readdir(dir))) {
-        const char* name = entry->d_name;
-        int nameLength;
-
-        // ignore "." and ".."
-        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
-            continue;
-        }
-
-       nameLength = strlen(name);
-       if (nameLength > pathRemaining) {
-            ERROR("path %s/%s too long\n", path, name);
-            continue;
-        }
-        strcpy(fileSpot, name);
-
-        // make sure owner and group are correct
-        chown(pathbuf, uid, gid);
-
-        if (name_needs_normalizing(name)) {
-            /* rename file to lower case file name */
-            strlcpy(oldpath, pathbuf, sizeof(oldpath));
-            normalize_name(pathbuf);
-            rename(oldpath, pathbuf);
-        }
-
-        if (entry->d_type == DT_DIR) {
-            /* recurse to subdirectories */
-            recursive_fix_files(pathbuf);
-        }
-    }
-    closedir(dir);
-}
-
 void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
 {
     struct node *node;
@@ -682,7 +683,16 @@
             fuse_status(fuse, hdr->unique, -ENOENT);
             return;
         }
-        newpath = node_get_path(newparent, newbuffer, newname);
+        if (newparent == node) {
+            /* Special case for renaming a file where destination
+             * is same path differing only by case.
+             * In this case we don't want to look for a case insensitive match.
+             * This allows commands like "mv foo FOO" to work as expected.
+             */
+            newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH);
+        } else {
+            newpath = node_get_path(newparent, newbuffer, newname);
+        }
 
         if (!remove_child(node, target->nid)) {
             ERROR("RENAME remove_child not found");
@@ -922,30 +932,19 @@
     int fd;
     int res;
     const char *path = NULL;
-    int fix_files = 0;
     int i;
 
     for (i = 1; i < argc; i++) {
         char* arg = argv[i];
-        if (arg[0] == '-') {
-            if (!strcmp(arg, "-l")) {
-                force_lower_case = 1;
-            } else if (!strcmp(arg, "-f")) {
-                fix_files = 1;
-            } else {
-                return usage();
-            }
-        } else {
-            if (!path)
-                path = arg;
-            else if (uid == -1)
-                uid = strtoul(arg, 0, 10);
-            else if (gid == -1)
-                gid = strtoul(arg, 0, 10);
-            else {
-                ERROR("too many arguments\n");
-                return usage();
-            }
+        if (!path)
+            path = arg;
+        else if (uid == -1)
+            uid = strtoul(arg, 0, 10);
+        else if (gid == -1)
+            gid = strtoul(arg, 0, 10);
+        else {
+            ERROR("too many arguments\n");
+            return usage();
         }
     }
 
@@ -976,9 +975,6 @@
         return -1;
     }
 
-    if (fix_files)
-        recursive_fix_files(path);
-
     if (setgid(gid) < 0) {
         ERROR("cannot setgid!\n");
         return -1;