]> git.sven.stormbind.net Git - sven/fuse-exfat.git/blobdiff - libexfat/node.c
New upstream version 1.3.0+git20220115
[sven/fuse-exfat.git] / libexfat / node.c
index ab1d7d6d04c7d36831c34c20dda7b0c5a4fade6e..9cffbcb8a68cfc36da6965560578ddb0cc1d6c4c 100644 (file)
@@ -87,7 +87,7 @@ static int read_entries(struct exfat* ef, struct exfat_node* dir,
 
        size = exfat_generic_pread(ef, dir, entries,
                        sizeof(struct exfat_entry[n]), offset);
-       if (size == sizeof(struct exfat_entry[n]))
+       if (size == (ssize_t) sizeof(struct exfat_entry) * n)
                return 0; /* success */
        if (size == 0)
                return -ENOENT;
@@ -108,7 +108,7 @@ static int write_entries(struct exfat* ef, struct exfat_node* dir,
 
        size = exfat_generic_pwrite(ef, dir, entries,
                        sizeof(struct exfat_entry[n]), offset);
-       if (size == sizeof(struct exfat_entry[n]))
+       if (size == (ssize_t) sizeof(struct exfat_entry) * n)
                return 0; /* success */
        if (size < 0)
                return -EIO;
@@ -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,
@@ -395,6 +396,7 @@ static int readdir(struct exfat* ef, struct exfat_node* parent,
        const struct exfat_entry_label* label;
        uint64_t upcase_size = 0;
        le16_t* upcase_comp = NULL;
+       le16_t label_name[EXFAT_ENAME_MAX];
 
        for (;;)
        {
@@ -507,7 +509,10 @@ static int readdir(struct exfat* ef, struct exfat_node* parent,
                                exfat_error("too long label (%hhu chars)", label->length);
                                return -EIO;
                        }
-                       if (utf16_to_utf8(ef->label, label->name,
+                       /* copy to a temporary buffer to avoid unaligned access to a
+                          packed member */
+                       memcpy(label_name, label->name, sizeof(label_name));
+                       if (exfat_utf16_to_utf8(ef->label, label_name,
                                                sizeof(ef->label), EXFAT_ENAME_MAX) != 0)
                                return -EIO;
                        break;
@@ -627,6 +632,7 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
        struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
        struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
        int rc;
+       le16_t edate, etime;
 
        if (!node->is_dirty)
                return 0; /* no need to flush */
@@ -645,9 +651,14 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
                return -EIO;
 
        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);
+       exfat_unix2exfat(node->mtime, &edate, &etime,
+                       &meta1->mtime_cs, &meta1->mtime_tzo);
+       meta1->mdate = edate;
+       meta1->mtime = etime;
+       exfat_unix2exfat(node->atime, &edate, &etime,
+                       NULL, &meta1->atime_tzo);
+       meta1->adate = edate;
+       meta1->atime = etime;
        meta2->size = meta2->valid_size = cpu_to_le64(node->size);
        meta2->start_cluster = cpu_to_le32(node->start_cluster);
        meta2->flags = EXFAT_FLAG_ALWAYS1;
@@ -730,7 +741,7 @@ static int shrink_directory(struct exfat* ef, struct exfat_node* dir,
                /* two subentries with meta info */
                entries += 2;
                /* subentries with file name */
-               entries += DIV_ROUND_UP(utf16_length(last_node->name),
+               entries += DIV_ROUND_UP(exfat_utf16_length(last_node->name),
                                EXFAT_ENAME_MAX);
        }
 
@@ -798,7 +809,7 @@ static int check_slot(struct exfat* ef, struct exfat_node* dir, off_t offset,
 {
        struct exfat_entry entries[n];
        int rc;
-       size_t i;
+       int i;
 
        /* Root directory contains entries, that don't have any nodes associated
           with them (clusters bitmap, upper case table, label). We need to be
@@ -836,7 +847,7 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir,
                return -ENOMEM;
        }
        for (p = dir->child; p != NULL; p = p->next)
-               for (i = 0; i < 1 + p->continuations; i++)
+               for (i = 0; i < 1u + p->continuations; i++)
                        BMAP_SET(dmap, p->entry_offset / sizeof(struct exfat_entry) + i);
 
        /* find a slot in the directory entries bitmap */
@@ -847,21 +858,24 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir,
                        if (contiguous++ == 0)
                                *offset = (off_t) i * sizeof(struct exfat_entry);
                        if (contiguous == n)
+                       {
+                               int rc;
+
                                /* suitable slot is found, check that it's not occupied */
-                               switch (check_slot(ef, dir, *offset, n))
+                               rc = check_slot(ef, dir, *offset, n);
+                               if (rc == -EINVAL)
                                {
-                               case 0:
-                                       free(dmap);
-                                       return 0;
-                               case -EIO:
-                                       free(dmap);
-                                       return -EIO;
-                               case -EINVAL:
                                        /* slot at (i-n) is occupied, go back and check (i-n+1) */
                                        i -= contiguous - 1;
                                        contiguous = 0;
-                                       break;
                                }
+                               else
+                               {
+                                       /* slot is free or an error occurred */
+                                       free(dmap);
+                                       return rc;
+                               }
+                       }
                }
                else
                        contiguous = 0;
@@ -881,24 +895,26 @@ static int commit_entry(struct exfat* ef, struct exfat_node* dir,
                const le16_t* name, off_t offset, uint16_t attrib)
 {
        struct exfat_node* node;
-       const size_t name_length = utf16_length(name);
+       const size_t name_length = exfat_utf16_length(name);
        const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
        struct exfat_entry entries[2 + name_entries];
        struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
        struct exfat_entry_meta2* meta2 = (struct exfat_entry_meta2*) &entries[1];
        int i;
        int rc;
+       le16_t edate, etime;
 
        memset(entries, 0, sizeof(struct exfat_entry[2]));
 
        meta1->type = EXFAT_ENTRY_FILE;
        meta1->continuations = 1 + name_entries;
        meta1->attrib = cpu_to_le16(attrib);
-       exfat_unix2exfat(time(NULL), &meta1->crdate, &meta1->crtime,
-                       &meta1->crtime_cs);
-       meta1->adate = meta1->mdate = meta1->crdate;
-       meta1->atime = meta1->mtime = meta1->crtime;
+       exfat_unix2exfat(time(NULL), &edate, &etime,
+                       &meta1->crtime_cs, &meta1->crtime_tzo);
+       meta1->adate = meta1->mdate = meta1->crdate = edate;
+       meta1->atime = meta1->mtime = meta1->crtime = etime;
        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;
@@ -953,7 +969,7 @@ static int create(struct exfat* ef, const char* path, uint16_t attrib)
        }
 
        rc = find_slot(ef, dir, &offset,
-                       2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+                       2 + DIV_ROUND_UP(exfat_utf16_length(name), EXFAT_ENAME_MAX));
        if (rc != 0)
        {
                exfat_put_node(ef, dir);
@@ -1009,7 +1025,7 @@ int exfat_mkdir(struct exfat* ef, const char* path)
 static int rename_entry(struct exfat* ef, struct exfat_node* dir,
                struct exfat_node* node, const le16_t* name, off_t new_offset)
 {
-       const size_t name_length = utf16_length(name);
+       const size_t name_length = exfat_utf16_length(name);
        const int name_entries = DIV_ROUND_UP(name_length, EXFAT_ENAME_MAX);
        struct exfat_entry entries[2 + name_entries];
        struct exfat_entry_meta1* meta1 = (struct exfat_entry_meta1*) &entries[0];
@@ -1132,7 +1148,7 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
        }
 
        rc = find_slot(ef, dir, &offset,
-                       2 + DIV_ROUND_UP(utf16_length(name), EXFAT_ENAME_MAX));
+                       2 + DIV_ROUND_UP(exfat_utf16_length(name), EXFAT_ENAME_MAX));
        if (rc != 0)
        {
                exfat_put_node(ef, dir);
@@ -1201,7 +1217,8 @@ int exfat_set_label(struct exfat* ef, const char* label)
        struct exfat_entry_label entry;
 
        memset(label_utf16, 0, sizeof(label_utf16));
-       rc = utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1, strlen(label));
+       rc = exfat_utf8_to_utf16(label_utf16, label, EXFAT_ENAME_MAX + 1,
+                       strlen(label));
        if (rc != 0)
                return rc;
 
@@ -1212,7 +1229,7 @@ int exfat_set_label(struct exfat* ef, const char* label)
                return rc;
 
        entry.type = EXFAT_ENTRY_LABEL;
-       entry.length = utf16_length(label_utf16);
+       entry.length = exfat_utf16_length(label_utf16);
        memcpy(entry.name, label_utf16, sizeof(entry.name));
        if (entry.length == 0)
                entry.type ^= EXFAT_ENTRY_VALID;