OSDN Git Service

Remove unused field of struct iterator.
[android-x86/external-exfat.git] / libexfat / node.c
index 4020874..a5491b1 100644 (file)
@@ -3,7 +3,7 @@
        exFAT file system implementation library.
 
        Free exFAT implementation.
-       Copyright (C) 2010-2013  Andrew Nayenko
+       Copyright (C) 2010-2016  Andrew Nayenko
 
        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
@@ -30,7 +30,6 @@ struct iterator
 {
        cluster_t cluster;
        off_t offset;
-       int contiguous;
        char* chunk;
 };
 
@@ -50,14 +49,14 @@ void exfat_put_node(struct exfat* ef, struct exfat_node* node)
        if (node->references < 0)
        {
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
-               exfat_bug("reference counter of `%s' is below zero", buffer);
+               exfat_bug("reference counter of '%s' is below zero", buffer);
        }
        else if (node->references == 0 && node != ef->root)
        {
                if (node->flags & EXFAT_ATTRIB_DIRTY)
                {
                        exfat_get_name(node, buffer, sizeof(buffer) - 1);
-                       exfat_warn("dirty node `%s' with zero references", buffer);
+                       exfat_warn("dirty node '%s' with zero references", buffer);
                }
        }
 }
@@ -99,7 +98,6 @@ static int opendir(struct exfat* ef, const struct exfat_node* dir,
                exfat_bug("not a directory");
        it->cluster = dir->start_cluster;
        it->offset = 0;
-       it->contiguous = IS_CONTIGUOUS(*dir);
        it->chunk = malloc(CLUSTER_SIZE(*ef->sb));
        if (it->chunk == NULL)
        {
@@ -109,6 +107,7 @@ static int opendir(struct exfat* ef, const struct exfat_node* dir,
        if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb),
                        exfat_c2o(ef, it->cluster)) < 0)
        {
+               free(it->chunk);
                exfat_error("failed to read directory cluster %#x", it->cluster);
                return -EIO;
        }
@@ -119,12 +118,11 @@ static void closedir(struct iterator* it)
 {
        it->cluster = 0;
        it->offset = 0;
-       it->contiguous = 0;
        free(it->chunk);
        it->chunk = NULL;
 }
 
-static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent,
+static bool fetch_next_entry(struct exfat* ef, const struct exfat_node* parent,
                struct iterator* it)
 {
        /* move iterator to the next entry in the directory */
@@ -135,23 +133,23 @@ static int fetch_next_entry(struct exfat* ef, const struct exfat_node* parent,
                /* reached the end of directory; the caller should check this
                   condition too */
                if (it->offset >= parent->size)
-                       return 0;
+                       return true;
                it->cluster = exfat_next_cluster(ef, parent, it->cluster);
                if (CLUSTER_INVALID(it->cluster))
                {
                        exfat_error("invalid cluster 0x%x while reading directory",
                                        it->cluster);
-                       return 1;
+                       return false;
                }
                if (exfat_pread(ef->dev, it->chunk, CLUSTER_SIZE(*ef->sb),
                                exfat_c2o(ef, it->cluster)) < 0)
                {
                        exfat_error("failed to read the next directory cluster %#x",
                                        it->cluster);
-                       return 1;
+                       return false;
                }
        }
-       return 0;
+       return true;
 }
 
 static struct exfat_node* allocate_node(void)
