external/exfat
Revision | 75f28b558a1a86b45308a3e7d0af222624bc2c61 (tree) |
---|---|
Zeit | 2019-04-21 03:14:49 |
Autor | Nathan Hunsperger <nathan@huns...> |
Commiter | relan |
Add support for timezone offsets.
Timestamps are stored in local time. exFAT includes timezone offset
fields to allow timestamps to remain correct when mounted under a
different timezone. The timezone offset is now used to calculate the
correct timestamp on read, and set on write.
@@ -228,9 +228,10 @@ int exfat_set_label(struct exfat* ef, const char* label); | ||
228 | 228 | int exfat_mount(struct exfat* ef, const char* spec, const char* options); |
229 | 229 | void exfat_unmount(struct exfat* ef); |
230 | 230 | |
231 | -time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec); | |
231 | +time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec, | |
232 | + uint8_t tzoffset); | |
232 | 233 | void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, |
233 | - uint8_t* centisec); | |
234 | + uint8_t* centisec, uint8_t* tzoffset); | |
234 | 235 | void exfat_tzset(void); |
235 | 236 | |
236 | 237 | bool exfat_ask_to_fix(const struct exfat* ef); |
@@ -144,7 +144,8 @@ struct exfat_entry_meta1 /* file or directory info (part 1) */ | ||
144 | 144 | le16_t atime, adate; /* latest access date and time */ |
145 | 145 | uint8_t crtime_cs; /* creation time in cs (centiseconds) */ |
146 | 146 | uint8_t mtime_cs; /* latest modification time in cs */ |
147 | - uint8_t __unknown2[10]; | |
147 | + uint8_t crtime_tzo, mtime_tzo, atime_tzo; /* timezone offset encoded */ | |
148 | + uint8_t __unknown2[7]; | |
148 | 149 | } |
149 | 150 | PACKED; |
150 | 151 | STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32); |
@@ -135,9 +135,10 @@ static void init_node_meta1(struct exfat_node* node, | ||
135 | 135 | node->attrib = le16_to_cpu(meta1->attrib); |
136 | 136 | node->continuations = meta1->continuations; |
137 | 137 | node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime, |
138 | - meta1->mtime_cs); | |
138 | + meta1->mtime_cs, meta1->mtime_tzo); | |
139 | 139 | /* there is no centiseconds field for atime */ |
140 | - node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0); | |
140 | + node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, | |
141 | + 0, meta1->atime_tzo); | |
141 | 142 | } |
142 | 143 | |
143 | 144 | static void init_node_meta2(struct exfat_node* node, |
@@ -646,8 +647,9 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node) | ||
646 | 647 | |
647 | 648 | meta1->attrib = cpu_to_le16(node->attrib); |
648 | 649 | exfat_unix2exfat(node->mtime, &meta1->mdate, &meta1->mtime, |
649 | - &meta1->mtime_cs); | |
650 | - exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, NULL); | |
650 | + &meta1->mtime_cs, &meta1->mtime_tzo); | |
651 | + exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, | |
652 | + NULL, &meta1->atime_tzo); | |
651 | 653 | meta2->size = meta2->valid_size = cpu_to_le64(node->size); |
652 | 654 | meta2->start_cluster = cpu_to_le32(node->start_cluster); |
653 | 655 | meta2->flags = EXFAT_FLAG_ALWAYS1; |
@@ -895,10 +897,11 @@ static int commit_entry(struct exfat* ef, struct exfat_node* dir, | ||
895 | 897 | meta1->continuations = 1 + name_entries; |
896 | 898 | meta1->attrib = cpu_to_le16(attrib); |
897 | 899 | exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime, |
898 | - &meta1->crtime_cs); | |
900 | + &meta1->crtime_cs, &meta1->crtime_tzo); | |
899 | 901 | meta1->adate = meta1->mdate = meta1->crdate; |
900 | 902 | meta1->atime = meta1->mtime = meta1->crtime; |
901 | 903 | meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */ |
904 | + meta1->atime_tzo = meta1->mtime_tzo = meta1->crtime_tzo; | |
902 | 905 | |
903 | 906 | meta2->type = EXFAT_ENTRY_FILE_INFO; |
904 | 907 | meta2->flags = EXFAT_FLAG_ALWAYS1; |
@@ -53,7 +53,8 @@ static const time_t days_in_year[] = | ||
53 | 53 | 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 |
54 | 54 | }; |
55 | 55 | |
56 | -time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec) | |
56 | +time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec, | |
57 | + uint8_t tzoffset) | |
57 | 58 | { |
58 | 59 | time_t unix_time = EPOCH_DIFF_SEC; |
59 | 60 | uint16_t ndate = le16_to_cpu(date); |
@@ -100,13 +101,18 @@ time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec) | ||
100 | 101 | unix_time += centisec / 100; |
101 | 102 | |
102 | 103 | /* exFAT stores timestamps in local time, so we correct it to UTC */ |
103 | - unix_time += exfat_timezone; | |
104 | + if (tzoffset & 0x80) | |
105 | + /* lower 7 bits are signed timezone offset in 15 minute increments */ | |
106 | + unix_time -= (int8_t)(tzoffset << 1) * 15 * 60 / 2; | |
107 | + else | |
108 | + /* timezone offset not present, assume our local timezone */ | |
109 | + unix_time += exfat_timezone; | |
104 | 110 | |
105 | 111 | return unix_time; |
106 | 112 | } |
107 | 113 | |
108 | 114 | void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, |
109 | - uint8_t* centisec) | |
115 | + uint8_t* centisec, uint8_t* tzoffset) | |
110 | 116 | { |
111 | 117 | time_t shift = EPOCH_DIFF_SEC + exfat_timezone; |
112 | 118 | uint16_t day, month, year; |
@@ -146,6 +152,9 @@ void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time, | ||
146 | 152 | *time = cpu_to_le16(twosec | (min << 5) | (hour << 11)); |
147 | 153 | if (centisec) |
148 | 154 | *centisec = (unix_time % 2) * 100; |
155 | + | |
156 | + /* record our local timezone offset in exFAT (15 minute increment) format */ | |
157 | + *tzoffset = (uint8_t)(-exfat_timezone / 60 / 15) | 0x80; | |
149 | 158 | } |
150 | 159 | |
151 | 160 | void exfat_tzset(void) |