OSDN Git Service

Address clusters bitmap using size_t-sized blocks instead of bytes. This should be...
[android-x86/external-exfat.git] / libexfat / node.c
index cce1de7..e83ec9e 100644 (file)
@@ -2,11 +2,12 @@
        node.c (09.10.09)
        exFAT file system implementation library.
 
        node.c (09.10.09)
        exFAT file system implementation library.
 
+       Free exFAT implementation.
        Copyright (C) 2010-2013  Andrew Nayenko
 
        Copyright (C) 2010-2013  Andrew Nayenko
 
-       This program is free software: you can redistribute it and/or modify
+       This program is free software; you can redistribute it and/or modify
        it under the terms of the GNU General Public License as published by
        it under the terms of the GNU General Public License as published by
-       the Free Software Foundation, either version 3 of the License, or
+       the Free Software Foundation, either version 2 of the License, or
        (at your option) any later version.
 
        This program is distributed in the hope that it will be useful,
        (at your option) any later version.
 
        This program is distributed in the hope that it will be useful,
@@ -14,8 +15,9 @@
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
 
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
 
-       You should have received a copy of the GNU General Public License
-       along with this program.  If not, see <http://www.gnu.org/licenses/>.
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
 #include "exfat.h"
 */
 
 #include "exfat.h"
@@ -44,23 +46,21 @@ void exfat_put_node(struct exfat* ef, struct exfat_node* node)
 {
        if (--node->references < 0)
        {
 {
        if (--node->references < 0)
        {
-               char buffer[EXFAT_NAME_MAX + 1];
-               exfat_get_name(node, buffer, EXFAT_NAME_MAX);
+               char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
                exfat_bug("reference counter of `%s' is below zero", buffer);
        }
 
        if (node->references == 0)
        {
                exfat_bug("reference counter of `%s' is below zero", buffer);
        }
 
        if (node->references == 0)
        {
-               if (node->flags & EXFAT_ATTRIB_DIRTY)
-                       exfat_flush_node(ef, node);
+               exfat_flush_node(ef, node);
                if (node->flags & EXFAT_ATTRIB_UNLINKED)
                {
                        /* free all clusters and node structure itself */
                if (node->flags & EXFAT_ATTRIB_UNLINKED)
                {
                        /* free all clusters and node structure itself */
-                       exfat_truncate(ef, node, 0);
+                       exfat_truncate(ef, node, 0, true);
                        free(node);
                }
                        free(node);
                }
-               if (ef->cmap.dirty)
-                       exfat_flush_cmap(ef);
+               exfat_flush(ef);
        }
 }
 
        }
 }
 
@@ -218,6 +218,12 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                exfat_error("too few continuations (%hhu)", continuations);
                                goto error;
                        }
                                exfat_error("too few continuations (%hhu)", continuations);
                                goto error;
                        }
+                       if (continuations > 1 +
+                                       DIV_ROUND_UP(EXFAT_NAME_MAX, EXFAT_ENAME_MAX))
+                       {
+                               exfat_error("too many continuations (%hhu)", continuations);
+                               goto error;
+                       }
                        reference_checksum = le16_to_cpu(meta1->checksum);
                        actual_checksum = exfat_start_checksum(meta1);
                        *node = allocate_node();
                        reference_checksum = le16_to_cpu(meta1->checksum);
                        actual_checksum = exfat_start_checksum(meta1);
                        *node = allocate_node();
@@ -276,7 +282,10 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        file_name = (const struct exfat_entry_name*) entry;
                        actual_checksum = exfat_add_checksum(entry, actual_checksum);
 
                        file_name = (const struct exfat_entry_name*) entry;
                        actual_checksum = exfat_add_checksum(entry, actual_checksum);
 
