OSDN Git Service

Added pointer to parent for each node.
[android-x86/external-exfat.git] / libexfat / lookup.c
1 /*
2  *  lookup.c
3  *  exFAT file system implementation library.
4  *
5  *  Created by Andrew Nayenko on 02.09.09.
6  *  This software is distributed under the GNU General Public License 
7  *  version 3 or any later.
8  */
9
10 #include "exfat.h"
11 #include <string.h>
12 #include <errno.h>
13 #include <inttypes.h>
14
15 int exfat_opendir(struct exfat* ef, struct exfat_node* dir,
16                 struct exfat_iterator* it)
17 {
18         int rc;
19
20         exfat_get_node(dir);
21         it->parent = dir;
22         it->current = NULL;
23         rc = exfat_cache_directory(ef, dir);
24         if (rc != 0)
25                 exfat_put_node(ef, dir);
26         return rc;
27 }
28
29 void exfat_closedir(struct exfat* ef, struct exfat_iterator* it)
30 {
31         exfat_put_node(ef, it->parent);
32         it->parent = NULL;
33         it->current = NULL;
34 }
35
36 struct exfat_node* exfat_readdir(struct exfat* ef, struct exfat_iterator* it)
37 {
38         if (it->current == NULL)
39                 it->current = it->parent->child;
40         else
41                 it->current = it->current->next;
42
43         if (it->current != NULL)
44                 return exfat_get_node(it->current);
45         else
46                 return NULL;
47 }
48
49 static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
50 {
51         if (a >= ef->upcase_chars || b >= ef->upcase_chars)
52                 return (int) a - (int) b;
53
54         return (int) le16_to_cpu(ef->upcase[a]) - (int) le16_to_cpu(ef->upcase[b]);
55 }
56
57 static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
58 {
59         while (le16_to_cpu(*a) && le16_to_cpu(*b))
60         {
61                 int rc = compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
62                 if (rc != 0)
63                         return rc;
64                 a++;
65                 b++;
66         }
67         return compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b));
68 }
69
70 static int lookup_name(struct exfat* ef, struct exfat_node* parent,
71                 struct exfat_node** node, const char* name, size_t n)
72 {
73         struct exfat_iterator it;
74         le16_t buffer[EXFAT_NAME_MAX + 1];
75         int rc;
76
77         rc = utf8_to_utf16(buffer, name, EXFAT_NAME_MAX, n);
78         if (rc != 0)
79                 return rc;
80
81         rc = exfat_opendir(ef, parent, &it);
82         if (rc != 0)
83                 return rc;
84         while ((*node = exfat_readdir(ef, &it)))
85         {
86                 if (compare_name(ef, buffer, (*node)->name) == 0)
87                 {
88                         exfat_closedir(ef, &it);
89                         return 0;
90                 }
91                 exfat_put_node(ef, *node);
92         }
93         exfat_closedir(ef, &it);
94         return -ENOENT;
95 }
96
97 static size_t get_comp(const char* path, const char** comp)
98 {
99         const char* end;
100
101         *comp = path + strspn(path, "/");                               /* skip leading slashes */
102         end = strchr(*comp, '/');
103         if (end == NULL)
104                 return strlen(*comp);
105         else
106                 return end - *comp;
107 }
108
109 int exfat_lookup(struct exfat* ef, struct exfat_node** node,
110                 const char* path)
111 {
112         struct exfat_node* parent;
113         const char* p;
114         size_t n;
115
116         /* start from the root directory */
117         parent = *node = exfat_get_node(ef->root);
118         for (p = path; (n = get_comp(p, &p)); p += n)
119         {
120                 if (n == 1 && *p == '.')                                /* skip "." component */
121                         continue;
122                 if (lookup_name(ef, parent, node, p, n) != 0)
123                 {
124                         exfat_put_node(ef, parent);
125                         return -ENOENT;
126                 }
127                 exfat_put_node(ef, parent);
128                 parent = *node;
129         }
130         return 0;
131 }