OSDN Git Service

Initial check in.
[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 void exfat_opendir(struct exfat_node* node, struct exfat_iterator* it)
16 {
17         if (!(node->flags & EXFAT_ATTRIB_DIR))
18                 exfat_bug("`%s' is not a directory", node->name);
19         it->cluster = node->start_cluster;
20         it->offset = 0;
21         it->contiguous = IS_CONTIGUOUS(*node);
22         it->chunk = NULL;
23 }
24
25 void exfat_closedir(struct exfat_iterator* it)
26 {
27         it->cluster = 0;
28         it->offset = 0;
29         it->contiguous = 0;
30         free(it->chunk);
31         it->chunk = NULL;
32 }
33
34 /*
35  * Reads one entry in directory at position pointed by iterator and fills
36  * node structure.
37  */
38 int exfat_readdir(struct exfat* ef, struct exfat_node* node,
39                 struct exfat_iterator* it)
40 {
41         const struct exfat_entry* entry;
42         const struct exfat_file* file;
43         const struct exfat_file_info* file_info;
44         const struct exfat_file_name* file_name;
45         const struct exfat_upcase* upcase;
46         uint8_t continuations = 0;
47         le16_t* namep = NULL;
48
49         if (it->chunk == NULL)
50         {
51                 it->chunk = malloc(CLUSTER_SIZE(*ef->sb));
52                 if (it->chunk == NULL)
53                 {
54                         exfat_error("out of memory");
55                         return -ENOMEM;
56                 }
57                 exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb),
58                                 exfat_c2o(ef, it->cluster), ef->fd);
59         }
60
61         for (;;)
62         {
63                 /* every directory (even empty one) occupies at least one cluster and
64                    must contain EOD entry */
65                 entry = (const struct exfat_entry*)
66                                 (it->chunk + it->offset % CLUSTER_SIZE(*ef->sb));
67                 /* move iterator to the next entry in the directory */
68                 it->offset += sizeof(struct exfat_entry);
69
70                 switch (entry->type)
71                 {
72                 case EXFAT_ENTRY_EOD:
73                         if (continuations != 0)
74                         {
75                                 exfat_error("expected %hhu continuations before EOD",
76                                                 continuations);
77                                 return -EIO;
78                         }
79                         return -ENOENT; /* that's OK, means end of directory */
80
81                 case EXFAT_ENTRY_FILE:
82                         if (continuations != 0)
83                         {
84                                 exfat_error("expected %hhu continuations before new entry",
85                                                 continuations);
86                                 return -EIO;
87                         }
88                         memset(node, 0, sizeof(struct exfat_node));
89                         file = (const struct exfat_file*) entry;
90                         node->flags = le16_to_cpu(file->attrib);
91                         node->mtime = exfat_exfat2unix(file->mdate, file->mtime);
92                         node->atime = exfat_exfat2unix(file->adate, file->atime);
93                         namep = node->name;
94                         continuations = file->continuations;
95                         /* each file entry must have at least 2 continuations:
96                            info and name */
97                         if (continuations < 2)
98                         {
99                                 exfat_error("too few continuations (%hhu)", continuations);
100                                 return -EIO;
101                         }
102                         break;
103
104                 case EXFAT_ENTRY_FILE_INFO:
105                         if (continuations < 2)
106                         {
107                                 exfat_error("unexpected continuation (%hhu)",
108                                                 continuations);
109                                 return -EIO;
110                         }
111                         file_info = (const struct exfat_file_info*) entry;
112                         node->size = le64_to_cpu(file_info->size);
113                         node->start_cluster = le32_to_cpu(file_info->start_cluster);
114                         if (file_info->flag == EXFAT_FLAG_CONTIGUOUS)
115                                 node->flags |= EXFAT_ATTRIB_CONTIGUOUS;
116                         --continuations;
117                         break;
118
119                 case EXFAT_ENTRY_FILE_NAME:
120                         if (continuations == 0)
121                         {
122                                 exfat_error("unexpected continuation");
123                                 return -EIO;
124                         }
125                         file_name = (const struct exfat_file_name*) entry;
126                         memcpy(namep, file_name->name, EXFAT_ENAME_MAX * sizeof(le16_t));
127                         namep += EXFAT_ENAME_MAX;
128                         if (--continuations == 0)
129                                 return 0; /* entry completed */
130                         break;
131
132                 case EXFAT_ENTRY_UPCASE:
133                         if (ef->upcase != NULL)
134                                 break;
135                         upcase = (const struct exfat_upcase*) entry;
136                         if (CLUSTER_INVALID(le32_to_cpu(upcase->start_cluster)))
137                         {
138                                 exfat_error("invalid cluster in upcase table");
139                                 return -EIO;
140                         }
141                         if (le64_to_cpu(upcase->size) == 0 ||
142                                 le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) ||
143                                 le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0)
144                         {
145                                 exfat_error("bad upcase table size (%"PRIu64" bytes)",
146                                                 le64_to_cpu(upcase->size));
147                                 return -EIO;
148                         }
149                         ef->upcase = malloc(le64_to_cpu(upcase->size));
150                         ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t);
151
152                         /* set up a few fields in node just to satisfy exfat_read() */
153                         memset(node, 0, sizeof(struct exfat_node));
154                         node->start_cluster = le32_to_cpu(upcase->start_cluster);
155                         node->flags = EXFAT_ATTRIB_CONTIGUOUS;
156                         node->size = le64_to_cpu(upcase->size);
157                         if (exfat_read(ef, node, ef->upcase, node->size, 0) != node->size)
158                                 return -EIO;
159                         break;
160                 }
161
162                 /* fetch the next cluster if needed */
163                 if ((it->offset & (CLUSTER_SIZE(*ef->sb) - 1)) == 0)
164                 {
165                         it->cluster = exfat_next_cluster(ef, it->cluster, it->contiguous);
166                         if (CLUSTER_INVALID(it->cluster))
167                         {
168                                 exfat_error("invalid cluster while reading directory");
169                                 return -EIO;
170                         }
171                         exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb),
172                                         exfat_c2o(ef, it->cluster), ef->fd);
173                 }
174         }
175         /* we never reach here */
176 }
177
178 static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
179 {
180         if (a >= ef->upcase_chars || b >= ef->upcase_chars)
181                 return a - b;
182         else
183                 return le16_to_cpu(ef->upcase[a]) - le16_to_cpu(ef->upcase[b]);
184 }
185
186 static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
187 {
188         while (le16_to_cpu(*a) && le16_to_cpu(*b))
189         {
190                 if (compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b)) != 0)
191                         break;
192                 a++;
193                 b++;
194         }
195         return le16_to_cpu(*a) - le16_to_cpu(*b);
196 }
197
198 static int lookup_name(struct exfat* ef, struct exfat_node* node,
199                 const le16_t* name)
200 {
201         struct exfat_iterator it;
202
203         exfat_opendir(node, &it);
204         while (exfat_readdir(ef, node, &it) == 0)
205         {
206                 if (compare_name(ef, name, node->name) == 0)
207                 {
208                         exfat_closedir(&it);
209                         return 0;
210                 }
211         }
212         exfat_closedir(&it);
213         return -ENOENT;
214 }
215
216 int exfat_lookup(struct exfat* ef, struct exfat_node* node,
217                 const char* path)
218 {
219         le16_t buffer[EXFAT_NAME_MAX + 1];
220         int rc;
221         le16_t* p;
222         le16_t* subpath;
223
224         if (strlen(path) > EXFAT_NAME_MAX)
225         {
226                 exfat_error("file name `%s' is too long", path);
227                 return -ENAMETOOLONG;
228         }
229
230         rc = utf8_to_utf16(buffer, path, EXFAT_NAME_MAX, strlen(path));
231         if (rc != 0)
232                 return rc;
233
234         /* start from the root directory */
235         node->flags = EXFAT_ATTRIB_DIR;
236         node->size = 0;
237         node->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
238         node->name[0] = cpu_to_le16('\0');
239         /* exFAT does not have time attributes for the root directory */
240         node->mtime = ef->mount_time;
241         node->atime = ef->mount_time;
242
243         for (subpath = p = buffer; p; subpath = p + 1)
244         {
245                 while (le16_to_cpu(*subpath) == '/')
246                         subpath++;              /* skip leading slashes */
247                 for (p = subpath; ; p++)
248                 {
249                         if (le16_to_cpu(*p) == '\0')
250                         {
251                                 p = NULL;
252                                 break;
253                         }
254                         if (le16_to_cpu(*p) == '/')
255                         {
256                                 *p = cpu_to_le16('\0');
257                                 break;
258                         }
259                 }
260                 if (le16_to_cpu(*subpath) == '\0')
261                         break;                  /* skip trailing slashes */
262                 if (le16_to_cpu(subpath[0]) == '.' && le16_to_cpu(subpath[1]) == '\0')
263                         continue;               /* skip "." component */
264                 if (lookup_name(ef, node, subpath) != 0)
265                         return -ENOENT;
266         }
267         return 0;
268 }