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
{
cluster_t cluster;
off_t offset;
- int contiguous;
char* chunk;
};
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);
}
}
}
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)
{
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;
}
{
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 */
/* 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)
}
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];
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.
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;
}
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))
{
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 */
}
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:
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 */
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);
}
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 */
}
node->flags &= ~EXFAT_ATTRIB_DIRTY;
- return 0;
+ return exfat_flush(ef);
}
static bool erase_entry(struct exfat* ef, struct exfat_node* node)
return rc;
}
}
- if (fetch_next_entry(ef, dir, &it) != 0)
+ if (!fetch_next_entry(ef, dir, &it))
{
closedir(&it);
return -EIO;
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);
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;
return 0;
}
- if (fetch_next_entry(ef, ef->root, &it) != 0)
+ if (!fetch_next_entry(ef, ef->root, &it))
{
closedir(&it);
return -EIO;