3 * exFAT file system implementation library.
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.
15 void exfat_opendir(struct exfat_node* node, struct exfat_iterator* it)
17 if (!(node->flags & EXFAT_ATTRIB_DIR))
18 exfat_bug("`%s' is not a directory", node->name);
19 it->cluster = node->start_cluster;
21 it->contiguous = IS_CONTIGUOUS(*node);
25 void exfat_closedir(struct exfat_iterator* it)
35 * Reads one entry in directory at position pointed by iterator and fills
38 int exfat_readdir(struct exfat* ef, struct exfat_node* node,
39 struct exfat_iterator* it)
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 const struct exfat_bitmap* bitmap;
47 const struct exfat_label* label;
48 uint8_t continuations = 0;
51 if (it->chunk == NULL)
53 it->chunk = malloc(CLUSTER_SIZE(*ef->sb));
54 if (it->chunk == NULL)
56 exfat_error("out of memory");
59 exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb),
60 exfat_c2o(ef, it->cluster), ef->fd);
65 /* every directory (even empty one) occupies at least one cluster and
66 must contain EOD entry */
67 entry = (const struct exfat_entry*)
68 (it->chunk + it->offset % CLUSTER_SIZE(*ef->sb));
69 /* move iterator to the next entry in the directory */
70 it->offset += sizeof(struct exfat_entry);
75 if (continuations != 0)
77 exfat_error("expected %hhu continuations before EOD",
81 return -ENOENT; /* that's OK, means end of directory */
83 case EXFAT_ENTRY_FILE:
84 if (continuations != 0)
86 exfat_error("expected %hhu continuations before new entry",
90 memset(node, 0, sizeof(struct exfat_node));
91 file = (const struct exfat_file*) entry;
92 node->flags = le16_to_cpu(file->attrib);
93 node->mtime = exfat_exfat2unix(file->mdate, file->mtime);
94 node->atime = exfat_exfat2unix(file->adate, file->atime);
96 continuations = file->continuations;
97 /* each file entry must have at least 2 continuations:
99 if (continuations < 2)
101 exfat_error("too few continuations (%hhu)", continuations);
106 case EXFAT_ENTRY_FILE_INFO:
107 if (continuations < 2)
109 exfat_error("unexpected continuation (%hhu)",
113 file_info = (const struct exfat_file_info*) entry;
114 node->size = le64_to_cpu(file_info->size);
115 node->start_cluster = le32_to_cpu(file_info->start_cluster);
116 if (file_info->flag == EXFAT_FLAG_CONTIGUOUS)
117 node->flags |= EXFAT_ATTRIB_CONTIGUOUS;
121 case EXFAT_ENTRY_FILE_NAME:
122 if (continuations == 0)
124 exfat_error("unexpected continuation");
127 file_name = (const struct exfat_file_name*) entry;
128 memcpy(namep, file_name->name, EXFAT_ENAME_MAX * sizeof(le16_t));
129 namep += EXFAT_ENAME_MAX;
130 if (--continuations == 0)
131 return 0; /* entry completed */
134 case EXFAT_ENTRY_UPCASE:
135 if (ef->upcase != NULL)
137 upcase = (const struct exfat_upcase*) entry;
138 if (CLUSTER_INVALID(le32_to_cpu(upcase->start_cluster)))
140 exfat_error("invalid cluster in upcase table");
143 if (le64_to_cpu(upcase->size) == 0 ||
144 le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) ||
145 le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0)
147 exfat_error("bad upcase table size (%"PRIu64" bytes)",
148 le64_to_cpu(upcase->size));
151 ef->upcase = malloc(le64_to_cpu(upcase->size));
152 if (ef->upcase == NULL)
154 exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
155 le64_to_cpu(upcase->size));
158 ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t);
160 exfat_read_raw(ef->upcase, le64_to_cpu(upcase->size),
161 exfat_c2o(ef, le32_to_cpu(upcase->start_cluster)), ef->fd);
164 case EXFAT_ENTRY_BITMAP:
165 bitmap = (const struct exfat_bitmap*) entry;
166 if (CLUSTER_INVALID(le32_to_cpu(bitmap->start_cluster)))
168 exfat_error("invalid cluster in clusters bitmap");
171 if (le64_to_cpu(bitmap->size) !=
172 ((le32_to_cpu(ef->sb->cluster_count) + 7) / 8))
174 exfat_error("invalid bitmap size: %"PRIu64" (expected %u)",
175 le64_to_cpu(bitmap->size),
176 (le32_to_cpu(ef->sb->cluster_count) + 7) / 8);
181 case EXFAT_ENTRY_LABEL:
182 label = (const struct exfat_label*) entry;
183 if (label->length > EXFAT_ENAME_MAX)
185 exfat_error("too long label (%hhu chars)", label->length);
191 if (entry->type & EXFAT_ENTRY_VALID)
193 exfat_error("unknown entry type 0x%hhu", entry->type);
199 /* fetch the next cluster if needed */
200 if ((it->offset & (CLUSTER_SIZE(*ef->sb) - 1)) == 0)
202 it->cluster = exfat_next_cluster(ef, it->cluster, it->contiguous);
203 if (CLUSTER_INVALID(it->cluster))
205 exfat_error("invalid cluster while reading directory");
208 exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb),
209 exfat_c2o(ef, it->cluster), ef->fd);
212 /* we never reach here */
215 static int compare_char(struct exfat* ef, uint16_t a, uint16_t b)
217 if (a >= ef->upcase_chars || b >= ef->upcase_chars)
220 return le16_to_cpu(ef->upcase[a]) - le16_to_cpu(ef->upcase[b]);
223 static int compare_name(struct exfat* ef, const le16_t* a, const le16_t* b)
225 while (le16_to_cpu(*a) && le16_to_cpu(*b))
227 if (compare_char(ef, le16_to_cpu(*a), le16_to_cpu(*b)) != 0)
232 return le16_to_cpu(*a) - le16_to_cpu(*b);
235 static int lookup_name(struct exfat* ef, struct exfat_node* node,
238 struct exfat_iterator it;
240 exfat_opendir(node, &it);
241 while (exfat_readdir(ef, node, &it) == 0)
243 if (compare_name(ef, name, node->name) == 0)
253 int exfat_lookup(struct exfat* ef, struct exfat_node* node,
256 le16_t buffer[EXFAT_NAME_MAX + 1];
261 if (strlen(path) > EXFAT_NAME_MAX)
263 exfat_error("file name `%s' is too long", path);
264 return -ENAMETOOLONG;
267 rc = utf8_to_utf16(buffer, path, EXFAT_NAME_MAX, strlen(path));
271 /* start from the root directory */
272 node->flags = EXFAT_ATTRIB_DIR;
274 node->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
275 node->name[0] = cpu_to_le16('\0');
276 /* exFAT does not have time attributes for the root directory */
277 node->mtime = ef->mount_time;
278 node->atime = ef->mount_time;
280 for (subpath = p = buffer; p; subpath = p + 1)
282 while (le16_to_cpu(*subpath) == '/')
283 subpath++; /* skip leading slashes */
284 for (p = subpath; ; p++)
286 if (le16_to_cpu(*p) == '\0')
291 if (le16_to_cpu(*p) == '/')
293 *p = cpu_to_le16('\0');
297 if (le16_to_cpu(*subpath) == '\0')
298 break; /* skip trailing slashes */
299 if (le16_to_cpu(subpath[0]) == '.' && le16_to_cpu(subpath[1]) == '\0')
300 continue; /* skip "." component */
301 if (lookup_name(ef, node, subpath) != 0)