node.c (09.10.09)
exFAT file system implementation library.
+ Free exFAT implementation.
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
- 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,
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"
{
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)
{
- 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 */
- exfat_truncate(ef, node, 0);
+ exfat_truncate(ef, node, 0, true);
free(node);
}
- if (ef->cmap.dirty)
- exfat_flush_cmap(ef);
+ exfat_flush(ef);
}
}
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();
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)
{
*/
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);
}
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;
}
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)",
- 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;
- 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 "
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;
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;
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);
}
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);
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");
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;
{
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,
{
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));
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);
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 */
exfat_pwrite(ef->dev, &entry, sizeof(struct exfat_entry_label),
co2o(ef, cluster, offset));
+ strcpy(ef->label, label);
return 0;
}