|
@@ -1050,6 +1050,20 @@ int16_t SdBaseFile::read(void *buf, uint16_t nbyte) {
|
1050
|
1050
|
}
|
1051
|
1051
|
|
1052
|
1052
|
/**
|
|
1053
|
+ * Calculate a checksum for an 8.3 filename
|
|
1054
|
+ *
|
|
1055
|
+ * \param name The 8.3 file name to calculate
|
|
1056
|
+ *
|
|
1057
|
+ * \return The checksum byte
|
|
1058
|
+ */
|
|
1059
|
+uint8_t lfn_checksum(const uint8_t *name) {
|
|
1060
|
+ uint8_t sum = 0;
|
|
1061
|
+ for (uint8_t i = 11; i; i--)
|
|
1062
|
+ sum = ((sum & 1) << 7) + (sum >> 1) + *name++;
|
|
1063
|
+ return sum;
|
|
1064
|
+}
|
|
1065
|
+
|
|
1066
|
+/**
|
1053
|
1067
|
* Read the next entry in a directory.
|
1054
|
1068
|
*
|
1055
|
1069
|
* \param[out] dir The dir_t struct that will receive the data.
|
|
@@ -1065,51 +1079,68 @@ int8_t SdBaseFile::readDir(dir_t *dir, char *longFilename) {
|
1065
|
1079
|
// if not a directory file or miss-positioned return an error
|
1066
|
1080
|
if (!isDir() || (0x1F & curPosition_)) return -1;
|
1067
|
1081
|
|
|
1082
|
+ #define INVALIDATE_LONGNAME() (longFilename[0] = longFilename[1] = '\0')
|
|
1083
|
+
|
1068
|
1084
|
// If we have a longFilename buffer, mark it as invalid.
|
1069
|
1085
|
// If a long filename is found it will be filled automatically.
|
1070
|
|
- if (longFilename) { longFilename[0] = '\0'; longFilename[1] = '\0'; }
|
|
1086
|
+ if (longFilename) INVALIDATE_LONGNAME();
|
|
1087
|
+
|
|
1088
|
+ uint8_t checksum_error = 0xFF, checksum = 0;
|
1071
|
1089
|
|
1072
|
1090
|
while (1) {
|
1073
|
1091
|
|
1074
|
1092
|
n = read(dir, sizeof(dir_t));
|
1075
|
1093
|
if (n != sizeof(dir_t)) return n ? -1 : 0;
|
1076
|
1094
|
|
1077
|
|
- // last entry if DIR_NAME_FREE
|
|
1095
|
+ // Last entry if DIR_NAME_FREE
|
1078
|
1096
|
if (dir->name[0] == DIR_NAME_FREE) return 0;
|
1079
|
1097
|
|
1080
|
|
- // skip deleted entry and entry for . and ..
|
|
1098
|
+ // Skip deleted entry and entry for . and ..
|
1081
|
1099
|
if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
|
1082
|
|
- if (longFilename) { longFilename[0] = '\0'; longFilename[1] = '\0'; } // Invalidate erased file long name, if any
|
|
1100
|
+ if (longFilename) INVALIDATE_LONGNAME(); // Invalidate erased file long name, if any
|
1083
|
1101
|
continue;
|
1084
|
1102
|
}
|
1085
|
1103
|
|
1086
|
|
- // Fill the long filename if we have a long filename entry.
|
1087
|
|
- // Long filename entries are stored before the short filename.
|
1088
|
|
- if (longFilename && DIR_IS_LONG_NAME(dir)) {
|
1089
|
|
- vfat_t *VFAT = (vfat_t*)dir;
|
1090
|
|
- // Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
|
1091
|
|
- if (VFAT->firstClusterLow == 0) {
|
1092
|
|
- const uint8_t seq = VFAT->sequenceNumber & 0x1F;
|
1093
|
|
- if (WITHIN(seq, 1, MAX_VFAT_ENTRIES)) {
|
1094
|
|
- // TODO: Store the filename checksum to verify if a long-filename-unaware system modified the file table.
|
1095
|
|
- n = (seq - 1) * (FILENAME_LENGTH);
|
1096
|
|
- LOOP_L_N(i, FILENAME_LENGTH) {
|
1097
|
|
- uint16_t utf16_ch = (i < 5) ? VFAT->name1[i] : (i < 11) ? VFAT->name2[i - 5] : VFAT->name3[i - 11];
|
1098
|
|
- #if ENABLED(UTF_FILENAME_SUPPORT)
|
1099
|
|
- // We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
|
1100
|
|
- // needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
|
1101
|
|
- uint16_t idx = (n + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
|
1102
|
|
- longFilename[idx] = utf16_ch & 0xFF;
|
1103
|
|
- longFilename[idx + 1] = (utf16_ch >> 8) & 0xFF;
|
1104
|
|
- #else
|
1105
|
|
- // Replace all multibyte characters to '_'
|
1106
|
|
- longFilename[n + i] = (utf16_ch > 0xFF) ? '_' : (utf16_ch & 0xFF);
|
1107
|
|
- #endif
|
|
1104
|
+ if (longFilename) {
|
|
1105
|
+ // Fill the long filename if we have a long filename entry.
|
|
1106
|
+ // Long filename entries are stored before the short filename.
|
|
1107
|
+ if (DIR_IS_LONG_NAME(dir)) {
|
|
1108
|
+ vfat_t *VFAT = (vfat_t*)dir;
|
|
1109
|
+ // Sanity-check the VFAT entry. The first cluster is always set to zero. And the sequence number should be higher than 0
|
|
1110
|
+ if (VFAT->firstClusterLow == 0) {
|
|
1111
|
+ const uint8_t seq = VFAT->sequenceNumber & 0x1F;
|
|
1112
|
+ if (WITHIN(seq, 1, MAX_VFAT_ENTRIES)) {
|
|
1113
|
+ n = (seq - 1) * (FILENAME_LENGTH);
|
|
1114
|
+ if (n == 0) {
|
|
1115
|
+ checksum = VFAT->checksum;
|
|
1116
|
+ checksum_error = 0;
|
|
1117
|
+ }
|
|
1118
|
+ else if (checksum != VFAT->checksum) // orphan detected
|
|
1119
|
+ checksum_error = 1;
|
|
1120
|
+
|
|
1121
|
+ LOOP_L_N(i, FILENAME_LENGTH) {
|
|
1122
|
+ const uint16_t utf16_ch = (i >= 11) ? VFAT->name3[i - 11] : (i >= 5) ? VFAT->name2[i - 5] : VFAT->name1[i];
|
|
1123
|
+ #if ENABLED(UTF_FILENAME_SUPPORT)
|
|
1124
|
+ // We can't reconvert to UTF-8 here as UTF-8 is variable-size encoding, but joining LFN blocks
|
|
1125
|
+ // needs static bytes addressing. So here just store full UTF-16LE words to re-convert later.
|
|
1126
|
+ uint16_t idx = (n + i) * 2; // This is fixed as FAT LFN always contain UTF-16LE encoding
|
|
1127
|
+ longFilename[idx] = utf16_ch & 0xFF;
|
|
1128
|
+ longFilename[idx + 1] = (utf16_ch >> 8) & 0xFF;
|
|
1129
|
+ #else
|
|
1130
|
+ // Replace all multibyte characters to '_'
|
|
1131
|
+ longFilename[n + i] = (utf16_ch > 0xFF) ? '_' : (utf16_ch & 0xFF);
|
|
1132
|
+ #endif
|
|
1133
|
+ }
|
|
1134
|
+ // If this VFAT entry is the last one, add a NUL terminator at the end of the string
|
|
1135
|
+ if (VFAT->sequenceNumber & 0x40)
|
|
1136
|
+ longFilename[(n + FILENAME_LENGTH) * LONG_FILENAME_CHARSIZE] = '\0';
|
1108
|
1137
|
}
|
1109
|
|
- // If this VFAT entry is the last one, add a NUL terminator at the end of the string
|
1110
|
|
- if (VFAT->sequenceNumber & 0x40) longFilename[(n + FILENAME_LENGTH) * LONG_FILENAME_CHARSIZE] = '\0';
|
1111
|
1138
|
}
|
1112
|
1139
|
}
|
|
1140
|
+ else {
|
|
1141
|
+ if (!checksum_error && lfn_checksum(dir->name) != checksum) checksum_error = 1; // orphan detected
|
|
1142
|
+ if (checksum_error) INVALIDATE_LONGNAME();
|
|
1143
|
+ }
|
1113
|
1144
|
}
|
1114
|
1145
|
|
1115
|
1146
|
// Post-process normal file or subdirectory longname, if any
|