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

* commit '575a2bbee30d70a1940077a5b699aaf93cb4e2cd':
  sdcard: Remove lower case squashing of file names
This commit is contained in:
Mike Lockwood 2011-01-23 16:46:03 -08:00 committed by Android Git Automerger
commit e12e5f376b

View file

@ -95,6 +95,12 @@ struct node {
__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 @@ struct fuse {
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 @@ static void normalize_name(char *name)
* 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 @@ char *node_get_path(struct node *node, char *buf, const char *name)
}
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 @@ char *node_get_path(struct node *node, char *buf, const char *name)
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 @@ static void add_node_to_parent(struct node *node, struct node *parent) {
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 @@ struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64
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 @@ static char *rename_node(struct node *node, const char *name)
return 0;
node->name = newname;
memcpy(node->name, name, node->namelen + 1);
node_find_actual_name(node);
return node->name;
}
@ -397,6 +466,7 @@ void node_release(struct node *node)
/* 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 @@ void lookup_entry(struct fuse *fuse, struct node *node,
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 @@ void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *da
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 main(int argc, char **argv)
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 @@ int main(int argc, char **argv)
return -1;
}
if (fix_files)
recursive_fix_files(path);
if (setgid(gid) < 0) {
ERROR("cannot setgid!\n");
return -1;