3 exFAT file system implementation library.
5 Copyright (C) 2009, 2010 Andrew Nayenko
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /* on-disk nodes iterator */
35 struct exfat_node* exfat_get_node(struct exfat_node* node)
37 /* if we switch to multi-threaded mode we will need atomic
38 increment here and atomic decrement in exfat_put_node() */
43 void exfat_put_node(struct exfat* ef, struct exfat_node* node)
45 if (--node->references < 0)
47 char buffer[EXFAT_NAME_MAX + 1];
48 exfat_get_name(node, buffer, EXFAT_NAME_MAX);
49 exfat_bug("reference counter of `%s' is below zero", buffer);
52 if (node->references == 0)
54 if (node->flags & EXFAT_ATTRIB_DIRTY)
55 exfat_flush_node(ef, node);
56 if (node->flags & EXFAT_ATTRIB_UNLINKED)
58 /* free all clusters and node structure itself */
59 exfat_truncate(ef, node, 0);
67 static int opendir(struct exfat* ef, const struct exfat_node* dir,
70 if (!(dir->flags & EXFAT_ATTRIB_DIR))
71 exfat_bug("not a directory");
72 it->cluster = dir->start_cluster;
74 it->contiguous = IS_CONTIGUOUS(*dir);
75 it->chunk = malloc(CLUSTER_SIZE(*ef->sb));
76 if (it->chunk == NULL)
78 exfat_error("out of memory");
81 exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb),
82 exfat_c2o(ef, it->cluster), ef->fd);
86 static void closedir(struct iterator* it)
95 static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent,
98 /* move iterator to the next entry in the directory */
99 it->offset += sizeof(struct exfat_entry);
100 /* fetch the next cluster if needed */
101 if ((it->offset & (CLUSTER_SIZE(*ef->sb) - 1)) == 0)
103 it->cluster = exfat_next_cluster(ef, parent, it->cluster);
104 if (CLUSTER_INVALID(it->cluster))
106 exfat_error("invalid cluster while reading directory");
109 exfat_read_raw(it->chunk, CLUSTER_SIZE(*ef->sb),
110 exfat_c2o(ef, it->cluster), ef->fd);
115 static struct exfat_node* allocate_node(void)
117 struct exfat_node* node = malloc(sizeof(struct exfat_node));
120 exfat_error("failed to allocate node");
123 memset(node, 0, sizeof(struct exfat_node));
127 static void init_node_meta1(struct exfat_node* node,
128 const struct exfat_file* meta1)
130 node->flags = le16_to_cpu(meta1->attrib);
131 node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime);
132 node->atime = exfat_exfat2unix(meta1->adate, meta1->atime);
135 static void init_node_meta2(struct exfat_node* node,
136 const struct exfat_file_info* meta2)
138 node->size = le64_to_cpu(meta2->size);
139 node->start_cluster = le32_to_cpu(meta2->start_cluster);
140 node->fptr_cluster = node->start_cluster;
141 if (meta2->flag == EXFAT_FLAG_CONTIGUOUS)
142 node->flags |= EXFAT_ATTRIB_CONTIGUOUS;
146 * Reads one entry in directory at position pointed by iterator and fills
149 static int readdir(struct exfat* ef, const struct exfat_node* parent,
150 struct exfat_node** node, struct iterator* it)
152 const struct exfat_entry* entry;
153 const struct exfat_file* file;
154 const struct exfat_file_info* file_info;
155 const struct exfat_file_name* file_name;
156 const struct exfat_upcase* upcase;
157 const struct exfat_bitmap* bitmap;
158 const struct exfat_label* label;
159 uint8_t continuations = 0;
160 le16_t* namep = NULL;
161 uint16_t reference_checksum = 0;
162 uint16_t actual_checksum = 0;
168 /* every directory (even empty one) occupies at least one cluster and
169 must contain EOD entry */
170 entry = (const struct exfat_entry*)
171 (it->chunk + it->offset % CLUSTER_SIZE(*ef->sb));
175 case EXFAT_ENTRY_EOD:
176 if (continuations != 0)
178 exfat_error("expected %hhu continuations before EOD",
182 return -ENOENT; /* that's OK, means end of directory */
184 case EXFAT_ENTRY_FILE:
185 if (continuations != 0)
187 exfat_error("expected %hhu continuations before new entry",
191 file = (const struct exfat_file*) entry;
192 continuations = file->continuations;
193 /* each file entry must have at least 2 continuations:
195 if (continuations < 2)
197 exfat_error("too few continuations (%hhu)", continuations);
200 reference_checksum = le16_to_cpu(file->checksum);
201 actual_checksum = exfat_start_checksum(file);
202 *node = allocate_node();
205 /* new node has zero reference counter */
206 (*node)->entry_cluster = it->cluster;
207 (*node)->entry_offset = it->offset % CLUSTER_SIZE(*ef->sb);
208 init_node_meta1(*node, file);
209 namep = (*node)->name;
212 case EXFAT_ENTRY_FILE_INFO:
213 if (continuations < 2)
215 exfat_error("unexpected continuation (%hhu)",
219 file_info = (const struct exfat_file_info*) entry;
220 init_node_meta2(*node, file_info);
221 actual_checksum = exfat_add_checksum(entry, actual_checksum);
222 /* There are two fields that contain file size. Maybe they plan
223 to add compression support in the future and one of those
224 fields is visible (uncompressed) size and the other is real
225 (compressed) size. Anyway, currently it looks like exFAT does
226 not support compression and both fields must be equal. */
227 if (le64_to_cpu(file_info->real_size) != (*node)->size)
229 exfat_error("real size does not equal to size "
230 "(%"PRIu64" != %"PRIu64")",
231 le64_to_cpu(file_info->real_size), (*node)->size);
234 /* directories must be aligned on at cluster boundary */
235 if (((*node)->flags & EXFAT_ATTRIB_DIR) &&
236 (*node)->size % CLUSTER_SIZE(*ef->sb) != 0)
238 char buffer[EXFAT_NAME_MAX + 1];
240 exfat_get_name(*node, buffer, EXFAT_NAME_MAX);
241 exfat_error("directory `%s' has invalid size %"PRIu64" bytes",
242 buffer, (*node)->size);
248 case EXFAT_ENTRY_FILE_NAME:
249 if (continuations == 0)
251 exfat_error("unexpected continuation");
254 file_name = (const struct exfat_file_name*) entry;
255 actual_checksum = exfat_add_checksum(entry, actual_checksum);
257 memcpy(namep, file_name->name, EXFAT_ENAME_MAX * sizeof(le16_t));
258 namep += EXFAT_ENAME_MAX;
259 if (--continuations == 0)
261 if (actual_checksum != reference_checksum)
263 exfat_error("invalid checksum (0x%hx != 0x%hx)",
264 actual_checksum, reference_checksum);
267 if (fetch_next_entry(ef, parent, it) != 0)
269 return 0; /* entry completed */
273 case EXFAT_ENTRY_UPCASE:
274 if (ef->upcase != NULL)
276 upcase = (const struct exfat_upcase*) entry;
277 if (CLUSTER_INVALID(le32_to_cpu(upcase->start_cluster)))
279 exfat_error("invalid cluster in upcase table");
282 if (le64_to_cpu(upcase->size) == 0 ||
283 le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) ||
284 le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0)
286 exfat_error("bad upcase table size (%"PRIu64" bytes)",
287 le64_to_cpu(upcase->size));
290 ef->upcase = malloc(le64_to_cpu(upcase->size));
291 if (ef->upcase == NULL)
293 exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
294 le64_to_cpu(upcase->size));
297 ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t);
299 exfat_read_raw(ef->upcase, le64_to_cpu(upcase->size),
300 exfat_c2o(ef, le32_to_cpu(upcase->start_cluster)), ef->fd);
303 case EXFAT_ENTRY_BITMAP:
304 bitmap = (const struct exfat_bitmap*) entry;
305 if (CLUSTER_INVALID(le32_to_cpu(bitmap->start_cluster)))
307 exfat_error("invalid cluster in clusters bitmap");
310 ef->cmap.size = le32_to_cpu(ef->sb->cluster_count) -
311 EXFAT_FIRST_DATA_CLUSTER;
312 if (le64_to_cpu(bitmap->size) != (ef->cmap.size + 7) / 8)
314 exfat_error("invalid bitmap size: %"PRIu64" (expected %u)",
315 le64_to_cpu(bitmap->size), (ef->cmap.size + 7) / 8);
318 ef->cmap.start_cluster = le32_to_cpu(bitmap->start_cluster);
319 /* FIXME bitmap can be rather big, up to 512 MB */
320 ef->cmap.chunk_size = ef->cmap.size;
321 ef->cmap.chunk = malloc(le64_to_cpu(bitmap->size));
322 if (ef->cmap.chunk == NULL)
324 exfat_error("failed to allocate clusters map chunk "
325 "(%"PRIu64" bytes)", le64_to_cpu(bitmap->size));
329 exfat_read_raw(ef->cmap.chunk, le64_to_cpu(bitmap->size),
330 exfat_c2o(ef, ef->cmap.start_cluster), ef->fd);
333 case EXFAT_ENTRY_LABEL:
334 label = (const struct exfat_label*) entry;
335 if (label->length > EXFAT_ENAME_MAX)
337 exfat_error("too long label (%hhu chars)", label->length);
343 if (entry->type & EXFAT_ENTRY_VALID)
345 exfat_error("unknown entry type 0x%hhu", entry->type);
351 if (fetch_next_entry(ef, parent, it) != 0)
354 /* we never reach here */
362 int exfat_cache_directory(struct exfat* ef, struct exfat_node* dir)
366 struct exfat_node* node;
367 struct exfat_node* current = NULL;
369 if (dir->flags & EXFAT_ATTRIB_CACHED)
370 return 0; /* already cached */
372 rc = opendir(ef, dir, &it);
375 while ((rc = readdir(ef, dir, &node, &it)) == 0)
380 current->next = node;
381 node->prev = current;
393 for (current = dir->child; current; current = node)
395 node = current->next;
402 dir->flags |= EXFAT_ATTRIB_CACHED;
406 static void reset_cache(struct exfat* ef, struct exfat_node* node)
408 struct exfat_node* child;
409 struct exfat_node* next;
411 for (child = node->child; child; child = next)
413 reset_cache(ef, child);
417 if (node->references != 0)
419 char buffer[EXFAT_NAME_MAX + 1];
420 exfat_get_name(node, buffer, EXFAT_NAME_MAX);
421 exfat_warn("non-zero reference counter (%d) for `%s'",
422 node->references, buffer);
424 while (node->references--)
425 exfat_put_node(ef, node);
427 node->flags &= ~EXFAT_ATTRIB_CACHED;
430 void exfat_reset_cache(struct exfat* ef)
432 reset_cache(ef, ef->root);
435 void next_entry(struct exfat* ef, const struct exfat_node* parent,
436 cluster_t* cluster, off_t* offset)
438 if (*offset + sizeof(struct exfat_entry) == CLUSTER_SIZE(*ef->sb))
440 /* next cluster cannot be invalid */
441 *cluster = exfat_next_cluster(ef, parent, *cluster);
445 *offset += sizeof(struct exfat_entry);
449 void exfat_flush_node(struct exfat* ef, struct exfat_node* node)
453 off_t meta1_offset, meta2_offset;
454 struct exfat_file meta1;
455 struct exfat_file_info meta2;
458 exfat_bug("unable to flush node to read-only FS");
460 if (node->parent == NULL)
461 return; /* do not flush unlinked node */
463 cluster = node->entry_cluster;
464 offset = node->entry_offset;
465 meta1_offset = exfat_c2o(ef, cluster) + offset;
466 next_entry(ef, node->parent, &cluster, &offset);
467 meta2_offset = exfat_c2o(ef, cluster) + offset;
469 exfat_read_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd);
470 if (meta1.type != EXFAT_ENTRY_FILE)
471 exfat_bug("invalid type of meta1: 0x%hhx", meta1.type);
472 meta1.attrib = cpu_to_le16(node->flags);
473 exfat_unix2exfat(node->mtime, &meta1.mdate, &meta1.mtime);
474 exfat_unix2exfat(node->atime, &meta1.adate, &meta1.atime);
476 exfat_read_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd);
477 if (meta2.type != EXFAT_ENTRY_FILE_INFO)
478 exfat_bug("invalid type of meta2: 0x%hhx", meta2.type);
479 meta2.size = meta2.real_size = cpu_to_le64(node->size);
480 meta2.start_cluster = cpu_to_le32(node->start_cluster);
481 /* empty files must be marked as fragmented */
482 if (node->size != 0 && IS_CONTIGUOUS(*node))
483 meta2.flag = EXFAT_FLAG_CONTIGUOUS;
485 meta2.flag = EXFAT_FLAG_FRAGMENTED;
486 /* name hash remains unchanged, no need to recalculate it */
488 meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name);
490 exfat_write_raw(&meta1, sizeof(meta1), meta1_offset, ef->fd);
491 exfat_write_raw(&meta2, sizeof(meta2), meta2_offset, ef->fd);
493 node->flags &= ~EXFAT_ATTRIB_DIRTY;
496 static void erase_entry(struct exfat* ef, struct exfat_node* node)
498 cluster_t cluster = node->entry_cluster;
499 off_t offset = node->entry_offset;
500 int name_entries = DIV_ROUND_UP(utf16_length(node->name), EXFAT_ENAME_MAX);
503 entry_type = EXFAT_ENTRY_FILE & ~EXFAT_ENTRY_VALID;
504 exfat_write_raw(&entry_type, 1, exfat_c2o(ef, cluster) + offset, ef->fd);
506 next_entry(ef, node->parent, &cluster, &offset);
507 entry_type = EXFAT_ENTRY_FILE_INFO & ~EXFAT_ENTRY_VALID;
508 exfat_write_raw(&entry_type, 1, exfat_c2o(ef, cluster) + offset, ef->fd);
510 while (name_entries--)
512 next_entry(ef, node->parent, &cluster, &offset);
513 entry_type = EXFAT_ENTRY_FILE_NAME & ~EXFAT_ENTRY_VALID;
514 exfat_write_raw(&entry_type, 1, exfat_c2o(ef, cluster) + offset,
519 static void tree_detach(struct exfat_node* node)
522 node->prev->next = node->next;
523 else /* this is the first node in the list */
524 node->parent->child = node->next;
526 node->next->prev = node->prev;
532 static void tree_attach(struct exfat_node* dir, struct exfat_node* node)
537 dir->child->prev = node;
538 node->next = dir->child;
543 static void delete(struct exfat* ef, struct exfat_node* node)
545 erase_entry(ef, node);
546 exfat_update_mtime(node->parent);
548 /* file clusters will be freed when node reference counter becomes 0 */
549 node->flags |= EXFAT_ATTRIB_UNLINKED;
552 int exfat_unlink(struct exfat* ef, struct exfat_node* node)
554 if (node->flags & EXFAT_ATTRIB_DIR)
560 int exfat_rmdir(struct exfat* ef, struct exfat_node* node)
562 if (!(node->flags & EXFAT_ATTRIB_DIR))
564 /* check that directory is empty */
565 exfat_cache_directory(ef, node);
572 static int grow_directory(struct exfat* ef, struct exfat_node* dir,
573 uint64_t asize, uint32_t difference)
575 return exfat_truncate(ef, dir,
576 DIV_ROUND_UP(asize + difference, CLUSTER_SIZE(*ef->sb))
577 * CLUSTER_SIZE(*ef->sb));
580 static void write_eod(struct exfat* ef, struct exfat_node* dir,
581 cluster_t cluster, off_t offset, int seek)
583 struct exfat_entry eod;
586 next_entry(ef, dir, &cluster, &offset);
587 memset(&eod, 0, sizeof(struct exfat_entry));
588 exfat_write_raw(&eod, sizeof(struct exfat_entry),
589 exfat_c2o(ef, cluster) + offset, ef->fd);
592 static int find_slot(struct exfat* ef, struct exfat_node* dir,
593 cluster_t* cluster, off_t* offset, int subentries)
597 const struct exfat_entry* entry;
600 rc = opendir(ef, dir, &it);
607 *cluster = it.cluster;
608 *offset = it.offset % CLUSTER_SIZE(*ef->sb);
610 entry = (const struct exfat_entry*)
611 (it.chunk + it.offset % CLUSTER_SIZE(*ef->sb));
612 if (entry->type == EXFAT_ENTRY_EOD)
614 rc = grow_directory(ef, dir,
615 it.offset + sizeof(struct exfat_entry), /* actual size */
616 (subentries - contiguous) * sizeof(struct exfat_entry));
622 write_eod(ef, dir, *cluster, *offset, subentries - contiguous);
625 if (entry->type & EXFAT_ENTRY_VALID)
629 if (contiguous == subentries)
630 break; /* suitable slot it found */
631 if (fetch_next_entry(ef, dir, &it) != 0)
641 static int write_entry(struct exfat* ef, struct exfat_node* dir,
642 const le16_t* name, cluster_t cluster, off_t offset, uint16_t attrib)
644 struct exfat_node* node;
645 struct exfat_file meta1;
646 struct exfat_file_info meta2;
647 const size_t name_length = utf16_length(name);
648 const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
651 node = allocate_node();
654 node->entry_cluster = cluster;
655 node->entry_offset = offset;
656 memcpy(node->name, name, name_length * sizeof(le16_t));
658 memset(&meta1, 0, sizeof(meta1));
659 meta1.type = EXFAT_ENTRY_FILE;
660 meta1.continuations = 1 + name_entries;
661 meta1.attrib = cpu_to_le16(attrib);
662 exfat_unix2exfat(time(NULL), &meta1.crdate, &meta1.crtime);
663 meta1.adate = meta1.mdate = meta1.crdate;
664 meta1.atime = meta1.mtime = meta1.crtime;
665 /* crtime_cs and mtime_cs contain addition to the time in centiseconds;
666 just ignore those fields because we operate with 2 sec resolution */
668 memset(&meta2, 0, sizeof(meta2));
669 meta2.type = EXFAT_ENTRY_FILE_INFO;
670 meta2.flag = EXFAT_FLAG_FRAGMENTED;
671 meta2.name_length = name_length;
672 meta2.name_hash = exfat_calc_name_hash(ef, node->name);
673 meta2.start_cluster = cpu_to_le32(EXFAT_CLUSTER_FREE);
675 meta1.checksum = exfat_calc_checksum(&meta1, &meta2, node->name);
677 exfat_write_raw(&meta1, sizeof(meta1), exfat_c2o(ef, cluster) + offset,
679 next_entry(ef, dir, &cluster, &offset);
680 exfat_write_raw(&meta2, sizeof(meta2), exfat_c2o(ef, cluster) + offset,
682 for (i = 0; i < name_entries; i++)
684 struct exfat_file_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
685 memcpy(name_entry.name, node->name + i * EXFAT_ENAME_MAX,
686 EXFAT_ENAME_MAX * sizeof(le16_t));
687 next_entry(ef, dir, &cluster, &offset);
688 exfat_write_raw(&name_entry, sizeof(name_entry),
689 exfat_c2o(ef, cluster) + offset, ef->fd);
692 init_node_meta1(node, &meta1);
693 init_node_meta2(node, &meta2);
695 tree_attach(dir, node);
696 exfat_update_mtime(dir);
700 static int create(struct exfat* ef, const char* path, uint16_t attrib)
702 struct exfat_node* dir;
703 cluster_t cluster = EXFAT_CLUSTER_BAD;
705 le16_t name[EXFAT_NAME_MAX + 1];
708 /* FIXME filter name characters */
710 rc = exfat_split(ef, &dir, name, path);
714 rc = find_slot(ef, dir, &cluster, &offset,
715 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
718 exfat_put_node(ef, dir);
721 rc = write_entry(ef, dir, name, cluster, offset, attrib);
722 exfat_put_node(ef, dir);
726 int exfat_mknod(struct exfat* ef, const char* path)
728 return create(ef, path, EXFAT_ATTRIB_ARCH);
731 int exfat_mkdir(struct exfat* ef, const char* path)
734 struct exfat_node* node;
736 rc = create(ef, path, EXFAT_ATTRIB_ARCH | EXFAT_ATTRIB_DIR);
739 rc = exfat_lookup(ef, &node, path);
742 /* directories always have at least one cluster */
743 rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb));
747 exfat_put_node(ef, node);
750 exfat_put_node(ef, node);
754 static void rename_entry(struct exfat* ef, struct exfat_node* dir,
755 struct exfat_node* node, const le16_t* name, cluster_t new_cluster,
758 struct exfat_file meta1;
759 struct exfat_file_info meta2;
760 cluster_t old_cluster = node->entry_cluster;
761 off_t old_offset = node->entry_offset;
762 const size_t name_length = utf16_length(name);
763 const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
766 exfat_read_raw(&meta1, sizeof(meta1),
767 exfat_c2o(ef, old_cluster) + old_offset, ef->fd);
768 next_entry(ef, node->parent, &old_cluster, &old_offset);
769 exfat_read_raw(&meta2, sizeof(meta2),
770 exfat_c2o(ef, old_cluster) + old_offset, ef->fd);
771 meta1.continuations = 1 + name_entries;
772 meta2.name_hash = exfat_calc_name_hash(ef, name);
773 meta2.name_length = name_length;
774 meta1.checksum = exfat_calc_checksum(&meta1, &meta2, name);
776 erase_entry(ef, node);
778 node->entry_cluster = new_cluster;
779 node->entry_offset = new_offset;
781 exfat_write_raw(&meta1, sizeof(meta1),
782 exfat_c2o(ef, new_cluster) + new_offset, ef->fd);
783 next_entry(ef, dir, &new_cluster, &new_offset);
784 exfat_write_raw(&meta2, sizeof(meta2),
785 exfat_c2o(ef, new_cluster) + new_offset, ef->fd);
787 for (i = 0; i < name_entries; i++)
789 struct exfat_file_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
790 memcpy(name_entry.name, name + i * EXFAT_ENAME_MAX,
791 EXFAT_ENAME_MAX * sizeof(le16_t));
792 next_entry(ef, dir, &new_cluster, &new_offset);
793 exfat_write_raw(&name_entry, sizeof(name_entry),
794 exfat_c2o(ef, new_cluster) + new_offset, ef->fd);
797 memcpy(node->name, name, (name_length + 1) * sizeof(le16_t));
799 tree_attach(dir, node);
802 int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
804 struct exfat_node* node;
805 struct exfat_node* dir;
806 cluster_t cluster = EXFAT_CLUSTER_BAD;
808 le16_t name[EXFAT_NAME_MAX + 1];
811 rc = exfat_lookup(ef, &node, old_path);
815 memset(name, 0, (EXFAT_NAME_MAX + 1) * sizeof(le16_t));
816 rc = exfat_split(ef, &dir, name, new_path);
819 exfat_put_node(ef, node);
823 rc = find_slot(ef, dir, &cluster, &offset,
824 2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
827 exfat_put_node(ef, dir);
828 exfat_put_node(ef, node);
831 rename_entry(ef, dir, node, name, cluster, offset);
832 exfat_put_node(ef, dir);
833 exfat_put_node(ef, node);
837 void exfat_utimes(struct exfat_node* node, const struct timespec tv[2])
839 node->atime = tv[0].tv_sec;
840 node->mtime = tv[1].tv_sec;
841 node->flags |= EXFAT_ATTRIB_DIRTY;
844 void exfat_update_atime(struct exfat_node* node)
846 node->atime = time(NULL);
847 node->flags |= EXFAT_ATTRIB_DIRTY;
850 void exfat_update_mtime(struct exfat_node* node)
852 node->mtime = time(NULL);
853 node->flags |= EXFAT_ATTRIB_DIRTY;