@@ -194,7 +192,7 @@ static const struct exfat_entry* get_entry_ptr(const struct exfat* ef,
 }
 
 static bool check_node(const struct exfat_node* node, uint16_t actual_checksum,
-               uint16_t reference_checksum, uint64_t real_size)
+               uint16_t reference_checksum, uint64_t valid_size)
 {
        char buffer[UTF8_BYTES(EXFAT_NAME_MAX) + 1];
 
@@ -205,26 +203,48 @@ static bool check_node(const struct exfat_node* node, uint16_t actual_checksum,
        if (actual_checksum != reference_checksum)
        {
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
-               exfat_error("`%s' has invalid checksum (%#hx != %#hx)", buffer,
+               exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer,
                                actual_checksum, reference_checksum);
                return false;
        }
 
        /*
-          It's unclear what is real_size field needed for. It usually equals to
-          the size but may contain any value less than size (including 0).
+          exFAT does not support sparse files but allows files with uninitialized
+          clusters. For such files valid_size means initialized data size and
+          cannot be greater than file size. See SetFileValidData() function
+          description in MSDN.
        */
-       if (real_size > node->size)
+       if (valid_size > node->size)
        {
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
-               exfat_error("`%s' has real size (%"PRIu64") greater than size "
-                               "(%"PRIu64")", buffer, real_size, node->size);
+               exfat_error("'%s' has valid size (%"PRIu64") greater than size "
+                               "(%"PRIu64")", buffer, valid_size, node->size);
                return false;
        }
 
        return true;
 }
 
+static void decompress_upcase(uint16_t* output, const le16_t* source,
+               size_t size)
+{
+       size_t si;
+       size_t oi;
+
+       for (oi = 0; oi < EXFAT_UPCASE_CHARS; oi++)
+               output[oi] = oi;
+
+       for (si = 0, oi = 0; si < size && oi < EXFAT_UPCASE_CHARS; si++)
+       {
+               uint16_t ch = le16_to_cpu(source[si]);
+
+               if (ch == 0xffff && si + 1 < size)      /* indicates a run */
+                       oi += le16_to_cpu(source[++si]);
+               else
+                       output[oi++] = ch;
+       }
+}
+
 /*
  * Reads one entry in directory at position pointed by iterator and fills
  * node structure.
@@ -244,7 +264,9 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
        le16_t* namep = NULL;
        uint16_t reference_checksum = 0;
        uint16_t actual_checksum = 0;
-       uint64_t real_size = 0;
+       uint64_t valid_size = 0;
+       uint64_t upcase_size = 0;
+       le16_t* upcase_comp = NULL;
 
        *node = NULL;
 
@@ -315,7 +337,7 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        }
                        init_node_meta2(*node, meta2);
                        actual_checksum = exfat_add_checksum(entry, actual_checksum);
-                       real_size = le64_to_cpu(meta2->real_size);
+                       valid_size = le64_to_cpu(meta2->valid_size);
                        /* empty files must be marked as non-contiguous */
                        if ((*node)->size == 0 && (meta2->flags & EXFAT_FLAG_CONTIGUOUS))
                        {
@@ -351,9 +373,9 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        if (--continuations == 0)
                        {
                                if (!check_node(*node, actual_checksum, reference_checksum,
-                                               real_size))
+                                               valid_size))
                                        goto error;
-                               if (fetch_next_entry(ef, parent, it) != 0)
+                               if (!fetch_next_entry(ef, parent, it))
                                        goto error;
                                return 0; /* entry completed */
                        }
@@ -369,33 +391,48 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                                                le32_to_cpu(upcase->start_cluster));
                                goto error;
                        }
-                       if (le64_to_cpu(upcase->size) == 0 ||
-                               le64_to_cpu(upcase->size) > 0xffff * sizeof(uint16_t) ||
-                               le64_to_cpu(upcase->size) % sizeof(uint16_t) != 0)
+                       upcase_size = le64_to_cpu(upcase->size);
+                       if (upcase_size == 0 ||
+                               upcase_size > EXFAT_UPCASE_CHARS * sizeof(uint16_t) ||
+                               upcase_size % sizeof(uint16_t) != 0)
                        {
                                exfat_error("bad upcase table size (%"PRIu64" bytes)",
-                                               le64_to_cpu(upcase->size));
+                                               upcase_size);
                                goto error;
                        }
-                       ef->upcase = malloc(le64_to_cpu(upcase->size));
-                       if (ef->upcase == NULL)
+                       upcase_comp = malloc(upcase_size);
+                       if (upcase_comp == NULL)
                        {
                                exfat_error("failed to allocate upcase table (%"PRIu64" bytes)",
-                                               le64_to_cpu(upcase->size));
+                                               upcase_size);
                                rc = -ENOMEM;
                                goto error;
                        }
-                       ef->upcase_chars = le64_to_cpu(upcase->size) / sizeof(le16_t);
 