-                       memcpy(namep, file_name->name, EXFAT_ENAME_MAX * sizeof(le16_t));
+                       memcpy(namep, file_name->name,
+                                       MIN(EXFAT_ENAME_MAX,
+                                               ((*node)->name + EXFAT_NAME_MAX - namep)) *
+                                       sizeof(le16_t));
                        namep += EXFAT_ENAME_MAX;
                        if (--continuations == 0)
                        {
                        namep += EXFAT_ENAME_MAX;
                        if (--continuations == 0)
                        {
@@ -293,9 +302,9 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                */
                                if (real_size != (*node)->size)
                                {
                                */
                                if (real_size != (*node)->size)
                                {
-                                       char buffer[EXFAT_NAME_MAX + 1];
+                                       char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
 
 
-                                       exfat_get_name(*node, buffer, EXFAT_NAME_MAX);
+                                       exfat_get_name(*node, buffer, sizeof(buffer) - 1);
                                        exfat_error("`%s' real size does not equal to size "
                                                        "(%"PRIu64" != %"PRIu64")", buffer,
                                                        real_size, (*node)->size);
                                        exfat_error("`%s' real size does not equal to size "
                                                        "(%"PRIu64" != %"PRIu64")", buffer,
                                                        real_size, (*node)->size);
@@ -303,9 +312,9 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                }
                                if (actual_checksum != reference_checksum)
                                {
                                }
                                if (actual_checksum != reference_checksum)
                                {
-                                       char buffer[EXFAT_NAME_MAX + 1];
+                                       char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
 
 
-                                       exfat_get_name(*node, buffer, EXFAT_NAME_MAX);
+                                       exfat_get_name(*node, buffer, sizeof(buffer) - 1);
                                        exfat_error("`%s' has invalid checksum (0x%hx != 0x%hx)",
                                                        buffer, actual_checksum, reference_checksum);
                                        goto error;
                                        exfat_error("`%s' has invalid checksum (0x%hx != 0x%hx)",
                                                        buffer, actual_checksum, reference_checksum);
                                        goto error;
@@ -359,16 +368,17 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        }
                        ef->cmap.size = le32_to_cpu(ef->sb->cluster_count) -
                                EXFAT_FIRST_DATA_CLUSTER;
                        }
                        ef->cmap.size = le32_to_cpu(ef->sb->cluster_count) -
                                EXFAT_FIRST_DATA_CLUSTER;
-                       if (le64_to_cpu(bitmap->size) < (ef->cmap.size + 7) / 8)
+                       if (le64_to_cpu(bitmap->size) < DIV_ROUND_UP(ef->cmap.size, 8))
                        {
                                exfat_error("invalid clusters bitmap size: %"PRIu64
                                                " (expected at least %u)",
                        {
                                exfat_error("invalid clusters bitmap size: %"PRIu64
                                                " (expected at least %u)",
-                                               le64_to_cpu(bitmap->size), (ef->cmap.size + 7) / 8);
+                                               le64_to_cpu(bitmap->size),
+                                               DIV_ROUND_UP(ef->cmap.size, 8));
                                goto error;
                        }
                        /* FIXME bitmap can be rather big, up to 512 MB */
                        ef->cmap.chunk_size = ef->cmap.size;
                                goto error;
                        }
                        /* FIXME bitmap can be rather big, up to 512 MB */
                        ef->cmap.chunk_size = ef->cmap.size;
-                       ef->cmap.chunk = malloc(le64_to_cpu(bitmap->size));
+                       ef->cmap.chunk = malloc(BMAP_SIZE(ef->cmap.chunk_size));
                        if (ef->cmap.chunk == NULL)
                        {
                                exfat_error("failed to allocate clusters bitmap chunk "
                        if (ef->cmap.chunk == NULL)
                        {
                                exfat_error("failed to allocate clusters bitmap chunk "
@@ -377,7 +387,8 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                goto error;
                        }
 
                                goto error;
                        }
 
-                       exfat_pread(ef->dev, ef->cmap.chunk, le64_to_cpu(bitmap->size),
+                       exfat_pread(ef->dev, ef->cmap.chunk,
+                                       BMAP_SIZE(ef->cmap.chunk_size),
                                        exfat_c2o(ef, ef->cmap.start_cluster));
                        break;
 
                                        exfat_c2o(ef, ef->cmap.start_cluster));
                        break;
 
@@ -389,7 +400,7 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                goto error;
                        }
                        if (utf16_to_utf8(ef->label, label->name,
                                goto error;
                        }
                        if (utf16_to_utf8(ef->label, label->name,
-                                               sizeof(ef->label), EXFAT_ENAME_MAX) != 0)
+                                               sizeof(ef->label) - 1, EXFAT_ENAME_MAX) != 0)
                                goto error;
                        break;
 
                                goto error;
                        break;
 
@@ -493,8 +504,8 @@ static void reset_cache(struct exfat* ef, struct exfat_node* node)
        node->flags &= ~EXFAT_ATTRIB_CACHED;
        if (node->references != 0)
        {
        node->flags &= ~EXFAT_ATTRIB_CACHED;
        if (node->references != 0)
        {
-               char buffer[EXFAT_NAME_MAX + 1];
-               exfat_get_name(node, buffer, EXFAT_NAME_MAX);
+               char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
+               exfat_get_name(node, buffer, sizeof(buffer) - 1);
                exfat_warn("non-zero reference counter (%d) for `%s'",
                                node->references, buffer);
        }
                exfat_warn("non-zero reference counter (%d) for `%s'",
                                node->references, buffer);
        }
@@ -507,7 +518,7 @@ void exfat_reset_cache(struct exfat* ef)
        reset_cache(ef, ef->root);
 }
 
        reset_cache(ef, ef->root);
 }
 
