+
+struct vfat_lfn_entry {
+ uint8_t seq;
+ uint16_t name0[5];
+ uint8_t attr;
+ uint8_t reserved;
+ uint8_t cksum;
+ uint16_t name1[6];
+ uint16_t cluster;
+ uint16_t name2[2];
+} PACKED;
+
+static uint8_t fat_lfn_checksum(const uint8_t name[11])
+{
+ uint8_t cksum = 0;
+ int i;
+
+ /* http://en.wikipedia.org/wiki/File_Allocation_Table */
+ for (i = 0; i < 11; i++)
+ cksum = ((cksum & 1) ? 0x80 : 0) + (cksum >> 1) + name[i];
+
+ return cksum;
+}
+
+static size_t fat_read_lfn(uint8_t *filename, size_t fnsize,
+ struct vfat_dir_entry *dir,
+ struct vfat_dir_entry *entry)
+{
+ uint8_t buffer[VFAT_LFN_SEQ_MAX * VFAT_LFN_CHARS_PER_ENTRY * 2];
+ uint8_t expected_seq = 1;
+ uint8_t cksum;
+ size_t len = 0;
+ size_t fnlen = 0;
+
+ cksum = fat_lfn_checksum(entry->name);
+
+ while (--entry >= dir) {
+ struct vfat_lfn_entry *lfn = (struct vfat_lfn_entry *) entry;
+
+ if (expected_seq > VFAT_LFN_SEQ_MAX)
+ break;
+
+ if ((lfn->attr & FAT_ATTR_MASK) != FAT_ATTR_LONG_NAME)
+ break;
+
+ if (lfn->cksum != cksum)
+ break;
+
+ if ((lfn->seq & VFAT_LFN_SEQ_MASK) != expected_seq++)
+ break;
+
+ if (lfn->cluster != 0)
+ break;
+
+ /* extra paranoia -- should never happen */
+ if (len + sizeof(lfn->name0) + sizeof(lfn->name1) +
+ sizeof(lfn->name2) > sizeof(buffer))
+ break;
+
+ memcpy (&buffer[len], lfn->name0, sizeof(lfn->name0));
+ len += sizeof(lfn->name0);
+ memcpy (&buffer[len], lfn->name1, sizeof(lfn->name1));
+ len += sizeof(lfn->name1);
+ memcpy (&buffer[len], lfn->name2, sizeof(lfn->name2));
+ len += sizeof(lfn->name2);
+
+ if (lfn->seq & VFAT_LFN_SEQ_LAST) {
+ fnlen = volume_id_set_unicode16(filename, fnsize, buffer, LE, len);
+ break;
+ }
+ }
+
+ return fnlen;
+}
+
+static size_t fat_read_filename(uint8_t *filename, size_t fnsize,
+ struct vfat_dir_entry *dir, struct vfat_dir_entry *entry)
+{
+ size_t len;
+ int i;
+
+ /* check if maybe we have LFN entries */
+ len = fat_read_lfn(filename, fnsize, dir, entry);
+ if (len > 0)
+ goto out;
+
+ /* else, read the normal 8.3 name */
+ for (i = 0; i < 11; i++) {
+ if (entry->lowercase & ((i < 8) ? VFAT_LOWERCASE_NAME : VFAT_LOWERCASE_EXT))
+ filename[i] = tolower(entry->name[i]);
+ else
+ filename[i] = entry->name[i];
+ }
+ len = 11;
+
+out:
+ filename[len] = '\0';
+ return len;
+}
+
+/* fills filename, returns string length */
+static size_t get_fat_attr_volume_id(uint8_t *filename, size_t fnsize,
+ struct vfat_dir_entry *dir, unsigned int count)