-                       if (exfat_pread(ef->dev, ef->upcase, le64_to_cpu(upcase->size),
+                       /* read compressed upcase table */
+                       if (exfat_pread(ef->dev, upcase_comp, upcase_size,
                                        exfat_c2o(ef, le32_to_cpu(upcase->start_cluster))) < 0)
                        {
+                               free(upcase_comp);
                                exfat_error("failed to read upper case table "
                                                "(%"PRIu64" bytes starting at cluster %#x)",
-                                               le64_to_cpu(upcase->size),
+                                               upcase_size,
                                                le32_to_cpu(upcase->start_cluster));
                                goto error;
                        }
+
+                       /* decompress upcase table */
+                       ef->upcase = calloc(EXFAT_UPCASE_CHARS, sizeof(uint16_t));
+                       if (ef->upcase == NULL)
+                       {
+                               free(upcase_comp);
+                               exfat_error("failed to allocate decompressed upcase table");
+                               rc = -ENOMEM;
+                               goto error;
+                       }
+                       decompress_upcase(ef->upcase, upcase_comp,
+                                       upcase_size / sizeof(uint16_t));
+                       free(upcase_comp);
                        break;
 
                case EXFAT_ENTRY_BITMAP:
@@ -452,15 +489,25 @@ static int readdir(struct exfat* ef, const struct exfat_node* parent,
                        break;
 
                default:
-                       if (entry->type & EXFAT_ENTRY_VALID)
+                       if (!(entry->type & EXFAT_ENTRY_VALID))
+                               break; /* deleted entry, ignore it */
+                       if (!(entry->type & EXFAT_ENTRY_OPTIONAL))
                        {
-                               exfat_error("unknown entry type 0x%hhx", entry->type);
+                               exfat_error("unknown entry type %#hhx", entry->type);
+                               goto error;
+                       }
+                       /* optional entry, warn and skip */
+                       exfat_warn("unknown entry type %#hhx", entry->type);
+                       if (continuations == 0)
+                       {
+                               exfat_error("unexpected continuation");
                                goto error;
                        }
+                       --continuations;
                        break;
                }
 
-               if (fetch_next_entry(ef, parent, it) != 0)
+               if (!fetch_next_entry(ef, parent, it))
                        goto error;
        }
        /* we never reach here */
@@ -554,13 +601,13 @@ static void reset_cache(struct exfat* ef, struct exfat_node* node)
        if (node->references != 0)
        {
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
-               exfat_warn("non-zero reference counter (%d) for `%s'",
+               exfat_warn("non-zero reference counter (%d) for '%s'",
                                node->references, buffer);
        }
        if (node != ef->root && (node->flags & EXFAT_ATTRIB_DIRTY))
        {
                exfat_get_name(node, buffer, sizeof(buffer) - 1);
-               exfat_bug("node `%s' is dirty", buffer);
+               exfat_bug("node '%s' is dirty", buffer);
        }
        while (node->references)
                exfat_put_node(ef, node);
@@ -630,7 +677,7 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
        }
        if (meta2.type != EXFAT_ENTRY_FILE_INFO)
                exfat_bug("invalid type of meta2: 0x%hhx", meta2.type);
-       meta2.size = meta2.real_size = cpu_to_le64(node->size);
+       meta2.size = meta2.valid_size = cpu_to_le64(node->size);
        meta2.start_cluster = cpu_to_le32(node->start_cluster);
        meta2.flags = EXFAT_FLAG_ALWAYS1;
        /* empty files must not be marked as contiguous */
@@ -652,7 +699,7 @@ int exfat_flush_node(struct exfat* ef, struct exfat_node* node)
        }
 
        node->flags &= ~EXFAT_ATTRIB_DIRTY;
-       return 0;
+       return exfat_flush(ef);
 }
 
 static bool erase_entry(struct exfat* ef, struct exfat_node* node)
@@ -830,7 +877,7 @@ static int find_slot(struct exfat* ef, struct exfat_node* dir,
                                return rc;
                        }
                }
-               if (fetch_next_entry(ef, dir, &it) != 0)
+               if (!fetch_next_entry(ef, dir, &it))
                {
                        closedir(&it);
                        return -EIO;
@@ -961,7 +1008,7 @@ int exfat_mkdir(struct exfat* ef, const char* path)
        int rc;
        struct exfat_node* node;
 
-       rc = create(ef, path, EXFAT_ATTRIB_ARCH | EXFAT_ATTRIB_DIR);
+       rc = create(ef, path, EXFAT_ATTRIB_DIR);
        if (rc != 0)
                return rc;
        rc = exfat_lookup(ef, &node, path);
@@ -1118,6 +1165,16 @@ int exfat_rename(struct exfat* ef, const char* old_path, const char* new_path)
                        exfat_put_node(ef, existing);
                        if (rc != 0)
                        {
+                               /* free clusters even if something went wrong; overwise they
+                                  will be just lost */
+                               exfat_cleanup_node(ef, existing);
+                               exfat_put_node(ef, dir);
+                               exfat_put_node(ef, node);
+                               return rc;
+                       }
+                       rc = exfat_cleanup_node(ef, existing);
+                       if (rc != 0)
+                       {
                                exfat_put_node(ef, dir);
                                exfat_put_node(ef, node);
                                return rc;
@@ -1190,7 +1247,7 @@ static int find_label(struct exfat* ef, cluster_t* cluster, off_t* offset)
                        return 0;
                }
 
-               if (fetch_next_entry(ef, ef->root, &it) != 0)
+               if (!fetch_next_entry(ef, ef->root, &it))
                {
                        closedir(&it);
                        return -EIO;