+ node->is_contiguous = ((meta2->flags & EXFAT_FLAG_CONTIGUOUS) != 0);
+}
+
+static void init_node_name(struct exfat_node* node,
+ const struct exfat_entry* entries, int n)
+{
+ int i;
+
+ for (i = 0; i < n; i++)
+ memcpy(node->name + i * EXFAT_ENAME_MAX,
+ ((const struct exfat_entry_name*) &entries[i])->name,
+ EXFAT_ENAME_MAX * sizeof(le16_t));
+}
+
+static bool check_entries(const struct exfat_entry* entry, int n)
+{
+ int previous = EXFAT_ENTRY_NONE;
+ int current;
+ int i;
+
+ /* check transitions between entries types */
+ for (i = 0; i < n + 1; previous = current, i++)
+ {
+ bool valid = false;
+
+ current = (i < n) ? entry[i].type : EXFAT_ENTRY_NONE;
+ switch (previous)
+ {
+ case EXFAT_ENTRY_NONE:
+ valid = (current == EXFAT_ENTRY_FILE);
+ break;
+ case EXFAT_ENTRY_FILE:
+ valid = (current == EXFAT_ENTRY_FILE_INFO);
+ break;
+ case EXFAT_ENTRY_FILE_INFO:
+ valid = (current == EXFAT_ENTRY_FILE_NAME);
+ break;
+ case EXFAT_ENTRY_FILE_NAME:
+ valid = (current == EXFAT_ENTRY_FILE_NAME ||
+ current == EXFAT_ENTRY_NONE ||
+ current >= EXFAT_ENTRY_FILE_TAIL);
+ break;
+ case EXFAT_ENTRY_FILE_TAIL ... 0xff:
+ valid = (current >= EXFAT_ENTRY_FILE_TAIL ||
+ current == EXFAT_ENTRY_NONE);
+ break;
+ }
+
+ if (!valid)
+ {
+ exfat_error("unexpected entry type %#x after %#x at %d/%d",
+ current, previous, i, n);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool check_node(const struct exfat* ef, struct exfat_node* node,
+ le16_t actual_checksum, const struct exfat_entry_meta1* meta1,
+ const struct exfat_entry_meta2* meta2)
+{
+ int cluster_size = CLUSTER_SIZE(*ef->sb);
+ uint64_t clusters_heap_size =
+ (uint64_t) le32_to_cpu(ef->sb->cluster_count) * cluster_size;
+ char buffer[EXFAT_UTF8_NAME_BUFFER_MAX];
+ bool ret = true;
+
+ /*
+ Validate checksum first. If it's invalid all other fields probably
+ contain just garbage.
+ */
+ if (le16_to_cpu(actual_checksum) != le16_to_cpu(meta1->checksum))
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' has invalid checksum (%#hx != %#hx)", buffer,
+ le16_to_cpu(actual_checksum), le16_to_cpu(meta1->checksum));
+ if (!EXFAT_REPAIR(invalid_node_checksum, ef, node))
+ ret = false;
+ }
+
+ /*
+ 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 (le64_to_cpu(meta2->valid_size) > node->size)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' has valid size (%"PRIu64") greater than size "
+ "(%"PRIu64")", buffer, le64_to_cpu(meta2->valid_size),
+ node->size);
+ ret = false;
+ }
+
+ /*
+ Empty file must have zero start cluster. Non-empty file must start
+ with a valid cluster. Directories cannot be empty (i.e. must always
+ have a valid start cluster), but we will check this later while
+ reading that directory to give user a chance to read this directory.
+ */
+ if (node->size == 0 && node->start_cluster != EXFAT_CLUSTER_FREE)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' is empty but start cluster is %#x", buffer,
+ node->start_cluster);
+ ret = false;
+ }
+ if (node->size > 0 && CLUSTER_INVALID(*ef->sb, node->start_cluster))
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' points to invalid cluster %#x", buffer,
+ node->start_cluster);
+ ret = false;
+ }
+
+ /* File or directory cannot be larger than clusters heap. */
+ if (node->size > clusters_heap_size)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' is larger than clusters heap: %"PRIu64" > %"PRIu64,
+ buffer, node->size, clusters_heap_size);
+ ret = false;
+ }
+
+ /* Empty file or directory must be marked as non-contiguous. */
+ if (node->size == 0 && node->is_contiguous)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' is empty but marked as contiguous (%#hx)", buffer,
+ node->attrib);
+ ret = false;
+ }
+
+ /* Directory size must be aligned on at cluster boundary. */
+ if ((node->attrib & EXFAT_ATTRIB_DIR) && node->size % cluster_size != 0)
+ {
+ exfat_get_name(node, buffer);
+ exfat_error("'%s' directory size %"PRIu64" is not divisible by %d", buffer,
+ node->size, cluster_size);
+ ret = false;
+ }
+
+ return ret;