+ /*
+ 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;
+}
+
+static int parse_file_entries(struct exfat* ef, struct exfat_node* node,
+ const struct exfat_entry* entries, int n)
+{
+ const struct exfat_entry_meta1* meta1;
+ const struct exfat_entry_meta2* meta2;
+ int mandatory_entries;
+
+ if (!check_entries(entries, n))
+ return -EIO;
+
+ meta1 = (const struct exfat_entry_meta1*) &entries[0];
+ if (meta1->continuations < 2)
+ {
+ exfat_error("too few continuations (%hhu)", meta1->continuations);
+ return -EIO;
+ }
+ meta2 = (const struct exfat_entry_meta2*) &entries[1];
+ if (meta2->flags & ~(EXFAT_FLAG_ALWAYS1 | EXFAT_FLAG_CONTIGUOUS))
+ {
+ exfat_error("unknown flags in meta2 (%#hhx)", meta2->flags);
+ return -EIO;
+ }
+ mandatory_entries = 2 + DIV_ROUND_UP(meta2->name_length, EXFAT_ENAME_MAX);
+ if (meta1->continuations < mandatory_entries - 1)
+ {
+ exfat_error("too few continuations (%hhu < %d)",
+ meta1->continuations, mandatory_entries - 1);
+ return -EIO;
+ }
+
+ init_node_meta1(node, meta1);
+ init_node_meta2(node, meta2);
+ init_node_name(node, entries + 2, mandatory_entries - 2);
+
+ if (!check_node(ef, node, exfat_calc_checksum(entries, n), meta1, meta2))
+ return -EIO;
+
+ return 0;
+}
+
+static int parse_file_entry(struct exfat* ef, struct exfat_node* parent,
+ struct exfat_node** node, off_t* offset, int n)
+{
+ struct exfat_entry entries[n];
+ int rc;
+
+ rc = read_entries(ef, parent, entries, n, *offset);
+ if (rc != 0)
+ return rc;
+
+ /* a new node has zero references */
+ *node = allocate_node();
+ if (*node == NULL)
+ return -ENOMEM;
+ (*node)->entry_offset = *offset;
+
+ rc = parse_file_entries(ef, *node, entries, n);
+ if (rc != 0)
+ {
+ free(*node);
+ return rc;
+ }
+
+ *offset += sizeof(struct exfat_entry[n]);
+ return 0;
+}
+
+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;
+ }