-void next_entry(struct exfat* ef, const struct exfat_node* parent,
+static void next_entry(struct exfat* ef, const struct exfat_node* parent,
                cluster_t* cluster, off_t* offset)
 {
        *offset += sizeof(struct exfat_entry);
                cluster_t* cluster, off_t* offset)
 {
        *offset += sizeof(struct exfat_entry);
@@ -524,6 +535,9 @@ void exfat_flush_node(struct exfat* ef, struct exfat_node* node)
        struct exfat_entry_meta1 meta1;
        struct exfat_entry_meta2 meta2;
 
        struct exfat_entry_meta1 meta1;
        struct exfat_entry_meta2 meta2;
 
+       if (!(node->flags & EXFAT_ATTRIB_DIRTY))
+               return; /* no need to flush */
+
        if (ef->ro)
                exfat_bug("unable to flush node to read-only FS");
 
        if (ef->ro)
                exfat_bug("unable to flush node to read-only FS");
 
@@ -627,7 +641,7 @@ static int shrink_directory(struct exfat* ef, struct exfat_node* dir,
                new_size = CLUSTER_SIZE(*ef->sb);
        if (new_size == dir->size)
                return 0;
                new_size = CLUSTER_SIZE(*ef->sb);
        if (new_size == dir->size)
                return 0;
-       rc = exfat_truncate(ef, dir, new_size);
+       rc = exfat_truncate(ef, dir, new_size, true);
        if (rc != 0)
                return rc;
        return 0;
        if (rc != 0)
                return rc;
        return 0;
@@ -673,7 +687,7 @@ static int grow_directory(struct exfat* ef, struct exfat_node* dir,
 {
        return exfat_truncate(ef, dir,
                        DIV_ROUND_UP(asize + difference, CLUSTER_SIZE(*ef->sb))
 {
        return exfat_truncate(ef, dir,
                        DIV_ROUND_UP(asize + difference, CLUSTER_SIZE(*ef->sb))
-                               * CLUSTER_SIZE(*ef->sb));
+                               * CLUSTER_SIZE(*ef->sb), true);
 }
 
 static int find_slot(struct exfat* ef, struct exfat_node* dir,
 }
 
 static int find_slot(struct exfat* ef, struct exfat_node* dir,
@@ -764,7 +778,8 @@ static int write_entry(struct exfat* ef, struct exfat_node* dir,
        {
                struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
                memcpy(name_entry.name, node->name + i * EXFAT_ENAME_MAX,
        {
                struct exfat_entry_name name_entry = {EXFAT_ENTRY_FILE_NAME, 0};
                memcpy(name_entry.name, node->name + i * EXFAT_ENAME_MAX,
-                               EXFAT_ENAME_MAX * sizeof(le16_t));
+                               MIN(EXFAT_ENAME_MAX, EXFAT_NAME_MAX - i * EXFAT_ENAME_MAX) *
+                               sizeof(le16_t));
                next_entry(ef, dir, &cluster, &offset);
                exfat_pwrite(ef->dev, &name_entry, sizeof(name_entry),
                                co2o(ef, cluster, offset));
                next_entry(ef, dir, &cluster, &offset);
                exfat_pwrite(ef->dev, &name_entry, sizeof(name_entry),
                                co2o(ef, cluster, offset));
@@ -826,7 +841,7 @@ int exfat_mkdir(struct exfat* ef, const char* path)
        if (rc != 0)
                return 0;
        /* directories always have at least one cluster */
        if (rc != 0)
                return 0;
        /* directories always have at least one cluster */
-       rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb));
+       rc = exfat_truncate(ef, node, CLUSTER_SIZE(*ef->sb), true);
        if (rc != 0)
        {
                delete(ef, node);
        if (rc != 0)
        {
                delete(ef, node);
@@ -905,6 +920,23 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
                exfat_put_node(ef, node);
                return rc;
        }
                exfat_put_node(ef, node);
                return rc;
        }
+
+       /* check that target is not a subdirectory of the source */
+       if (node->flags & EXFAT_ATTRIB_DIR)
+       {
+               struct exfat_node* p;
+
+               for (p = dir; p; p = p->parent)
+                       if (node == p)
+                       {
+                               if (existing != NULL)
+                                       exfat_put_node(ef, existing);
+                               exfat_put_node(ef, dir);
+                               exfat_put_node(ef, node);
+                               return -EINVAL;
+                       }
+       }
+
        if (existing != NULL)
        {
                /* remove target if it's not the same node as source */
        if (existing != NULL)
        {
                /* remove target if it's not the same node as source */
@@ -1034,5 +1066,6 @@ int exfat_set_label(struct exfat* ef, const char* label)
 
        exfat_pwrite(ef->dev, &entry, sizeof(struct exfat_entry_label),
                        co2o(ef, cluster, offset));
 
        exfat_pwrite(ef->dev, &entry, sizeof(struct exfat_entry_label),
                        co2o(ef, cluster, offset));
+       strcpy(ef->label, label);
        return 0;
 }
        return 0;
 }