OSDN Git Service

Add support for timezone offsets.
authorNathan Hunsperger <nathan@hunsperger.com>
Thu, 18 Apr 2019 23:12:49 +0000 (16:12 -0700)
committerrelan <relan@users.noreply.github.com>
Sat, 20 Apr 2019 18:14:49 +0000 (21:14 +0300)
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.

libexfat/exfat.h
libexfat/exfatfs.h
libexfat/node.c
libexfat/time.c

index 2342be4..8e9765f 100644 (file)
@@ -228,9 +228,10 @@ int exfat_set_label(struct exfat* ef, const char* label);
 int exfat_mount(struct exfat* ef, const char* spec, const char* options);
 void exfat_unmount(struct exfat* ef);
 
-time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec);
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec,
+               uint8_t tzoffset);
 void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
-               uint8_t* centisec);
+               uint8_t* centisec, uint8_t* tzoffset);
 void exfat_tzset(void);
 
 bool exfat_ask_to_fix(const struct exfat* ef);
index b7b6cac..b9ea268 100644 (file)
@@ -144,7 +144,8 @@ struct exfat_entry_meta1                    /* file or directory info (part 1) */
        le16_t atime, adate;                    /* latest access date and time */
        uint8_t crtime_cs;                              /* creation time in cs (centiseconds) */
        uint8_t mtime_cs;                               /* latest modification time in cs */
-       uint8_t __unknown2[10];
+       uint8_t crtime_tzo, mtime_tzo, atime_tzo;       /* timezone offset encoded */
+       uint8_t __unknown2[7];
 }
 PACKED;
 STATIC_ASSERT(sizeof(struct exfat_entry_meta1) == 32);
index ab1d7d6..3b78c63 100644 (file)
@@ -135,9 +135,10 @@ static void init_node_meta1(struct exfat_node* node,
        node->attrib = le16_to_cpu(meta1->attrib);
        node->continuations = meta1->continuations;
        node->mtime = exfat_exfat2unix(meta1->mdate, meta1->mtime,
-                       meta1->mtime_cs);
+                       meta1->mtime_cs, meta1->mtime_tzo);
        /* there is no centiseconds field for atime */
-       node->atime = exfat_exfat2unix(meta1->adate, meta1->atime, 0);
+       node->atime = exfat_exfat2unix(meta1->adate, meta1->atime,
+                       0, meta1->atime_tzo);
 }
 
 static void init_node_meta2(struct exfat_node* node,
@@ -646,8 +647,9 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
 
        meta1->attrib = cpu_to_le16(node->attrib);
        exfat_unix2exfat(node->mtime, &meta1->mdate, &meta1->mtime,
-                       &meta1->mtime_cs);
-       exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime, NULL);
+                       &meta1->mtime_cs, &meta1->mtime_tzo);
+       exfat_unix2exfat(node->atime, &meta1->adate, &meta1->atime,
+                       NULL, &meta1->atime_tzo);
        meta2->size = meta2->valid_size = cpu_to_le64(node->size);
        meta2->start_cluster = cpu_to_le32(node->start_cluster);
        meta2->flags = EXFAT_FLAG_ALWAYS1;
@@ -895,10 +897,11 @@ static int commit_entry(struct exfat* ef, struct exfat_node* dir,
        meta1->continuations = 1 + name_entries;
        meta1->attrib = cpu_to_le16(attrib);
        exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime,
-                       &meta1->crtime_cs);
+                       &meta1->crtime_cs, &meta1->crtime_tzo);
        meta1->adate = meta1->mdate = meta1->crdate;
        meta1->atime = meta1->mtime = meta1->crtime;
        meta1->mtime_cs = meta1->crtime_cs; /* there is no atime_cs */
+       meta1->atime_tzo = meta1->mtime_tzo = meta1->crtime_tzo;
 
        meta2->type = EXFAT_ENTRY_FILE_INFO;
        meta2->flags = EXFAT_FLAG_ALWAYS1;
index 31ae5a2..e2a3b23 100644 (file)
@@ -53,7 +53,8 @@ static const time_t days_in_year[] =
        0,   0,  31,  59,  90, 120, 151, 181, 212, 243, 273, 304, 334
 };
 
-time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
+time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec,
+               uint8_t tzoffset)
 {
        time_t unix_time = EPOCH_DIFF_SEC;
        uint16_t ndate = le16_to_cpu(date);
@@ -100,13 +101,18 @@ time_t exfat_exfat2unix(le16_t date, le16_t time, uint8_t centisec)
        unix_time += centisec / 100;
 
        /* exFAT stores timestamps in local time, so we correct it to UTC */
-       unix_time += exfat_timezone;
+       if (tzoffset & 0x80)
+               /* lower 7 bits are signed timezone offset in 15 minute increments */
+               unix_time -= (int8_t)(tzoffset << 1) * 15 * 60 / 2;
+       else
+               /* timezone offset not present, assume our local timezone */
+               unix_time += exfat_timezone;
 
        return unix_time;
 }
 
 void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
-               uint8_t* centisec)
+               uint8_t* centisec, uint8_t* tzoffset)
 {
        time_t shift = EPOCH_DIFF_SEC + exfat_timezone;
        uint16_t day, month, year;
@@ -146,6 +152,9 @@ void exfat_unix2exfat(time_t unix_time, le16_t* date, le16_t* time,
        *time = cpu_to_le16(twosec | (min << 5) | (hour << 11));
        if (centisec)
                *centisec = (unix_time % 2) * 100;
+
+       /* record our local timezone offset in exFAT (15 minute increment) format */
+       *tzoffset = (uint8_t)(-exfat_timezone / 60 / 15) | 0x80;
 }
 
 void exfat_tzset(void)